countrycheck
Offline country lookup.
On this page
Interactive demo
lon,lat[,name] per line (or a header naming
lat/lon columns in either order). GeoJSON: a
FeatureCollection of Points (Points mode) or a LineString (Route mode).
Files stay on your machine.no country: open ocean (confidence 1.0)
border cell (mixed)
answer changed by refinement
Load sample points or your own file (CSV lon,lat or GeoJSON points), and
every point is resolved to a country in your browser by the bundled library, with no
server and no network call per lookup. Each dot is coloured by the country it lands in; open
ocean stays grey. The lookups-per-second figure is measured tightly around the classification
loop on your machine (map rendering and file parsing excluded), so it is the real
library throughput. Use the 100k-random button for a stable number.
Switch to Route mode to classify a polyline — draw one on the map or pick an
example — and see the countries it crosses in order, each with its distance.
Click any classified point for its full answer:
country code (GADM gid_0), ISO 2, name, kind, confidence, area share and
cell address (computed on the fly for open-ocean points, whose cells are not stored).
Switching on exact border refinement makes the source polygons authoritative in every
cell a border crosses. Caveats inherited from the source data: coastal waters are an
approximate distance-based assignment, not legal EEZ; disputed territories follow GADM
(Crimea, Western Sahara…); lakes belong to their surrounding country, except the
Caspian Sea, which is its own XCA entry.
User guide
JavaScript (browser or Node)
From a checkout of this repository:
npm install ./countrycheck
import { CountryCheck } from "countrycheck";
// Node: bundled file · browser: fetch the 323 KB dataset
const cc = await CountryCheck.fromFile(); // Node
// Browser alternative: import the .mjs file directly, then
// const cc = await CountryCheck.fromUrl("countries_L10.tfcs");
cc.country(24.7536, 59.437); // 'EST' (lon, lat)
cc.check(-0.1276, 51.5072);
// { country: 'GBR', iso2: 'GB', name: 'United Kingdom',
// kind: 'country', confidence: 1, share: 1,
// cell: 'TFA95BM', refined: false }
// a whole route: countries crossed, in order
cc.checkPolyline([[13.4,52.5],[21.0,52.2],[25.3,54.7]]).segments;
// [{country:'DEU',...,distanceKm,fraction}, ...]
Python (stdlib only)
From a checkout of this repository:
pip install ./countrycheck
from countrycheck import CountryCheck
cc = CountryCheck() # bundled data
cc.country(24.7536, 59.4370) # 'EST'
cc.check(-0.1276, 51.5072)
# CountryResult(country='GBR', iso2='GB',
# name='United Kingdom', kind='country',
# confidence=1.0, share=1.0, cell='TFA95BM',
# refined=False)
# a whole route: countries crossed, in order
cc.check_polyline(
[(13.4, 52.5), (21.0, 52.2), (25.3, 54.7)]).segments
What the answer means
| kind | meaning | country | confidence |
|---|---|---|---|
| country | cell wholly inside one country | that country | 1.0 |
| none | cell absent from the dataset (international waters) | null | 1.0 |
| border | mixed cell; bundled best call decides (may be none) | best call | area share |
| border + refined | decided by the exact source polygon | exact | 0.99 |
Measured accuracy: 99.82% agreement with exact polygon containment on
30,000 uniform random points. The country and none answers were 100%
correct; all residual error lives in border answers, which self-report lower
confidence. With the border refinement loaded, agreement reaches 100.0% on the same
sample.
Command line
$ python countrycheck/python/countrycheck.py 24.7536 59.4370
EST iso2=EE name='Estonia' kind=country confidence=1.000 share=1.0 cell=TFAVKGR refined=False
The CLI loads the border refinement automatically when
borders_L10.tfcr is present. Install with pip install countrycheck or
npm install countrycheck, or run straight from a repo checkout.
Technical info
An offline lookup library built on the Trifold grid. The level-10 grid (~7 km cells) classified against country polygons with accurate OSM-derived borders — extended with coastal waters and including X-coded territories like Kosovo and the Caspian Sea — collapses into a 323 KB dataset that names the country anywhere on Earth in microseconds, with a confidence value for every answer. Python and JavaScript give identical results. This page runs the real JS library in your browser; the dataset is embedded right in this HTML file.
Any Trifold cell at level ≤ 10 maps to a
contiguous range in the level-10 index space (face·410 + path):
a level-l cell covers exactly 410−l consecutive indices. The whole country
classification becomes run-length intervals.
222,403 runs as
varint(gap), varint(len·2|border) — interior runs carry a country id,
border runs a best call plus a 4-bit area share — over a country table of 256
code/iso2/name strings, all zlib-compressed. Level-agnostic.
Pure-float point location (no dependencies, bit-identical to the SDK) descends 10 subdivision levels, then one binary search over the run starts. ~0.6 µs in Node, ~13 µs in pure Python, ~3 µs batched with numpy.
Source country polygons clipped to every border cell, quantized to a cell-local 16-bit grid (~0.1 m), one zone per country present with zigzag-varint rings and the even-odd rule. A point-in-polygon test then decides the exact country (or none) in those cells.
The borders come from the timezone-boundary-builder “with oceans”
polygons (OSM-derived, already reaching into territorial water); GADM level-0 supplies only the
country identity and ISO codes, joined by max land overlap. 256 countries and territories, 6.90M
level-10 cells belonging to some country, of which 195,473 are border cells. Full documentation,
the one-pass build.py, the PostGIS source build (sql/build_countries_coastal.sql)
and the cross-language test suite live in
countrycheck/ on GitHub.
Roadmap: an L12 (~1.8 km) variant and timezone detection from the same source data.
Accuracy: tested on 57,501 real airports
Ground truth is the OurAirports dump — 57,501 points, each tagged with an ISO country code from an unrelated source. countrycheck places 99.50% of them in the correct country from the bundled 323 KB data, and 99.68% with the border refinement loaded. Interior-country answers are 99.91% correct; the refinement works only on the 729 airports that fall in a border cell, and there it lifts agreement from 80.66% to 95.20% — the payoff of the accurate OSM-derived borders.
overall agreement with airport country codes, bundled vs. border-refined. The residual is mostly disputed/border territory the sources map differently, dependencies coded to a parent state, and offshore or placeholder coordinates.
against exact SQL point-in-polygon containment over the same source polygons (100,000 random points): the refinement resolved all but 5 of the 168 base-mode border disagreements (those 5 are coastal-overlap tie-breaks). Base mode: 99.83%.
Reproduce with scripts/accuracy_countrycheck_airports.py (airports)
and scripts/benchmark_countrycheck.py (vs. SQL containment).
Benchmark: 7–97× faster than SQL spatial engines
One workload, four engines: assign a country (gid_0) to 100,000 sphere-uniform
random points against the same country polygons. Median of seven warm runs, Apple M5 Pro,
June 2026. The refined Trifold answers reproduce exact polygon containment to 99.995% (see above)
while running an order of magnitude faster — ~7× PostGIS, ~97× DuckDB Spatial.
Called one point at a time, the gap holds.
Batch · 100,000 points per call
Singular · one point per call
Route (polyline) · per sampled point
DuckDB and PostGIS compute exact containment
and returned byte-identical answers; BigQuery is documented but was not run in this pass. PostGIS
singular includes localhost TCP + Docker transport; DuckDB runs embedded in-process. The
Route (polyline) row is per sampled point — a line query is one point-in-polygon per
sample for every engine; the live figure in the demo panel is the same per-sample throughput
measured on your own device. These bars are generated from the benchmark doc at build time.
Full methodology, dataset manifest, the airport test and the BigQuery procedure:
countrycheck_benchmark.md.
countrycheck · a Trifold T3 library · MIT license · source · borders from the OSM-based timezone-boundary-builder, identity from GADM