Skip to content
Joel Natividad edited this page May 20, 2026 · 5 revisions

Geospatial

Tier: Intermediate Commands covered: geocode, geoconvert

Per-command flag reference lives in /docs/help/. This page is the workflow layer — when to reach for each command and how they compose.

Two commands, big footprint. geocode handles forward geocoding, reverse geocoding, city suggestions, IP geolocation, and country info — all against a local Geonames index plus an optional MaxMind GeoLite2 database. No network round-trip per row, 360,000 records / sec with caching and multithreading. It can also geocode online via the OpenCage API (the opencage / opencagenow subcommands) when you need true street-address geocoding that the local city index can't do.

geoconvert flips between CSV and spatial formats (GeoJSON, SHP, KML, GPX, …).

Quick decision table

If you want to… Use Notes
Find lat/lon from a city name geocode suggest Jaro-Winkler fuzzy matching
Find the nearest city to a lat/lon geocode reverse Local index; no API rate limits
Find country code from an IP geocode iplookup Needs MaxMind GeoLite2-City.mmdb
Enrich with US state/county FIPS codes geocode suggest / reverse with FIPS dyncols Census data prep
Geocode a full street address (online) geocode opencage OpenCage API; needs an API key
Reverse-geocode a coordinate to a full address (online) geocode opencage Same subcommand; mode auto-detected
CSV → GeoJSON for QGIS / web maps geoconvert Specify --geometry for WKT or lat/lon
GeoJSON / SHP → CSV geoconvert Drops to a flat tabular form

geocode

Nine subcommands; the big four are suggest, suggestnow, reverse, reversenow. Plus iplookup, countryinfo, countryinfonow, the online opencage / opencagenow subcommands, and the geocode index-* subcommands for managing the local Geonames cities index.

Setting up the index

# Downloads the prebuilt Geonames cities15000 index (~26k cities, pop > 15k)
qsv geocode suggest --help    # first run triggers the download

# Manage the local index
qsv geocode index-load <geonames-zip>      # use a different city set
qsv geocode index-update                   # refresh from Geonames
qsv geocode index-info                     # show metadata

For IP-based lookups, manually download GeoLite2-City.mmdb from your MaxMind account (free) and copy it to ~/.qsv-cache/ (or set QSV_GEOIP2_FILENAME).

Forward geocoding (suggest)

Example: enrich a list of city names with coordinates

# input has a "city" column with values like "Brooklyn", "san jose", "PARIS"
qsv geocode suggest city \
  --new-column 'lat,lon' \
  --formatstr "%latitude,%longitude" \
  cities.csv > cities_with_coords.csv

Example: US-only suggestion that also pulls FIPS codes (for Census joins)

qsv geocode suggest city_col --country US -f \
  "%dyncols: {geocoded_city:name},{state:admin1},{county:admin2},\
{state_fips:us_state_fips_code},{county_fips:us_county_fips_code}" \
  voters.csv -o voters_with_fips.csv

The %dyncols: directive expands to multiple new columns named on the left of each : pair.

Reverse geocoding (reverse)

Example: reverse-geocode NYC 311 lat/lon to Borough / NTA

qsv geocode reverse 'Location' \
  --new-column 'nearest_city,admin1,admin2' \
  --formatstr "%name,%admin1,%admin2" \
  --country US \
  NYC_311_SR_2010-2020-sample-1M.csv > nyc311_reversed.csv

Location is the source column in "lat, lon" or "(lat, lon)" format.

Example: reverse-geocode FIPS codes for downstream Census tract joins

qsv geocode reverse coordinate_col --country US -f \
  "%dyncols: {city:name},{state:admin1},{county:admin2},\
{state_fips:us_state_fips_code},{county_fips:us_county_fips_code}" \
  events.csv -o events_with_fips.csv

One-shot variants (suggestnow, reversenow, countryinfonow)

For ad-hoc one-off lookups from the command line (not a CSV):

qsv geocode suggestnow "Brooklyn"
qsv geocode reversenow "40.6782, -73.9442"
qsv geocode countryinfonow US

IP lookup (iplookup)

Needs the MaxMind GeoLite2-City.mmdb.

qsv geocode iplookup --new-column country ip_col logs.csv > logs_with_country.csv

Country info (countryinfo)

qsv geocode countryinfo country_code visitors.csv > with_country_info.csv
# Adds columns: name, capital, area, population, languages, currency, ...

Online geocoding with OpenCage (opencage, opencagenow)

suggest / reverse are city-level and offline — fast, but they can't resolve a full street address. When you need real address geocoding, opencage calls the online OpenCage API. One subcommand does forward (address → coordinates) and reverse (coordinates → address) geocoding — the mode is auto-detected per row (pass --reverse to force reverse).

Requires an OpenCage API key — set it with --api-key or the QSV_OPENCAGE_API_KEY environment variable. A free key allows 2,500 requests/day: https://opencagedata.com/users/sign_up.

# forward-geocode a column of street addresses to coordinates
qsv geocode opencage address --new-column coordinates -f %location addresses.csv

# reverse-geocode "(lat, lon)" coordinates to a full formatted address (the default format)
qsv geocode opencage Location --reverse --new-column address events.csv

# pull individual OpenCage components with dotted dynamic formatting
qsv geocode opencage address -f '{components.city}, {components.country}' addresses.csv

# one-off lookups from the shell
qsv geocode opencagenow "1600 Pennsylvania Ave NW, Washington DC"
qsv geocode opencagenow "40.71427, -74.00597"

OpenCage's terms of service permit caching, so results are kept in a persistent on-disk cache — re-runs and duplicate queries never re-hit the API (--cache-ttl sets the TTL; --no-cache disables it). Rows are processed sequentially behind a rate limiter (--rate-limit, default 1 request/sec — the free-tier limit), so reach for opencage for enrichment quality and for the offline subcommands for raw throughput.

OpenCage --formatstr options: %formatted (default — the full formatted address), %lat-long, %location, %city, %state, %county, %country, %country_name, %postcode, %confidence, %json, %pretty-json; plus dotted dynamic keys {components.<name>} and {annotations.<dotted.path>}.

See also: /docs/help/geocode.md, Geonames, MaxMind GeoLite2, OpenCage API, Recipe: Geographic Enrichment, Lookup Tables — for fallback when geocode misses.

geoconvert

Convert between CSV/SVG and spatial formats. Available formats include geojson, shp (Shapefile), kml, gpx, geojsonl, and more — check qsv geoconvert --help for the full list in your build.

Example: GeoJSON → CSV (for SQL analysis)

qsv geoconvert nta_boundaries.geojson geojson csv > nta.csv
qsv sqlp nta.csv "SELECT borough, COUNT(*) FROM nta GROUP BY borough"

Example: CSV with WKT geometry → GeoJSON for a web map

qsv geoconvert parcels.csv csv geojson --geometry geometry > parcels.geojson

Example: SHP → CSV (drops to tabular form)

qsv geoconvert nyc_boroughs.shp shp csv > nyc_boroughs.csv

Example: pick a file via dialog, then convert

qsv prompt -m 'Choose a GeoJSON file' -F geojson \
  | qsv geoconvert - geojson csv > result.csv

Example: combine geocode + geoconvert to enrich and map

# 1. Reverse-geocode NYC 311 to get the borough column
qsv geocode reverse 'Location' --new-column 'borough' \
  --formatstr "%admin2" --country US nyc311.csv > step1.csv

# 2. Bulk-convert lat/lon pairs into WKT, then to GeoJSON
qsv apply operations wkt_point Latitude Longitude \
  --new-column geometry step1.csv > step2.csv
qsv geoconvert step2.csv csv geojson --geometry geometry > nyc311.geojson

See also: /docs/help/geoconvert.md, geocode, Conversion & I/O.

See also

Clone this wiki locally