[object Object]

← back to Nationalrealestate

TK-16: add Ventura County CA (06111) situs-address coverage — pass 7, completes LA-adjacent ring

0386ff5ef1c36faf772001108d373b8f4c36312d · 2026-08-10 15:30:00 -0700 · Steve Abrams

Ventura DataDownloads/Address layer, 299,739 rows, 100% apn. COVERAGE-ONLY (AB-1785 +
no value/characteristics in public REST) = situs address + city + zip, like santa-clara.
lat/lng is on a separate apn-keyed layer (CommonData/2) = future join enhancement.

Verified e2e vs local usre: 3708 parcels, 100% addressed, priced=0 expected,
parcel_source(06111). tsc clean (my files). Local master, NOT pushed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 0386ff5ef1c36faf772001108d373b8f4c36312d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 10 15:30:00 2026 -0700

    TK-16: add Ventura County CA (06111) situs-address coverage — pass 7, completes LA-adjacent ring
    
    Ventura DataDownloads/Address layer, 299,739 rows, 100% apn. COVERAGE-ONLY (AB-1785 +
    no value/characteristics in public REST) = situs address + city + zip, like santa-clara.
    lat/lng is on a separate apn-keyed layer (CommonData/2) = future join enhancement.
    
    Verified e2e vs local usre: 3708 parcels, 100% addressed, priced=0 expected,
    parcel_source(06111). tsc clean (my files). Local master, NOT pushed.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 SOURCES.md                         |  4 +++-
 src/ingest/parcels/arcgis_sales.ts | 14 ++++++++++++++
 src/ingest/parcels/engine.ts       |  1 +
 src/jobs/hourly_loop.ts            |  1 +
 4 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/SOURCES.md b/SOURCES.md
index 47a72a7..8e58248 100644
--- a/SOURCES.md
+++ b/SOURCES.md
@@ -108,7 +108,7 @@ incl. 8 gated/blocked states → `BROKER-COVERAGE.md`. Adapters: `src/ingest/bro
 
 ## Free priced-DEED feeds — config-driven ArcGIS (`src/ingest/parcels/arcgis_sales.ts`)
 
-Each entry is a keyless ArcGIS REST layer that publishes real recorded SALE PRICE; one `SOURCES` config maps a page of features → parcel row + `parcel_event(event_type='sale')` with grantor/grantee/doc + a `source_url` deep-link. Registered in `parcel_source`, paginated over time by the hourly loop (`src/jobs/hourly_loop.ts`, `runSources` = the config's `cursorKey`). Live configs: `oregon-rlis` (41051/67/05), `spokane` (53063), `alameda` (06001), `deschutes` (41017), `crook` (41013), `sonoma` (06097), `colorado` (statewide composite `08*`), `maricopa` (04013), `thurston` (53067), `palm-beach` (12099), `klamath` (41035), `marion` (41047), `yakima` (53077), `lane` (41039), `jackson` (41029), `orange-county` (06059, coverage-only — AB-1785, no price).
+Each entry is a keyless ArcGIS REST layer that publishes real recorded SALE PRICE; one `SOURCES` config maps a page of features → parcel row + `parcel_event(event_type='sale')` with grantor/grantee/doc + a `source_url` deep-link. Registered in `parcel_source`, paginated over time by the hourly loop (`src/jobs/hourly_loop.ts`, `runSources` = the config's `cursorKey`). Live configs: `oregon-rlis` (41051/67/05), `spokane` (53063), `alameda` (06001), `deschutes` (41017), `crook` (41013), `sonoma` (06097), `colorado` (statewide composite `08*`), `maricopa` (04013), `thurston` (53067), `palm-beach` (12099), `klamath` (41035), `marion` (41047), `yakima` (53077), `lane` (41039), `jackson` (41029), `orange-county` (06059, coverage-only — AB-1785, no price), `ventura-county` (06111, coverage-only — address).
 
 - **Klamath County OR (41035)** — added 2026-07-27 (TK-16). ArcGIS Online FeatureServer `https://services.arcgis.com/H6Mh1bySxR4oHx6x/arcgis/rest/services/KC_ParcelSales/FeatureServer/0` (item `f6314b12b8cc4c35899157172601bf59`, "KC_ParcelSales" = Klamath County Parcel Sales). `where=SALE_PRICE>1000` → **38,796** priced sales. Rich fields: `SALE_PRICE` (int), `SALE_DATE` (epoch-ms), `OWNER_NAME` (→ grantee), `MIN_SITUS_ADDRESS`+`FIRST_CITY_NAME`, `SUM_FINSQFT`/`SUM_BEDRMS`/`SUM_BATH`, `MIN_YRBLT`, `SUM_Tot_Appr` (total appraised → `total_value`), `SALEBK` (→ doc_number, internal double-spaces collapsed by `clean()`), `FIRST_PROP_ID` (→ apn/source_id), `FIRST_PCLCD` (→ use_desc). Server caps pages at 1000. Verified end-to-end against the local `usre` DB (1,000 parcels + 1,000 sale events + links + source registration on first batch).
 
@@ -121,3 +121,5 @@ Each entry is a keyless ArcGIS REST layer that publishes real recorded SALE PRIC
 - **Jackson County OR / Medford (41029)** — added 2026-08-10 (TK-16, pass 5). Jackson County Assessment "LatestSales" FeatureServer `https://spatial.jacksoncountyor.gov/arcgis/rest/services/Assessment/LatestSales/FeatureServer/0` (HTTPS only — HTTP 302-redirects). `where=SalesPrice>1000` → **58,400** priced sales. An unusually rich, CLEAN (unqualified-key) layer: `SalesPrice` (double → `last_sale_price`), `SalesDate` (epoch-ms → `last_sale_date`), `DocumentNumber` (→ doc_number, e.g. `2025-18743`), `DocumentTypeDescription` (deed type e.g. "BARGAIN & SALE" → doc_type; null on some rows), `Grantor`+`Grantee` NAMES (both real), `SiteAddress`/`SiteCity`, `StatClassDescription` (→ use_desc), `YearBuilt`, `SquareFeet` (→ sqft), `maptaxlot` (→ apn/source_id — first `outField` for the deep-link). Also carries `DocumentURL` (recorded-instrument image link) preserved in raw. Verified end-to-end against the local `usre` DB; `tsc` clean.
 
 - **Orange County CA (06059)** — added 2026-08-10 (TK-16, pass 6; first of the "all California, radiating from LA" sweep). OC Public Works ArcGIS `https://ocgis.com/arcpub/rest/services/LegalLotsAttributeOpenData/FeatureServer/0`, **~982,299** parcels. **COVERAGE-ONLY** — CA **AB-1785** suppresses recorded sale price, so like `la-county`/`san-diego-sandag` this config carries NO price/sale-events (`priced=0`); it ingests address + assessed value + centroid lat/lng. Fields: `AssessmentNo` (→ apn/source_id — NOT `LegalLotID`, which repeats across condo units), `SiteAddress`, `SiteCityState` (→ city), `SiteZip5`, `LandVal`/`ImprovedVal`/`AssdAmt` (all `esriFieldTypeString` holding numbers — `num()` parses; `total_value = AssdAmt || LandVal+ImprovedVal` since `AssdAmt` is often null on condo units), `GPLU_DESC`/`ZC_DESCR` (use — almost entirely null, ~1.5k of 982k). lat/lng via the new `centroid:true` Source flag → `returnCentroid=true&outSR=4326` (reads `f.centroid.{x,y}`; native SR is CA State Plane VI feet, so `outSR=4326` is required), which avoids downloading ~982k polygons. `maxRecordCount=2000` (pageSize matched). Verified end-to-end against the local `usre` DB; `tsc` clean.
+
+- **Ventura County CA (06111)** — added 2026-08-10 (TK-16, pass 7; LA-adjacent ring). `https://maps.ventura.org/arcgis/rest/services/DataDownloads/Address/MapServer/0` (HTTPS only — HTTP 301-redirects), **299,739** rows, 100% apn. **COVERAGE-ONLY** — AB-1785 (no price) AND Ventura's public REST exposes no value or characteristics: this is situs-ADDRESS coverage (like `santa-clara`). Fields: `apn` (→ source_id), `fullsitus` (→ address), `city`, `zip`. `maxRecordCount=2000`; the layer's `objectIdField` is declared `None` but `orderByFields=objectid` + `resultOffset` paging works. lat/lng lives on a SEPARATE apn-keyed layer (`DataDownloads/CommonData/MapServer/2` has explicit `lat`/`lon`) — a future join enhancement (the one-layer-per-source engine can't join mid-pass). Verified end-to-end against the local `usre` DB; `tsc` clean.
diff --git a/src/ingest/parcels/arcgis_sales.ts b/src/ingest/parcels/arcgis_sales.ts
index b57d3d8..8e2e6f3 100644
--- a/src/ingest/parcels/arcgis_sales.ts
+++ b/src/ingest/parcels/arcgis_sales.ts
@@ -387,6 +387,20 @@ const SOURCES: Record<string, Source> = {
         lat: num(c.y), lng: num(c.x), use: clean(a.GPLU_DESC) || clean(a.ZC_DESCR),
         totalValue: assd || ((land || 0) + (imp || 0) || null), raw: a } as Rec; }).filter(Boolean) as Rec[],
   },
+  'ventura-county': {
+    // Ventura County CA — LA-adjacent ring. AB-1785 (no sale price) AND Ventura's public
+    // REST carries no value/characteristics — this is situs-ADDRESS coverage only (like
+    // santa-clara). apn-keyed Address layer, 299,739 rows. lat/lng lives on a SEPARATE
+    // apn-keyed layer (DataDownloads/CommonData/2 has lat/lon) — a future join enhancement.
+    key: 'ventura-county', label: 'Ventura County CA (situs addresses)', cursorKey: 'ventura_county',
+    geometry: false, orderBy: 'objectid', pageSize: 2000,
+    layer: 'https://maps.ventura.org/arcgis/rest/services/DataDownloads/Address/MapServer/0',
+    where: 'apn IS NOT NULL',
+    outFields: 'apn,fullsitus,city,zip',
+    fieldMap: { source_id: 'apn', address: 'fullsitus', city: 'city', zip: 'zip' },
+    toRecords: (fs) => fs.map(f => { const a = f.attributes; const apn = s(a.apn); if (!apn) return null;
+      return { fips: '06111', apn, address: clean(a.fullsitus), city: clean(a.city), zip: s(a.zip), raw: a } as Rec; }).filter(Boolean) as Rec[],
+  },
 };
 
 const execFileP = promisify(execFile);
diff --git a/src/ingest/parcels/engine.ts b/src/ingest/parcels/engine.ts
index 41d577f..e8fcdf6 100644
--- a/src/ingest/parcels/engine.ts
+++ b/src/ingest/parcels/engine.ts
@@ -40,6 +40,7 @@ const ADAPTERS: Record<string, () => Promise<{ run: () => Promise<{ upserted: nu
   lane: async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('lane') }; },
   jackson: async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('jackson') }; },
   'orange-county': async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('orange-county') }; },
+  'ventura-county': async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('ventura-county') }; },
 };
 
 async function main() {
diff --git a/src/jobs/hourly_loop.ts b/src/jobs/hourly_loop.ts
index 64549f2..7b36d7a 100644
--- a/src/jobs/hourly_loop.ts
+++ b/src/jobs/hourly_loop.ts
@@ -76,6 +76,7 @@ const JOBS: Job[] = [
   { name: 'deeds-lane',  script: 'src/ingest/parcels/engine.ts',     args: ['lane'],        minIntervalHours: 4, runSources: ['lane'] },
   { name: 'deeds-jackson',script:'src/ingest/parcels/engine.ts',     args: ['jackson'],     minIntervalHours: 3, runSources: ['jackson'] },
   { name: 'parcels-orange',script:'src/ingest/parcels/engine.ts',    args: ['orange-county'], minIntervalHours: 2, runSources: ['orange_county'] },
+  { name: 'parcels-ventura',script:'src/ingest/parcels/engine.ts',   args: ['ventura-county'], minIntervalHours: 2, runSources: ['ventura_county'] },
   // TK-5: full San Diego County (1.09M) via SANDAG — zoning + assessed value; was in engine but missing from rotation.
   { name: 'parcels-sd-sandag', script: 'src/ingest/parcels/engine.ts', args: ['san-diego-sandag'], minIntervalHours: 2, runSources: ['san_diego_sandag'] },
   // TK-5: Santa Clara County CA (San Jose, 493k) — situs address; was in engine but missing from rotation.

← 9af3911 TK-16: add Orange County CA (06059) parcel coverage + centro  ·  back to Nationalrealestate  ·  TK-16: add next CA ring — Santa Barbara (06083) + Imperial ( 25d8509 →