Skip to main content

PMTiles Viewers

VizX Overview · Multi-Layer Compositor · Vector · Raster

These helpers render a .pmtiles archive or a Cloud-Optimized GeoTIFF in a notebook — driver-side, no tile server, no distributed op. See the Helios notebooks for a worked end-to-end example: plot_pmtiles renders all three archives (vector MVT, raster XYZ, hillshade) and plot_cog visualizes a 3DEP terrain COG inline in a Databricks notebook.

pmtiles_info

from databricks.labs.gbx.pmtiles import pmtiles_info
pmtiles_info(path_or_bytes) -> dict

Parse a .pmtiles archive header and count tiles. path is a filesystem path (Volume/DBFS scheme prefixes are stripped automatically) or the archive bytes. Returns a plain dict:

KeyTypeDescription
tile_typestr"mvt", "png", "jpeg", "webp", "avif", or "unknown"
tile_compressionstr"none", "gzip", "brotli", "zstd", or "unknown"
min_zoom / max_zoomintZoom range of the archive
boundstuple(min_lon, min_lat, max_lon, max_lat) in degrees
centertuple(lon, lat, zoom)
tile_countintTotal number of tiles in the archive
metadatadictArchive metadata (e.g. vector_layers)
Inspect a PMTiles archive
from databricks.labs.gbx.pmtiles import pmtiles_info

archive = _build_archive(
[
(0, 0, 0, _distinct_png(0)),
(1, 0, 0, _distinct_png(1)),
],
TileType.PNG,
)
info = pmtiles_info(archive)

assert info["tile_type"] == "png"
assert info["tile_count"] == 2
assert info["min_zoom"] == 0
assert info["max_zoom"] == 1
assert len(info["bounds"]) == 4 # (min_lon, min_lat, max_lon, max_lat)
assert len(info["center"]) == 3 # (lon, lat, zoom)

plot_pmtiles

plot_pmtiles(
path_or_bytes, *, max_embed_mb=None, fallback=True, style=None, **map_kwargs
)

Render a .pmtiles archive inline in a Databricks or Jupyter notebook.

Interactive path (default): when the archive fits within max_embed_mb (base64-inflated size), builds a self-contained MapLibre GL JS + pmtiles.js page with the archive embedded inline as an in-browser FileSource. No tile server, no remote range requests. Vector (MVT) archives render as a vector layer; raster archives (PNG/JPEG/WebP/AVIF) render as a raster layer — auto-detected from the archive header.

Static fallback: when the embedded archive would exceed max_embed_mb and fallback=True (the default), decodes tiles on the driver instead — raster archives → plot_raster; vector archives → decode MVT to geometries and plot_static over a contextily basemap. Pass max_embed_mb=0 to always take the static path (useful for GitHub-renderable notebooks where interactive HTML renders blank).

Parameters:

ParameterTypeDefaultDescription
path_or_bytesstr or bytesPath to a .pmtiles file (Volume/DBFS scheme stripped) or the archive bytes.
max_embed_mbfloat | NoneNoneEmbed budget in MB. None (default) resolves to ~3 MB, or ~6 MB when vizx raises the notebook cell-output cap to its max. Exceeding it triggers the static fallback; 0 forces the static path. See the note below on the notebook ceiling.
fallbackboolTrueWhen False, raise instead of falling back to the static render.
styledict or NoneNoneOverride the auto-generated MapLibre style on the interactive path.
**map_kwargsOn the static fallback, forwarded to the matching static plotter: plot_raster for raster archives (PNG/JPEG/WebP/AVIF) or plot_static for vector archives (MVT). Must be keyword arguments valid for that plotter — e.g. basemap, column, cmap, title, fig_w/fig_h for plot_static; an unrecognised keyword raises TypeError. Ignored on the interactive path.

The interactive page pins CDN versions for reproducibility: MapLibre GL JS 4.7.1 and pmtiles.js 3.2.1.

Render a PMTiles archive — static path (offline)
import matplotlib.pyplot as plt
from databricks.labs.gbx.vizx import plot_pmtiles

plt.close("all")

tile_bytes = _build_raster_tile_archive()
archive = _build_archive(
[(0, 0, 0, tile_bytes)],
# Declare PNG type in the PMTiles header so plot_pmtiles routes to the
# raster static fallback; the actual payload bytes are a GeoTIFF, which
# rasterio opens without warnings (it has a valid geotransform).
TileType.PNG,
)

# max_embed_mb=0 → static raster fallback (offline-safe).
# basemap=False is forwarded through plot_pmtiles; the static raster path
# strips it before calling plot_raster (which does not accept basemap).
plot_pmtiles(archive, max_embed_mb=0, basemap=False)

# The static path delegates to plot_raster which opens a matplotlib figure.
assert len(plt.get_fignums()) >= 1
plt.close("all")
Notebook embed ceiling

The interactive embed is bounded by the notebook cell-output cap, not by max_embed_mb alone. Databricks caps cell output at ~10 MB by default (20 MB max, which vizx raises automatically via %set_cell_max_output_size_in_mb), and displayHTML inflates the embedded payload ~2–3× — so an archive over ~4–5 MB cannot embed inline and takes the static fallback. Raising max_embed_mb past the cell ceiling does not help; reduce what you embed or stream from a URL instead.

To inspect a small archive interactively, just call plot_pmtiles — it embeds inline, no server. For a large archive, drop the densest zooms until it fits, or stream full detail from a URL:

from databricks.labs.gbx.vizx import plot_pmtiles, pmtiles_layer, plot_interactive

# Small archive — embedded inline as an interactive map, no tile server
plot_pmtiles("/Volumes/main/my_schema/my_vol/tiles.pmtiles")

# Large archive — drop the densest zoom levels until it fits (interactive, reduced detail)
plot_pmtiles("/Volumes/main/my_schema/my_vol/big.pmtiles", interactive_fit="downzoom")

# Or stream full detail from an https:// URL — zero embed cost, always interactive
plot_interactive([pmtiles_layer("https://example.com/big.pmtiles")])

plot_cog

plot_cog(
path, *, band=None, max_pixels=2000, fig_w=10, fig_h=10,
basemap=True, basemap_source=None, title=None,
)

Render a Cloud-Optimized GeoTIFF over a contextily basemap as a static matplotlib figure. Reads the file decimated so the longest edge is at most max_pixels — COG overviews are used when present. Volume/DBFS scheme prefixes are stripped automatically.

Parameters:

ParameterTypeDefaultDescription
pathstrPath to a GeoTIFF/COG (Volume/DBFS scheme stripped).
bandint or NoneNone1-based band index. None reads all bands: 1 band → viridis, 3+ bands → RGB composite.
max_pixelsint2000Decimate above this longest-edge pixel count before rendering.
fig_w / fig_hfloat10Figure dimensions in inches.
basemapboolTrueFetch and overlay a contextily basemap (requires network). Pass False for deterministic, offline renders.
basemap_sourceNoneOverride the default contextily.providers.CartoDB.Positron.
titlestr or NoneNoneAxes title (defaults to "COG").

Requires the [vizx] extra plus rasterio. Integer rasters with values above 255 (typical for EO data — Sentinel-2 UInt16 reflectance, Landsat, etc.) are automatically stretched to the 2nd–98th percentile range so the visible range is not clipped.

Render a GeoTIFF with plot_cog
import tempfile

import matplotlib.pyplot as plt
import numpy as np
import rasterio
from databricks.labs.gbx.vizx import plot_cog
from rasterio.transform import from_bounds

plt.close("all")

# Build a minimal GeoTIFF in memory (3-band UInt16, Web Mercator).
with tempfile.NamedTemporaryFile(suffix=".tif", delete=False) as f:
path = f.name

try:
size = 16
rng7 = np.random.default_rng(7)
data = (rng7.random((3, size, size)) * 1000).astype("uint16")
transform = from_bounds(-1.36e7, 4.5e6, -1.35e7, 4.51e6, size, size)
with rasterio.open(
path,
"w",
driver="GTiff",
height=size,
width=size,
count=3,
dtype="uint16",
crs="EPSG:3857",
transform=transform,
) as dst:
dst.write(data)

# basemap=False skips contextily (offline-safe)
plot_cog(path, basemap=False)

assert len(plt.get_fignums()) >= 1
plt.close("all")
finally:
os.unlink(path)