gridtracker/package.nw/lib/third-party.js
Tag Loomis 5469779198 Safety commit
Better processing indicator

Async load local adif files

BIGCTY update 0105

Baby step

another step

Lint 1

Removed debug and dead code

DO9KW

DO9KW II

Lint free safety commit

big baby step

Remove uneeded timer

more speed opt

Massive speed improvements

Lint

Fixed WSPR grid colors and dimming

Auto-mute on single or no audio devices.

lint

Chinese Google Maps and OAMS spot/decode metering

Decodes are small and once a minute.

Properly save *all* settings on language change

Proper method name and code reduction

Moved trackQSO to gtCommon in prep for log event

30 minutes inactivity

WSJT-X log event not a heavy hit to stat parsing

BIGCTY 0125

version bump for testing

A bigger hit on uls lookups, but they won't get stuck as un resolved this way

Dumped ULS websql database

Save lookupssettings and fix changelog

lint fix

whitespace?

roll version, changelog fix

Fix really old bug, always set the liveCallsigns with stations info

Moved eslint disabled code to bottom of file

Removed last traces of .rect, cleaned up vector layer accessors

Removed dead code

Local ADIF loads now add to startup log list and auto load when added via dialogs
2024-02-11 13:05:44 -08:00

564 lines
17 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;
}
}
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;
}