Skip to content

Improve camera node rendering#137

Draft
dougborg wants to merge 9 commits intoFoggedLens:mainfrom
dougborg:feat/camera-rendering-improvements
Draft

Improve camera node rendering#137
dougborg wants to merge 9 commits intoFoggedLens:mainfrom
dougborg:feat/camera-rendering-improvements

Conversation

@dougborg
Copy link
Collaborator

@dougborg dougborg commented Mar 3, 2026

Summary

  • Add marker clustering at low zoom (≤13) using flutter_map_marker_cluster to prevent overcrowding
  • Replace donut-shaped direction cones with wedge/pie shapes emanating from camera center, with smoother arcs (36 pts/90° vs 12/45°)
  • Zoom-gate static camera cones to only appear at zoom ≥14 (session cones remain always visible during add/edit)
  • Scale camera marker diameter with zoom level (8–28px, 18px baseline at z15)
  • Raise default max rendered nodes from 500 to 2000 (clustering handles visual density; still user-configurable in settings for lower-end devices)

Test plan

  • flutter analyze — 0 issues
  • flutter test — all 66 tests pass
  • Manual: zoom from level 10→19, markers scale smoothly, cones appear at 14
  • Manual: at zoom 10–13, clusters visible with counts, no cones
  • Manual: at zoom 14+, individual markers with smooth wedge-shaped cones
  • Manual: add/edit camera session — cone always visible regardless of zoom
  • Manual: tap cluster to zoom in and break cluster apart
  • Manual: verify performance with many cameras on low-end device

🤖 Generated with Claude Code

dougborg and others added 6 commits March 7, 2026 12:34
- Add ServicePolicy framework with OSM-specific rate limiting and TTL
- Add per-provider disk tile cache (ProviderTileCacheStore) with O(1)
  lookup, oldest-modified eviction, and ETag/304 revalidation
- Rewrite DeflockTileProvider with two paths: common (NetworkTileProvider)
  and offline-first (disk cache -> local tiles -> network with caching)
- Add zoom-aware offline routing so tiles outside offline area zoom ranges
  use the efficient common path instead of the overhead-heavy offline path
- Fix HTTP client lifecycle: dispose() is now a no-op for flutter_map
  widget recycling; shutdown() handles permanent teardown
- Add TileLayerManager with exponential backoff retry (2s->60s cap),
  provider switch detection, and backoff reset
- Guard null provider/tileType in download dialog with localized error
- Fix Nominatim cache key to use normalized viewbox values
- Comprehensive test coverage (1800+ lines across 6 test files)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow offline area downloads for OSM tile server. Move the "downloads
not permitted" check from inside the download dialog to the download
button itself — the button is now disabled (greyed out) when the
current tile type doesn't support offline downloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a user edits a tile type's URL template, max zoom, or API key
without changing IDs, the cached DeflockTileProvider would keep the old
frozen config. Now _getOrCreateProvider() computes a config fingerprint
and replaces the provider when drift is detected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When Overpass queries hit the 50k node limit, _fetchSplitAreas() splits
the area into 4 quadrants. Previously these were fetched sequentially —
at max split depth (3) that's up to 64 serial HTTP requests, which is
the primary cause of the slow "splitting" network status users report.

This change:

- Parallelizes quadrant fetches with Future.wait() instead of a for-loop
- Adds a dynamic concurrency limiter (_AsyncSemaphore) sized from the
  Overpass /api/status slot count, so we never exceed the server's
  per-IP rate limit
- Replaces the "sleep 30s and give up on 429" behavior with smart retry:
  polls /api/status until a slot is available (modeled after OSMnx and
  OSMPythonTools), then retries up to 2 times
- Resizes the semaphore on retry with the latest observed slot count,
  adapting to changing server conditions

Refactors for testability:
- HTTP client injection in OverpassService (following RoutingService pattern)
- DI constructors + forTesting() factories on NodeDataManager and
  NodeSpatialCache
- splitBounds made static + @VisibleForTesting for direct unit testing

18 new tests covering: splitBounds geometry, getSlotCount/waitForSlot
parsing, fetchWithSplitting (happy path, split, max depth, rate limit
retry + cap), partial/total quadrant failure, recursive splitting, and
semaphore init deduplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the user pans/zooms mid-fetch, queued sub-requests now bail out
instead of blocking the semaphore. Each getNodesFor() call increments
_fetchGeneration; fetchWithSplitting checks _isStale(generation) at
6 cooperative checkpoints (before semaphore, inside lambda, before
splitting, before/after waitForSlot, top of _fetchSplitAreas).

Null generation (offline download, existing tests) is never stale,
so there is zero regression risk for non-production-path callers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each completed quadrant now triggers notifyListeners() immediately so the
map renders nodes as they arrive, rather than waiting for all quadrants
to finish. NodeProviderWithCache listens to NodeDataManager directly,
replacing the manual notifyListeners() after getNodesFor() returns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dougborg dougborg force-pushed the feat/camera-rendering-improvements branch from 670dc14 to 7256950 Compare March 7, 2026 19:39
dougborg and others added 3 commits March 7, 2026 12:41
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add flutter_map_marker_cluster for clustering camera markers at zoom <= 13
- Replace donut-shaped direction cones with wedge/pie shapes emanating from
  camera center, with smoother arcs (36 points/90° vs 12/45°)
- Zoom-gate static camera cones to only appear at zoom >= 14 (session cones
  remain always visible during add/edit)
- Scale camera marker diameter with zoom level (8–28px range, 18px at z15)
- Move user location marker out of node markers into separate layer to avoid
  it being clustered with camera nodes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clustering now handles visual density at low zoom, so the previous 500
limit was overly conservative. Raise to 2000 while keeping it user-
configurable in settings for lower-end devices. Bump the performance
warning threshold from 1000 to 5000 to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dougborg dougborg force-pushed the feat/camera-rendering-improvements branch from 7256950 to 25bf473 Compare March 7, 2026 19:41
@stopflock
Copy link
Collaborator

There are performance gains to be had in this area. Clustering would be good, matches the website.
We talked a little about this but the nodes should not scale with zoom.
Wedge/pie shaped FOV cones are going to look weird if you can see the vertex in the middle of the dot. Maybe I just wasn't smart enough to make it work but we're definitely spending a lot of CPU cycles on those cones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants