@tubular/astronomy
Version:
Astronomical calculations for planetary positions, moon phases, eclipses, rise, transit, and set times, and more.
149 lines • 6.83 kB
JavaScript
// Note: I've modified the standard refraction formulas so that if they are
// fed values well below the horizon they won't return weird values, but will
// instead make a smooth transition between -2 and -4 degrees to an identity
// function for angles from -4 to -90 degrees.
// Degrees in, degrees out.
//
import { Angle, floor, interpolate, limitNeg1to1, max, min, pow, round, SphericalPosition, sqrt, tan_deg, Unit } from '@tubular/math';
import { blendColors } from '@tubular/util';
import { GALACTIC_ASCENDING_NODE_B1950, GALACTIC_NORTH_B1950, JD_B1950, JD_J2000, LOW_PRECISION, MOON, QUICK_SUN, SUN } from './astro-constants';
import { Ecliptic } from './ecliptic';
export const COLOR_NIGHT = 'black';
export const COLOR_ASTRONOMICAL_TWILIGHT = '#000044';
export const COLOR_NAUTICAL_TWILIGHT = '#000066';
export const COLOR_CIVIL_TWILIGHT = '#990066';
export const COLOR_NEAR_SUNRISE = '#CC6600';
export const COLOR_EARLY_SUNRISE = '#DDBB33';
export const COLOR_LATE_SUNRISE = '#DDDDAA';
export const COLOR_DAY = '#99CCFF';
export const COLORS_MOONLIGHT = ['black', '#333333', '#666666', '#999999'];
const COLORS_DEEP_TWILIGHT = [COLOR_ASTRONOMICAL_TWILIGHT, COLOR_NAUTICAL_TWILIGHT];
const TWILIGHT_MOON_BLENDS = [];
(function () {
for (let i = 0; i < 2; ++i) {
TWILIGHT_MOON_BLENDS[i] = [];
for (let j = 0; j < 3; ++j)
TWILIGHT_MOON_BLENDS[i][j] = blendColors(COLORS_DEEP_TWILIGHT[i], COLORS_MOONLIGHT[j + 1]);
}
})();
const h_adj = refractedAltitudeAux(90);
const h0_adj = unrefractedAltitudeAux(90);
export function refractedAltitude(trueAltitude) {
if (trueAltitude < -4)
return trueAltitude;
const h2 = trueAltitude + refractedAltitudeAux(trueAltitude) - h_adj;
if (trueAltitude < -2)
return interpolate(-4, trueAltitude, -2, trueAltitude, h2);
else
return h2;
}
function refractedAltitudeAux(h) {
// Tweaked a little for agreement with standard of 0.5833 degrees at horizon
// (Original form was 1.02 / tan_deg(h... ))
return 1.033879 / tan_deg(h + 10.3 / (h + 5.11)) / 60;
}
// Degrees in, degrees out.
//
export function unrefractedAltitude(apparentAltitude) {
if (apparentAltitude < -4)
return apparentAltitude;
const h2 = apparentAltitude - unrefractedAltitudeAux(apparentAltitude) + h0_adj;
if (apparentAltitude < -2)
return interpolate(-4, apparentAltitude, -2, apparentAltitude, h2);
else
return h2;
}
function unrefractedAltitudeAux(h0) {
// Tweaked a little for agreement with standard of 0.5833 degrees at horizon
// (Original form was 1 / tan_deg(h0... ))
return 1.015056 / tan_deg(h0 + 7.31 / (h0 + 4.4)) / 60;
}
export function getSkyColor(sunPos, skyPos, eclipseTotality = 0) {
const sunAltitude = sunPos.altitude.degrees;
if (sunAltitude <= -18)
return 'black';
let elongation = skyPos.distanceFrom(sunPos).degrees;
const skyAltitude = skyPos.altitude.degrees;
const shade = min((18 + sunAltitude) / 18, 1);
const sunRed = min(1.2 * shade, 1);
const sunGreen = pow(shade, 1.6);
const sunBlue = 0.8 * pow(0.8 * shade, 2.2);
const baseRed = 0.4 * shade;
const baseGreen = 0.6 * shade;
const baseBlue = shade;
if (sunAltitude < 0)
elongation = max(elongation + sunAltitude, 0.20);
const sunBias = min(max((45 - elongation) / 45, 0), 1);
const baseBias = 1 - sunBias / 2.5;
const altBias = 1 - (sqrt(max(skyAltitude, 0))) / 30;
let eclBias = 1 - 0.8 * eclipseTotality;
if (eclipseTotality > 0.99)
eclBias = 20.8 * (1 - eclipseTotality);
const r = (sunRed * sunBias + baseRed * baseBias) * altBias * eclBias;
const g = (sunGreen * sunBias + baseGreen * baseBias) * altBias * eclBias;
const b = (sunBlue * sunBias + baseBlue * baseBias) * altBias * (0.2 + eclBias * 0.8);
const scale = 255 / max(r, g, b, 1);
return 'rgb(' + round(r * scale) + ',' + round(g * scale) + ',' + round(b * scale) + ')';
}
export function getInsolationColor(observer, solarSystem, time_JDU, moonlight = false, blendMoonlight = true) {
let color;
let twilightIndex = -1;
let moonIndex;
let altitudeOfMoon;
let illuminationOfMoon;
const altitudeOfSun = solarSystem.getHorizontalPosition(SUN, time_JDU, observer, QUICK_SUN).altitude.degrees;
if (altitudeOfSun < -18)
color = COLOR_NIGHT;
else if (altitudeOfSun < -12) {
color = COLOR_ASTRONOMICAL_TWILIGHT;
twilightIndex = 0;
}
else if (altitudeOfSun < -6) {
color = COLOR_NAUTICAL_TWILIGHT;
twilightIndex = 1;
}
else if (altitudeOfSun < -3)
color = COLOR_CIVIL_TWILIGHT;
else if (altitudeOfSun < -0.833)
color = COLOR_NEAR_SUNRISE;
else if (altitudeOfSun < 4)
color = COLOR_EARLY_SUNRISE;
else if (altitudeOfSun < 8)
color = COLOR_LATE_SUNRISE;
else
color = COLOR_DAY;
if (moonlight && altitudeOfSun < -6) {
altitudeOfMoon = solarSystem.getHorizontalPosition(MOON, time_JDU, observer, LOW_PRECISION).altitude.degrees;
if (altitudeOfMoon >= 0) {
// Technically this should be Dynamical Time, not Universal Time,
// but the difference is trivial here.
illuminationOfMoon = solarSystem.getLunarIlluminatedFraction(time_JDU);
moonIndex = floor((illuminationOfMoon + 0.16) * 3);
if (moonIndex > 0) {
if (twilightIndex >= 0 && blendMoonlight)
color = TWILIGHT_MOON_BLENDS[twilightIndex][moonIndex - 1];
else
color = COLORS_MOONLIGHT[moonIndex];
}
}
}
return color;
}
const A_G = GALACTIC_NORTH_B1950.rightAscension;
const D_G = GALACTIC_NORTH_B1950.declination;
const AN1 = GALACTIC_ASCENDING_NODE_B1950.add(new Angle(270, Unit.DEGREES));
const AN2 = GALACTIC_ASCENDING_NODE_B1950.add(new Angle(90, Unit.DEGREES));
const AG2 = A_G.subtract(new Angle(180, Unit.DEGREES));
export function equatorialToGalactic(pos, time_JDE = JD_J2000) {
pos = Ecliptic.precessEquatorial(pos, time_JDE, JD_B1950);
const ga_a = A_G.subtract(pos.rightAscension);
const d = pos.declination;
return new SphericalPosition(AN1.subtract(Angle.atan2_nonneg(ga_a.sin, ga_a.cos * D_G.sin - d.tan * D_G.cos)), Angle.asin(limitNeg1to1(d.sin * D_G.sin + d.cos * D_G.cos * ga_a.cos)));
}
export function galacticToEquatorial(pos, time_JDE = JD_J2000) {
const l_an2 = pos.rightAscension.subtract(AN2);
const b = pos.declination;
pos = new SphericalPosition(AG2.add(Angle.atan2_nonneg(l_an2.sin, l_an2.cos * D_G.sin - b.tan * D_G.cos)), Angle.asin(limitNeg1to1(b.sin * D_G.sin + b.cos * D_G.cos * l_an2.cos)));
return Ecliptic.precessEquatorial(pos, JD_B1950, time_JDE);
}
//# sourceMappingURL=astronomy-util.js.map