Skip to main content

Geocoder vs provided coordinates — TX HHSC nursing facilities (#619)

2026-06-17. A free, real-world geocoder accuracy check: the TX HHSC nursing-facilities registry ships an authoritative Geo Location (lat,lon) per facility, so we can geocode each facility's streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. address with our own pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. and measure the great-circle delta. 1172 facilities with a usable address + in-stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. coordinate (3 skipped). Neutral scope: this measures GEOCODER ACCURACY on real public addresses — it makes no claim about the facilities.

Reproduce: scripts/record-matcher/txhhsc-to-oarow.tsscripts/eval/oa-resolver-eval.ts --eval <jsonl> --address-points address-points-us-tx.db --interpolation interpolation-us-tx.db.

Geocoder accuracy by tier (our pipeline: neural parse → resolver)

tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous.coord p50coord p90coord p99tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. hit rate
admin-centroid (citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.)3.4 km27.2 km741.6 km100% (always)
+ address-point (rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data.)0.7 km13.5 km486.8 km47.0% (551/1172)
+ interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. (streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.)0.1 km8.2 km476.7 km12.5% (146/1172)

The finer tierstierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. are the story. The admin-centroid tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. lands a facility in the right citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. — p50 3.4 km, which is just "the citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.'s middle is a few km from the facility." Switch in the address-point tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. where we have a rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. for the parsed streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. + number, and p50 collapses to 0.7 km; the interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. (no exact point, interpolated along the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. segmentsegmentA punctuation-bounded chunk of the normalized input — the comma-separated parts of 'Portland, OR' — used to give downstream stages structural context.) lands p50 0.1 km — 100 m, streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.-accurate. The honest caveat is coveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present.: the rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. fires on 47% of these facilities and interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. on a further 12.5%, so ~40% still fall back to the city centroidlocality centroidThe representative centre point of a city or locality, used as a coarse coordinate when no exact address point is available — the coarsest tier of the geocode cascade.. The tail (p99 ~470–740 km) is wrong-place resolutions, not tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. imprecision — a handful of facilities whose parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. resolves to the wrong localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. entirely.

The honest surprise — v0 out-parses neural on this distribution

Graded through the same resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon., the rules parser (v0) beats neural on localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.-match here: 96.8% vs 90.1% (and coord p50 3.0 vs 3.4 km). That is the inverse of the clean-OpenAddressesOpenAddresses (OA). A global open aggregation of address points collected from many official sources. A primary source of component-supervised training data outside proprietary registries. result, where neural leads 84.0% vs 82.1% (2026-06-17-per-type-headtohead.md).

The cause, confirmed by a direct probe (scripts/eval/case-check.ts): these are ALL-CAPS facility records (214 JONES RD, ELKHART, TX 75839), and the neural modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' — trained predominantly on mixed-case text — degrades on them. On a 5-address spot-check, localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. is correct 3/5 in all-caps vs 5/5 in title-case, and the failure is a clean tokenization-boundary artifact: PALESTINE parsesaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. to a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. of ALESTINE (the leading P is dropped) in all-caps, but Palestine parsesaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. correctly. v0's dictionaries are case-folded by construction, so on a SHOUTING dataset that robustness wins.

This is a real "where we lose," with a near-free fix — and we built it (#690). parse(…, { normalizeCase: true }) title-cases a detected all-caps ASCII input before the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' (detection is strict: mixed-case and non-ASCII/accented input are left untouched, so the path is byte-stable by construction). Re-running this evalevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error. with --normalize-case:

metricall-caps (default)+ normalizeCase (#690)
neural localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.-match90.1%99.7% (now > v0's 96.8%)
address-point hit rate47.0%61.8%
coord p50 (admin)3.4 km2.8 km
coord p50 (+address-point)0.7 km0.1 km
coord p99 (+address-point)486.8 km20.7 km

The fix doesn't just close the gap — it overtakes v0 on localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. (99.7 vs 96.8) and collapses the catastrophic-miss tail (p99 487 → 21 km), because correct localities resolve to the right place and the parsed streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels./number then hits the rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. shardshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. more often (47% → 62%). The one cost: regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. dips 100.0% → 98.0% — title-casing the 2-letter stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. (TXTx) trips ~2% of regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. resolutions, a small, fixable artifact (preserve all-caps 2-letter stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. codes) against a large netneural networkA model made of layers of simple numeric units whose connection strengths (weights) are learned from data. The transformer encoder at Mailwoman's core is a neural network. win. Ships default-OFF behind the normalizeCase opt.

Reading

  • The geocoder works where it has data. On real TX facility addresses, the address-point + streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. tierstierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. put p50 error at 0.1–0.7 km — rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data.-to-streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. accuracy, not citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.-centroid. The pre-geocoded seed (#619's other half) lets us skip re-geocodinggeocodingThe process of converting an address into geographic coordinates (latitude and longitude). Mailwoman geocodes in a multi-tier cascade: exact address-point match → street interpolation → locality centroid. Each tier is progressively coarser but more widely available. where the source already carries an authoritative point; this validation is what justifies trusting our own coordinate where it doesn't.
  • CoveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present., not precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1., is the frontier. ~40% of these facilities fall back to the city centroidlocality centroidThe representative centre point of a city or locality, used as a coarse coordinate when no exact address point is available — the coarsest tier of the geocode cascade. for lack of a TX rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data./interp hit on the parsed streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. — the address-point shardshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. coveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present. is the lever, not the tiertierInternal versioning of which label classes the model emits. Tier 1 is the coarse components (country, region, locality, postcode); Tier 2 adds venue, street, house_number; Tier 3 (future) would add attention, po_box, and POI venue subtyping. Historically called 'Stage 1/2/3' before the runtime-pipeline naming made that ambiguous. math.
  • Case robustness is a measurable neural gap. All-caps compliance/registry data is common, and we lose 6.7pp of localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. there vs the rules parser. Cheap to fix, worth fixing.