diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3fd7822..58342354a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +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. - Distinguish PARTIAL vessel snapshots from STALE data in the layer panel, with accepted-record counts and unchanged retention, freshness and outage safeguards. diff --git a/DATA_SOURCES.md b/DATA_SOURCES.md index 850d27e39..5a86b0ad3 100644 --- a/DATA_SOURCES.md +++ b/DATA_SOURCES.md @@ -16,6 +16,8 @@ How to read this: | Source | Used for | License / terms | Attribution | | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **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 | | **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 28561c222..a43609780 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,6 @@ _Why cockpit mode exists: you're riding a real aircraft over real terrain — an ## 🎙️ Talk to It -> Voice needs an **OpenAI key**. Without one the entire app still runs — the mic button just reports voice is unavailable. Prefer typing? Set `GEV_VOICE_UI=off` in `.env` and the mic control disappears altogether; the typed **Mapillary AI** box below still works. The same key drives the **AI HUD summary**: a terse, five-word intelligence-style readout of the current view that regenerates as you move. Click **GEV MIC**, grant the microphone, and just talk. This is more than a voice-controlled remote: @@ -273,15 +272,13 @@ _Ask for radio near anywhere and the globe starts broadcasting — every station > Needs a free **Mapillary client token**; the AI query additionally needs an **Anthropic key**. Both go in POWER UP. -Turn on **Street Level** and Mapillary's coverage appears at every altitude: capture points from orbit, then the sequence web drawn onto the streets in green as you descend (magenta for 360° captures, dimmer for imagery older than two years). The **IMAGERY** filters narrow all of it to 360° or flat captures and to a capture date. Click a sequence for its image cones; click a cone, or press **LOOK HERE**, and the embedded MapillaryJS viewer opens inside the Mapillary dock. **STREET COCKPIT** locks the globe camera to the street-level pose, **⤢ EXPAND** grows the viewer to most of the screen (Esc shrinks it), and **GOOGLE 3D** switches the globe to Photorealistic 3D Tiles when a Google key or Cesium ion token is configured. The Mapillary dock and the CCTV panel are portable windows: drag a header to move one, pull the corner grip to resize, double-click the header to snap it back. -**Mapillary AI** is the typed box at the top of the dock. _"show me all fire hydrants in Sacramento"_ becomes a plan (place, taxonomy classes, time window, 360° preference), the place is geocoded, and the city is fanned out over z14 map-feature tiles that stream back as NDJSON — results fill in tile by tile from the centre outwards, first as dots, then as Mapillary's own sprite icons for every class. Follow-ups refine the previous plan: _"only the ones seen after 2024"_, _"same thing in Detroit"_. Click any result and it is ringed on the globe, the image that detected it opens, and the detection itself is outlined in the photo. **3D OBJECTS** stands result sets of up to 1,500 up as procedural models — signs as textured plates on poles facing Mapillary's aligned direction, hydrants, street lights, benches, traffic lights and more. The planner runs on Claude with a prompt-cached taxonomy of 43 object classes and 900 traffic-sign families; unknown classes are reported rather than guessed. Works for any place Nominatim can find, capped at 600 tiles per run. --- ## 🛰️ What's on the Globe -Fifteen layers and map sources. **Thirteen have a keyless path.** Some offer additional capabilities with a provider key. (🟢 no key · 🟡 free key · 🔴 metered.) +Sixteen layers and map sources. **Thirteen have a keyless path.** Some offer additional capabilities with a provider key. (🟢 no key · 🟡 free key · 🔴 metered.) | Layer | What you get | Source | Auth | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | @@ -293,7 +290,7 @@ Fifteen layers and map sources. **Thirteen have a keyless path.** Some offer add | 🌍 **Earthquakes** | Global seismic activity, last 24h | USGS | 🟢 | | 🚗 **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) · 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 | 🟢 | -| 📷 **Street Level** | Mapillary coverage drawn onto the streets, click a sequence for its image cones, open any image in the embedded MapillaryJS viewer, and **Street Cockpit** — the globe camera follows the street-level view. Ask **Mapillary AI** in plain English (_"show me all fire hydrants in Sacramento"_) and the detected objects and traffic signs stream onto the globe tile by tile with Mapillary's own icons | Mapillary + Anthropic | 🟡 Mapillary client token · 🔴 Anthropic key for the AI query | +| 📷 **Street Level** | Mapillary coverage drawn onto the streets, click a sequence for its image cones, open any image in the embedded MapillaryJS viewer, and **Street Cockpit** — the globe camera follows the street-level view. Ask **the plain-English query box** in plain English (_"show me all fire hydrants in Sacramento"_) and the detected objects and traffic signs stream onto the globe tile by tile with Mapillary's own icons | Mapillary + Anthropic | 🟡 Mapillary client token · 🔴 Anthropic key for the AI query | | 📻 **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 | 🟢 | @@ -407,8 +404,8 @@ Six keys. Four have a free tier, and the two 🔴 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, sequences and the embedded viewer, plus the detected objects + traffic signs behind Mapillary AI. A client token runs in the browser by design (MapillaryJS fetches imagery directly) | [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers) — register an app, copy the client token | -| 🔴 | **Anthropic** | 🧠 Mapillary AI — turns a sentence into a Mapillary query plan (Claude, low effort, prompt-cached; a few cents per hundred questions). Server-side only | [console.anthropic.com](https://console.anthropic.com/settings/keys) — metered | +| 🟡 | **Mapillary** | 📷 Street-level coverage, sequences and the embedded viewer, plus the detected objects + traffic signs behind the plain-English query box. A client token runs in the browser by design (MapillaryJS fetches imagery directly) | [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers) — register an app, copy the client token | +| 🔴 | **Anthropic** | 🧠 the plain-English query box — turns a sentence into a Mapillary query plan (Claude, low effort, prompt-cached; a few cents per hundred questions). Server-side only | [console.anthropic.com](https://console.anthropic.com/settings/keys) — metered | ![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 a21ea99a4..0bbc91256 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,14 +22,15 @@ The golden rule: **secret-bearing API keys stay on the server side.** The dev/pr | OpenSky OAuth (`OPENSKY_CLIENT_ID/SECRET`) | Server only | Server mints + refreshes the token behind `/api/opensky` | | `GOOGLE_MAPS_SERVER_API_KEY` (optional, #33) | Server only | Server calls Places (`/api/google/nearby-places`, `/api/google/text-search`) and the Street View fallback with this key; falls back to `GOOGLE_MAPS_API_KEY` when unset | -### Two deliberately client-side keys — restrict them +### Three deliberately client-side keys — restrict them These are designed to be used directly in the browser (like a Mapbox public token). They are injected into the client bundle via Vite's `define`, so they **will** be visible in browser devtools. Scope and restrict them rather than trying to hide them: 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. -> The explicit browser `define` block in `build/vite.js` controls exactly what reaches the client: only these two keys. Everything else stays server-side. +> The explicit browser `define` block in `build/vite.js` controls exactly what reaches the client: only these three keys. Everything else stays server-side. **Places and Street View never needed to be on that list** (#33): they're called from the server-side proxies in the table above, which use `GOOGLE_MAPS_SERVER_API_KEY` when it's set. Splitting it from the browser-exposed key lets each key's Google Cloud restriction actually match what it does — the browser key referrer-restricted to the APIs the client loads, the server key IP-restricted (never a referrer, since it never leaves your server) to Places + Street View Static — instead of one key that has to be either over-permissioned or broken for one of its two jobs. A single shared `GOOGLE_MAPS_API_KEY` still works if you don't split them; it just has to cover every API both sides use. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index b5727f353..90572e150 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -2515,6 +2515,20 @@ 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, 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 (Mapillary) lives in the right context rail next to CCTV as an +ordinary collapsible panel and starts collapsed. Without `MAPILLARY_CLIENT_TOKEN` +the panel shows a gated empty state naming the variable; 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. + 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