Genericize Wisconsin's solution
This commit is contained in:
parent
d4282a6eae
commit
fd3fd0b105
|
@ -10,8 +10,6 @@ with open('states.json') as f:
|
||||||
with open("query.graphql") as f:
|
with open("query.graphql") as f:
|
||||||
QUERY = f.read()
|
QUERY = f.read()
|
||||||
|
|
||||||
data = {}
|
|
||||||
|
|
||||||
for state, baseURL in states.items():
|
for state, baseURL in states.items():
|
||||||
PAYLOAD = [
|
PAYLOAD = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
import al from './al/layer.js';
|
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 castlerocklayers from './castle-rock/index.js';
|
||||||
|
import travelIqLayers from './travel-iq/index.js';
|
||||||
|
|
||||||
const dot_cams = {
|
const dot_cams = {
|
||||||
name: "State DOT Cameras",
|
name: "State DOT Cameras",
|
||||||
layers: [
|
layers: [
|
||||||
...castlerocklayers,
|
...castlerocklayers,
|
||||||
|
...travelIqLayers,
|
||||||
{
|
{
|
||||||
name: "Alabama: ALDOT/ALGO",
|
name: "Alabama: ALDOT/ALGO",
|
||||||
layer: al,
|
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>`,
|
details: `<a href="#" onclick="this.closest('details').querySelectorAll('ul li input').forEach(e => e.checked || e.click()); return false;">Enable All</a>`,
|
||||||
};
|
};
|
||||||
|
|
92
layers/dot-cams/travel-iq/get_data.py
Executable file
92
layers/dot-cams/travel-iq/get_data.py
Executable 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")
|
60
layers/dot-cams/travel-iq/index.js
Normal file
60
layers/dot-cams/travel-iq/index.js
Normal 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;
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
3
layers/dot-cams/travel-iq/states.json
Normal file
3
layers/dot-cams/travel-iq/states.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"wi": "https://511wi.gov/"
|
||||||
|
}
|
|
@ -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")
|
|
|
@ -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;
|
|
Loading…
Reference in a new issue