Model / geo · All operations
Geocode addresses
A variant of Work with geography
Turn street addresses or postcodes into coordinates, or coordinates back into an address.
Status
- In arcform
Runs as a SQL step
arcform carries it as a
sql:step today, with thespatialextension.
The DuckDB step
Turn street addresses and postcodes into coordinates, and coordinates back into an address
INSTALL spatial; LOAD spatial; SET geometry_always_xy = true;
-- the address index is the reader's to supply: one row per address, the way an offline geocoder holds one (a country-wide index is the same table at scale)
CREATE TEMP TABLE addresses AS SELECT * FROM (VALUES
(1, '10', 'Downing Street', 'London', 'SW1A 2AA', -0.1276, 51.5034), (2, '11', 'Downing Street', 'London', 'SW1A 2AB', -0.1277, 51.5035), (5, '12', 'Downing Street', 'London', 'SW1A 2AB', -0.1279, 51.5036),
(3, '221B', 'Baker Street', 'London', 'NW1 6XE', -0.1585, 51.5237), (4, '1', 'Abbey Road', 'London', 'NW8 9AY', -0.1778, 51.5320)) t(id, number, street, city, postcode, lon, lat);
-- the text of an address in one form: lower case, no accents or punctuation, one space, and 'st' written out
CREATE MACRO norm(a) AS regexp_replace(regexp_replace(lower(strip_accents(trim(a))), '[.,]', ' ', 'g'), '\s+', ' ', 'g');
CREATE MACRO expand(a) AS trim(regexp_replace(' ' || norm(a) || ' ', ' st ', ' street ', 'g'));
CREATE TEMP TABLE indexed AS SELECT *, expand(number || ' ' || street || ' ' || city) AS full_text, norm(postcode) AS pc FROM addresses;
CREATE TEMP TABLE asked AS SELECT * FROM (VALUES (1, '10 downing street, london'), (2, '221b Baker St., London'), (3, 'SW1A 2AB'), (4, 'NW8'), (5, '99 Nowhere Lane')) t(q, text);
-- forward: an exact address match, else a full postcode, else the centre of the addresses under a postcode outward code.
-- Each branch is grouped to one row per key first (a postcode holds many addresses, and an address text may be listed twice), so a query never doubles.
CREATE TEMP TABLE by_address AS SELECT full_text AS k, avg(lon) AS lon, avg(lat) AS lat FROM indexed GROUP BY full_text;
CREATE TEMP TABLE by_postcode AS SELECT pc AS k, avg(lon) AS lon, avg(lat) AS lat FROM indexed GROUP BY pc;
CREATE TEMP TABLE by_outward AS SELECT split_part(pc, ' ', 1) AS k, avg(lon) AS lon, avg(lat) AS lat FROM indexed GROUP BY 1;
CREATE TEMP TABLE forward AS
SELECT a.q, a.text, coalesce(m.lon, p.lon, o.lon) AS lon, coalesce(m.lat, p.lat, o.lat) AS lat,
CASE WHEN m.k IS NOT NULL THEN 'address' WHEN p.k IS NOT NULL THEN 'postcode' WHEN o.k IS NOT NULL THEN 'outward code' END AS matched_on
FROM asked a
LEFT JOIN by_address m ON m.k = expand(a.text)
LEFT JOIN by_postcode p ON p.k = norm(a.text)
LEFT JOIN by_outward o ON o.k = norm(a.text);
-- backward: the nearest indexed address, and only when it is within 100 m
CREATE TEMP TABLE points AS SELECT * FROM (VALUES (1, -0.12771, 51.50351), (2, -0.1780, 51.5319), (3, -0.5, 51.0)) t(q, lon, lat);
CREATE TEMP TABLE backward AS
SELECT q, id AS address_id, round(metres, 1) AS metres FROM (
SELECT p.q, a.id, ST_Distance_Sphere(ST_Point(p.lon, p.lat), ST_Point(a.lon, a.lat)) AS metres, row_number() OVER (PARTITION BY p.q ORDER BY ST_Distance_Sphere(ST_Point(p.lon, p.lat), ST_Point(a.lon, a.lat))) AS rk
FROM points p CROSS JOIN addresses a) WHERE rk = 1 AND metres <= 100;
SELECT * FROM forward ORDER BY q;
SELECT p.q, b.address_id, b.metres FROM points p LEFT JOIN backward b USING (q) ORDER BY p.q;
SELECT CASE WHEN ((SELECT count(*) = 5 AND count(DISTINCT q) = 5 FROM forward)
AND (SELECT list([round(lon, 6), round(lat, 6)] ORDER BY q) = [[-0.1276, 51.5034], [-0.1585, 51.5237], [-0.1278, 51.50355], [-0.1778, 51.532], [NULL, NULL]] AND list(matched_on ORDER BY q) = ['address', 'address', 'postcode', 'outward code', NULL] FROM forward)
AND (SELECT count(*) = 2 AND list(address_id ORDER BY q) = [2, 4] FROM backward)
AND (SELECT metres < 5 FROM backward WHERE q = 1)) THEN 'as expected' ELSE error('the five queries should give one row each, the four known texts should resolve to the coordinates of their addresses, postcode and outward code, the unknown street should not, and the first two points should resolve to addresses 2 and 4 while the far point resolves to none') END AS verdict;- The statement builds its own data.
- It needs the
spatialextension. - The statement checks its own result and raises an error when it is wrong: “the five queries should give one row each, the four known texts should resolve to the coordinates of their addresses, postcode and outward code, the unknown street should not, and the first two points should resolve to addresses 2 and 4 while the far point resolves to none”.
- It passed on DuckDB
v2.0.0 (Cyanoptera) d54cbbe3df.
What other tools call it
Meridian's classification; an independent blind rating agreed on 95.7% of 324 rows (variant, version 3).
Alteryx Designer (4)
Reverse Geocoderread 2026-09-27Turn latitude and longitude into an address through the TomTom reverse geocoding API
Street Geocoderread 2026-09-27Geocode a multi-line address to a point by address interpolation
US Geocoderread 2026-09-27Geocode a US customer file by chaining CASS, Street Geocoder and ZIP9 Coder
US ZIP9 Coderread 2026-09-27Append latitude, longitude and census geography for US ZIP+4 codes
Talend (3)
tGoogleAddressRowread 2026-09-27Geocode addresses to coordinates and other geographic details through the Google Places API
tGoogleGeocoderread 2026-09-27Geocode addresses to coordinates through the Google Places API
tGoogleMapLookupread 2026-09-27Look up detailed geographic information for coordinates through the Google Places API
VisiData (1)
addcol-geocoderead 2026-09-23(plugin) Geocode current column
Background
Address geocoding takes a text description of a location, such as an address or place name, and returns coordinates, typically a latitude and longitude.[1] Reverse geocoding converts a location described by geographic coordinates into a human-readable address or place name.[2] Geocoding relies on a computer representation of address points, the street or road network, and postal and administrative boundaries.[1]
When to use it
The coordinates a geocoder returns vary greatly in positional accuracy, from building and land parcel centroids to interpolated street positions and postal code centroids.[1] Keep the match level a geocoder reports beside each pair of coordinates, so that a postcode centroid is never read as a front door. (Meridian's view) To achieve the greatest accuracy, input addresses commonly go through data cleansing first, often called address scrubbing, to find and correct errors.[1]
Interpolated geocoding assumes that parcels are evenly spread along a street segment, which is almost never true, and a geocoded address off by several thousand feet is not uncommon.[1] With the geocoding process used for the Topologically Integrated Geographic Encoding and Referencing (TIGER) datasets of the U.S. census, 5–7.5% of addresses may be allocated to a different census tract.[1]
In Alteryx Designer, the Street Geocoder tool can return every candidate for a multiple match, so the number of records coming out may differ from the number going in.[3] Where a geocoder can return several candidates, decide whether you want one row per address or one per candidate before joining the result back. (Meridian's view) Points extracted from published maps can be reverse geocoded to obtain the street address of an individual, which raises privacy concerns.[2]
The method
A geocoder matches each input to a reference dataset that holds the locations of geographic features, such as buildings, streets or counties, in a common spatial reference system.[1] A direct match expects each input to correspond to a single whole feature in the reference dataset, much like a relational table join, but usually with handling for approximate matches.[1]
In 1967 a Census Bureau team including James Corbett and Donald Cooke invented Dual Independent Map Encoding (DIME), which encoded address ranges into street network files.[1] Reverse geocoding can interpolate a street address from the range assigned to the nearest road segment, which returns an estimate of what should be there rather than an actual address.[2]
Sources
- [1]Address geocoding - Wikipediasecondary source
- [2]Reverse geocoding - Wikipediasecondary source
- [3]Street Geocoder Tooldocumentation