h3-guide: area-weight the fractional-overlay examples, and use the published per-cell weights assets - #357
Merged
Conversation
The same-resolution overlay example divided by SUM(frac), weighting every cell equally, while the coarse-feature example on the same page divided by SUM(h3_cell_area). H3 cells are not equal-area, so the unweighted form pulls a share toward whichever latitudes hold more cells — the CWHR13-vs-conserved question missed California's published 2025 Biodiversity Assessment on 12 of 13 habitat classes, by up to 1.24pp. Carry h3_cell_area through both fractional-overlay examples, and point them at the published per-cell weights assets (-hex-weights, -hex-weights-res9, -hex-weights-res8; data-workflows#506/#508) instead of deriving the weight by hand from Acres/Total_Acre and a SUM(w)/49 rollup. The res-10 weights asset is sparse, so the example keeps LEFT JOIN + COALESCE and bounds the denominator with the dense ca30x30-ecoregion land grid. The coarse case weights by nland * h3_cell_area rather than cell area alone: statewide GAP 1+2 share comes to 26.135% from the res-8 asset that way, matching the res-10 ground truth exactly, versus 25.684% weighting by nland alone and 26.4% ignoring the partial-land correction. Verified against live data: the issue's reproduce query now returns 33.31 for CWHR13 code 32 (assessment: 33.27), 21.65 for code 51 (21.59), and 51.70 for code 20 (51.67).
cboettig
force-pushed
the
fix/356-fractional-overlay-area-weight
branch
from
August 7, 2026 20:50
b1cf35a to
04f3fa8
Compare
This was referenced Aug 7, 2026
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.
Closes #356.
Important
Not validated yet — do not promote to prod on the strength of this PR. Per AGENTS.md §"Validating guidance changes", a prompt-artifact change is not done until the headless model suite on dev shows both (a) the targeted failure fixed and (b) no baseline regression. That run happens after merge, since dev tracks
:main. Everything below is operator SQL establishing ground truth — which AGENTS.md is explicit does not substitute: it proves the pattern works, not that the guidance steers the model to it. Two blockers are flagged at the bottom.What was wrong
h3-guide.mdcarried two worked examples for overlaying a partial-coverage feature against a partial-coverage layer, and they disagreed on whether to weight by cell area. The same-resolution one (line 305, repeated at 339) divided bySUM(frac); the coarse-feature one (line 333) divided bySUM(h3_cell_area). Line 36 of the same file states the rule — H3 cells are not equal-area — and line 38 calls the scoped case the accuracy-critical one.The unweighted branch is the one that fires for a res-10 feature, which is the CWHR13-vs-conserved-areas question a partner asked the California 30x30 app.
The fix
h3_cell_areathrough both fractional-overlay examples, matching line 333.…-hex-weights,…-hex-weights-res9,…-hex-weights-res8, from data-workflows#506/#508) instead of deriving the weight by hand fromMAX(Acres / Total_Acre)and aSUM(w) / 49rollup. The issue flagged this as worth doing in the same pass; it also removes a second source of error, sinceAcres / Total_Acreis a unit's GAP 1+2 share, not its share of the cell.LEFT JOIN+COALESCE(…, 0)and joinsca30x30-ecoregionat res 10. This turned out to be load-bearing, not cosmetic: the cwhr13 layer extends past California, and without the mask code 32 comes out at 29.89 rather than 33.31.nland * h3_cell_area(h8, 'km^2'), since coastal and border res-8 cells are only partly land.The general reductions (
MAXwithin a fine cell, mean across children) are kept in prose as the fallback for layers with no published weights asset. No antipattern blocks, per AGENTS.md:137. The section grows 547 → 598 words (+9%) — worth a reviewer's eye against AGENTS.md's "tighter wording is safer wording"; the growth is the fallback paragraph.Ground truth (operator SQL, prod MCP)
The issue's reproduce query, after the fix:
Two independent totals cross-check the corrected form: per-class areas now sum to 101.45M acres (California's land area) against 96.4M for the old formula, and GAP 1+2 to 26.51M against the 26.47M stated in the collection description, where the old formula gave 24.49M.
For the coarse case, statewide GAP 1+2 share from the res-8 weights asset:
nland * h3_cell_area(h8)(this PR)nlandalone105 passedontests/.Blockers before this can ship
geo-agent-benchmark/suite/gold/ca-30x30.mdQ4 andsuite/questions/ca-30x30/ca-cwhr13-pct-conserved.yamlcarry gold SQL that is verbatim the old line-305 formula — sameMAX(Acres/Total_Acre)CTE, sameSUM(frac*w)/SUM(frac). Gold says 28.8% for Conifer Woodland against a±3pttolerance; the corrected guidance yields 33.3%, so theregressiontier fails the fix. The gold needs re-deriving first (branch prepared, PR held — see 2). Q5 (hardwood woodland, L3 report-authoritative, 13.6%) is unaffected: the corrected form still gives 13.60%.-hex-weights-res9/-res8asset descriptions documentSUM((w1+w2)*nland)/SUM(nland)as correct — the 25.684% row above. Until that lands, the injected guide and the per-dataset text tell a model opposite things about the same operation.Suggested order: data-workflows#523 → re-derive gold → merge here → dev matrix (
MODELS="z-ai/glm-5.2 deepseek/deepseek-v4-flash-0731", the cost-bounded standard pair) → prod.