What
The build reads attribute rows and never the geometry column, yet the geometry is the field a perimeter dataset exists for. This adds geometry to the measured fields: for each FRAP record, whether a geometry is present, whether it is empty, whether it passes an OGC validity check, whether it is multipart, and whether its computed area agrees with GIS_ACRES within a stated tolerance; for each DINS record, whether latitude and longitude are present, numeric, and inside California's published extent. Each is published as a three-state field (recorded, recorded-as-unknown, empty) with counts, per year for FRAP and per incident for DINS, plus the count of records whose recorded acreage and computed area disagree.
Why it matters
A record with attributes and no polygon is incomplete in the way the project already counts, and an invalid polygon answers a spatial question undefined; the sibling wildfire-service-territory-overlap measured exactly that on the CEC layer (eight invalid outlines, two of them the largest) and had to repair before use. Nothing here repairs, draws, or republishes a shape or a coordinate; it counts. The acquisition pulls attributes only today, so acquire.py gains an explicit --with-geometry, and the artifacts record whether geometry was acquired, publishing null for these fields when it was not.
Scope
acquire.py --with-geometry and a recorded geometry_acquired fact in acquisition.json and the artifacts.
src/perimeter/geometry.py: validity via shapely (new pinned dependency), area in the projection FRAP publishes, tolerance declared in schema.py with its reason.
- Fields
GEOMETRY_PRESENT, GEOMETRY_VALID, GEOMETRY_PARTS, AREA_AGREES_WITH_GIS_ACRES (FRAP) and COORDINATE_IN_EXTENT (DINS); page tables and docs/MARKERS.md entries.
- Fixtures gain a hand-written invalid polygon and an out-of-extent coordinate.
Out of scope
- Repairing geometry, publishing any shape, or spatial joins.
- Republishing any coordinate; only counts leave the build.
Done when
- Fixtures report the invalid polygon and the out-of-extent point in the expected buckets, and a build without geometry publishes
null for every geometry field with geometry_acquired: false.
- A geometry column present but unreadable raises
SchemaDriftError rather than counting as empty.
- The determinism gate holds with geometry included.
Pointers
src/perimeter/acquire.py, records.py, schema.py (FieldSpec), artifacts.py
wildfire-service-territory-overlap/src/wildfire_service_territory_overlap/geometry.py (validity ledger)
Proposed with AI assistance.
What
The build reads attribute rows and never the geometry column, yet the geometry is the field a perimeter dataset exists for. This adds geometry to the measured fields: for each FRAP record, whether a geometry is present, whether it is empty, whether it passes an OGC validity check, whether it is multipart, and whether its computed area agrees with
GIS_ACRESwithin a stated tolerance; for each DINS record, whether latitude and longitude are present, numeric, and inside California's published extent. Each is published as a three-state field (recorded, recorded-as-unknown, empty) with counts, per year for FRAP and per incident for DINS, plus the count of records whose recorded acreage and computed area disagree.Why it matters
A record with attributes and no polygon is incomplete in the way the project already counts, and an invalid polygon answers a spatial question undefined; the sibling
wildfire-service-territory-overlapmeasured exactly that on the CEC layer (eight invalid outlines, two of them the largest) and had to repair before use. Nothing here repairs, draws, or republishes a shape or a coordinate; it counts. The acquisition pulls attributes only today, soacquire.pygains an explicit--with-geometry, and the artifacts record whether geometry was acquired, publishingnullfor these fields when it was not.Scope
acquire.py --with-geometryand a recordedgeometry_acquiredfact inacquisition.jsonand the artifacts.src/perimeter/geometry.py: validity via shapely (new pinned dependency), area in the projection FRAP publishes, tolerance declared inschema.pywith its reason.GEOMETRY_PRESENT,GEOMETRY_VALID,GEOMETRY_PARTS,AREA_AGREES_WITH_GIS_ACRES(FRAP) andCOORDINATE_IN_EXTENT(DINS); page tables anddocs/MARKERS.mdentries.Out of scope
Done when
nullfor every geometry field withgeometry_acquired: false.SchemaDriftErrorrather than counting as empty.Pointers
src/perimeter/acquire.py,records.py,schema.py(FieldSpec),artifacts.pywildfire-service-territory-overlap/src/wildfire_service_territory_overlap/geometry.py(validity ledger)Proposed with AI assistance.