tighten the remaining voice outline review items

- One place-name key for server and tiles, so "St. Paul" and "Saint Paul"
  agree in Nominatim name checks.
- Tile street names: a lone letter is a direction only beside another name
  word ("E Street" is not "East Street"), and a trailing quadrant ("E St NW")
  may go unsaid in the ask.
- A building cut at the tile edge that is nearer the point than any whole
  one leaves the point instead of a farther neighbour standing in; an edge
  without a tile buffer counts as a cut, so a cut area never answers grounds.
- A batch building install re-judges only results that installed; a late
  timeout during caption conversion keeps the pending point drawn; a failed
  plain recaption keeps the old mark addressable.
- An interrupted Nominatim dispatch is reclaimed by the next lock owner ten
  minutes after the start its marker names; node:sqlite loads on first use
  without its experimental warning.
- PHOTON_URL accepts only https, which the page's connect-src allows.
- Shared OpenFreeMap tiles decode each layer when a consumer first selects
  it; outline selection reuses the shared ring area and simplification, and
  ground probes the shared ring centroid; the grounds word list lives once.
This commit is contained in:
Sameh Khamis committed 2026-11-02 08:50:05 -08:00
1 parent a38a065c5c
commit a61d0f4c2a
24 files changed
+422 -128

No files matched your search

+14 -11
View File
@@ -5380,16 +5380,19 @@ Explicit public-service configuration retains an identifying `User-Agent` and `R
least 1.1 seconds between public search and outline starts, a shared durable
per-install daily allowance and pause state that fail closed when unusable.
The shared state lock covers actual fetch invocation and recording the next
start. An unfinished dispatch marker disables further public requests until
operator recovery; it is never automatically reclaimed. These controls do not
establish an application-wide public-service allowance or authorize its use.
For manual recovery, stop every process sharing the state file and account for
the interrupted dispatch before removing only its confirmed orphan
`state.json.dispatch.*` marker. Preserve the state file's count, pause and next
start, and wait at least the public spacing interval after the last possible
invocation. `stats().persistence.dispatchPending` exposes the marker; an unlocked
status read can also briefly see a live dispatch. Do not clear markers while an
owner is active or reset usage state to restore access.
start. An unfinished dispatch marker (`state.json.dispatch.since-<ms>.*`, left
by a crash or a persistence failure) disables further public requests for ten
minutes after the start it names, far beyond any request's 9-second timeout.
A marker exists only inside the lock, so the next lock owner after that window
removes it and restarts spacing from its own clock; the count was persisted
before the interrupted invocation. A marker naming no start keeps refusing
until removed by hand. These controls do not establish an application-wide
public-service allowance or authorize its use. For manual recovery, stop every
process sharing the state file before removing only a confirmed orphan marker,
and preserve the state file's count, pause and next start.
`stats().persistence.dispatchPending` exposes the marker; an unlocked status
read can also briefly see a live dispatch. Do not reset usage state to restore
access.
Answers are cached
for 30 days (search), 90 days (outline) or 24 hours (genuine no result),
one upstream call shared between identical searches
@@ -5463,7 +5466,7 @@ request viewbox and continues searching without that geographic preference.
- Runtime entry: `src/main.js` calls `initAnnotations({ viewer, tileset })`, exposes `window.__gevAnnotations`, and passes the engine into the voice action runner.
- Engine contract: `src/annotations/annotationEngine.js` owns annotation state, TTL/fade lifecycle, concurrent anchor resolution, duplicate detection keyed on geometry, cancellation on clear/newer generations, and a hard cap of 120 live marks. Deferred outline upgrades drain FIFO at concurrency 2; queued work retains the owning abort controller and is discarded on a generation change before it can fetch.
- Resolver contract: `src/annotations/resolver.js` converts names, coordinates and screen pixels into world anchors and optional geometry. Google Geocode/Places supplies a centroid and scope; route requests use `/api/route`; ambiguous or distant results are rejected or recovered near the current view instead of drawing misleading blobs. Outline resolution keeps this order: bundled admin/place/neighborhood/Natural Earth geometry; OpenFreeMap street or pointed-building geometry where applicable; the configured OSM/Overpass admin/place/footprint/street/enclosing-area ladder; guarded Nominatim for eligible named cities, admin units, landmarks, buildings and grounds; then an OpenFreeMap building fallback that accepts a named building only when its resolved anchor lies in the selected filled footprint. Pointed-at building requests retain nearby selection within 40 m. Tile building and grounds answers also need the tile holding the anchor: when that tile fails to load and only neighbours arrive, no neighbouring building or area stands in. Tile grounds answer a named grounds ask only from an area of the same name, or, when the ask's head noun names a kind of grounds (school, university/college, hospital, cemetery, stadium, zoo; "campus" alone names none), from the part of the smallest unnamed area of that class that holds the anchor (a class word elsewhere in a name, as in "University Avenue Park", does not count); a differently named area never answers. If no rung can supply honest geometry, the mark remains a point or reports the capability unavailable. A real polygon, definitive miss, configured-ladder approximation, rate limit or transient result keeps its existing finality; Nominatim is only tried after higher-priority configured sources cannot answer. Building tile selection returns the selected component and its holes; a shared vector feature ID does not establish that unrelated components are one building. Other trusted multi-part outlines and holes are retained in `anno.polygons`, with `ring` anchored to the part whose filled area contains the resolved point, outside its holes, when possible. Guarded Nominatim keeps every accepted result for an ask, ranks names holding every asked word first, refuses a partial landmark match whose name lacks the ask's last word (the place around a feature, such as a park around its zoo, is not the feature), and answers each ask with the cached result whose filled area holds the resolved anchor, else the first. A street ribbon that holds neither its ring mean nor the anchor is centred on the street's nearest point to the anchor. Missing capability never invents a radius outline. Only explicitly country/state/county-scoped asks bypass near-view recovery and proximity gating; state scope requires a leading `state of …`/`the state of …` phrase, while bare names, proper names ending in “State,” and administrative geocode result types alone remain guarded. Overpass throttles remain distinct from normal transients: `Retry-After` is honored for one retry, and a repeated throttle ends only that mark's outline upgrade.
- Resolver contract: `src/annotations/resolver.js` converts names, coordinates and screen pixels into world anchors and optional geometry. Google Geocode/Places supplies a centroid and scope; route requests use `/api/route`; ambiguous or distant results are rejected or recovered near the current view instead of drawing misleading blobs. Outline resolution keeps this order: bundled admin/place/neighborhood/Natural Earth geometry; OpenFreeMap street or pointed-building geometry where applicable; the configured OSM/Overpass admin/place/footprint/street/enclosing-area ladder; guarded Nominatim for eligible named cities, admin units, landmarks, buildings and grounds; then an OpenFreeMap building fallback that accepts a named building only when its resolved anchor lies in the selected filled footprint. Pointed-at building requests retain nearby selection within 40 m. Tile building and grounds answers also need the tile holding the anchor: when that tile fails to load and only neighbours arrive, no neighbouring building or area stands in. Tile grounds answer a named grounds ask only from an area of the same name, or, when the ask's head noun names a kind of grounds (school, university/college, hospital, cemetery, stadium, zoo; "campus" alone names none), from the part of the smallest unnamed area of that class that holds the anchor (a class word elsewhere in a name, as in "University Avenue Park", does not count); a differently named area never answers. If no rung can supply honest geometry, the mark remains a point or reports the capability unavailable. A real polygon, definitive miss, configured-ladder approximation, rate limit or transient result keeps its existing finality; Nominatim is only tried after higher-priority configured sources cannot answer. Building tile selection returns the selected component and its holes; a shared vector feature ID does not establish that unrelated components are one building. Other trusted multi-part outlines and holes are retained in `anno.polygons`, with `ring` anchored to the part whose filled area contains the resolved point, outside its holes, when possible. Guarded Nominatim keeps every accepted result for an ask, ranks an exact name first, then a name holding every asked word, then a partial match; a partial landmark match whose name lacks the ask's last word is refused unless that word appears in the result's address or type (the place around a feature, such as a park around its zoo, is not the feature, while "Space Needle Seattle" still finds the Space Needle). Each ask is answered with the cached result whose filled area holds the resolved anchor (read across the antimeridian), else the first. Server and tiles fold place names with one key, so "St. Paul" and "Saint Paul" agree. A street ribbon that holds neither its ring mean nor the anchor is centred on the street's nearest point to the anchor when the street runs within 100 m of it. In tile street names a lone letter is a direction only beside another name word ("N Lamar Blvd"), so "E Street" is not "East Street", and a trailing quadrant ("E St NW") may go unsaid in the ask. A building or area cut at the tile edge (with or without a tile buffer) is not complete: a cut building nearer the point than any whole one leaves the point, and a cut area does not answer grounds. Missing capability never invents a radius outline. Only explicitly country/state/county-scoped asks bypass near-view recovery and proximity gating; state scope requires a leading `state of …`/`the state of …` phrase, while bare names, proper names ending in “State,” and administrative geocode result types alone remain guarded. Overpass throttles remain distinct from normal transients: `Retry-After` is honored for one retry, and a repeated throttle ends only that mark's outline upgrade.
- With no detailed-feature capability, annotations retain their pins and say
"Detailed outline unavailable"; location focus reports the same result without
retrying. Missing capability never synthesizes an outline from a radius.