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 datetime import datetime, timedelta, timezone, date
|
||||||
|
|
||||||
from sqlalchemy import func, and_, between, case
|
|
||||||
|
|
||||||
from ogn_python.model import AircraftBeacon, Device, Receiver
|
from ogn_python.model import AircraftBeacon, Device, Receiver
|
||||||
|
|
||||||
from ogn_python import db
|
from ogn_python import db
|
||||||
|
from ogn_python.model.receiver_beacon import ReceiverBeacon
|
||||||
|
|
||||||
|
|
||||||
def utc_to_local(utc_dt):
|
def utc_to_local(utc_dt):
|
||||||
|
@ -20,19 +19,24 @@ def decode(code):
|
||||||
|
|
||||||
|
|
||||||
def rec():
|
def rec():
|
||||||
last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
|
min_online_timestamp = 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)
|
|
||||||
|
|
||||||
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('<?xml version="1.0" encoding="UTF-8"?>')
|
||||||
lines.append('<markers>')
|
lines.append('<markers>')
|
||||||
lines.append('<m e="0"/>')
|
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}"/>'
|
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>')
|
lines.append('</markers>')
|
||||||
xml = '\n'.join(lines)
|
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):
|
def lxml(show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_min=-180):
|
||||||
|
|
||||||
if show_offline:
|
timestamp_range_filter = [db.between(AircraftBeacon.timestamp, datetime(2018, 7, 31, 11, 55, 0), datetime(2018, 7, 31, 12, 5, 0))]
|
||||||
observation_start = date.today()
|
|
||||||
else:
|
|
||||||
observation_start = datetime.utcnow() - timedelta(minutes=5)
|
|
||||||
|
|
||||||
position_query = db.session.query(AircraftBeacon, Device) \
|
last_seen_query = db.session.query(AircraftBeacon) \
|
||||||
.filter(and_(between(func.ST_Y(AircraftBeacon.location_wkt), lat_min, lat_max),
|
.filter(*timestamp_range_filter) \
|
||||||
between(func.ST_X(AircraftBeacon.location_wkt), lon_min, lon_max))) \
|
.order_by(AircraftBeacon.device_id, AircraftBeacon.timestamp) \
|
||||||
.filter(Device.lastseen > observation_start) \
|
.distinct(AircraftBeacon.device_id) \
|
||||||
.filter(Device.lastseen == AircraftBeacon.timestamp) \
|
|
||||||
.filter(Device.id == AircraftBeacon.device_id) \
|
|
||||||
.order_by(AircraftBeacon.timestamp)
|
|
||||||
|
|
||||||
lines = list()
|
lines = list()
|
||||||
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
||||||
lines.append('<markers>')
|
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)
|
code = encode(device.address)
|
||||||
|
|
||||||
if device.info:
|
if device.info:
|
||||||
|
|
|
@ -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
|
import datetime
|
||||||
|
|
||||||
from flask import request, render_template
|
from flask import request, render_template, send_file
|
||||||
from sqlalchemy import func, and_, or_
|
from flask_cors import cross_origin
|
||||||
|
|
||||||
from ogn_python import app
|
from ogn_python import app
|
||||||
from ogn_python import db
|
from ogn_python import db
|
||||||
|
@ -24,6 +24,7 @@ def get_countries_in_receivers():
|
||||||
def get_countries_in_logbook():
|
def get_countries_in_logbook():
|
||||||
query = db.session.query(Country.iso2) \
|
query = db.session.query(Country.iso2) \
|
||||||
.filter(Country.iso2 == Airport.country_code) \
|
.filter(Country.iso2 == Airport.country_code) \
|
||||||
|
.filter(Logbook.takeoff_airport_id == Airport.id) \
|
||||||
.order_by(Country.iso2) \
|
.order_by(Country.iso2) \
|
||||||
.distinct(Country.iso2)
|
.distinct(Country.iso2)
|
||||||
|
|
||||||
|
@ -43,11 +44,11 @@ def get_airports_in_country(sel_country):
|
||||||
|
|
||||||
@cache.memoize()
|
@cache.memoize()
|
||||||
def get_dates_for_airport(sel_airport):
|
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(Airport.id == sel_airport) \
|
||||||
.filter(or_(Airport.id == Logbook.takeoff_airport_id, Airport.id == Logbook.landing_airport_id)) \
|
.filter(db.or_(Airport.id == Logbook.takeoff_airport_id, Airport.id == Logbook.landing_airport_id)) \
|
||||||
.group_by(func.date(Logbook.reftime)) \
|
.group_by(db.func.date(Logbook.reftime)) \
|
||||||
.order_by(func.date(Logbook.reftime).desc())
|
.order_by(db.func.date(Logbook.reftime).desc())
|
||||||
|
|
||||||
return [{'date': date, 'logbook_count': logbook_count} for (date, logbook_count) in query.all()]
|
return [{'date': date, 'logbook_count': logbook_count} for (date, logbook_count) in query.all()]
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ def receivers():
|
||||||
# Get receiver selection list
|
# Get receiver selection list
|
||||||
if sel_country:
|
if sel_country:
|
||||||
receivers = db.session.query(Receiver) \
|
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)
|
.order_by(Receiver.name)
|
||||||
else:
|
else:
|
||||||
receivers = db.session.query(Receiver) \
|
receivers = db.session.query(Receiver) \
|
||||||
|
@ -109,9 +110,9 @@ def receiver_detail():
|
||||||
.one()
|
.one()
|
||||||
|
|
||||||
airport = db.session.query(Airport) \
|
airport = db.session.query(Airport) \
|
||||||
.filter(and_(Receiver.id == sel_receiver_id,
|
.filter(db.and_(Receiver.id == sel_receiver_id,
|
||||||
func.st_contains(func.st_buffer(Receiver.location_wkt, 0.5), Airport.location_wkt),
|
db.func.st_contains(db.func.st_buffer(Receiver.location_wkt, 0.5), Airport.location_wkt),
|
||||||
func.st_distance_sphere(Airport.location_wkt, Receiver.location_wkt) < 1000)) \
|
db.func.st_distance_sphere(Airport.location_wkt, Receiver.location_wkt) < 1000)) \
|
||||||
.filter(Airport.style.in_((2,4,5))) \
|
.filter(Airport.style.in_((2,4,5))) \
|
||||||
|
|
||||||
return render_template('receiver_detail.html',
|
return render_template('receiver_detail.html',
|
||||||
|
@ -215,6 +216,18 @@ def logbook():
|
||||||
dates=dates,
|
dates=dates,
|
||||||
logbook=logbook)
|
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')
|
@app.route('/statistics.html')
|
||||||
def statistics():
|
def statistics():
|
||||||
|
@ -228,10 +241,3 @@ def statistics():
|
||||||
return render_template('statistics.html',
|
return render_template('statistics.html',
|
||||||
title='Receiver Statistics',
|
title='Receiver Statistics',
|
||||||
receiverstats=receiverstats)
|
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>
|
<!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>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
<link rel="shortcut icon" href="favicon.gif"/>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
<link rel="icon" type="image/gif" href="favicon.png"/>
|
||||||
<title>Spot the gliders!</title>
|
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||||
<link href="cunimb.css" rel="stylesheet" type="text/css" />
|
<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" />
|
||||||
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry&sensor=false"></script>
|
<link href="osm.css" rel="stylesheet" type="text/css" />
|
||||||
<script type="text/javascript" src="util.js"></script>
|
<script type="text/javascript" src="ol.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript" src="util.js"></script>
|
||||||
var cxml = "lxml.php";
|
<script type="text/javascript">
|
||||||
var cxml1 = "livexml1.php";
|
var cxml = "lxml.php";
|
||||||
var dxml = "dataxml.php";
|
var cxml1 = "livexml1.php";
|
||||||
var rxml = "rec.php";
|
var dxml = "dataxml.php";
|
||||||
var tld = "http://{{ ip_address }}:{{ port }}";
|
var rxml = "rec.php";
|
||||||
var vlon = 11.0;
|
var tld = "http://{{ host }}";
|
||||||
var vlat = 48.0;
|
var vlon = 5;
|
||||||
var vlatmin = 40.0;
|
var vlat = 45;
|
||||||
var vlonmin = 10.0;
|
var bound = false;
|
||||||
var vlatmax = 60.0;
|
var boundc = '';
|
||||||
var vlonmax = 20.0;
|
var amax = 85;
|
||||||
var bound = false;
|
var amin = -85;
|
||||||
var boundc = '';
|
var omax = 180;
|
||||||
var amax = 85;
|
var omin = -180;
|
||||||
var amin = -85;
|
var recc = "";
|
||||||
var omax = 180;
|
var parc = "";
|
||||||
var omin = -180;
|
var tz;
|
||||||
var recc = "";
|
try {
|
||||||
var parc = "";
|
|
||||||
var tz;
|
|
||||||
try {
|
|
||||||
tz = new Date().getTimezoneOffset();
|
tz = new Date().getTimezoneOffset();
|
||||||
} catch (e) {
|
}
|
||||||
|
catch(e) {
|
||||||
tz = 0;
|
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>
|
</head>
|
||||||
|
|
||||||
<body onload="initialize()">
|
<body onload="initialize()">
|
||||||
<div id="popup" onclick="cp('popup');"></div>
|
<div id="popup" onclick="cp('popup');"></div>
|
||||||
<div id="map_canvas"></div>
|
<div id="map_canvas"></div>
|
||||||
<div id="ac" class="acright" onclick="this.style.display='none';"></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="dlist" class="lright">
|
||||||
<DIV id="ett1"></DIV>
|
<DIV id="ett1" ></DIV>
|
||||||
<DIV id="ett2"></DIV>
|
<DIV id="ett2" ></DIV>
|
||||||
<DIV id="dtable"></DIV>
|
<DIV id="dtable"></DIV>
|
||||||
</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>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
Ładowanie…
Reference in New Issue