From 33ee9bf23e0d33fd0ef7aa061c52e0a6efaa4471 Mon Sep 17 00:00:00 2001 From: Said Turksever Date: Fri, 25 Sep 2026 18:37:42 +0100 Subject: [PATCH] docs(street-level): README, DATA_SOURCES, CURRENT-STATE, SECURITY, CHANGELOG Describe the provider-neutral layer, the PROVIDERS chip row, the shared filter and its share-link options, and how a new provider registers. Drop every mention of the removed plain-English query and Anthropic key. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 2 +- DATA_SOURCES.md | 3 +-- README.md | 20 ++++++++++---------- SECURITY.md | 2 +- docs/CURRENT-STATE.md | 44 ++++++++++++++++++++++++++++++------------- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a05fcf16..d2d462031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -- Street Level (Mapillary): a new layer draws Mapillary coverage on the globe from orbit down to a single street, opens photos and 360° panoramas in an embedded MapillaryJS viewer beside the CCTV panel, and answers plain-English questions ("show me all fire hydrants in Sacramento") with Mapillary's detected objects and traffic signs when an Anthropic key is present; the layer needs a free Mapillary client token and credits CC BY-SA 4.0 imagery on the globe. +- Street Level: a provider-neutral street-level imagery layer modelled on the iD editor's photo overlay. One panel in the right rail carries a chip per imagery provider, shared 360°/flat and captured-since filters, coverage drawn on the globe from orbit down to a single street, image cones per sequence, and one embedded viewer whose adapter follows the provider of the open image; each active provider credits its imagery on the globe, and share links carry the provider switches and the filter. Mapillary is the first provider (free client token; CC BY-SA 4.0 imagery); Google Street View, KartaView and Panoramax are next. - CCTV cameras whose bearing is a guess now say so. Packs mark bearings derived from a hash of the camera id as `headingConfidence: 'low'`, but nothing read the diff --git a/DATA_SOURCES.md b/DATA_SOURCES.md index 831b283b4..dae310cb3 100644 --- a/DATA_SOURCES.md +++ b/DATA_SOURCES.md @@ -21,8 +21,7 @@ How to read this: | **NOAA nowCOAST lightning density** (`nowcoast.noaa.gov/geoserver/observations/lightning_detection/ows`) | Lightning density: 15-minute accumulated strike density on an approximately 8 km grid, Americas and Pacific. Keyless; layer metadata refreshed every 10 minutes, exact-time images cached for up to 24 hours | NOAA Level-5 derived product from Vaisala NLDN/GLD360; the [product description](https://ocean.weather.gov/lightning/lightning_pdd.php) permits public distribution of this density product, not of raw Vaisala detections | "NOAA/NWS nowCOAST; derived from Vaisala NLDN/GLD360" | | **NOAA NHC / CPHC advisories** (`www.nhc.noaa.gov/CurrentStorms.json` + the `mapservices.weather.noaa.gov` tropical weather summary MapServer) | Cyclone advisories: storm positions, forecast tracks, points and cones for the Atlantic and eastern/central North Pacific. Keyless through `/api/cyclones`; cached for 5 minutes | [NWS public-data terms](https://www.weather.gov/disclaimer); no endorsement implied | "NOAA/NWS NHC / CPHC" | | **OpenStreetMap ALPR camera locations** (including DeFlock community mapping) | Optional mapped automatic license-plate-reader camera layer | [ODbL 1.0](https://opendatacommons.org/licenses/odbl/1-0/); commercial use permitted with applicable attribution and database share-alike obligations | [© OpenStreetMap contributors](https://www.openstreetmap.org/copyright); [DeFlock](https://deflock.org) community mapping | -| **Mapillary** (vector tiles, Graph API, MapillaryJS) | Street Level layer: coverage, image cones, embedded viewer, and the map features / traffic signs behind the plain-English query box | Imagery and derived data [CC BY-SA 4.0](https://www.mapillary.com/terms); API use under the Mapillary Platform Terms with a client token; tiles are cached locally for 24 h | "Imagery © Mapillary contributors, CC BY-SA 4.0" — shown on the globe while the layer is on, and by the embedded viewer's attribution component | -| **mapillary_sprite_source** (icons) | Map-feature icons on the globe | [MIT](https://github.com/mapillary/mapillary_sprite_source/blob/master/LICENSE) | Copyright (c) 2015 mapillary | +| **Mapillary** (vector tiles, Graph API, MapillaryJS) | Street Level layer, Mapillary provider: coverage lines, image cones and the embedded viewer | Imagery and derived data [CC BY-SA 4.0](https://www.mapillary.com/terms); API use under the Mapillary Platform Terms with a client token; coverage tiles are cached locally for 24 h | "Street Level: imagery © Mapillary contributors, CC BY-SA 4.0" — shown on the globe while the provider is on, and by the embedded viewer's attribution component | | **Google Map Tiles API** (Photorealistic 3D Tiles) + Places/Geocoding | The 3D globe, voice scene context, and on-demand nearby installation search | Google Maps Platform ToS (proprietary, your own key + billing) | "Google" / "Google Maps" logo — **shown in-app**, required | | **OpenSky Network** | Primary worldwide live-flight snapshot | Non-commercial research/education license | Schäfer et al., _"Bringing Up OpenSky"_, IPSN 2014 + opensky-network.org | | **adsb.lol point API** | Bounded live-flight fallback when OpenSky has no usable snapshot | ODbL 1.0 | adsb.lol contributors; `api.adsb.lol/v2/lat/{lat}/lon/{lon}/dist/{radius}` | diff --git a/README.md b/README.md index 59495b637..7810ca83c 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ Twenty layers and map sources. **Seventeen have a keyless path.** Some offer add | 🚗 **Traffic** | Simulated vehicles on OSM roads. With TomTom, live flow speeds drive the simulation and congestion colors below ~8 km; individual vehicle positions are not live observations | TomTom + OSM | 🟢 simulation · 🟡 live flow speeds | | 📹 **CCTV Mesh** | ~3,600 public cameras projected _into_ the 3D space — Austin · Texas (TxDOT) · California (Caltrans) · London (TfL) · Ontario (511) · Finland (Fintraffic) · British Columbia (DriveBC) · Estonia (Tallinn, Tarktee) · Delaware (DelDOT live video) · New South Wales (Live Traffic NSW) · Calgary. Positions are published; poses are estimated priors **you calibrate by dragging a gizmo on the camera itself** | City APIs | 🟢 | | 📷 **Mapped ALPR Cameras** | License-plate-reader camera locations tagged by OpenStreetMap contributors, loaded one city-sized view at a time, with **SHOW NEAREST**. Locations and tags only: no plate data, no video | OpenStreetMap (incl. DeFlock mapping) | 🟢 | -| 📷 **Street Level** | Mapillary coverage drawn onto the streets from orbit down to a single road, image cones per sequence, an embedded MapillaryJS viewer with a **CAMERA FOLLOWS VIEW** mode, and a plain-English query box (_"show me all fire hydrants in Sacramento"_) that streams Mapillary's detected objects and traffic signs onto the globe with Mapillary's own icons | Mapillary (+ Anthropic for the query box) | 🟡 Mapillary client token · 🔴 Anthropic key for plain-English queries only | +| 📷 **Street Level** | Street-level imagery from several providers behind one panel, modelled on the iD editor's photo overlay: a chip per provider, shared 360°/flat and captured-since filters, coverage drawn onto the streets from orbit down to a single road, image cones per sequence, and an embedded viewer with a **CAMERA FOLLOWS VIEW** mode. Mapillary ships in this build; Google Street View, KartaView and Panoramax are planned | Mapillary | 🟡 Mapillary client token | | 📻 **Radio** | Geolocated world radio with an **analog tuner** — drag the needle across up to 750 stations and the globe flies to each broadcaster | Radio Browser / broadcasters | 🟢 | | 🚌 **Transit** | Live buses, trams, metros, trains and ferries with delayed playback between reports, selected-vehicle trails, and mode-coloured DETECT labels — Boston, Austin, Minneapolis, Helsinki, the Netherlands, Norway, South East Queensland | Operator GTFS-Realtime feeds | 🟢 | | 🚲 **Bikeshare** | Live station availability | GBFS | 🟢 | @@ -326,18 +326,19 @@ _The Space Missions layer replaying a Falcon 9 ascent — labeled `RECONSTRUCTED --- -## 📷 Street Level (Mapillary) +## 📷 Street Level -> Needs a free **Mapillary client token**; the plain-English query box additionally needs an **Anthropic key**. Both go in POWER UP. +> One panel for street-level imagery from any provider. This build registers **Mapillary**, which needs a free **Mapillary client token** in POWER UP; Google Street View, KartaView and Panoramax are the next providers. -**Getting the keys** +Setup for Mapillary: -1. **Mapillary client token (required, free).** Sign in at [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers), choose **Register application**, give it any name and website, tick **Read** access, and copy the **Client Token** (it starts with `MLY|`). Paste it into **POWER UP → Mapillary**, or set `MAPILLARY_CLIENT_TOKEN` in `.env` and restart `npm run dev`. -2. **Anthropic key (optional).** Create one at [console.anthropic.com](https://console.anthropic.com/settings/keys) and add it in **POWER UP → Anthropic** or as `ANTHROPIC_API_KEY` in `.env`. It stays on the server; only the plain-English query box uses it. +1. **Mapillary client token (free).** Sign in at [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers), choose **Register application**, give it any name and website, tick **Read** access, and copy the **Client Token** (it starts with `MLY|`). Paste it into **POWER UP → Mapillary**, or set `MAPILLARY_CLIENT_TOKEN` in `.env` and restart `npm run dev`. -Without the Mapillary token the STREET LEVEL panel reads **KEY REQUIRED** and its controls stay greyed out; without the Anthropic key everything except the query box works. +Without the token the STREET LEVEL panel reads **KEY REQUIRED**, the Mapillary chip turns amber and the controls stay greyed out. -Turn on **Street Level (Mapillary)** under DATA LAYERS → Cameras, or open the STREET LEVEL strip in the right rail. Mapillary's coverage draws as ground-hugging lines (recent captures bright green, older ones dim, 360° panoramas magenta); click a line for its image cones and a cone to open the photo in the embedded MapillaryJS viewer. **EXPAND** grows the viewer to most of the screen and **CAMERA FOLLOWS VIEW** drives the globe camera from the panorama. With an Anthropic key, type a request such as _"stop signs in downtown Detroit"_ and the matching detections stream onto the globe tile by tile with Mapillary's own icons; click one to see the photo with the detection outlined. Mapillary blurs faces and licence plates before publishing imagery, and the globe credits "© Mapillary contributors, CC BY-SA 4.0" while the layer is on. +Turn on **Street Level** under DATA LAYERS → Cameras, or open the STREET LEVEL strip in the right rail. The **PROVIDERS** row has one chip per registered provider; switch a provider off and its coverage, its on-globe credit and its share-link bit go with it. The **IMAGERY** filter (ALL / 360° / FLAT) and the **SINCE** window apply to every provider at once. Mapillary's coverage draws as ground-hugging lines (recent captures bright green, older ones dim, 360° panoramas magenta); click a line for its image cones and a cone to open the photo in the embedded viewer, or press **OPEN NEAREST PHOTO** for the closest image to the view centre. **EXPAND** grows the viewer to most of the screen and **CAMERA FOLLOWS VIEW** drives the globe camera from the panorama; the caption links to the image on the provider's own site. Mapillary blurs faces and licence plates before publishing imagery, and the globe credits "© Mapillary contributors, CC BY-SA 4.0" while the provider is on. + +Share links carry the layer's provider switches and filter (`3.m.0` turns Mapillary off, `3.p.p` keeps only panoramas, `3.s.365` shows the last year). Adding a provider means implementing the contract in `src/layers/streetLevel/registry.js` and registering it in `src/app/layers/streetLevel.js`; the chip, credit and share bit follow. ## 🎖️ Field Missions @@ -430,8 +431,7 @@ Eight keys. Five have a free tier, and the three 🔴 ones are metered: | 🟡 | **AISStream** | 🚢 Live global ships | [aisstream.io](https://aisstream.io) — free signup | | 🟡 | **NASA FIRMS** | 🔥 Live active fires | [firms.modaps.eosdis.nasa.gov](https://firms.modaps.eosdis.nasa.gov/api/map_key/) — free | | 🟡 | **TomTom** | 🚦 Live flow speeds and congestion colors for the simulated traffic layer | [developer.tomtom.com](https://developer.tomtom.com) — free tier available | -| 🟡 | **Mapillary** | 📷 Street-level coverage, photos and detected objects on the globe. A public client token by design (see SECURITY.md) | [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers) — register an app, copy its Client Token | -| 🔴 | **Anthropic** | 💬 Plain-English questions in the Street Level panel, turned into Mapillary queries server-side. Coverage, photos and filters work without it | [console.anthropic.com](https://console.anthropic.com/settings/keys) — metered, roughly a cent per question | +| 🟡 | **Mapillary** | 📷 Street-level coverage and photos on the globe (the Street Level layer's Mapillary provider). A public client token by design (see SECURITY.md) | [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers) — register an app, copy its Client Token | ![Diving from city-scale live congestion straight into an intersection's public camera](docs/media/05-traffic-to-cctv.gif) diff --git a/SECURITY.md b/SECURITY.md index 2494b9c6a..6342cfeca 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -28,7 +28,7 @@ These are designed to be used directly in the browser (like a Mapbox public toke 1. **Google Maps API key** — loads Photorealistic 3D Tiles directly and powers GEV place search. **Restrict it** (HTTP referrer + API restriction to the required Google APIs) in the Google Cloud Console. An unrestricted key in a public deployment can be abused and billed to you. 2. **Cesium ion token** (`CESIUM_ION_TOKEN`, optional — for ion-hosted Google Photorealistic 3D Tiles, Bing world imagery, and world terrain) — used as `Cesium.Ion.defaultAccessToken` client-side. Use a public **`assets:read`** token with **URL restrictions** for any hosted deployment. The Community plan has eligibility and usage limits; a public token is not a secret, but it can still consume the account's quota. -3. **Mapillary client token** (`MAPILLARY_CLIENT_TOKEN`, optional — street-level imagery, coverage tiles and the plain-English query box) — used by the embedded MapillaryJS viewer and for Graph API lookups client-side. Mapillary client tokens are designed for browser use and can be scoped per application in the Mapillary developer dashboard; the dev server uses the same token for cached vector-tile fetches. The Anthropic key behind the query box never leaves the server. +3. **Mapillary client token** (`MAPILLARY_CLIENT_TOKEN`, optional — the Street Level layer's Mapillary provider: street-level imagery and coverage tiles) — used by the embedded MapillaryJS viewer and for Graph API lookups client-side. Mapillary client tokens are designed for browser use and can be scoped per application in the Mapillary developer dashboard; the dev server uses the same token for cached coverage-tile fetches. > The explicit browser `define` block in `build/vite.js` controls exactly what reaches the client: only these three keys. Everything else stays server-side. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 0bf229095..1e1f5488b 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -2766,24 +2766,42 @@ its criteria cannot be silently ignored. | Dams ▰ | OpenInfraMap/OSM extract (bundled) | `src/data/localLayers.js` | — | static | | Submarine Cables ◠ | TeleGeography public map (bundled) | `src/data/telegeographySubmarineCables.js` | — | static | | FIRMS Active Fires ▲ | NASA FIRMS live (VIIRS ×3 NRT + MODIS NRT, trailing 24h) | `src/data/firmsHeatmap.js` | `/api/firms` (`FIRMS_MAP_KEY`) | 10 min (proxy TTL 30 min) | -| Street Level 📷 | Mapillary vector tiles + Graph API + MapillaryJS (optional Claude planner, server-side key) | `src/layers/mapillary/` via `src/app/layers/mapillary.js` | `/api/mapillary/status`, `/tiles`, `/features` (NDJSON), `/plan`, `/sprite` (`MAPILLARY_CLIENT_TOKEN`; `ANTHROPIC_API_KEY` optional) | camera-driven (320 ms debounce, ≤9 coverage tiles); queries on demand (≤600 tiles, cached 24 h) | +| Street Level 📷 | Provider-neutral street-level imagery; Mapillary registered (vector tiles + Graph API + MapillaryJS) | `src/layers/streetLevel/` (core) + `src/layers/streetLevel/providers/mapillary/` via `src/app/layers/streetLevel.js` | `/api/mapillary/status`, `/api/mapillary/tiles/coverage/{z}/{x}/{y}` (`MAPILLARY_CLIENT_TOKEN`) | camera-driven (320 ms debounce, ≤9 coverage tiles, cached 24 h) | | Wind 🌬 | NOAA GFS 10 m wind (keyless, 0.25°→1° grid; animated particles) | `src/data/wind.js` | `/api/wind` | 1 h (forecast cycle) | | Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) | Fire Perimeters uses capped, timed server reads with stale-on-error caching and a per-client limit. Unchanged snapshots retain geometry; link checks cancel on disable or selection change, and the row legend shows reported containment. -Street Level (Mapillary) lives in the right context rail next to CCTV as an -ordinary collapsible panel and starts collapsed. Without `MAPILLARY_CLIENT_TOKEN` -the header status reads KEY REQUIRED and the controls stay disabled (key setup -is documented in the README's Street Level section); with it, coverage -draws as ground-clamped sequence lines (z0–5 overview points from orbit, -z11–14 sequences below 60 km) and the proxy strips the unused `image` point -layer from z14 tiles in transit (12 MB → ~80 KB). The plain-English query box -is only live with `ANTHROPIC_API_KEY` (server-side; the browser posts the -sentence to `/api/mapillary/plan`). While enabled the layer adds an on-globe -Cesium credit ("Imagery © Mapillary contributors, CC BY-SA 4.0"). Follow-ups -kept out of the first landing: procedural 3D objects for results, a Google 3D -map-stack shortcut, and portable/resizable rail panels. +Street Level lives in the right context rail next to CCTV as an ordinary +collapsible panel (`#street-level-panel`, share token `3`, option owner +`street-level`) and starts collapsed. It is modelled on the iD editor's photo +overlay: one panel, a PROVIDERS chip per registered imagery provider, shared +360°/flat and captured-since filters (stored as relative days so a link keeps +its meaning), one viewer host, and one on-globe credit per active provider. +Share options: `m` (Mapillary on/off), `p` (a/p/f panorama mode), `s` +(since, days). Only Mapillary is registered in this build. Without +`MAPILLARY_CLIENT_TOKEN` the header reads KEY REQUIRED, the Mapillary chip +turns amber and the controls stay disabled; with it, coverage draws as +ground-clamped sequence lines (z0–5 overview points from orbit, z11–14 +sequences below 60 km) and the proxy strips the unused `image` point layer +from z14 tiles in transit (12 MB → ~80 KB). + +Street-level providers implement the contract documented in +`src/layers/streetLevel/registry.js`: a definition (`id`, `name`, `label`, +`requiresKeyId`, `pickPrefix`, `colors`, `credit`, `capabilities`, `legend`, +`externalUrl`, `create`) whose `create(context)` returns an instance with +`status`, `init`/`activate`/`deactivate`/`destroy`, `refreshCoverage`, +`setFilter`, `coverageStats`, `handlePick`, optional sequence selection, +`nearestImage`, and a viewer adapter (`mount`/`open`/`close`/`unmount`/ +`resize`/`onPose`) that emits a provider-neutral pose. The core routes clicks +by pick prefix, swaps viewer adapters in the one host, adds and removes each +provider's Cesium credit, and fans the filter out to every provider. Adding a +provider: implement the definition, register it in +`src/app/layers/streetLevel.js`, add its boolean option to the `street-level` +group in `src/data/layerState.js` and its modules to +`scripts/package-boundaries.json`; the chip, credit and legend follow. Once a +keyless provider registers, `requiresKeyId` on the layer becomes null and the +key gate moves to the chips, by design. Directions is a keyless front end to the routing the voice agent already uses. Its row chips are the whole interface: DRIVE / WALK / BIKE pick the