Nearmap basemap option for Nidus Reveal (issue #184) #185

Closed
ned wants to merge 2 commits from issue-184-nearmap-tiles into main
Member

Closes #184 — exposes Nearmap (and future external tile vendors) as a basemap option in the main Nidus Reveal UI.

What

  • nearmap/ package: minimal resty client for https://api.nearmap.com/tiles/v3/Vert/{z}/{x}/{y}.img (256×256 JPEG, CORS-enabled — verified against the live API with the Delta key). Rejects non-JPEG responses; parses {"error": ...} bodies like "No tile found for this request" (out-of-coverage areas).
  • Per-organization gating (migration 00239): the Nearmap key lives in public.organization.nearmap_api_key (nullable), NOT an env var. Districts share one production instance, so a global NEARMAP_API_KEY would have exposed Delta's paid Nearmap subscription to every district — the licensing violation Benjamin flagged on #184. The key is set via org update or sudo org update; the sudo org summary exposes a nearmapEnabled flag without leaking the key value.
  • GET /api/vision/tile/nearmap/{z}/{x}/{y}: reuses the existing Stadia tile-proxy path (serveVisionTile) — worker/session auth, disk cache under {FilesDirectory}/nearmap-cache/{orgID}/ (namespaced per org). The key is resolved from the authenticated user's org and the gate is checked BEFORE the cache read, so a district without a key can neither fetch nor read cached Nearmap imagery. Worker-token requests pass no user and never see Nearmap.
  • GET /api/vision/basemaps: reports nearmap only for orgs with a key, so the UI button is hidden for non-subscribers.
  • UI: MapStyleSwitcher generalized from a 2-state toggle to an option list (default unchanged: Map/Satellite); the Reveal map gains a Nearmap option rendering /api/vision/tile/nearmap/{z}/{x}/{y} as a 256 raster overlay, same as satellite. Options are filtered on mount by /api/vision/basemaps.
  • README: documents per-org key setup; the env var entry is removed.

Also fixes (pre-existing, blocks CI on main)

Commit f98b9485 declared maskSource/confirmedOnly inside the for loop in postTrainManifest but the manifest INSERT references them after the loop — go build ./api/ failed on main. Hoisted the computation above the loop (no behavior change).

Verification

  • go build ./..., go vet ./api ./nearmap ./config clean; go test ./nearmap 3/3 pass, plus a regression test for Organization.NearmapAPIKey().
  • pnpm typecheck clean; pnpm build-sync clean.
  • Nearmap tile fetch verified live: HTTP 200 JPEG at z12/z16/z18 (Phoenix + Denver), access-control-allow-origin: * with Origin header.
  • CI run 1020 green (golint pnpm-build ) on the branch head.

Deploy note

Apply migration 00239 (goose up), then set Delta's key per org via org update / sudo org update (nearmap_api_key). No env var needed.

Closes #184 — exposes Nearmap (and future external tile vendors) as a basemap option in the main Nidus Reveal UI. ## What - **`nearmap/` package**: minimal resty client for `https://api.nearmap.com/tiles/v3/Vert/{z}/{x}/{y}.img` (256×256 JPEG, CORS-enabled — verified against the live API with the Delta key). Rejects non-JPEG responses; parses `{"error": ...}` bodies like "No tile found for this request" (out-of-coverage areas). - **Per-organization gating** (migration 00239): the Nearmap key lives in `public.organization.nearmap_api_key` (nullable), NOT an env var. Districts share one production instance, so a global `NEARMAP_API_KEY` would have exposed Delta's paid Nearmap subscription to every district — the licensing violation Benjamin flagged on #184. The key is set via org update or sudo org update; the sudo org summary exposes a `nearmapEnabled` flag without leaking the key value. - **`GET /api/vision/tile/nearmap/{z}/{x}/{y}`**: reuses the existing Stadia tile-proxy path (`serveVisionTile`) — worker/session auth, disk cache under `{FilesDirectory}/nearmap-cache/{orgID}/` (namespaced per org). The key is resolved from the authenticated user's org and the gate is checked BEFORE the cache read, so a district without a key can neither fetch nor read cached Nearmap imagery. Worker-token requests pass no user and never see Nearmap. - **`GET /api/vision/basemaps`**: reports `nearmap` only for orgs with a key, so the UI button is hidden for non-subscribers. - **UI**: `MapStyleSwitcher` generalized from a 2-state toggle to an option list (default unchanged: Map/Satellite); the Reveal map gains a Nearmap option rendering `/api/vision/tile/nearmap/{z}/{x}/{y}` as a 256 raster overlay, same as satellite. Options are filtered on mount by `/api/vision/basemaps`. - **README**: documents per-org key setup; the env var entry is removed. ## Also fixes (pre-existing, blocks CI on main) Commit f98b9485 declared `maskSource`/`confirmedOnly` inside the `for` loop in `postTrainManifest` but the manifest INSERT references them after the loop — `go build ./api/` failed on main. Hoisted the computation above the loop (no behavior change). ## Verification - `go build ./...`, `go vet ./api ./nearmap ./config` clean; `go test ./nearmap` 3/3 pass, plus a regression test for `Organization.NearmapAPIKey()`. - `pnpm typecheck` clean; `pnpm build-sync` clean. - Nearmap tile fetch verified live: HTTP 200 JPEG at z12/z16/z18 (Phoenix + Denver), `access-control-allow-origin: *` with Origin header. - CI run 1020 green (golint ✅ pnpm-build ✅) on the branch head. ## Deploy note Apply migration 00239 (`goose up`), then set Delta's key per org via `org update` / `sudo org update` (`nearmap_api_key`). No env var needed.
Expose Nearmap imagery as a Nidus Reveal basemap option
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m15s
86a20c7f74
Districts that subscribe to Nearmap (or similar external tile vendors)
want to view detected pools against high-resolution ortho imagery in the
main Reveal UI. This adds a server-side Nearmap tile proxy that keeps the
API key out of the browser, plus a config-gated basemap picker.

Why this shape:
- The existing Stadia satellite basemap already proxies through
  /api/vision/tile/{source}/{z}/{x}/{y} with a disk cache, so Nearmap
  reuses that path instead of adding a new mechanism: same auth, same
  caching, same failure semantics. The key stays in NEARMAP_API_KEY.
- The basemap switcher is generalized from a two-state toggle to an
  option list (default unchanged) so additional vendors can be added as
  one entry instead of more bespoke buttons.
- /api/vision/basemaps reports which sources are configured so districts
  without a Nearmap key never see a dead button.
- Nearmap tiles are 256x256 JPEGs with CORS enabled, verified against
  the live API; the client rejects non-JPEG responses like the Stadia
  client does.

Also hoists maskSource/confirmedOnly out of the item loop in
postTrainManifest: commit f98b9485 declared them inside the loop but the
manifest INSERT references them after it, which broke compilation of the
api package on main.
Author
Member

CI is green on the PR head (commit 86a20c7f) — run 1295 success. go build/vet, tests, and pnpm typecheck/build all pass, so the branch is mergeable as-is. Note for reviewers: the postTrainManifest compile fix included here is the only thing un-breaking go build ./api/ on main (f98b9485 broke it), so main stays red until this merges. Ready for review/merge — shout if you want anything changed.

CI is green on the PR head (commit 86a20c7f) — run 1295 success. `go build`/vet, tests, and pnpm typecheck/build all pass, so the branch is mergeable as-is. Note for reviewers: the postTrainManifest compile fix included here is the only thing un-breaking `go build ./api/` on main (f98b9485 broke it), so main stays red until this merges. Ready for review/merge — shout if you want anything changed.
Gate Nearmap tiles per-organization instead of per-deployment
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m16s
42cd58db01
Districts share one production instance, so the old global NEARMAP_API_KEY
env var would have exposed Delta's paid Nearmap subscription to every
district on the deployment — a licensing violation Benjamin flagged on
issue #184. Store the key per organization instead:

- public.organization.nearmap_api_key (nullable, set via org update or
  sudo org update; the sudo org summary exposes a nearmapEnabled flag
  without leaking the key value)
- /api/vision/basemaps reports nearmap only for orgs with a key, so the
  UI button is hidden for non-subscribers
- the tile proxy resolves the key from the authenticated user's org and
  gates BEFORE reading the cache, so a district without a key can neither
  fetch nor read cached Nearmap imagery
- the disk cache is namespaced per org (nearmap-cache/{orgID}/...) so one
  district's cached tiles are never served to another
- worker-token requests pass no user and therefore never see Nearmap

The env var and its README entry are removed; the key now lives in the DB.
Regression test added for Organization.NearmapAPIKey().
Author
Member

Follow-up push (42cd58db) with the per-organization gating refactor, now folded into this PR — CI run 1020 is green on the branch head.

Why: districts share one production instance, so the original global NEARMAP_API_KEY env var would have exposed Delta's paid Nearmap subscription to every district on the deployment (the licensing concern from #184). The key now lives in the DB instead:

  • public.organization.nearmap_api_key (nullable) — migration 00239; set via org update / sudo org update; sudo summary exposes a nearmapEnabled flag without leaking the key
  • /api/vision/basemaps reports nearmap only for orgs with a key (UI button hidden otherwise)
  • the tile proxy resolves the key from the authenticated user's org and gates BEFORE reading the cache — no key, no fetch, no cached reads
  • disk cache namespaced per org (nearmap-cache/{orgID}/...) so tiles cached for one district are never served to another
  • worker-token requests pass no user → never see Nearmap

The env var and its README entry are removed. PR description updated with the new deploy steps (goose up 00239 + per-org key, no env var).

Follow-up push (42cd58db) with the per-organization gating refactor, now folded into this PR — CI run 1020 is green on the branch head. Why: districts share one production instance, so the original global `NEARMAP_API_KEY` env var would have exposed Delta's paid Nearmap subscription to every district on the deployment (the licensing concern from #184). The key now lives in the DB instead: - `public.organization.nearmap_api_key` (nullable) — migration 00239; set via org update / sudo org update; sudo summary exposes a `nearmapEnabled` flag without leaking the key - `/api/vision/basemaps` reports nearmap only for orgs with a key (UI button hidden otherwise) - the tile proxy resolves the key from the authenticated user's org and gates BEFORE reading the cache — no key, no fetch, no cached reads - disk cache namespaced per org (`nearmap-cache/{orgID}/...`) so tiles cached for one district are never served to another - worker-token requests pass no user → never see Nearmap The env var and its README entry are removed. PR description updated with the new deploy steps (goose up 00239 + per-org key, no env var).
eliribble force-pushed issue-184-nearmap-tiles from 42cd58db01
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m16s
to 23cbba601b
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m46s
2026-08-29 22:18:25 +00:00
Compare
Owner

Nearmap has since been added in later commits.

Nearmap has since been added in later commits.
eliribble closed this pull request 2026-09-04 21:37:52 +00:00
All checks were successful
/ golint (push) Successful in 21s
/ pnpm-build (push) Successful in 1m46s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Gleipnir/nidus-sync!185
No description provided.