display locations parsed from ysf on map

This commit is contained in:
Jakob Ketterl 2019-07-01 21:20:53 +02:00
parent 272caa7100
commit 3b2b51f07c
4 changed files with 121 additions and 2 deletions

View File

@ -12,6 +12,33 @@
ws.send("SERVER DE CLIENT client=map.js type=map"); ws.send("SERVER DE CLIENT client=map.js type=map");
}; };
var map;
var markers = {};
var updateQueue = [];
var processUpdates = function(updates) {
if (!map) {
updateQueue = updateQueue.concat(updates);
return;
}
updates.forEach(function(update){
// TODO maidenhead locator implementation
if (update.location.type != 'latlon') return;
var pos = new google.maps.LatLng(update.location.lat, update.location.lon)
if (markers[update.callsign]) {
console.info("updating");
markers[update.callsign].setPosition(pos);
} else {
console.info("initializing");
markers[update.callsign] = new google.maps.Marker({
position: pos,
map: map,
title: update.callsign
});
}
});
}
ws.onmessage = function(e){ ws.onmessage = function(e){
if (typeof e.data != 'string') { if (typeof e.data != 'string') {
console.error("unsupported binary data on websocket; ignoring"); console.error("unsupported binary data on websocket; ignoring");
@ -27,15 +54,19 @@
case "config": case "config":
var config = json.value; var config = json.value;
$.getScript("https://maps.googleapis.com/maps/api/js?key=" + config.google_maps_api_key).done(function(){ $.getScript("https://maps.googleapis.com/maps/api/js?key=" + config.google_maps_api_key).done(function(){
var map = new google.maps.Map($('body')[0], { map = new google.maps.Map($('body')[0], {
center: { center: {
lat: config.receiver_gps[0], lat: config.receiver_gps[0],
lng: config.receiver_gps[1] lng: config.receiver_gps[1]
}, },
zoom: 8 zoom: 8
}); });
processUpdates(updateQueue);
}) })
break break
case "update":
processUpdates(json.value);
break
} }
} catch (e) { } catch (e) {
// don't lose exception // don't lose exception

View File

@ -3,10 +3,12 @@ from owrx.source import DspManager, CpuUsageThread, SdrService, ClientRegistry
from owrx.feature import FeatureDetector from owrx.feature import FeatureDetector
from owrx.version import openwebrx_version from owrx.version import openwebrx_version
import json import json
from owrx.map import Map
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Client(object): class Client(object):
def __init__(self, conn): def __init__(self, conn):
self.conn = conn self.conn = conn
@ -165,9 +167,17 @@ class MapConnection(Client):
pm = PropertyManager.getSharedInstance() pm = PropertyManager.getSharedInstance()
self.write_config(pm.collect("google_maps_api_key", "receiver_gps").__dict__()) self.write_config(pm.collect("google_maps_api_key", "receiver_gps").__dict__())
Map.getSharedInstance().addClient(self)
def close(self):
Map.getSharedInstance().removeClient(self)
super().close()
def write_config(self, cfg): def write_config(self, cfg):
self.protected_send({"type":"config","value":cfg}) self.protected_send({"type":"config","value":cfg})
def write_update(self, update):
self.protected_send({"type":"update","value":update})
class WebSocketMessageHandler(object): class WebSocketMessageHandler(object):
def __init__(self): def __init__(self):

62
owrx/map.py Normal file
View File

@ -0,0 +1,62 @@
from datetime import datetime
class Location(object):
def __dict__(self):
return {}
class Map(object):
sharedInstance = None
@staticmethod
def getSharedInstance():
if Map.sharedInstance is None:
Map.sharedInstance = Map()
return Map.sharedInstance
def __init__(self):
self.clients = []
self.positions = {}
super().__init__()
def broadcast(self, update):
for c in self.clients:
c.write_update(update)
def addClient(self, client):
self.clients.append(client)
client.write_update([{"callsign": callsign, "location": record["loc"].__dict__()} for (callsign, record) in self.positions.items()])
def removeClient(self, client):
try:
self.clients.remove(client)
except ValueError:
pass
def updateLocation(self, callsign, loc: Location):
self.positions[callsign] = {"loc": loc, "updated": datetime.now()}
self.broadcast([{"callsign": callsign, "location": loc.__dict__()}])
class LatLngLocation(Location):
def __init__(self, lat: float, lon: float):
self.lat = lat
self.lon = lon
def __dict__(self):
return {
"type":"latlon",
"lat":self.lat,
"lon":self.lon
}
class LocatorLocation(Location):
def __init__(self, locator: str):
self.locator = locator
def __dict__(self):
return {
"type":"locator",
"locator":self.locator
}

View File

@ -4,6 +4,7 @@ import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
import threading import threading
from owrx.map import Map, LatLngLocation
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -14,18 +15,22 @@ class DmrCache(object):
if DmrCache.sharedInstance is None: if DmrCache.sharedInstance is None:
DmrCache.sharedInstance = DmrCache() DmrCache.sharedInstance = DmrCache()
return DmrCache.sharedInstance return DmrCache.sharedInstance
def __init__(self): def __init__(self):
self.cache = {} self.cache = {}
self.cacheTimeout = timedelta(seconds = 86400) self.cacheTimeout = timedelta(seconds = 86400)
def isValid(self, key): def isValid(self, key):
if not key in self.cache: return False if not key in self.cache: return False
entry = self.cache[key] entry = self.cache[key]
return entry["timestamp"] + self.cacheTimeout > datetime.now() return entry["timestamp"] + self.cacheTimeout > datetime.now()
def put(self, key, value): def put(self, key, value):
self.cache[key] = { self.cache[key] = {
"timestamp": datetime.now(), "timestamp": datetime.now(),
"data": value "data": value
} }
def get(self, key): def get(self, key):
if not self.isValid(key): return None if not self.isValid(key): return None
return self.cache[key]["data"] return self.cache[key]["data"]
@ -34,6 +39,7 @@ class DmrCache(object):
class DmrMetaEnricher(object): class DmrMetaEnricher(object):
def __init__(self): def __init__(self):
self.threads = {} self.threads = {}
def downloadRadioIdData(self, id): def downloadRadioIdData(self, id):
cache = DmrCache.getSharedInstance() cache = DmrCache.getSharedInstance()
try: try:
@ -44,6 +50,7 @@ class DmrMetaEnricher(object):
except json.JSONDecodeError: except json.JSONDecodeError:
cache.put(id, None) cache.put(id, None)
del self.threads[id] del self.threads[id]
def enrich(self, meta): def enrich(self, meta):
if not PropertyManager.getSharedInstance()["digital_voice_dmr_id_lookup"]: return None if not PropertyManager.getSharedInstance()["digital_voice_dmr_id_lookup"]: return None
if not "source" in meta: return None if not "source" in meta: return None
@ -60,9 +67,18 @@ class DmrMetaEnricher(object):
return None return None
class YsfMetaEnricher(object):
def enrich(self, meta):
if "source" in meta and "lat" in meta and "lon" in meta:
# TODO parsing the float values should probably happen earlier
Map.getSharedInstance().updateLocation(meta["source"], LatLngLocation(float(meta["lat"]), float(meta["lon"])))
return None
class MetaParser(object): class MetaParser(object):
enrichers = { enrichers = {
"DMR": DmrMetaEnricher() "DMR": DmrMetaEnricher(),
"YSF": YsfMetaEnricher()
} }
def __init__(self, handler): def __init__(self, handler):