[object Object]

← 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

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 →