2019-07-01 14:49:39 +00:00
|
|
|
(function(){
|
|
|
|
var protocol = 'ws';
|
|
|
|
if (window.location.toString().startsWith('https://')) {
|
|
|
|
protocol = 'wss';
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
if (query.callsign) expectedCallsign = 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-07-01 14:49:39 +00:00
|
|
|
var ws_url = protocol + "://" + (window.location.origin.split("://")[1]) + "/ws/";
|
|
|
|
if (!("WebSocket" in window)) return;
|
|
|
|
|
2019-07-01 19:20:53 +00:00
|
|
|
var map;
|
|
|
|
var markers = {};
|
2019-07-06 20:43:36 +00:00
|
|
|
var rectangles = {};
|
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;
|
2019-07-07 18:46:12 +00:00
|
|
|
|
2019-07-01 19:20:53 +00:00
|
|
|
var processUpdates = function(updates) {
|
|
|
|
if (!map) {
|
|
|
|
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-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-07-07 18:46:12 +00:00
|
|
|
marker = new google.maps.Marker();
|
|
|
|
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
|
|
|
|
}, getMarkerOpacityOptions(update.lastseen) ));
|
|
|
|
marker.lastseen = update.lastseen;
|
2019-07-06 20:43:36 +00:00
|
|
|
|
|
|
|
// TODO the trim should happen on the server side
|
|
|
|
if (expectedCallsign && expectedCallsign == update.callsign.trim()) {
|
|
|
|
map.panTo(pos);
|
|
|
|
delete(expectedCallsign);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
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(){
|
|
|
|
showInfoWindow(update.location.locator, center);
|
|
|
|
});
|
2019-07-06 20:43:36 +00:00
|
|
|
rectangles[update.callsign] = rectangle;
|
|
|
|
}
|
2019-07-07 18:46:12 +00:00
|
|
|
rectangle.setOptions($.extend({
|
2019-07-06 20:43:36 +00:00
|
|
|
strokeColor: '#FF0000',
|
|
|
|
strokeWeight: 2,
|
|
|
|
fillColor: '#FF0000',
|
|
|
|
map: map,
|
|
|
|
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) ));
|
|
|
|
rectangle.lastseen = update.lastseen;
|
2019-07-07 19:24:56 +00:00
|
|
|
rectangle.locator = update.location.locator;
|
2019-07-07 20:36:34 +00:00
|
|
|
|
|
|
|
if (expectedLocator && expectedLocator == update.location.locator) {
|
|
|
|
map.panTo(center);
|
|
|
|
showInfoWindow(expectedLocator, center);
|
|
|
|
delete(expectedLocator);
|
|
|
|
}
|
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);
|
|
|
|
markers = {};
|
|
|
|
rectangles = {};
|
|
|
|
};
|
|
|
|
|
|
|
|
var connect = function(){
|
|
|
|
var ws = new WebSocket(ws_url);
|
|
|
|
ws.onopen = function(){
|
|
|
|
ws.send("SERVER DE CLIENT client=map.js type=map");
|
|
|
|
};
|
|
|
|
|
|
|
|
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") {
|
|
|
|
console.log("Server acknowledged WebSocket connection.");
|
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
var json = JSON.parse(e.data);
|
|
|
|
switch (json.type) {
|
|
|
|
case "config":
|
|
|
|
var config = json.value;
|
|
|
|
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: {
|
|
|
|
lat: config.receiver_gps[0],
|
|
|
|
lng: config.receiver_gps[1]
|
|
|
|
},
|
|
|
|
zoom: 5
|
|
|
|
});
|
|
|
|
processUpdates(updateQueue);
|
|
|
|
updateQueue = [];
|
|
|
|
$.getScript("/static/lib/nite-overlay.js").done(function(){
|
|
|
|
nite.init(map);
|
|
|
|
setInterval(function() { nite.refresh() }, 10000); // every 10s
|
|
|
|
});
|
2019-07-06 22:52:11 +00:00
|
|
|
});
|
2019-07-10 21:13:03 +00:00
|
|
|
retention_time = config.map_position_retention_time * 1000;
|
|
|
|
break;
|
|
|
|
case "update":
|
|
|
|
processUpdates(json.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} 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();
|
|
|
|
setTimeout(connect, 5000);
|
|
|
|
};
|
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();
|
|
|
|
|
2019-07-07 19:24:56 +00:00
|
|
|
var infowindow;
|
2019-07-07 20:36:34 +00:00
|
|
|
var showInfoWindow = function(locator, pos) {
|
2019-07-07 19:24:56 +00:00
|
|
|
if (!infowindow) infowindow = new google.maps.InfoWindow();
|
2019-07-07 20:36:34 +00:00
|
|
|
var inLocator = $.map(rectangles, function(r, callsign) {
|
2019-07-08 19:01:30 +00:00
|
|
|
return {callsign: callsign, locator: r.locator, lastseen: r.lastseen}
|
2019-07-07 20:36:34 +00:00
|
|
|
}).filter(function(d) {
|
|
|
|
return d.locator == locator;
|
|
|
|
});
|
|
|
|
infowindow.setContent(
|
|
|
|
'<h3>Locator: ' + locator + '</h3>' +
|
|
|
|
'<div>Active Callsigns:</div>' +
|
|
|
|
'<ul>' +
|
2019-07-08 19:01:30 +00:00
|
|
|
inLocator.map(function(i){
|
|
|
|
var timestring = moment(i.lastseen).fromNow();
|
|
|
|
return '<li>' + i.callsign + ' (' + timestring + ')</li>'
|
|
|
|
}).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
|
|
|
|
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);
|
|
|
|
|
2019-07-01 14:49:39 +00:00
|
|
|
})();
|