From a6ff3d42f0d2d53418e0de3e75f7f3cde11ea881 Mon Sep 17 00:00:00 2001 From: Chandler Swift Date: Mon, 29 Jan 2024 02:24:53 -0600 Subject: [PATCH] Add 511MN cameras layer --- layers/511mn/layer.js | 24 +++++++++++++ layers/511mn/pin.svg | 21 ++++++++++++ layers/511mn/process_data.py | 66 ++++++++++++++++++++++++++++++++++++ layers/511mn/query.graphql | 40 ++++++++++++++++++++++ layers/index.js | 5 +++ 5 files changed, 156 insertions(+) create mode 100644 layers/511mn/layer.js create mode 100644 layers/511mn/pin.svg create mode 100644 layers/511mn/process_data.py create mode 100644 layers/511mn/query.graphql diff --git a/layers/511mn/layer.js b/layers/511mn/layer.js new file mode 100644 index 0000000..af140b4 --- /dev/null +++ b/layers/511mn/layer.js @@ -0,0 +1,24 @@ +import VectorLayer from 'ol/layer/Vector'; +import {Vector as VectorSource} from 'ol/source.js'; +import GeoJSON from 'ol/format/GeoJSON.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, + }), + }), +}); + +export default vectorLayer; diff --git a/layers/511mn/pin.svg b/layers/511mn/pin.svg new file mode 100644 index 0000000..fb93212 --- /dev/null +++ b/layers/511mn/pin.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/layers/511mn/process_data.py b/layers/511mn/process_data.py new file mode 100644 index 0000000..4d3bf3e --- /dev/null +++ b/layers/511mn/process_data.py @@ -0,0 +1,66 @@ +#!/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":53.23294, + "south":40.40589, + "east":-78.6823, + "west":-107.24675, + "zoom":9, + "layerSlugs": ["normalCameras"], + "nonClusterableUris": ["dashboard"] + }, + "plowType":"plowCameras", + } + } +] + +res = requests.post('https://511mn.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 [\d]* cameras", c['tooltip']): + raise Exception(f"Not zoomed in enough! Finding aggregate cameras: {c}") + + 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/511mn/query.graphql b/layers/511mn/query.graphql new file mode 100644 index 0000000..d032daa --- /dev/null +++ b/layers/511mn/query.graphql @@ -0,0 +1,40 @@ +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 + } + category + } + } + ... on Plow { + views(limit: 5, plowType: $plowType) { + uri + ... on PlowCameraView { + url + } + category + } + } + } + error { + message + type + } + } +} diff --git a/layers/index.js b/layers/index.js index 4ae7f7b..95999b4 100644 --- a/layers/index.js +++ b/layers/index.js @@ -14,6 +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 _511mncamerasLayer from './511mn/layer.js'; const layerCategories = [ { // Base maps @@ -75,6 +76,10 @@ const layerCategories = [ name: "Bikepacking.com Routes", layer: bikepackingLayer, }, + { + name: "511MN Cameras", + layer: _511mncamerasLayer, + } ] }, trips,