Skip to content

Benchmark Results

Comparing PMTiles and QBTiles index sizes using the same tile entries, serialized with gzip compression.

Both formats sort entries by their respective key (tile_id for PMTiles, quadkey for QBTiles) with data arranged in that order.

Index Size Comparison (gzip)

Dataset Entries PMTiles QBTiles Reduction
worldmap-boundary 907 2,315 B 2,164 B -6.5%
adm-korea 36,149 80,891 B 61,251 B -24.3%
seoul_filtered_osm 12,068 25,024 B 24,640 B -1.5%
korea_filtered_osm 39,186 77,680 B 76,700 B -1.3%
building-korea-mvt 35,069 74,589 B 76,333 B +2.3%
korea_osm_all 39,108 84,725 B 81,440 B -3.9%
dem-mapzen-korea 60,092 150,240 B 139,537 B -7.1%
osm_z0-z10 656,695 0.90 MB 0.95 MB +5.5%
hillshade_webp_z0_z11_256 4,624,740 8.34 MB 4.36 MB -47.8%
osm-20240812-full 160,819,550 300.65 MB 235.19 MB -21.8%

Bytes per Entry (gzip)

Dataset Entries PM B/entry QB B/entry Diff
worldmap-boundary 907 2.55 2.39 -0.17
adm-korea 36,149 2.24 1.69 -0.54
seoul_filtered_osm 12,068 2.07 2.04 -0.03
korea_filtered_osm 39,186 1.98 1.96 -0.03
building-korea-mvt 35,069 2.13 2.18 +0.05
korea_osm_all 39,108 2.17 2.08 -0.08
dem-mapzen-korea 60,092 2.50 2.32 -0.18
osm_z0-z10 656,695 1.44 1.52 +0.08
hillshade_webp_z0_z11_256 4,624,740 1.89 0.99 -0.90
osm-20240812-full 160,819,550 1.96 1.53 -0.43

Raw vs Gzip — Full OSM (160M entries)

PMTiles QBTiles Reduction
raw 985.81 MB (6.43 B/entry) 656.85 MB (4.28 B/entry) -33.4%
gzip 300.65 MB (1.96 B/entry) 235.19 MB (1.53 B/entry) -21.8%

Observations

  • QBTiles is smaller in 10 of 12 tested files
  • Larger and denser datasets benefit more from QBTiles
  • hillshade (4.6M entries, dense raster): 47.8% reduction
  • Full OSM (160M entries): 21.8% reduction (gzip), 33.4% reduction (raw)
  • Pre-compression saving of 2.15 bytes per entry (6.43 → 4.28) — the effect of not storing tile_id deltas
  • gzip narrows the gap because PMTiles' delta patterns also compress well
  • QBTiles is larger in: building (+2.3%), osm_z0-z10 (+5.5%) — mixed zoom levels or spatially sparse tiles

Spatial Grid Data Compression

When used as a data container (not a tile index), the bitmask structure eliminates grid IDs entirely — position is implied by the tree structure. Tested with South Korea's 100m population grid (931,495 cells × 3 values: total, male, female).

Format Size Ratio
GPKG 93.0 MB 55x
GPKG + zip 19.3 MB 11x
GeoTIFF (LZW) 7.5 MB 4.5x
Parquet 6.2 MB 3.7x
GeoTIFF (deflate) 4.5 MB 2.7x
GeoTIFF (deflate) + zip 4.1 MB 2.4x
Parquet + zip 3.6 MB 2.1x
QBTiles bitmask (gzip) 1.6 MB 1.0x
  • Grid occupancy: 1.4% (931K out of 67M possible cells) — highly irregular
  • GeoTIFF stores the full 8192×8192 raster with NoData; compression helps but cannot match skipping empty cells entirely
  • Parquet stores grid IDs explicitly; even compressed, ID overhead remains
  • QBTiles stores no IDs — the bitmask itself encodes which cells exist

See Example: Bitmask as a Data Container for the full workflow.

Conditions

  • PMTiles: sorted by tile_id, serialize_directory() (varint delta encoding, gzip)
  • QBTiles: sorted by quadkey, write_qbt_variable() (bitmask BFS + columnar varint, gzip)