npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

offline-csv-geocoder

v1.0.0

Published

Offline, zero-dependency forward geocoder that appends Lat/Lon columns to CSVs by City/State/Country with US ZIP and Canadian postal-code precision. No API key.

Readme

offline-csv-geocoder

Offline forward geocoder (no API key, no rate limits, zero npm dependencies) that appends Lat and Lon columns to a CSV using the City, State, and Country columns — with US ZIP and Canadian postal-code precision when a Zip/Postal column is present. One file.

It fills a specific gap: the offline geocoders on npm are almost all reverse (coordinates → place), and the CSV batch tools are online-only and need an API key. This does forward, offline, CSV-native geocoding with postal precision.

npx offline-csv-geocoder input.csv                        # writes input_with_latlon.csv
npx offline-csv-geocoder input.csv -o out.csv             # explicit output file
npx offline-csv-geocoder "data/*.csv" -o results/         # glob in, output directory
npx offline-csv-geocoder --help

Or install it: npm i -g offline-csv-geocoder, then run offline-csv-geocoder ....

What it's good for

Batch-geocoding spreadsheets of organizations, grant recipients, mailing lists, survey responses — anything with city/state/country (and ideally a ZIP/postal) column — when you want coordinates without sending the data to a third-party API or paying per row. Great for air-gapped/offline environments and large one-off files.

Accuracy is centroid-level, not rooftop. A US ZIP resolves to the ZIP's centroid; a Canadian postal code resolves to its FSA (first three characters, a postal district); city matches resolve to the city centroid. That's ideal for mapping, regional aggregation, and radius queries down to a few miles — it is not street-address geocoding.

Lookup order (per row)

  1. Postal code — US ZIP or Canadian postal code, from any column whose header mentions "Zip" or "Postal" (so Updated Zip, Postal Code, etc. all work).
  2. City + State + Country — state may be a code (OH, BC) or full name (Ohio, British Columbia).
  3. City + Country.
  4. Country fallback — the capital's coordinates, else the most populous city.

Rows that match nothing get empty Lat/Lon and are listed (deduped) in the console summary.

Real-world-data handling baked in

  • US territories (Puerto Rico, US Virgin Islands, Guam, American Samoa, Northern Marianas) are labeled "United States" in most data but live in separate GeoNames tables — they're merged in, so San Juan, PR 00921 lands in the Caribbean, not San Juan, Texas.
  • Rural Canadian FSAs (x0x) cover whole regions, so for those an exact city+province match is preferred and the FSA centroid is the fallback. This also corrects same-named towns across provinces (Lethbridge AB vs NL).
  • Excel-stripped leading zeros — a ZIP that arrived as 4029 is re-padded to 04029.
  • ZIP+4 (37402-1234) and spaced/truncated Canadian codes (V9A 2R3, T0B 1G) are tolerated.
  • UTF-8 BOM on the header row is stripped.
  • Garbage postal values (NA, UK postcodes) fall through to the city/country path instead of failing the row.

Options

-o, --output <path>    Single input: output file. Multiple inputs: a directory.
    --no-overwrite     Skip any input whose output already exists.
    --city-col <h>     Override column detection. Each accepts a header name
    --state-col <h>    (case-insensitive) or a 1-based column index. Only a
    --country-col <h>  Country column OR a Zip/Postal column is required; the
    --zip-col <h>      rest are optional.
    --aliases <file>   JSON map of country name -> ISO2 for dataset-specific
                       spellings/typos (see below).
-h, --help

With multiple inputs, --output is treated as a directory. Without --output, each result is written next to its input as <name>_with_latlon.csv. Globs (*, ?) are expanded by the tool and never match *_with_latlon.csv, so re-running on a directory won't reprocess previous output.

Column detection

By default: City/State/Country are matched by those header names (case-insensitive), and the postal column is any header containing "zip" or "postal". If your headers differ, point at them explicitly:

offline-csv-geocoder --country-col Nation --city-col Municipality --zip-col "Post Code" input.csv
offline-csv-geocoder --country-col 2 --state-col 3 --city-col 4 --zip-col 5 input.csv   # by index

Custom country aliases

The tool ships legitimate country-name variants (USA, UK, Ivory Coast, etc.). For dataset-specific misspellings, pass a JSON map — it's merged on top of the built-ins and overrides them:

{ "United Sates": "US", "South Afria": "ZA" }
offline-csv-geocoder --aliases aliases.example.json input.csv

See aliases.example.json.

Data source & attribution

Uses the free GeoNames dump, licensed CC-BY 4.0. On first run it downloads ~12MB (cities500, admin1CodesASCII, countryInfo, and the US/PR/VI/GU/AS/MP/CA postal tables); after that everything runs fully offline.

Downloaded data is cached in ./data for a local checkout, or in $XDG_CACHE_HOME/offline-csv-geocoder (default ~/.cache/offline-csv-geocoder) for a global install. Override with $OFFLINE_CSV_GEOCODER_DATA_DIR. Delete the cache to force a refresh.

If you publish maps or data derived from these coordinates, you must attribute GeoNames (per CC-BY 4.0).

Requirements

Node 18+ (built-in fetch) and unzip on PATH (preinstalled on macOS/Linux).

License

Source code: MIT. GeoNames data: CC-BY 4.0 (see above).