diff --git a/layers/dot-cams/ia/layer.js b/layers/511mn/layer.js similarity index 91% rename from layers/dot-cams/ia/layer.js rename to layers/511mn/layer.js index bf985d2..0f7cfd7 100644 --- a/layers/dot-cams/ia/layer.js +++ b/layers/511mn/layer.js @@ -39,7 +39,9 @@ vectorLayer.customPopupCallback = function(feature) { if (view.category.toLowerCase() == "video") { const video = document.getElementById('popupVideo'); - const videoSrc = view.sources[0].src; + const videoID = view.url.split('/').pop(); + + const videoSrc = `https://video.dot.state.mn.us/public/${videoID}.stream/playlist.m3u8`; if (Hls.isSupported()) { var hls = new Hls(); hls.loadSource(videoSrc); diff --git a/layers/dot-cams/ia/pin.svg b/layers/511mn/pin.svg similarity index 100% rename from layers/dot-cams/ia/pin.svg rename to layers/511mn/pin.svg diff --git a/layers/dot-cams/mn/get_data.py b/layers/511mn/process_data.py old mode 100755 new mode 100644 similarity index 78% rename from layers/dot-cams/mn/get_data.py rename to layers/511mn/process_data.py index 285171d..4d3bf3e --- a/layers/dot-cams/mn/get_data.py +++ b/layers/511mn/process_data.py @@ -40,17 +40,9 @@ for c in camera_views: print(c) raise Exception(f"Unexpected number of features: {len(c['features'])}") - if re.match(r"Show .* cameras", c['tooltip']): + if re.match(r"Show [\d]* cameras", c['tooltip']): raise Exception(f"Not zoomed in enough! Finding aggregate cameras: {c}") - for view in c['views']: - if len(view['sources']) != 1 if view['category'] == 'VIDEO' else 0: - print(view) - raise Exception(f"Unexpected number of sources ({len(view['sources'])})") - for source in view['sources'] or []: - if source['type'] != 'application/x-mpegURL': - raise Exception(f"Unexpected type {source['type']}") - viewCount += len(c['views']) cameras.append({ "type": "Feature", diff --git a/layers/dot-cams/mn/query.graphql b/layers/511mn/query.graphql similarity index 88% rename from layers/dot-cams/mn/query.graphql rename to layers/511mn/query.graphql index 8c53b14..d032daa 100644 --- a/layers/dot-cams/mn/query.graphql +++ b/layers/511mn/query.graphql @@ -18,11 +18,6 @@ query MapFeatures($input: MapFeaturesArgs!, $plowType: String) { uri ... on CameraView { url - sources { - type - src - } - title } category } diff --git a/layers/dot-cams/ia/get_data.py b/layers/dot-cams/ia/get_data.py deleted file mode 100755 index fad76c5..0000000 --- a/layers/dot-cams/ia/get_data.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 - -import requests -import json -import re - -with open("query.graphql") as f: - QUERY = f.read() - -PAYLOAD = [ - { - "query": QUERY, - "variables": { - "input": { - # Cover the whole state (this is pretty overkill, admittedly) - "north":45.2, - "south":38.2, - "east":-82.9, - "west":-98.3, - "zoom":11, - "layerSlugs": ["normalCameras"], - "nonClusterableUris": ["dashboard"] - }, - "plowType":"plowCameras", - } - } -] - -res = requests.post('https://511ia.org/api/graphql', json=PAYLOAD) -res.raise_for_status() - -camera_views = res.json()[0]['data']['mapFeaturesQuery']['mapFeatures'] - -cameras = [] - -viewCount = 0 - -for c in camera_views: - if len(c['features']) != 1: - print(c) - raise Exception(f"Unexpected number of features: {len(c['features'])}") - - if re.match(r"Show .* cameras", c['tooltip']): - raise Exception(f"Not zoomed in enough! Finding aggregate cameras: {c}") - - for view in c['views']: - if len(view['sources']) != 1 if view['category'] == 'VIDEO' else 0: - print(view) - raise Exception(f"Unexpected number of sources ({len(view['sources'])})") - for source in view['sources'] or []: - if source['type'] != 'application/x-mpegURL': - raise Exception(f"Unexpected type {source['type']}") - - viewCount += len(c['views']) - cameras.append({ - "type": "Feature", - "geometry": c['features'][0]['geometry'], - "properties": { - 'address': c['tooltip'], - 'website': c['views'][0]['url'], - 'originalData': c, - }, - }) - -geojson = { - "type": "FeatureCollection", - "features": cameras, -} - -with open("data.geojson", "w") as f: - f.write(json.dumps(geojson)) - -print(f"{len(cameras)} locations found") -print(f"{viewCount} total views") diff --git a/layers/dot-cams/ia/query.graphql b/layers/dot-cams/ia/query.graphql deleted file mode 100644 index 8c53b14..0000000 --- a/layers/dot-cams/ia/query.graphql +++ /dev/null @@ -1,45 +0,0 @@ -query MapFeatures($input: MapFeaturesArgs!, $plowType: String) { - mapFeaturesQuery(input: $input) { - mapFeatures { - bbox - tooltip - uri - features { - id - geometry - properties - } - ... on Event { - priority - } - __typename - ... on Camera { - views(limit: 5) { - uri - ... on CameraView { - url - sources { - type - src - } - title - } - category - } - } - ... on Plow { - views(limit: 5, plowType: $plowType) { - uri - ... on PlowCameraView { - url - } - category - } - } - } - error { - message - type - } - } -} diff --git a/layers/dot-cams/index.js b/layers/dot-cams/index.js deleted file mode 100644 index ba1a093..0000000 --- a/layers/dot-cams/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import mn from './mn/layer.js'; -import wi from './wi/layer.js'; -import ia from './ia/layer.js'; - -const dot_cams = { - name: "State DOT Cameras", - layers: [ - { - name: "MNDOT/511MN", - layer: mn, - }, - { - name: "WisDOT/511WI", - layer: wi, - }, - { - name: "Iowa DOT/511IA", - layer: ia, - }, - ], -}; - -export default dot_cams; diff --git a/layers/dot-cams/mn/layer.js b/layers/dot-cams/mn/layer.js deleted file mode 100644 index bf985d2..0000000 --- a/layers/dot-cams/mn/layer.js +++ /dev/null @@ -1,55 +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) { - const view = feature.values_.originalData.views[0]; - if (view.category.toLowerCase() == "video") { - return ``; - } else if (view.category.toLowerCase() == "image") { - return ``; - } else { - throw new Exception(`unknown category ${view.category}`); - } -}; - -vectorLayer.customPopupCallback = function(feature) { - const view = feature.values_.originalData.views[0]; - if (view.category.toLowerCase() == "video") { - const video = document.getElementById('popupVideo'); - - const videoSrc = view.sources[0].src; - 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; diff --git a/layers/dot-cams/mn/pin.svg b/layers/dot-cams/mn/pin.svg deleted file mode 100644 index fb93212..0000000 --- a/layers/dot-cams/mn/pin.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - diff --git a/layers/dot-cams/wi/get_data.py b/layers/dot-cams/wi/get_data.py deleted file mode 100644 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; diff --git a/layers/dot-cams/wi/pin.svg b/layers/dot-cams/wi/pin.svg deleted file mode 100644 index fb93212..0000000 --- a/layers/dot-cams/wi/pin.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - diff --git a/layers/index.js b/layers/index.js index 109133c..95999b4 100644 --- a/layers/index.js +++ b/layers/index.js @@ -14,7 +14,7 @@ import cellular from './cellular.js'; import light_pollution from './light_pollution.js'; import state_land from './state-land/index.js'; import trips from './trips/index.js'; -import dot_cams from './dot-cams/index.js'; +import _511mncamerasLayer from './511mn/layer.js'; const layerCategories = [ { // Base maps @@ -76,9 +76,12 @@ const layerCategories = [ name: "Bikepacking.com Routes", layer: bikepackingLayer, }, + { + name: "511MN Cameras", + layer: _511mncamerasLayer, + } ] }, - dot_cams, trips, chains, census_bureau,