FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(gradient-profile): ride the geom trace for the hover map dot by mousebrains · Pull Request #98 · mousebrains/kayak_python · GitHub

fix(gradient-profile): ride the geom trace for the hover map dot - #98

Merged
mousebrains merged 1 commit into
mainfrom
fix-gradient-dot-track
May 30, 2026
Merged

fix(gradient-profile): ride the geom trace for the hover map dot#98
mousebrains merged 1 commit into
mainfrom
fix-gradient-dot-track

Conversation

Copy link
Copy Markdown
Owner

Problem

On description.php?id=419 (Canyon Creek), hovering the gradient/elevation plot moves a dot on the companion map. It follows the trace until ~river-mile 2.7, then shoots in a straight line across the reservoir to the take-out instead of following the channel.

Cause

gradient-profile.js positioned the map dot by linear lat/lon interpolation between gradient-sample anchors (put-in, each bin centre that carries a lat/lon, take-out). Reach 419's samples are dense (every ~0.2 mi) only to d_mi 2.5; the entire reservoir collapses into one coarse, insignificant bin (d_mi 3.4, w_mi 1.6, 0.1 ft/mi). With no intermediate anchors across the pool, the dot draws straight chords 2.5 → 3.4 → take-out — cutting across the curve.

Meanwhile the map already draws the full reach.geom (200 dense points, 33 of them tracing the reservoir) as data-track="[[lat,lon],...]".

Fix

Position the dot along the drawn geom polyline by cumulative arc-length. d_mi is distance-from-put-in along the trace — the same parameterization as the geom's cumulative length — so the plot's axis fraction maps 1:1 onto a point on the drawn line. The dot now rides every bend to the take-out. The old sample-anchor interpolation stays as a fallback for a chart with no companion map track.

JS-only — reuses the geom the map already has; no DB/PHP/payload change.

Verification (reach 419, real data)

Distance of the computed dot from the drawn trace:

section old (sparse anchors) new (rides geom)
creek (d_mi ≤ 2.5) 6–17 m on-trace
reservoir (2.8–4.2) 78–198 m off on the polyline*

*new points are interpolated between adjacent geom vertices, so they lie on the drawn line by construction. A d_mi=2.5 consistency check (new position vs the sample's own lat/lon = 23 m) confirms d_mi ≈ geom arc-length.

biome clean. Generalizes to every reach with a reservoir/flat section, not just 419.

Deploy

Ships via the normal levels build (copies static/ → docroot; the ?v= cache-bust updates on the new mtime). No migration.

🤖 Generated with Claude Code

On description.php the gradient-plot hover drops a dot on the companion
map. It was positioned by linearly interpolating lat/lon between gradient
SAMPLE anchors (put-in, each bin centre that has a lat/lon, take-out).
Across a flat reservoir the analysis collapses the whole pool into a
single coarse, insignificant bin, so there are no intermediate anchors
there and the dot cut a straight chord across the curved channel — on
Canyon Creek (id=419) it left the trace around mile 2.7 and ran straight
to the take-out instead of following the reservoir.

The map already draws the full reach.geom as data-track="[[lat,lon],...]"
(200 dense points on 419). Position the dot along THAT polyline by
cumulative arc-length: d_mi is distance-from-put-in along the trace, the
same parameterization as the geom's cumulative length, so the plot axis
fraction maps 1:1 onto a point on the drawn line. The sample-anchor
interpolation stays as a fallback for a chart with no companion map track.

Verified against 419's data: the old method sat 78-198 m off-trace across
the reservoir (the straight chord); the new method returns points that lie
on the drawn polyline by construction. JS-only; no DB/PHP change.

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

Copy link
Copy Markdown
Owner Author

Code Review — #98 fix(gradient-profile): ride the geom trace for the hover map dot

Verdict: ✅ Approve. Correct root-cause fix, and the chosen approach (parameterize by arc-length fraction along the already-drawn geom) is more robust than the bug it replaces. CI is green. I traced every load-bearing assumption against the code rather than trusting the description.

Root cause is accurately diagnosed

The old interpolateLatLon() interpolates between sparse sample anchors (put-in, bin centres with a lat/lon, take-out). A flat reservoir collapses into one coarse insignificant bin, so there are no anchors across the pool and the dot draws a straight chord — exactly the 2.5 → 3.4 → take-out cut described. Confirmed in gradient-profile.js:109‑131.

The fix is sound — verified end to end

  • Same [lat,lon] order, same element. feature-map.js:5 documents data-track as [[lat,lon],…], and the map renders the trace correctly, so the order is guaranteed; haversineMi/placeMapDot treat a[0]=lat, a[1]=lon consistently. Crucially, readGeomTrack() and getMap() resolve the same element (#feature-map || #reach-map, lines 65‑67), so the dot is always drawn on the map whose geom was measured — no cross-wiring risk.
  • Endpoints align. xMin/xMax = payload.x_min/x_max is the d_mi plot domain, with put-in pinned at d_mi=0 and take-out at d_mi=xMax (lines 118/127). The geom is ordered put-in→take-out (feature-map.js:381/386 draw the put-in/take-out dashed connectors to track[0]/track[last]). So f=0 → track[0] (put-in) and f=1 → track[last] (take-out) line up correctly, and the f<=0/f>=1 clamps cover the edges.
  • The f * total normalization is the robust choice. Mapping the fraction of the d_mi axis onto the same fraction of geom arc-length absorbs any total-length difference between the (possibly full-res) gradient parameterization and the simplified reach.geom — so a naive "d_mi miles → arc-length miles" mismatch can't accumulate. The residual is only non-uniform reparameterization error, which the PR quantifies at ~23 m at d_mi=2.5 vs the 78‑198 m it fixes. Fine for a hover dot.
  • Efficient + degrades cleanly. cum[] is built once at setup (O(n) haversines); each hover is an O(log n) binary search with a zero-length-segment guard. trackLatLon(dMi) || interpolateLatLon(dMi) keeps the old path only when a chart has no companion map track (no data-track → readGeomTrack() returns null). Good.

Nice property worth calling out

Even if a reach's geom were ever edited independently of its gradient_profile, riding the drawn polyline is still the visually correct behavior — the dot belongs on the trace the user sees. So this is strictly better than the chord-cutting anchor interpolation in every case, not just the reservoir one.

Notes (non-blocking)

  • JS-only with no automated coverage — consistent with the repo (these map/chart modules have no JS unit harness; biome is the only JS gate). The manual verification against reach 419's real data, with quantified off-trace distances, is the right level of diligence here.
  • catch (_e) uses the underscore-unused convention, so biome stays clean. 👍

🤖 Reviewed with Claude Code

mousebrains merged commit db34ae0 into main May 30, 2026
8 checks passed
mousebrains deleted the fix-gradient-dot-track branch May 30, 2026 20:52
mousebrains added a commit that referenced this pull request May 31, 2026
* docs: round-6 deep project review (graded B+, ▲ from B−)

Sixth deep project review of the entire tracked repo — 6 cold facet
auditors (Python, PHP/security, schema/data, tests/CI, ops, docs) +
synthesizer hand-re-verification, judging two bands: (A) did round-5's
fixes durably stick, and (B) what did #93#98 + migrations 0069–0071 +
the two direct-to-main commits introduce.

The recursive integrity check passes cleanly for the first time in the
series: every round-5 fix (R1.1/R1.2/R1.3/R1.5/R2.1/R3.x/R4.x) landed as
a committed PR and is still present at HEAD, and every mechanized guard
is proven non-vacuous by break-it experiment. New code is clean — no
CRIT/HIGH: #93 USACE kcfs→cfs (correct, per-series), migrations
0069/0070/0071 (idempotent, FK-clean, Bridgeport DROP cascade
residue-free), #96/#97 multi-state pickers, #95/#98 gradient JS.

Two MED findings, both recurrences of round-5 classes closed by
documentation not mechanization: (1) two direct-to-main commits, one of
which broke CI on main (the {}-is-a-dict bug); (2) a nightly snapshot
overrode migration 0067's sort_name for gauge 217 with no migration.
Root cause is shared — main accepts un-CI-gated direct pushes from both
humans and the snapshot bot. Lever: route everything through a CI gate
(branch protection + a self-gating/auto-merging snapshot), a
snapshot-column drift guard, and teach seed_gauge_display to preserve
migration-pinned sort_names.

Two facet over-claims dissolved on hand-re-verification (the USACE
temperature-docstring drop is a correct fix; check_reaches DOES
range-check vertices via validate_lat_lon).

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

* docs: fold external-review corrections into the round-6 review (#99)

The PR #99 external verification pass re-confirmed every finding,
severity, and the B+ grade against db34ae0 (recommendation: merge), and
flagged one inaccurate evidence line plus three off-by-one citations.
Corrected:

  - MED #1: drop the `git branch --contains` "reachable only from main"
    claim — feature branches later cut from main now contain 9b428bb /
    6007c21, so containment no longer distinguishes them. The direct-to-
    main conclusion stands on the durable evidence (linear f3ed673..HEAD,
    no merge commit, missing (#NN) suffix).
  - citations: ci.yml:114→115, SourceUrlTest.php:83-84→84-85,
    check_reaches.py:212→213.

Added an External-review note recording the pass + the one below-LOW item
it surfaced (the 0069/0070 header comments' now-stale PENDING_RECONCILIATION
wording).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant


Back | FazBrowse Home | New Git URL