Added support for integrated Google Maps interface via QWebView/JavaScript/JSONP. Broken due to something hairy wrt QWebView and /tmp.
This commit is contained in:
parent
fbe3c464fb
commit
55cd17de67
@ -19,9 +19,9 @@
|
|||||||
# Boston, MA 02110-1301, USA.
|
# Boston, MA 02110-1301, USA.
|
||||||
#
|
#
|
||||||
|
|
||||||
import os, sys, time, threading, datetime, math, csv
|
import os, sys, time, threading, datetime, math, csv, tempfile
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from PyQt4 import QtCore,QtGui
|
from PyQt4 import QtCore,QtGui,QtWebKit
|
||||||
from PyQt4.Qwt5 import Qwt
|
from PyQt4.Qwt5 import Qwt
|
||||||
from gnuradio import gr, gru, optfir, eng_notation, blks2
|
from gnuradio import gr, gru, optfir, eng_notation, blks2
|
||||||
from gnuradio.eng_option import eng_option
|
from gnuradio.eng_option import eng_option
|
||||||
@ -72,7 +72,8 @@ class mainwindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
#disable by default
|
#disable by default
|
||||||
self.ui.check_adsbonly.setCheckState(QtCore.Qt.Unchecked)
|
self.ui.check_adsbonly.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
|
#set up the radio stuff
|
||||||
self.queue = gr.msg_queue(10)
|
self.queue = gr.msg_queue(10)
|
||||||
self.running = False
|
self.running = False
|
||||||
self.kmlgen = None #necessary bc we stop its thread in shutdown
|
self.kmlgen = None #necessary bc we stop its thread in shutdown
|
||||||
@ -288,12 +289,27 @@ class mainwindow(QtGui.QMainWindow):
|
|||||||
self.az_map_output = air_modes.az_map_output(self._cpr_dec, self.az_model, self._publisher)
|
self.az_map_output = air_modes.az_map_output(self._cpr_dec, self.az_model, self._publisher)
|
||||||
#self._relay.subscribe("dl_data", self.az_map_output.output)
|
#self._relay.subscribe("dl_data", self.az_map_output.output)
|
||||||
|
|
||||||
|
#set up map
|
||||||
|
self._htmlfile = open("/home/nick/wat.html", 'wb+')#tempfile.NamedTemporaryFile()
|
||||||
|
self._jsonfile = tempfile.NamedTemporaryFile()
|
||||||
|
|
||||||
self.livedata = air_modes.output_print(self._cpr_dec, self._publisher)
|
self.livedata = air_modes.output_print(self._cpr_dec, self._publisher)
|
||||||
#add output for live data box
|
#add output for live data box
|
||||||
#self._relay.subscribe("dl_data", self.output_live_data)
|
#self._relay.subscribe("dl_data", self.output_live_data)
|
||||||
|
|
||||||
#create SQL database for KML and dashboard displays
|
#create SQL database for KML and dashboard displays
|
||||||
self.dbwriter = air_modes.output_sql(self._cpr_dec, self.dbname, self.lock, self._publisher)
|
self.dbwriter = air_modes.output_sql(self._cpr_dec, self.dbname, self.lock, self._publisher)
|
||||||
|
self.jsonpgen = air_modes.output_jsonp(self._jsonfile.name, self.dbname, my_position, self.lock, timeout=1)
|
||||||
|
htmlstring = air_modes.html_template(my_position, self._jsonfile.name)
|
||||||
|
self._htmlfile.write(htmlstring)
|
||||||
|
self._htmlfile.flush()
|
||||||
|
class WebPage(QtWebKit.QWebPage):
|
||||||
|
def javaScriptConsoleMessage(self, msg, line, source):
|
||||||
|
print '%s line %d: %s' % (source, line, msg)
|
||||||
|
page = WebPage()
|
||||||
|
self.ui.mapView.setPage(page)
|
||||||
|
self.ui.mapView.load( QtCore.QUrl( QtCore.QUrl.fromLocalFile("/home/nick/wat.html") ) )
|
||||||
|
self.ui.mapView.show()
|
||||||
|
|
||||||
#output to update reports/sec widget
|
#output to update reports/sec widget
|
||||||
self._relay.subscribe("dl_data", self.increment_reportspersec)
|
self._relay.subscribe("dl_data", self.increment_reportspersec)
|
||||||
|
@ -34,6 +34,7 @@ GR_PYTHON_INSTALL(
|
|||||||
altitude.py
|
altitude.py
|
||||||
az_map.py
|
az_map.py
|
||||||
cpr.py
|
cpr.py
|
||||||
|
html_template.py
|
||||||
mlat.py
|
mlat.py
|
||||||
exceptions.py
|
exceptions.py
|
||||||
flightgear.py
|
flightgear.py
|
||||||
|
@ -57,7 +57,7 @@ from parse import *
|
|||||||
from msprint import output_print
|
from msprint import output_print
|
||||||
from sql import output_sql
|
from sql import output_sql
|
||||||
from sbs1 import output_sbs1
|
from sbs1 import output_sbs1
|
||||||
from kml import output_kml
|
from kml import output_kml, output_jsonp
|
||||||
from raw_server import raw_server
|
from raw_server import raw_server
|
||||||
from radio import modes_radio
|
from radio import modes_radio
|
||||||
from exceptions import *
|
from exceptions import *
|
||||||
@ -65,6 +65,7 @@ from az_map import *
|
|||||||
from types import *
|
from types import *
|
||||||
from altitude import *
|
from altitude import *
|
||||||
from cpr import cpr_decoder
|
from cpr import cpr_decoder
|
||||||
|
from html_template import html_template
|
||||||
#this is try/excepted in case the user doesn't have numpy installed
|
#this is try/excepted in case the user doesn't have numpy installed
|
||||||
try:
|
try:
|
||||||
from flightgear import output_flightgear
|
from flightgear import output_flightgear
|
||||||
|
92
python/html_template.py
Normal file
92
python/html_template.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#HTML template for Mode S map display
|
||||||
|
#Nick Foster, 2013
|
||||||
|
|
||||||
|
def html_template(my_position, json_file):
|
||||||
|
if my_position is None:
|
||||||
|
my_position = [37, -122]
|
||||||
|
|
||||||
|
return """
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ADS-B Aircraft Map</title>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||||
|
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var map;
|
||||||
|
var markers = [];
|
||||||
|
var defaultLocation = new google.maps.LatLng(%f, %f);
|
||||||
|
var defaultZoomLevel = 9;
|
||||||
|
|
||||||
|
function requestJSONP() {
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.src = "%s?" + Math.random();
|
||||||
|
script.params = Math.random();
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
|
};
|
||||||
|
|
||||||
|
var planeMarker;
|
||||||
|
var planes = [];
|
||||||
|
|
||||||
|
function clearMarkers() {
|
||||||
|
for (var i = 0; i < planes.length; i++) {
|
||||||
|
planes[i].setMap(null);
|
||||||
|
}
|
||||||
|
planes = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
function jsonp_callback(results) { // from JSONP
|
||||||
|
clearMarkers();
|
||||||
|
airplanes = {};
|
||||||
|
for (var i = 0; i < results.length; i++) {
|
||||||
|
airplanes[results[i].icao] = {
|
||||||
|
center: new google.maps.LatLng(results[i].lat, results[i].lon),
|
||||||
|
heading: results[i].hdg,
|
||||||
|
altitude: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
refreshIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshIcons() {
|
||||||
|
for (var airplane in airplanes) {
|
||||||
|
var plane_icon = {
|
||||||
|
url: "http://www.nerdnetworks.org/~bistromath/airplane_sprite.png",
|
||||||
|
size: new google.maps.Size(128,128),
|
||||||
|
origin: new google.maps.Point(parseInt(airplanes[airplane].heading/10)*128,0),
|
||||||
|
anchor: new google.maps.Point(64,64),
|
||||||
|
//scaledSize: new google.maps.Size(4608,126)
|
||||||
|
};
|
||||||
|
var planeOptions = {
|
||||||
|
map: map,
|
||||||
|
position: airplanes[airplane].center,
|
||||||
|
icon: plane_icon
|
||||||
|
};
|
||||||
|
planeMarker = new google.maps.Marker(planeOptions);
|
||||||
|
planes.push(planeMarker);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function initialize()
|
||||||
|
{
|
||||||
|
var myOptions =
|
||||||
|
{
|
||||||
|
zoom: defaultZoomLevel,
|
||||||
|
center: defaultLocation,
|
||||||
|
disableDefaultUI: true,
|
||||||
|
mapTypeId: google.maps.MapTypeId.TERRAIN
|
||||||
|
};
|
||||||
|
|
||||||
|
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
|
||||||
|
|
||||||
|
requestJSONP();
|
||||||
|
setInterval("requestJSONP()", 1000);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="initialize()">
|
||||||
|
<div id="map_canvas" style="width:100%%; height:100%%">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>""" % (my_position[0], my_position[1], json_file)
|
@ -170,3 +170,92 @@ class output_kml(threading.Thread):
|
|||||||
|
|
||||||
retstr+= '\n\t</Folder>\n</Document>\n</kml>'
|
retstr+= '\n\t</Folder>\n</Document>\n</kml>'
|
||||||
return retstr
|
return retstr
|
||||||
|
|
||||||
|
#we just inherit from output_kml because we're doing the same thing, only in a different format.
|
||||||
|
class output_jsonp(output_kml):
|
||||||
|
def genkml(self):
|
||||||
|
retstr="""jsonp_callback(["""
|
||||||
|
|
||||||
|
# if self.my_coords is not None:
|
||||||
|
# retstr += """\n\t<Folder>\n\t\t<name>Range rings</name>\n\t\t<open>0</open>"""
|
||||||
|
# for rng in [100, 200, 300]:
|
||||||
|
# retstr += """\n\t\t<Placemark>\n\t\t\t<name>%inm</name>\n\t\t\t<styleUrl>#rangering</styleUrl>\n\t\t\t<LinearRing>\n\t\t\t\t<coordinates>%s</coordinates>\n\t\t\t</LinearRing>\n\t\t</Placemark>""" % (rng, self.draw_circle(self.my_coords, rng),)
|
||||||
|
# retstr += """\t</Folder>\n"""
|
||||||
|
|
||||||
|
# retstr += """\t<Folder>\n\t\t<name>Aircraft locations</name>\n\t\t<open>0</open>"""
|
||||||
|
|
||||||
|
#read the database and add KML
|
||||||
|
q = "select distinct icao from positions where seen > datetime('now', '-5 minute')"
|
||||||
|
c = self._db.cursor()
|
||||||
|
self.locked_execute(c, q)
|
||||||
|
icaolist = c.fetchall()
|
||||||
|
#now we have a list icaolist of all ICAOs seen in the last 5 minutes
|
||||||
|
|
||||||
|
for icao in icaolist:
|
||||||
|
icao = icao[0]
|
||||||
|
#print "ICAO: %x" % icao
|
||||||
|
# q = "select * from positions where icao=%i and seen > datetime('now', '-2 hour') ORDER BY seen DESC limit 1" % icao
|
||||||
|
# self.locked_execute(c, q)
|
||||||
|
# pos = c.fetchall()
|
||||||
|
#print "Track length: %i" % len(track)
|
||||||
|
# if len(track) != 0:
|
||||||
|
# lat = track[0][3]
|
||||||
|
# if lat is None: lat = 0
|
||||||
|
# lon = track[0][4]
|
||||||
|
# if lon is None: lon = 0
|
||||||
|
# alt = track[0][2]
|
||||||
|
# if alt is None: alt = 0
|
||||||
|
|
||||||
|
# metric_alt = alt * 0.3048 #google earth takes meters, the commie bastards
|
||||||
|
|
||||||
|
# trackstr = ""
|
||||||
|
|
||||||
|
# for pos in track:
|
||||||
|
# trackstr += " %f,%f,%f" % (pos[4], pos[3], pos[2]*0.3048)
|
||||||
|
|
||||||
|
# trackstr = string.lstrip(trackstr)
|
||||||
|
# else:
|
||||||
|
# alt = 0
|
||||||
|
# metric_alt = 0
|
||||||
|
# lat = 0
|
||||||
|
# lon = 0
|
||||||
|
# trackstr = str("")
|
||||||
|
|
||||||
|
#now get metadata
|
||||||
|
# q = "select ident from ident where icao=%i" % icao
|
||||||
|
# self.locked_execute(c, q)
|
||||||
|
# r = c.fetchall()
|
||||||
|
# if len(r) != 0:
|
||||||
|
# ident = r[0][0]
|
||||||
|
# else: ident=""
|
||||||
|
#if ident is None: ident = ""
|
||||||
|
#get most recent speed/heading/vertical
|
||||||
|
q = "select seen, speed, heading, vertical from vectors where icao=%i order by seen desc limit 1" % icao
|
||||||
|
self.locked_execute(c, q)
|
||||||
|
r = c.fetchall()
|
||||||
|
if len(r) != 0:
|
||||||
|
seen = r[0][0]
|
||||||
|
speed = r[0][1]
|
||||||
|
heading = r[0][2]
|
||||||
|
vertical = r[0][3]
|
||||||
|
|
||||||
|
else:
|
||||||
|
seen = 0
|
||||||
|
speed = 0
|
||||||
|
heading = 0
|
||||||
|
vertical = 0
|
||||||
|
|
||||||
|
q = "select lat, lon from positions where icao=%i order by seen desc limit 1" % icao
|
||||||
|
self.locked_execute(c, q)
|
||||||
|
r = c.fetchall()
|
||||||
|
if len(r) != 0:
|
||||||
|
lat = r[0][0]
|
||||||
|
lon = r[0][1]
|
||||||
|
else:
|
||||||
|
lat = 0
|
||||||
|
lon = 0
|
||||||
|
#now generate some KML
|
||||||
|
retstr+= """{"icao": "%.6x", "lat": %f, "lon": %f, "hdg": %i, "speed": %i, "vertical": %i},""" % (icao, lat, lon, heading, speed, vertical)
|
||||||
|
|
||||||
|
retstr+= """]);"""
|
||||||
|
return retstr
|
||||||
|
@ -80,7 +80,7 @@ class output_sql:
|
|||||||
c = self._db.cursor()
|
c = self._db.cursor()
|
||||||
c.execute(query)
|
c.execute(query)
|
||||||
c.close()
|
c.close()
|
||||||
# self._db.commit()
|
self._db.commit()
|
||||||
|
|
||||||
except ADSBError:
|
except ADSBError:
|
||||||
pass
|
pass
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="setup">
|
<widget class="QWidget" name="setup">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -856,7 +856,7 @@
|
|||||||
<string>Climb</string>
|
<string>Climb</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QwtCompass" name="compass_heading">
|
<widget class="QwtCompass" name="compass_heading" native="true">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>200</x>
|
<x>200</x>
|
||||||
@ -865,10 +865,10 @@
|
|||||||
<height>91</height>
|
<height>91</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="readOnly">
|
<property name="readOnly" stdset="0">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="lineWidth">
|
<property name="lineWidth" stdset="0">
|
||||||
<number>4</number>
|
<number>4</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -901,7 +901,7 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QwtCompass" name="compass_bearing">
|
<widget class="QwtCompass" name="compass_bearing" native="true">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>-20</x>
|
<x>-20</x>
|
||||||
@ -910,10 +910,10 @@
|
|||||||
<height>91</height>
|
<height>91</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="readOnly">
|
<property name="readOnly" stdset="0">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="lineWidth">
|
<property name="lineWidth" stdset="0">
|
||||||
<number>4</number>
|
<number>4</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -928,6 +928,18 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="map_tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Map</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_8">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QWebView" name="mapView" native="true">
|
||||||
|
<zorder>gridLayoutWidget</zorder>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="livedatatab">
|
<widget class="QWidget" name="livedatatab">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
@ -1016,11 +1028,6 @@
|
|||||||
<widget class="QStatusBar" name="statusbar"/>
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
|
||||||
<class>QwtCompass</class>
|
|
||||||
<extends>QwtDial</extends>
|
|
||||||
<header>qwt_compass.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QwtDial</class>
|
<class>QwtDial</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
@ -1032,6 +1039,17 @@
|
|||||||
<header location="global">air_modes/az_map</header>
|
<header location="global">air_modes/az_map</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>QwtCompass</class>
|
||||||
|
<extends>QwtDial</extends>
|
||||||
|
<header>qwt_compass.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>QWebView</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header location="global">QtWebKit/qwebview.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
Loading…
Reference in New Issue
Block a user