distance-calculation-cli
Compute drive distance and drive time between US addresses, one pair at a time or in bulk from a CSV of origin/destination pairs.
- Geocoding: US Census Geocoder (free, keyless, TIGER-based), with OpenStreetMap Nominatim as a fallback for addresses Census can't match.
- Routing: OSRM — the public demo server by default (politely throttled), or a self-hosted local containerized OSRM instance for large batch jobs.
Quickstart
uvx distance-calculation-cli "1 Campus Martius, Detroit, MI" "300 Ottawa Ave NW, Grand Rapids, MI"
Routing via: https://router.project-osrm.org (public demo server, throttled)
Origin matched: 1 CAMPUS MARTIUS, DETROIT, MI, 48226
Destination matched: 300 OTTAWA AVE NW, GRAND RAPIDS, MI, 49503
Distance: 157.5 miles
Drive time: 2h 56m
Batch mode:
uvx distance-calculation-cli --csv addresses.csv -o results.csv
Installation
uv tool install distance-calculation-cli
# or, one-off:
uvx distance-calculation-cli ...
CSV format
Input CSV must have (at minimum) an origin and destination address column,
named origin_address / destination_address by default (override with
--origin-col / --destination-col):
| origin_address | destination_address |
|---|---|
| 1 Campus Martius, Detroit, MI | 300 Ottawa Ave NW, Grand Rapids, MI |
| 2 Woodward Ave, Detroit, MI | 124 W Michigan Ave, Lansing, MI |
Any other input columns are preserved as-is. The output CSV adds:
| column | meaning |
|---|---|
matched_origin_address |
Address as matched by the geocoder |
origin_lat, origin_lon |
Geocoded origin coordinates |
matched_destination_address |
Address as matched by the geocoder |
destination_lat, destination_lon |
Geocoded destination coordinates |
distance_miles |
Drive distance |
drive_time_minutes |
Drive time |
status |
See status reference below |
error_detail |
Human-readable failure reason, if any |
routing_server |
OSRM server used for this row |
Rows are written incrementally as they complete (crash-safe, bounded memory), with a progress bar when running in a terminal. Failed rows are flagged, never dropped.
Status reference
| status | meaning |
|---|---|
ok |
Geocoded and routed successfully |
geocode_failed_origin |
Could not geocode the origin address |
geocode_failed_destination |
Could not geocode the destination address |
geocode_failed_both |
Could not geocode either address |
route_failed |
Both addresses geocoded, but OSRM found no route |
invalid_input_row |
Row was missing an origin or destination value |
Options
| option | env var | default | meaning |
|---|---|---|---|
--osrm-url |
DISTANCE_CLI_OSRM_URL |
(auto-resolved) | OSRM server to route against |
--osrm-rps |
(server-dependent) | Override the OSRM requests/second cap | |
--geocoder-url |
DISTANCE_CLI_GEOCODER_URL |
(auto-resolved) | Self-hosted Nominatim to use as the primary geocoder |
--no-throttle |
off | Disable OSRM rate limiting entirely | |
--concurrency |
20 | Max concurrent geocode/route requests | |
--origin-col / --destination-col |
origin_address / destination_address |
CSV column names | |
--units |
miles |
miles or km |
|
--no-fallback |
off | Disable the Nominatim fallback geocoder | |
--nominatim-user-agent |
DISTANCE_CLI_NOMINATIM_UA |
Required to enable Nominatim fallback | |
--retries |
3 | Retry attempts for transient errors | |
--timeout |
15s | Per-request timeout | |
--cache-dir |
Enable a persistent SQLite geocode cache (90-day TTL) | ||
--dry-run |
off | Parse + dedupe the input only; no network calls | |
--resume |
off | Skip rows already ok in an existing output file |
|
--public |
off | Force the public OSRM server even if local OSRM is running | |
--no-probe |
off | Skip probing for local OSRM/Nominatim instances | |
--yes |
off | Skip confirmation prompts (for scripts) |
Routing server resolution order
--osrm-urlflagDISTANCE_CLI_OSRM_URLenvironment variable- Probe
http://localhost:5000for a running local OSRM instance - Fall back to the public
router.project-osrm.orgdemo server
A loopback server is used unthrottled; the public server is capped at 1
request/second; any other custom URL is throttled by default (use
--no-throttle if you know it can take more). The CLI prints which server
it's using at startup, e.g. Routing via: http://localhost:5000 (local OSRM, unthrottled).
Agent skill (Claude Code)
To teach a coding agent how to drive this CLI — command reference, server resolution behavior, resource requirements, and common failure modes — install the bundled skill:
distance-calculation-cli skill install # ~/.claude/skills (all sessions)
distance-calculation-cli skill install --project # ./.claude/skills (this repo only)
distance-calculation-cli skill show # preview the content
distance-calculation-cli skill uninstall
New Claude Code sessions pick the skill up automatically; use --force to
overwrite after upgrading the CLI.
Rate-limit etiquette
The Census geocoder and the public OSRM demo server are free, shared, keyless
services — please be a good citizen. For a few one-off lookups the defaults
are fine. For tens of thousands of pairs, run a local OSRM instance (see
below) and consider a --cache-dir for geocoding.
Nominatim's usage policy requires a descriptive User-Agent identifying your
application; without one (--nominatim-user-agent or
DISTANCE_CLI_NOMINATIM_UA), the Nominatim fallback is disabled and a warning
is printed — Census-only results still work, just with a lower match rate.
Local OSRM (for large batches)
For workloads of tens of thousands of pairs, run OSRM locally instead of hammering the public server:
distance-calculation-cli osrm setup us/michigan # download + process (one-time)
distance-calculation-cli osrm start us/michigan # runs osrm-routed in a container
distance-calculation-cli "..." "..." # auto-detects localhost:5000
distance-calculation-cli osrm status
distance-calculation-cli osrm stop
REGION is a Geofabrik
path fragment, e.g. us/michigan. Michigan is a good first example — small,
fast to process. us-latest (the whole US) needs 64GB+ of RAM to process and
requires --yes to proceed. Requires a container engine — Podman is preferred
if installed, otherwise Docker (override with --engine or
DISTANCE_CLI_CONTAINER_ENGINE). Data is stored under
~/.local/share/distance-calculation-cli/osrm/.
Once a local OSRM instance is running, it's auto-detected via a health probe
of localhost:5000 — no extra flags needed.
Local geocoding (fully offline batches)
Routing locally removes one bottleneck, but geocoding still runs at 1–5 requests/second against shared public services. To go fully local, run a self-hosted Nominatim for the same region:
distance-calculation-cli geocoder start us/michigan # first start imports the data
distance-calculation-cli "..." "..." # auto-detects localhost:8080
distance-calculation-cli geocoder status
distance-calculation-cli geocoder stop # imported data is kept
distance-calculation-cli geocoder purge us/michigan # delete the imported data
The region's .osm.pbf download is shared with osrm setup. On the first
start, Nominatim imports the extract into PostgreSQL inside the container —
for a state-sized region this takes 15–60+ minutes, needs 8GB+ of RAM, and
uses ~10–20GB of disk (kept in a named container volume so later starts are
fast). Progress and the current import stage are shown while it runs.
When a local Nominatim is running (or --geocoder-url /
DISTANCE_CLI_GEOCODER_URL points at one), it becomes the primary
geocoder, unthrottled, with the Census geocoder as fallback — no User-Agent
required. Resolution order mirrors routing: flag → env var → probe
localhost:8080 → default Census-first behavior.
Note that OSM address coverage is thinner than the Census TIGER data in some US areas; keeping the Census fallback (the default) gives the best match rate.
Geocode-only mode
To resolve addresses to coordinates without routing:
distance-calculation-cli geocode "800 W Main St, Midland, MI"
distance-calculation-cli geocode "800 W Main St, Midland, MI" --json
distance-calculation-cli geocode --csv addresses.csv -o geocoded.csv
Batch mode reads an address column (override with --address-col),
dedupes, and writes the input columns plus status, matched_address,
lat, lon, and source. It uses the same geocoder resolution as the
distance command (local Nominatim auto-detected, Census otherwise, same
--cache-dir cache) and exits nonzero if any address failed.
Metadata
Release files for distance-calculation-cli 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| distance_calculation_cli-0.5.0.tar.gz | 27.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| distance_calculation_cli-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 63.2 kB
Release files / distance_calculation_cli-0.5.0.tar.gz
| Download URL | distance_calculation_cli-0.5.0.tar.gz |
|---|---|
| Size | 27.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
53a39c5bf918b8338929660ae792cd4bd857db81ca310b78e3aea7d3ef0cf9c1
|
|
BLAKE2b-256 checksum How to use checksums |
d65a34b520058495ac91bfed0cdc684f050f145614f5313e3208e5889f296629
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / distance_calculation_cli-0.5.0-py3-none-any.whl
| Download URL | distance_calculation_cli-0.5.0-py3-none-any.whl |
|---|---|
| Size | 35.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c3fd52cc5eea304f769be6b3028641a6cca93c0c245bc60451d8bf586e1c15f
|
|
BLAKE2b-256 checksum How to use checksums |
659d74ad74f0b5d74624d51c8dd65fca16d9c4b8a06756bc9f66f85a6a3a42b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|