country_code_locator

CI License: MIT

Offline WGS 84 coordinate-to-country-code lookup for Flutter. The package loads a bundled, versioned boundary index once and then returns a strict, officially assigned ISO 3166-1 Alpha-2 code synchronously—without network access, GPS, permissions, a platform geocoder, or a map-service SDK.

Features

  • Fully offline lookup after package installation.
  • Synchronous queries after one asynchronous initialization.
  • Pure Dart spatial grid, bounding-box, and point-in-polygon implementation.
  • Polygon, MultiPolygon, holes, islands, and antimeridian support.
  • Deterministic shared-boundary handling independent of source row order.
  • Strict official ISO Alpha-2 filtering; placeholders and XK never escape.
  • Validated binary data with format versioning, lengths, semantic checks, and CRC-32 integrity protection.
  • Reproducible, checksum-pinned Natural Earth data pipeline.

Install

flutter pub add country_code_locator

The boundary asset is declared by the package. Applications do not need to add it to their own pubspec.yaml.

Use

Load one locator during application startup and retain it for every query:

import 'package:country_code_locator/country_code_locator.dart';

final locator = await OfflineCountryCode.load();

final code = locator.lookup(
  latitude: 35.6812,
  longitude: 139.7671,
);

print(code); // JP

lookup returns null for ocean, uncovered or uncoded land, and a point that matches different codes on a shared boundary.

Input validation

Latitude must be finite and in [-90, 90]; longitude must be finite and in [-180, 180]. Invalid values throw ArgumentError. Longitudes -180 and 180 are treated as the same meridian.

Custom asset loading

Applications with their own loading layer can validate and initialize directly from bytes:

final locator = OfflineCountryCode.fromBytes(bytes);

fromBytes decodes all runtime structures immediately and does not retain or mutate the supplied Uint8List. OfflineCountryCode.load(bundle: customBundle) is also available for a custom Flutter AssetBundle.

Result policy

Situation Result
Point inside one officially coded land polygon That uppercase Alpha-2 code
Multiple matching polygons with the same code That code
Boundary shared by different codes null
Ocean or data gap null
Uncoded or non-official source region, including Kosovo/XK null
Invalid coordinate Throws ArgumentError

This package identifies land polygons only. It does not identify territorial waters, exclusive economic zones, addresses, administrative subdivisions, or legal sovereignty.

Data and accuracy

The bundled asset is generated from Natural Earth 5.1.1, 1:10m Admin 0 – Map Units. Natural Earth depicts boundaries according to its de facto policy; results do not express a legal position, sovereignty claim, or diplomatic recognition.

Coordinates are quantized to 1e-5 degree and no polygon simplification or island-area threshold is applied. Accuracy remains limited by the source map's scale and policy. See data provenance and regeneration and the binary format.

Natural Earth data is Public Domain. The library source is MIT licensed.

Performance

The asset is 2,570,639 bytes (2.45 MiB). A reference profile-mode run on a physical Pixel 10 measured 55.345 ms cold load, 8.875 µs average warm lookup, and a 13.50 MiB process-wide peak-RSS delta. These are measurements, not service level guarantees; application, device, build mode, and workload matter. Full methodology and reproduction guidance are in PERFORMANCE.md.

Runtime lookup consults a 2° spatial grid before polygon and ring bounds. It does not scan every global polygon, reparse the asset, or copy the geometry per query.

Example

example/lib/main.dart contains a Material 3 application with editable latitude and longitude fields. The package test suite also covers fixed real-world coordinates, holes, MultiPolygon members, antimeridian wrapping, ambiguity, invalid inputs, and corrupt assets.

Updating the data

The generator uses only the Python standard library:

python3 tool/generate_boundaries.py
python3 tool/generate_boundaries.py --check

The first command downloads the source only when the local cache is absent, verifies its SHA-256, regenerates the asset and sidecar metadata, and reports changes from the previous asset. The second regenerates in memory and fails if committed output differs. Review DATA.md before changing a pin.

Contributing and releasing

Libraries

country_code_locator
Offline WGS 84 coordinate to ISO 3166-1 Alpha-2 lookup.