Published
Edited
Jul 12, 2020
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
/**
* 计算方法来自 美国国家海洋和大气管理局[NOAA](https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html)
* Online Demo: https://www.esrl.noaa.gov/gmd/grad/solcalc/
*/

// 初始化计算时使用的基本变量和对象
const PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
tan = Math.tan,
asin = Math.asin,
acos = Math.acos,
pow = Math.pow,
deg = r => r / (PI / 180),
rad = d => d * (PI / 180);

// 关于时间的换算
const DAY_IN_MS = 1000 * 60 * 60 * 24,
J1970 = 2440588,
timezone = date => -date.getTimezoneOffset() / 60,
// 根据Date对象求儒略日
toJulian = date => date.valueOf() / DAY_IN_MS - 0.5 + J1970,
// 根据Date对象求儒略世纪
julianCentury = date => (toJulian(date) - 2451545) / 36525,
//将date对象转换为当天午夜之后过了多少个小时例如1.5个小时就是1个小时30分钟,单位:小时
hours = date =>
date.getHours() +
date.getMinutes() / 60 +
date.getSeconds() / 3600 +
date.getMilliseconds() / 3600000;

// 真黄赤交角
const obliqCorr = jc => {
const sec =
21.448 - 46.815 * jc - 0.00059 * pow(jc, 2) + 0.001813 * pow(jc, 3);

//Mean Obliq Ecliptic 平黄赤交角
const meanObliqEcliptic = 23 + (26 + sec / 60) / 60;
// 修正
const corr = 0.00256 * cos(rad(125.04 - 1934.136 * jc));
return rad(meanObliqEcliptic + corr);
};

// 太阳平近点角
const geomMeanAnomSun = jc =>
rad(357.52911 + jc * (35999.05029 - 0.0001537 * jc));

// 太阳几何平黄经
const geomMeanSunLng = jc => 280.46646 + jc * (36000.76983 + jc * 0.0003032);

/**
* 太阳赤纬角公式
* 单位:弧度
* @param jc 儒略世纪
*/
const declination = jc => {
//太阳平近点角
const gmas = geomMeanAnomSun(jc);

// 太阳几何平黄经
const gmsl = geomMeanSunLng(jc);

// 太阳中心差
const c =
sin(gmas) * (1.914602 - jc * (0.004817 + 0.000014 * jc)) +
sin(2 * gmas) * (0.019993 - 0.000101 * jc) +
sin(3 * gmas) * 0.000289;

//太阳真黄经
const sunTrueLng = (gmsl % 360) + c;

//太阳视黄经
const sunAppLng = rad(
sunTrueLng - 0.00569 - 0.00478 * sin(rad(125.04 - 1934.136 * jc))
);

//真黄赤交角
const oc = obliqCorr(jc);

return asin(sin(oc) * sin(sunAppLng));
};

/**
* 太阳时角公式
* 单位:弧度
* @param date 时间对象
* @param lng 经度
* @param jc 儒略日
*/
const hourAngle = (date, lng, jc) => {
// Geom Mean Long Sun
const gmsl = rad(geomMeanSunLng(jc) % 360),
//Eccent Earth Orbit 地球轨道离心率
e = 0.016708634 - jc * (0.000042037 + 0.0000001267 * jc),
oc = obliqCorr(jc),
//var y
y = pow(tan(oc / 2), 2),
gmas = geomMeanAnomSun(jc);

// 时差
const equationOfTime =
4 *
deg(
y * sin(2 * gmsl) -
2 * e * sin(gmas) +
4 * e * y * sin(gmas) * cos(2 * gmsl) -
0.5 * pow(y, 2) * sin(4 * gmsl) -
1.25 * pow(e, 2) * sin(2 * gmas)
);

// 时区,单位小时
const tz = timezone(date);

// 这一天从午夜算起一共过了多长时间(天)
const days = hours(date) / 24;

// 真太阳时(分钟)
// days * 1440 是将天数转换为分钟
// 全球分24个时区,每个时区时间相隔1小时.每个时区的中央经线相隔15°,即一度为4分钟,240秒÷60=4(秒/分)
let tst = (days * 1440 + equationOfTime + 4 * lng - 60 * tz) % 1440;

tst /= 4;

return rad(tst < 0 ? tst + 180 : tst - 180);
};

/**
* 太阳天顶角公式
* 单位:弧度
* @param lat 纬度
* @param dec 赤维角
* @param ha 时角
*/
const zenith = (lat, dec, ha) =>
acos(sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(ha));

/**
* 太阳方位角公式
* 单位:弧度
* @param lat 纬度
* @param ha 时角
* @param zen 天顶角
* @param dec 赤纬角
*/
const azimuth = (lat, ha, zen, dec) => {
const r = deg(
acos((sin(lat) * cos(zen) - sin(dec)) / (cos(lat) * sin(zen)))
);
return ha > 0 ? rad((r + 180) % 360) : rad((540 - r) % 360);
};

/**
* 根据Date和经纬度求出这一时刻太阳的高度角和方位角
* @param date Date对象
* @param lat 纬度
* @param lng 经度
*/
const sunPosition = (date, lat, lng) => {
const jc = julianCentury(date);
const dec = declination(jc);
const ha = hourAngle(date, lng, jc);
const zen = zenith(rad(lat), dec, ha);

return {
altitude: deg(PI / 2 - zen),
azimuth: deg(azimuth(rad(lat), ha, zen, dec))
};
};

return CalculateSunPostion(sunPosition);
}
Insert cell
Insert cell
{
const deg = r => r / (Math.PI / 180);
const rad = d => d * (Math.PI / 180);
const sunPosition = (date, lat, lng) => {
const pos = SunCalc.getPosition(date, lat, lng);

return {
altitude: deg(pos.altitude),
azimuth: deg(pos.azimuth) + 180
};
};
return CalculateSunPostion(sunPosition);
}
Insert cell
CalculateSunPostion = compute => {
{
// 大寒日8点至16点
let m = 1;
let start = 8;
let end = 16;

if (jie === 'dz') {
// 冬至日9点至15点
m = 12;
start = 9;
end = 15;
}

const div = Math.floor(60 / timeDiv);

const res = [];

for (let i = start; i < end; i++) {
for (let j = 0; j < div; j++) {
const date = new Date();
date.setFullYear(2012, m - 1, 21);
date.setHours(i, j * (60 / div), 0, 0);
const pos = compute(date, coords[1], coords[0]);
res.push({
高度角: pos.altitude,
方位角: pos.azimuth
});
}
}

return res;
}
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more