2021-02-27 00:00:38 +00:00
|
|
|
$(function(){
|
2019-07-06 13:04:39 +00:00
|
|
|
var query = window.location.search.replace(/^\?/, '').split('&').map(function(v){
|
|
|
|
var s = v.split('=');
|
2019-07-09 15:32:49 +00:00
|
|
|
var r = {};
|
|
|
|
r[s[0]] = s.slice(1).join('=');
|
2019-07-06 13:04:39 +00:00
|
|
|
return r;
|
|
|
|
}).reduce(function(a, b){
|
|
|
|
return a.assign(b);
|
|
|
|
});
|
|
|
|
|
|
|
|
var expectedCallsign;
|
2021-01-17 17:50:55 +00:00
|
|
|
if (query.callsign) expectedCallsign = decodeURIComponent(query.callsign);
|
2019-07-07 20:36:34 +00:00
|
|
|
var expectedLocator;
|
|
|
|
if (query.locator) expectedLocator = query.locator;
|
2019-07-06 13:04:39 +00:00
|
|
|
|
2019-12-03 17:57:32 +00:00
|
|
|
var protocol = window.location.protocol.match(/https/) ? 'wss' : 'ws';
|
2019-12-03 17:53:57 +00:00
|
|
|
|
|
|
|
var href = window.location.href;
|
|
|
|
var index = href.lastIndexOf('/');
|
|
|
|
if (index > 0) {
|
|
|
|
href = href.substr(0, index + 1);
|
|
|
|
}
|
|
|
|
href = href.split("://")[1];
|
|
|
|
href = protocol + "://" + href;
|
|
|
|
if (!href.endsWith('/')) {
|
|
|
|
href += '/';
|
|
|
|
}
|
|
|
|
var ws_url = href + "ws/";
|
2019-07-01 14:49:39 +00:00
|
|
|
|
2019-07-01 19:20:53 +00:00
|
|
|
var map;
|
|
|
|
var markers = {};
|
2019-07-06 20:43:36 +00:00
|
|
|
var rectangles = {};
|
2020-12-11 16:47:17 +00:00
|
|
|
var receiverMarker;
|
2019-07-01 19:20:53 +00:00
|
|
|
var updateQueue = [];
|
|
|
|
|
2019-07-07 18:46:12 +00:00
|
|
|
// reasonable default; will be overriden by server
|
|
|
|
var retention_time = 2 * 60 * 60 * 1000;
|
2019-07-09 15:32:49 +00:00
|
|
|
var strokeOpacity = 0.8;
|
|
|
|
var fillOpacity = 0.35;
|
2022-11-19 19:34:47 +00:00
|
|
|
var callsign_url = null;
|
2019-07-07 18:46:12 +00:00
|
|
|
|
2019-07-28 13:28:39 +00:00
|
|
|
var colorKeys = {};
|
2019-07-28 20:13:55 +00:00
|
|
|
var colorScale = chroma.scale(['red', 'blue', 'green']).mode('hsl');
|
2019-07-28 13:28:39 +00:00
|
|
|
var getColor = function(id){
|
|
|
|
if (!id) return "#000000";
|
|
|
|
if (!colorKeys[id]) {
|
|
|
|
var keys = Object.keys(colorKeys);
|
|
|
|
keys.push(id);
|
2020-12-10 21:22:08 +00:00
|
|
|
keys.sort(function(a, b) {
|
|
|
|
var pa = parseFloat(a);
|
|
|
|
var pb = parseFloat(b);
|
|
|
|
if (isNaN(pa) || isNaN(pb)) return a.localeCompare(b);
|
|
|
|
return pa - pb;
|
|
|
|
});
|
2019-07-28 14:26:03 +00:00
|
|
|
var colors = colorScale.colors(keys.length);
|
2019-07-28 13:28:39 +00:00
|
|
|
colorKeys = {};
|
|
|
|
keys.forEach(function(key, index) {
|
|
|
|
colorKeys[key] = colors[index];
|
|
|
|
});
|
|
|
|
reColor();
|
2019-07-28 13:57:33 +00:00
|
|
|
updateLegend();
|
2019-07-28 13:28:39 +00:00
|
|
|
}
|
|
|
|
return colorKeys[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
// when the color palette changes, update all grid squares with new color
|
|
|
|
var reColor = function() {
|
|
|
|
$.each(rectangles, function(_, r) {
|
2019-07-28 14:17:23 +00:00
|
|
|
var color = getColor(colorAccessor(r));
|
2019-07-28 13:28:39 +00:00
|
|
|
r.setOptions({
|
|
|
|
strokeColor: color,
|
|
|
|
fillColor: color
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-07-28 14:17:23 +00:00
|
|
|
var colorMode = 'byband';
|
|
|
|
var colorAccessor = function(r) {
|
|
|
|
switch (colorMode) {
|
|
|
|
case 'byband':
|
|
|
|
return r.band;
|
|
|
|
case 'bymode':
|
|
|
|
return r.mode;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$(function(){
|
|
|
|
$('#openwebrx-map-colormode').on('change', function(){
|
|
|
|
colorMode = $(this).val();
|
|
|
|
colorKeys = {};
|
2021-01-19 23:39:34 +00:00
|
|
|
filterRectangles(allRectangles);
|
2019-07-28 14:17:23 +00:00
|
|
|
reColor();
|
|
|
|
updateLegend();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-07-28 13:57:33 +00:00
|
|
|
var updateLegend = function() {
|
|
|
|
var lis = $.map(colorKeys, function(value, key) {
|
2021-01-19 23:39:34 +00:00
|
|
|
// fake rectangle to test if the filter would match
|
|
|
|
var fakeRectangle = Object.fromEntries([[colorMode.slice(2), key]]);
|
|
|
|
var disabled = rectangleFilter(fakeRectangle) ? '' : ' disabled';
|
|
|
|
return '<li class="square' + disabled + '" data-selector="' + key + '"><span class="illustration" style="background-color:' + chroma(value).alpha(fillOpacity) + ';border-color:' + chroma(value).alpha(strokeOpacity) + ';"></span>' + key + '</li>';
|
2019-07-28 13:57:33 +00:00
|
|
|
});
|
|
|
|
$(".openwebrx-map-legend .content").html('<ul>' + lis.join('') + '</ul>');
|
|
|
|
}
|
|
|
|
|
2019-07-01 19:20:53 +00:00
|
|
|
var processUpdates = function(updates) {
|
2019-09-21 11:41:04 +00:00
|
|
|
if (typeof(AprsMarker) == 'undefined') {
|
2019-07-01 19:20:53 +00:00
|
|
|
updateQueue = updateQueue.concat(updates);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
updates.forEach(function(update){
|
2019-07-06 13:04:39 +00:00
|
|
|
|
2019-07-06 20:43:36 +00:00
|
|
|
switch (update.location.type) {
|
|
|
|
case 'latlon':
|
2019-07-09 15:32:49 +00:00
|
|
|
var pos = new google.maps.LatLng(update.location.lat, update.location.lon);
|
2019-07-07 18:46:12 +00:00
|
|
|
var marker;
|
2019-09-18 23:35:58 +00:00
|
|
|
var markerClass = google.maps.Marker;
|
2019-09-19 00:25:32 +00:00
|
|
|
var aprsOptions = {}
|
2019-09-18 23:35:58 +00:00
|
|
|
if (update.location.symbol) {
|
|
|
|
markerClass = AprsMarker;
|
2019-09-19 00:25:32 +00:00
|
|
|
aprsOptions.symbol = update.location.symbol;
|
|
|
|
aprsOptions.course = update.location.course;
|
|
|
|
aprsOptions.speed = update.location.speed;
|
2019-09-18 23:35:58 +00:00
|
|
|
}
|
2019-07-06 20:43:36 +00:00
|
|
|
if (markers[update.callsign]) {
|
2019-07-07 18:46:12 +00:00
|
|
|
marker = markers[update.callsign];
|
2019-07-06 20:43:36 +00:00
|
|
|
} else {
|
2019-09-18 23:35:58 +00:00
|
|
|
marker = new markerClass();
|
2019-07-11 19:21:01 +00:00
|
|
|
marker.addListener('click', function(){
|
|
|
|
showMarkerInfoWindow(update.callsign, pos);
|
|
|
|
});
|
2019-07-07 18:46:12 +00:00
|
|
|
markers[update.callsign] = marker;
|
2019-07-06 20:43:36 +00:00
|
|
|
}
|
2019-07-07 18:46:12 +00:00
|
|
|
marker.setOptions($.extend({
|
|
|
|
position: pos,
|
|
|
|
map: map,
|
|
|
|
title: update.callsign
|
2019-09-19 00:25:32 +00:00
|
|
|
}, aprsOptions, getMarkerOpacityOptions(update.lastseen) ));
|
2019-07-07 18:46:12 +00:00
|
|
|
marker.lastseen = update.lastseen;
|
2022-11-30 02:26:51 +00:00
|
|
|
marker.mode = update.mode;
|
|
|
|
marker.band = update.band;
|
|
|
|
marker.comment = update.location.comment;
|
|
|
|
marker.weather = update.location.weather;
|
|
|
|
marker.altitude = update.location.altitude;
|
2022-12-06 03:59:09 +00:00
|
|
|
marker.height = update.location.height;
|
|
|
|
marker.power = update.location.power;
|
|
|
|
marker.gain = update.location.gain;
|
2022-11-30 02:26:51 +00:00
|
|
|
marker.device = update.location.device;
|
2022-12-06 03:59:09 +00:00
|
|
|
marker.directivity = update.location.directivity;
|
2019-07-06 20:43:36 +00:00
|
|
|
|
2021-01-17 18:21:13 +00:00
|
|
|
if (expectedCallsign && expectedCallsign == update.callsign) {
|
2019-07-06 20:43:36 +00:00
|
|
|
map.panTo(pos);
|
2019-07-11 19:21:01 +00:00
|
|
|
showMarkerInfoWindow(update.callsign, pos);
|
2020-05-08 23:18:51 +00:00
|
|
|
expectedCallsign = false;
|
|
|
|
}
|
|
|
|
|
2021-01-17 18:21:13 +00:00
|
|
|
if (infowindow && infowindow.callsign && infowindow.callsign == update.callsign) {
|
2020-05-08 23:18:51 +00:00
|
|
|
showMarkerInfoWindow(infowindow.callsign, pos);
|
2019-07-06 20:43:36 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'locator':
|
|
|
|
var loc = update.location.locator;
|
|
|
|
var lat = (loc.charCodeAt(1) - 65 - 9) * 10 + Number(loc[3]);
|
2019-07-06 21:15:33 +00:00
|
|
|
var lon = (loc.charCodeAt(0) - 65 - 9) * 20 + Number(loc[2]) * 2;
|
2019-07-07 20:36:34 +00:00
|
|
|
var center = new google.maps.LatLng({lat: lat + .5, lng: lon + 1});
|
2019-07-06 20:43:36 +00:00
|
|
|
var rectangle;
|
2019-07-28 14:17:23 +00:00
|
|
|
// the accessor is designed to work on the rectangle... but it should work on the update object, too
|
|
|
|
var color = getColor(colorAccessor(update));
|
2019-07-06 20:43:36 +00:00
|
|
|
if (rectangles[update.callsign]) {
|
|
|
|
rectangle = rectangles[update.callsign];
|
|
|
|
} else {
|
|
|
|
rectangle = new google.maps.Rectangle();
|
2019-07-07 20:36:34 +00:00
|
|
|
rectangle.addListener('click', function(){
|
2019-07-28 14:36:12 +00:00
|
|
|
showLocatorInfoWindow(this.locator, this.center);
|
2019-07-07 20:36:34 +00:00
|
|
|
});
|
2019-07-06 20:43:36 +00:00
|
|
|
rectangles[update.callsign] = rectangle;
|
|
|
|
}
|
2021-01-19 23:39:34 +00:00
|
|
|
rectangle.lastseen = update.lastseen;
|
|
|
|
rectangle.locator = update.location.locator;
|
|
|
|
rectangle.mode = update.mode;
|
|
|
|
rectangle.band = update.band;
|
|
|
|
rectangle.center = center;
|
|
|
|
|
2019-07-07 18:46:12 +00:00
|
|
|
rectangle.setOptions($.extend({
|
2019-07-28 13:28:39 +00:00
|
|
|
strokeColor: color,
|
2019-07-06 20:43:36 +00:00
|
|
|
strokeWeight: 2,
|
2019-07-28 13:28:39 +00:00
|
|
|
fillColor: color,
|
2021-01-19 23:39:34 +00:00
|
|
|
map: rectangleFilter(rectangle) ? map : undefined,
|
2019-07-06 20:43:36 +00:00
|
|
|
bounds:{
|
|
|
|
north: lat,
|
|
|
|
south: lat + 1,
|
|
|
|
west: lon,
|
2019-07-06 21:15:33 +00:00
|
|
|
east: lon + 2
|
2019-07-06 20:43:36 +00:00
|
|
|
}
|
2019-07-07 18:46:12 +00:00
|
|
|
}, getRectangleOpacityOptions(update.lastseen) ));
|
2019-07-07 20:36:34 +00:00
|
|
|
|
|
|
|
if (expectedLocator && expectedLocator == update.location.locator) {
|
|
|
|
map.panTo(center);
|
2019-07-11 19:21:01 +00:00
|
|
|
showLocatorInfoWindow(expectedLocator, center);
|
2020-05-08 23:18:51 +00:00
|
|
|
expectedLocator = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (infowindow && infowindow.locator && infowindow.locator == update.location.locator) {
|
|
|
|
showLocatorInfoWindow(infowindow.locator, center);
|
2019-07-07 20:36:34 +00:00
|
|
|
}
|
2019-07-06 20:43:36 +00:00
|
|
|
break;
|
2019-07-06 13:04:39 +00:00
|
|
|
}
|
2019-07-01 19:20:53 +00:00
|
|
|
});
|
2019-07-09 15:32:49 +00:00
|
|
|
};
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-07-10 21:13:03 +00:00
|
|
|
var clearMap = function(){
|
|
|
|
var reset = function(callsign, item) { item.setMap(); };
|
|
|
|
$.each(markers, reset);
|
|
|
|
$.each(rectangles, reset);
|
2020-12-11 16:47:17 +00:00
|
|
|
receiverMarker.setMap();
|
2019-07-10 21:13:03 +00:00
|
|
|
markers = {};
|
|
|
|
rectangles = {};
|
|
|
|
};
|
|
|
|
|
2019-07-13 19:44:48 +00:00
|
|
|
var reconnect_timeout = false;
|
|
|
|
|
2021-02-27 00:00:38 +00:00
|
|
|
var config = {}
|
|
|
|
|
2019-07-10 21:13:03 +00:00
|
|
|
var connect = function(){
|
|
|
|
var ws = new WebSocket(ws_url);
|
|
|
|
ws.onopen = function(){
|
|
|
|
ws.send("SERVER DE CLIENT client=map.js type=map");
|
2019-07-13 19:44:48 +00:00
|
|
|
reconnect_timeout = false
|
2019-07-10 21:13:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
ws.onmessage = function(e){
|
|
|
|
if (typeof e.data != 'string') {
|
|
|
|
console.error("unsupported binary data on websocket; ignoring");
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (e.data.substr(0, 16) == "CLIENT DE SERVER") {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
var json = JSON.parse(e.data);
|
|
|
|
switch (json.type) {
|
|
|
|
case "config":
|
2021-02-27 00:00:38 +00:00
|
|
|
Object.assign(config, json.value);
|
|
|
|
if ('receiver_gps' in config) {
|
|
|
|
var receiverPos = {
|
|
|
|
lat: config.receiver_gps.lat,
|
|
|
|
lng: config.receiver_gps.lon
|
|
|
|
};
|
|
|
|
if (!map) $.getScript("https://maps.googleapis.com/maps/api/js?key=" + config.google_maps_api_key).done(function(){
|
|
|
|
map = new google.maps.Map($('.openwebrx-map')[0], {
|
|
|
|
center: receiverPos,
|
|
|
|
zoom: 5,
|
|
|
|
});
|
2021-01-19 23:39:34 +00:00
|
|
|
|
2021-02-27 00:00:38 +00:00
|
|
|
$.getScript("static/lib/nite-overlay.js").done(function(){
|
|
|
|
nite.init(map);
|
|
|
|
setInterval(function() { nite.refresh() }, 10000); // every 10s
|
|
|
|
});
|
|
|
|
$.getScript('static/lib/AprsMarker.js').done(function(){
|
|
|
|
processUpdates(updateQueue);
|
|
|
|
updateQueue = [];
|
|
|
|
});
|
2020-12-11 16:47:17 +00:00
|
|
|
|
2021-02-27 00:00:38 +00:00
|
|
|
var $legend = $(".openwebrx-map-legend");
|
|
|
|
setupLegendFilters($legend);
|
|
|
|
map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push($legend[0]);
|
|
|
|
|
|
|
|
if (!receiverMarker) {
|
|
|
|
receiverMarker = new google.maps.Marker();
|
|
|
|
receiverMarker.addListener('click', function() {
|
|
|
|
showReceiverInfoWindow(receiverMarker);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
receiverMarker.setOptions({
|
|
|
|
map: map,
|
|
|
|
position: receiverPos,
|
|
|
|
title: config['receiver_name'],
|
|
|
|
config: config
|
|
|
|
});
|
|
|
|
}); else {
|
|
|
|
receiverMarker.setOptions({
|
|
|
|
map: map,
|
|
|
|
position: receiverPos,
|
|
|
|
config: config
|
2020-12-11 16:47:17 +00:00
|
|
|
});
|
|
|
|
}
|
2021-02-27 00:00:38 +00:00
|
|
|
}
|
|
|
|
if ('receiver_name' in config && receiverMarker) {
|
2020-12-11 16:47:17 +00:00
|
|
|
receiverMarker.setOptions({
|
2021-02-27 00:00:38 +00:00
|
|
|
title: config['receiver_name']
|
2020-12-11 16:47:17 +00:00
|
|
|
});
|
|
|
|
}
|
2021-02-27 00:00:38 +00:00
|
|
|
if ('map_position_retention_time' in config) {
|
|
|
|
retention_time = config.map_position_retention_time * 1000;
|
|
|
|
}
|
2022-11-19 19:34:47 +00:00
|
|
|
if ('callsign_url' in config) {
|
|
|
|
callsign_url = config['callsign_url'];
|
|
|
|
}
|
2019-07-10 21:13:03 +00:00
|
|
|
break;
|
|
|
|
case "update":
|
|
|
|
processUpdates(json.value);
|
|
|
|
break;
|
2020-05-08 22:20:38 +00:00
|
|
|
case 'receiver_details':
|
2021-02-05 16:56:02 +00:00
|
|
|
$('.webrx-top-container').header().setDetails(json['value']);
|
2020-05-08 22:20:38 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.warn('received message of unknown type: ' + json['type']);
|
2019-07-10 21:13:03 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
// don't lose exception
|
|
|
|
console.error(e);
|
2019-07-01 17:49:58 +00:00
|
|
|
}
|
2019-07-10 21:13:03 +00:00
|
|
|
};
|
|
|
|
ws.onclose = function(){
|
|
|
|
clearMap();
|
2019-07-13 19:44:48 +00:00
|
|
|
if (reconnect_timeout) {
|
|
|
|
// max value: roundabout 8 and a half minutes
|
|
|
|
reconnect_timeout = Math.min(reconnect_timeout * 2, 512000);
|
|
|
|
} else {
|
|
|
|
// initial value: 1s
|
|
|
|
reconnect_timeout = 1000;
|
|
|
|
}
|
|
|
|
setTimeout(connect, reconnect_timeout);
|
2019-07-10 21:13:03 +00:00
|
|
|
};
|
2019-07-01 14:49:39 +00:00
|
|
|
|
2019-07-10 21:13:03 +00:00
|
|
|
window.onbeforeunload = function() { //http://stackoverflow.com/questions/4812686/closing-websocket-correctly-html5-javascript
|
|
|
|
ws.onclose = function () {};
|
|
|
|
ws.close();
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
ws.onerror = function(){
|
|
|
|
console.info("websocket error");
|
|
|
|
};
|
|
|
|
*/
|
2019-07-01 14:49:39 +00:00
|
|
|
};
|
|
|
|
|
2019-07-10 21:13:03 +00:00
|
|
|
connect();
|
|
|
|
|
2020-05-08 23:18:51 +00:00
|
|
|
var getInfoWindow = function() {
|
|
|
|
if (!infowindow) {
|
|
|
|
infowindow = new google.maps.InfoWindow();
|
|
|
|
google.maps.event.addListener(infowindow, 'closeclick', function() {
|
|
|
|
delete infowindow.locator;
|
|
|
|
delete infowindow.callsign;
|
|
|
|
});
|
|
|
|
}
|
2021-01-17 18:21:13 +00:00
|
|
|
delete infowindow.locator;
|
|
|
|
delete infowindow.callsign;
|
2020-05-08 23:18:51 +00:00
|
|
|
return infowindow;
|
|
|
|
}
|
|
|
|
|
2022-11-19 19:34:47 +00:00
|
|
|
var linkifyCallsign = function(callsign) {
|
|
|
|
if ((callsign_url == null) || (callsign_url == ''))
|
|
|
|
return callsign;
|
|
|
|
else
|
|
|
|
return '<a target="callsign_info" href="' +
|
|
|
|
callsign_url.replaceAll('{}', callsign.replace(new RegExp('-.*$'), '')) +
|
|
|
|
'">' + callsign + '</a>';
|
|
|
|
};
|
|
|
|
|
|
|
|
var distanceKm = function(p1, p2) {
|
|
|
|
// Earth radius in km
|
|
|
|
var R = 6371.0;
|
|
|
|
// Convert degrees to radians
|
|
|
|
var rlat1 = p1.lat() * (Math.PI/180);
|
|
|
|
var rlat2 = p2.lat() * (Math.PI/180);
|
|
|
|
// Compute difference in radians
|
|
|
|
var difflat = rlat2-rlat1;
|
|
|
|
var difflon = (p2.lng()-p1.lng()) * (Math.PI/180);
|
|
|
|
// Compute distance
|
|
|
|
d = 2 * R * Math.asin(Math.sqrt(
|
|
|
|
Math.sin(difflat/2) * Math.sin(difflat/2) +
|
|
|
|
Math.cos(rlat1) * Math.cos(rlat2) * Math.sin(difflon/2) * Math.sin(difflon/2)
|
|
|
|
));
|
|
|
|
return Math.round(d);
|
|
|
|
}
|
|
|
|
|
2019-07-07 19:24:56 +00:00
|
|
|
var infowindow;
|
2019-07-11 19:21:01 +00:00
|
|
|
var showLocatorInfoWindow = function(locator, pos) {
|
2020-05-08 23:18:51 +00:00
|
|
|
var infowindow = getInfoWindow();
|
|
|
|
infowindow.locator = locator;
|
2019-07-07 20:36:34 +00:00
|
|
|
var inLocator = $.map(rectangles, function(r, callsign) {
|
2019-07-14 17:32:48 +00:00
|
|
|
return {callsign: callsign, locator: r.locator, lastseen: r.lastseen, mode: r.mode, band: r.band}
|
2021-01-19 23:39:34 +00:00
|
|
|
}).filter(rectangleFilter).filter(function(d) {
|
2019-07-07 20:36:34 +00:00
|
|
|
return d.locator == locator;
|
2019-07-11 18:53:59 +00:00
|
|
|
}).sort(function(a, b){
|
|
|
|
return b.lastseen - a.lastseen;
|
2019-07-07 20:36:34 +00:00
|
|
|
});
|
2022-11-19 19:34:47 +00:00
|
|
|
var distance = receiverMarker?
|
|
|
|
" at " + distanceKm(receiverMarker.position, pos) + " km" : "";
|
2019-07-07 20:36:34 +00:00
|
|
|
infowindow.setContent(
|
2022-11-19 19:34:47 +00:00
|
|
|
'<h3>Locator: ' + locator + distance + '</h3>' +
|
2019-07-07 20:36:34 +00:00
|
|
|
'<div>Active Callsigns:</div>' +
|
|
|
|
'<ul>' +
|
2019-07-08 19:01:30 +00:00
|
|
|
inLocator.map(function(i){
|
|
|
|
var timestring = moment(i.lastseen).fromNow();
|
2022-11-19 19:34:47 +00:00
|
|
|
var message = linkifyCallsign(i.callsign) + ' (' + timestring + ' using ' + i.mode;
|
2019-07-14 17:32:48 +00:00
|
|
|
if (i.band) message += ' on ' + i.band;
|
|
|
|
message += ')';
|
|
|
|
return '<li>' + message + '</li>'
|
2019-07-08 19:01:30 +00:00
|
|
|
}).join("") +
|
2019-07-07 20:36:34 +00:00
|
|
|
'</ul>'
|
|
|
|
);
|
|
|
|
infowindow.setPosition(pos);
|
|
|
|
infowindow.open(map);
|
2019-07-09 15:32:49 +00:00
|
|
|
};
|
2019-07-07 19:24:56 +00:00
|
|
|
|
2022-11-30 01:24:14 +00:00
|
|
|
var degToCompass = function(deg) {
|
|
|
|
dir = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"];
|
|
|
|
return dir[Math.floor((deg/22.5) + 0.5) % 16];
|
|
|
|
}
|
|
|
|
|
|
|
|
var makeListTitle = function(name) {
|
|
|
|
return '<div style="border-bottom:2px solid;"><b>' + name + '</b></div>';
|
|
|
|
}
|
|
|
|
|
|
|
|
var makeListItem = function(name, value) {
|
|
|
|
return '<div style="border-bottom:1px dotted;">'
|
|
|
|
+ '<span>' + name + '</span>'
|
|
|
|
+ '<span style="float:right;">' + value + '</span>'
|
|
|
|
+ '</div>';
|
|
|
|
}
|
|
|
|
|
2019-07-11 19:21:01 +00:00
|
|
|
var showMarkerInfoWindow = function(callsign, pos) {
|
2020-05-08 23:18:51 +00:00
|
|
|
var infowindow = getInfoWindow();
|
|
|
|
infowindow.callsign = callsign;
|
2019-07-11 19:21:01 +00:00
|
|
|
var marker = markers[callsign];
|
|
|
|
var timestring = moment(marker.lastseen).fromNow();
|
2019-08-11 16:42:41 +00:00
|
|
|
var commentString = "";
|
2022-11-30 01:24:14 +00:00
|
|
|
var weatherString = "";
|
2022-11-30 02:26:51 +00:00
|
|
|
var detailsString = "";
|
2022-11-19 19:34:47 +00:00
|
|
|
var distance = "";
|
2022-11-30 01:24:14 +00:00
|
|
|
|
2019-08-11 16:42:41 +00:00
|
|
|
if (marker.comment) {
|
2022-11-30 01:24:14 +00:00
|
|
|
commentString += '<p>' + makeListTitle('Comment') + '<div>' +
|
|
|
|
marker.comment + '</div></p>';
|
2019-08-11 16:42:41 +00:00
|
|
|
}
|
2022-11-30 01:24:14 +00:00
|
|
|
|
|
|
|
if (marker.weather) {
|
|
|
|
weatherString += '<p>' + makeListTitle('Weather');
|
|
|
|
|
|
|
|
if (marker.weather.temperature) {
|
|
|
|
weatherString += makeListItem('Temperature', marker.weather.temperature.toFixed(2) + ' oC');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.weather.humidity) {
|
|
|
|
weatherString += makeListItem('Humidity', marker.weather.humidity + '%');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.weather.barometricpressure) {
|
|
|
|
weatherString += makeListItem('Pressure', marker.weather.barometricpressure.toFixed(1) + ' mbar');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.weather.wind && (marker.weather.wind.speed>0)) {
|
|
|
|
weatherString += makeListItem('Wind',
|
|
|
|
degToCompass(marker.weather.wind.direction) + ' ' +
|
|
|
|
marker.weather.wind.speed.toFixed(1) + ' km/h '
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-30 02:26:51 +00:00
|
|
|
if (marker.weather.wind.gust && (marker.weather.wind.gust>0)) {
|
|
|
|
weatherString += makeListItem('Gusts', marker.weather.wind.gust.toFixed(1) + ' km/h');
|
2022-11-30 01:24:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.weather.rain && (marker.weather.rain.day>0)) {
|
|
|
|
weatherString += makeListItem('Rain',
|
2022-12-06 03:59:09 +00:00
|
|
|
marker.weather.rain.hour.toFixed(0) + ' mm/h, ' +
|
|
|
|
marker.weather.rain.day.toFixed(0) + ' mm/day'
|
2022-11-30 01:24:14 +00:00
|
|
|
// marker.weather.rain.sincemidnight + ' mm since midnight'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.weather.snowfall) {
|
2022-12-06 03:59:09 +00:00
|
|
|
weatherString += makeListItem('Snow', marker.weather.snowfall.toFixed(1) + ' cm');
|
2022-11-30 01:24:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
weatherString += '</p>';
|
|
|
|
}
|
|
|
|
|
2022-12-06 03:59:09 +00:00
|
|
|
if (marker.altitude || marker.device || marker.power || marker.height || marker.gain || marker.directivity) {
|
2022-11-30 02:26:51 +00:00
|
|
|
detailsString += '<p>' + makeListTitle('Details');
|
|
|
|
|
|
|
|
if (marker.altitude) {
|
2022-12-06 03:59:09 +00:00
|
|
|
detailsString += makeListItem('Altitude', marker.altitude.toFixed(0) + ' m');
|
2022-11-30 02:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.device) {
|
|
|
|
detailsString += makeListItem('Device',
|
|
|
|
marker.device.device + " by " +
|
|
|
|
marker.device.manufacturer
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-06 03:59:09 +00:00
|
|
|
if (marker.height) {
|
|
|
|
detailsString += makeListItem('Height', marker.height.toFixed(0) + ' m');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.power) {
|
2022-12-06 04:05:09 +00:00
|
|
|
detailsString += makeListItem('Power', marker.power + " W");
|
2022-12-06 03:59:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.gain) {
|
2022-12-06 04:05:09 +00:00
|
|
|
detailsString += makeListItem('Gain', marker.gain + " dB");
|
2022-12-06 03:59:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (marker.directivity) {
|
|
|
|
detailsString += makeListItem('Direction', marker.directivity);
|
|
|
|
}
|
|
|
|
|
2022-11-30 02:26:51 +00:00
|
|
|
detailsString += '</p>';
|
|
|
|
}
|
|
|
|
|
2022-11-19 19:34:47 +00:00
|
|
|
if (receiverMarker) {
|
|
|
|
distance = " at " + distanceKm(receiverMarker.position, marker.position) + " km";
|
|
|
|
}
|
2022-11-30 01:24:14 +00:00
|
|
|
|
2019-07-11 19:21:01 +00:00
|
|
|
infowindow.setContent(
|
2022-11-19 19:34:47 +00:00
|
|
|
'<h3>' + linkifyCallsign(callsign) + distance + '</h3>' +
|
2022-11-30 01:24:14 +00:00
|
|
|
'<div align="center">' + timestring + ' using ' + marker.mode + ( marker.band ? ' on ' + marker.band : '' ) + '</div>' +
|
2022-11-30 02:26:51 +00:00
|
|
|
commentString + weatherString + detailsString
|
2019-07-11 19:21:01 +00:00
|
|
|
);
|
2022-11-30 01:24:14 +00:00
|
|
|
|
2019-07-11 19:21:01 +00:00
|
|
|
infowindow.open(map, marker);
|
|
|
|
}
|
|
|
|
|
2020-12-11 16:47:17 +00:00
|
|
|
var showReceiverInfoWindow = function(marker) {
|
|
|
|
var infowindow = getInfoWindow()
|
|
|
|
infowindow.setContent(
|
|
|
|
'<h3>' + marker.config['receiver_name'] + '</h3>' +
|
|
|
|
'<div>Receiver location</div>'
|
|
|
|
);
|
|
|
|
infowindow.open(map, marker);
|
|
|
|
}
|
|
|
|
|
2019-07-07 18:46:12 +00:00
|
|
|
var getScale = function(lastseen) {
|
|
|
|
var age = new Date().getTime() - lastseen;
|
|
|
|
var scale = 1;
|
|
|
|
if (age >= retention_time / 2) {
|
|
|
|
scale = (retention_time - age) / (retention_time / 2);
|
|
|
|
}
|
|
|
|
return Math.max(0, Math.min(1, scale));
|
2019-07-09 15:32:49 +00:00
|
|
|
};
|
2019-07-07 18:46:12 +00:00
|
|
|
|
|
|
|
var getRectangleOpacityOptions = function(lastseen) {
|
|
|
|
var scale = getScale(lastseen);
|
|
|
|
return {
|
|
|
|
strokeOpacity: strokeOpacity * scale,
|
|
|
|
fillOpacity: fillOpacity * scale
|
|
|
|
};
|
2019-07-09 15:32:49 +00:00
|
|
|
};
|
2019-07-07 18:46:12 +00:00
|
|
|
|
|
|
|
var getMarkerOpacityOptions = function(lastseen) {
|
|
|
|
var scale = getScale(lastseen);
|
|
|
|
return {
|
|
|
|
opacity: scale
|
|
|
|
};
|
2019-07-09 15:32:49 +00:00
|
|
|
};
|
2019-07-07 18:46:12 +00:00
|
|
|
|
|
|
|
// fade out / remove positions after time
|
|
|
|
setInterval(function(){
|
|
|
|
var now = new Date().getTime();
|
|
|
|
$.each(rectangles, function(callsign, m) {
|
|
|
|
var age = now - m.lastseen;
|
|
|
|
if (age > retention_time) {
|
|
|
|
delete rectangles[callsign];
|
|
|
|
m.setMap();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m.setOptions(getRectangleOpacityOptions(m.lastseen));
|
|
|
|
});
|
|
|
|
$.each(markers, function(callsign, m) {
|
|
|
|
var age = now - m.lastseen;
|
|
|
|
if (age > retention_time) {
|
|
|
|
delete markers[callsign];
|
|
|
|
m.setMap();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m.setOptions(getMarkerOpacityOptions(m.lastseen));
|
|
|
|
});
|
|
|
|
}, 1000);
|
|
|
|
|
2021-01-19 23:39:34 +00:00
|
|
|
var rectangleFilter = allRectangles = function() { return true; };
|
|
|
|
|
|
|
|
var filterRectangles = function(filter) {
|
|
|
|
rectangleFilter = filter;
|
|
|
|
$.each(rectangles, function(_, r) {
|
|
|
|
r.setMap(rectangleFilter(r) ? map : undefined);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
var setupLegendFilters = function($legend) {
|
|
|
|
$content = $legend.find('.content');
|
|
|
|
$content.on('click', 'li', function() {
|
|
|
|
var $el = $(this);
|
|
|
|
$lis = $content.find('li');
|
|
|
|
if ($lis.hasClass('disabled') && !$el.hasClass('disabled')) {
|
|
|
|
$lis.removeClass('disabled');
|
|
|
|
filterRectangles(allRectangles);
|
|
|
|
} else {
|
|
|
|
$el.removeClass('disabled');
|
|
|
|
$lis.filter(function() {
|
|
|
|
return this != $el[0]
|
|
|
|
}).addClass('disabled');
|
|
|
|
|
|
|
|
var key = colorMode.slice(2);
|
|
|
|
var selector = $el.data('selector');
|
|
|
|
filterRectangles(function(r) {
|
|
|
|
return r[key] === selector;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-27 00:00:38 +00:00
|
|
|
});
|