kopia lustrzana https://github.com/glidernet/ogn-python
Added routes for ogn-live (#24)
rodzic
b9eca062b5
commit
bfb9ebc08a
|
@ -1,10 +1,9 @@
|
|||
from datetime import datetime, timedelta, timezone, date
|
||||
|
||||
from sqlalchemy import func, and_, between, case
|
||||
|
||||
from ogn_python.model import AircraftBeacon, Device, Receiver
|
||||
|
||||
from ogn_python import db
|
||||
from ogn_python.model.receiver_beacon import ReceiverBeacon
|
||||
|
||||
|
||||
def utc_to_local(utc_dt):
|
||||
|
@ -20,19 +19,24 @@ def decode(code):
|
|||
|
||||
|
||||
def rec():
|
||||
last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
|
||||
receiver_query = db.session.query(Receiver,
|
||||
case([(Receiver.lastseen > last_10_minutes, True)],
|
||||
else_=False).label('is_online')) \
|
||||
.order_by(Receiver.name)
|
||||
min_online_timestamp = datetime.utcnow() - timedelta(minutes=10)
|
||||
|
||||
lines = list()
|
||||
timestamp_range_filter = [db.between(ReceiverBeacon.timestamp, datetime(2018, 7, 31, 11, 55, 0), datetime(2018, 7, 31, 12, 5, 0))]
|
||||
|
||||
last_seen_query = db.session.query(ReceiverBeacon) \
|
||||
.filter(*timestamp_range_filter) \
|
||||
.order_by(ReceiverBeacon.receiver_id, ReceiverBeacon.timestamp) \
|
||||
.distinct(ReceiverBeacon.receiver_id)
|
||||
|
||||
lines = []
|
||||
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
||||
lines.append('<markers>')
|
||||
lines.append('<m e="0"/>')
|
||||
for [receiver, is_online] in receiver_query.all():
|
||||
for receiver_beacon in last_seen_query:
|
||||
if receiver_beacon.location == None or receiver_beacon.name.startswith('FNB'):
|
||||
continue
|
||||
lines.append('<m a="{0}" b="{1:.7f}" c="{2:.7f}" d="{3:1d}"/>'
|
||||
.format(receiver.name, receiver.location.latitude, receiver.location.longitude, is_online))
|
||||
.format(receiver_beacon.name, receiver_beacon.location.latitude, receiver_beacon.location.longitude, receiver_beacon.timestamp < min_online_timestamp))
|
||||
|
||||
lines.append('</markers>')
|
||||
xml = '\n'.join(lines)
|
||||
|
@ -42,24 +46,20 @@ def rec():
|
|||
|
||||
def lxml(show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_min=-180):
|
||||
|
||||
if show_offline:
|
||||
observation_start = date.today()
|
||||
else:
|
||||
observation_start = datetime.utcnow() - timedelta(minutes=5)
|
||||
timestamp_range_filter = [db.between(AircraftBeacon.timestamp, datetime(2018, 7, 31, 11, 55, 0), datetime(2018, 7, 31, 12, 5, 0))]
|
||||
|
||||
position_query = db.session.query(AircraftBeacon, Device) \
|
||||
.filter(and_(between(func.ST_Y(AircraftBeacon.location_wkt), lat_min, lat_max),
|
||||
between(func.ST_X(AircraftBeacon.location_wkt), lon_min, lon_max))) \
|
||||
.filter(Device.lastseen > observation_start) \
|
||||
.filter(Device.lastseen == AircraftBeacon.timestamp) \
|
||||
.filter(Device.id == AircraftBeacon.device_id) \
|
||||
.order_by(AircraftBeacon.timestamp)
|
||||
last_seen_query = db.session.query(AircraftBeacon) \
|
||||
.filter(*timestamp_range_filter) \
|
||||
.order_by(AircraftBeacon.device_id, AircraftBeacon.timestamp) \
|
||||
.distinct(AircraftBeacon.device_id) \
|
||||
|
||||
lines = list()
|
||||
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
||||
lines.append('<markers>')
|
||||
|
||||
for [aircraft_beacon, device] in position_query.all():
|
||||
for aircraft_beacon in last_seen_query:
|
||||
device = aircraft_beacon.device
|
||||
|
||||
code = encode(device.address)
|
||||
|
||||
if device.info:
|
||||
|
@ -105,4 +105,4 @@ def lxml(show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_min=-180)
|
|||
lines.append('</markers>')
|
||||
xml = '\n'.join(lines)
|
||||
|
||||
return xml
|
||||
return xml
|
|
@ -0,0 +1,90 @@
|
|||
from flask import request, render_template, make_response, send_file
|
||||
from flask_cors import cross_origin
|
||||
|
||||
from ogn_python.backend.liveglidernet import rec, lxml
|
||||
|
||||
from ogn_python import app
|
||||
from ogn_python import db
|
||||
from ogn_python import cache
|
||||
|
||||
|
||||
@app.route('/live.html')
|
||||
@cross_origin()
|
||||
def live():
|
||||
return render_template('ogn_live.html', host=request.host)
|
||||
|
||||
|
||||
@app.route('/rec.php')
|
||||
def rec_php():
|
||||
a = request.args.get('a')
|
||||
z = request.args.get('z')
|
||||
|
||||
xml = rec()
|
||||
resp = app.make_response(xml)
|
||||
resp.mimetype = "text/xml"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route('/lxml.php')
|
||||
def lxml_php():
|
||||
a = request.args.get('a')
|
||||
b = request.args.get('b')
|
||||
c = request.args.get('c')
|
||||
d = request.args.get('d')
|
||||
e = request.args.get('e')
|
||||
z = request.args.get('z')
|
||||
|
||||
xml = lxml()
|
||||
resp = app.make_response(xml)
|
||||
resp.mimetype = "text/xml"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route('/pict/<filename>')
|
||||
def pict(filename):
|
||||
return app.send_static_file('ognlive/pict/' + filename)
|
||||
|
||||
|
||||
@app.route('/favicon.gif')
|
||||
def favicon_gif():
|
||||
return app.send_static_file('ognlive/pict/favicon.gif')
|
||||
|
||||
|
||||
@app.route('/horizZoomControl.js')
|
||||
def horizZoomControl_js():
|
||||
return app.send_static_file('ognlive/horizZoomControl.js')
|
||||
|
||||
|
||||
@app.route('/barogram.js')
|
||||
def barogram_js():
|
||||
return app.send_static_file('ognlive/barogram.js')
|
||||
|
||||
|
||||
@app.route('/util.js')
|
||||
def util_js():
|
||||
return app.send_static_file('ognlive/util.js')
|
||||
|
||||
|
||||
@app.route('/ogn.js')
|
||||
def ogn_js():
|
||||
return app.send_static_file('ognlive/ogn.js')
|
||||
|
||||
|
||||
@app.route('/ol.js')
|
||||
def ol_js():
|
||||
return app.send_static_file('ognlive/ol.js')
|
||||
|
||||
|
||||
@app.route('/osm.js')
|
||||
def osm_js():
|
||||
return app.send_static_file('ognlive/osm.js')
|
||||
|
||||
|
||||
@app.route('/ol.css')
|
||||
def ol_css():
|
||||
return app.send_static_file('ognlive/ol.css')
|
||||
|
||||
|
||||
@app.route('/osm.css')
|
||||
def osm_css():
|
||||
return app.send_static_file('ognlive/osm.css')
|
|
@ -1,7 +1,7 @@
|
|||
import datetime
|
||||
|
||||
from flask import request, render_template
|
||||
from sqlalchemy import func, and_, or_
|
||||
from flask import request, render_template, send_file
|
||||
from flask_cors import cross_origin
|
||||
|
||||
from ogn_python import app
|
||||
from ogn_python import db
|
||||
|
@ -24,6 +24,7 @@ def get_countries_in_receivers():
|
|||
def get_countries_in_logbook():
|
||||
query = db.session.query(Country.iso2) \
|
||||
.filter(Country.iso2 == Airport.country_code) \
|
||||
.filter(Logbook.takeoff_airport_id == Airport.id) \
|
||||
.order_by(Country.iso2) \
|
||||
.distinct(Country.iso2)
|
||||
|
||||
|
@ -43,11 +44,11 @@ def get_airports_in_country(sel_country):
|
|||
|
||||
@cache.memoize()
|
||||
def get_dates_for_airport(sel_airport):
|
||||
query = db.session.query(func.date(Logbook.reftime), func.count(Logbook.id).label('logbook_count')) \
|
||||
query = db.session.query(db.func.date(Logbook.reftime), db.func.count(Logbook.id).label('logbook_count')) \
|
||||
.filter(Airport.id == sel_airport) \
|
||||
.filter(or_(Airport.id == Logbook.takeoff_airport_id, Airport.id == Logbook.landing_airport_id)) \
|
||||
.group_by(func.date(Logbook.reftime)) \
|
||||
.order_by(func.date(Logbook.reftime).desc())
|
||||
.filter(db.or_(Airport.id == Logbook.takeoff_airport_id, Airport.id == Logbook.landing_airport_id)) \
|
||||
.group_by(db.func.date(Logbook.reftime)) \
|
||||
.order_by(db.func.date(Logbook.reftime).desc())
|
||||
|
||||
return [{'date': date, 'logbook_count': logbook_count} for (date, logbook_count) in query.all()]
|
||||
|
||||
|
@ -87,7 +88,7 @@ def receivers():
|
|||
# Get receiver selection list
|
||||
if sel_country:
|
||||
receivers = db.session.query(Receiver) \
|
||||
.filter(and_(Receiver.country_id == Country.gid, Country.iso2 == sel_country)) \
|
||||
.filter(db.and_(Receiver.country_id == Country.gid, Country.iso2 == sel_country)) \
|
||||
.order_by(Receiver.name)
|
||||
else:
|
||||
receivers = db.session.query(Receiver) \
|
||||
|
@ -109,9 +110,9 @@ def receiver_detail():
|
|||
.one()
|
||||
|
||||
airport = db.session.query(Airport) \
|
||||
.filter(and_(Receiver.id == sel_receiver_id,
|
||||
func.st_contains(func.st_buffer(Receiver.location_wkt, 0.5), Airport.location_wkt),
|
||||
func.st_distance_sphere(Airport.location_wkt, Receiver.location_wkt) < 1000)) \
|
||||
.filter(db.and_(Receiver.id == sel_receiver_id,
|
||||
db.func.st_contains(db.func.st_buffer(Receiver.location_wkt, 0.5), Airport.location_wkt),
|
||||
db.func.st_distance_sphere(Airport.location_wkt, Receiver.location_wkt) < 1000)) \
|
||||
.filter(Airport.style.in_((2,4,5))) \
|
||||
|
||||
return render_template('receiver_detail.html',
|
||||
|
@ -215,6 +216,18 @@ def logbook():
|
|||
dates=dates,
|
||||
logbook=logbook)
|
||||
|
||||
@app.route('/download.html')
|
||||
def download_flight():
|
||||
from io import StringIO
|
||||
buffer = StringIO()
|
||||
buffer.write('Moin moin\nAlter Verwalter')
|
||||
buffer.seek(0)
|
||||
|
||||
return send_file(buffer,
|
||||
as_attachment=True,
|
||||
attachment_filename='wtf.igc',
|
||||
mimetype = 'text/plain')
|
||||
|
||||
|
||||
@app.route('/statistics.html')
|
||||
def statistics():
|
||||
|
@ -228,10 +241,3 @@ def statistics():
|
|||
return render_template('statistics.html',
|
||||
title='Receiver Statistics',
|
||||
receiverstats=receiverstats)
|
||||
|
||||
# Backend routes for other sites
|
||||
|
||||
|
||||
@app.route('/live.html')
|
||||
def live():
|
||||
return render_template('ogn_live.jinja')
|
||||
|
|
|
@ -1,22 +1,6 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
ogn-live - Display glider traffic from OpenGliderNetwork
|
||||
Copyright (C) 2015 Sebastien Chaumontet and others
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
____ _____ _ _ _ _ _ _ _
|
||||
/ __ \ / ____| (_) | | | \ | | | | | |
|
||||
| | | |_ __ ___ _ __ | | __| |_ __| | ___ _ __ | \| | ___| |___ _____ _ __| | __
|
||||
|
@ -28,54 +12,60 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<title>Spot the gliders!</title>
|
||||
<link href="cunimb.css" rel="stylesheet" type="text/css" />
|
||||
<link rel="shortcut icon" href="favicon.gif"/>
|
||||
<link rel="icon" type="image/gif" href="favicon.png"/>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
|
||||
<title>Spot the gliders!</title>
|
||||
<link href="ol.css" rel="stylesheet" type="text/css" />
|
||||
<link href="osm.css" rel="stylesheet" type="text/css" />
|
||||
<script type="text/javascript" src="ol.js"></script>
|
||||
<script type="text/javascript" src="util.js"></script>
|
||||
<script type="text/javascript">
|
||||
var cxml = "lxml.php";
|
||||
var cxml1 = "livexml1.php";
|
||||
var dxml = "dataxml.php";
|
||||
var rxml = "rec.php";
|
||||
var tld = "http://{{ host }}";
|
||||
var vlon = 5;
|
||||
var vlat = 45;
|
||||
var bound = false;
|
||||
var boundc = '';
|
||||
var amax = 85;
|
||||
var amin = -85;
|
||||
var omax = 180;
|
||||
var omin = -180;
|
||||
var recc = "";
|
||||
var parc = "";
|
||||
var tz;
|
||||
try {
|
||||
tz = new Date().getTimezoneOffset();
|
||||
}
|
||||
catch(e) {
|
||||
tz = 0;
|
||||
}
|
||||
|
||||
|
||||
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry&sensor=false"></script>
|
||||
<script type="text/javascript" src="util.js"></script>
|
||||
<script type="text/javascript">
|
||||
var cxml = "lxml.php";
|
||||
var cxml1 = "livexml1.php";
|
||||
var dxml = "dataxml.php";
|
||||
var rxml = "rec.php";
|
||||
var tld = "http://{{ ip_address }}:{{ port }}";
|
||||
var vlon = 11.0;
|
||||
var vlat = 48.0;
|
||||
var vlatmin = 40.0;
|
||||
var vlonmin = 10.0;
|
||||
var vlatmax = 60.0;
|
||||
var vlonmax = 20.0;
|
||||
var bound = false;
|
||||
var boundc = '';
|
||||
var amax = 85;
|
||||
var amin = -85;
|
||||
var omax = 180;
|
||||
var omin = -180;
|
||||
var recc = "";
|
||||
var parc = "";
|
||||
var tz;
|
||||
try {
|
||||
tz = new Date().getTimezoneOffset();
|
||||
} catch (e) {
|
||||
tz = 0;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="cunimb.js"></script>
|
||||
</script>
|
||||
<script type="text/javascript" src="ogn.js"></script>
|
||||
<script type="text/javascript" src="barogram.js"></script>
|
||||
<script type="text/javascript" src="horizZoomControl.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="initialize()">
|
||||
<div id="popup" onclick="cp('popup');"></div>
|
||||
<div id="map_canvas"></div>
|
||||
<div id="ac" class="acright" onclick="this.style.display='none';"></div>
|
||||
<div id="dlist" class="lright">
|
||||
<DIV id="ett1"></DIV>
|
||||
<DIV id="ett2"></DIV>
|
||||
<DIV id="dtable"></DIV>
|
||||
</div>
|
||||
<div id="popup" onclick="cp('popup');"></div>
|
||||
<div id="map_canvas"></div>
|
||||
<div id="ac" class="acright" onclick="this.style.display='none';"></div>
|
||||
<div id="lonlatoverlay" style="background-color: white; border-radius: 10px; border: 1px solid black; padding: 5px 10px; display: none;"></div>
|
||||
<div id="dlist" class="lright">
|
||||
<DIV id="ett1" ></DIV>
|
||||
<DIV id="ett2" ></DIV>
|
||||
<DIV id="dtable"></DIV>
|
||||
</div>
|
||||
<div id="dbaro" class="baroleft" style="display:block; visibility:hidden;">
|
||||
<canvas id="div_baroScale" width="45" ></canvas>
|
||||
<canvas id="div_baro" width="70"></canvas>
|
||||
<canvas id="div_baroMark" width="30" ></canvas>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
Ładowanie…
Reference in New Issue