Documentation

geolith vs Planetiler (Overture + OSM)

geolith was benchmarked head-to-head against Planetiler (Java) on identical input data and region bounds.

Test Configuration

ParameterValue
RegionWestern India (67.67,14.39,80.92,24.72)
Max zoom15
OSM PBFwestern-zone-latest.osm.pbf (196 MB)
OvertureBuildings + Divisions (hive-partitioned GeoParquet)
India boundaryGeoJSON
Natural EarthSQLite
Land polygonsShapefile (EPSG:3857)
Water polygonsShapefile (EPSG:3857)
MachineApple M2 Max, 96 GB RAM, NVMe SSD

Both tools processed the same layers: earth, water, boundaries, landuse, roads, transit, buildings, places, pois, and divisions. Planetiler additionally processed daylight landcover (0.3s wall time, negligible impact).

Results

Metricgeolith (Rust)Planetiler (Java)
Wall time5:156:20
CPU time (user)709s3,343s
CPU utilization297%895%
Output size2.0 GB3.4 GB
Peak memory~2 GB25 GB (-Xmx24g)
Tiles written1,612,666

Summary

  • 17% faster wall time (5:15 vs 6:20)
  • 4.7x less CPU time (709s vs 3,343s)
  • 41% smaller output (2.0 GB vs 3.4 GB)
  • ~12x less memory (~2 GB vs 25 GB heap)

geolith achieves competitive performance with significantly lower resource usage. The smaller output is primarily due to tile content deduplication and efficient MVT encoding.

Phase Breakdown (geolith)

PhaseDurationDetails
Phase 1: Overture GeoParquet~52s6.4M features read, 66.3M tile features
Phase 1: OSM PBF (2-pass)~25s31.8M nodes, 3.8M features
Phase 1: Natural Earth<1s18,939 features from 12 tables
Phase 1: Land polygons~78s833K polygons from shapefile
Phase 1: Water polygons~90s14.5K polygons from shapefile
Phase 1: India boundary~2s1 GeoJSON feature
Phase 2: External sort<1s34 LZ4-compressed chunks, k-way merge
Phase 3: Tile encode~50s1,612,666 tiles, gzip compressed
Total5:159.5M input features, 77.5M tile features

geolith CLI

geolith \
    --data ./overture-data/ \
    --osm-pbf western-zone-latest.osm.pbf \
    --natural-earth natural_earth_vector.sqlite \
    --land-polygons ./land-polygons-split-3857/ \
    --water-polygons ./water-polygons-split-3857/ \
    --india-boundary india-boundary.geojson \
    --output western-zone.pmtiles \
    --bbox 67.67,14.39,80.92,24.72 \
    --max-zoom 15 \
    --node-cache

Planetiler CLI

java -Xmx24g -jar protomaps-basemap-HEAD-with-deps.jar \
    --osm-path=western-zone-latest.osm.pbf \
    --output=western-zone-planetiler.pmtiles \
    --bounds=67.67,14.39,80.92,24.72 \
    --buildings_source=overture \
    --overture_buildings_path=./overture-buildings/ \
    --divisions_source=overture \
    --overture_divisions_path=./overture-divisions/ \
    --india_boundary_path=india-boundary.geojson \
    --nodemap-type=sparsearray --nodemap-storage=mmap \
    --download --force

geolith vs Planetiler (OSM Only)

A second benchmark isolates OSM-only performance, removing Overture data entirely. This tests the core tile pipeline: OSM PBF reading, Natural Earth, shapefiles, and India boundary.

Test Configuration

ParameterValue
RegionWestern India (67.67,14.39,80.92,24.72)
Max zoom15
OSM PBFwestern-zone-latest.osm.pbf (197 MB)
OvertureNone (--no-conflate, empty data directory)
Natural EarthSQLite (923 MB)
Land polygonsShapefile (EPSG:3857)
Water polygonsShapefile (EPSG:3857)
India boundaryGeoJSON
MachineApple M2 Max, 96 GB RAM, NVMe SSD

Both tools processed the same OSM-derived layers: earth, water, boundaries, roads, buildings, and places. Planetiler additionally processed daylight landcover (0.3s wall time, negligible impact). No Overture themes (divisions, bathymetry, pois) were included.

Results

Metricgeolith (Rust)Planetiler (Java)
Wall time3:312:29
CPU time (total)587s778s
Peak memory5.4 GB10.9 GB
Output size472 MB418 MB
Tiles written1,612,6661,612,666
Input features3.7M14.7M
Tile features14.6M

Analysis

Planetiler wins on wall time (1.4x faster) in this OSM-only configuration, primarily due to its streaming pipeline that overlaps reading and encoding phases. geolith's 4-phase architecture processes each stage sequentially.

However, geolith shows significant advantages in resource efficiency:

  • 25% less CPU time (587s vs 778s total)
  • ~2x less memory (5.4 GB vs 10.9 GB)
  • OSM processing itself (pass 1 + pass 2) took only 47s — comparable to Planetiler's ~69s

Output sizes are within 13% (472 MB vs 418 MB). The difference is due to geolith including OSM waterway features (rivers, streams, canals) that Planetiler filters differently.

Parallel shapefile improvement: With parallel land + water polygon reads, geolith improved from 4:483:31 wall time — a 27% reduction. Land and water polygons now overlap on separate OS threads:

  • Land polygons: ~106s
  • Water polygons: ~135s (bottleneck)
  • Combined wall time: ~135s (vs ~223s sequential)

Phase Breakdown (geolith)

PhaseDurationDetails
osmium extract~4sbbox filter (197 MB → 197 MB, region covers entire file)
OSM pass 1 (nodes + relations)~25s31.9M nodes, 128K ways with relation info
OSM pass 2 (features)~17s3.8M features, 68K tile features; 51K water features from OSM
Natural Earth<1s18,939 features from 12 tables
Land + Water polygons (parallel)~135s833K land + 14.5K water polygons on separate threads
India boundary~2s1 GeoJSON feature
External sort<1s16 LZ4-compressed chunks, k-way merge
Tile encode + write~26s1,612,666 tiles, gzip compressed
Total3:313.7M input features, 14.6M tile features

geolith CLI

geolith \
    --data ./empty-overture/ \
    --osm-pbf western-zone-latest.osm.pbf \
    --natural-earth natural_earth.sqlite \
    --land-polygons ./land-polygons/ \
    --water-polygons ./water-polygons/ \
    --india-boundary india-boundary.geojson \
    --output western-zone-osm.pmtiles \
    --bbox 67.67,14.39,80.92,24.72 \
    --max-zoom 15 \
    --no-conflate \
    --node-cache

Planetiler CLI

java -Xmx24g -jar protomaps-basemap-HEAD-with-deps.jar \
    --osm-path=western-zone-latest.osm.pbf \
    --output=western-zone-planetiler-osm.pmtiles \
    --bounds=67.67,14.39,80.92,24.72 \
    --nodemap-type=sparsearray --nodemap-storage=mmap \
    --force

Expected Performance

DatasetSizeZoomTimeOutputMachine
Regional (western India)~196 MB OSM + Overture15~5 min~2 GB PMTilesM2 Max, 96 GB RAM
Regional (India full)~5 GB OSM + Overture15~15–25 min~5–8 GB PMTilesM2 Max, 96 GB RAM
Planet (all themes)~70 GB OSM + ~290 GB Overture15TBDTBD32 cores, 64 GB RAM, NVMe

Planet-scale benchmarks are planned. The regional result above demonstrates geolith already outperforms Planetiler on identical workloads.

Bottlenecks

Performance is typically bound by one of three factors:

  1. Disk I/O — Reading GeoParquet/PBF input and writing temporary sort files. Use NVMe SSDs for the data directory and --tmpdir.
  2. CPU — Feature processing (projection, clipping, simplification) is parallelized via rayon. More cores = faster processing.
  3. Memory — External sort keeps memory usage bounded. The main memory consumers are the Parquet reader buffers and the OSM node store.

Tuning Recommendations

FactorRecommendation
Disk speedNVMe SSD for --data, --tmpdir, and --output paths
Thread countDefault (all cores) is usually optimal. Reduce with --threads if sharing the machine.
Temp directoryPlace on the fastest available disk. Avoid network mounts.
Max zoomEach additional zoom level roughly quadruples tile count. See Output Tuning.
Node cacheUse --node-cache to skip OSM pass 1 on subsequent runs with the same PBF.

Running Micro-Benchmarks

# Run the full benchmark suite
cargo bench

# Run a specific benchmark file
cargo bench --bench tile_encode

# Filter by benchmark name
cargo bench -- "encode_tile"

Micro-benchmarks use Criterion.rs for statistical analysis. Results include confidence intervals and regression detection. Reports are saved to target/criterion/ with HTML visualizations.