mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
docs(street-level): shorten comments and docs, drop audit references
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d59b4d05bb
commit
48b889590d
12 files changed
+41
-76
No files matched your search
+6
-8
@@ -71,16 +71,14 @@
|
||||
proposed diagnosis in #927, and [kvnloo](https://github.com/kvnloo) for identifying
|
||||
the repair race. Windows/Claude Desktop Cowork confirmation remains outstanding.
|
||||
|
||||
- Street Level: Mapillary street-level imagery modelled on the iD editor's
|
||||
photo overlay (free client token, CC BY-SA 4.0 imagery). A docked right-rail
|
||||
panel holds an ON/OFF pill, 360°/flat and captured-since filters, a legend
|
||||
and an embedded viewer with full-screen EXPAND and FIT/FILL. Coverage lines
|
||||
(below 60 km) and image cones drape on the terrain and Google 3D tiles;
|
||||
- Street Level: Mapillary street-level imagery (free client token, CC BY-SA
|
||||
4.0). A right-rail panel holds an ON/OFF pill, 360°/flat and captured-since
|
||||
filters, a legend and an embedded viewer with EXPAND and FIT/FILL. Coverage
|
||||
lines (below 60 km) and image cones drape on terrain and Google 3D tiles;
|
||||
opening a photo frames it with one camera flight. Share links carry the
|
||||
switch, the filters and the panel's collapsed state. A server proxy adds the
|
||||
token to coverage-tile requests, trims the tiles and caches them in memory
|
||||
for 24 h; it refuses cross-site requests and upstream redirects, rate-limits
|
||||
per IP and runs at most six Mapillary fetches at once. The panel reads
|
||||
token to coverage tiles, trims and caches them for 24 h, refuses cross-site
|
||||
requests and redirects, and limits load per IP and upstream. The panel reads
|
||||
KEY REQUIRED without a token and KEY REJECTED when Mapillary refuses it.
|
||||
|
||||
- Add Norway's road cameras from Statens vegvesen as a CCTV pack. The camera
|
||||
|
||||
+3
-4
@@ -66,10 +66,9 @@ gate for the feature you changed.
|
||||
> **CI does not cover this for you.** The workflow runs the setup policy
|
||||
> checks, the formatting and package-boundary checks, the unit suite and the
|
||||
> production build, plus a Windows onboarding job. It runs neither
|
||||
> `npm run test:track` nor any other `qa-*.mjs` gate — both need a live dev
|
||||
> server and a browser. The one exception is the hermetic Street Level fixture
|
||||
> gate (`street-level-browser` job). Include the applicable local run in your PR's validation
|
||||
> evidence.
|
||||
> `npm run test:track` nor any `qa-*.mjs` gate except the Street Level fixture
|
||||
> gate (`street-level-browser` job) — they need a live dev server and a
|
||||
> browser. Include the applicable local run in your PR's validation evidence.
|
||||
|
||||
## Share-link layer tokens
|
||||
|
||||
|
||||
+8
-12
@@ -3028,9 +3028,8 @@ Street Level is a docked, collapsible right-rail panel (`#street-level-panel`,
|
||||
layer token `0`, option owner `street-level`, panel `ui` token `t`) that starts
|
||||
collapsed. It opens itself only when the user, voice or a tool switches the
|
||||
layer on or a photo opens, never on a restore, and those opens are not stored.
|
||||
Like the iD editor's photo overlay it shows Mapillary only: 360°/flat and
|
||||
captured-since filters (relative days, so a link keeps its meaning), one
|
||||
embedded viewer and an on-globe credit while the layer is on. Share options:
|
||||
It has 360°/flat and captured-since filters (relative days, so a link keeps
|
||||
its meaning), one embedded viewer and an on-globe credit while the layer is on. Share options:
|
||||
`m` (Mapillary on/off; off draws nothing), `p` (`a`/`p`/`f` panoramas), `s`
|
||||
(since, days). The header pill is the only layer switch.
|
||||
|
||||
@@ -3047,9 +3046,8 @@ pauses them for its Retry-After.
|
||||
The server proxy (`server/providers/mapillary.js`) adds the token to
|
||||
coverage-tile requests (the browser holds it too, for MapillaryJS and the Graph
|
||||
API), serves only z11–14 coverage, refuses upstream redirects, strips the
|
||||
unused `image` layer, coalesces concurrent requests, runs at most six Mapillary
|
||||
fetches at once (a fetch every client has left is cancelled, or never started)
|
||||
and keeps tiles in a 24 h memory cache.
|
||||
unused `image` layer, shares one fetch per tile, runs at most six at once
|
||||
(cancelling those every client has left) and keeps tiles in memory for 24 h.
|
||||
|
||||
MapillaryJS loads on the first photo. Opening one frames it with a single
|
||||
camera flight through the application's deferred navigation (`begin` /
|
||||
@@ -3064,12 +3062,10 @@ Mapillary back on. At phone width the viewer height comes from the rail band
|
||||
so the whole photo fits.
|
||||
|
||||
Browser gates: `npm run qa:street-level -- --url <server>` runs against real
|
||||
Mapillary with a token; `npm run qa:street-level:fixtures` answers every
|
||||
Mapillary request from fixtures (`providers/mapillary/coverageFixture.mjs`,
|
||||
`scripts/fixtures/street-level/`); its server (default `:4173`) still needs a
|
||||
`MAPILLARY_CLIENT_TOKEN`, and any value will do. CI runs the fixture gate with
|
||||
`--strict` against a production build served by `vite preview` with a dummy
|
||||
token.
|
||||
Mapillary; `npm run qa:street-level:fixtures` answers every Mapillary request
|
||||
from fixtures (`providers/mapillary/coverageFixture.mjs`,
|
||||
`scripts/fixtures/street-level/`), but its server (default `:4173`) still needs
|
||||
some `MAPILLARY_CLIENT_TOKEN`. CI runs it `--strict` against `vite preview`.
|
||||
|
||||
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
|
||||
|
||||
@@ -57,9 +57,8 @@ Related keys (current versions):
|
||||
- Panel collapsed state: `godsEyeView.v6.panelCollapsed.<panel-id>` — `'0'` open,
|
||||
`'1'` closed, absent means the panel's own default. A view opened from a share
|
||||
link ignores the stored value entirely.
|
||||
- Panel positions: `godsEyeView.v8.panelPos.<panel-id>` — the versioned name for a
|
||||
stored position. The current layout writes none, so deleting one changes
|
||||
nothing.
|
||||
- Panel positions: `godsEyeView.v8.panelPos.<panel-id>` — the current layout
|
||||
writes none.
|
||||
- CCTV calibration: `godsEyeView.cctv.calibration.v2`
|
||||
|
||||
---
|
||||
|
||||
@@ -14,10 +14,7 @@ const DROP_LAYERS = ['image'];
|
||||
/** A z14 tile with its image layer is ~11 MB; anything past this is wrong. */
|
||||
export const TILE_MAX_BYTES = 48 * 1024 * 1024;
|
||||
const FETCH_TIMEOUT_MS = 60_000;
|
||||
/**
|
||||
* Upstream fetches at once; more wait their turn. A fetch every client has
|
||||
* left is cancelled, or never started, so the queue holds live tiles only.
|
||||
*/
|
||||
/** Upstream fetches at once; more wait their turn. */
|
||||
export const TILE_UPSTREAM_CONCURRENCY = 6;
|
||||
/** Tiles change only when new imagery is processed. */
|
||||
export const TILE_TTL_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
@@ -4,10 +4,8 @@ import { plausibleSurfaceHeight, sampledSurfaceHeight } from './view.js';
|
||||
/** Fly the globe camera once to frame a photo after it opens. */
|
||||
export function createCameraFraming({ state }) {
|
||||
/**
|
||||
* The application's camera authority (`attachNavigation`): `begin(noun)`
|
||||
* takes a deferred ticket (a generation, or false when refused) that
|
||||
* `reassert(generation)` honours, releasing tracking, only if nothing newer
|
||||
* took the camera. Without it, framing flies directly.
|
||||
* The application's camera authority: `reassert(begin())` succeeds only if
|
||||
* nothing newer took the camera. Without it, framing flies directly.
|
||||
*/
|
||||
let navigation = null;
|
||||
/** Our framing flight while it is current; Cesium calls `cancel` when another flight starts. */
|
||||
|
||||
@@ -218,11 +218,7 @@ export function createStreetLevelLayer({
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* The application's camera authority, the deferred `begin(noun)` /
|
||||
* `reassert(generation)` pair: a photo claims the camera when opening
|
||||
* starts and frames only if it still owns it once loaded.
|
||||
*/
|
||||
/** A photo frames itself only if it still owns the camera once loaded. */
|
||||
attachNavigation(navigation) {
|
||||
parts.framing.attachNavigation(navigation);
|
||||
},
|
||||
|
||||
@@ -31,10 +31,9 @@ export function sampledSurfaceHeight(scene, carto) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ellipsoidal height (m) of the ground under the camera, or null. The globe
|
||||
* answers where shown; Google 3D hides it, so the rendered surface does
|
||||
* (roofs count: a building's height at most). A sample above the camera is
|
||||
* ignored: a raised ground would put the camera underground.
|
||||
* Ellipsoidal height (m) of the ground under the camera, or null. Under
|
||||
* Google 3D (globe hidden) the rendered surface counts, minus any roof above
|
||||
* the camera.
|
||||
*/
|
||||
export function groundUnderCamera(viewer) {
|
||||
const carto = viewer?.camera?.positionCartographic;
|
||||
@@ -81,11 +80,9 @@ function groundHit(camera, point, ellipsoid, maxRange) {
|
||||
const BBOX_GRID = 5;
|
||||
|
||||
/**
|
||||
* Visible [west, south, east, north] degrees, or null when fewer than four
|
||||
* rays hit. Only hits count, so a horizon cannot inflate the box to the world.
|
||||
* `groundHeight` raises the ellipsoid to the ground (rays to the bare
|
||||
* ellipsoid under Denver land kilometres too far out) and `maxRange` (m)
|
||||
* drops near-horizon hits.
|
||||
* Visible [west, south, east, north] degrees from the rays that hit the
|
||||
* ground, or null when fewer than four do. `groundHeight` raises the ellipsoid
|
||||
* to the ground (Denver is 1,600 m up); `maxRange` (m) drops far hits.
|
||||
*/
|
||||
export function visibleBbox(
|
||||
viewer,
|
||||
@@ -158,10 +155,8 @@ export function cameraNadir(viewer) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Where coverage tiles are ranked from: halfway between the ground under the
|
||||
* camera and the ground at the centre of the screen, so a tilted view loads
|
||||
* what it looks at as well as what is near. The nadir when the centre ray
|
||||
* misses the ground within `maxRange`. Options as `visibleBbox`.
|
||||
* Where coverage tiles are ranked from: halfway between the nadir and the
|
||||
* screen centre's ground, or the nadir when that is out of `maxRange`.
|
||||
*/
|
||||
export function viewFocus(
|
||||
viewer,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { freshStreet } from './state.js';
|
||||
|
||||
/**
|
||||
* Owns the panel's viewer element and the photo viewer mounted in it (the
|
||||
* MapillaryJS adapter, or a stand-in). Its poses update `state.street` and
|
||||
* the marker; MapillaryJS loads on the first open.
|
||||
*/
|
||||
/** The panel's photo viewer: its poses update `state.street` and the marker. */
|
||||
export function createViewerHost({ state, parts, adapter }) {
|
||||
/** Set once mounted, until `unmount`. */
|
||||
let unsubscribe = null;
|
||||
|
||||
@@ -45,7 +45,7 @@ const UNTRIMMED = tile([
|
||||
{ name: 'image', payload: Buffer.alloc(4000, 1) },
|
||||
]);
|
||||
|
||||
/** Mount the plugin and return `call(route, url, method, headers)`. */
|
||||
/** Mount the plugin and return `call(route, url, method, headers, onResponse)`. */
|
||||
function install(mode = 'configureServer') {
|
||||
const routes = new Map();
|
||||
mapillaryProxy()[mode]({
|
||||
|
||||
@@ -79,9 +79,8 @@ export class PanelPositionControls {
|
||||
this._restorePanelPosition(spec.id, spec.panel);
|
||||
this._makePanelDraggable(spec.id, spec.panel, spec.handle);
|
||||
}
|
||||
// Keep a positioned panel on-screen when its HEIGHT changes after restore — it expands to its
|
||||
// full row set a frame or two later, so the restore-time clamp used a stale (shorter) height and
|
||||
// the panel could still hang off the bottom (audit U2). Re-clamp on every size change.
|
||||
// A restored panel grows a frame or two later, so the restore-time clamp
|
||||
// is stale: re-clamp on every size change.
|
||||
if (this._ppToggles && typeof ResizeObserver !== 'undefined') {
|
||||
this._draggableResizeObserver = new ResizeObserver(() =>
|
||||
this._reclampDraggablePanels(),
|
||||
@@ -159,8 +158,7 @@ export class PanelPositionControls {
|
||||
const pos = JSON.parse(raw);
|
||||
if (!pos || typeof pos.left !== 'number' || typeof pos.top !== 'number')
|
||||
return;
|
||||
// Clamp to the viewport: a position saved at one window size would otherwise land off-screen at
|
||||
// another (audit U2 — observed a panel at x:-192). The drag handler clamps; restore must too.
|
||||
// A position saved at another window size can land off-screen.
|
||||
const { left, top } = this._clampToViewport(
|
||||
Math.round(pos.left),
|
||||
Math.round(pos.top),
|
||||
|
||||
@@ -105,10 +105,8 @@ export class StreetLevelControls {
|
||||
this._setParams({ sinceDays: sinceDays() }),
|
||||
);
|
||||
this.listen(el.viewerClose, 'click', () => this.layer.closeViewer?.());
|
||||
// EXPAND is the browser's own fullscreen: it handles Esc, focus and the
|
||||
// rest of the page. iPhone Safari has no element fullscreen, so there the
|
||||
// viewer opens as a top-layer popover over the page instead (Esc and the
|
||||
// button close it). With neither, the button is not offered.
|
||||
// EXPAND is the browser's fullscreen. iPhone Safari has no element
|
||||
// fullscreen, so there it is a top-layer popover; with neither, no button.
|
||||
this._expandMode =
|
||||
typeof el.viewerWrap?.requestFullscreen === 'function'
|
||||
? 'fullscreen'
|
||||
@@ -195,9 +193,8 @@ export class StreetLevelControls {
|
||||
|
||||
async _toggleEnabled() {
|
||||
const enabled = this.actions.isEnabled?.() === true;
|
||||
// The pill is the only switch. A share link or tool can switch Mapillary
|
||||
// off under a layer that is on; the pill then reads OFF, and switching on
|
||||
// brings Mapillary back (and the layer, if it is off too).
|
||||
// The pill is the only switch: it also brings back Mapillary that a link
|
||||
// or tool switched off under a layer that is on.
|
||||
if (this._state?.providerOn === false) {
|
||||
this._setParams({ mapillary: true });
|
||||
if (enabled) return;
|
||||
@@ -252,11 +249,7 @@ export class StreetLevelControls {
|
||||
this._syncExpanded();
|
||||
}
|
||||
|
||||
/**
|
||||
* While expanded, leave when Clean View, recording or the cockpit starts:
|
||||
* they hide every panel, and the photo would stay over the globe (and in a
|
||||
* recording).
|
||||
*/
|
||||
/** Clean View, recording and the cockpit hide every panel, so they end EXPAND too. */
|
||||
_watchPanelHidingModes(on) {
|
||||
const body = document.body;
|
||||
if (!on || !body) {
|
||||
|
||||
Reference in new issue
Block a user