slim-natural-earth
v1.0.0
Published
Strip Natural Earth admin-0 GeoJSON down to a small property set, merge in named subunits, generate polygon labels, and emit gzip/brotli copies.
Maintainers
Readme
slim-natural-earth
Strips Natural Earth admin-0 (country / map-subunit) GeoJSON down to a small, web-map-friendly property set, optionally merges named subunits into the country layer, generates polygon-label point features, and emits gzip + brotli copies — all from one CLI.
npx slim-natural-earth build --version-tag 5.1.2 --property-set micro
npx slim-natural-earth --helpOr install it: npm i -g slim-natural-earth, then run
slim-natural-earth ....
Hardened: 2026-10-30 · d1d82c5 · gate CLOSED WITH FOLLOW-UPS
What it’s good for
Natural Earth’s admin-0 country and map-subunits GeoJSON files ship with
50+ cartographic/demographic properties per feature — more than most web
maps need, and enough to noticeably bloat a bundle. This tool strips each
feature down to a named property set (min or micro, or your own custom
list), optionally folds a handful of named subunits (dependent territories,
constituent countries, etc.) into the country layer so they render
separately from their parent, generates a label point for each polygon via
geojson-polygon-labels,
and writes gzip/brotli copies of the results using Node’s built-in zlib
(no external compression CLI).
Getting the data
This tool operates on Natural Earth’s admin-0 GeoJSON releases — it does not download or bundle them. Grab the files you need manually:
Go to naturalearthdata.com and pick a scale (
110m,50m, or10m—50mis a common web-map choice). Under Cultural vectors, download:- Admin 0 – Countries (
ne_<scale>_admin_0_countries.geojson) - Admin 0 – Map subunits (
ne_<scale>_admin_0_map_subunits.geojson), only needed if you plan to usemerge/--merge-subunits
Direct download pages:
- https://www.naturalearthdata.com/downloads/50m-cultural-vectors/50m-admin-0-countries/
- https://www.naturalearthdata.com/downloads/50m-cultural-vectors/50m-admin-0-details/
Or pull GeoJSON straight from the mirrored source repo: https://github.com/nvkelso/natural-earth-vector/tree/master/geojson
- Admin 0 – Countries (
Rename the downloaded files to include a version tag and place them in an input directory (
./libby default), matching the pattern this tool expects:lib/ne_50m_admin_0_countries-5.1.2.geojson lib/ne_50m_admin_0_map_subunits-5.1.2.geojsonThe version tag (
5.1.2above) can be anything you want — it’s just carried through into the output filenames via--version-tagso you can tell releases apart. Natural Earth doesn’t stamp a version into its own filenames, so this is a convention this tool introduces, not one it reads from the download.Run
slim-natural-earth build(see Usage below) pointing--input-dirat that directory.
Natural Earth data is public domain; see their terms of use (no attribution required).
Usage
slim-natural-earth has five subcommands. build runs the common pipeline
end to end; the other four are the individual steps, usable on their own.
build — the full pipeline
slim-natural-earth build \
--input-dir ./lib \
--output-dir ./dist \
--version-tag 5.1.2 \
--property-set micro \
--merge-subunits \
--subunit-names "Greenland,Puerto Rico"--input-dir <dir> Directory containing the raw Natural Earth files
(default: ./lib)
--output-dir <dir> Directory to write results into (default: ./dist)
--prefix <string> Filename prefix shared by the input layers
(default: ne_50m_admin_0_)
--version-tag <string> Version tag embedded in filenames (required)
--countries-name <string> Countries layer basename (default: countries)
--subunits-name <string> Subunits layer basename (default: map_subunits)
--property-set, -p "min", "micro", or a comma-separated custom list
of field names (default: min)
--merge-subunits Merge named subunits into the country layer
--subunit-names <list> Comma-separated subunit "name" values to merge in
(required with --merge-subunits)
--labels Generate a polygon-labels file (default: true;
--no-labels to skip)
--label-method <string> geojson-polygon-labels placement method:
polylabel, center, center-of-mass, center-mean,
center-median, centroid, point-in-polygon
(default: polylabel)
--label-collections geojson-polygon-labels Multi*/GeometryCollection
handling: largest, explode, combine
(default: largest)
--compress Write gzip/brotli copies of the results
(default: true; --no-compress to skip)--label-collections largest gives one label per feature, placed in its
largest part. combine labels the outline around all the parts together,
so a country with distant parts (France and French Guiana, say) gets its
label in the gap between them; explode gives one label per part.
A bad --label-method, --label-collections or --property-set value is
refused before anything is read or written.
Without --merge-subunits, build just slims the countries layer and
writes <prefix><countries-name>-<property-set>-<version-tag>.geojson. With
it, both layers are slimmed, the named subunits are folded into the country
features, and the result is written as
<prefix>custom-<property-set>-<version-tag>.geojson — mirroring the
ne_50m_admin_0_countries-… / ne_50m_admin_0_custom-… naming split.
slim — strip properties on a single file
slim-natural-earth slim --input ./lib/ne_50m_admin_0_countries-5.1.2.geojson \
--output ./dist/countries-min.geojson --property-set min--property-set (-p) accepts min, micro, or a comma-separated list of
your own field names, e.g. --property-set name,iso_a3,pop_est. Pass
--subunit-layer when slimming a map-subunits file, so every output feature
gets is_subunit: true.
merge — fold named subunits into a country layer
slim-natural-earth merge \
--countries ./dist/countries-min.geojson \
--subunits ./dist/subunits-min.geojson \
--names "Greenland,Puerto Rico" \
--output ./dist/custom-min.geojsonMatched subunit features are prepended ahead of the country features, so
renderers that paint in feature order (SVG/D3, canvas, Leaflet, and features
within one GL layer) draw them first, underneath the countries. To draw them
on top, put the is_subunit: true features in a later layer. Name matching
is case-insensitive. A name that matches no subunit prints a warning on
stderr; if none of the names match, the command fails (exit 1) and writes
nothing. build --merge-subunits does the same. An empty list (--names ',')
is refused.
label — generate label points
slim-natural-earth label --input ./dist/custom-min.geojson \
--output ./dist/custom-min.labels.geojson --method polylabel --collections largestThin wrapper around
geojson-polygon-labels;
see its README for the full set of --method and --collections options.
--collections defaults to largest here (upstream’s default is combine;
see build above for why). Polylabel runs at a precision of 0.001
coordinate units (about 100 m in degrees) rather than upstream’s 0.000001,
which can exhaust memory on long, straight-sided polygons.
Input must be longitude/latitude, as RFC 7946 GeoJSON is. In projected
coordinates (metres, say) 0.001 is a millimetre and the label step can hang,
so input with any coordinate beyond ±360 longitude or ±90 latitude is
refused. If geojson-polygon-labels writes output that does not parse (it can
exit 0 after an error), the labels file is removed and the command fails.
--output must be a file path, not a directory.
compress — gzip/brotli one or more files
slim-natural-earth compress --input ./dist/custom-min.geojson ./dist/custom-min.labels.geojsonWrites <file>.gz and <file>.br next to each input via Node’s built-in
zlib module. Pass --no-gzip or --no-brotli to skip one (not both).
Property sets
Field names are Natural Earth’s standard admin-0 attribute schema (see the
Natural Earth documentation
for what each column means). Every output feature also gets an is_subunit
boolean, set from context (the slim/build subunit-layer flag), regardless
of which property set is chosen.
micro— a tiny, display-oriented set:type,name,name_long,admin,iso_a3,formal_en,continent,subregion,region_wb.min— a fuller set covering identification, geometry-adjacent cartographic helper fields, and demographics:type,featurecla,scalerank,labelrank,sovereignt,sov_a3,adm0_dif,level,tlc,admin,adm0_a3,geou_dif,geounit,gu_a3,su_dif,subunit,su_a3,name,name_long,abbrev,postal,formal_en,name_ciawf,name_sort,name_alt,mapcolor7,mapcolor8,mapcolor9,mapcolor13,pop_est,pop_rank,pop_year,gdp_md,gdp_year,economy,income_grp,fips_10,iso_a2,iso_a3,iso_n3,woe_id,woe_note,adm0_iso,adm0_diff,adm0_tlc,adm0_a3_un,continent,region_un,subregion,region_wb,tiny,homepart,min_zoom,min_label,max_label,label_x,label_y,ne_id,wikidataid.custom — pass any comma-separated list of field names to
--property-set/-p(case-insensitive; input property keys are lower-cased before matching). Fields absent from a given feature are simply omitted from its output, not padded withnull. An empty list (-p ''or-p ',') is refused.
Notes
A known upstream quirk, worked around internally:
[email protected]’s default (--input-format=geojson) code path has athis-binding bug (this.queue(...)inside an arrow function passed to thethroughpackage) that silently drops every feature, plus separately double-nests its--output-format=geojsonwrapper. This tool’slabelstep converts its input to newline-delimited GeoJSON (--input-format=geojsonseq) before invoking the CLI, which sidesteps both bugs — no patched dependency needed, no action required on your part. Both bugs are reported upstream with a fix (issue #30, PR #31); if the PR merges and a release follows, this workaround can be retired.lib/anddist/are gitignored in this repo. The tool writes no.gitignoreof its own, so in your own project add the directories you point--input-dir/--output-dirat, if you don’t want Natural Earth source files and generated output committed.
Exit codes
0— success, including a merge where some (not all) subunit names matched; each unmatched name is aWarning:line on stderr.1— anything else. A usage error (unknown option, bad choice, missing required option) prints the help screen and the reason. A runtime error (missing or unreadable input, input that is not a GeoJSON FeatureCollection, an empty--property-setor name list, no subunit name matched, coordinates that are not longitude/latitude, a failed label run) prints oneError: …line.
Known limits
- Polylabel precision is fixed at 0.001 coordinate units; there is no flag to change it. That is about 100 m for longitude/latitude data, finer than a label needs. Projected input is refused only when a coordinate is out of longitude/latitude range, so projected data near the origin gets through.
- If a run is killed (Ctrl-C) during the label step, the temporary
directory
slim-natural-earth-labels-*under your system temp directory is left behind. A label step that fails on its own removes it, and the partial labels file. buildwrites the slimmed file before the label step runs, so a label failure leaves the slimmed.geojsonin the output directory without its labels or compressed copies.- Only the
geojson-polygon-labels2.0.1 code path is tested; the dependency range is^2.0.1, so a later 2.x reaches you untested (see the upstream note above). - Not tested against real Natural Earth releases in CI (the test fixtures are synthetic) or on Windows.
Requirements
Node 18+.
