← back to Nationalrealestate
TK-16: add Josephine County OR (41033) free priced-deed feed
d7d2c15c52fc49a18f6fb36d5646a52ea78625ac · 2026-08-24 09:28:39 -0700 · Steve
Rich clean-key ArcGIS Assessor_Taxlots feed: 30,140 priced sales w/ SALE_PRICE
+SALE_DATE+DEED_TYPE+INST_NO(doc)+situs+owner+RMV value+YR_BLT/SQ_FT/BEDRMS+
explicit Latitude/Longitude (no geometry download). Config in arcgis_sales.ts,
adapter appended in engine.ts, deeds-josephine loop job (3h), SOURCES.md.
Verified local usre: 5,997 parcels/5,993 events (all doc#, 19 deed types,
1970-2026, $1.5k-$46.9M). tsc 0 errors. Prod deploy + backfill Steve-gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M SOURCES.mdM src/ingest/parcels/arcgis_sales.tsM src/ingest/parcels/engine.tsM src/jobs/hourly_loop.ts
Diff
commit d7d2c15c52fc49a18f6fb36d5646a52ea78625ac
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Aug 24 09:28:39 2026 -0700
TK-16: add Josephine County OR (41033) free priced-deed feed
Rich clean-key ArcGIS Assessor_Taxlots feed: 30,140 priced sales w/ SALE_PRICE
+SALE_DATE+DEED_TYPE+INST_NO(doc)+situs+owner+RMV value+YR_BLT/SQ_FT/BEDRMS+
explicit Latitude/Longitude (no geometry download). Config in arcgis_sales.ts,
adapter appended in engine.ts, deeds-josephine loop job (3h), SOURCES.md.
Verified local usre: 5,997 parcels/5,993 events (all doc#, 19 deed types,
1970-2026, $1.5k-$46.9M). tsc 0 errors. Prod deploy + backfill Steve-gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
SOURCES.md | 4 +++-
src/ingest/parcels/arcgis_sales.ts | 24 ++++++++++++++++++++++++
src/ingest/parcels/engine.ts | 1 +
src/jobs/hourly_loop.ts | 1 +
4 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/SOURCES.md b/SOURCES.md
index 83dd726..5276800 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), `ventura-county` (06111, coverage-only — address), `santa-barbara` (06083, coverage), `imperial` (06025, coverage), `san-bernardino` (06071, coverage), `san-joaquin` (06077, coverage), `fresno` (06019, coverage), `monterey` (06053, coverage), `solano` (06095, coverage), `butte` (06007, coverage), `sacramento` (06067, coverage), `stanislaus` (06099, coverage), `napa` (06055, coverage), `contra-costa` (06013, coverage), `el-dorado` (06017, coverage), `nevada` (06057, coverage), `tulare` (06107, coverage), `whatcom` (53073, priced deeds).
+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), `santa-barbara` (06083, coverage), `imperial` (06025, coverage), `san-bernardino` (06071, coverage), `san-joaquin` (06077, coverage), `fresno` (06019, coverage), `monterey` (06053, coverage), `solano` (06095, coverage), `butte` (06007, coverage), `sacramento` (06067, coverage), `stanislaus` (06099, coverage), `napa` (06055, coverage), `contra-costa` (06013, coverage), `el-dorado` (06017, coverage), `nevada` (06057, coverage), `tulare` (06107, coverage), `whatcom` (53073, priced deeds), `josephine` (41033, priced deeds).
- **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).
@@ -120,6 +120,8 @@ 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.
+- **Josephine County OR / Grants Pass (41033)** — added 2026-08-24 (TK-16, claude-run-16). Josephine County Assessor "Assessor_Taxlots" master FeatureServer `https://gis.co.josephine.or.us/arcgis/rest/services/Assessor/Assessor_Taxlots/FeatureServer/0` (keyless). `where=SALE_PRICE>1000` → **30,140** priced sales of 41,991 total parcels. A RICH, CLEAN-key priced-deed layer on par with Jackson, and rarer still it combines the priced-deed fields WITH explicit lat/lng: `SALE_PRICE` (double → `last_sale_price`), `SALE_DATE` (epoch-ms → `last_sale_date`), `DEED_TYPE` (WD/BS/... → doc_type), `INST_NO` (recorder doc, e.g. `20-010845` → doc_number), `SITUS`/`SITUS_CITY`/`SITUS_ZIP` (situs — some rural rows come as `* SPEAKER RD` where `*` is a missing house #, so the leading `* ` is stripped), `NAME` (owner → grantee), `RMV` (real-market total value → total_value), `YR_BLT`/`SQ_FT`/`BEDRMS` (characteristics), `PROP_CLASS` (assessor class code → use_desc), `ACCOUNT` (→ apn/source_id — first `outField` for the per-parcel GIS deep-link `where=ACCOUNT='<apn>'`), and explicit `Latitude`/`Longitude` fields (so `geometry:false` — no polygon download, like `imperial`/`butte`). `maxRecordCount=2000` (pageSize matched). 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 5a4f390..6c1726f 100644
--- a/src/ingest/parcels/arcgis_sales.ts
+++ b/src/ingest/parcels/arcgis_sales.ts
@@ -682,6 +682,30 @@ const SOURCES: Record<string, Source> = {
return { fips: '53073', apn, price: anyPrice(a.sale_price), saleDate: anyDate(a.deed_date),
docNum: s(a.sale_id), docType: clean(a.sale_type), raw: a } as Rec; }).filter(Boolean) as Rec[],
},
+ josephine: {
+ // Josephine County OR (Grants Pass) — a RICH, clean-key priced-deed county on par with Jackson:
+ // the Assessor_Taxlots master layer carries SALE_PRICE(double)+SALE_DATE(epoch-ms)+DEED_TYPE+
+ // INST_NO(recorder doc)+situs+owner(NAME)+RMV(total real-market value)+YR_BLT+SQ_FT+BEDRMS+
+ // PROP_CLASS(use) AND explicit Latitude/Longitude fields (no geometry download — like imperial).
+ // where=SALE_PRICE>1000 → 30,140 priced sales of 41,991 total parcels. ACCOUNT is the parcel key.
+ // Some rural situs come as "* SPEAKER RD" (asterisk = missing house #) → leading "* " stripped.
+ key: 'josephine', label: 'Josephine County OR (Grants Pass)', cursorKey: 'josephine', geometry: false, orderBy: 'OBJECTID', pageSize: 2000,
+ layer: 'https://gis.co.josephine.or.us/arcgis/rest/services/Assessor/Assessor_Taxlots/FeatureServer/0',
+ where: 'SALE_PRICE>1000',
+ // ACCOUNT MUST be first — engine derives the per-parcel GIS deep-link as where=<first>='<apn>'.
+ outFields: 'ACCOUNT,SITUS,SITUS_CITY,SITUS_ZIP,SALE_PRICE,SALE_DATE,DEED_TYPE,INST_NO,NAME,RMV,YR_BLT,SQ_FT,BEDRMS,PROP_CLASS,Latitude,Longitude',
+ fieldMap: { source_id: 'ACCOUNT', address: 'SITUS', city: 'SITUS_CITY', zip: 'SITUS_ZIP', use_desc: 'PROP_CLASS',
+ total_value: 'RMV', year_built: 'YR_BLT', sqft: 'SQ_FT', beds: 'BEDRMS', owner_name: 'NAME',
+ doc_type: 'DEED_TYPE', doc_number: 'INST_NO', last_sale_price: 'SALE_PRICE', last_sale_date: 'SALE_DATE',
+ lat: 'Latitude', lng: 'Longitude' },
+ toRecords: (fs) => fs.map(f => { const a = f.attributes; const apn = s(a.ACCOUNT); if (!apn) return null;
+ let addr = clean(a.SITUS); if (addr) addr = addr.replace(/^\*\s*/, '') || null;
+ return { fips: '41033', apn, price: anyPrice(a.SALE_PRICE), saleDate: anyDate(a.SALE_DATE),
+ address: addr, city: clean(a.SITUS_CITY), zip: s(a.SITUS_ZIP), use: clean(a.PROP_CLASS),
+ year: num(a.YR_BLT), sqft: num(a.SQ_FT), beds: num(a.BEDRMS), totalValue: num(a.RMV),
+ lat: num(a.Latitude), lng: num(a.Longitude),
+ grantee: clean(a.NAME), docNum: clean(a.INST_NO), docType: clean(a.DEED_TYPE), 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 f6a69db..b6d687f 100644
--- a/src/ingest/parcels/engine.ts
+++ b/src/ingest/parcels/engine.ts
@@ -66,6 +66,7 @@ const ADAPTERS: Record<string, () => Promise<{ run: () => Promise<{ upserted: nu
// TK-10777: Whatcom historical layers — history-safe (date-guarded) upsert
'whatcom-2021': async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('whatcom-2021') }; },
'whatcom-2020': async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('whatcom-2020') }; },
+ josephine: async () => { const m = await import('./arcgis_sales.ts'); return { run: m.ingestArcgisSales('josephine') }; }, // TK-16: OR priced-deed (Grants Pass)
};
async function main() {
diff --git a/src/jobs/hourly_loop.ts b/src/jobs/hourly_loop.ts
index 1ff5e08..493572d 100644
--- a/src/jobs/hourly_loop.ts
+++ b/src/jobs/hourly_loop.ts
@@ -75,6 +75,7 @@ const JOBS: Job[] = [
{ name: 'deeds-yakima',script:'src/ingest/parcels/engine.ts', args: ['yakima'], minIntervalHours: 3, runSources: ['yakima'] },
{ 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: 'deeds-josephine',script:'src/ingest/parcels/engine.ts', args: ['josephine'], minIntervalHours: 3, runSources: ['josephine'] }, // TK-16: OR priced-deed (Grants Pass, 30k priced)
{ 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'] },
{ name: 'parcels-sb', script: 'src/ingest/parcels/engine.ts', args: ['santa-barbara'], minIntervalHours: 2, runSources: ['santa_barbara'] },
← 1326a54 Whatcom WA: add situs/owner join + SWDSales2020/2021 history
·
back to Nationalrealestate
·
Snohomish County WA (53061): add free priced-deed feed (arcg 86cb190 →