gridtracker/package.nw/lib/shadow.js

456 wiersze
12 KiB
JavaScript

/**
**/
(function (global, factory)
{
typeof exports === "object" && typeof module !== "undefined"
? (module.exports = factory())
: typeof define === "function" && define.amd
? define(factory)
: (global.GeoJSONTerminator = factory());
})(this, function ()
{
"use strict";
function julian(date)
{
/* Calculate the present UTC Julian Date. Function is valid after
* the beginning of the UNIX epoch 1970-01-01 and ignores leap
* seconds. */
return date / 86400000 + 2440587.5;
}
function GMST(julianDay)
{
/* Calculate Greenwich Mean Sidereal Time according to
http://aa.usno.navy.mil/faq/docs/GAST.php */
var d = julianDay - 2451545.0;
// Low precision equation is good enough for our purposes.
return (18.697374558 + 24.06570982441908 * d) % 24;
}
class Terminator
{
constructor(options = { resolution: 1 })
{
this.options = options;
this.version = "0.1.0";
this._R2D = 180 / Math.PI;
this._D2R = Math.PI / 180;
// this.options.resolution = options.resolution || this.options.resolution;
// this.options.time = options.time;
var latLngs = this._compute(this.options.time);
return this._toGeoJSON(latLngs);
}
setTime(date)
{
this.options.time = date;
var latLngs = this._compute(date);
return this._toGeoJSON(latLngs);
}
_toGeoJSON(latLngs)
{
/* Return 'pseudo' GeoJSON representation of the coordinates
Why 'pseudo'?
Coordinates longitude range go from -360 to 360
whereas it should be -180, + 180
API like OpenLayers or Leaflet can consume them although invalid
from GeoJSON spec
In this case, use something like GDAL/OGR to clip to a valid range with
ogr2ogr -f "GeoJSON" output.geojson input.geojson \
-clipsrc -180 90 180 90
*/
return {
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[
...latLngs.map((latLng) =>
{
return [latLng[1], latLng[0]];
}),
[latLngs[0][1], latLngs[0][0]]
]
.slice()
.reverse()
]
}
};
}
_sunEclipticPosition(julianDay)
{
/* Compute the position of the Sun in ecliptic coordinates at
julianDay. Following
http://en.wikipedia.org/wiki/Position_of_the_Sun */
// Days since start of J2000.0
var n = julianDay - 2451545.0;
// mean longitude of the Sun
var L = 280.46 + 0.9856474 * n;
L %= 360;
// mean anomaly of the Sun
var g = 357.528 + 0.9856003 * n;
g %= 360;
// ecliptic longitude of Sun
var lambda =
L +
1.915 * Math.sin(g * this._D2R) +
0.02 * Math.sin(2 * g * this._D2R);
return { lambda: lambda };
}
_eclipticObliquity(julianDay)
{
// Following the short term expression in
// http://en.wikipedia.org/wiki/Axial_tilt#Obliquity_of_the_ecliptic_.28Earth.27s_axial_tilt.29
var n = julianDay - 2451545.0;
// Julian centuries since J2000.0
var T = n / 36525;
var epsilon =
23.43929111 -
T *
(46.836769 / 3600 -
T *
(0.0001831 / 3600 +
T *
(0.0020034 / 3600 -
T * (0.576e-6 / 3600 - (T * 4.34e-8) / 3600))));
return epsilon;
}
_jday(date)
{
return date.getTime() / 86400000.0 + 2440587.5;
}
_calculatePositionOfSun(date)
{
date = date instanceof Date ? date : new Date();
var rad = 0.017453292519943295;
// based on NOAA solar calculations
var ms_past_midnight =
((date.getUTCHours() * 60 + date.getUTCMinutes()) * 60 +
date.getUTCSeconds()) *
1000 +
date.getUTCMilliseconds();
var jc = (this._jday(date) - 2451545) / 36525;
var mean_long_sun =
(280.46646 + jc * (36000.76983 + jc * 0.0003032)) % 360;
var mean_anom_sun = 357.52911 + jc * (35999.05029 - 0.0001537 * jc);
var sun_eq =
Math.sin(rad * mean_anom_sun) *
(1.914602 - jc * (0.004817 + 0.000014 * jc)) +
Math.sin(rad * 2 * mean_anom_sun) * (0.019993 - 0.000101 * jc) +
Math.sin(rad * 3 * mean_anom_sun) * 0.000289;
var sun_true_long = mean_long_sun + sun_eq;
var sun_app_long =
sun_true_long -
0.00569 -
0.00478 * Math.sin(rad * 125.04 - 1934.136 * jc);
var mean_obliq_ecliptic =
23 +
(26 + (21.448 - jc * (46.815 + jc * (0.00059 - jc * 0.001813))) / 60) /
60;
var obliq_corr =
mean_obliq_ecliptic + 0.00256 * Math.cos(rad * 125.04 - 1934.136 * jc);
var lat =
Math.asin(Math.sin(rad * obliq_corr) * Math.sin(rad * sun_app_long)) /
rad;
var eccent = 0.016708634 - jc * (0.000042037 + 0.0000001267 * jc);
var y =
Math.tan(rad * (obliq_corr / 2)) * Math.tan(rad * (obliq_corr / 2));
var rq_of_time =
4 *
((y * Math.sin(2 * rad * mean_long_sun) -
2 * eccent * Math.sin(rad * mean_anom_sun) +
4 *
eccent *
y *
Math.sin(rad * mean_anom_sun) *
Math.cos(2 * rad * mean_long_sun) -
0.5 * y * y * Math.sin(4 * rad * mean_long_sun) -
1.25 * eccent * eccent * Math.sin(2 * rad * mean_anom_sun)) /
rad);
var true_solar_time_in_deg =
((ms_past_midnight + rq_of_time * 60000) % 86400000) / 240000;
var lng = -(true_solar_time_in_deg < 0
? true_solar_time_in_deg + 180
: true_solar_time_in_deg - 180);
return [lng, lat];
}
_sunEquatorialPosition(sunEclLng, eclObliq)
{
/* Compute the Sun's equatorial position from its ecliptic
* position. Inputs are expected in degrees. Outputs are in
* degrees as well. */
var alpha =
Math.atan(
Math.cos(eclObliq * this._D2R) * Math.tan(sunEclLng * this._D2R)
) * this._R2D;
var delta =
Math.asin(
Math.sin(eclObliq * this._D2R) * Math.sin(sunEclLng * this._D2R)
) * this._R2D;
var lQuadrant = Math.floor(sunEclLng / 90) * 90;
var raQuadrant = Math.floor(alpha / 90) * 90;
alpha = alpha + (lQuadrant - raQuadrant);
return { alpha: alpha, delta: delta };
}
_hourAngle(lng, sunPos, gst)
{
/* Compute the hour angle of the sun for a longitude on
* Earth. Return the hour angle in degrees. */
var lst = gst + lng / 15;
return lst * 15 - sunPos.alpha;
}
_latitude(ha, sunPos)
{
/* For a given hour angle and sun position, compute the
* latitude of the terminator in degrees. */
var lat =
Math.atan(
-Math.cos(ha * this._D2R) / Math.tan(sunPos.delta * this._D2R)
) * this._R2D;
return lat;
}
_compute(time)
{
var today = time ? new Date(time) : new Date();
var julianDay = julian(today);
var gst = GMST(julianDay);
var latLng = [];
var startMinus = -360;
var sunEclPos = this._sunEclipticPosition(julianDay);
var eclObliq = this._eclipticObliquity(julianDay);
var sunEqPos = this._sunEquatorialPosition(sunEclPos.lambda, eclObliq);
for (var i = 0; i <= 720 * this.options.resolution; i++)
{
var lng = startMinus + i / this.options.resolution;
var ha = this._hourAngle(lng, sunEqPos, gst);
latLng[i + 1] = [this._latitude(ha, sunEqPos), lng];
}
if (sunEqPos.delta < 0)
{
latLng[0] = [90, startMinus];
latLng[latLng.length] = [90, 360];
}
else
{
latLng[0] = [-90, startMinus];
latLng[latLng.length] = [-90, 360];
}
return latLng;
}
}
function terminator(options)
{
return new Terminator(options);
}
return terminator;
});
var dayNight = {
map: null,
vectorLayer: null,
init: function (map)
{
this.map = map;
var geoJSON = new GeoJSONTerminator();
this.vectorSource = new ol.source.Vector({
features: new ol.format.GeoJSON().readFeatures(geoJSON, {
featureProjection: "EPSG:3857"
})
});
this.vectorLayer = new ol.layer.Vector({
source: this.vectorSource,
style: new ol.style.Style({
fill: new ol.style.Fill({
color: "rgb(0,0,0)"
}),
stroke: null
}),
opacity: Number(GT.mapSettings.shadow),
zIndex: 0
});
this.map.getLayers().insertAt(1, this.vectorLayer);
},
refresh: function ()
{
var circleStyle = new ol.style.Style({
fill: new ol.style.Fill({
color: "rgb(0,0,0)"
})
});
this.vectorLayer.setStyle(circleStyle);
this.vectorLayer.setOpacity(Number(GT.mapSettings.shadow));
this.vectorSource.clear();
this.vectorSource.addFeature(
new ol.format.GeoJSON().readFeature(new GeoJSONTerminator(), {
featureProjection: "EPSG:3857"
})
);
var point = ol.proj.fromLonLat([GT.myLon, GT.myLat]);
var arr = this.vectorSource.getFeaturesAtCoordinate(point);
return arr.length > 0;
},
show: function ()
{
this.vectorLayer.setVisible(true);
return this.refresh();
},
hide: function ()
{
this.vectorLayer.setVisible(false);
},
isVisible: function ()
{
return this.vectorLayer.getVisible();
}
};
var moonLayer = {
map: null,
vectorLayer: null,
icon: null,
pin: null,
init: function (map)
{
this.map = map;
this.icon = new ol.style.Icon({
src: "./img/luna.png",
anchorYUnits: "pixels",
anchorXUnits: "pixels",
anchor: [255, 255],
scale: 0.1,
opacity: 0.5
});
this.pin = iconFeature(
ol.proj.fromLonLat(subLunar(timeNowSec()).ll),
this.icon,
0
);
this.pin.size = 99;
this.vectorSource = new ol.source.Vector({});
this.vectorLayer = new ol.layer.Vector({
source: this.vectorSource,
zIndex: 30
});
this.map.getLayers().insertAt(1, this.vectorLayer);
},
future: function (now)
{
var r = 0;
var x = 25;
var i = 3600;
var data = Array();
for (r = 0; r < x; r++)
{
data.push(subLunar(now + r * i).ll);
}
line = [];
var lonOff = 0;
var lastc = 0;
for (var i = 0; i < data.length; i++)
{
var c = data[i];
if (isNaN(c[0]))
{
continue;
}
if (Math.abs(lastc - c[0]) > 270)
{
// Wrapped
if (c[0] < lastc)
{
lonOff += 360;
}
else
{
lonOff -= 360;
}
}
lastc = c[0];
line.push(ol.proj.fromLonLat([c[0] + lonOff, c[1]]));
}
if (line.length == 0)
{
line.push(ol.proj.fromLonLat(start));
}
line = new ol.geom.LineString(line);
var feature = new ol.Feature({ geometry: line, name: "moonFlight" });
feature.setStyle(
new ol.style.Style({
stroke: new ol.style.Stroke({ color: "#FFF", width: 1 })
})
);
return feature;
},
refresh: function ()
{
this.vectorSource.clear();
if (GT.appSettings.moonTrack == 1)
{
now = timeNowSec();
if (GT.appSettings.moonPath == 1)
{ this.vectorSource.addFeature(this.future(now)); }
this.pin = iconFeature(
ol.proj.fromLonLat(subLunar(now).ll),
this.icon,
0
);
this.pin.size = 99;
this.vectorSource.addFeature(this.pin);
}
},
show: function ()
{
this.refresh();
this.vectorLayer.setVisible(true);
lunaButonImg.style.webkitFilter = "brightness(100%)";
},
hide: function ()
{
this.vectorLayer.setVisible(false);
lunaButonImg.style.webkitFilter = "brightness(50%)";
},
isVisible: function ()
{
return this.vectorLayer.getVisible();
}
};