Skip to content

Fix/chart1 pages#26

Merged
beetlebugorg merged 10 commits into
mainfrom
fix/chart1-pages
Jun 26, 2026
Merged

Fix/chart1 pages#26
beetlebugorg merged 10 commits into
mainfrom
fix/chart1-pages

Conversation

@beetlebugorg

Copy link
Copy Markdown
Owner

No description provided.

beetlebugorg and others added 10 commits June 26, 2026 09:57
…ggle

The SY(INFORM01) "additional information available" box-on-a-leader markers are
baked display-category Other, so enabling Other dumped an (i) marker on every
feature carrying INFORM/TXTDSC/etc. — too much clutter on dense charts. Give them
their own opt-in mariner toggle ("Information callouts", OFF by default), the same
treatment CHDATD01 / data-quality already get: a client-side filter on the baked
symbol_name, so it toggles live with no re-bake.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… safe water

UDWHAZ05 flags an isolated danger when the feature depth is below the safety
contour AND its surrounding water is itself safe (or unknown). DerivedAttrs only
supplied defaultClearanceDepth, never surroundingDepth, so the rule always took its
conservative "unknown surrounding ⇒ dangerous" branch and stamped a magenta ISODGR01
X on every sub-contour OBSTRN/WRECKS/UWTROC — including the "area of wrecks or
obstructions in safe waters" (Chart 1 page 244), which is explicitly NOT dangerous.

Supply surroundingDepth = the shoalest DRVAL1 of the containing depth area, ONLY
when such an area is found; with no containing area it stays absent so the rule's
conservative default still flags deep/unknown dangers. Verified on page 244: the
safe-water area loses its X while "foul area dangerous for navigation" keeps it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ayer

S-52 §10.3.3.8 producer text instructions must always render. The PresLib "ECDIS
Chart 1" two-line captions ("restricted area," + "anchoring prohibited") are encoded
as TWO point features stacked one line apart, each a single-line SYMINS TX. The
shared general `text` layer declutters (text-allow-overlap:false + text-optional),
so the two adjacent boxes collided and the lower line was dropped — looking truncated.

Give NEWOBJ text its own always-on layer (allow-overlap + ignore-placement),
mirroring the existing light-text layer, and exclude NEWOBJ from the collidable
general layer to avoid double-draw. Real geographic labels (not NEWOBJ) stay
decluttered, and text-max-width:40 is untouched so single-line labels never wrap.
Client-only, no re-bake. Verified on page 246: all five second lines now show.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per S-52 §10.1.4 the largest-scale data takes precedence; a coarser-band line drawn
where a finer cell covers the same ground double-draws (no opaque fill hides a
stroke). coverageScaleAt skipped cells with only a DERIVED extent rectangle (no
M_COVR — e.g. the PresLib "ECDIS Chart 1" cells) for every non-point query, so the
overview band's pipelines/contours/coastlines bled across the harbor panels live.

Let derived coverage suppress coarse LINES (the centre test is hole-safe — a line
in a genuine gap still shows), while still EXCLUDING it from FILL suppression so a
coarser fill keeps filling real gaps (§10.1.4) instead of punching no-data holes.
Renamed the flag pointQuery→includeDerived for what it now means. Verified live;
the per-cell preslib harness can't show it (one cell, one band) so the static
render diff is 0 — that's expected, not a no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…permanently)

The "don't add the catalog overlay before the style finishes loading" guard added
an `if (!map.isStyleLoaded()) return` at the TOP of addCatalogOverlay — but that
method's tail also wires the one-time map interaction listeners (the ECDIS crosshair
cursor, the click → cursor-pick, coverage tap-to-fly). When the first onReady call
hit a mid-rebuild style the guard bailed, and if no later style.load re-fired the
whole block never ran: no crosshair, no pick, no coverage (focus source absent,
_catalogMapWired stayed false).

Fix: call addCatalogOverlay only on a LOADED style — directly when it already is,
else on style.load — and drop the internal isStyleLoaded-and-return. This still
avoids the "Style is not done loading" addSource throw, but never skips the wiring.
Verified on the live app: crosshair restored, _catalogMapWired/coverage/focus all
true, pick handler attached again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ses it

The search box (active charts) showed nearly the whole NOAA catalog: /api/cells?active
filtered the cell index by bbox-overlap with each enabled pack's bounding box, but a
pack whose cells are scattered worldwide has an effectively GLOBAL bbox, so every
cached-but-not-baked cell in ENC_ROOT matched (7408/7413 "active"). The server had no
record of which cells a pack was actually baked from.

Record it: bakeAndRegister writes a per-pack cell manifest (<setDir>/<set>.cells.json)
from the exact bake set. serveCells?active now returns cells that are IN an enabled
pack's manifest; it falls back to bbox-overlap only for legacy packs baked before this
(re-importing such a pack writes its manifest and moves it onto the exact path).
Verified: import 14 cells + inject a cached-not-baked cell → active=14 (excludes it),
all=15.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cursor-pick can't "own" an info callout: the SY(INFORM01) box floats offset from
the feature, but the symbol's hit quad is centred on the feature, so the fuzzy
queryRenderedFeatures makes the whole symbol area "close enough" and symbol declutter
/ z-order drops some boxes entirely ("some work, some don't"). Overlay a transparent,
exactly-positioned DOM pad on each visible box instead — a real clickable element
(like the AIS-target Markers), placed via the feature's baked icon `scale` + the live
size-scale so it tracks the rendered box. Tap → showInfoForFeature opens that object's
additional information; tapping the feature itself still picks the feature.

Sparse by design (only info-bearing features), so DOM markers are affordable here —
unlike the general pick, which must stay on GPU queryRenderedFeatures. Follows the
"Information callouts" toggle for free (symbol unrendered → no pads).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…line

Whole-chart features rendered ~1.333x too big: the baker emits sizes at the 1/96"
CSS reference pixel (0.26458 mm, = portrayal.DefaultPxPerSymbolUnit), but the client's
physical-size multiplier assumed the 1/72" typographic point (0.35278 mm) and scaled
everything by 0.35278/pxPitch. Align the client reference to 0.26458 mm so the math is
correct (and it explains why changing the BAKER constant was a render no-op — the
mismatch was client-side).

True physical size still needs the screen's real CSS-pixel pitch, which the browser
won't reveal — so add a "Calibration" settings tab (its own left-rail section, a
contribution like the Advanced tab): a reference box that should be exactly 5 mm (the
S-52 CHKSYM check box). Measure it with a ruler, enter the value, and it sets pxPitch =
pitch × measured/5 (physical size ∝ 1/pitch), rescaling symbols/lines/text to true
size and persisting via setPxPitch (localStorage + server settings). Reset returns to
the CSS-reference default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…water render

On a cell with OVERLAPPING area polygons — the PresLib "ECDIS Chart 1" inset has one
deep-water DEPARE under the shallow water + land — the `areas` fill layer had no
fill-sort-key, so MapLibre painted in tile order and the deep-water polygon landed on
top, hiding the land and shallow water under uniform deep-water blue (worst above z13,
where the harbor cell takes over from the coarser overview band). Real ENCs tile their
areas (no overlap) so this never bit them.

Add fill-sort-key = draw_prio*1000 - drval1: paints in S-52 DrawingPriority order
(DEPARE 3 < LNDARE 12, so land draws over water) with a depth tiebreaker (shallower
water over deeper). No-op for non-overlapping real charts. Page 241 now shows tan land
over the blue river instead of solid deep-water fill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cell index used a single `built` flag: rebuild() reset it to false and
build() re-claimed it. Two rapid reindexes (forget + rebuild on import) could
race so that the second build() found built==true and returned WITHOUT
re-scanning — the just-forgotten cells were never re-indexed and the search
went stale until the next index event ("search index getting stale when I
remove and reindex").

Replace the flag with a single-flight scan plus a dirty re-run: kick() starts a
scan if none is running, else marks the index dirty so the running scan loops
once more when it finishes. A (re)build requested mid-scan is therefore never
lost. build()/rebuild() are now async wrappers over kick(); wait() (backed by a
sync.Cond) lets tests and synchronous callers block until the index settles.

Also harden the delete path: handleDeleteSet now removes the lingering
<set>.cells.json manifest (so the pack dir actually empties) and reconciles the
index. The active search (?active=1) already drops a deleted pack via packDel +
the removed manifest; the source cells stay in ENC_ROOT for a future re-bake.

Adds TestActiveCellsDropOnDelete and waits in the cell-index tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@beetlebugorg beetlebugorg merged commit 2adf1b4 into main Jun 26, 2026
4 checks passed
@beetlebugorg beetlebugorg deleted the fix/chart1-pages branch June 26, 2026 17:55
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.

1 participant