MESA
home / methods / geocoding

Offline address geocoding

MESA attaches coordinates to addresses by matching them against a local copy of authoritative address points, entirely in memory, with no network calls. The matcher is deterministic (same input and reference produce the same output) and every result is labeled with how precisely it was placed.

on this page
  1. 1Reference data
  2. 2Normalization & parsing
  3. 3Candidate generation
  4. 4The geometry gate
  5. 5Fellegi-Sunter scoring
  6. 6Match modes
  7. 7Precision tiers
  8. 8Validation

01 reference data

The geocoder reads one required layer and, optionally, two supplemental layers, each a separate SQLite file that is loaded once into compact in-memory indexes. Nothing is merged, and no web service is ever called. You supply the data; a bundled Python script builds it from the authoritative source.

addressesrequired. Authoritative address points (street, city, ZIP, lat, lon, optional grid/unit/landmark columns). All validation uses Utah's UGRC statewide points, 1,489,417 records.
road_segmentsoptional. Road centerlines with address ranges, consulted only after the point layer fails, to interpolate a block-level position.
address_pointsoptional local supplement, consulted first but only for exact ZIP+street matches; it never fuzzy-matches.

Coordinates must be WGS84 (EPSG:4326) decimal degrees. Provenance tables record the dataset, build, and source licenses (for Utah: UGRC address points and roads, public domain under Utah GRAMA / SGID).

02 normalization & parsing

The pipeline is Normalize → Parse → Retrieve → Score → Place. Normalization uppercases text, strips punctuation, collapses whitespace, and canonicalizes standard abbreviations (AVENUE → AVE) and compound directionals (NORTHEAST → NE) before any lookup. Parsing splits the address into house number, directional, street name, type, unit, city, state, and ZIP. The canonical full address stays the primary match field; the parsed components are structured inputs to scoring.

Several jurisdiction-specific normalizations matter for the rural western address forms MESA targets:

A ZIP+4 is accepted but only the first five digits are used.

03 candidate generation

Candidates are retrieved through several blocking channels (sorted in-memory lookup tables built once at load), keyed by ZIP, city, house+ZIP, house+city, and so on. The shipped cascade is: exact hash lookup first; then, if needed, house-number-keyed fuzzy buckets. Fuzzy matching is skipped entirely if a candidate set exceeds 500 records (it would be both slow and low-precision), and exact string equality within the set is always tried before any fuzzy comparison.

04 the address-geometry gate

Before any fuzzy name comparison, a candidate must pass a geometry-skeleton gate: the numeric and directional tokens of the address (its "skeleton") must agree. This is the single most important guard against false matches.

A name-token alignment check then requires every canonical street-name token to appear in the query within a typo allowance, which tolerates trailing locality junk (like a county name) while rejecting a fabricated street name grafted onto a real house and ZIP.

The geometry gate does most of this work: in validation it raised hard-negative specificity from 71.6% to 99.4% on the adversarial suite by requiring exact house / grid / directional agreement before any fuzzy match is even considered.

String similarity

The live matcher uses two string metrics, both fully specified for reproducibility:

bounded Levenshteinearly-exit banded edit distance, used for token-level checks4
OSA distanceoptimal string alignment (transposition-aware Damerau-Levenshtein); a swap of two adjacent characters is a single edit, which matches how real address typos and OCR errors behave3
For faithfulness: Jaro-Winkler, Double Metaphone, and token-set matching are not in the shipped scoring path. They were used only as offline forensic tools to characterize why the unmatched residual is unmatched, and are documented here so the record is accurate.

05 the match score

Surviving candidates are ranked by a Fellegi-Sunter record-linkage score1: a sum of per-field log-odds weights. Each weight is log2(m/u) in bits, where m is the probability a true match agrees on a field (taken as 0.97 under realistic input noise) and u is the probability two records agree by chance, measured from random same-ZIP address pairs in the Utah corpus.

measured field weights (bits)\[ \begin{array}{lrlr} \text{house number} & +5.88 & \quad\text{suffix} & +2.93 \\ \text{directional} & +1.92 & \quad\text{grid} & +1.51 \end{array} \]

A field's weight is added on agreement and subtracted on disagreement (a suffix mismatch is penalized more softly, at half weight). The street name contributes a rarity × similarity term: each name token is weighted by how rare it is in the corpus (its inverse document frequency) and scaled by 2·similarity − 1, where similarity is 1 − OSA / max(len). So an exact token earns its full (rarity-weighted) positive weight, a half-corrupted one earns roughly zero, and a genuinely different name earns a negative weight. This is what lets the score separate a real typo from a fabricated name dropped onto a real block. ZIP and city agreement are weak nudges, since candidates are already ZIP/city-scoped.

The total log-odds is mapped to a 0-1 certainty by a Platt sigmoid2, fit by logistic regression on the development split only:

Platt calibration\[ \text{certainty} = \frac{1}{1 + \exp\!\big(-(2.875 + 0.1048 \cdot \text{logOdds})\big)} \]

An ambiguity guard withholds a point (degrading it to a centroid) when a different-ZIP record sharing the query's house number scores within a small log-odds margin of the winner; same-ZIP rivals are treated as data duplicates, not ambiguity.

A confident match can still be imprecise. Certainty is a Platt-scaled pseudo-confidence in the text match; it is explicitly not a calibrated probability that the coordinate is correct. A ZIP centroid can carry high certainty (the ZIP was unambiguous) yet coarse precision. For any analysis that needs point-level accuracy, filter on the precision tier, not on certainty.

06 match modes

A match whose certainty is below the active mode's threshold is degraded to an APPROXIMATE centroid rather than emitted as a possibly-wrong point. The mode changes only this threshold, not the scoring.

modethresholduse
Permissive0.00emit every point match; maximize yield, review downstream
Balanced0.90default. Calibrated, but effectively non-withholding, so real-world point yield is preserved
Strict0.98withhold low-confidence points; maximize precision, accept lower yield

Balanced is the default for an evidence-based reason. On the held-out synthetic test set, Strict looked nearly free; but on the real patient sample it cut point yield from 62% to 50%, because real patient-entered addresses score lower than synthetic positives and a threshold tuned on synthetic data does not transfer. Balanced keeps the calibrated score without paying that yield cost.

07 precision tiers

Every geocoded row carries a Geocode_Precision label. Read it first; it, not the certainty score, tells you whether a coordinate is a real location.

tiermeaningposition
POINTmatched an authoritative address pointthe reference coordinate itself
INTERPOLATEDestimated between known house numbers, or along a road centerline whose range brackets the housemedian 9 m, P95 85 m (96% < 100 m)
RELOCATEDunique street+house recovered in a neighbouring ZIP (a ZIP/city typo), gated by proximityaddress-level
LANDMARKthe "street" was a named place found in the stated ZIPapproximate
APPROXIMATEno street match; a ZIP, city, or ZIP+city centroidlocality only
VERY_LOWlast-resort whole-database centroiddo not map as a location
NONEcould not be placed; coordinates blankn/a

MESA also emits a derived analytic-support class (address_point, interpolated_street, zip_centroid, and so on) and a plain-language description, computed deterministically from the precision and method so downstream analysis can filter to the tier it needs.

08 validation

Accuracy here means recovery of the correct reference record under noisy input, not independent real-world coordinate accuracy (which these figures do not measure). A query counts as a success only if it resolves to within 50 m of the correct record's coordinate, so snapping to a different nearby record is scored a miss. The noise generators deliberately corrupt the exact tokens the production gate guards (house numbers, directionals, grid ordinals), and the negative controls are fabricated by mechanisms the gate was not tuned on (directional swaps to real adjacent locations, transposed grid ordinals, house numbers lifted from distant real addresses, phonetic fake streets, cross-county ZIP splices), so a high specificity means plausible fakes are rejected rather than forced onto a point.

Evaluated queries are split into disjoint development and test partitions by a stable hash of the address id; the reference database is always the full corpus. Calibration constants and thresholds were fit on development only, and confidence intervals come from a county-stratified bootstrap of at least 10,000 resamples. Changes that improved only the synthetic harness, or one metric at another's significant expense, were rejected.

metric (held-out test, Balanced)estimate99% CI
sensitivity92.7%[92.3, 93.1]
specificity98.2%[97.9, 98.4]
precision (PPV)95.7%[95.4, 96.0]
accuracy94.9%[94.6, 95.1]

There is deliberately no "positional error" headline: because the reference coordinate is both what the matcher returns and what it is scored against, a correct match is 0 m by construction, so that number would restate precision rather than measure independent accuracy. The honest measures are the recovery and rejection rates above.

On real free-text input (rather than the clean reference), some addresses fall back to labeled centroids. A forensic review of that residual found it dominated not by matcher failures but by addresses genuinely absent from the reference data: non-standard grid and rural addressing, naming-convention mismatches, and house numbers outside any reference range. Spot-checked cases were absent from both UGRC and OpenStreetMap. The residual is therefore an authoritative-data-coverage limit, not a matching one, and it moves with the completeness of whatever address data you load.

These figures are internal validation against the Utah UGRC reference, the study area used for testing. The geocoder works with whatever authoritative address data you supply; its accuracy must be revalidated for any other jurisdiction's data before it can be assumed to transfer, and real-world coordinate accuracy against an independent gold standard has not been measured.

§ references

  1. Fellegi, I. P., & Sunter, A. B. (1969). A theory for record linkage. Journal of the American Statistical Association, 64(328), 1183-1210.
  2. Platt, J. C. (1999). Probabilistic outputs for support vector machines and comparisons to regularized likelihood methods. In Advances in Large Margin Classifiers (pp. 61-74). MIT Press.
  3. Damerau, F. J. (1964). A technique for computer detection and correction of spelling errors. Communications of the ACM, 7(3), 171-176.
  4. Levenshtein, V. I. (1966). Binary codes capable of correcting deletions, insertions, and reversals. Soviet Physics Doklady, 10(8), 707-710.
  5. Goldberg, D. W., Wilson, J. P., & Knoblock, C. A. (2007). From text to geographic coordinates: the current state of geocoding. URISA Journal, 19(1), 33-46.
  6. Zandbergen, P. A. (2008). A comparison of address point, parcel and street geocoding techniques. Computers, Environment and Urban Systems, 32(3), 214-232.
  7. United States Postal Service. Publication 28: Postal Addressing Standards.
  8. Utah Geospatial Resource Center (UGRC). Utah Statewide Address Points, Utah SGID. gis.utah.gov
← methods Gi* & Moran's I →