feat/31-pois-from-vector-tile #33

Merged
jeroen merged 3 commits from feat/31-pois-from-vector-tile into main 2026-08-10 12:09:55 +00:00
Owner
No description provided.
The transit and shop markers are streamed from the Overpass API: one live
bbox query per z14 cell, 49 cells per layer per anchor, against a service
that rate-limits, reports a timeout as HTTP 200, and needs a 15 s → 30 s →
1 m → 2 m → 4 m backoff ladder in the client to survive. Meanwhile
`map_geometry` has already fetched, cached and decoded the tile those
markers stand on, and Shortbread puts the same features in it — measured on
the Groningen z14 fixture (z14/8490/5319): `public_transport` holds 25 stops
and `pois` holds 467 features, 55 of them carrying a `shop`.

So the placement costs no request at all. `pois::extract_pois` is the reader
for it, with the two rules `furniture` already follows for the same layer —
classify on key *and* value (`pois` has no `kind`), and own exactly
`[0, extent)` so a feature buffered into two neighbouring tiles lands in one
cell with no tolerance to tune.

What the tile does not carry is identity and contact detail: no OSM id, no
`website`, no `addr:street`, no `ref:IFOPT` (the key the live departure
boards are fetched by). Against the same tile Overpass answers 65 shops to
the tile's 55, and 32 of those have a website. That is why the next commit
demotes Overpass rather than deleting it.

Additive only — nothing calls this yet. `single_point` and `tag_str` move
here from `furniture` so the two readers of the `pois` layer share one
implementation rather than drifting.
feat(layers): place POI markers from the vector tile, not Overpass
Some checks failed
CI / cargo check (push) Failing after 4m51s
CI / build & test viberfox (push) Successful in 6m22s
ae12c58ac1
The transit and shop markers were streamed from Overpass: 7×7 cells per
layer per anchor, i.e. 98 live bbox queries to establish a location, against
a service that rate-limits, answers a timeout with HTTP 200, and needs the
15 s → 30 s → 1 m → 2 m → 4 m ladder in `CellStream` to survive. Nothing
appeared until it answered.

A LAYER_ZOOM cell *is* a tile of the vector source, and Shortbread already
carries these features in the body `map_geometry` fetched for the ground
under them — so the placement costs a `tile_source` cache hit, and a marker
now stands the frame its tile lands. It also inherits that module's
single-flight and byte cache instead of keeping a parallel one.

**Overpass is demoted, not deleted**, because the tile does not carry
identity or contact detail. Measured on the fixture tile (z14/8490/5319):
Overpass answers 65 shops to the tile's 55, and 32 of those have a website
the tile has no field for; `ref:IFOPT`, which `transit_live` keys departure
boards by, is likewise absent. `Feature::id` looks like an OSM id and is not
one — zero of the 55 intersect the node ids Overpass returns for the same
bbox — so it is neither a join key nor a link. The detail pass therefore
runs on its own stream at DETAIL_CELL_RADIUS = 1 (9 cells, not 49) and
`merge_details` patches its answers onto markers that are already standing,
matching on position and category. It spawns nothing, may arrive in either
order, and may never arrive at all: the mesh depends only on position and
category, which came from the tile.

Radius 1 is sized to its two readers — a popup needs the POI within
`handle_poi_click`'s 500 m and a board needs its stop within
`transit_live::FETCH_RANGE`'s 800 m, both inside a 3×3 ring of ~1.4 km cells.

A raster tile source has no point layers to read, so Overpass stays the
whole marker source there — `--shot` runs with no manifest `tile_url` land
on that path, which keeps it live rather than dead.

The classifiers are now two readings of one vocabulary (`shop_category`, the
transit `kind` table) with a test that they agree: a stop drawn from the tile
and described by Overpass must not end up in different categories, or a live
board gets mounted on a marker drawn as something else.

Unverified here: the container has no GPU and no wasm target, so this needs
a visual check that the markers stand where they used to and a
`cargo check --target wasm32-unknown-unknown`.
docs(notes): record that the tile now places the POI markers
Some checks failed
CI / cargo check (push) Failing after 5m38s
CI / build & test viberfox (push) Successful in 9m31s
CI / cargo check (pull_request) Failing after 2m25s
CI / build & test viberfox (pull_request) Successful in 8m46s
4a0f501c0c
The layer survey still said `public_transport` was used for "nothing —
transit stops come from Overpass instead", which stopped being true two
commits ago, and the `pois` row named only its street-furniture reader.

Adds the measurement that decides how the two sources now divide: the tile
carries no OSM id, `website`, `addr:street` or `ref:IFOPT`, Overpass answers
65 shops to the tile's 55 with 32 websites among them, and `Feature::id` is
not an OSM id — zero of the 55 intersect the node ids Overpass returns for
the same bbox. That is why the match is position + category, and why a
raster source (a `--shot` run with no manifest `tile_url`) keeps Overpass as
the whole marker source rather than losing its markers.
jeroen merged commit abb99f2105 into main 2026-08-10 12:09:55 +00:00
Sign in to join this conversation.
No description provided.