// 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( '