← back to Homesonspec
dr-horton: capture community coords from in-feed ld+json (yolo iter-4) — fixes 22.5k no-geo
d99677fddc7a5dff95c01ab73f9fdd5882aeeb2f · 2026-07-29 00:38:58 -0700 · Steve Abrams
Iteration-4 native-coord capture (DTD 4/4 verdict A). dr-horton's community pages (already fetched
as the adapter's source) carry the community's coordinate in a schema.org ld+json block (lowercase
latitude/longitude); the adapter was hardcoding lat/lon=null. Now parses it — $0, ZERO extra HTTP
requests — and assigns community-level geo to the community record + every home in it. State-gated +
coarse US bbox (a wrong pin is worse than null). Verified accurate: Seaford DE, Byhalia/Jackson/Canton
MS all land in the right city. dr-horton is 22,525 of the ~28k geo debt (80%); a FORCE_REEXTRACT
backfill geocodes them all. Future extracts get coords automatically.
Files touched
M collectors/dr-horton/src/index.ts
Diff
commit d99677fddc7a5dff95c01ab73f9fdd5882aeeb2f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Jul 29 00:38:58 2026 -0700
dr-horton: capture community coords from in-feed ld+json (yolo iter-4) — fixes 22.5k no-geo
Iteration-4 native-coord capture (DTD 4/4 verdict A). dr-horton's community pages (already fetched
as the adapter's source) carry the community's coordinate in a schema.org ld+json block (lowercase
latitude/longitude); the adapter was hardcoding lat/lon=null. Now parses it — $0, ZERO extra HTTP
requests — and assigns community-level geo to the community record + every home in it. State-gated +
coarse US bbox (a wrong pin is worse than null). Verified accurate: Seaford DE, Byhalia/Jackson/Canton
MS all land in the right city. dr-horton is 22,525 of the ~28k geo debt (80%); a FORCE_REEXTRACT
backfill geocodes them all. Future extracts get coords automatically.
---
collectors/dr-horton/src/index.ts | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/collectors/dr-horton/src/index.ts b/collectors/dr-horton/src/index.ts
index 3dad1b84..9449c12d 100644
--- a/collectors/dr-horton/src/index.ts
+++ b/collectors/dr-horton/src/index.ts
@@ -91,6 +91,22 @@ const num = (v: unknown): number | null => {
};
const str = (v: unknown): string | null => (typeof v === "string" && v.trim() ? v.trim() : null);
+// Community coordinate from the page's schema.org ld+json block (lowercase "latitude"/"longitude" —
+// the community's own geo; the UPPERCASE "Latitude" entries are a nearby-communities list, ignored).
+// DRH already fetches these pages, so this is a $0 parse-only geo source. State-gated: rejected if the
+// ld+json addressregion doesn't match the community's parsed state, or the coord fails a coarse US
+// bbox (a wrong pin is worse than null — same principle as the Census geocoder). [yolo iter-4]
+function communityGeo(html: string, state: string | null): { lat: number; lon: number } | null {
+ const m = html.match(/"latitude"\s*:\s*(-?\d+(?:\.\d+)?)\s*,\s*"longitude"\s*:\s*(-?\d+(?:\.\d+)?)/);
+ if (!m) return null;
+ const lat = Number(m[1]), lon = Number(m[2]);
+ if (!Number.isFinite(lat) || !Number.isFinite(lon) || lat === 0 || lon === 0) return null;
+ if (lat < 15 || lat > 72 || lon < -180 || lon > -60) return null; // coarse US bbox (lon must be west)
+ const region = html.match(/"addressregion"\s*:\s*"([A-Za-z]{2})"/i)?.[1]?.toUpperCase();
+ if (state && region && region !== String(state).toUpperCase()) return null; // wrong-state -> reject
+ return { lat, lon };
+}
+
export const drHortonAdapter: SourceAdapter = {
key: "dr-horton-site",
version: "1.0.0",
@@ -129,6 +145,7 @@ export const drHortonAdapter: SourceAdapter = {
// Community geography from the first item (Items carry CityStateZip).
const first = items[0] ?? {};
const geo = parseCityStateZip(str(first.CityStateZip));
+ const cgeo = communityGeo(html, geo.state); // $0 in-feed community coord (state-gated) — [yolo iter-4]
const urlCity = seg[2] ? titleCase(seg[2]) : null;
const salesPhoneRaw = html.match(/tel:([+0-9()\s.\-]{7,})/)?.[1] ?? null;
const salesPhone = salesPhoneRaw ? salesPhoneRaw.replace(/[^0-9+]/g, "") : null;
@@ -145,8 +162,8 @@ export const drHortonAdapter: SourceAdapter = {
zip: fv(geo.zip, geo.zip, page.url),
county: fv<string>(null, null, page.url),
metro: fv(seg[1] ? titleCase(seg[1]) : null, seg[1] ?? null, page.url),
- lat: fv<number>(null, null, page.url),
- lon: fv<number>(null, null, page.url),
+ lat: fv(cgeo?.lat ?? null, cgeo ? String(cgeo.lat) : null, page.url, cgeo ? "community ld+json geo" : null),
+ lon: fv(cgeo?.lon ?? null, cgeo ? String(cgeo.lon) : null, page.url, cgeo ? "community ld+json geo" : null),
hoaFeeMonthly: fv<number>(null, null, page.url),
schoolDistrict: fv<string>(null, null, page.url),
ageRestricted: fv<boolean>(null, null, page.url),
@@ -188,6 +205,8 @@ export const drHortonAdapter: SourceAdapter = {
sqft: fv(num(it.SquareFootage), it.SquareFootage === undefined ? null : String(it.SquareFootage), page.url),
stories: fv(num(it.NumberOfStories), null, page.url),
garageSpaces: fv(num(it.NumberOfGarages), null, page.url),
+ lat: fv(cgeo?.lat ?? null, cgeo ? String(cgeo.lat) : null, page.url, cgeo ? "community ld+json geo (home inherits community coord)" : null),
+ lon: fv(cgeo?.lon ?? null, cgeo ? String(cgeo.lon) : null, page.url, cgeo ? "community ld+json geo (home inherits community coord)" : null),
homeType: fv("SINGLE_FAMILY" as const, null, page.url, "D.R. Horton inventory home"),
constructionStatus: fv(statusToEnum(status), status, page.url),
estCompletionDate: fv<string>(null, null, page.url),
← 13b17887 geocode: 2k-address chunks (was 10k) so bulk backfill fits i
·
back to Homesonspec
·
geocode: one-time dr-horton coord backfill from CACHED snaps fa040b5f →