Last active
June 27, 2022 16:58
-
-
Save k-nut/58692cdd339766806c22469922b36c5f to your computer and use it in GitHub Desktop.
CriticalTracks
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
import sqlite3 | |
import json | |
import sys | |
FILENAME = "./criticaltracks.sqlite" | |
def get_distane(point, other_point): | |
from math import sin, cos, sqrt, atan2, radians | |
# approximate radius of earth in m | |
R = 6373.0 * 1_000 | |
lon1, lat1 = [radians(x) for x in point["geometry"]["coordinates"]] | |
lon2, lat2 = [radians(x) for x in other_point["geometry"]["coordinates"]] | |
dlon = lon2 - lon1 | |
dlat = lat2 - lat1 | |
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2 | |
c = 2 * atan2(sqrt(a), sqrt(1 - a)) | |
return R * c | |
def may_show(point, points): | |
NEIGHBORS = 3 | |
DISTANCE = 100 | |
found = 0 | |
for candidate in points: | |
if get_distane(candidate, point) < DISTANCE: | |
found += 1 | |
if found == NEIGHBORS: | |
return True | |
return False | |
def create_point(key, values): | |
lat = values["latitude"] / 1_000_000 | |
lon = values["longitude"] / 1_000_000 | |
return { | |
"type": "Feature", | |
"geometry": {"type": "Point", "coordinates": [lon, lat]}, | |
} | |
def main(): | |
con = sqlite3.connect(FILENAME) | |
cur = con.cursor() | |
rows = [] | |
for row in cur.execute('select * from tracks'): | |
timestamp, data = row | |
print(timestamp, file=sys.stderr) | |
points = [create_point(key, value) for key, value in json.loads(data)['locations'].items()] | |
filtered_points = [point for point in points if may_show(point, points)] | |
if len(filtered_points): | |
rows.append({"timestamp": timestamp, "data": filtered_points}) | |
print(json.dumps(rows)) | |
if __name__ == "__main__": | |
main() |
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
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<script src='https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.js'></script> | |
<link href='https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.css' rel='stylesheet'/> | |
<body> | |
<div id='map'></div> | |
<div id="controls"> | |
<div> | |
<span id="time">17:55</span> | |
<button class="play">⏸</button> | |
</div> | |
<input type="range" id="timeselector" min="0" value=0> | |
</div> | |
<style> | |
body { | |
display: flex; | |
width: 98vw; | |
height: 98vh; | |
} | |
#map { | |
flex-grow: 1; | |
} | |
#controls { | |
font-size: 1.5em; | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
color: white; | |
font-family: sans-serif; | |
} | |
.play { | |
font-size: 1em; | |
background: none; | |
border: none; | |
outline: none; | |
padding: 0; | |
} | |
</style> | |
<script> | |
mapboxgl.accessToken = 'YOUR_TOKEN'; | |
const map = new mapboxgl.Map({ | |
container: 'map', // container ID | |
style: 'mapbox://styles/mapbox/dark-v10', // style URL | |
center: [13.4047, 52.5147], | |
zoom: 12 | |
}); | |
const animateRow = (row) => { | |
const data = { | |
"type": "FeatureCollection", | |
features: row | |
}; | |
map.getSource('bikes').setData(data) | |
}; | |
fetch('data.json') | |
.then(response => response.json()) | |
.then(json => { | |
map.on('load', () => { | |
map.addSource('bikes', { | |
'type': 'geojson', | |
data: { | |
"type": "FeatureCollection", | |
features: [] | |
} | |
}); | |
map.addLayer({ | |
'id': 'bikes', | |
'source': 'bikes', | |
'type': 'circle', | |
'paint': { | |
'circle-radius': 5, | |
'circle-color': 'rgba(241,202,17,0.69)' | |
} | |
}); | |
const input = document.querySelector('input') | |
input.max = json.length; | |
let interval; | |
const autoAnimate = () => { | |
interval = setInterval(() => { | |
if (input.value >= json.length){ | |
clearInterval(interval); | |
return | |
} | |
input.value++; | |
showBasedOnInput(); | |
}, 100); | |
} | |
const button = document.querySelector('.play'); | |
const startButton = () => { | |
button.innerText = '⏸' | |
autoAnimate(); | |
}; | |
const stopButton = () => { | |
button.innerText = '▶️' | |
clearInterval(interval) | |
} | |
button.onclick = () => { | |
const isPlaying = button.innerText === '⏸'; | |
if (isPlaying){ | |
stopButton(); | |
} else { | |
startButton(); | |
} | |
} | |
const display = (row) => { | |
const {timestamp, data} = row; | |
const time = new Date(timestamp.replace(" ", "T") + "Z").toLocaleTimeString().slice(0,5); | |
document.querySelector('#time').innerText = time; | |
animateRow(data); | |
} | |
const showBasedOnInput = () => { | |
display(json[input.value]); | |
} | |
input.addEventListener('input', () => { | |
stopButton(); | |
showBasedOnInput(); | |
}) | |
autoAnimate(); | |
}) | |
}) | |
</script> | |
</body> | |
</head> |
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
import urllib.request | |
import sqlite3 | |
from time import sleep | |
URL = 'https://api.criticalmaps.net/postv2' | |
FILENAME = "criticaltracks.sqlite" | |
def get_data(): | |
with urllib.request.urlopen(URL) as url: | |
return url.read().decode() | |
def add_row(cur, con, data: str): | |
cur.execute('insert into tracks (data) values(?);', (data,)) | |
con.commit() | |
def main(): | |
con = sqlite3.connect(FILENAME) | |
cur = con.cursor() | |
cur.execute('CREATE TABLE IF NOT EXISTS tracks (timestamp datetime default current_timestamp, data text);') | |
con.commit() | |
while True: | |
data = get_data() | |
add_row(cur, con, data) | |
sleep(5) | |
print(".", end="") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment