Guard against unusable boundary geometry, and tooling to repair it - #86
Open
mikaalnaik wants to merge 1 commit into
Open
Guard against unusable boundary geometry, and tooling to repair it#86mikaalnaik wants to merge 1 commit into
mikaalnaik wants to merge 1 commit into
Conversation
Production had 73,390 boundaries whose geometry was unusable — polygons
spanning the globe — and nothing had noticed, because a shapefile loaded
without reprojecting from its source SRID yields coordinates in metres that
the geography column silently wraps into valid-looking degrees. Row counts,
column types and the ingestion status all looked healthy.
The loader now checks the extent of what it just wrote against Canada, inside
the transaction, so a bad load rolls back rather than replacing good geometry
with garbage. Verified by reverting the reprojection fix and loading the real
CSD file: it raises with the offending bbox, marks the ingestion failed, and
writes nothing.
Longitude does the real work in that check. The northern limit is 90 rather
than Canada's northernmost land, because StatCan draws Arctic boundaries to
the pole — FSAs X0E and X0A both reach 89.999, and a tighter ceiling rejected
every correctly-projected file covering the north. I found that by having the
guard reject a load that was in fact correct.
Also adds the two things needed to repair the existing rows:
* Fetcher#fetch(force:) — checksum dedupe answers "did upstream change",
which is the wrong question when the loader changed and the rows it wrote
are wrong. Replaces flipping ingestion status by hand.
* geo:verify_boundaries to report geometry health per boundary type, and
geo:reload_boundaries[source] to force a reload. Verified end to end on
the real FSA file: 1,641 boundaries, M5V's centroid landing on downtown
Toronto.
Co-Authored-By: Claude Opus 5 <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
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.
Follow-up to #82. Production has 73,390 boundaries whose geometry is unusable — polygons spanning the globe — and nothing noticed for months. #82 fixed the cause for CSD (StatCan files ship in EPSG:3347 and weren't being reprojected) and I reloaded CSD by hand after deploying. This adds the guard so it can't recur silently, plus the tooling to repair the remaining nine types.
The guard
A shapefile loaded without reprojecting from its source SRID yields coordinates in metres, which the
geographycolumn silently wraps into valid-looking degrees. Row counts, column types, and ingestion status all look healthy while every polygon is in the wrong place. That's why this went unnoticed.The loader now checks the extent of what it just wrote against Canada, inside the transaction, so a bad load rolls back instead of replacing good geometry with garbage.
Verified by reverting the reprojection fix and loading the real CSD file:
The guard caught me out once, correctly. My first ceiling was 84°N — Canada's northernmost land is 83.1°N at Cape Columbia — and it rejected a correct FSA load reaching 89.999°N. StatCan draws Arctic boundaries to the pole (X0E in NWT, X0A in Nunavut), so the limit is now 90 and longitude does the discriminating work: wrapped metres spread across the full -180..180, which no Canadian file does. Confirmed the load was genuinely good by checking
M5V's centroid, which lands on downtown Toronto.Repair tooling
Fetcher#fetch(force:)— checksum dedupe answers "did upstream change", which is the wrong question when the loader changed and the rows it wrote are wrong. Replaces flipping ingestion status by hand (what I did in production).geo:verify_boundaries— reports geometry health per boundary type and exits non-zero if any is outside Canada. This is the check that should have existed.geo:reload_boundaries[source_name]— forces a re-download and reload; no argument does every boundary source.Exercised end to end on the real FSA file: 1,641 boundaries, extent correct,
M5Vcentroid on downtown Toronto.Still broken in production, and not fixed here
Nine boundary types remain corrupt. They pre-date #82 and this PR only gives us the means to fix them:
dactfsapopctrpedcd/cma/er/prcsdfedThe crosswalks are affected too.
geo:build_crosswalkassigns DAs to ridings by point-in-polygon on DA centroids, so every spatial crosswalk was computed from garbage geometry and needs rebuilding after the reload. Anything reading/api/v1/geo/boundaries,/api/v1/geo/crosswalk, or the crosswalk tables is currently getting wrong answers.Reload is the DA file first (it's the big one and the crosswalks depend on it), then the rest, then
geo:build_crosswalk. I'd rather run that deliberately with someone watching than fold it into this PR.Testing
852 runs, 2801 assertions, 0 failures. RuboCop clean across 520 files. Guard coverage includes the wrapped-globe case, the Arctic-to-the-pole case, an empty load, and
forcereusing the ingestion row rather than duplicating it.🤖 Generated with Claude Code