The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Location geocoding workflow

locatr turns a messy address table into an audit-ready crosswalk. The chunks below are not evaluated because they need live geocoding services, but they show the intended end-to-end run. The package assumes you already have a data frame; it does not connect to databases or source systems.

locatr does not replace tidygeocoder; it builds on it. tidygeocoder is the geocoding engine for the main batch passes. locatr adds the workflow layer: address cleaning, bad-address flagging, region validation, tier-by-tier audit columns, local geography joins, review exports, manual overrides, and final crosswalk output. If your addresses are already clean and you only need one service’s coordinates, call tidygeocoder::geo() or tidygeocoder::geocode() directly. Use locatr when the coordinates need to be defensible and reusable.

1. Pull and clean

library(locatr)

cleaned <- records %>%
  clean_addresses(
    id = `Location ID`, address = Address,
    city = City, zip = Zip, name = `Location Name`
  ) %>%
  flag_bad_addresses()

clean_addresses() adds *_clean columns and a full_address_clean string; flag_bad_addresses() sends PO boxes and placeholders straight to review. Only address and city are required. If your file has no ID, locatr generates row-number IDs; if it has no ZIP, zip_clean stays NA and the single-line address omits the ZIP instead of ending in NA:

cleaned_minimal <- records %>%
  clean_addresses(address = Address, city = City, state = "NJ") %>%
  flag_bad_addresses()

Missing ZIP is recorded as bad_address_flag == "missing_zip" for audit, but it does not block geocoding when address + city + state are present.

For one-off review, use geocode_address() to see ranked ArcGIS candidates for a single address:

if (interactive()) {
  geocode_address("1600 Pennsylvania Ave NW", city = "Washington", state = "DC")
}

2. Geocode with a guarded cascade

geocoded <- geocode_records(cleaned)   # Census -> ArcGIS -> name lookup

geocode_records() runs each tier in turn and validates against the configured region after each pass, so a later, fuzzier service only retries what is still unplaced. The geocode_pass column records which tier placed each row.

3. Geography join

with_geography <- add_county_muni(geocoded, state = "NJ")

The geography step is independent of the input address columns once coordinates exist. add_county_muni() builds Census TIGER/Line geography and attaches county/locality fields. Pass an sf boundary layer to adapt the geography join, e.g. add_muni_from_shapes(geocoded, muni_shapes = my_local_shapes), or use add_muni_from_key() when your records and geography share a code column.

4. Review, override, export

write_geocode_review(with_geography, "manual_review.csv")

final <- with_geography %>%
  apply_manual_overrides("manual_review_completed.csv") %>%
  export_location_crosswalk("location_crosswalk.csv")

final is ready for Tableau, GIS joins, or a reusable reference table, and every row carries the audit columns that explain how its coordinate was produced.

5. Reproducible runs and provenance

Because the cascade calls external services, reuse a locatr_cache() to make a run reproducible and cheap to repeat:

cache <- locatr_cache("geocode_cache.rds")  # omit the path for a memory-only cache

geocoded <- geocode_records(cleaned, cache = cache)

# a repeat run replays cached coordinates instead of re-querying
geocoded_again <- geocode_records(cleaned, cache = cache)

cache_info(cache)

The cache is keyed by the exact query and request parameters and stores one row per candidate result (with a no-match sentinel so misses replay too). Pass refresh = TRUE to re-query and overwrite. Nothing is written to disk unless you give locatr_cache() a path.

Every geocode_records() result also carries a run manifest and two per-row provenance columns:

geocode_provenance(geocoded)

geocoded[, c("record_id", "placed_at", "cache_status")]

cache_status is fresh, cached, reference, manual, or unplaced, and placed_at is when the coordinate actually entered the output (the cached timestamp for cached rows, not the current run). Both columns are carried into export_location_crosswalk(). The manifest is attached as an attribute, so read it with geocode_provenance() right after the run, before any later data-frame operation that might drop attributes.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.