Skip to main content

PMTiles Reader

The lightweight pmtiles_gbx reader produces a tile pyramid as (z, x, y, bytes) rows. It has two sources, chosen by the source option:

  • source="raster" — build an XYZ (slippy-map) tile mosaic pyramid from a directory of Cloud-Optimized GeoTIFFs / rasters. Each slippy tile in the area of interest is rendered (one composited PNG per (z, x, y)) across the cluster.
  • source="archive" — read tiles back out of an existing .pmtiles archive, one row per stored tile.

Both sources emit the same (z, x, y, bytes) schema — which is exactly the input schema the PMTiles writer expects — so a read feeds straight into a write with no reshaping.

Lightweight-only read path

pmtiles_gbx is a pure-Python DataSource V2 reader: no JAR, no init script, and it runs on Serverless, standard (shared), and ARM clusters. The beta-release-notes note that PMTiles "read is not yet supported" refers to the heavyweight spark.read.format("pmtiles") path — the lightweight pmtiles_gbx reader documented here is a supported read path.

Register the lightweight DataSources once per session:

# Register the lightweight DataSources (once per session)
from databricks.labs.gbx.ds.register import register
register(spark)

Output schema

Both sources produce the same four columns — identical to the PMTiles writer input:

root
|-- z: integer (zoom level, not null)
|-- x: integer (slippy tile column, not null)
|-- y: integer (slippy tile row, not null)
|-- bytes: binary (tile payload — a rendered PNG for source="raster",
| the stored tile bytes verbatim for source="archive")

source="raster" — mosaic pyramid from rasters

Point the reader at a directory of rasters and an area of interest; it enumerates the slippy tiles across the zoom range, attaches only the sources that intersect each tile, and renders one tile per (z, x, y) with rio-tiler.

# source="raster": tile a directory of COGs into an XYZ pyramid.
# Emits one rendered (z, x, y, bytes) tile row per slippy tile in the AOI.
df = (spark.read.format("pmtiles_gbx")
.option("source", "raster")
.option("bbox", "-122.50,37.74,-122.40,37.79") # minx,miny,maxx,maxy (WGS84)
.option("minZoom", "12")
.option("maxZoom", "16")
.option("tileFormat", "png")
.load("/Volumes/main/geobrix_samples/naip/sf/"))
# columns: z (int), x (int), y (int), bytes (binary — a rendered PNG tile)
df.show()

Because the output is already the writer's input schema, packaging the pyramid into a .pmtiles archive is a direct hand-off:

# The (z, x, y, bytes) rows feed straight into the pmtiles writer —
# no reshaping — packaging the mosaic pyramid into a single archive.
(df.write.format("pmtiles_gbx")
.option("shardZoom", "0") # 0 = one single .pmtiles archive
.mode("overwrite")
.save("/Volumes/main/geobrix_samples/naip/sf.pmtiles"))

Options — source="raster"

OptionDefaultDescription
path (.load(...))— (required)Directory of source rasters.
bboxsource unionArea of interest as "minx,miny,maxx,maxy" in WGS84. Absent → the combined bounds of all sources.
minZoom / maxZoom"0" / "0"Inclusive slippy-map zoom range to render.
tileFormat"png"Rendered tile encoding.
tilesPerPartition"64"Tiles per Spark partition (fan-out granularity).
filterRegex".*\.tif$"Keep only source files whose path matches.
pixelSelection"first"Mosaic pixel-selection method. Only "first" is supported in v1; any other value raises.

An AOI (or bbox) that intersects zero tiles for the requested zoom range logs a warning and returns an empty DataFrame with the standard schema — not an error.

source="archive" — read an existing .pmtiles

Read the tiles stored in a .pmtiles file back into a DataFrame, one row per tile. The tile bytes are returned verbatim (whatever encoding the archive stores — PNG/JPEG/WebP/MVT).

# source="archive": read tiles back out of an existing .pmtiles file.
df = (spark.read.format("pmtiles_gbx")
.option("source", "archive")
.load("/Volumes/main/geobrix_samples/naip/sf.pmtiles"))
# same (z, x, y, bytes) schema — bytes are the stored tile payload, verbatim
df.show()

Options — source="archive"

OptionDefaultDescription
path (.load(...))— (required)The .pmtiles archive to read.
tilesPerPartition"2048"Tiles per Spark partition.

Next Steps