Last active
May 20, 2022 11:51
-
-
Save tkardi/f9cc6199713adf14f8525ec4494b6710 to your computer and use it in GitHub Desktop.
Code companion to 'Building a flightradar in Leaflet (pt I)'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Flask app to run the `proxy.py` script. | |
# Run it on the path where both of the files are saved with | |
# (on Linux, for other systems plese see | |
# http://flask.pocoo.org/docs/1.0/quickstart/#a-minimal-application) | |
# | |
# $ export FLASK_APP=flaskapp.py | |
# $ flask run | |
# | |
# This will make the API available at http://127.0.0.1:5000/flightradar | |
# | |
# The following Python code is published under the Unlicense. | |
# | |
# SPDX-License-Identifier: Unlicense # | |
import json | |
from flask import Flask | |
from flask import Response | |
from proxy import get_flight_radar_data | |
app = Flask(__name__) | |
@app.route('/flightradar') | |
def flightradar(): | |
return Response( | |
response=json.dumps(get_flight_radar_data()), | |
status=200, | |
mimetype='application/json' | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<!-- The following HTML page together with the accompanying | |
javascript is published under the Unlicense. | |
SPDX-License-Identifier: Unlicense | |
--> | |
<head> | |
<meta charset="utf-8"> | |
<title>Air traffic map</title> | |
<link | |
rel="stylesheet" | |
type="text/css" | |
href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.1/leaflet.css"/> | |
<script | |
src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.1/leaflet.js" | |
type="text/javascript"> | |
</script> | |
<script | |
src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-realtime/2.0.0/leaflet-realtime.min.js" | |
type="text/javascript"> | |
</script> | |
<style> | |
#map { | |
position: absolute; | |
top: 0; | |
left: 0; | |
bottom: 0; | |
right: 0; | |
} | |
.aeroplane-visible { | |
background: #109856; | |
border: none; | |
opacity: 1.0; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="map"> | |
</div> | |
<script> | |
// set up map | |
var center = [58.65, 25.06]; | |
var map = L.map('map').setView(center, 7); | |
// Stamen's Toner basemap | |
L.tileLayer( | |
'https://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', { | |
attribution: 'Map tiles by <a href="http://stamen.com">' + | |
'Stamen Design</a>, under' + | |
'<a href="http://creativecommons.org/licenses/by/3.0">' + | |
'CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">' + | |
'OpenStreetMap</a>, under' + | |
'<a href="http://www.openstreetmap.org/copyright">ODbL</a>.' | |
}).addTo(map); | |
// air traffic locations layer, | |
// should actually request when the "radar beam" is pointing N e.g | |
// but we'll go with L.Realtime for the moment. | |
var realtime = L.realtime('http://127.0.0.1:5000/flightradar', { | |
// interval of data refresh (in milliseconds) | |
interval: 10 * 1000, | |
getFeatureId: function(feature) { | |
// required for L.Realtime to track which feature is which | |
// over consecutive data requests. | |
return feature.id; | |
}, | |
pointToLayer: function(feature, latlng) { | |
// style the aeroplane loction markers with L.DivIcons | |
var marker = L.marker(latlng, { | |
icon: L.divIcon({ | |
className:'aeroplane-visible', | |
iconSize: [10,10] | |
}), | |
riseOnHover: true | |
}).bindTooltip( | |
// and as we're already here, bind a tooltip based on feature | |
// property values | |
'<b>{callsign}</b><br>Alt: {geo_altitude} m @ {velocity} m/s'.replace( | |
L.Util.templateRe, function (str, key) { | |
var value = feature.properties[key]; | |
if (value === undefined || value == null) { | |
value = 'N/A'; | |
} | |
return value; | |
}), | |
{ | |
permanent: false, opacity: 0.7} | |
); | |
return marker; | |
} | |
}).addTo(map); | |
map.attributionControl.addAttribution( | |
'<br>Marker animation: <a href="https://github.com/perliedman/leaflet-realtime">Leaflet Realtime</a>' | |
); | |
map.attributionControl.addAttribution( | |
'<br>Air traffic location data from <a href="http://www.opensky-network.org">The OpenSky Network</a>\'s public <a href="https://opensky-network.org/apidoc/">API</a>' | |
); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is the proxy.py script for grabbing realtime aircraft locations | |
# from the OpenSky Network's (https://opensky-network.org/) | |
# public API (https://opensky-network.org/apidoc/) and | |
# reformatting the data as GeoJSON. | |
# | |
# Run it as a flask app in your localhost | |
# | |
# The following Python code is published under the Unlicense. | |
# | |
# SPDX-License-Identifier: Unlicense # | |
import requests | |
from collections import OrderedDict | |
# note the use of bounding box coords in the URL | |
# this is roughly the area for Latvia, Estonia and | |
# a small part of southern Finland | |
url = 'https://opensky-network.org/api/states/all?lamin=57.48&lomin=21.6&lamax=59.82&lomax=28.52' | |
keys = [ | |
'icao24', 'callsign', 'origin_country', 'time_position', 'last_contact', | |
'longitude', 'latitude', 'baro_altitude', 'on_ground', 'velocity', | |
'true_track', 'vertical_rate', 'sensors', 'geo_altitude', 'squawk', | |
'spi', 'position_source' | |
] | |
def get_flight_radar_data(): | |
r = requests.get(url) | |
r.raise_for_status() | |
return to_geojson(r.json()) | |
def to_geojson(data): | |
f = [ | |
OrderedDict( | |
type='Feature', | |
id=ac[0], | |
geometry=OrderedDict(type='Point', coordinates=[ac[5],ac[6]]), | |
properties=OrderedDict(zip(keys, ac)) | |
) for ac in data.get('states', []) | |
] | |
return dict( | |
type='FeatureCollection', | |
features=f | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment