gridtracker/package.nw/lib/third-party.js

619 wiersze
18 KiB
JavaScript

/* eslint-disable */
// HamGridSquare.js
// Copyright 2014 Paul Brewer KI6CQ
// License: MIT License http://opensource.org/licenses/MIT
//
// Javascript routines to convert from lat-lon to Maidenhead Grid Squares
// typically used in Ham Radio Satellite operations and VHF Contests
//
// Inspired in part by K6WRU Walter Underwood's python answer
// http://ham.stackexchange.com/a/244
// to this stack overflow question:
// How Can One Convert From Lat/Long to Grid Square
// http://ham.stackexchange.com/questions/221/how-can-one-convert-from-lat-long-to-grid-square
//
function latLonToGridSquare(param1,param2, width = 4){
let lat=-100.0;
let lon=0.0;
let adjLat,adjLon,GLat,GLon,nLat,nLon,gLat,gLon,rLat,rLon;
let U = 'ABCDEFGHIJKLMNOPQRSTUVWX';
// support Chris Veness 2002-2012 LatLon library and
// other objects with lat/lon properties
// properties could be getter functions, numbers, or strings
function toNum(x){
if (typeof(x) == 'number') return x;
if (typeof(x) == 'string') return parseFloat(x);
if (typeof(x) == 'function') return parseFloat(x());
throw "HamGridSquare -- toNum -- can not convert input: "+x;
}
if (typeof(param1)=='object'){
if (param1.length == 2){
lat = toNum(param1[0]);
lon = toNum(param1[1]);
} else if (('lat' in param1) && ('lon' in param1)){
lat = toNum(param1.lat);
lon = toNum(param1.lon);
} else if (('latitude' in param1) && ('longitude' in param1)){
lat = toNum(param1.latitude);
lon = toNum(param1.longitude);
} else {
throw "HamGridSquare -- can not convert object -- "+param1;
}
} else {
lat = toNum(param1);
lon = toNum(param2);
}
if (isNaN(lat)) throw "lat is NaN";
if (isNaN(lon)) throw "lon is NaN";
if (Math.abs(lat) == 90.0) throw "grid g_grids invalid at N/S poles";
if (Math.abs(lat) > 90) throw "invalid latitude: "+lat;
if (Math.abs(lon) > 180)
{
if ( lon > 180 )
{
var temp = lon + 360;
temp = temp % 360;
lon = temp - 360;
}
while ( lon < -180 )
{
lon += 180;
lon = 180 + lon;
} // 53032
}
adjLat = lat + 90;
adjLon = lon + 180;
GLat = U[Math.trunc(adjLat/10)];
GLon = U[Math.trunc(adjLon/20)];
nLat = ''+Math.trunc(adjLat % 10);
nLon = ''+Math.trunc((adjLon/2) % 10);
if (width == 4)
{
return GLon+GLat+nLon+nLat;
}
else
{
rLat = (adjLat - Math.trunc(adjLat)) * 60;
rLon = (adjLon - 2*Math.trunc(adjLon/2)) *60;
gLat = U[Math.trunc(rLat/2.5)];
gLon = U[Math.trunc(rLon/5)];
return GLon+GLat+nLon+nLat+gLon+gLat;
}
}
function bitwise(str){
var hash = 0;
if (str.length == 0) return hash;
for (var i = 0; i < str.length; i++) {
var ch = str.charCodeAt(i);
hash = ((hash<<5)-hash) + ch;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
// convert 10 binary to customized binary, max is 62
function binaryTransfer(integer, binary) {
binary = binary || 62;
var stack = [];
var num;
var result = '';
var sign = integer < 0 ? '-' : '';
function table (num) {
var t = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return t[num];
}
integer = Math.abs(integer);
while (integer >= binary) {
num = integer % binary;
integer = Math.floor(integer / binary);
stack.push(table(num));
}
if (integer > 0) {
stack.push(table(integer));
}
for (var i = stack.length - 1; i >= 0; i--) {
result += stack[i];
}
return sign + result;
}
/**
* why choose 61 binary, because we need the last element char to replace the minus sign
* eg: -aGtzd will be ZaGtzd
*/
function unique (text) {
var id = binaryTransfer(bitwise(text), 61);
return id.replace('-', 'Z');
}
var MyCircle = {
validateRadius: function(unit) {
var r = {'M': 6371009, 'KM': 6371.009, 'MI': 3958.761, 'NM': 3440.070, 'YD': 6967420, 'FT': 20902260, 'DG':57.2958};
if ( unit in r ) return r[unit];
else return unit;
},
distance: function(lat1, lon1, lat2, lon2, unit) {
if ( unit == undefined ) unit = 'KM';
var r = this.validateRadius(unit);
lat1 *= Math.PI / 180;
lon1 *= Math.PI / 180;
lat2 *= Math.PI / 180;
lon2 *= Math.PI / 180;
var lonDelta = lon2 - lon1;
var a = Math.pow(Math.cos(lat2) * Math.sin(lonDelta) , 2) + Math.pow(Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lonDelta) , 2);
var b = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lonDelta);
var angle = Math.atan2(Math.sqrt(a) , b);
return angle;
},
bearing: function(lat1, lon1, lat2, lon2) {
lat1 *= Math.PI / 180;
lon1 *= Math.PI / 180;
lat2 *= Math.PI / 180;
lon2 *= Math.PI / 180;
var lonDelta = lon2 - lon1;
var y = Math.sin(lonDelta) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lonDelta);
var brng = Math.atan2(y, x);
brng = brng * (180 / Math.PI);
if ( brng < 0 ) { brng += 360; }
return brng;
},
destination: function(lat1, lon1, brng, dt, unit) {
if ( unit == undefined ) unit = 'KM';
var r = this.validateRadius(unit);
lat1 *= Math.PI / 180;
lon1 *= Math.PI / 180;
var lat3 = Math.asin(Math.sin(lat1) * Math.cos(dt / r) + Math.cos(lat1) * Math.sin(dt / r) * Math.cos( brng * Math.PI / 180 ));
var lon3 = lon1 + Math.atan2(Math.sin( brng * Math.PI / 180 ) * Math.sin(dt / r) * Math.cos(lat1) , Math.cos(dt / r) - Math.sin(lat1) * Math.sin(lat3));
return {
'LAT': lat3 * 180 / Math.PI,
'LON': lon3 * 180 / Math.PI
};
}
}
if (typeof module != 'undefined' && module.exports) {
module.exports = MyCircle;
} else {
window['MyCircle'] = MyCircle;
}
/**
* XML2jsobj v1.0
* Converts XML to a JavaScript object
* so it can be handled like a JSON message
*
* By Craig Buckler, @craigbuckler, http://optimalworks.net
*
* As featured on SitePoint.com:
* http://www.sitepoint.com/xml-to-javascript-object/
*
* Please use as you wish at your own risk.
*/
function XML2jsobj(node) {
var data = null;
// append a value
function Add(name, value) {
if (value == null) return;
if (data == null) data = {};
if (data[name]) {
if (data[name].constructor != Array) {
data[name] = [data[name]];
}
data[name][data[name].length] = value;
}
else {
data[name] = value;
}
};
// element attributes
var c, cn;
for (c = 0; cn = node.attributes[c]; c++) {
Add(cn.name, cn.value);
}
// child elements
for (c = 0; cn = node.childNodes[c]; c++) {
if (cn.nodeType == 1) {
if (cn.childNodes.length == 1 && cn.firstChild.nodeType == 3) {
// text value
Add(cn.nodeName, cn.firstChild.nodeValue);
}
else {
// sub-object
Add(cn.nodeName, XML2jsobj(cn));
}
}
}
return data;
}
// From https://pskreporter.info/
// Many many thanks!!!
function flightFeature(line, opts, layer, canAnimate) {
var steps = opts.steps;
// Map coords into lat lngs
var start = ol.proj.toLonLat(line[0]);
var end = ol.proj.toLonLat(line[1]);
var generator = new arc.GreatCircle({ x: start[0], y: start[1] }, { x: end[0], y: end[1] });
var path = generator.Arc(steps, { offset: 10 });
line = [];
var geom = path.geometries;
var lonOff = 0;
var lastc = 0;
for (var j = 0; j < geom.length; j++) {
var ls = geom[j];
for (var i = 0; i < ls.coords.length; i++) {
var c = ls.coords[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));
}
var dash = [];
var dashOff = 0;
if ( canAnimate == true && GT.mapSettings.animate == true )
{
dash = GT.flightPathLineDash;
dashOff = GT.flightPathTotal - GT.flightPathOffset;
}
var featureArrow = new ol.Feature(new ol.geom.Point(line[0]));
line = new ol.geom.LineString(line);
var feature = new ol.Feature({ geometry: line, prop: 'flight' });
feature.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({ color: opts.color, width: opts.weight, lineDash: dash, lineDashOffset:dashOff}) }));
var stroke = new ol.style.Stroke({color: opts.color, width: opts.weight});
var thisStle = new ol.style.Style({
image: new ol.style.Circle({
stroke: stroke,
radius: 3
})
})
featureArrow.setStyle(thisStle);
feature.Arrow = featureArrow;
GT.layerSources[layer].addFeature(featureArrow);
GT.layerSources[layer].addFeature(feature);
return feature;
}
function rad2deg (r) { return (57.29578*r); }
function deg2rad (d) { return (0.01745329*d); }
function sind( x) { return (sin(deg2rad(x))); }
function cosd( x) { return (cos(deg2rad(x))); }
function tand( x) { return (tan(deg2rad(x))); }
function acosd(x) { return (rad2deg(acos(x))); }
function atand(x) { return (rad2deg(atan(x))); }
function sin(x) { return Math.sin(x); }
function cos(x) { return Math.cos(x); }
function atan2(x,y) { return Math.atan2(x,y); }
function sqrt(x) { return Math.sqrt(x); }
function fmod(a,b) { return Number((a - (Math.floor(a / b) * b)).toPrecision(8)); };
/* given seconds since 1/1/1970 compute sublunar lat and long.
* http://www.stjarnhimlen.se/comp/ppcomp.html
*/
function subLunar (t)
{
// want days since 1999 Dec 31, 0:00 UT
d = (t - 946598400)/(3600.0*24.0);
/* use this if given year month day hour
* double d = 367*y - 7 * ( y + (m+9)/12 ) / 4 + 275*m/9 + D - 730530; // all integer divisions
* d = d + UT/24.0;
*/
M_PI = Math.PI;
// obliquity of the ecliptic
ecl = M_PI/180.0*(23.4393 - 3.563E-7 * d);
/* N = longitude of the ascending node
* i = inclination to the ecliptic
* w = argument of perihelion
* a = semi-major axis
* e = eccentricity (0=circle, 0-1=ellipse, 1=parabola)
* M = mean anomaly (0 at perihelion; increases uniformly with time)
*/
// lunar orbital elements, with respect to Earth
N_m = M_PI/180.0*(125.1228 - 0.0529538083 * d);
i_m = M_PI/180.0*(5.1454);
w_m = M_PI/180.0*(318.0634 + 0.1643573223 * d);
a_m = 60.2666; // Earth radii
e_m = 0.054900;
M_m = M_PI/180.0*(115.3654 + 13.0649929509 * d);
// solar orbital elements (really Earth's)
// double N_s = M_PI/180.0 * (0.0);
// double i_s = M_PI/180.0 * (0.0);
w_s = M_PI/180.0 * (282.9404 + 4.70935E-5 * d);
// double a_s = 1.000000; // AU
// double e_s = 0.016709 - 1.151E-9 * d;
M_s = M_PI/180.0 * (356.0470 + 0.9856002585 * d);
// solar eccentric anomaly
// double E_s = M_s + e_s * sin(M_s) * ( 1.0 + e_s * cos(M_s) );
// eccentric anomaly, no need to refine if e < ~0.05
E_m = M_m + e_m * sin(M_m) * ( 1.0 + e_m * cos(M_m) );
// solar distance and true anomaly
// double xv_s = cos(E_s) - e_s;
// double yv_s = sqrt(1.0 - e_s*e_s) * sin(E_s);
// double v_s = atan2( yv_s, xv_s );
// double r_s = sqrt( xv_s*xv_s + yv_s*yv_s );
// lunar distance and true anomaly
xv_m = a_m * ( cos(E_m) - e_m );
yv_m = a_m * ( sqrt(1.0 - e_m*e_m) * sin(E_m) );
v_m = atan2 ( yv_m, xv_m );
r_m = sqrt ( xv_m*xv_m + yv_m*yv_m );
// ideal (without perturbations) geocentric ecliptic position in 3-dimensional space:
xh_m = r_m * ( cos(N_m) * cos(v_m+w_m) - sin(N_m) * sin(v_m+w_m) * cos(i_m) );
yh_m = r_m * ( sin(N_m) * cos(v_m+w_m) + cos(N_m) * sin(v_m+w_m) * cos(i_m) );
zh_m = r_m * ( sin(v_m+w_m) * sin(i_m) );
// ecliptic long and lat
lonecl_m = atan2( yh_m, xh_m );
latecl_m = atan2( zh_m, sqrt(xh_m*xh_m+yh_m*yh_m) );
// add enough perturbations to yield max error 0.25 degrees long, 0.15 degs lat
L_s = M_s + w_s; // Mean Longitude of the Sun (Ns=0)
L_m = M_m + w_m + N_m; // Mean longitude of the Moon
D_m = L_m - L_s; // Mean elongation of the Moon
F_m = L_m - N_m; // Argument of latitude for the Moon
lonecl_m += M_PI/180.0 * (-1.274 * sin(M_m - 2*D_m)); // Ptolemy's "Evection"
lonecl_m += M_PI/180.0 * ( 0.658 * sin(2*D_m)); // Brahe's "Variation"
lonecl_m += M_PI/180.0 * ( 0.186 * sin(M_s)); // Brahe's "Yearly Equation"
latecl_m += M_PI/180.0 * (-0.173 * sin(F_m - 2*D_m));
// convert back to geocentric, now with perturbations applied
xh_m = r_m * cos(lonecl_m) * cos(latecl_m);
yh_m = r_m * sin(lonecl_m) * cos(latecl_m);
zh_m = r_m * sin(latecl_m);
// lunar ecliptic to geocentric (already)
xg_m = xh_m;
yg_m = yh_m;
zg_m = zh_m;
// convert to equatorial by rotating ecliptic by obliquity
xe_m = xg_m;
ye_m = yg_m * cos(ecl) - zg_m * sin(ecl);
ze_m = yg_m * sin(ecl) + zg_m * cos(ecl);
// compute the planet's Right Ascension (RA) and Declination (Dec):
RA = 180/M_PI * fmod (atan2( ye_m, xe_m ) + 2*M_PI, 2*M_PI); // degrees
Dec = atan2( ze_m, sqrt(xe_m*xe_m+ye_m*ye_m) ); // rads
ll = Object();
ll.lat = Dec;
ll.lat_d = rad2deg(ll.lat);
JD = (t/86400.0) + 2440587.5;
D = JD - 2451545.0;
GMST = fmod(15*(18.697374558 + 24.06570982441908*D), 360.0);
ll.lng_d = fmod(RA-GMST+36000.0+180.0, 360.0) - 180.0;
ll.lng = deg2rad(ll.lng_d);
data = Object();
data.ll = [ll.lng_d,ll.lat_d];
data.RA = RA/15;
data.Dec = 180/M_PI*Dec;
return data;
}
function doRAconvert(lg, la, ras, decs) {
jd=datetojd();
lgt=lg;
lat=rad(la);
ra=ras;
dec=rad(decs);
st=sidTime(jd-2400000.5, lgt)
return convert(ra, dec, st,lat);
}
function fraction(x) {
x=x-Math.floor(x)
if(x<0) {
x++
}
return(x)
}
function sidTime(mjd, lambda) {
mjdo=Math.floor(mjd)
ut=(mjd-mjdo)*24
t=(mjdo-51544.5)/36525.0
gmst=6.697374558+1.0027379093*ut+(8640184.812866+(0.093104-6.2E-6*t)*t)*t/3600.0
return(24.0*fraction((gmst+lambda/15.0)/24.0))
}
function datetojd(datestring)
{
jd = (timeNowSec() /86400.0) + 2440587.5;
return jd
}
function deg(angle) {
return angle*180/Math.PI
}
function rad(angle) {
return angle*Math.PI/180
}
function convert(ra, dec, lmst,lat) {
hangle=rad((lmst-ra)*15)
sinalt=Math.sin(dec)*Math.sin(lat)+Math.cos(dec)*Math.cos(hangle)*Math.cos(lat)
alt=Math.asin(sinalt)
sinaz=-Math.cos(dec)*Math.sin(hangle)/Math.cos(alt)
cosaz=Math.sin(dec)*Math.cos(lat)-Math.cos(dec)*Math.cos(hangle)*Math.sin(lat)
if(cosaz <= 0.0) {
az=Math.PI-Math.asin(sinaz)
} else {
if(sinaz <= 0.0) {
az=2*Math.PI+Math.asin(sinaz)
} else {
az=Math.asin(sinaz)
}
}
var data = Object();
data.azimuth = deg(az);
data.elevation = deg(alt);
return data;
}
function isMergeableObject(val) {
var nonNullObject = val && typeof val == 'object'
return nonNullObject
&& Object.prototype.toString.call(val) !== '[object RegExp]'
&& Object.prototype.toString.call(val) !== '[object Date]'
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {}
}
function cloneIfNecessary(value, optionsArgument) {
var clone = optionsArgument && optionsArgument.clone == true
return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value
}
function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice()
source.forEach(function(e, i) {
if (typeof destination[i] == 'undefined') {
destination[i] = cloneIfNecessary(e, optionsArgument)
} else if (isMergeableObject(e)) {
destination[i] = deepmerge(target[i], e, optionsArgument)
} else if (target.indexOf(e) == -1) {
destination.push(cloneIfNecessary(e, optionsArgument))
}
})
return destination
}
function mergeObject(target, source, optionsArgument) {
var destination = {}
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = cloneIfNecessary(target[key], optionsArgument)
})
}
Object.keys(source).forEach(function (key) {
if (!isMergeableObject(source[key]) || !target[key]) {
destination[key] = cloneIfNecessary(source[key], optionsArgument)
} else {
destination[key] = deepmerge(target[key], source[key], optionsArgument)
}
})
return destination
}
function deepmerge(target, source, optionsArgument) {
var array = Array.isArray(source);
var options = optionsArgument || { arrayMerge: defaultArrayMerge }
var arrayMerge = options.arrayMerge || defaultArrayMerge
if (array) {
return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument)
} else {
return mergeObject(target, source, optionsArgument)
}
}
deepmerge.all = function deepmergeAll(array, optionsArgument) {
if (!Array.isArray(array) || array.length < 2) {
throw new Error('first argument should be an array with at least two elements')
}
// we are sure there are at least 2 values, so it is safe to have no initial value
return array.reduce(function(prev, next) {
return deepmerge(prev, next, optionsArgument)
})
}
// https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
function pickTextColorBasedOnBgColorAdvanced(bgColor, lightColor, darkColor) {
var color = (bgColor.charAt(0) == '#') ? bgColor.substring(1, 7) : bgColor;
var r = parseInt(color.substring(0, 2), 16); // hexToR
var g = parseInt(color.substring(2, 4), 16); // hexToG
var b = parseInt(color.substring(4, 6), 16); // hexToB
var uicolors = [r / 255, g / 255, b / 255];
var c = uicolors.map((col) => {
if (col <= 0.03928) {
return col / 12.92;
}
return Math.pow((col + 0.055) / 1.055, 2.4);
});
var L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
return (L > 0.179) ? darkColor : lightColor;
}