Companion to POI_VISIBILITY.md (which owns the map above this doc's seam), MAP_OPTIONS.md, VOLUNTEERING.md, SAYING_THANKS.md, SOURCE_REGISTRY.md, EVENTING.md and ../OurHikeValues.md #1.
Answers #532 — Decide what the zoomed-out map shows instead of pins: cluster counts, or the waypoint lanes, and answers it with neither.
Partly fixed, 2026-08-15 (#603). The dot rank now draws all the way down (
POI_DOT_MIN_ZOOM = 0), so the opening view carries a stipple of every waypoint on the trail rather than nothing, and the legend's sentence is corrected to match. The club sections this doc argues for are still the subject it is missing — dots are texture, not a subject, and nothing below answers itself.CORRIDOR_BOUNDSis untouched and the section below stands.
CORRIDOR_BOUNDS in App.tsx:240 opens the app on the whole 2,197-mile
corridor, which lands near z4 on a phone. POI_MIN_ZOOM = 9 means no waypoints. So the opening
screen is a thin line down fourteen states with nothing on it, and the legend says Nothing on
this part of the map yet — pan or zoom out to see more, which is false in both halves.
Every previous answer to this took the same shape: find something to draw in place of the waypoints that were deleted. Count bubbles that expand on tap; the elevation ribbon's waypoint lanes driven from the viewport instead of the GPS fix. Both are written up in POI_VISIBILITY.md's history, and #532 exists to choose between them.
Neither is what a zoomed-out map is for. A hiker at a kitchen table looking at fourteen states is not asking how many privies are in Virginia. A line of bubbles reading 312, 289, 410 down a uniformly dense trail answers a question nobody has. The corridor view has a subject of its own, and the waypoints are not it.
POI_VISIBILITY.md owns the number — POI_PIN_MIN_ZOOM = 9, chosen on one
criterion: a day's hike is 16–24 miles, the planning window is twice that so the day has ground
around it, and a phone map covers 50.9 miles at z9. Below it, this doc. Above it, that one.
So this view owns z0–z8, which on a phone runs from the whole corridor at about z4 down to a screen holding roughly 57 × 102 miles — four days of walking. That is the range in which somebody is choosing where to go rather than looking at where they are, which is the right span for a screen about exploring.
It was z12, then z10, before settling here on 2026-08-13, and the corrections all ran the same way: each earlier number handed this view more of the map than it should have had, on a criterion that turned out to be about pin comfort rather than about what a hiker is doing. POI_VISIBILITY.md's seam section records them.
The property that makes the split honest, and it is the whole reason this is a separate doc: below the seam the map is not an incomplete map of places. It is a complete map of something else. All thirty club sections, all of the stretches. Nothing sampled, nothing dropped, nothing needing a caption that admits it. There is no visibility problem down here because visibility is not the subject.
1. The thirty clubs — built 2026-08-13 (#594)
The A.T. is maintained by thirty clubs. Below the seam the trail draws as thirty named
stretches rather than one line, and tapping one says who looks after it — a question this app
already cares about in three other places (VOLUNTEERING.md,
SAYING_THANKS.md, the backend's Club and MaintainerAssignment models)
and has never been able to answer on the map.
The source is not the one this section originally named, and the change is worth recording
because the obvious layer was the worse one. This doc said to export trail_club_sections —
thirty polygons, registered since 2026-07-25 and read by nothing. Checking upstream first
turned up that centerline already carries the club on every one of its 3,025 features, in
Trail_Club, Acronym and Reg_Acro:
centerline |
trail_club_sections |
|
|---|---|---|
| last edited | 2026-08-04 | 2024-08-15 |
| attribution sits on | the trail line → exact mile ranges | polygons → derived by point-in-polygon |
| already fetched | yes, and already exported | fetched, read by nothing |
So export_club_sections.py uses the centerline for which stretch belongs to whom, and the
polygon layer for how a club's name is spelled and its region. Fresh source decides the
fact; stale source decides only the wording. That split is what keeps ATC's two misspellings
(Potomac Appalachain Trail Club, New York - New Jersey Trail Conference) off a hiker's
screen while the attribution stays nine days old rather than two years.
41 miles of trail have no club, and the app says so. 47 centerline features carry a digit string where a club acronym belongs — 1.90% of the corridor. Those miles publish as unattributed rather than being backfilled from the older polygons: a stretch the fresh source cannot name should say so, not borrow a two-year-old answer and present it as current. The published stretches tile the trail exactly, Springer to Katahdin, so "not recorded" is visibly different from "no trail here".
The measured output: 30 clubs, 2,197.5 miles tiled with no seams or overlaps — GATC's 77 miles in Georgia at one end, MATC's 262 at the other, Randolph Mountain Club's 2.5 in the Whites, and 38.5 miles unattributed across 27 runs.
The second half, and the one that makes the corridor view a thing to explore rather than a thing to read: named stretches of trail — Roan Highlands, Franconia Ridge, McAfee Knob, Grayson Highlands, Mahoosuc Notch — each with a mile range, a length, an ascent, a Naismith time, and a reason it is named.
The numbers cost nothing. export_elevation.py already ships ~141,000 samples at 25 m
(0.87 MB gzipped, offline), elevationGain.ts computes confirmed ascent over any window with the
3 m dead band that keeps DEM noise out, and naismith.ts turns distance plus ascent into a
duration. A stretch is two mileposts; everything else is derived from artifacts already on the
phone.
The list is the hard part, and "popular" is three different questions.
The ask was popular hikes. The word hides three claims with completely different evidence behind them, and the failure mode is blending them into one number that means none of them — which is the same failure as the count that read zero.
So: one record type, three bases, and the app never says "popular" flatly. It says what it knows and where it came from.
| basis | what it actually claims | evidence | when it ships |
|---|---|---|---|
named |
This stretch is well known | A curated in-repo list, each entry with a citation | v1 |
published |
ATC lists this as a hike | ATC's own day-hike material and the communities layer, through the pipeline |
when the source is registered |
visited |
Hikers using this app sent something from here | Counted from reports, photos and thanks that already carry a mile | fills in over time; ships empty |
Twenty or thirty entries in the repository, each carrying a source. Not a ranking, not a score: a list of stretches that a reasonable person would agree are known, with the reason attached. It is editorial, it says so, and it is the honest way to have something on the screen before there is any data.
Cheap to be wrong about and cheap to fix, which is the argument for starting here rather than waiting for either of the other two.
ATC publishes day-hike material and communities is already source [7] in sources.json. An
entry sourced this way is attributable to ATC rather than to us, which is strictly better than
named for the same stretch, and the two coexist: the same stretch can carry both bases, and the
stronger one is what the app cites.
This is ordinary SOURCE_REGISTRY.md work — register the source, record its freshness, publish with attribution — and it is not blocked on anything.
This is the one that could go badly, so the rule is worth stating before the feature is:
Never from the eventing pipe. EVENTING.md's rule 2 is "No geography, ever. No
coordinates, no mile, no segment id, no POI id, no region", and it is not a preference — it is
the finding #252 already paid for, that a stable
identifier next to a trail position and a time is a hiker's route down the corridor, recoverable
with curl. A popularity metric built on analytics is a machine for producing exactly that pair.
From the records that already exist, instead, which is EVENTING.md §4's own
rule — do not instrument what the server already knows. A report carries a mile
(report.py:134) because a blowdown with no location is
useless; photos and thanks carry one for the same reason. These are things a hiker chose to
publish. Counting them is not new collection, and it survives an opt-out and a dropped queue,
which an event stream does not.
Three constraints on the output, and the first is borrowed rather than invented:
- No published cell below k = 25, EVENTING.md §6's suppression floor, applied at the query layer so the slice nobody thought about is covered too. A count of three is a description of three people.
- Coarse grain. A stretch, never a point and never a POI. The whole hazard is precision.
- Labelled as what it is. This measures where hikers using this app sent something, not where people hike. It is biased toward where the app has users, toward the kind of terrain that generates reports, and it lags. The app says the former; it must never imply the latter.
And it ships empty. On day one every stretch is named, and that is fine — a feature that fills
in as the app is used is a feature that improves without anybody shipping anything, which is rare
enough to be worth designing for.
No blended score. No weighting named against visited into a single number, no sort order
that implies a ranking across bases. The three answer different questions and a hiker reading
"popular" deserves to know which one they are being told. OurHikeValues.md #1's warning about
prescriptive gamification is the same instinct pointed at a different feature: a leaderboard of
trail sections is exactly the thing that tells people there is a right way to hike.
#532 asked for a decision between two options and this is a refusal of both, so the reasons belong here rather than in a commit message.
Not cluster bubbles (#532 Option A). They are a density map of a uniformly dense trail; they
break the tap path, since poiTaps.ts resolves to a poi_id that cluster features do not carry
and getClusterExpansionZoom is async; and they re-cluster on every zoom, so a group tapped at z5
and at z7 is two different things wearing one appearance. The deeper objection is the one at the
top of this doc: they answer how many waypoints are hidden here, which is only a question if
you have accepted hiding them. POI_VISIBILITY.md stops hiding them.
Not the waypoint lanes driven from the viewport (#532 Option B). The lanes are good and the idea was the strongest thing in the old design — but a 2,197-mile window collapses them into a handful of pills reading in the hundreds, and it puts a second, differently-derived window beside the elevation ribbon's when ../WIREFRAMES.md §1.3–1.4's whole point is that the two agree about what stretch they are showing. The lanes stay what they are.
Not a trip planner. HIKE_PLANNING.md and SEGMENTS.md own planning and are v2. This is a map that shows what is out there; the moment it starts holding a route someone is building, it is that feature and should be built there.
Two published artifacts, both small, both offline-first like everything else here.
Club sections. The exported trail_club_sections geometry plus, per club, its name, its mile
range, and the source date it came from. Thirty features. The backend already holds Club and
MaintainerAssignment for the authoritative answer used when a thanks is resolved
(maintainerLookup.ts is the client's half); this is the
map's copy for drawing and for working offline, and where the two disagree the backend is right.
Stretches.
Stretch
id stable, minted in the pipeline - a stretch is referenceable
name
start_mile, end_mile
bases: [ named | published | visited ] - one or more, never blended
citation per basis - who says so, and when
visited_count - suppressed below k=25, absent otherwise
Length, ascent, descent and Naismith time are not stored. They are derived on the phone from the elevation profile it already has, which keeps one number in one place and means a better profile improves every stretch without a republish.
No new preference keys.
Thirty clubs or thirty-five.Answered 2026-08-13 — thirty, and the two sources never disagreed.AT_ClubMap's 35 points are 35 label points for the same 30 clubs: four clubs maintain discontiguous trail and get a point each, and one appears under both a correct and a typo'd spelling (Outdoor Club of Virginina Tech). Randolph Mountain Club has its own polygon in the 30-layer, sopipeline/SOURCE_SURVEY.md's claim that it was "folded elsewhere" was wrong; that entry is corrected, and §3e there is the full finding. The question turned out to matter less than it looked, because neither layer supplies the attribution now — the centerline does.- How many
namedstretches, and who writes them. Twenty is a screen; two hundred is a gazetteer and a maintenance burden. And an editorial list in a repository is a thing somebody has to own. - Whether a club section is tappable below the seam only. Above the seam it would be a polygon over a map a hiker is navigating by, which is clutter; but "who maintains where I am standing" is a good question at any zoom, and the status strip or the attribution line may be the better home for it there.
- Whether
visitedshould count downloads too. A map package downloaded from R2 is a server-side record (EVENTING.md §4 lists it), it is geographic by construction, and it is arguably a better signal of intent than a report is — a hiker downloads a sheet before going, and reports only if something is wrong. It is also the one that most needs the k floor. - What the corridor view does with a hiker who has a GPS fix. Opening on the whole trail is
right for planning at home and wrong for someone standing on it.
cameraMemory.tsalready decides this; the corridor view should not re-decide it. - Whether this is v1. #532 is labelled
researchand deliberately did not assume its build falls inside v1. The club sections are cheap enough to argue for; thevisitedbasis plainly is not, and ships empty regardless.