openwebrx/htdocs/map-google.js
Stanislav Lechev [0xAF] 8826be467e fix GMaps implementation
2023-09-21 02:07:21 +03:00

311 lines
11 KiB
JavaScript

// Marker.linkify() uses these URLs
var callsign_url = null;
var vessel_url = null;
var flight_url = null;
var modes_url = null;
// reasonable default; will be overriden by server
var retention_time = 2 * 60 * 60 * 1000;
// Our Google Map
var map = null;
// Receiver location marker
var receiverMarker = null;
// Information bubble window
var infoWindow = null;
// Updates are queued here
var updateQueue = [];
// Web socket connection management, message processing
var mapManager = new MapManager();
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 = query.callsign? decodeURIComponent(query.callsign) : null;
var expectedLocator = query.locator? query.locator : null;
var expectedIcao = query.icao? query.icao: null;
// Get information bubble window
function getInfoWindow() {
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;
};
// Show information bubble for a locator
function showLocatorInfoWindow(locator, pos) {
var iw = getInfoWindow();
iw.locator = locator;
iw.setContent(mapManager.lman.getInfoHTML(locator, pos, receiverMarker));
iw.setPosition(pos);
iw.open(map);
};
// Show information bubble for a marker
function showMarkerInfoWindow(name, pos) {
var marker = mapManager.mman.find(name);
var iw = getInfoWindow();
iw.callsign = name;
iw.setContent(marker.getInfoHTML(name, receiverMarker));
iw.open(map, marker);
};
// Show information bubble for the receiver location
function showReceiverInfoWindow(marker) {
var iw = getInfoWindow()
iw.setContent(
'<h3>' + marker.config['receiver_name'] + '</h3>' +
'<div>Receiver Location</div>'
);
iw.open(map, marker);
};
var sourceToKey = function(source) {
// special treatment for special entities
// not just for display but also in key treatment in order not to overlap with other locations sent by the same callsign
if ('item' in source) return source['item'];
if ('object' in source) return source['object'];
if ('icao' in source) return source['icao'];
if ('flight' in source) return source['flight'];
var key = source.callsign;
if ('ssid' in source) key += '-' + source.ssid;
return key;
};
// we can reuse the same logic for displaying and indexing
var sourceToString = sourceToKey;
//
// GOOGLE-SPECIFIC MAP MANAGER METHODS
//
MapManager.prototype.setReceiverName = function(name) {
if (receiverMarker) receiverMarker.setOptions({ title: name });
}
MapManager.prototype.removeReceiver = function() {
if (receiverMarker) receiverMarker.setMap();
}
MapManager.prototype.initializeMap = function(receiver_gps, api_key, weather_key) {
var receiverPos = { lat: receiver_gps.lat, lng: receiver_gps.lon };
if (map) {
receiverMarker.setOptions({
map : map,
position : receiverPos,
config : this.config
});
} else {
var self = this;
// After Google Maps API loads...
$.getScript("https://maps.googleapis.com/maps/api/js?key=" + api_key).done(function() {
// Create a map instance
map = new google.maps.Map($('.openwebrx-map')[0], {
center : receiverPos,
zoom : 5,
});
// Load and initialize day-and-night overlay
$.getScript("static/lib/nite-overlay.js").done(function() {
nite.init(map);
setInterval(function() { nite.refresh() }, 10000); // every 10s
});
// Load and initialize OWRX-specific map item managers
$.getScript('static/lib/GoogleMaps.js').done(function() {
// Process any accumulated updates
self.processUpdates(updateQueue);
updateQueue = [];
});
// Create map legend selectors
var $legend = $(".openwebrx-map-legend");
self.setupLegendFilters($legend);
map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push($legend[0]);
// Create receiver marker
if (!receiverMarker) {
receiverMarker = new google.maps.Marker();
receiverMarker.addListener('click', function() {
showReceiverInfoWindow(receiverMarker);
});
}
// Set receiver marker position, name, etc.
receiverMarker.setOptions({
map : map,
position : receiverPos,
title : self.config['receiver_name'],
config : self.config
});
});
}
};
MapManager.prototype.processUpdates = function(updates) {
var self = this;
if (typeof(GMarker) === 'undefined') {
updateQueue = updateQueue.concat(updates);
return;
}
updates.forEach(function(update) {
var key = sourceToKey(update.source);
switch (update.location.type) {
case 'latlon':
var marker = self.mman.find(key);
var markerClass = GSimpleMarker;
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) {
// AF: here shall be created ICAO markers for planes.
// either by adapting the PlaneMarker.js or by reusing the AprsMarkers as in OWRX+
// I'll leave this to someone more competent or will try to implement it myself
// when I have the time to spend to understand how.
// As of now, the planes are shown on the map, but with default icon.
marker = new markerClass();
self.mman.add(key, marker);
marker.addListener('click', function() {
showMarkerInfoWindow(key, marker.position);
});
}
// Keep track of new marker types as they may change
self.mman.addType(update.mode);
// Update marker attributes and age
marker.update(update);
// Assign marker to map
marker.setMap(self.mman.isEnabled(update.mode)? map : undefined);
// Apply marker options
marker.setMarkerOptions(aprsOptions);
if (expectedIcao && expectedIcao === update.source.icao) {
map.panTo(marker.position);
showMarkerInfoWindow(key, marker.position);
expectedIcao = false;
}
if (expectedCallsign && expectedCallsign == key) {
map.panTo(marker.position);
showMarkerInfoWindow(key, marker.position);
expectedCallsign = false;
}
if (infoWindow && infoWindow.callsign && infoWindow.callsign == key) {
showMarkerInfoWindow(infoWindow.callsign, marker.position);
}
break;
case 'feature':
var marker = self.mman.find(key);
var options = {}
// If no symbol or color supplied, use defaults by type
if (update.location.symbol) {
options.symbol = update.location.symbol;
} else {
options.symbol = self.mman.getSymbol(update.mode);
}
if (update.location.color) {
options.color = update.location.color;
} else {
options.color = self.mman.getColor(update.mode);
}
// If new item, create a new marker for it
if (!marker) {
marker = new GFeatureMarker();
self.mman.addType(update.mode);
self.mman.add(key, marker);
marker.addListener('click', function() {
showMarkerInfoWindow(key, marker.position);
});
}
// Update marker attributes and age
marker.update(update);
// Assign marker to map
marker.setMap(self.mman.isEnabled(update.mode)? map : undefined);
// Apply marker options
marker.setMarkerOptions(options);
if (expectedCallsign && expectedCallsign == key) {
map.panTo(marker.position);
showMarkerInfoWindow(key, marker.position);
expectedCallsign = false;
}
if (infoWindow && infoWindow.callsign && infoWindow.callsign == key) {
showMarkerInfoWindow(infoWindow.callsign, marker.position);
}
break;
case 'locator':
var rectangle = self.lman.find(key);
// If new item, create a new locator for it
if (!rectangle) {
rectangle = new GLocator();
self.lman.add(key, 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(self.lman.filter(rectangle)? map : undefined);
rectangle.setColor(self.lman.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;
}
});
};