mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 22:02:21 +00:00
Street Level: shorter comments and docs
- Comments in the action, the layer, embed, the Mapillary policy and the controls say why in fewer words; restatements of the code are gone. - CURRENT-STATE's "Show me X on Street View" section is a list by topic and no longer says the browser gate aims at coverageLines. - TOOLS.md's street-level view paragraph is shorter.
This commit is contained in:
1 parent
f3996da0af
commit
e34b5103c6
7 files changed
+167
-215
No files matched your search
+97
-105
@@ -5073,111 +5073,103 @@ core's lookups alone. The MCP globe panel allows the meta-catalogue and the
|
||||
three biggest instances; a picture on another instance shows only in the
|
||||
app.
|
||||
|
||||
**"Show me X on Street View."** The `show_street_level` assistant action
|
||||
(voice and typed; `src/voice/gevActions.js`) is one call: it checks the
|
||||
source has its key (a keyless source explains itself and changes nothing, as
|
||||
a chip does), flies to the place at street framing and waits for arrival (a
|
||||
name, coordinates, a pointed spot, a numbered item, or nothing for the
|
||||
ground under the screen centre, as OPEN uses: `aim: 'screen-centre'` with
|
||||
`armed: false`, so sky refuses and a link's armed point is not used and is
|
||||
kept until a photo opens), switches to the map the source needs (Google Street View: Google 3D),
|
||||
lights that source through `setLayerParams` with origin `voice` (one source at
|
||||
a time, kept in the share link like a chip click; a link runs the action with
|
||||
origin `programmatic`, for the source switch and the layer, so it never writes
|
||||
the recipient's saved layer state), turns the layer on and
|
||||
calls `openNearest`. `provider` is `google` for "Street View", `mapillary`,
|
||||
`kartaview` or `panoramax` by name, else the lit source. Stages fail separately (`location`, `view`, `map`,
|
||||
`provider`, `layer`, `key`, `imagery`; `view` when cockpit, clean view,
|
||||
recording or scene playback hides the panel, checked after the flight and
|
||||
before anything switches or loads; embed mode exempts its own clean-view
|
||||
class because the photo is lifted above the globe; an empty `imagery` result names a
|
||||
narrowing 360°/flat or SINCE filter the source honours, as `filter`;
|
||||
`noImagery` marks an empty result, `imageryError` a source or viewer failure
|
||||
and `aim: 'sky'` a sky centre, from the layer's `street.nearestOutcome`) and a
|
||||
newer turn retires the lookup
|
||||
(`openNearest`'s `signal` option), so nothing opens after the user moved on.
|
||||
For a source without a coverage layer the lookup widens to 250 m
|
||||
(`NEAREST_RADIUS_MAX_M`; Google honours it, Mapillary's API caps at 50 m);
|
||||
clicks keep 50 m. Mapillary's search gives up after 12 s, body included
|
||||
(`NEAREST_TIMEOUT_MS`), and asks for 100 photos, the API's most: it
|
||||
returns the radius's photos in no particular order and cuts them at the limit
|
||||
first, so the distance sort and the imagery filter run over the spot's photos.
|
||||
On a phone the panel sits in the Cameras tab sheet: a photo that opens, and a
|
||||
link that arms, open that sheet and scroll the panel into view
|
||||
(`revealPanel`, which does nothing above 720px, scrolling included); every
|
||||
photo the action opens reveals the panel again, also one the user had
|
||||
collapsed. The layer exposes `requiredMapStack(id)` and
|
||||
`providerStatus(id)` for callers that must decide before the panel lists a
|
||||
source, and `coverageLines(id)` (the lines a provider draws now, current and
|
||||
stale tiles, as `[lon, lat]` parts) for diagnostics and the browser gate,
|
||||
which aims its line clicks at that geometry, waits for the asynchronous
|
||||
ground primitives to be ready, and clicks in the same JavaScript turn as the
|
||||
pick so a rebuilt frame cannot come between them. Views carry the same request as `street_level` (`src/view/index.js`:
|
||||
source and point, defaulting to where the camera looks; a `google` source
|
||||
brings `map: photoreal` and refuses another map; not with `follow`), links as
|
||||
`sl=<source>,<lat>,<lon>`; `show_in_gods_eye_view` accepts it, the globe panel
|
||||
applies it through the action, and a plain link runs the action once restored
|
||||
(one shot: the rewritten hash drops `sl`). A link pasted into the open tab
|
||||
runs it after its restore settles (`gev:share-navigation-restore-settled`),
|
||||
and only when the restore's camera landed (`share.camera === 'applied'`): a
|
||||
link whose restore flight the visitor stopped with a drag or a search, or
|
||||
whose restore a newer link superseded, never runs and never pulls the camera
|
||||
back. In embed mode the rail holding the
|
||||
panel is hidden, so an open photo moves to an overlay on the globe
|
||||
(`sl-viewer-wrap-embed`) and back when it closes; Esc closes it (in full
|
||||
screen, Esc leaves full screen first). EXPAND stays open under embed mode's
|
||||
own clean view; cockpit, recording and scene playback hide the overlay. A
|
||||
posted view whose photo step fails before the imagery stage closes the
|
||||
earlier photo before its camera flies; one the user overtook (a click on the
|
||||
map, a newer request) leaves their photo and camera alone, and one that
|
||||
found a photo it could not load keeps that error in view. The action's
|
||||
lookup replaces an open photo in place (`openNearest` `replace: true`), so
|
||||
the viewer and EXPAND stay up, and closes it only when it opens nothing; once
|
||||
an image is found, `proceed()` asks whether the camera and the panel are
|
||||
still the request's before a billed photo loads. Image-loading failures render inside
|
||||
the embedded overlay, since the panel's error region is hidden with the rail.
|
||||
A caller's abort is
|
||||
honoured through the viewer's mount and load, not just the lookup, and only
|
||||
the lookup that still owns the panel may change its spinner or error. Cancelling
|
||||
a lookup clears its loading state even when an existing photo stays open;
|
||||
internally retired lookups also release the caller's abort listener. A
|
||||
passive link's photos stand down if anyone navigated (a gesture, a search,
|
||||
the assistant) while the link waited behind its own marks. The action itself
|
||||
checks again after every await (flight, map switch, layer on, lookup): a
|
||||
newer turn, a shell navigation stamp (`_navigationGeneration`, re-taken where
|
||||
its own flight lands) or a press or wheel on the globe stops the remaining
|
||||
stages (`superseded: true`), and cockpit, clean view, recording or scene
|
||||
playback entered meanwhile ends it at the `view` stage. During the lookup the
|
||||
same checks run before a found photo loads (`proceed`), so nothing billed
|
||||
loads for a request that was given up, and a lookup given up leaves the
|
||||
panel as it was; a photo that opened after either is closed again. The
|
||||
hidden-panel check also runs before the flight, so a recording's camera never
|
||||
moves for it. A photo already open stays up while the new request looks (the
|
||||
viewer is replaced, not closed, so EXPAND stays, also when the request
|
||||
switches the source) and closes if the request opens nothing, so a place with
|
||||
no imagery shows its own error, not the previous place's photo; a link that
|
||||
only arms closes it at once. A plain
|
||||
link never loads a photo from a source that bills every load
|
||||
(`billedPerImage` on the provider; Google): the action runs with
|
||||
`autoOpen: false`, arms the lookup on the layer (`armNearest`: the link's
|
||||
point and radius, consumed only by the next OPEN press (`aim:
|
||||
'screen-centre'`), and only while the ground under the screen centre is within
|
||||
1 km of the point (`ARMED_REACH_M`; a user who moved on opens what the view
|
||||
shows) or cannot be picked; a sky centre refuses; any other lookup, open, close, disable, its source
|
||||
switched off or its map leaving drops it), opens the panel and a toast
|
||||
points at OPEN GOOGLE STREET VIEW. Only the panel page's own link (inline
|
||||
embed) for a tool result issued in the last 5 minutes, and posted views, open
|
||||
the photo: `show_in_gods_eye_view` stamps a street-level result `issued_at`,
|
||||
and a host that mounts the panel again later hands back the old result, which
|
||||
only arms. The panel checks every result, not just the first: each `gev:view`
|
||||
it posts carries `fresh`, and the inline app applies a view without it with
|
||||
`autoOpen: false` (a framing page's posted views still open); `?embed=1` on a published link only
|
||||
arms, without the panel nudge or toast (the rail is hidden in a frame). A link that follows an entity ignores `sl`.
|
||||
The look-at default applies below 20 km, at a pitch steeper than -5° and
|
||||
within 10 km ahead; otherwise the camera's own point is used. A view's
|
||||
altitude is above the ellipsoid, so `show_in_gods_eye_view` measures it from
|
||||
the terrain under the camera (`services.terrain`) before taking the look-at
|
||||
point; without a terrain answer it keeps the view's own estimate.
|
||||
**"Show me X on Street View."** `show_street_level` (voice and typed;
|
||||
`src/voice/gevActions.js`) is one call whose stages fail separately
|
||||
(`location`, `view`, `map`, `provider`, `layer`, `key`, `imagery`):
|
||||
|
||||
- **Key first.** A keyless source explains itself and changes nothing, as a
|
||||
chip does.
|
||||
- **Place.** A name, coordinates, a pointed spot or a numbered item: it flies
|
||||
there at street framing and waits for arrival. With no place it looks at the
|
||||
ground under the screen centre, as OPEN does (`aim: 'screen-centre'`,
|
||||
`armed: false`: sky refuses, and a link's armed point is not used and is kept
|
||||
until a photo opens). Coordinates off the globe are refused.
|
||||
- **Map and source.** It switches to the map the source needs (Google Street
|
||||
View: Google 3D), lights the source through `setLayerParams`, one at a time
|
||||
and kept like a chip click (origin `voice`; a link runs the action with
|
||||
origin `programmatic`, so it never writes the recipient's saved layer state),
|
||||
turns the layer on and calls `openNearest`. `provider` is `google` for
|
||||
"Street View", `mapillary`, `kartaview` or `panoramax` by name, else the lit
|
||||
source.
|
||||
- **Hidden panel.** Cockpit, clean view, recording or scene playback end it at
|
||||
`view`, before the flight and after every await; embed mode's own clean view
|
||||
is exempt, since the photo is lifted over the globe.
|
||||
- **Superseded.** After every await (flight, map switch, layer on, lookup) a
|
||||
newer turn, a shell navigation stamp (`_navigationGeneration`, re-taken where
|
||||
its own flight lands) or a press or wheel on the globe stops it
|
||||
(`superseded: true`). The same checks run before a found photo loads
|
||||
(`proceed`), so nothing billed loads for a request that was given up, and a
|
||||
lookup given up leaves the panel as it was; a photo that opened after either
|
||||
is closed again.
|
||||
- **Replacing a photo.** An open photo stays up while the new request looks
|
||||
(`openNearest` `replace: true`; the viewer is replaced, not closed, so EXPAND
|
||||
stays, also across sources through `holdViewer`) and closes if nothing
|
||||
opens, so a place with no imagery shows its own error; a link that only arms
|
||||
closes it at once. A replacement stops a playing Mapillary sequence.
|
||||
- **Outcome.** `noImagery` marks an empty result, `imageryError` a source or
|
||||
viewer failure and `aim: 'sky'` a sky centre (from `street.nearestOutcome`);
|
||||
an empty result names a narrowing 360°/flat or SINCE filter the source
|
||||
honours, as `filter`. Every photo the action opens reveals the panel, also
|
||||
one the user had collapsed.
|
||||
- **Search.** A source without a coverage layer searches up to 250 m
|
||||
(`NEAREST_RADIUS_MAX_M`; Google honours it); clicks keep 50 m. Mapillary
|
||||
asks for 100 photos within 50 m, its API's most (the API returns them
|
||||
unordered and cuts at the limit), and gives up after 12 s, body included
|
||||
(`NEAREST_TIMEOUT_MS`).
|
||||
- **Phone.** The panel sits in the Cameras tab sheet: a photo that opens, or a
|
||||
link that arms, opens the sheet and scrolls the panel into view
|
||||
(`revealPanel`, which does nothing above 720px).
|
||||
|
||||
The layer also exposes `requiredMapStack(id)` and `providerStatus(id)` for
|
||||
callers that decide before the panel lists a source, `holdViewer()`, and
|
||||
`coverageLines(id)` (the lines a provider draws now, as `[lon, lat]` parts)
|
||||
for diagnostics. A caller's abort is honoured through the viewer's mount and
|
||||
load, and only the lookup that still owns the panel changes its spinner or
|
||||
error.
|
||||
|
||||
Views carry the request as `street_level` (`src/view/index.js`): a source and
|
||||
a point, which defaults to where the camera looks (below 20 km, at a pitch
|
||||
steeper than -5° and within 10 km ahead; else the camera's own point, also
|
||||
for half a point). A `google` source brings `map: photoreal` and refuses
|
||||
another map; `street_level` does not combine with `follow`.
|
||||
`show_in_gods_eye_view` measures a view's altitude from the terrain under the
|
||||
camera (`services.terrain`) before taking the look-at point, and keeps the
|
||||
view's own estimate without a terrain answer. Links carry
|
||||
`sl=<source>,<lat>,<lon>`; the globe panel applies views through the action,
|
||||
and a plain link runs it once restored (one shot: the rewritten hash drops
|
||||
`sl`). A link that follows an entity ignores `sl`.
|
||||
|
||||
- **Billing gate.** A plain link never loads a photo from a source that bills
|
||||
every load (`billedPerImage`; Google): the action runs with
|
||||
`autoOpen: false` and arms the layer (`armNearest`: the link's point and
|
||||
radius, for the next OPEN press only, and only while the ground under the
|
||||
screen centre is within 1 km of it (`ARMED_REACH_M`) or cannot be picked; a
|
||||
sky centre refuses; any other lookup, open, close, disable, the source
|
||||
switched off or its map leaving drops it), opens the panel and shows a toast
|
||||
pointing at OPEN GOOGLE STREET VIEW.
|
||||
- **Who may open.** Posted views, and the panel page's own link (inline embed)
|
||||
for a tool result issued in the last 5 minutes, open the photo:
|
||||
`show_in_gods_eye_view` stamps a street-level result `issued_at`, every
|
||||
`gev:view` the panel posts carries `fresh`, and the inline app applies a
|
||||
view without it with `autoOpen: false` (a framing page's posted views still
|
||||
open). `?embed=1` on a published link only arms, without the panel nudge or
|
||||
toast.
|
||||
- **Who owns the camera.** A link's photo step is skipped when a newer link
|
||||
superseded its restore, when its restore flight never landed
|
||||
(`share.camera !== 'applied'`: the visitor dragged or searched), or after
|
||||
any navigation since the generation that flight owned (`navigationToken` on
|
||||
the settled result), also while its layers still load. A link pasted into
|
||||
the open tab runs its step after its restore settles
|
||||
(`gev:share-navigation-restore-settled`).
|
||||
- **Embed.** The rail is hidden, so an open photo moves to an overlay on the
|
||||
globe (`sl-viewer-wrap-embed`) and back when it closes; Esc closes it (in
|
||||
full screen, Esc leaves full screen first). EXPAND stays under embed mode's
|
||||
clean view; cockpit, recording and scene playback hide the overlay. Loading
|
||||
errors render inside the overlay, and inside the viewer while EXPAND is on.
|
||||
A posted view whose photo step fails before the imagery stage closes the
|
||||
earlier photo before its camera flies; one the user overtook (a click on the
|
||||
map, a newer request) leaves their photo and camera alone; one that found a
|
||||
photo it could not load keeps that error.
|
||||
|
||||
Street Level has two browser gates. `npm run qa:street-level -- --url <server>`
|
||||
runs against real Mapillary and needs `MAPILLARY_CLIENT_TOKEN`. `npm run
|
||||
|
||||
+9
-11
@@ -104,18 +104,16 @@ draws them once the link has been restored.
|
||||
|
||||
A view can open street-level photos too: `street_level` names a source
|
||||
(`google` for Street View, `mapillary`, `kartaview`, `panoramax`, or none for
|
||||
whichever the panel has lit) and a point, which defaults to the spot the camera looks at. Street View
|
||||
the lit one) and a point, by default the spot the camera looks at. Street View
|
||||
shows only on the Google 3D map, so a `google` source sets `map` to
|
||||
`photoreal` and any other map is refused; a view cannot both follow an entity
|
||||
and open photos. The `street-level` layer is turned on. Links carry it as
|
||||
`sl=<source>,<lat>,<lon>`; once the link has restored, the app runs its
|
||||
`show_street_level` action, which flies to the point at street framing and
|
||||
opens the nearest photo. A plain link with a `google` source stops before the
|
||||
billed load: the panel is armed and OPEN GOOGLE STREET VIEW is one click away.
|
||||
A view posted into the embedded app, or the panel page's own opening link,
|
||||
opens the photo, since it is the client's own request; `?embed=1` on a
|
||||
published link does not. Mapillary, KartaView and Panoramax links open the
|
||||
photo at once; none of them bills per image. A link that follows an entity ignores `sl`.
|
||||
`photoreal` and refuses another; a view cannot both follow an entity and open
|
||||
photos. Links carry it as `sl=<source>,<lat>,<lon>`: once restored, the app
|
||||
runs `show_street_level`, which flies there and opens the nearest photo. A
|
||||
plain link with a `google` source only arms the panel for OPEN GOOGLE STREET
|
||||
VIEW; a view posted into the embedded app, or the panel page's own opening
|
||||
link, opens it, and `?embed=1` on a published link does not. Mapillary,
|
||||
KartaView and Panoramax, which do not bill per image, open at once. A link
|
||||
that follows an entity ignores `sl`.
|
||||
|
||||
Tools take a view as `VIEW_ARGUMENTS`: an `area` to frame from above, or a
|
||||
`camera`, plus `layers`, `style`, `map`, `follow`, `annotations` and
|
||||
|
||||
+19
-31
@@ -183,8 +183,7 @@ function flyTo(viewer, camera) {
|
||||
|
||||
/**
|
||||
* The `show_street_level` arguments for a view's street-level photos. A plain
|
||||
* link (`autoOpen: false`) never loads a photo from a source that bills every
|
||||
* load: the app arms the panel and the OPEN button is one click away. A view
|
||||
* link (`autoOpen: false`) only arms a source that bills every load; a view
|
||||
* posted into the embedded app is the client's own request and opens.
|
||||
*/
|
||||
function streetLevelArgs(streetLevel, { autoOpen = true } = {}) {
|
||||
@@ -260,20 +259,18 @@ export async function applyView(
|
||||
if (view.annotations.length)
|
||||
await act('annotate_map', { annotations: [...view.annotations] });
|
||||
if (view.street_level) {
|
||||
// The photos frame the camera: the action flies to the point at street
|
||||
// level, lights the source and opens the nearest photo. When it fails
|
||||
// before flying, the view's own camera still applies.
|
||||
// The photos frame the camera; when the action fails before flying, the
|
||||
// view's own camera still applies.
|
||||
const result = await act(
|
||||
'show_street_level',
|
||||
streetLevelArgs(view.street_level, { autoOpen }),
|
||||
);
|
||||
if (result?.ok !== true) {
|
||||
// Overtaken (a click on the map, a newer request): the panel and the
|
||||
// camera are someone else's now.
|
||||
// Overtaken (a click on the map, a newer request): hands off.
|
||||
if (result?.cancelled || result?.superseded) return steps;
|
||||
// An earlier view's photo must not stay over a place this view left.
|
||||
// From the imagery stage on the action has settled the panel itself:
|
||||
// a photo it found but could not load keeps its error in view.
|
||||
// From the imagery stage on the action has settled the panel itself
|
||||
// (a photo it could not load keeps its error).
|
||||
if (result?.stage !== 'imagery')
|
||||
dataManager.layers?.get?.(STREET_LEVEL_LAYER)?.module?.closeViewer?.();
|
||||
if (!result?.location)
|
||||
@@ -331,18 +328,13 @@ export function installViews({
|
||||
const hashParams = new URLSearchParams(
|
||||
String(location?.hash || '').replace(/^#/, ''),
|
||||
);
|
||||
// What the link itself asks for runs first, one thing at a time; posted
|
||||
// views queue behind it. One failing step does not take the next with it.
|
||||
// The camera's owner at restore: anyone who navigates while the link's
|
||||
// steps wait (a gesture, a search, the assistant) takes it, and a passive
|
||||
// link must not fly over them.
|
||||
// A start-up restore that a newer link superseded hands the camera to that
|
||||
// link: its own steps never run.
|
||||
// A restore whose camera never landed (the recipient dragged, searched or
|
||||
// opened another place during its flight) leaves the camera with them.
|
||||
// The link's own steps run first, one at a time, and posted views queue
|
||||
// behind them; one failing step does not take the next with it. A passive
|
||||
// link never flies over someone who took the camera: its steps are skipped
|
||||
// when a newer link superseded it, when its flight never landed (the
|
||||
// recipient dragged or searched), or after any navigation since the
|
||||
// generation its restore flight owned.
|
||||
const cameraLanded = (outcome) => outcome?.share?.camera === 'applied';
|
||||
// The generation the restore's own flight owned: a navigation after it
|
||||
// landed, while the layers still loaded, has moved on from it.
|
||||
const restoreGeneration = (outcome) =>
|
||||
Number.isInteger(outcome?.navigationToken)
|
||||
? outcome.navigationToken
|
||||
@@ -369,11 +361,9 @@ export function installViews({
|
||||
const linked = annotationsFromParams(hashParams);
|
||||
if (linked.length)
|
||||
queue = linkStep(() => run('annotate_map', { annotations: linked }));
|
||||
// A link to street-level photos opens them once the link has restored. A
|
||||
// link anyone can publish stops short of a billed load and leaves it to a
|
||||
// click; only the panel page's own link for a result issued just now is
|
||||
// the client's request (a host remounting the panel hands back old ones).
|
||||
// A link that follows an entity keeps the camera on it.
|
||||
// A link's street-level photos open once it has restored. A billed source
|
||||
// only arms, except for the panel page's own link to a result issued just
|
||||
// now. A link that follows an entity keeps the camera on it.
|
||||
const linkedStreetLevel = (params) =>
|
||||
followFromParams(params) ? null : streetLevelFromParams(params);
|
||||
const openLinkedStreetLevel = async (
|
||||
@@ -388,9 +378,8 @@ export function installViews({
|
||||
streetLevelArgs(streetLevel, { autoOpen }),
|
||||
{ origin: 'programmatic' },
|
||||
);
|
||||
// The restore window keeps the panel collapsed; OPEN must be in view.
|
||||
// Embed mode hides the rail that holds it, so there the link only arms
|
||||
// (a posted view is the way to open a photo in a frame).
|
||||
// OPEN must be in view, but embed mode hides the rail that holds it: in
|
||||
// a frame a posted view is the way to open a photo.
|
||||
if (result?.armed === true && !isEmbedded(location)) {
|
||||
shell.setPanelCollapsed?.(STREET_LEVEL_PANEL_ID, false, {
|
||||
explicit: true,
|
||||
@@ -410,9 +399,8 @@ export function installViews({
|
||||
autoOpen: isFreshPanelResult(),
|
||||
}),
|
||||
);
|
||||
// A link pasted into the open tab restores camera and layers on its own;
|
||||
// its photos follow once that restore settles, under the same rule: a plain
|
||||
// link never loads a billed photo by itself.
|
||||
// A link pasted into the open tab: its photos follow once its restore
|
||||
// settles, under the same rules.
|
||||
const onNavigatedRestore = (event) => {
|
||||
if (event?.detail?.status !== 'settled' || !cameraLanded(event.detail))
|
||||
return;
|
||||
|
||||
@@ -697,22 +697,19 @@ export function createStreetLevelLayer({
|
||||
/**
|
||||
* Open the nearest image of the active providers (or `providerIds`) near
|
||||
* a point, else the view centre, or with `aim: 'screen-centre'` the ground
|
||||
* under the screen centre (sky: nothing). `radiusM` widens the search for
|
||||
* providers that can (a geocoded landmark is rarely within 50 m of a
|
||||
* street); clicks keep the default. A caller's `signal` retires the
|
||||
* lookup: nothing opens after it fires. Providers without
|
||||
* `nearestImage` are skipped. `armed: false` (a request of its own, such
|
||||
* as the assistant's) does not use a link's armed point, and keeps it
|
||||
* while it looks; a photo it opens ends it, as any opened photo does. Why
|
||||
* nothing opened is left in `street.nearestOutcome`: 'empty' (no imagery
|
||||
* in reach), 'sky' (the screen centre is not ground), or null (an error).
|
||||
* `replace: true` (a request that takes over the panel) keeps an open
|
||||
* photo up while it looks, so the viewer and EXPAND stay, and closes it
|
||||
* when the lookup opens nothing: the panel shows this request's outcome.
|
||||
* `proceed()` is asked before a found image loads and before an empty
|
||||
* or failed lookup reports; false retires the request and leaves the
|
||||
* panel as it was (a photo that bills every load must not load for a
|
||||
* request its caller has given up).
|
||||
* under the screen centre (sky: nothing). Providers without
|
||||
* `nearestImage` are skipped. Options:
|
||||
* - `radiusM` widens the search where a provider can (a geocoded landmark
|
||||
* is rarely within 50 m of a street);
|
||||
* - `signal` retires the lookup: nothing opens after it fires;
|
||||
* - `armed: false` (the assistant's own request) ignores a link's armed
|
||||
* point and keeps it until a photo opens;
|
||||
* - `replace: true` keeps an open photo (and EXPAND) up while it looks and
|
||||
* closes it if nothing opens;
|
||||
* - `proceed()` is asked before a found photo loads or an empty lookup
|
||||
* reports; false retires the request and leaves the panel as it was.
|
||||
* Why nothing opened is in `street.nearestOutcome`: 'empty', 'sky' or
|
||||
* null (an error).
|
||||
*/
|
||||
async openNearest(
|
||||
point,
|
||||
@@ -737,9 +734,8 @@ export function createStreetLevelLayer({
|
||||
if (replace && state.street.open) closeViewer();
|
||||
};
|
||||
if (!point && aim === 'screen-centre' && armed) {
|
||||
// The button promises the centre of the view: the link's spot only
|
||||
// while the view still shows it, or when the view cannot be picked.
|
||||
// Sky refuses below, as any OPEN press does.
|
||||
// OPEN promises the view centre: the link's spot only while the view
|
||||
// still shows it, or when the view cannot be picked.
|
||||
const centre = armed.point ? screenCentreGround() : null;
|
||||
if (!armed.point) radiusM ??= armed.radiusM;
|
||||
else if (
|
||||
@@ -777,11 +773,9 @@ export function createStreetLevelLayer({
|
||||
callerSignal?.addEventListener('abort', onCallerAbort, { once: true });
|
||||
const retired = () => {
|
||||
callerSignal?.removeEventListener('abort', onCallerAbort);
|
||||
// Only the lookup that still owns the panel may touch it: a newer one
|
||||
// has its own spinner and error.
|
||||
// A newer lookup owns the panel, its spinner and its error.
|
||||
if (nearestLookup !== lookup) return false;
|
||||
nearestLookup = null;
|
||||
// The caller moved on: the panel shows nothing of this lookup.
|
||||
if (state.street.loading) {
|
||||
state.street.loading = false;
|
||||
notify();
|
||||
@@ -822,8 +816,7 @@ export function createStreetLevelLayer({
|
||||
if (proceed && proceed() !== true) return retired();
|
||||
nearestLookup = null;
|
||||
callerSignal?.removeEventListener('abort', onCallerAbort);
|
||||
// The photo asked for stays on screen: a sequence playing in the
|
||||
// photo it replaces stops.
|
||||
// A sequence playing in the replaced photo would step past this one.
|
||||
if (replace && state.street.open) parts.viewerHost.stopPlayback();
|
||||
// The caller's signal still counts while the viewer mounts and loads.
|
||||
return openImage(entry.def.id, imageId, {
|
||||
@@ -834,8 +827,7 @@ export function createStreetLevelLayer({
|
||||
callerSignal?.removeEventListener('abort', onCallerAbort);
|
||||
// Replaced while it looked: the panel is the newer lookup's.
|
||||
if (nearestLookup !== lookup) return false;
|
||||
// Given up by its caller while it looked: the open photo stays, and
|
||||
// the panel reports nothing of this lookup.
|
||||
// Given up by its caller: the panel stays as it was.
|
||||
if (proceed && proceed() !== true) return retired();
|
||||
nearestLookup = null;
|
||||
closeReplaced();
|
||||
@@ -867,8 +859,7 @@ export function createStreetLevelLayer({
|
||||
closeViewer,
|
||||
/**
|
||||
* Keep the open photo up while a request that replaces it switches the
|
||||
* source (the viewer and EXPAND are replaced, not closed). The returned
|
||||
* release closes it if its source is still off by then.
|
||||
* source, so EXPAND stays. Release closes it if its source is still off.
|
||||
*/
|
||||
holdViewer() {
|
||||
viewerHolds += 1;
|
||||
@@ -883,10 +874,9 @@ export function createStreetLevelLayer({
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Prepare a nearest lookup for the next OPEN press without running it: a
|
||||
* link to a source that bills every load stops here. The press opens at
|
||||
* this point with this radius; any other lookup, open or close, the
|
||||
* source switched off, or its map leaving clears it.
|
||||
* Arm a nearest lookup for the next OPEN press without running it (a link
|
||||
* to a source that bills every load). Any other lookup, open or close, the
|
||||
* source switched off or its map leaving clears it.
|
||||
*/
|
||||
armNearest(point, { providerIds = null, radiusM = null } = {}) {
|
||||
state.street.armed = {
|
||||
|
||||
@@ -75,17 +75,14 @@ export const SEQUENCE_IMAGE_FIELDS =
|
||||
'id,captured_at,compass_angle,geometry,is_pano';
|
||||
|
||||
/**
|
||||
* Images asked for in a nearest-image search (show_street_level and links
|
||||
* open the photo nearest a point; the graph API caps the radius at 50 m).
|
||||
* The API returns the radius's images in no particular order and cuts them
|
||||
* at the limit first, so this asks for its most (100): the distance sort and
|
||||
* the imagery filter then choose among the spot's photos, not a few of them.
|
||||
* Images asked for in a nearest-image search: the API cuts at the limit
|
||||
* before any order, so this is its most, and the distance sort and the
|
||||
* imagery filter choose among all of the spot's photos.
|
||||
*/
|
||||
export const NEAREST_LIMIT = 100;
|
||||
/**
|
||||
* A nearest-image search that has not answered (headers and body) in this
|
||||
* long is given up: a link or a posted view waits on it, and so does every
|
||||
* view queued behind that one.
|
||||
* A nearest-image search not answered in full (body included) by then is
|
||||
* given up: a link or posted view, and every view queued behind it, waits.
|
||||
*/
|
||||
export const NEAREST_TIMEOUT_MS = 12_000;
|
||||
/** Fields a nearest-image search needs: where each is, and the filter's. */
|
||||
|
||||
@@ -316,11 +316,9 @@ export class StreetLevelControls {
|
||||
}
|
||||
|
||||
/**
|
||||
* Embed mode hides the right rail, panel and viewer with it: an open photo
|
||||
* moves to <body> as an overlay on the globe (`sl-viewer-wrap-embed`, see
|
||||
* embed.css) and returns to its place in the panel when it closes. The
|
||||
* panel view keeps patching the wrap where it is: its siblings are static,
|
||||
* so Vue never inserts next to it.
|
||||
* Embed mode hides the right rail: an open photo moves to <body> as an
|
||||
* overlay on the globe (`sl-viewer-wrap-embed`, embed.css) and goes back
|
||||
* when it closes. Vue keeps patching the wrap there: its siblings are static.
|
||||
*/
|
||||
_syncEmbedOverlay(open) {
|
||||
const wrap = this._elements.viewerWrap;
|
||||
@@ -404,8 +402,6 @@ export class StreetLevelControls {
|
||||
if (!isEmbedPage()) this.actions.revealPanel?.();
|
||||
}
|
||||
this._wasOpen = open;
|
||||
// Embed mode hides the rail the panel sits in: an open photo is shown over
|
||||
// the globe instead, and goes back into the panel when it closes.
|
||||
this._syncEmbedOverlay(open);
|
||||
}
|
||||
|
||||
|
||||
+12
-21
@@ -3819,9 +3819,8 @@ function streetLevelPanelHidden() {
|
||||
* (links) stops short of a billed load: the panel is armed for OPEN.
|
||||
*/
|
||||
export async function showStreetLevel(args, context) {
|
||||
// Every await can hand the request's moment to someone else: a newer turn,
|
||||
// a navigation or gesture that takes the camera, or a mode that hides the
|
||||
// panel. Each stage after the place is settled checks again.
|
||||
// After any await a newer turn, a navigation, a globe gesture or a mode
|
||||
// that hides the panel may have taken over: each later stage checks again.
|
||||
const owner = watchCameraOwner(context.viewer, context.styleManager);
|
||||
// A photo held up while the request switches its source is let go however
|
||||
// the request ends: replaced by then, or closed if its source stayed off.
|
||||
@@ -3912,8 +3911,7 @@ async function requestStreetLevel(
|
||||
const status = layerModule.providerStatus?.(provider) || null;
|
||||
providerName = status?.name || provider;
|
||||
|
||||
// The key first, on any map: a chip without a key explains it and switches
|
||||
// nothing, and so does this.
|
||||
// The key first: without one nothing changes, as with a chip.
|
||||
if (status && (status.keyRequired === true || status.configured === false))
|
||||
return fail('key', status.error || `${providerName} needs its key`, {
|
||||
keyRejected: status.keyRejected === true,
|
||||
@@ -3998,9 +3996,8 @@ async function requestStreetLevel(
|
||||
point = { lat: location.latitude, lon: location.longitude };
|
||||
}
|
||||
|
||||
// Cockpit, clean view, recording and scene playback hide the panel the
|
||||
// photo opens in, and may have started during the flight.
|
||||
// The camera is the request's from where its own flight landed.
|
||||
// A mode that hides the panel may have started during the flight. The
|
||||
// camera is the request's from where its flight landed.
|
||||
if (location) owner.claim();
|
||||
const arrived = interrupted('view');
|
||||
if (arrived) return arrived;
|
||||
@@ -4076,13 +4073,11 @@ async function requestStreetLevel(
|
||||
location,
|
||||
});
|
||||
|
||||
// A billed source never loads from a link without a click: everything is
|
||||
// ready and OPEN is one press away at the view centre.
|
||||
// A link never loads a billed photo by itself: it arms OPEN instead.
|
||||
if (args.autoOpen === false && status?.billed === true) {
|
||||
// A photo of another place must not stay up beside the armed request.
|
||||
if (layerModule.getUIState?.()?.street?.open === true)
|
||||
layerModule.closeViewer?.();
|
||||
// The next OPEN press loads exactly what the link asked for.
|
||||
layerModule.armNearest?.(point, {
|
||||
providerIds: [provider],
|
||||
...(snapshot.groundClick === true
|
||||
@@ -4101,13 +4096,10 @@ async function requestStreetLevel(
|
||||
};
|
||||
}
|
||||
|
||||
// A source with no coverage to click (Street View) is searched wider: a
|
||||
// geocoded landmark is rarely within 50 m of a street. A newer turn
|
||||
// retires the lookup: nothing opens after the user moved on. A photo of
|
||||
// another place stays up while it looks (the viewer, EXPAND included, is
|
||||
// replaced, not closed) and closes if this request opens nothing: the
|
||||
// panel shows this request's outcome. Once an image is found it loads
|
||||
// only if the camera and the panel are still the request's.
|
||||
// Street View has no coverage to click, so it searches wider: a geocoded
|
||||
// landmark is rarely within 50 m of a street. An open photo stays up while
|
||||
// this looks and closes if nothing opens; a found photo loads only while
|
||||
// the camera and the panel are still the request's.
|
||||
reportProgress('imagery', label);
|
||||
const opened = await layerModule.openNearest(point, {
|
||||
providerIds: [provider],
|
||||
@@ -4123,9 +4115,8 @@ async function requestStreetLevel(
|
||||
: {}),
|
||||
});
|
||||
const street = layerModule.getUIState?.()?.street || {};
|
||||
// A cancelled request opens nothing (the lookup and the viewer both honour
|
||||
// the turn's signal). A photo that opened after the camera moved on, or
|
||||
// into a panel hidden meanwhile, is closed again: nobody asked to see it.
|
||||
// A photo that opened after the camera moved on, or into a panel hidden
|
||||
// meanwhile, is closed again.
|
||||
const late = interrupted('imagery');
|
||||
if (late) {
|
||||
if (opened === true) layerModule.closeViewer?.();
|
||||
|
||||
Reference in new issue
Block a user