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:
| Key | Type | Description |
|---|---|---|
tile_type | str | "mvt", "png", "jpeg", "webp", "avif", or "unknown" |
tile_compression | str | "none", "gzip", "brotli", "zstd", or "unknown" |
min_zoom / max_zoom | int | Zoom range of the archive |
bounds | tuple | (min_lon, min_lat, max_lon, max_lat) in degrees |
center | tuple | (lon, lat, zoom) |
tile_count | int | Total number of tiles in the archive |
metadata | dict | Archive metadata (e.g. vector_layers) |
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:
| Parameter | Type | Default | Description |
|---|---|---|---|
path_or_bytes | str or bytes | — | Path to a .pmtiles file (Volume/DBFS scheme stripped) or the archive bytes. |
max_embed_mb | float | None | None | Embed 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. |
fallback | bool | True | When False, raise instead of falling back to the static render. |
style | dict or None | None | Override the auto-generated MapLibre style on the interactive path. |
**map_kwargs | On 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.
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")
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:
| Parameter | Type | Default | Description |
|---|---|---|---|
path | str | — | Path to a GeoTIFF/COG (Volume/DBFS scheme stripped). |
band | int or None | None | 1-based band index. None reads all bands: 1 band → viridis, 3+ bands → RGB composite. |
max_pixels | int | 2000 | Decimate above this longest-edge pixel count before rendering. |
fig_w / fig_h | float | 10 | Figure dimensions in inches. |
basemap | bool | True | Fetch and overlay a contextily basemap (requires network). Pass False for deterministic, offline renders. |
basemap_source | None | Override the default contextily.providers.CartoDB.Positron. | |
title | str or None | None | Axes 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.
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)