Vector
VizX Overview · Multi-Layer Compositor · PMTiles Viewers
Interactive Maps
For multi-layer interactive maps, see the Multi-Layer Compositor.
plot_interactive renders one or more layers as a pan/zoom
MapLibre GL map — the interactive counterpart to
plot_static, sharing the same layer model. It is the recommended way to get an
interactive map from GeoBrix results: it is scale-safe and
Databricks-safe, neither of which a bare GeoDataFrame.explore() is.
- Scale-safe. The whole map is assembled into one self-contained HTML string
and embedded inline, so its size is bounded by an embed budget
(
max_embed_mb), not by a tile server. When the layers exceed the budget,plot_interactivedegrades gracefully — by default (fallback=True) it falls back toplot_static; the Multi-Layer Compositor documents the full delivery ladder (inline embed, URL streaming, downzoom, static). Passdry_run=Trueto inspect the size audit without rendering. - Databricks-safe. It renders via
displayHTMLinternally and returnsNone. Returning a folium map (or calling.explore()) does not auto-render in a Databricks notebook;plot_interactivedoes the right thing in both Databricks and plain Jupyter (outside a notebook — e.g. in tests — it returns the HTML string so callers can assert on it).
plot_interactive
plot_interactive(
layers, *, basemap="carto-positron", simplify_tiles_spec=None,
max_embed_mb=None, set_cell_max_output=True, fallback=True,
center=None, zoom=None, dry_run=False, debug_mode=1, emphasis="blend",
)
layers is a Layer, a list of layers, or any bare input as_layers accepts (a
geopandas.GeoDataFrame or Spark DataFrame → vector_layer, a .pmtiles archive
→ pmtiles_layer, a raster path / numpy.ndarray → raster_layer). Build layers
with the constructors documented in the Multi-Layer Compositor —
vector_layer, grid_layer, raster_layer, pmtiles_layer — each of which
carries its own column / grid_system / cmap / styling. The map renders as
the function's last statement: in Databricks via displayHTML (returns None),
outside a notebook by returning the HTML string.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
layers | Layer, list[Layer], or bare input | — | The layer(s) to render. A bare GeoDataFrame / Spark DataFrame / .pmtiles archive / raster is coerced via as_layers. |
basemap | str | "carto-positron" | Base layer style: "carto-positron" (CARTO Positron) or "none" (blank dark canvas). |
simplify_tiles_spec | dict or None | None | Optional vector-tile simplification spec (see the compositor page). |
max_embed_mb | float or None | None | Max assembled-HTML embed size, in MiB, measured against the base64-rendered HTML. None resolves to ~6 MB when set_cell_max_output is on (sized for the raised 20 MB cap), else ~3 MB. |
set_cell_max_output | bool | True | On Databricks Serverless, raise the cell-output cap to its 20 MB max before an interactive embed so a larger map isn't truncated (graceful no-op elsewhere). False leaves the cap untouched. |
fallback | bool | True | When the embed budget is exceeded, degrade to plot_static (True) or raise (False). |
center | [lon, lat] or None | None | Map centre override. |
zoom | float or None | None | Initial zoom level override. |
dry_run | bool | False | Return the size-audit dict (see audit_layers) without rendering — no displayHTML, no HTML string. |
debug_mode | int | 1 | [vizx] status verbosity: 0 silent / 1 (default) concise (audit verdict, cap-raise) / 2 adds per-layer diagnostics. |
emphasis | str | "blend" | "data" styles data layers to pop against the full-strength basemap; "blend" (default) is a softer composite. Explicit per-layer style kwargs always override. |
from databricks.labs.gbx.vizx import grid_layer, plot_interactive
# Interactive H3 choropleth straight from a Spark DataFrame of cell ids —
# grid_layer decodes the cells and colours them by `band_level` through `cmap`.
plot_interactive(grid_layer(cells_df, grid_system="h3", column="band_level"))
For a multi-layer overlay (say a raster DEM under vector boundaries under grid cells), pass a list of layers — see the Multi-Layer Compositor for the layer constructors, the embed-size ladder, and the audit/simplify workflow.
Static Maps
For compositing multiple layers in a single map, see the Multi-Layer Compositor — it explains the embed-size ladder, Layer types, and the audit/simplify workflow.
plot_static renders Spark- or GeoPandas-derived geometries (or H3 cells) over
a basemap as a static matplotlib figure — the GitHub-renderable counterpart
to GeoDataFrame.explore() (whose Leaflet/folium output renders a blank
"Make this Notebook Trusted" placeholder on GitHub and the docs site).
The basemap is fetched from a web tile server (via contextily) at execution
time and rasterized into the figure, so it bakes into the committed notebook
output PNG — GitHub then displays it with no network. If the executing
environment has no egress, the map renders without a basemap and a warning is
emitted (never a hard error).
plot_static
plot_static(
data, *, column=None, geom_col=None, grid_system=None, grid_conf=None,
max_rows=10_000, sample_seed=None, srid=None, cmap="viridis", legend=True,
basemap=True, basemap_source=None, alpha=None, edgecolor=None, fill=True,
markersize=None, title=None, fig_w=10, fig_h=10, ax=None,
emphasis="blend", debug_mode=1,
)
cmap, alpha, edgecolor, and markersize left unset resolve to values
derived from emphasis ("blend" default / "data"); debug_mode controls
[vizx] status verbosity (0/1/2), and sample_seed selects how the
max_rows cap is filled (None = first rows; an int = a reproducible sample).
data is a Spark DataFrame or a geopandas.GeoDataFrame. Returns the
matplotlib Axes; pass it back via ax= to overlay layers on one map. Every
layer is reprojected to Web Mercator (EPSG:3857), so a basemap=False overlay
lines up with a basemap layer on the same axes. plot_static does not call
pyplot.show() — in a notebook the figure auto-displays at cell end with all
overlaid layers; a script can call plt.show() itself. Pass fill=False to
draw geometries as outlines only (no face), so a boundary doesn't cover the
layers beneath it.
Geometry columns accept the same encodings as every other gbx_st_*
function — WKT, EWKT, WKB, EWKB, and native GEOMETRY / GEOGRAPHY (coerced
in-Spark via st_asbinary). Set grid_system to treat the column as DGGS
cell ids instead:
grid_system | Behaviour |
|---|---|
None (default) | Column is a geometry encoding (WKT/EWKT/WKB/EWKB/GEOMETRY/GEOGRAPHY). |
'h3' | H3 cell ids (string index or bigint) → cell-boundary polygons (lon/lat). |
'quadbin' | Quadbin cell ids (bigint) → tile-boundary polygons (lon/lat). |
'bng' | British National Grid cell ids (string, e.g. "TQ38") → cell polygons in EPSG:27700. |
'custom' | Custom-grid cell ids → cell polygons; requires grid_conf= (the grid spec that defines the grid). CRS comes from the grid's srid. |
Cell boundaries reuse the lightweight GridX cell→geometry implementations, so
they match gbx_h3_* / gbx_quadbin_aswkb / gbx_bng_aswkb / gbx_custom_cellaswkb
exactly. Each grid's native CRS is honoured (BNG is metres in EPSG:27700) and
reprojected to Web Mercator for the basemap. A custom grid whose srid is <= 0
has no CRS, so its basemap is skipped (with a warning); pass basemap=False.
For a custom grid, grid_conf is the grid-spec Row/dict — the same struct the
custom GridX functions consume (bound_x_min/bound_x_max, bound_y_min/bound_y_max,
cell_splits, root_cell_size_x/root_cell_size_y, srid):
plot_static(cells_df, grid_system="custom", grid_conf=grid_spec, column="value")
from databricks.labs.gbx.vizx import plot_static
# H3 choropleth over a basemap, then overlay the shared-canvas boundary as a
# red outline (fill=False so it doesn't cover the cells; basemap=False so it
# doesn't re-fetch tiles). Both layers reproject to 3857, so they align.
ax = plot_static(cells_df, grid_system="h3", column="count", title="Coverage")
plot_static(grid_gdf, ax=ax, fill=False, edgecolor="red", basemap=False)
basemap_source overrides the default contextily.providers.CartoDB.Positron;
basemap=False skips tiles entirely (deterministic, no network).
Adapters
The vector adapters collect to the driver for interactive mapping. By default they cap the collect at max_rows=10_000 and emit a warning if the input has more rows (pass max_rows=None to collect everything — at your own risk on large frames). The returned GeoDataFrame is in EPSG:4326, ready for .plot() (matplotlib) or — for an interactive map — plot_interactive.
For interactive (pan/zoom) maps, prefer plot_interactive over a raw GeoDataFrame.explore(): it is scale-safe (bare .explore() embeds every vertex inline, so millions of vertices hang folium and render blank) and Databricks-safe (it renders via displayHTML, whereas a returned folium map does not auto-render in a Databricks notebook). Calling gdf.explore() directly still works for small in-memory frames in plain Jupyter.
as_gdf
as_gdf(df, wkt_col="wkt", *, max_rows=10_000)
Convert a Spark DataFrame with a WKT geometry column into a geopandas.GeoDataFrame. Non-geometry columns are preserved; the WKT column is replaced by the geometry:
from databricks.labs.gbx.vizx import as_gdf
gdf = as_gdf(df_with_wkt, wkt_col="wkt")
gdf.explore() # interactive folium map (needs the [vizx] extra)
cells_as_gdf
cells_as_gdf(
df, cell_col="cellid", extra_cols=(), *,
max_rows=10_000, dissolve_by=None, dissolve_engine="auto", sample_seed=None,
)
Convert a DataFrame of H3 cell ids into a GeoDataFrame of cell-boundary polygons (boundaries computed with the h3 library). Carry through attribute columns with extra_cols.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
df | DataFrame | — | Spark DataFrame with an H3 cell-id column. |
cell_col | str | "cellid" | Name of the column containing H3 cell ids (bigint). |
extra_cols | tuple or list | () | Additional columns to carry through to the GeoDataFrame. |
max_rows | int or None | 10_000 | Row cap for driver-side collects; None collects all rows. Not applied on the product-dissolve path (see dissolve_engine). |
dissolve_by | str or None | None | When set, dissolve cell polygons by this column, returning one footprint polygon per distinct value. Must be in extra_cols. |
dissolve_engine | str | "auto" | Controls how the dissolve is performed when dissolve_by is set. See the engine table below. |
sample_seed | int or None | None | None (default) takes the first max_rows rows (first-N, not a representative sample). An int draws a reproducible random sample via DataFrame.sample at the cost of one extra count() job. |
dissolve_engine values:
| Value | Behaviour |
|---|---|
"auto" (default) | Attempt the Spark-side product dissolve; if the product functions are not available (local Spark, older runtime), fall back to the geopandas driver-side dissolve automatically. |
"product" | Product dissolve only — uses h3_boundaryaswkb, st_geomfromwkb, st_union_agg, and st_asbinary built into Databricks Runtime (DBR 11.3+ / Serverless). Raises if the functions are not available; no silent fallback. |
"geopandas" | Driver-side geopandas dissolve — collects up to max_rows cells, builds per-cell polygons via the h3 library, then dissolves with GeoDataFrame.dissolve. Works everywhere. |
Product dissolve path (dissolve_engine="auto" or "product"): the aggregation executes as a Spark SQL expression,
st_asbinary(st_union_agg(st_geomfromwkb(h3_boundaryaswkb({cell_col}))))
grouped by dissolve_by. This pushes the geometry union into the cluster, so only one small row per group is collected to the driver. The max_rows cap does not apply on this path.
Oversize guard: on any driver-collect path (non-dissolve or dissolve_engine="geopandas") where max_rows was not supplied explicitly, if the frame count exceeds 500,000 cells a ValueError is raised before the collect. Pass max_rows=<n>, sample_seed=<int>, or dissolve_by=<col> to proceed.
Without dissolve_by, each row represents one cell (useful for per-cell tooltips in .explore()). With dissolve_by, rows are merged per group into a single union footprint — far fewer geometries for large cell sets:
from databricks.labs.gbx.vizx import cells_as_gdf
# Per-cell choropleth:
gdf = cells_as_gdf(df_cells, cell_col="cellid", extra_cols=["count"])
gdf.explore(column="count") # choropleth (needs mapclassify, bundled in [vizx])
# Dissolve to one footprint polygon per category — uses product ST on DBR,
# falls back to geopandas on local Spark (dissolve_engine="auto" by default):
gdf_dissolved = cells_as_gdf(
df_cells,
cell_col="cellid",
extra_cols=["category"],
dissolve_by="category",
)
gdf_dissolved.explore()
# Force geopandas dissolve (no product functions required):
gdf_local = cells_as_gdf(
df_cells,
extra_cols=["category"],
dissolve_by="category",
dissolve_engine="geopandas",
)
grid_as_gdf
grid_as_gdf(grid, srid=None)
Convert a grid spec — as returned by rst_h3_gridspec — into a 1-row GeoDataFrame of its bounding-box rectangle in EPSG:4326. Compose with cells_as_gdf(...).explore() to overlay the shared canvas boundary over the H3 cells it contains.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
grid | Row, dict | — | A Spark Row or dict with xmin, ymin, xmax, ymax fields — the struct that rst_h3_gridspec returns in its grid field. |
srid | int or None | None | CRS override. Falls back to the grid's own srid field; if both are absent, EPSG:4326 is assumed. When the source CRS is not 4326, the bounding box is reprojected via pyproj. |
Optional metadata fields pixel_size, width, and height are carried through if present on the input.
from databricks.labs.gbx.vizx import cells_as_gdf, grid_as_gdf
# grid_row is the 'grid' field from an rst_h3_gridspec result
grid_gdf = grid_as_gdf(grid_row)
cells_gdf = cells_as_gdf(df_cells, cell_col="cellid", extra_cols=["count"])
# Compose: H3 cells as a choropleth with the canvas boundary drawn on top
m = cells_gdf.explore(column="count")
grid_gdf.explore(m=m, color="black", style_kwds={"fill": False})
See the H3 rasterize notebook for a worked example pairing grid_as_gdf with cells_as_gdf to build a shared-canvas overlay.