openwebrxplus/htdocs/map.js

417 lines
16 KiB
JavaScript

// Marker.linkify() uses these URLs
var callsign_url = null;
var vessel_url = null;
var flight_url = null;
// reasonable default; will be overriden by server
var retention_time = 2 * 60 * 60 * 1000;
// Function to toggle legend box on/off
function toggleElement(el_name) {
var el = document.getElementById(el_name);
if (el) el.style.display = el.style.display === 'none'? 'block' : 'none';
}
$(function(){
var query = window.location.search.replace(/^\?/, '').split('&').map(function(v){
var s = v.split('=');
var r = {};
r[s[0]] = s.slice(1).join('=');
return r;
}).reduce(function(a, b){
return a.assign(b);
});
var expectedCallsign;
if (query.callsign) expectedCallsign = decodeURIComponent(query.callsign);
var expectedLocator;
if (query.locator) expectedLocator = query.locator;
var protocol = window.location.protocol.match(/https/) ? 'wss' : 'ws';
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/";
var map;
var receiverMarker;
var updateQueue = [];
// marker and locator managers
var markmanager = null;
var locmanager = null;
// clock
var clock = new Clock($("#openwebrx-clock-utc"));
$(function() {
$('#openwebrx-map-colormode').on('change', function() {
locmanager.setColorMode(map, $(this).val());
});
});
var processUpdates = function(updates) {
if (!markmanager || !locmanager) {
updateQueue = updateQueue.concat(updates);
return;
}
updates.forEach(function(update) {
switch (update.location.type) {
case 'latlon':
var pos = new google.maps.LatLng(update.location.lat, update.location.lon);
var marker = markmanager.find(update.callsign);
var markerClass = google.maps.Marker;
var aprsOptions = {}
if (update.location.symbol) {
markerClass = GAprsMarker;
aprsOptions.symbol = update.location.symbol;
aprsOptions.course = update.location.course;
aprsOptions.speed = update.location.speed;
}
// If new item, create a new marker for it
if (!marker) {
marker = new markerClass();
markmanager.addType(update.mode);
markmanager.add(update.callsign, marker);
marker.addListener('click', function(){
showMarkerInfoWindow(update.callsign, pos);
});
}
// Apply marker options
marker.setOptions($.extend({
position: pos,
map: markmanager.isEnabled(update.mode)? map : undefined,
title: update.callsign
}, aprsOptions));
// Update marker attributes and age
marker.age(new Date().getTime() - update.lastseen);
marker.update(update);
if (expectedCallsign && expectedCallsign == update.callsign) {
map.panTo(pos);
showMarkerInfoWindow(update.callsign, pos);
expectedCallsign = false;
}
if (infowindow && infowindow.callsign && infowindow.callsign == update.callsign) {
showMarkerInfoWindow(infowindow.callsign, pos);
}
break;
case 'feature':
var pos = new google.maps.LatLng(update.location.lat, update.location.lon);
var marker = markmanager.find(update.callsign);
var options = {}
// If no symbol or color supplied, use defaults by type
if (update.location.symbol) {
options.symbol = update.location.symbol;
} else {
options.symbol = markmanager.getSymbol(update.mode);
}
if (update.location.color) {
options.color = update.location.color;
} else {
options.color = markmanager.getColor(update.mode);
}
// If new item, create a new marker for it
if (!marker) {
marker = new GFeatureMarker();
markmanager.addType(update.mode);
markmanager.add(update.callsign, marker);
marker.addListener('click', function(){
showMarkerInfoWindow(update.callsign, pos);
});
}
// Apply marker options
marker.setOptions($.extend({
position: pos,
map: markmanager.isEnabled(update.mode)? map : undefined,
title: update.callsign
}, options));
// Update marker attributes and age
marker.age(new Date().getTime() - update.lastseen);
marker.update(update);
if (expectedCallsign && expectedCallsign == update.callsign) {
map.panTo(pos);
showMarkerInfoWindow(update.callsign, pos);
expectedCallsign = false;
}
if (infowindow && infowindow.callsign && infowindow.callsign == update.callsign) {
showMarkerInfoWindow(infowindow.callsign, pos);
}
break;
case 'locator':
var rectangle = locmanager.find(update.callsign);
// If new item, create a new locator for it
if (!rectangle) {
rectangle = new GLocator();
locmanager.add(update.callsign, rectangle);
rectangle.rect.addListener('click', function() {
showLocatorInfoWindow(rectangle.locator, rectangle.center);
});
}
// Update locator attributes, center, age
rectangle.update(update);
// Assign locator to map and set its color
rectangle.setMap(locmanager.filter(rectangle)? map : undefined);
rectangle.setColor(locmanager.getColor(rectangle));
if (expectedLocator && expectedLocator == update.location.locator) {
map.panTo(rectangle.center);
showLocatorInfoWindow(expectedLocator, rectangle.center);
expectedLocator = false;
}
if (infowindow && infowindow.locator && infowindow.locator == update.location.locator) {
showLocatorInfoWindow(infowindow.locator, rectangle.center);
}
break;
}
});
};
var clearMap = function(){
var reset = function(callsign, item) { item.setMap(); };
receiverMarker.setMap();
markmanager.clear();
locmanager.clear();
};
var reconnect_timeout = false;
var config = {}
var connect = function(){
var ws = new WebSocket(ws_url);
ws.onopen = function(){
ws.send("SERVER DE CLIENT client=map.js type=map");
reconnect_timeout = false
};
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":
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,
});
$.getScript("static/lib/nite-overlay.js").done(function(){
nite.init(map);
setInterval(function() { nite.refresh() }, 10000); // every 10s
});
$.getScript('static/lib/MarkerManager.js').done(function(){
markmanager = new MarkerManager();
if (locmanager) {
processUpdates(updateQueue);
updateQueue = [];
}
});
$.getScript('static/lib/LocatorManager.js').done(function(){
locmanager = new LocatorManager();
if (markmanager) {
processUpdates(updateQueue);
updateQueue = [];
}
});
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
});
}
}
if ('receiver_name' in config && receiverMarker) {
receiverMarker.setOptions({
title: config['receiver_name']
});
}
if ('map_position_retention_time' in config) {
retention_time = config.map_position_retention_time * 1000;
}
if ('callsign_url' in config) {
callsign_url = config['callsign_url'];
}
if ('vessel_url' in config) {
vessel_url = config['vessel_url'];
}
if ('flight_url' in config) {
flight_url = config['flight_url'];
}
break;
case "update":
processUpdates(json.value);
break;
case 'receiver_details':
$('.webrx-top-container').header().setDetails(json['value']);
break;
default:
console.warn('received message of unknown type: ' + json['type']);
}
} catch (e) {
// don't lose exception
console.error(e);
}
};
ws.onclose = function(){
clearMap();
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);
};
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");
};
*/
};
connect();
var getInfoWindow = function() {
if (!infowindow) {
infowindow = new google.maps.InfoWindow();
google.maps.event.addListener(infowindow, 'closeclick', function() {
delete infowindow.locator;
delete infowindow.callsign;
});
}
delete infowindow.locator;
delete infowindow.callsign;
return infowindow;
}
var infowindow;
var showLocatorInfoWindow = function(locator, pos) {
var infowindow = getInfoWindow();
infowindow.locator = locator;
infowindow.setContent(locmanager.getInfoHTML(locator, pos, receiverMarker));
infowindow.setPosition(pos);
infowindow.open(map);
};
var showMarkerInfoWindow = function(name, pos) {
var infowindow = getInfoWindow();
var marker = markmanager.find(name);
infowindow.callsign = name;
infowindow.setContent(marker.getInfoHTML(name, receiverMarker));
infowindow.open(map, marker);
}
var showReceiverInfoWindow = function(marker) {
var infowindow = getInfoWindow()
infowindow.setContent(
'<h3>' + marker.config['receiver_name'] + '</h3>' +
'<div>Receiver location</div>'
);
infowindow.open(map, marker);
}
// Fade out / remove positions after time
setInterval(function(){
if (locmanager) locmanager.ageAll();
if (markmanager) markmanager.ageAll();
}, 1000);
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');
locmanager.setFilter(map);
} else {
$el.removeClass('disabled');
$lis.filter(function() {
return this != $el[0]
}).addClass('disabled');
locmanager.setFilter(map, $el.data('selector'));
}
});
$content1 = $legend.find('.features');
$content1.on('click', 'li', function() {
var $el = $(this);
var onoff = $el.hasClass('disabled');
if (onoff) {
$el.removeClass('disabled');
} else {
$el.addClass('disabled');
}
markmanager.toggle(map, $el.data('selector'), onoff);
});
}
});