feat(street-level): provider contract with Mapillary as the first provider

Street Level is now a provider-neutral layer modelled on the iD editor's
photo overlay. The core owns what every imagery provider shares: the
enable state and a switch per provider, the imagery filter (360°/flat and
captured-since in relative days), one click handler routed by pick prefix,
one viewer host that mounts whichever provider's viewer adapter owns the
open image, the position marker, camera follow, one on-globe credit per
active provider, and share-link parameters (one bit per provider plus the
filter). Providers implement the contract in registry.js: coverage,
sequences, nearest-image lookup and a viewer adapter emitting a common
pose. Mapillary is the only registered provider; Google Street View,
KartaView and Panoramax slot in beside it.

Panel: a PROVIDERS chip row (one chip per registered provider, keyless
chips read as errors), relative-day SINCE options, a provider-neutral
caption whose link label follows the provider, and no vendor mark in the
header. The panel inner is a rail scroller like CCTV's.

Tests cover the registry, params codec, filter, pick router, UI-state
composition, the provider definition and coverage-tile decoding; the QA
harness switches the provider off and on and checks the filter.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Fable 5.1 committed 2026-09-25 18:34:09 +01:00
1 parent 2dbc684fb1
commit 28e6a1faa7
41 files changed
+2460 -744

No files matched your search

+9 -11
View File
@@ -75,12 +75,8 @@
runtime next to CCTV (see PanelLayoutController). -->
<div id="street-level-panel" class="panel-collapsible collapsed" data-panel-id="street-level-panel">
<div class="panel-glow"></div>
<div class="street-level-panel-inner">
<div class="street-level-panel-inner" data-rail-scroller>
<div class="panel-header">
<!-- Mapillary mark: path from the MapillaryJS attribution component
(mapillary-js, MIT, © Mapillary). Mapillary is a trademark of
Meta Platforms, Inc.; it is used here to identify the data source. -->
<svg class="sl-mark" viewBox="0 0 13.5 13.8" aria-hidden="true" focusable="false"><path fill="currentColor" d="M6.38154274 9.6505067C6.14007412 9.52786404 6.08124006 9.31709453 6.21044037 9.08865565 6.27614941 8.97247561 6.41859234 8.74074422 6.53468168 8.5539822 6.69432724 8.29714786 6.92701434 8.29637588 7.05835866 8.37253362 7.18970298 8.44869135 9.04853604 9.42087063 9.16874871 9.48670656 9.4525926 9.64215711 9.87118629 9.28623741 9.68342251 8.97441326 9.59375332 8.82549731 8.8103938 7.4044783 8.5614978 6.93610438 8.44825156 6.72299696 8.47921005 6.53132251 8.731647 6.40377463 8.85656436 6.3406581 9.08004239 6.20890798 9.27394762 6.09190586 9.47189219 5.9724664 9.7353488 6.06223885 9.84245555 6.24022621 9.94956229 6.41821357 13.1209182 12.2294019 13.2221517 12.4098169 13.4231294 12.7679928 13.0322853 13.1511137 12.662949 12.953094 12.4781436 12.8540105 6.62301136 9.77314936 6.38154274 9.6505067zM.200146062 6.40081205C-.111554282 6.23274106-.0545442328 5.79604052.338782648 5.68466036.672030083 5.59029317 3.37820647 4.6882634 4.19715115 4.4149007 4.34479443 4.36561757 4.46005809 4.25228232 4.50965757 4.1075867 4.78858853 3.29386671 5.72024556.575961334 5.82468911.27126979 5.9477048-.087601979 6.39304077-.0889184867 6.57454671.258502251 6.63901566.381902346 9.08940484 4.79813543 9.17465946 4.98547051 9.25991408 5.17280559 9.20498731 5.38031434 9.01176406 5.50000515 8.8185408 5.61969596 8.50343522 5.79872065 8.3936507 5.86654323 8.19489463 5.98933057 8.00439135 5.90384324 7.91365501 5.71364816 7.82291866 5.52345307 7.12387828 4.29626886 6.83882952 3.77527795 6.69634941 3.51486341 6.2671132 3.41492796 6.12283721 3.83582159 6.01848488 4.14024669 5.77526264 4.84979439 5.64864557 5.219172 5.60020375 5.3604904 5.47741944 5.48212554 5.33398124 5.52937001 4.9780163 5.64661478 4.30162944 5.86939744 3.90672375 5.99946818 3.62491469 6.0922881 3.48793523 6.52150257 3.86841776 6.70224674 3.94789525 6.74000167 5.66097153 7.65404391 5.83772054 7.73764558 6.01446954 7.82124724 6.11275281 8.06257945 6.00925316 8.23355004 5.8680804 8.46675268 5.68734796 8.76272052 5.6217862 8.85880815 5.50815838 9.02534163 5.28495077 9.08080286 5.09748996 8.98251076 4.91002915 8.88421866.311522289 6.46086689.200146062 6.40081205z"/></svg>
<span class="panel-title">STREET LEVEL</span>
<span class="panel-divider"></span>
<span id="sl-status" class="sl-status" role="status" aria-live="polite">OFF</span>
@@ -90,7 +86,11 @@
<fieldset id="sl-controls" class="sl-controls">
<div class="cctv-controls">
<button id="sl-enable-btn" class="scene-btn" type="button" aria-pressed="false" title="Turn the Street Level layer on or off">STREET LEVEL OFF</button>
<button id="sl-look-btn" class="scene-btn" type="button" title="Open the nearest Mapillary photo to the view centre">OPEN NEAREST PHOTO</button>
<button id="sl-look-btn" class="scene-btn" type="button" title="Open the nearest street-level photo to the view centre">OPEN NEAREST PHOTO</button>
</div>
<div class="sl-providers" role="group" aria-label="Imagery providers">
<span class="cctv-summary-label">PROVIDERS</span>
<div id="sl-provider-chips" class="sl-chips"></div>
</div>
<div id="sl-error" class="sl-error" role="alert" hidden><span aria-hidden="true">⚠</span> <span id="sl-error-text"></span></div>
<div class="sl-filters" role="group" aria-label="Imagery filters">
@@ -106,9 +106,7 @@
<option value="365">last year</option>
<option value="730">2 years</option>
<option value="1826">5 years</option>
<option value="year:2024">2024</option>
<option value="year:2022">2022</option>
<option value="year:2020">2020</option>
<option value="3652">10 years</option>
</select>
</div>
<ul id="sl-legend" class="sl-legend" aria-label="Coverage colours"></ul>
@@ -127,12 +125,12 @@
<button id="sl-viewer-close" class="sl-viewer-x" type="button" title="Close the image and deselect it on the map" aria-label="Close the street-level image">×</button>
</div>
<div id="sl-viewer-placeholder" class="sl-viewer-placeholder" hidden></div>
<div id="sl-viewer" class="sl-viewer" aria-label="Mapillary street-level viewer"></div>
<div id="sl-viewer" class="sl-viewer" aria-label="Street-level viewer"></div>
<div id="sl-image-meta" class="sl-image-meta">
<span id="sl-image-by" class="sl-image-by"></span>
<span class="sl-image-right">
<span id="sl-image-when" class="sl-image-when"></span>
<a id="sl-image-link" class="sl-image-link" href="https://www.mapillary.com/app/" target="_blank" rel="noopener noreferrer" title="Open this image on mapillary.com">MAPILLARY ↗</a>
<a id="sl-image-link" class="sl-image-link" href="#" target="_blank" rel="noopener noreferrer" title="Open this image on the provider's site" hidden></a>
</span>
</div>
</div>