Genericize Wisconsin's solution

This commit is contained in:
Chandler Swift 2024-01-30 00:37:34 -06:00
parent d4282a6eae
commit fd3fd0b105
Signed by: chandlerswift
GPG key ID: A851D929D52FB93F
8 changed files with 158 additions and 127 deletions

View file

@ -10,8 +10,6 @@ with open('states.json') as f:
with open("query.graphql") as f:
QUERY = f.read()
data = {}
for state, baseURL in states.items():
PAYLOAD = [
{

View file

@ -1,20 +1,18 @@
import al from './al/layer.js';
import wi from './wi/layer.js';
import wi from './travel-iq/index.js';
import castlerocklayers from './castle-rock/index.js';
import travelIqLayers from './travel-iq/index.js';
const dot_cams = {
name: "State DOT Cameras",
layers: [
...castlerocklayers,
...travelIqLayers,
{
name: "Alabama: ALDOT/ALGO",
layer: al,
},
{
name: "WisDOT/511WI",
layer: wi,
},
],
details: `<a href="#" onclick="this.closest('details').querySelectorAll('ul li input').forEach(e => e.checked || e.click()); return false;">Enable All</a>`,
};

View file

@ -0,0 +1,92 @@
#!/usr/bin/python3
import requests
import json
with open('states.json') as f:
states = json.loads(f.read())
for state, baseURL in states.items():
query={
"columns": [ # no clue what any of this is, so here it stays
{
"data": None,
"name": "",
},
{
"name": "sortId",
"s": True,
},
{
"name": "region",
"s": True,
},
{
"name": "county",
"s": True,
},
{
"name": "roadway",
"s": True,
},
{
"name": "description1",
},
{
"data": 6,
"name": "",
},
],
"start": 0,
"length": 100,
}
cameras = []
available_cameras = 999_999 # lots
while len(cameras) < available_cameras:
res = requests.get(f"{baseURL}/List/GetData/Cameras", {
"query": json.dumps(query),
"lang": "en",
})
res.raise_for_status()
res = res.json()
available_cameras = res['recordsTotal']
for c in res['data']:
cameras.append({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [c['longitude'], c['latitude']], # yes, [lon, lat] since it's [x, y]
},
"properties": {
'address': c['displayName'],
'website': c['videoUrl'],
'originalData': c,
},
})
query['start'] += 100
geojson = {
"type": "FeatureCollection",
"features": cameras,
}
with open(f"data/{state}.geojson", "w") as f:
f.write(json.dumps(geojson))
print(f"{len(cameras)} locations found for {state}")
# hack hack hack
#
# If I write this to one big file, I can't take advantage of any lazy loading
# for performance reasons, so I'm constrained to having a bunch of files. I
# can't programmatically import those, since es6 imports don't allow for that.
# So, codegen it is (and fairly gross codegen at that!).
with open('data/states.js', 'w') as f:
for state in states:
f.write(f"import {state} from './{state}.geojson?url';\n")
f.write('\nexport default {\n')
for state in states:
f.write(f" {state}: {state},\n")
f.write("};\n")

View file

@ -0,0 +1,60 @@
import VectorLayer from 'ol/layer/Vector';
import {Vector as VectorSource} from 'ol/source.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import Hls from 'hls.js';
import {Style} from 'ol/style.js';
import Icon from 'ol/style/Icon.js';
import states from './data/states.js';
import pin from './pin.svg?url'; // TODO: remove `?url`?
// https://en.wikipedia.org/wiki/Department_of_transportation#List_of_U.S._state_and_insular_area_departments_of_transportation
const dot_names = {
wi: "WisDOT/511WI",
};
let vectorLayers = [];
for (let [state, url] of Object.entries(states)) {
const vectorLayer = new VectorLayer({
source: new VectorSource({
url: url,
format: new GeoJSON,
}),
style: new Style({
image: new Icon({
anchor: [0.5, 1],
src: pin,
}),
}),
});
vectorLayer.customPopup = function(feature) {
return `<video style="max-width: 80vw; max-height: 80vh;" id="popupVideo" autoplay controls></video>`;
};
vectorLayer.customPopupCallback = function(feature) {
const video = document.getElementById('popupVideo');
const videoSrc = feature.values_.originalData.videoUrl;
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
// iDevice support, untested (only works in Safari; required for iPhones)
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
}
vectorLayers.push({
name: dot_names[state] ?? state,
layer: vectorLayer,
});
}
export default vectorLayers;

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,3 @@
{
"wi": "https://511wi.gov/"
}

View file

@ -1,74 +0,0 @@
#!/usr/bin/python3
import requests
import json
query={
"columns": [ # no clue what any of this is, so here it stays
{
"data": None,
"name": "",
},
{
"name": "sortId",
"s": True,
},
{
"name": "region",
"s": True,
},
{
"name": "county",
"s": True,
},
{
"name": "roadway",
"s": True,
},
{
"name": "description1",
},
{
"data": 6,
"name": "",
},
],
"start": 0,
"length": 100,
}
cameras = []
available_cameras = 999_999 # lots
while len(cameras) < available_cameras:
res = requests.get("https://511wi.gov/List/GetData/Cameras", {
"query": json.dumps(query),
"lang": "en",
})
res.raise_for_status()
res = res.json()
available_cameras = res['recordsTotal']
for c in res['data']:
cameras.append({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [c['longitude'], c['latitude']], # yes, [lon, lat] since it's [x, y]
},
"properties": {
'address': c['displayName'],
'website': c['videoUrl'],
'originalData': c,
},
})
query['start'] += 100
geojson = {
"type": "FeatureCollection",
"features": cameras,
}
with open("data.geojson", "w") as f:
f.write(json.dumps(geojson))
print(f"{len(cameras)} locations found")

View file

@ -1,46 +0,0 @@
import VectorLayer from 'ol/layer/Vector';
import {Vector as VectorSource} from 'ol/source.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import Hls from 'hls.js';
import {Style} from 'ol/style.js';
import Icon from 'ol/style/Icon.js';
import url from './data.geojson?url'; // TODO: remove `?url`?
import pin from './pin.svg?url'; // TODO: remove `?url`?
const vectorLayer = new VectorLayer({
source: new VectorSource({
url: url,
format: new GeoJSON,
}),
style: new Style({
image: new Icon({
anchor: [0.5, 1],
src: pin,
}),
}),
});
vectorLayer.customPopup = function(feature) {
return `<video style="max-width: 80vw; max-height: 80vh;" id="popupVideo" autoplay controls></video>`;
};
vectorLayer.customPopupCallback = function(feature) {
const video = document.getElementById('popupVideo');
const videoSrc = feature.values_.originalData.videoUrl;
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
// iDevice support, untested (only works in Safari; required for iPhones)
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
}
export default vectorLayer;