@react-map-input/geopack
v0.1.0
Published
Build-time toolkit that turns administrative-boundary GeoJSON into a pre-projected geopack for react-map-input.
Readme
@react-map-input/geopack
Build-time toolkit that turns administrative-boundary GeoJSON into a pre-projected
react-map-input pack.
This is a development dependency. Nothing here runs in the browser — that is the point: all projection and simplification happens here so the runtime ships with zero dependencies.
There is deliberately no one-shot CLI. Every country's canonical attribute data — names, parent
links, expected counts — has a different shape, and a pack is only trustworthy if it is validated
against that data. So this package exposes the pipeline as composable steps; the reference
implementation is packages/data/tr/scripts/build.ts, which is
short enough to copy and adapt.
Pipeline
- Assign each subregion polygon to a region by interior point (
assign.ts). - Match those polygons to your canonical region/subregion list (
match.ts). - Simplify topology-aware with mapshaper, then dissolve subregions into regions
(
simplify.ts), so a region border is exactly the union of its subregions'. - Project once into a shared coordinate space and emit rounded SVG path data (
project.ts). - Validate against the canonical source and emit (
validate.ts,emit.ts).
Two things that will bite you
Ring winding. d3-geo wants clockwise exterior rings — the opposite of RFC 7946, which most
published GeoJSON follows. Given the wrong winding it reads each polygon as its own complement,
silently emitting a second enormous subpath and destroying any fitExtent built on the resulting
bounds. Nothing errors; the map just collapses into a corner. Always rewindGeometry() before
projecting. validatePack() asserts the regions actually fill their canvas so this cannot ship.
Interior points, not centroids. A concave coastal shape can put its area centroid out at sea,
where it belongs to no region and the nearest-neighbour fallback silently picks the wrong one.
interiorPoint() falls back to a scanline midpoint. In Türkiye this is the difference between
Yumurtalık landing in Adana, where it belongs, and in Hatay.
Matching
Source names rarely match canonical ones exactly. Rules run in descending order of confidence —
override, exact, central-alias, strip-central-suffix, fuzzy, sole-remaining — and each
match records the rule that produced it, so anything past a plain exact hit can be reviewed rather
than trusted. Against Türkiye: 919 exact, 50 central-alias (OSM writes Çanakkale Merkez where the
canonical data says just Merkez), and one override for Gökçeada, which OSM lists under its Greek
exonym Imbros.
Fuzzy matching only fires when a single candidate is strictly closest, so near-ties fall through to be resolved by hand instead of being guessed.
Preview
preview.mjs rasterises a built pack as terminal ASCII — a fast way to confirm a projection is
sane without opening a browser.
Installing
npm install --save-dev @react-map-input/geopackShips ESM and CommonJS builds with types, so it runs under plain Node — no loader required.
Licence
MIT.
