Spaces:
Running
Running
Adding a New Region (County)
The estimator is region-agnostic: each area you cover is one RegionConfig in
src/lawn_estimator/regions.py that wires concrete data-source adapters behind
the ports in src/lawn_estimator/sources/base.py. Adding a county is finding a
handful of public endpoints and writing a ~20-line build_<county>() function β
no core pipeline changes.
Resolution tries each registered region's geocoder in order; the first hit pins that region for the whole run (falling back to validated Google + parcel-pinning if no county address layer matches).
What to gather
Required (4)
| # | Source | What it is / how to find it | Used for |
|---|---|---|---|
| 1 | Address points | An Esri Address_Points-style FeatureServer/MapServer layer with a full-address field (usually FULLADDR). Search the county's ArcGIS portal (<county>.gov/gis, data.<county>...opendata.arcgis.com). |
Free, keyless geocoding that lands on the building (so the parcel lookup always hits). Optional β a region can geocode via Google only, but that costs API calls and is less precise. |
| 2 | Parcels | An Esri parcel FeatureServer (polygon) supporting point-in-polygon queries (Query capability). Plus a field map: which attribute is the parcel id, the site address, and year-built (if any). |
The legal boundary + the year-built vintage guard. |
| 3 | LiDAR | Almost always USGS via The National Map β national coverage. Query tnmaccess.nationalmap.gov/api/v1/products (datasets=Lidar Point Cloud (LPC), a bbox over the area) and note the dataset title prefix + flight year. A local county LiDAR bucket only if it's newer/denser (e.g. Douglas's 2022). |
Ground points β the high-confidence estimate (with imagery-only fallback when absent). |
| 4 | Local CRS | The correct UTM zone (EPSG). Eastern Nebraska = EPSG:26914 (14N); western Iowa / across the Missouri = EPSG:26915 (15N). |
Area math + LiDAR/point projection. |
Optional (graceful fallback if absent)
| # | Source | Absent β fallback |
|---|---|---|
| 5 | Street / road centerlines (Esri polyline layer) | Street-aware right-of-way extension (Phase 3.5). Absent β extend-all + neighbor subtraction (per-extension sizes still reported so the user can hand-correct). |
| 6 | Ortho imagery tile service (county aerials) | Crisp display layer. Absent β visualizations render on Google satellite. |
The two counties we ship today (worked examples)
Douglas County, NE (build_douglas)
- Address points:
dcgis.org/server/rest/services/vector/Address_Points/FeatureServer/0(FULLADDR) - Parcels:
dcgis.org/server/rest/services/vector/Parcels_public/FeatureServer/0β field map{object_id: OBJECTID, site_address: PROPERTY_A, year_built: BLDG_YRBLT, bldg_sqft: BLDG_SF} - LiDAR: county 2022 QL1 on public S3 (
DouglasS3LidarSource, vintage 2022, ~8 pts/mΒ²) - Streets:
.../vector/Street_Centerlines/FeatureServer/0Β· Ortho: 2025 county tiles Β· CRS: 26914
Sarpy County, NE (build_sarpy)
- Address points:
geodata.sarpy.gov/arcgis/rest/services/Cadastral/LandRecordsDynamic/MapServer/2(FULLADDR) - Parcels:
services.arcgis.com/OiG7dbwhQEWoy77N/.../Sarpy_Parcels_WFL1/FeatureServer/0β field map{object_id: OBJECTID, site_address: SITEADDRESS}(no year-built field β new construction rides the RGB-only fallback instead of a vintage warning) - LiDAR:
UsgsTnmLidarSource(["NE_Eastern_Nebraska_UA_LiDAR_2016"], vintage_year=2016)(~2 pts/mΒ²) - Streets:
.../LandRecordsDynamic/MapServer/3(Road Centerlines) Β· Ortho: none (Google display) Β· CRS: 26914
Note how the field map absorbs schema differences (Douglas PROPERTY_A vs Sarpy
SITEADDRESS) so the pipeline only ever sees canonical keys.
Steps
- Find endpoints 1β4 on the county's ArcGIS portal (~30 min). Verify the parcel
layer's
?f=jsonshows"capabilities": "Query"and lists the fields you'll map. - Confirm LiDAR with a TNM bbox query over a known address; record the dataset prefix + year. (If TNM has nothing, the region still works on RGB-only.)
- Write
build_<county>(session, config) -> RegionConfiginregions.py, reusingEsriAddressPointsGeocoder,EsriParcelSource(with your field map),UsgsTnmLidarSource(or a county source), and optional streets/ortho. - Append it to
REGION_BUILDERS. - Validate (the "quality LOOK"): run 3β5 known addresses. Confirm they resolve to the right region + parcel, LiDAR produces sane ground points (or cleanly falls back), and the visualization aligns (red points on roofs, green on lawn). Spot-check estimates against the RGB vegetation baseline.
- Regression: re-run the Douglas QA batch β it must stay byte-identical (adding a region must never perturb existing ones).
Gotchas learned the hard way
- Gov ArcGIS servers flake with transient 5xx. All Esri/TNM queries go through
http_get_with_retry;resolve_regiondegrades to Google on sustained errors. - Municipality labels for unincorporated addresses can be the county name; the geocoder prefers the city the user typed.
- Attached housing (duplex/townhome) has one parcel per unit β measuring only the queried address's parcel is correct, not a bug.
- LiDAR vintage is per-source, not per-region β a newer flight is a one-line swap.
- UTM zone: crossing the Missouri into Iowa means
EPSG:26915, not 26914.