cloudlog/assets/js/leaflet/L.Terminator.js
Peter Goodhall 9d90724eba Add migration 262 and fix terminator tiling
Bump migration version to 262 and add Migration_tag_2_8_10 which updates the stored app version to 2.8.10 and forces the version info dialog by resetting the user option. The migration includes a down() to revert the version to 2.8.9. Also update L.Terminator.js to generate three longitude-shifted polygon copies (offsets -360, 0, +360) so the night/day terminator overlay tiles correctly across world copies when the map is zoomed out.
2026-03-27 11:11:46 +00:00

132 lines
4.2 KiB
JavaScript

(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
module.exports = factory(require('leaflet'));
} else {
factory(window.L);
}
}(function(L) {
if (!L) {
return;
}
function toRadians(degrees) {
return degrees * Math.PI / 180;
}
function toDegrees(radians) {
return radians * 180 / Math.PI;
}
function normalizeLongitude(longitude) {
var normalized = ((longitude + 180) % 360 + 360) % 360 - 180;
return normalized;
}
function julianDate(date) {
return date.getTime() / 86400000 + 2440587.5;
}
function solarPosition(date) {
var jd = julianDate(date);
var n = jd - 2451545.0;
var Ls = (280.460 + 0.9856474 * n) % 360;
var g = (357.528 + 0.9856003 * n) % 360;
var lambda = Ls + 1.915 * Math.sin(toRadians(g)) + 0.020 * Math.sin(toRadians(2 * g));
var epsilon = 23.439 - 0.0000004 * n;
var lambdaRad = toRadians(lambda);
var epsilonRad = toRadians(epsilon);
var declination = Math.asin(Math.sin(epsilonRad) * Math.sin(lambdaRad));
var rightAscension = Math.atan2(Math.cos(epsilonRad) * Math.sin(lambdaRad), Math.cos(lambdaRad));
var gmst = 280.46061837 + 360.98564736629 * (jd - 2451545.0);
var subsolarLongitude = normalizeLongitude(toDegrees(rightAscension) - gmst);
return {
declination: declination,
subsolarLongitude: subsolarLongitude
};
}
function terminatorLatitude(longitude, declination, subsolarLongitude) {
var hourAngle = toRadians(longitude - subsolarLongitude);
var tanDeclination = Math.tan(declination);
if (Math.abs(tanDeclination) < 1e-10) {
tanDeclination = tanDeclination >= 0 ? 1e-10 : -1e-10;
}
var latitude = Math.atan(-Math.cos(hourAngle) / tanDeclination);
return toDegrees(latitude);
}
L.Terminator = L.Polygon.extend({
options: {
time: null,
resolution: 1,
weight: 1.5,
color: '#4a6fa5',
fillColor: '#001f3f',
fillOpacity: 0.18,
interactive: false,
bubblingMouseEvents: false
},
initialize: function(options) {
L.setOptions(this, options);
this._time = this.options.time || new Date();
this._setTerminatorLatLngs();
},
setTime: function(time) {
this._time = time || new Date();
this._setTerminatorLatLngs();
return this;
},
_setTerminatorLatLngs: function() {
var position = solarPosition(this._time);
var declination = position.declination;
var nightOverNorthPole = declination < 0;
var resolution = Math.max(1, this.options.resolution || 2);
var curve = [];
for (var lon = -180; lon <= 180; lon += resolution) {
curve.push([terminatorLatitude(lon, declination, position.subsolarLongitude), lon]);
}
if (curve[curve.length - 1][1] !== 180) {
curve.push([terminatorLatitude(180, declination, position.subsolarLongitude), 180]);
}
// Build 3 copies at -360, 0, +360 so the overlay tiles across
// multiple world copies when the map is zoomed out.
var polygons = [];
for (var offset = -360; offset <= 360; offset += 360) {
var shiftedCurve = curve.map(function(pt) {
return [pt[0], pt[1] + offset];
});
var polygon;
if (nightOverNorthPole) {
polygon = [[90, -180 + offset]].concat(shiftedCurve).concat([[90, 180 + offset]]);
} else {
polygon = [[-90, -180 + offset]].concat(shiftedCurve).concat([[-90, 180 + offset]]);
}
polygons.push([polygon]);
}
this.setLatLngs(polygons);
}
});
L.terminator = function(options) {
return new L.Terminator(options);
};
return L.Terminator;
}));