Client-side contour rendering - #13
Draft
bkeepers wants to merge 1 commit into
Draft
Conversation
layers() and style() gain a client-contours mode: contour lines and labels can read isolines generated in the browser from the Terrarium DEM via the openwatersio/maplibre-contour fork, whose lineLevels option carries the non-uniform INT ladder (stock only does uniform intervals). Soundings, drying, and coverage stay on the embedded vector source. ft/fm labels unit-convert the metric levels for now; there is no fathom-curve geometry client-side yet. clientContourSource() wraps a consumer-owned DemSource, and INT_ISOBATHS_M is exported (keep in sync with CONTOUR_LEVELS in pipelines/config.py). The viewer A/Bs the modes with ?contours=client. The fork is pinned as a git devDependency; its new prepare script builds dist on install. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
An opt-in client-side contour mode for the style package.
clientContourSource(demSource)wraps a maplibre-contourDemSourceinto a vector source spec using the standard INT isobath levels (INT_ISOBATHS_M, mirrored fromCONTOUR_LEVELSinpipelines/config.py), andstyle({ tilesBase, clientContours })switches the contour lines and labels onto it. Soundings, drying areas, and coverage always come from the embedded vector tiles. The demo viewer A/Bs the two modes with?contours=client.The isolines come from the openwatersio/maplibre-contour fork, pinned as a git dependency. Stock maplibre-contour only accepts uniform
[minor, major]intervals per zoom, which cannot express the INT ladder (2/5/10/20/30...). The fork addslineLevels(fixed level lists), spot soundings, and isoband polygons. openwatersio/maplibre-contour#1 adds apreparescript and a canvas upgrade so the fork installs as a git dependency on modern node.Verified live at NY harbor: client mode renders isolines on the depth-band edges with
eleproperties, soundings are identical in both modes, and no page errors. What you gain over the embedded tiles: contours at any zoom (the Worker's overzoomed DEM feeds them), and level changes without a pipeline rebuild.Why this is a draft
Client mode works, but it is not chart-grade yet. Three gaps, smallest first.
Jagged lines
Marching squares emits vertices quantized to the DEM grid, so lines stairstep. The DEM itself is already smoothed (the Worker serves the same slope-smoothed merged DEM the embedded contours are traced from), so the fix is post-trace geometry smoothing, the same job the pipeline's Chaikin pass does. Upstream has a draft PR adding smoothing options (onthegomap/maplibre-contour#416) that could be adapted into the fork. Seams need care: maplibre-contour traces in buffered tile space from shared neighbor data, so a deterministic smoother with a slightly larger buffer should produce identical geometry in the overlap. Same trick the pipeline uses: buffer the input, restrict the output.
No true fathom curves
ft/fm labels currently unit-convert the metric levels. The embedded tiles carry a second geometry set at the classic fathom curves (
sys=ft); client mode has nothing equivalent, so band edges in ft/fm mode do not land on isolines. The fix needs no fork change: generate a second contour protocol URL with fathom-depthlineLevelsfrom the sameDemSource(shared DEM cache, tiles generate on demand), and haveapplyStateswap the source tiles on unit change viaVectorTileSource.setTiles.Shoal safety
Chart generalization must never cut across or delete the shallow side of a line. Plain Chaikin is depth-neutral and can shave a shoal spur; naive speckle removal can delete an isolated shoal entirely. Marching squares knows which side of each line is shallow at trace time, which makes shoal-safe variants tractable: cut corners only into deep water, and drop small rings only when they are deep pockets. Notably this is the same median/ring-drop/safety work that is still deferred on the pipeline side. Building it in the fork would put client mode ahead of the embedded contours, and the logic could migrate back into the pipeline later.
Where the fork-side work should live
Two options for gaps 1 and 3.
Upstream (onthegomap/maplibre-contour). The maintainer is active and friendly to this ecosystem: Mapterhorn support merged recently, and slope/aspect and smoothing PRs are open from other contributors. Spot elevations have appeal beyond bathymetry, since mountain spot heights are the same feature. The friction: dependabot's canvas 3.x bumps were closed unmerged four times (their CI pins node 18/20), and the fork's
lineLevelsis a second, slower marching-squares implementation sitting next to the native tracer. A maintainer whose README leads with performance will probably want fixed levels reworked into the native algorithm before accepting them. Shoal-safe smoothing is nautical enough that it may never belong upstream at all.Permanent fork, renamed maplibre-bathymetry. Frees the API to diverge: polarity-aware smoothing, fathom level sets, and chart-specific defaults make no sense upstream. The cost is maintenance. The fork is currently 2 commits behind and cheap to rebase, but that drifts over time.
A middle path is to upstream the generic pieces (spot elevations, native-tracer fixed levels, canvas/CI fixes) and keep the chart-specific generalization in maplibre-bathymetry.
Merging this PR does not require that decision; the pin works either way. The decision shapes where the smoothing and shoal-safety work lands.