Madagascar boundaries: from a raw export to PMTiles¶
Convert 17,465 admin-4 boundary polygons for Madagascar (28 MB), from the fieldmaps.io global boundaries, into a PMTiles archive using the command line. First, build an overview file: queryable GeoParquet containing your data at every zoom level.
Each step is a script in
examples/madagascar-boundaries/.
CI runs the scripts on every pull request and checks this page's code and output.
Before you begin¶
You need three tools on your PATH:
| Tool | Install | Used for |
|---|---|---|
tylertoo |
cargo install tylertoo |
Overviews, tiles, validation, decoding |
gpio |
pip install geoparquet-io |
Sorting and repacking the input |
duckdb |
pip install duckdb-cli |
Adding GeoParquet metadata |
Run the scripts in order from the same empty working directory. Each step reads the previous step's files.
1. Prepare the input¶
tylertoo reads WGS84 GeoParquet. Hilbert sorting puts geographic neighbors near each other on disk; row groups of a few megabytes or more improve read performance. Raw database and Spark exports often need both adjustments.
This export also lacks geo metadata, despite having valid Well-Known Binary
(WKB) geometry. A DuckDB round trip adds the metadata so GeoParquet tools can
identify the geometry column. Then
geoparquet-io (gpio) sorts
and repacks the file.
File: 01-prepare.sh
#!/usr/bin/env bash
set -euo pipefail
# Download the input once: 17,465 admin-4 boundary polygons (28 MB).
SRC=fieldmaps-madagascar-adm4.parquet
if [ ! -f "$SRC" ]; then
curl -fsSLO "https://github.com/geoparquet-io/tylertoo/releases/download/fixtures-v1/$SRC"
fi
# The export holds WKB geometry but no GeoParquet `geo` metadata.
# DuckDB's spatial extension writes the metadata on a round trip.
duckdb -c "
INSTALL spatial;
LOAD spatial;
COPY (
SELECT * REPLACE (ST_GeomFromWKB(geometry) AS geometry)
FROM '$SRC'
) TO 'raw.parquet' (FORMAT parquet);
"
# Hilbert-sort, add a bbox column, compress with ZSTD, and pack 16 MB
# row groups, all in one gpio pass.
gpio convert geoparquet raw.parquet prepared.parquet --row-group-size-mb 16
gpio inspect prepared.parquet
Output: prepared.parquet (17.57 MB)
✓ Output passes GeoParquet validation (metadata checks)
...
Rows: 17,465
Row Groups: 3
Compression: ZSTD
GeoParquet Version: 1.1.0
CRS: OGC:CRS84 (default)
Geometry Types: MultiPolygon
Bbox: [43.187051, -25.606140, 50.493403, -11.949812]
...
gpio inspect confirms lon/lat WGS84 (OGC:CRS84) and three
Zstandard-compressed row groups. For projected data, run
gpio convert reproject input.parquet wgs84.parquet -d EPSG:4326 before sorting.
2. Preview one region¶
Test settings on a small region with --bbox, which takes lon/lat bounds as
xmin,ymin,xmax,ymax. tylertoo reads only row groups whose bounds intersect
the box, so a city preview from a country file finishes in seconds.
File: 02-preview.sh
#!/usr/bin/env bash
set -euo pipefail
# Build a quick overview of Antananarivo only. --bbox reads just the row
# groups whose bounds touch the box (xmin,ymin,xmax,ymax in lon/lat).
tylertoo overview prepared.parquet preview-ov.parquet \
--max-zoom 12 \
--bbox=47.3,-19.1,47.7,-18.7
✓ Overview prepared.parquet → preview-ov.parquet (Duplicating mode)
943 input features → 7,600 rows across 13 levels in 0.21s
lvl gsd(m) features vertices bytes
0 39135.76 1 5 4.17 KiB
1 19567.88 6 30 5.08 KiB
2 9783.94 24 120 7.27 KiB
3 4891.97 99 494 15.50 KiB
4 2445.98 335 1,649 39.03 KiB
5 1222.99 649 3,025 71.15 KiB
6 611.50 847 4,174 93.15 KiB
7 305.75 925 5,582 109.73 KiB
8 152.87 942 7,533 126.09 KiB
9 76.44 943 10,088 145.42 KiB
10 38.22 943 13,262 169.75 KiB
11 19.11 943 17,266 201.00 KiB
12 9.55 943 58,321 530.90 KiB
The box contains 943 polygons. gsd (ground sample distance) is the size of
one pixel in meters. At z0–z2, pixels span 10–40 km, wider than any of these
polygons. A polygon below the visibility gate is drawn as a small
placeholder square, so even the coarsest levels show where the polygons
are. Use --no-collapse to drop them or --collapse to draw dots. The
tuning reference
explains the available settings.
3. Build and validate the overview¶
tylertoo overview writes all levels to one GeoParquet file with a level
column. Each level thins and simplifies features for its scale; the finest
preserves the source geometry.
--max-zoom defaults to 6 for a continental view. Use z10 to see individual
communes, or --max-zoom auto to estimate a zoom from feature size and spacing.
tylertoo validate checks geo:overviews metadata, zoom-to-resolution
mapping, and per-level structure.
File: 03-overview.sh
#!/usr/bin/env bash
set -euo pipefail
# Build the full pyramid, z1 to z10, then check it against the spec.
tylertoo overview prepared.parquet madagascar-ov.parquet \
--min-zoom 1 \
--max-zoom 10
tylertoo validate madagascar-ov.parquet
✓ Overview prepared.parquet → madagascar-ov.parquet (Duplicating mode)
17,465 input features → 133,881 rows across 10 levels in 0.66s
lvl gsd(m) features vertices bytes
0 19567.88 1,537 7,713 180.90 KiB
1 9783.94 4,558 22,574 516.02 KiB
2 4891.97 9,817 47,936 1.07 MiB
3 2445.98 14,563 72,792 1.60 MiB
4 1222.99 16,503 96,337 1.90 MiB
5 611.50 17,131 130,419 2.20 MiB
6 305.75 17,385 177,754 2.61 MiB
7 152.87 17,457 239,481 3.14 MiB
8 76.44 17,465 322,984 3.86 MiB
9 38.22 17,465 1,749,880 17.45 MiB
Validating madagascar-ov.parquet
...
✓ valid overview file (12 checks passed)
At 19.6 km per pixel, 1,537 features remain: the polygons large enough to
see, plus placeholder squares for the smaller ones. The count grows at each
level until level 8 holds every polygon. The density budget (--drop-rate)
thins only points by default, so boundaries that tile the island keep
their coverage at every zoom. Level 9 holds all 17,465 polygons at full
detail. Any Parquet reader can open the file; the
Brazil tutorial
shows how to query an overview with DuckDB.
4. Export PMTiles¶
tylertoo export-pmtiles cuts the overview levels into vector tiles without
recomputing geometry. --layer-name sets the MVT source layer used by your
map style; --min-zoom 1 starts at the overview's first zoom.
tylertoo stats reads the archive directory and reports tile sizes by zoom
to help identify slow-loading tiles.
File: 04-export.sh
#!/usr/bin/env bash
set -euo pipefail
# Cut the overview into vector tiles, then report tile weight per zoom.
tylertoo export-pmtiles madagascar-ov.parquet madagascar.pmtiles \
--layer-name boundaries \
--min-zoom 1
tylertoo stats madagascar.pmtiles
Exporting madagascar-ov.parquet → madagascar.pmtiles
mode=duplicating zooms z1..z10
z1 (level 0): 1 tiles, 1537 features
...
z4 (level 3): 4 tiles, 16451 features, 1 oversized, 12 collapsed at extent
...
z10 (level 9): 524 tiles, 26831 features, 8 collapsed at extent
✓ 752 tiles, 161669 features, 2 oversized tiles in 1.49s
...
z tiles total mean p50 p99 max
1 1 74,326 74,326 74,326 74,326 74,326
...
10 524 4,057,907 7,744 6,100 28,397 88,323
Largest 10 tile(s):
z x y bytes
6 40 35 436,976
...
Polygons crossing tile edges appear in every tile they touch, so tile features outnumber overview features. Clipped slivers that vanish on the tile's 4096-unit grid are omitted and counted as "collapsed at extent". The largest tile is 437 KB compressed, at z6. Two tiles, at z4 and z5, went over the default limit of 500 KB compressed (and 2 MB before compression). The size valve merged their densest polygons into neighbors until each fit; the export counts these as "oversized".
View madagascar.pmtiles at pmtiles.io, or host it on
any server that supports HTTP range requests. No tile server is needed.
5. Decode tiles back to GeoParquet¶
tylertoo decode extracts chosen zooms from any PMTiles v3 vector archive
to GeoParquet. Use it to inspect a map's features at a zoom or compare
archives in SQL.
File: 05-decode.sh
#!/usr/bin/env bash
set -euo pipefail
# Read the z8 tiles back out as GeoParquet.
tylertoo decode madagascar.pmtiles z8.parquet --zoom 8
Decoding madagascar.pmtiles → z8.parquet
zooms z8..z8, layers: boundaries
✓ 21,186 features from 44 tiles (0 skipped as degenerate) in 0.44s
Decoded geometry retains tile simplification and clipping; a feature spanning
two tiles appears twice. The zoom, layer, and mvt_id columns identify
each row's origin.
Run the whole workflow¶
Run all five scripts from a fresh directory:
git clone https://github.com/geoparquet-io/tylertoo.git
mkdir madagascar
cd madagascar
for step in ../tylertoo/examples/madagascar-boundaries/0*.sh; do
bash "$step"
done
Next steps¶
- Combine steps 3 and 4 with
tylertoo prepared.parquet madagascar.pmtiles --max-zoom 10. Add--keep-overview madagascar-ov.parquetto save the overview too. - The Brazil tutorial
reads cloud data with
--files-from,--bbox, and--filter, splits the tiling across shards, and combines two layers withpyramid. - The CLI reference lists every flag with its default.