diff --git a/README.md b/README.md index 39eed1de4..607f37db0 100644 --- a/README.md +++ b/README.md @@ -421,7 +421,7 @@ _The Space Missions layer replaying a Falcon 9 ascent — labeled `RECONSTRUCTED **Setup.** At [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers), register an application with **Read** access and copy its **Client Token** (`MLY|…`). Paste it into **POWER UP → Mapillary**, or set `MAPILLARY_CLIENT_TOKEN` in `.env` and restart. Without it the panel reads **KEY REQUIRED**. **Google Street View** uses `GOOGLE_MAPS_API_KEY` with the **Maps JavaScript API** enabled for it in Google Cloud; its chip starts off, since every panorama is billed to the key. **KartaView** and **Panoramax** need nothing: their public APIs are keyless. -**Use.** Turn on **Street Level** under DATA LAYERS → Cameras, or with the pill in the STREET LEVEL panel header. Each provider has a chip in its own colour (Mapillary green), and one is lit at a time: lighting one switches the others off. Mapillary, which is free, is lit by default. Zoom in to street level (below 60 km) for the coverage lines; from orbit the panel says to zoom in. The **IMAGERY** (ALL / 360° / FLAT) and **SINCE** filters apply to Mapillary, KartaView and Panoramax; Street View panoramas are all 360°, so with Street View lit the filters are hidden. Click a coverage line for its image cones and a cone to open the photo. Light **KARTAVIEW** (purple) and zoom in below about 6 km: its lines are the streets KartaView has photos of, and a click on one opens the photo nearest the click, flat or 360° (drag to look around), with cones along its drive; **‹ ›** or ← → step through the drive (Shift+← →, ↑ ↓ and + − look around a 360° photo; pinch zooms on a phone). Photos KartaView has archived are skipped, and where all are archived the panel says so. For KartaView, **SINCE** goes by upload date, as KartaView's own map does (it can filter by nothing else), and the panel notes it under the slider; KartaView filters its lines on its own server, and a filtered area KartaView has not drawn before can take several seconds. Light **PANORAMAX** (orange) for the open, federated commons (strongest in France and Belgium): below about 60 km its lines are sequences. A click on a line opens that sequence's picture nearest the click without moving the camera, flat or 360°, with cones along the sequence around it; **‹ ›** or ← → step through it, and both filters apply. Its caption names the picture's authors (or the instance holding it) and licence (mostly CC BY-SA 4.0; pictures on the IGN instance are Licence Ouverte 2.0). On the **Google 3D** map (Google's terms keep Street View off other maps, where it does not appear), light **GOOGLE STREET VIEW** and click a street from below 3 km, or press **OPEN STREET VIEW** for the view centre, to open Google's panorama there; step along its arrows. Above the photo: **EXPAND** (full screen; Esc leaves) and **FIT / FILL** (Mapillary, and flat KartaView and Panoramax photos). The caption links to the image on the provider's site, and the globe credits each provider that is on ("© Mapillary contributors, CC BY-SA 4.0", "Street View imagery © Google", "© KartaView contributors, CC BY-SA 4.0", "© Panoramax contributors, licence per picture"). KartaView's own photo pages fail for signed-out visitors, so its link opens the full-size photo. +**Use.** Turn on **Street Level** under DATA LAYERS → Cameras, or with the pill in the STREET LEVEL panel header. Each provider has a chip in its own colour (Mapillary green), and one is lit at a time: lighting one switches the others off. Mapillary, which is free, is lit by default. Zoom in to street level (below 60 km) for the coverage lines; from orbit the panel says to zoom in. The **IMAGERY** (ALL / 360° / FLAT) and **SINCE** filters apply to Mapillary, KartaView and Panoramax; Street View panoramas are all 360°, so with Street View lit the filters are hidden. Click a coverage line for its image cones and a cone to open the photo. Light **KARTAVIEW** (purple) and zoom in below about 6 km: its lines are the streets KartaView has photos of, and a click on one opens the photo nearest the click, flat or 360° (drag to look around), with cones along its drive; **‹ ›** or ← → step through the drive (Shift+← →, ↑ ↓ and + − look around a 360° photo; pinch zooms on a phone). Photos KartaView has archived are skipped, and where all are archived the panel says so. For KartaView, **SINCE** goes by upload date, as KartaView's own map does (it can filter by nothing else), and the panel notes it under SINCE; KartaView filters its lines on its own server, and a filtered area KartaView has not drawn before can take several seconds. Light **PANORAMAX** (orange) for the open, federated commons (strongest in France and Belgium): below about 60 km its lines are sequences. A click on a line opens that sequence's picture nearest the click without moving the camera, flat or 360°, with cones along the sequence around it; **‹ ›** or ← → step through it, and both filters apply. Its caption names the picture's authors (or the instance holding it) and licence (mostly CC BY-SA 4.0; pictures on the IGN instance are Licence Ouverte 2.0). On the **Google 3D** map (Google's terms keep Street View off other maps, where it does not appear), light **GOOGLE STREET VIEW** and click a street from below 3 km, or press **OPEN STREET VIEW** for the view centre, to open Google's panorama there; step along its arrows. Above the photo: **EXPAND** (full screen; Esc leaves) and **FIT / FILL** (Mapillary, and flat KartaView and Panoramax photos). The caption links to the image on the provider's site, and the globe credits each provider that is on ("© Mapillary contributors, CC BY-SA 4.0", "Street View imagery © Google", "© KartaView contributors, CC BY-SA 4.0", "© Panoramax contributors, licence per picture"). KartaView's own photo pages fail for signed-out visitors, so its link opens the full-size photo. Share links carry the provider switches and filters (`0.m.0` Mapillary off, `0.m.0_0.r.google-1` Street View on, `0.m.0_0.r.google-0*kartaview-1*panoramax-0` KartaView on and `0.m.0_0.r.google-0*kartaview-0*panoramax-1` Panoramax on, as the app writes them (`0.m.0_0.r.kartaview-1` and `0.m.0_0.r.panoramax-1` work too; a link that leaves out `m` keeps Mapillary, the default, lit), `0.p.p` panoramas only, `0.s.365` the last year). To add a provider, implement the contract in `src/layers/streetLevel/registry.js` and register it in `src/app/layers/streetLevel.js`. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 314c9b668..91907b58c 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -3484,7 +3484,12 @@ Mapillary token, Street View on Google 3D with a Google key), the layer reports it (`keyAlternatives`), and the panel's KEY REQUIRED, its coverage line and the layer row's tooltip say to light it. The viewer sits under the header with EXPAND, FIT/FILL and close above the -image. SINCE is a stepped slider whose readout names the cut-off date. +image; FIT/FILL starts at FIT each session. IMAGERY and FIT/FILL are rows of +toggle buttons (`aria-pressed`), each its own tab stop. SINCE is a select of +the nine windows; a link's day count between them gets its own option, and +the tooltip names the cut-off date. The panel opens by itself only when a +photo opens (a click on the map or a line would otherwise load it out of +sight), never when the layer is switched on. MapillaryJS loads on the first photo. Opening one frames it with a single camera flight through the application's deferred navigation (`begin` / @@ -3493,7 +3498,8 @@ if newer navigation took the camera while the photo loaded; closing the photo cancels it. EXPAND is the browser's Fullscreen API on the viewer element; on iPhone, which has no element fullscreen, the viewer opens as a top-layer popover instead (hidden where neither API exists). Clean View, recording and -the cockpit take an expanded photo out of full screen. At phone width the +the cockpit take an expanded photo out of full screen: the shell calls the +panel's `shrinkViewer()` as it enters them. At phone width the viewer height comes from the rail band so the whole photo fits. Providers implement the contract in `src/layers/streetLevel/registry.js`. The @@ -3586,7 +3592,7 @@ lookup asks the photo API for the projection (`SPHERE`/`PLANE`; it ignores photos are near but none passes, the panel says so), and the cones pass on each photo's projection and upload date. The provider declares `sinceBasis: 'upload'`, so while it is lit the panel shows "KartaView: by -upload date" under the SINCE slider (`sinceNote` in the UI state). Tiles come uncompressed and +upload date" under the SINCE select (`sinceNote` in the UI state). Tiles come uncompressed and uncacheable, so the last 24 decoded tiles are kept for 10 minutes while KartaView is lit (`createTileCache`, dropped when it is switched off); one that does not answer in 20 s is asked once more, and one that is not a line diff --git a/src/cockpitMarkup.test.mjs b/src/cockpitMarkup.test.mjs index 0b376d9fc..8b89c10aa 100644 --- a/src/cockpitMarkup.test.mjs +++ b/src/cockpitMarkup.test.mjs @@ -693,7 +693,10 @@ test('fresh Cockpit entry temporarily collapses map panels and exit restores the } assert.match(ui, /onEntered: \(\) => this\.enterPanels\(\)/); - assert.match(ui, /enterPanels: \(\) => this\._panelChrome\.enterCockpit\(\)/); + assert.match( + ui, + /enterPanels: \(\) => \{[^}]*this\._panelChrome\.enterCockpit\(\);\s*\}/, + ); const callback = { 1: shellMethod('enterCockpit').toString() }; assert.ok(callback, 'Cockpit onEntered callback is missing'); assert.match( diff --git a/src/streetLevelMarkup.test.mjs b/src/streetLevelMarkup.test.mjs index 8d316c120..9a3f4ae23 100644 --- a/src/streetLevelMarkup.test.mjs +++ b/src/streetLevelMarkup.test.mjs @@ -70,24 +70,26 @@ test('the key gate holds every filter, with the error line outside it', () => { assert.match(html, /id="sl-error"[^>]*role="alert"/); }); -test('each radiogroup is one tab stop, on its checked button', () => { - for (const [group, checked] of [ +test('the segmented filters are toggle buttons, each its own tab stop', () => { + for (const [group, chosen] of [ ['data-sl-render', 'letterbox'], ['data-sl-pano', 'all'], ]) { - const radios = [ + const buttons = [ ...html.matchAll( new RegExp(`