Skip to content

Add dense-county precision rule for municipality-sized counties - #7

Merged
sebschlo merged 5 commits into
masterfrom
feat/dense-county-precision
Aug 20, 2026
Merged

sebschlo merged 5 commits into
masterfrom
feat/dense-county-precision

Conversation

@sebschlo

Copy link
Copy Markdown
Owner

Problem

The compact index assigns each geohash cell to exactly one winning place. In countries whose county placetype is a small municipality (Guatemala, Honduras, Costa Rica, El Salvador, Colombia, Mexico's south, Thailand, ...), the county precision cap of 4 produces cells of roughly 39x20 km, and the most populous municipality in each cell swallows its neighbors: they own zero cells and can never be returned by a lookup. Measured on a Guatemala build, only 47 of 303 municipalities are reachable, and Antigua Guatemala resolves to "Escuintla". Raising the county cap to 5 globally fixes precision but multiplies county rows by ~26 in the worst case, which a world build cannot afford.

The repo already has a precedent for area-keyed precision: the sparse-region rule (--region-sparse-max-precision / --region-sparse-min-area-km2) lowers precision for very large regions. This PR adds its mirror image for counties.

Change

  • New optional rule in scripts/generate_boundary_index.js: when both --county-dense-max-precision and --county-dense-max-area-km2 are set and valid, county polygons whose bbox area (bboxAreaKm2, same measure as the sparse-region rule) is at or under the threshold are covered at the dense precision. The dense precision never falls below the regular --county-max-precision and is clamped to the global --max-precision. The rule is off unless both flags are present.
  • Large counties are unaffected, and cells fully inside a polygon still terminate at the base precision, so the extra rows concentrate exactly on the small-municipality boundary cells that cause the pathology.
  • scripts/generate_wof_boundary.sh forwards the rule via WOF_COUNTY_DENSE_MAX_PRECISION / WOF_COUNTY_DENSE_MAX_AREA_KM2 (documented in the header; empty by default, i.e. off).
  • The end-of-run summary prints the active rule (Dense county rule: area_km2<=... => max_precision=...).
  • CHANGELOG bullet under ### Unreleased.

Measurements

Basket of 7 countries (GT, HN, CR, MX, CO, TH, FR) built with scripts/generate_wof_boundary.sh, WOF_MAX_PRECISION=5, defaults otherwise. Legs: the shipped configuration (WOF_COUNTY_MAX_PRECISION=4), the upper bound (county=5), and county=4 plus the dense rule at precision 5 with area thresholds 300 / 1000 / 3000 km2. (The FR archive also carries GF/GP/MQ/RE/YT; they are included in totals.)

County owners with at least one cell (and county lookup rows) per leg:

Country county=4 dense 300 dense 1000 dense 3000 county=5
GT 49 (162) 171 (1,020) 221 (2,183) 218 (3,340) 217 (4,131)
HN 63 (193) 172 (1,052) 241 (2,733) 241 (3,209) 241 (4,306)
CR 25 (109) 40 (213) 49 (409) 62 (1,345) 61 (2,103)
CO 328 (1,689) 601 (3,798) 900 (8,998) 994 (16,406) 1,000 (37,749)
MX 583 (3,171) 1,096 (6,243) 1,540 (15,544) 1,712 (31,174) 1,735 (71,233)
TH 305 (887) 348 (1,130) 592 (6,516) 723 (17,093) 724 (19,621)
FR 1,142 (1,185) 1,022 (1,161) 81 (181) 70 (134) 70 (134)
Total rows 49,465 56,758 79,914 117,193 187,158
DB size 4.6 MB 4.9 MB 6.0 MB 7.8 MB 11.1 MB

Observations:

  • At 1000 km2 the pathology is essentially fixed where it exists: HN recovers all 241 municipalities, GT 221 (county=5 itself reaches 217), CO 900 of 1,000, MX 1,540 of 1,735 - at 43% of the county=5 row cost.
  • FR is the control: totals stay flat (35,470 -> 35,587 rows, +0.3%). Its tiny cantons do go dense, but at precision 5 they lose contested cells to localities (which outrank counties), so lookups there return city-level names instead - no size cost, no precision loss.
  • Marginal cost is stable at ~49.5 bytes per lookup row across all legs.

World extrapolation

The shipped world DB (county cap 4) has 356,739 lookup rows / 21.7 MB, of which 142,185 are county cells. Bucketing its 114 county-bearing countries by average county footprint (county cells per owning county, a proxy for county size):

  • S (small municipality, cpo <= 4; GT/HN/CR/TH measured): 7,828 world county cells
  • M (medium, 4 < cpo <= 8; CO/MX measured): 11,675 cells
  • L (large county, cpo > 8; RU 29.8k, US 19.6k, CN 9.8k, AU 7.9k, ...): 122,682 cells

Extra rows per baseline county cell (cell-weighted from the basket): S = 1.53 / 7.81 / 18.05 and M = 1.06 / 4.06 / 8.84 at thresholds 300 / 1000 / 3000. The L bucket has no basket exemplar, so it is bracketed: an upper bound copying M (pretends RU/US/CN counties are as threshold-eligible as Mexican municipios - a deliberate overestimate), a floor from measured FR (~0.03-0.11), and a reasoned central estimate (0.05 / 0.35 / 1.5) from the small-county tails of the L membership (US east coast, Swedish kommuner, AR partidos). At ~49.5 bytes per row on top of 21.7 MB:

Threshold Central Upper bound (L=M) Floor (L=FR)
dense 300 22.2 MB 28.0 MB 22.1 MB
dense 1000 27.9 MB 49.3 MB 26.4 MB
dense 3000 41.0 MB 83.5 MB 32.9 MB
county=5 everywhere ~122 MB ~165 MB ~41 MB

Recommendation

WOF_COUNTY_DENSE_MAX_PRECISION=5 with WOF_COUNTY_DENSE_MAX_AREA_KM2=1000: the central world estimate is ~28 MB (under a ~35 MB working target), and even the deliberately pessimistic upper bound stays under a 50 MB ceiling, while recovering nearly all swallowed municipalities in the affected countries. 3000 km2 risks blowing past 50 MB if the L bucket is more eligible than expected; 300 km2 is the safe fallback (worst case 28 MB) but leaves CR, TH, CO, and MX only partially fixed. The first real world build with the flag should confirm the actual size before shipping.

Tests

  • New spec spec/boundary_builder_dense_county_spec.js spawns the CLI on synthetic GeoJSON: a municipality-sized county inside one precision-4 cell (bbox ~69 km2) gets all its cells at the dense precision 5, a ~104,000 km2 county in the same build stays at the county cap 4, and with the flags absent both stay at 4 (and the summary line is absent).
  • The main assertion was verified red-green: with the resolver change reverted, the spec fails with Expected $[0].len = 4 to equal 5.
  • Full suite green: 33 specs, 0 failures.

The compact index assigns each geohash cell to exactly one place. In
countries whose county placetype is a small municipality (Guatemala,
Honduras, El Salvador, ...), the county precision cap of 4 produces
cells of roughly 39x20 km, so the most populous municipality in each
cell swallows its neighbors: they own zero cells and can never be
returned by a lookup. Guatemala, for example, can only resolve to 47 of
its 303 municipalities, and Antigua Guatemala resolves to Escuintla.

Mirror the existing sparse-region rule (which lowers precision for very
large regions) with a dense-county rule that raises it for very small
counties: when both --county-dense-max-precision and
--county-dense-max-area-km2 are set, county polygons whose bbox area is
at or under the threshold are covered at the dense precision instead of
the normal county cap. The dense precision never falls below the county
cap and is clamped to the global --max-precision. Large counties are
unaffected, and their cells fully inside the polygon still terminate at
the base precision, so the row-count cost is concentrated where the
rule fires.

The rule is off by default; generate_wof_boundary.sh forwards it via
WOF_COUNTY_DENSE_MAX_PRECISION / WOF_COUNTY_DENSE_MAX_AREA_KM2.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72ddc6a13b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/generate_wof_boundary.sh
Codex review: WOF_COUNTY_MAX_PRECISION defaults to WOF_MAX_PRECISION
(5), so enabling only the two dense env vars built every county at the
dense precision already, making the area threshold a no-op and keeping
the global row-count cost the rule exists to avoid.

When both dense vars are set and WOF_COUNTY_MAX_PRECISION is not given
explicitly, default the regular county cap to one below the dense
precision (never below WOF_BASE_PRECISION). An explicit
WOF_COUNTY_MAX_PRECISION always wins, and nothing changes for users who
do not enable the rule.

Verified on the Guatemala WOF archive: dense vars alone now produce
county=4 with the dense rule active (2,280 rows); an explicit county
cap of 5 still wins (4,234 rows).
@sebschlo

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ec1a96691

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/generate_wof_boundary.sh Outdated
Comment thread scripts/generate_boundary_index.js
Codex review round 2: the derivation lived only in the shell helper, so
(1) passing the two dense flags directly to generate_boundary_index.js
without --county-max-precision left the county cap at the global max,
reproducing the no-op footgun at the CLI layer, and (2) the shell
derived the cap from the raw dense value, so a dense precision above
WOF_MAX_PRECISION (which the node side clamps) could derive a cap equal
to or beyond the global max.

Fix both by deriving in one place, in the node script: when both dense
options are set and valid and --county-max-precision was not provided,
the county cap defaults to one below the dense precision after it has
been clamped to --max-precision (and never below --base-precision). The
shell helper now simply omits --county-max-precision when
WOF_COUNTY_MAX_PRECISION is unset, so both entry points behave
identically; with the rule off, the node fallback (global max) matches
the helper's previous default.

New spec covers the dense-flags-only CLI path (verified red-green:
without the derivation the large county gains precision-5 cells).
Guatemala smoke via the helper: dense vars only => county=4 with the
rule active (2,280 rows); dense precision 6 with max 5 => same (clamped,
cap 4); explicit cap 5 still wins and rule-off still builds county=5
(4,234 rows).
@sebschlo

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: df9ed7db82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sebschlo

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: bff16464df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sebschlo
sebschlo merged commit 0cf543b into master Aug 20, 2026
2 checks passed
@sebschlo
sebschlo deleted the feat/dense-county-precision branch August 20, 2026 16:58
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