diff --git a/layers/dot-cams/castle-rock/get_data.py b/layers/dot-cams/castle-rock/get_data.py index 6a4111e..4d68eb9 100755 --- a/layers/dot-cams/castle-rock/get_data.py +++ b/layers/dot-cams/castle-rock/get_data.py @@ -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 = [ { diff --git a/layers/dot-cams/index.js b/layers/dot-cams/index.js index 7d16f40..736eac2 100644 --- a/layers/dot-cams/index.js +++ b/layers/dot-cams/index.js @@ -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: `Enable All`, }; diff --git a/layers/dot-cams/travel-iq/get_data.py b/layers/dot-cams/travel-iq/get_data.py new file mode 100755 index 0000000..bf66f2b --- /dev/null +++ b/layers/dot-cams/travel-iq/get_data.py @@ -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") diff --git a/layers/dot-cams/travel-iq/index.js b/layers/dot-cams/travel-iq/index.js new file mode 100644 index 0000000..5fd807b --- /dev/null +++ b/layers/dot-cams/travel-iq/index.js @@ -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 ``; + }; + + 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; diff --git a/layers/dot-cams/wi/pin.svg b/layers/dot-cams/travel-iq/pin.svg similarity index 100% rename from layers/dot-cams/wi/pin.svg rename to layers/dot-cams/travel-iq/pin.svg diff --git a/layers/dot-cams/travel-iq/states.json b/layers/dot-cams/travel-iq/states.json new file mode 100644 index 0000000..154736e --- /dev/null +++ b/layers/dot-cams/travel-iq/states.json @@ -0,0 +1,3 @@ +{ + "wi": "https://511wi.gov/" +} diff --git a/layers/dot-cams/wi/get_data.py b/layers/dot-cams/wi/get_data.py deleted file mode 100755 index c21fb45..0000000 --- a/layers/dot-cams/wi/get_data.py +++ /dev/null @@ -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") diff --git a/layers/dot-cams/wi/layer.js b/layers/dot-cams/wi/layer.js deleted file mode 100644 index 7a8a6e3..0000000 --- a/layers/dot-cams/wi/layer.js +++ /dev/null @@ -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 ``; -}; - -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;