Sun azimuth and altitude
64-bitThe sun's direction and height above the horizon at a moment and a place.
JD = ms ÷ 86 400 000 + 2 440 587.5; T = (JD − 2 451 545) ÷ 36 525; cos Z = sin φ·sin δ + cos φ·cos δ·cos H
The Julian day is a number near 2.46 million, so a 64-bit value resolves it to about 40 microseconds; the century fraction T is then a difference of two such numbers. The series constants are written as published (for example 280.46646 and 36000.76983). Both acos arguments are clamped to [−1, 1].
export function sunPosition(date, lat, lon) {
const ms = date instanceof Date ? date.getTime() : Number(date);
const jd = ms / 86400000 + 2440587.5, T = (jd - 2451545) / 36525;
const L0 = (((280.46646 + T * (36000.76983 + T * 0.0003032)) % 360) + 360) % 360;
const M = 357.52911 + T * (35999.05029 - 0.0001537 * T);
const e = 0.016708634 - T * (0.000042037 + 0.0000001267 * T);
const C = Math.sin(M * R) * (1.914602 - T * (0.004817 + 0.000014 * T)) + Math.sin(2 * M * R) * (0.019993 - 0.000101 * T) + Math.sin(3 * M * R) * 0.000289;
const omega = 125.04 - 1934.136 * T, lambda = L0 + C - 0.00569 - 0.00478 * Math.sin(omega * R);
const eps0 = 23 + (26 + (21.448 - T * (46.815 + T * (0.00059 - T * 0.001813))) / 60) / 60, eps = eps0 + 0.00256 * Math.cos(omega * R);
const decl = Math.asin(Math.sin(eps * R) * Math.sin(lambda * R)) / R;
const y = Math.tan((eps / 2) * R) ** 2;
const eot = (4 / R) * (y * Math.sin(2 * L0 * R) - 2 * e * Math.sin(M * R) + 4 * e * y * Math.sin(M * R) * Math.cos(2 * L0 * R) - 0.5 * y * y * Math.sin(4 * L0 * R) - 1.25 * e * e * Math.sin(2 * M * R));
const d = new Date(ms), minutes = d.getUTCHours() * 60 + d.getUTCMinutes() + d.getUTCSeconds() / 60 + d.getUTCMilliseconds() / 60000;
let tst = (minutes + eot + 4 * lon) % 1440; if (tst < 0) tst += 1440;
const ha = tst / 4 < 0 ? tst / 4 + 180 : tst / 4 - 180;
const cosZ = Math.sin(lat * R) * Math.sin(decl * R) + Math.cos(lat * R) * Math.cos(decl * R) * Math.cos(ha * R);
const zen = Math.acos(Math.max(-1, Math.min(1, cosZ))) / R, elev = 90 - zen;
// azimuth from north, clockwise
const sz = Math.sin(zen * R);
let az;
if (Math.abs(sz) < 1e-9) az = lat > decl ? 180 : 0;
else {
const c = (Math.sin(lat * R) * Math.cos(zen * R) - Math.sin(decl * R)) / (Math.cos(lat * R) * sz);
const a = Math.acos(Math.max(-1, Math.min(1, c))) / R;
az = ha > 0 ? (a + 180) % 360 : (540 - a) % 360;
}
return { azimuth: az, altitude: elev + refraction(elev), declination: decl, eot };
}