Function Reference
Overview
VectorX augments the product's native ST_* functions with vector-tile encoding, TIN surface modeling, authority-string CRS handling, antimeridian normalization, distributed-shapely geometry validity, cleaning, and coverage-validity, and legacy-geometry migration.
Key Features
As of v0.5.0 it covers:
- Vector tile encoding —
gbx_st_asmvtaggregator +gbx_st_asmvt_pyramidgenerator for publishing Mapbox Vector Tile (MVT) layers, available in both the lightweight (pyvx) and heavyweight (vectorx) tiers - TIN surface modeling —
gbx_st_triangulate,gbx_st_interpolateelevationbbox, andgbx_st_interpolateelevationgeomfor Delaunay triangulation and grid elevation interpolation from Z-valued points, in both tiers (with aconstrained/conformingmode selector) - CRS strings —
gbx_st_crs,gbx_st_setcrs, andgbx_st_transformcrsfor reading, stamping, and reprojecting a geometry's coordinate reference system by CRS string (ESRI codes, WKT, PROJ4 — not just an EPSG integer), in both tiers - Geometry validity —
gbx_st_makevalidandgbx_st_explainvalidityfor repairing and diagnosing OGC-invalid geometries, lightweight tier only (pyvx) - Coverage validity —
gbx_st_coverageisvalid,gbx_st_coverageinvalidedges(SQL grouped-aggregates), andcoverage_simplify(Python-API helper) for validating and simplifying polygon coverages, lightweight tier only (pyvx) - Geometry cleaning —
gbx_st_simplifypreservetopology,gbx_st_removerepeatedpoints,gbx_st_reduceprecision,gbx_st_node, andgbx_st_snapfor topology-safe simplification, vertex deduplication, precision reduction, linework noding, and feature snapping, lightweight tier only (pyvx) - Antimeridian handling —
gbx_st_shiftlongitude,gbx_st_wrapx, andgbx_st_splitfor normalizing geometries that cross the 180° antimeridian, lightweight tier only (pyvx) - OGR-based vector readers — Shapefile, GeoJSON, GeoPackage, FileGDB (heavyweight only)
- Legacy Mosaic conversion —
gbx_st_legacyaswkbfor migrating geometries written by DBLabs Mosaic, in both tiers
- Import paths — vector tile encoding:
databricks.labs.gbx.pyvx(lightweight) ·databricks.labs.gbx.vectorxPython /com.databricks.labs.gbx.vectorxScala (heavyweight). Legacy Mosaic conversion:databricks.labs.gbx.vectorx.jts.legacy(Python) /com.databricks.labs.gbx.vectorx.jts.legacy(Scala). - SQL examples — examples use SQL (and Python where shown); in SQL, VectorX functions are prefixed with
gbx_(e.g.gbx_st_legacyaswkb). For SQL, Python, and Scala usage patterns, see Language Bindings. - Geometry input encodings — every
gbx_st_*geometry input accepts WKB, EWKB, WKT, and EWKT interchangeably. WKB/WKT carry no SRID; EWKB/EWKT carry one. Pass whichever encoding your upstream produces — no separate conversion step is required.
Tier availability
| Function | Lightweight (pyvx) | Heavyweight (vectorx) |
|---|---|---|
st_asmvt | Supported | Supported |
st_asmvt_pyramid | Supported | Supported |
st_triangulate | Supported (constrained) | Supported (constrained + conforming) |
st_interpolateelevationbbox | Supported (constrained) | Supported (constrained + conforming) |
st_interpolateelevationgeom | Supported (constrained) | Supported (constrained + conforming) |
st_crs | Supported | Supported |
st_setcrs | Supported | Supported |
st_transformcrs | Supported | Supported |
st_legacyaswkb | Supported | Supported |
st_makevalid | Supported | — |
st_explainvalidity | Supported | — |
st_coverageisvalid | Supported | — |
st_coverageinvalidedges | Supported | — |
coverage_simplify | Supported (Python API) | — |
st_simplifypreservetopology | Supported | — |
st_removerepeatedpoints | Supported | — |
st_reduceprecision | Supported | — |
st_node | Supported | — |
st_snap | Supported | — |
st_shiftlongitude | Supported | — |
st_wrapx | Supported | — |
st_split | Supported | — |
Setup
With GeoBrix already installed, register VectorX in your session before running any example. Both tiers alias the module as vx, so every example below is identical regardless of tier — only this import line differs.
- Lightweight (pyvx)
- Heavyweight (vectorx)
from databricks.labs.gbx.pyvx import functions as vx
vx.register(spark)
from databricks.labs.gbx.vectorx import functions as vx
vx.register(spark)
For the legacy conversion function (st_legacyaswkb), use:
from databricks.labs.gbx.vectorx.jts.legacy import functions as vx
vx.register(spark)
The examples on this page read from four canonical DataFrames, one per function family. Each is available as a temp view for SQL examples. Point the placeholders at your own data to reproduce the examples:
| View / DataFrame | Schema | Backed by | Backs |
|---|---|---|---|
tin_survey | pts ARRAY<BINARY>, bl ARRAY<BINARY> | 4 WKB POINT Z forming a 10×10 m square (elevations 0, 0, 10, 5 m) | TIN examples: st_triangulate, st_interpolateelevationbbox, st_interpolateelevationgeom |
mvt_features | z INT, x INT, y INT, geom_wkb BINARY, attrs STRUCT<name,id> | 2 tile-local WKB POINTs in tile (z=0, x=0, y=0) | Vector-tile examples: st_asmvt, st_asmvt_pyramid |
vector_geoms | geom STRING | EWKT literal 'SRID=4326;POINT (13 42)' | CRS examples: st_crs, st_setcrs, st_transformcrs |
legacy_geoms | geom_legacy STRUCT<typeId,srid,boundaries,holes> | Legacy Mosaic struct for POINT(13, 42) | Migration: st_legacyaswkb |
All four views are built from inline literals in the doc-test fixture helpers (no external files or /Volumes dependency, so the examples run anywhere): WKB POINT Z mass points for tin_survey, tile-local WKB points for mvt_features, an EWKT string for vector_geoms, and a legacy Mosaic struct for legacy_geoms.
Examples — Conventions
How to read the four tabs
Every function on this page shows one example, expressed identically across four tabs:
| Tab | Tier | Badge |
|---|---|---|
| SQL | Both (default) | — |
| Python (light) | pyvx lightweight tier | — |
| Python (heavy) | vectorx heavyweight tier | Blue |
| Scala | vectorx heavyweight tier | Blue |
All four tabs operate on the same input fixture with the same arguments. Where a genuine tier difference exists — a diverging output schema or a mode available only in one tier — the affected tab carries a labeled :::note. A difference without a label is a documentation error.
In each Python example, df = spark.table("<view>") or an equivalent inline spark.sql(...) call loads the canonical fixture. Each SQL example reads FROM <view> or uses an inline CTE — no separate CREATE TEMP VIEW step is shown.
Output representation
The output cells in every function table follow a uniform convention so readers can compare tabs at a glance without decoding byte strings.
Binary geometry ([E]WKB) — geometry returned as BINARY is elided with one token and a format annotation. Use (WKB binary) when the output carries no embedded SRID; (EWKB binary) when an SRID is embedded:
... (WKB binary)
... (EWKB binary)
The WKB bytes are always the canonical output. Decode with ST_GeomFromWKB, ST_GeomFromEWKB, or any ISO WKB reader.
MVT bytes — st_asmvt and st_asmvt_pyramid return Mapbox Vector Tile protobufs (BINARY). Output is shown as:
... (MVT binary)
WKT / EWKT strings — short strings are shown in full; longer strings are truncated with a type annotation:
POINT (13 42)
MULTIPOLYGON (((... (WKT)
CRS strings — short authority strings (EPSG:4326, ESRI:54008) are shown in full; long WKT CRS definitions are truncated:
PROJCS["British National Grid", ...] (CRS)
Cell width — output cells are capped at approximately 60 characters. A longer value is truncated with ... and annotated with its type.
Identical-across-tier values — when all four tabs produce the same result (e.g. EPSG:4326 from st_crs), each tab shows that value identically with the same annotation. Genuine tier differences are called out in a labeled :::note.
CRS-family functions return BINARY
gbx_st_setcrs and gbx_st_transformcrs return BINARY (WKB or EWKB) in all input encodings and both tiers — a STRING (WKT/EWKT) input still yields BINARY output. Only gbx_st_crs returns STRING. The clickable pointer to the CRS contract: Coordinate Reference Systems.
Light tab for light-only UDTF generators
gbx_st_asmvt_pyramid, gbx_st_triangulate, gbx_st_interpolateelevationbbox, and gbx_st_interpolateelevationgeom are Python UDTFs in the lightweight tier — they have no Python DataFrame Column form. The Python (light) tab for these functions shows:
spark.sql("SELECT t.* FROM <view>, LATERAL gbx_<fn>(...) t")
This is the same invocation as the SQL tab, driven via Python. SQL LATERAL works for both tiers; the Python DataFrame Column form (.select(vx.fn(...))) is heavyweight-only for these generators.
Vector tile output
Encode features into Mapbox Vector Tile (MVT) protobufs. Pair the per-tile MVT bytes with gbx_pmtiles_agg or the PMTiles writer to publish a vector pyramid as a single .pmtiles archive targeting MapLibre, deck.gl, Mapbox GL JS, or Felt.
Both tiers expose identical st_asmvt / st_asmvt_pyramid names and identical output schemas. st_asmvt is swap-compatible across both the SQL and Python DataFrame APIs. st_asmvt_pyramid is interchangeable at the SQL level (invoked via LATERAL); its Python DataFrame Column form is heavy-only, because the lightweight pyramid is a Python UDTF, which SQL LATERAL calls but the DataFrame API does not expose. The one-line swap:
# Lightweight (pyvx) — Serverless-safe, no JAR
from databricks.labs.gbx.pyvx import functions as vx
# Heavyweight (vectorx) — classic x86 cluster, JAR required
from databricks.labs.gbx.vectorx import functions as vx
# Everything below is identical in both tiers:
vx.register(spark)
Options
st_asmvt takes a layer_name argument (plain string or Column). st_asmvt_pyramid additionally accepts:
| Argument | Default | Description |
|---|---|---|
layer_name | "layer" | MVT layer name embedded in the protobuf. |
extent | 4096 | MVT tile extent in pixels (MVT v2 standard). |
Compute compatibility
| Aspect | Lightweight (pyvx) | Heavyweight (vectorx) |
|---|---|---|
| Install | Volume-staged [light_env6] wheel (install) | Init script + JAR |
| Serverless / shared / ARM | Supported | Not supported |
| Lakeflow declarative pipelines | Supported | Not supported |
| Execution model | Python UDTF / pandas UDF | JVM (Scala + Spark columnar) |
| JVM access | None — spark.udf.register only | Required |
Native attribute typing
Both tiers encode MVT feature attributes with native protobuf value types:
- Integer / Long → int64 value
- Float / Double → double value
- Boolean → bool value
- String (and anything else) → string value
This means downstream clients (MapLibre GL JS, Mapbox GL JS, deck.gl) receive numbers as numbers and booleans as booleans — enabling numeric data-driven styles, filter expressions, and arithmetic without a client-side parseFloat call.
st_asmvt
LightweightHeavyweight Grouped-agg UDFAggregator that encodes a group of features into a single MVT protobuf blob for one
(z, x, y) tile. Each groupBy(z, x, y).agg(vx.st_asmvt(...)) call produces the
MVT bytes for exactly one tile. Both tiers expose the same grouped-aggregate API and
produce identical output bytes — a one-line swap between pyvx and vectorx.
Signature: st_asmvt(geom, attrs, layer_name) → BINARY
Parameters:
geom(BINARY) — Feature geometry (WKB, EWKB, WKT, or EWKT) in tile-local coordinates (pixel space,0..extent). Clip and project each feature to the tile coordinate system upstream before calling this aggregator.attrs(STRUCT<...>) — Per-feature attribute struct. Integer, float, boolean, and string fields are encoded with native MVT protobuf value types.layer_name(STRINGorstr) — MVT layer name. Pass a plain Python string or a Column.
Returns: BINARY — the MVT protobuf for one tile layer. Feed directly into
gbx_pmtiles_agg or the PMTiles Writer.
Composability: The BINARY output is the natural input to gbx_pmtiles_agg for packaging
multiple (z, x, y) tiles into a single PMTiles file.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT z, x, y, gbx_st_asmvt(geom_wkb, attrs, 'layer') AS mvt
FROM mvt_features
GROUP BY z, x, y;
+-+-+-+--------+
|z|x|y|mvt |
+-+-+-+--------+
|0|0|0|[binary]|
+-+-+-+--------+
... (MVT binary)
from pyspark.sql import functions as f
from databricks.labs.gbx.pyvx import functions as vx
df = spark.table("mvt_features")
result = df.groupBy("z", "x", "y").agg(
vx.st_asmvt("geom_wkb", f.col("attrs"), "layer").alias("mvt")
)
row = result.first()
+---+---+---+---------+
| z| x| y| mvt|
+---+---+---+---------+
| 0| 0| 0|[binary] |
+---+---+---+---------+
... (MVT binary)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("mvt_features")
result = df.groupBy("z", "x", "y").agg(
vx.st_asmvt(f.col("geom_wkb"), f.col("attrs"), f.lit("layer")).alias("mvt")
)
row = result.first()
+---+---+---+---------+
| z| x| y| mvt|
+---+---+---+---------+
| 0| 0| 0|[binary] |
+---+---+---+---------+
... (MVT binary)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("mvt_features")
val result = df.groupBy("z", "x", "y").agg(
vx.st_asmvt(col("geom_wkb"), col("attrs"), lit("layer")).alias("mvt")
)
result.first().getAs[Array[Byte]]("mvt")
+---+---+---+---------+
| z| x| y| mvt|
+---+---+---+---------+
| 0| 0| 0|[binary] |
+---+---+---+---------+
... (MVT binary)
st_asmvt_pyramid
LightweightHeavyweight Streaming UDTFGenerator that explodes one input feature into one row per intersecting (z, x, y) tile
across a zoom range, with MVT bytes already encoded in each row. The per-tile clip and
coordinate transform happen inside the function — no upstream ST_Intersection is needed.
Input geometry must be in EPSG:4326 lon/lat. The function handles per-tile projection
internally. max_z ≤ 20; total tile count across the zoom range capped at 10⁶.
Signature: gbx_st_asmvt_pyramid(geom, attrs, min_z, max_z, [layer_name], [extent])
Parameters:
geom(BINARY) — Feature geometry (WKB, EWKB, WKT, or EWKT) in EPSG:4326 lon/lat.attrs(STRUCT<...>) — Per-feature attributes. Same native-typed encoding asst_asmvt.min_z,max_z(INT) — Inclusive zoom range (0..20).layer_name(STRING, optional) — MVT layer name; defaults to"layer".extent(INT, optional) — MVT tile extent in pixels; defaults to4096.
The lightweight tier is a Python UDTF registered via spark.udtf.register — invoke it with LATERAL (SQL standard table-function syntax):
FROM features, LATERAL gbx_st_asmvt_pyramid(geom_wkb, attrs, 0, 12, 'layer', 4096) t
-- Output columns: t.z, t.x, t.y, t.mvt_bytes (direct)
The heavyweight tier is a JVM generator expression — invoke it with LATERAL VIEW (Hive-style):
FROM features LATERAL VIEW gbx_st_asmvt_pyramid(geom_wkb, attrs, 0, 12, 'layer', 4096) t AS tile
-- Output column: t.tile.z, t.tile.x, t.tile.y, t.tile.mvt_bytes (struct wrapper)
The Python DataFrame Column form (vx.st_asmvt_pyramid(col(...), ...)) is heavyweight-only; the lightweight UDTF has no Column API.
Full pipeline — vector pyramid to PMTiles (lightweight):
from databricks.labs.gbx.pyvx import functions as vx
from databricks.labs.gbx.pmtiles import functions as px
from pyspark.sql import functions as F
vx.register(spark)
px.register(spark)
# Step 1: explode features → per-tile MVT rows (distributed; LATERAL in SQL)
tiles_df = spark.sql("""
SELECT t.*
FROM features,
LATERAL gbx_st_asmvt_pyramid(geom_wkb, struct(name, id), 0, 10, 'roads', 4096) t
""")
# Step 2: aggregate the per-tile MVT bytes → single PMTiles archive
pmt_bytes = (
tiles_df.agg(
px.pmtiles_agg(
F.col("mvt_bytes"), F.col("z"), F.col("x"), F.col("y"),
'{"name":"roads","attribution":"© My Data"}',
).alias("pmt")
)
.collect()[0]["pmt"]
)
with open("/tmp/roads.pmtiles", "wb") as fh:
fh.write(pmt_bytes)
For larger pyramids that exceed the Spark cell limit, use the PMTiles Writer
(pmtiles_gbx for the lightweight tier) instead of pmtiles_agg.
- SQL
- Python (light)
- Python (heavy)
- Scala
WITH feats AS (
SELECT unhex('010100000000000000000000000000000000000000') AS geom_wkb,
named_struct('name', 'origin', 'id', 1L) AS attrs
)
SELECT t.tile.z AS z, t.tile.x AS x, t.tile.y AS y, t.tile.mvt_bytes AS mvt_bytes
FROM feats
LATERAL VIEW gbx_st_asmvt_pyramid(geom_wkb, attrs, 0, 2, 'layer', 4096) t AS tile;
+-+-+-+---------+
|z|x|y|mvt_bytes|
+-+-+-+---------+
|0|0|0|[binary] |
|1|1|1|[binary] |
|2|2|2|[binary] |
+-+-+-+---------+
... (MVT binary — one row per intersecting tile across zoom levels 0–2)
result = spark.sql(f"""
WITH feats AS (
SELECT unhex('{_WKB_POINT_0_0}') AS geom_wkb,
named_struct('name', 'origin', 'id', 1L) AS attrs
)
SELECT t.*
FROM feats, LATERAL gbx_st_asmvt_pyramid(geom_wkb, attrs, 0, 2, 'layer', 4096) t
""")
rows = result.collect()
+---+---+---+-----------+
| z| x| y| mvt_bytes|
+---+---+---+-----------+
| 0| 0| 0| [binary] |
| 1| 1| 1| [binary] |
| 2| 2| 2| [binary] |
+---+---+---+-----------+
... (MVT binary — one row per intersecting tile across zoom levels 0–2)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.sql(f"""
SELECT unhex('{_WKB_POINT_0_0}') AS geom_wkb,
named_struct('name', 'origin', 'id', 1L) AS attrs
""")
result = df.select(
vx.st_asmvt_pyramid(
f.col("geom_wkb"), f.col("attrs"), f.lit(0), f.lit(2), f.lit("layer")
).alias("t")
).selectExpr("t.z AS z", "t.x AS x", "t.y AS y", "t.mvt_bytes AS mvt_bytes")
rows = result.collect()
+---+---+---+-----------+
| z| x| y| mvt_bytes|
+---+---+---+-----------+
| 0| 0| 0| [binary] |
| 1| 1| 1| [binary] |
| 2| 2| 2| [binary] |
+---+---+---+-----------+
... (MVT binary — one row per intersecting tile across zoom levels 0–2)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.sql(
"SELECT unhex('010100000000000000000000000000000000000000') AS geom_wkb, " +
"named_struct('name', 'origin', 'id', 1L) AS attrs"
)
val result = df.select(
vx.st_asmvt_pyramid(col("geom_wkb"), col("attrs"), lit(0), lit(2), lit("layer")).alias("t")
).selectExpr("t.z AS z", "t.x AS x", "t.y AS y", "t.mvt_bytes AS mvt_bytes")
result.show()
+---+---+---+-----------+
| z| x| y| mvt_bytes|
+---+---+---+-----------+
| 0| 0| 0| [binary] |
| 1| 1| 1| [binary] |
| 2| 2| 2| [binary] |
+---+---+---+-----------+
... (MVT binary — one row per intersecting tile across zoom levels 0–2)
Triangulation and elevation
These generators build a Delaunay triangulated irregular network (TIN) from Z-valued mass points and optional breaklines, then either expose the triangles directly or sample the surface on a regular grid to produce elevation points. Useful for surface modeling, DTM/DEM derivation, and elevation sampling from survey point clouds. All three are available in both tiers; breaklines are honored in both.
Triangulation modes: constrained vs conforming
Each TIN function takes a trailing mode argument (default 'constrained'):
| Mode | Tiers | Behavior |
|---|---|---|
constrained (default) | Lightweight and heavyweight | Constrained Delaunay triangulation. Breaklines are honored as forced edges with no Steiner points — the output vertex set is exactly your input mass points plus breakline vertices. Identical algorithm in both tiers, so the result is a seamless cross-tier swap. |
conforming | Heavyweight only | JTS conforming-Delaunay triangulation: the mesh may insert additional Steiner points along breakline segments to satisfy the Delaunay property near constraints. Produces a smoother mesh around dense breaklines at the cost of extra vertices. |
The lightweight tier raises NotImplementedError on mode='conforming' — it has no Steiner-point refinement. This is a documented, intentional divergence, analogous to the H3 covering note elsewhere in these docs: the two tiers agree exactly in the default (constrained) mode, and the heavyweight tier offers conforming as a deliberate opt-in superset. If you need a result that is byte-identical across tiers, stay on constrained (the default).
In the lightweight tier these TIN generators are PySpark UDTFs with no Python DataFrame Column form — invoke them via SQL LATERAL (e.g. ... , LATERAL gbx_st_triangulate(...) t), the same pattern as gbx_st_asmvt_pyramid. In the heavyweight tier they are exposed as generator Columns (usable in select(...)) and via SQL LATERAL VIEW. SQL LATERAL works for both tiers; the Python DataFrame Column form is heavyweight-only for these generators.
st_interpolateelevationbbox
LightweightHeavyweight Streaming UDTFBuilds a TIN from mass points and breaklines, then samples elevation on a regular pixel grid covering an explicit bounding box. Use this when you already know the output extent in absolute coordinates — for example, when snapping to a fixed tile extent or aligning with a raster grid.
Signature: gbx_st_interpolateelevationbbox(points_array, breaklines_array, merge_tolerance, snap_tolerance, split_point_finder, xmin, ymin, xmax, ymax, width_px, height_px, srid, [mode])
Parameters:
points_array— Mass-point geometries with Z values. Accepts WKB/EWKB/WKT/EWKT.breaklines_array— Breakline geometries (or an empty array).merge_tolerance(DOUBLE) — Merge distance for coincident points.snap_tolerance(DOUBLE) — Snap distance to breakline vertices.split_point_finder(STRING) — Conforming-mesh strategy (e.g.'NONENCROACHING').xmin,ymin,xmax,ymax(DOUBLE) — Bounding box corners in the coordinate reference system given bysrid.width_px,height_px(INT) — Number of grid columns and rows. Together with the bbox dimensions these determine the cell size.srid(INT) — EPSG code of the bounding box coordinates (e.g.27700for British National Grid).mode(STRING, optional) —'constrained'(default, both tiers) or'conforming'(heavyweight only).
Generator: Emits one row per in-hull grid cell (cells whose centers fall outside the TIN convex hull are dropped). The output schema column is elevation_point (BINARY WKB POINT Z). Use with SQL LATERAL to materialize the grid.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT t.elevation_point
FROM tin_survey
LATERAL VIEW gbx_st_interpolateelevationbbox(pts, bl, 0, 0, 'NONENCROACHING', 0, 0, 10, 10, 3, 3, 0, 'constrained') t AS elevation_point
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 elevation grid, 9 rows)
result = spark.sql("""
SELECT t.elevation_point
FROM tin_survey, LATERAL gbx_st_interpolateelevationbbox(pts, bl, 0, 0, 'NONENCROACHING', 0, 0, 10, 10, 3, 3, 0, 'constrained') t
""")
rows = result.collect()
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 elevation grid, 9 rows)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("tin_survey")
result = df.select(
vx.st_interpolateelevationbbox(
f.col("pts"),
f.col("bl"),
f.lit(0),
f.lit(0),
f.lit("NONENCROACHING"),
f.lit(0),
f.lit(0),
f.lit(10),
f.lit(10),
f.lit(3),
f.lit(3),
f.lit(0),
"constrained",
).alias("elevation_point")
)
rows = result.collect()
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 elevation grid, 9 rows)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("tin_survey")
val result = df.select(
vx.st_interpolateelevationbbox(
col("pts"), col("bl"),
lit(0), lit(0), lit("NONENCROACHING"),
lit(0), lit(0), lit(10), lit(10),
lit(3), lit(3), lit(0),
"constrained"
).alias("elevation_point")
)
result.show()
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 elevation grid, 9 rows)
st_interpolateelevationgeom
LightweightHeavyweight Streaming UDTFBuilds a TIN from mass points and breaklines, then samples elevation on a regular grid anchored to a geometry origin with explicit cell sizes. Use this when the grid must be defined relative to a known point — for example, when the grid origin comes from data (a survey control point) or when different rows need different grid placements.
Signature: gbx_st_interpolateelevationgeom(points_array, breaklines_array, merge_tolerance, snap_tolerance, split_point_finder, grid_origin, grid_cols, grid_rows, cell_size_x, cell_size_y, [mode])
Parameters:
points_array— Mass-point geometries with Z values. Accepts WKB/EWKB/WKT/EWKT.breaklines_array— Breakline geometries (or an empty array).merge_tolerance(DOUBLE) — Merge distance for coincident points.snap_tolerance(DOUBLE) — Snap distance to breakline vertices.split_point_finder(STRING) — Conforming-mesh strategy (e.g.'NONENCROACHING').grid_origin— POINT geometry anchoring the top-left corner of the output grid. The output SRID is inherited from this geometry (encode as EWKB/EWKT to carry a non-zero SRID) — no separatesridargument.grid_cols,grid_rows(INT) — Number of grid columns and rows.cell_size_x(DOUBLE) — Horizontal cell size in the geometry's units (positive steps right).cell_size_y(DOUBLE) — Vertical cell size in the geometry's units. Pass a negative value to step downward (standard raster convention, e.g.-10.0for 10-unit cells stepping south).mode(STRING, optional) —'constrained'(default, both tiers) or'conforming'(heavyweight only).
Generator: Emits one row per in-hull grid cell. The output schema column is elevation_point (BINARY WKB POINT Z). Use with SQL LATERAL to materialize the grid.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT t.elevation_point
FROM tin_survey
LATERAL VIEW gbx_st_interpolateelevationgeom(pts, bl, 0, 0, 'NONENCROACHING', unhex('010100000000000000000000000000000000002440'), 3, 3, 3, -3, 'constrained') t AS elevation_point
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 origin-anchored grid, 9 rows)
result = spark.sql(f"""
SELECT t.elevation_point
FROM tin_survey, LATERAL gbx_st_interpolateelevationgeom(pts, bl, 0, 0, 'NONENCROACHING', unhex('{_WKB_POINT_0_10}'), 3, 3, 3, -3, 'constrained') t
""")
rows = result.collect()
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 origin-anchored grid, 9 rows)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("tin_survey").withColumn(
"origin", f.expr(f"unhex('{_WKB_POINT_0_10}')")
)
result = df.select(
vx.st_interpolateelevationgeom(
f.col("pts"),
f.col("bl"),
f.lit(0),
f.lit(0),
f.lit("NONENCROACHING"),
f.col("origin"),
f.lit(3),
f.lit(3),
f.lit(3),
f.lit(-3),
"constrained",
).alias("elevation_point")
)
rows = result.collect()
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 origin-anchored grid, 9 rows)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val origin = expr("unhex('010100000000000000000000000000000000002440')")
val df = spark.table("tin_survey")
val result = df.select(
vx.st_interpolateelevationgeom(
col("pts"), col("bl"),
lit(0), lit(0), lit("NONENCROACHING"),
origin, lit(3), lit(3), lit(3), lit(-3),
"constrained"
).alias("elevation_point")
)
result.show()
+---------------+
|elevation_point|
+---------------+
|[binary] |
|[binary] |
|... |
+---------------+
... (WKB binary — POINT Z geometries, 3×3 origin-anchored grid, 9 rows)
st_triangulate
LightweightHeavyweight Streaming UDTFBuilds a Delaunay TIN from mass-point geometries (with Z values) and optional breakline geometries, emitting one triangle polygon per row. Use this when you need the raw triangulation — e.g., to inspect mesh quality, clip triangles to an area of interest, or feed a custom sampler.
Signature: gbx_st_triangulate(points_array, breaklines_array, merge_tolerance, snap_tolerance, split_point_finder, [mode])
Parameters:
points_array— Array column of point geometries with Z values (the mass points that define the surface). Accepts WKB/EWKB/WKT/EWKT.breaklines_array— Array column of linestring geometries that the mesh must honor as edges (e.g., ridge lines, drainage channels). Pass an empty array if no breaklines are needed.merge_tolerance(DOUBLE) — Distance below which coincident points are merged before triangulation.snap_tolerance(DOUBLE) — Distance within which points are snapped to breakline vertices.split_point_finder(STRING) — Conforming-mesh refinement strategy. Use'NONENCROACHING'for a mesh that avoids encroaching on breakline segments;'MIDPOINT'is also valid (heavyweightconformingmode).mode(STRING, optional) —'constrained'(default, both tiers) or'conforming'(heavyweight only). See Triangulation modes above.
Generator: Emits one row per output triangle. Use with SQL LATERAL to materialize the triangles; the output schema column is triangle (BINARY WKB polygon).
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT t.triangle
FROM tin_survey
LATERAL VIEW gbx_st_triangulate(pts, bl, 0, 0, 'NONENCROACHING', 'constrained') t AS triangle
+--------+
|triangle|
+--------+
|[binary]|
|[binary]|
+--------+
... (WKB binary — 2 Delaunay triangle polygons)
result = spark.sql("""
SELECT t.triangle
FROM tin_survey, LATERAL gbx_st_triangulate(pts, bl, 0, 0, 'NONENCROACHING', 'constrained') t
""")
rows = result.collect()
+--------+
|triangle|
+--------+
|[binary]|
|[binary]|
+--------+
... (WKB binary — 2 Delaunay triangle polygons)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("tin_survey")
result = df.select(
vx.st_triangulate(
f.col("pts"),
f.col("bl"),
f.lit(0),
f.lit(0),
f.lit("NONENCROACHING"),
"constrained",
).alias("triangle")
)
rows = result.collect()
+--------+
|triangle|
+--------+
|[binary]|
|[binary]|
+--------+
... (WKB binary — 2 Delaunay triangle polygons)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("tin_survey")
val result = df.select(
vx.st_triangulate(col("pts"), col("bl"), lit(0), lit(0), lit("NONENCROACHING"), "constrained").alias("triangle")
)
result.show()
+--------+
|triangle|
+--------+
|[binary]|
|[binary]|
+--------+
... (WKB binary — 2 Delaunay triangle polygons)
mode='conforming' is heavyweight-onlyThe 'constrained' mode (default) is available in both tiers. The 'conforming' mode — which inserts Steiner points along breakline segments for a smoother mesh — is heavyweight-only: the lightweight pyvx tier raises NotImplementedError if you pass mode='conforming'. The examples on all tabs use the default 'constrained' mode.
Coordinate reference systems
Read, stamp, and reproject a geometry's CRS by CRS string — an ESRI code, a WKT definition, or a PROJ4 string, not only an EPSG integer. These complement the product's built-in ST_SRID / ST_SetSRID / ST_Transform, which take an integer SRID: use the built-ins when an EPSG code is all you need, and these when the CRS can only be named as a string. See Coordinate Reference Systems for the SRID-vs-CRS-string model shared with RasterX.
All three are available in both tiers under the same names, so a CRS query is a one-line tier swap.
Shared contracts
Geometry input — every geometry argument accepts WKB, EWKB, WKT, and EWKT. WKB/WKT carry no SRID; EWKB/EWKT carry one.
SQL output is always BINARY. gbx_st_setcrs and gbx_st_transformcrs return BINARY (WKB/EWKB) whichever encoding the geometry argument arrived in — a STRING geometry input still yields BINARY. One function has one declared return type: an input-dependent return type cannot be used in a view or any fixed schema, and WKB is how the rest of gbx_st_* and the built-in ST_* functions exchange geometries. To read a CRS back as text, wrap the result in gbx_st_crs; to hand it to a built-in, ST_GeomFromWKB accepts it directly. gbx_st_crs returns STRING.
Errors are the exception, not the rule. These functions degrade rather than fail a whole column:
| Situation | Result |
|---|---|
| NULL geometry | NULL |
NULL target_crs | NULL |
Geometry has no resolvable source CRS (plain WKB/WKT, no source_crs) | input returned unchanged |
Embedded SRID is in no registry (e.g. 999999) | input returned unchanged |
source_crs cannot be parsed | input returned unchanged |
target_crs cannot be parsed | raises |
gbx_st_setcrs given a CRS with no integer authority code | raises |
Z coordinates. A geometry whose vertices all carry a finite Z keeps its Z. A genuinely 2D geometry stays 2D — no Z ordinate is invented. For a geometry where only some vertices carry a Z, the current behavior differs between the two operations, because only one of them touches coordinates:
gbx_st_transformcrsreprojects it as 2D. Reprojecting a missing Z propagates it into X and Y and destroys the horizontal position, so dropping the Z is what keeps every X/Y correct.gbx_st_setcrskeeps the partial Z as-is, since stamping an SRID never moves coordinates.
A missing Z is never filled in with a substitute value such as 0, which would be indistinguishable from a surveyed elevation downstream. Both tiers behave identically here, in every input encoding.
These apply in every input encoding and on both tiers — they are properties of the operations, not of how you pass the geometry.
- Chaining
setcrs→transformcrson a partial-Z geometry yields a 2D result, for the reason described just above: the reproject drops a partial Z. So a geometry that entered the chain with some elevations leaves it with none. - Reprojecting out and back is not bit-exact. A round trip such as
EPSG:4326→EPSG:32633→EPSG:4326returns11.000000000000002where it started at11— a floating-point artifact of the projection math, in the last decimal place or two. It does not compound meaningfully: further round trips stay in that same last-place range rather than drifting away. Compare reprojected coordinates with a tolerance, never for exact equality. st_setcrsrelabels without reprojecting, so stamping a CRS whose units do not match the coordinates leaves the geometry mislabelled. A laterst_transformcrsthen transforms from the wrong CRS: projected metres taggedEPSG:4326come back asInfinitycoordinates on the lightweight tier, and raise a projection error on the heavyweight tier. Usest_transformcrswhen you want the coordinates moved.- Coordinates outside the target CRS's valid domain behave the same way — for example a latitude of
100, which does not exist. The lightweight tier returnsInfinitycoordinates; the heavyweight tier raises a projection error. Filter to the target CRS's area of use before reprojecting if your input may contain out-of-range coordinates. - M (measure) values are dropped. Both tiers carry X, Y and Z only, so a
ZMgeometry comes back asZwith the measure gone, and a geometry carrying M but no Z comes back plain 2D — noZis invented to fill the slot. This is worth noting because the product's own geometry type does persist M.
st_crs
LightweightHeavyweightReturns the canonical CRS string for the geometry's embedded SRID, or NULL.
Signature: gbx_st_crs(geom)
Parameters:
geom— Geometry. Accepts WKB/EWKB/WKT/EWKT.
Returns: STRING — the authority string ('EPSG:4326', 'ESRI:54008', …), or NULL for a plain WKB/WKT geometry with no embedded SRID, a NULL input, or an SRID in no known registry.
An SRID is classified against the authoritative PROJ registries, so an ESRI-range code comes back as ESRI:<n> rather than being mislabelled EPSG:<n>.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_crs(geom) AS crs FROM vector_geoms;
+---------+
|crs |
+---------+
|EPSG:4326|
+---------+
from databricks.labs.gbx.pyvx import functions as vx
df = spark.table("vector_geoms")
result = df.select(vx.st_crs("geom").alias("crs")).first()
+---------+
|crs |
+---------+
|EPSG:4326|
+---------+
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("vector_geoms")
result = df.select(vx.st_crs(f.col("geom")).alias("crs")).first()
+---------+
|crs |
+---------+
|EPSG:4326|
+---------+
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("vector_geoms")
val result = df.select(vx.st_crs(col("geom")).alias("crs")).first()
result.getString(0)
+---------+
|crs |
+---------+
|EPSG:4326|
+---------+
st_setcrs
LightweightHeavyweightStamps a CRS on a geometry without reprojecting — it relabels, it does not move coordinates. The counterpart to the built-in ST_SetSRID, taking a CRS string instead of an integer.
Signature: gbx_st_setcrs(geom, crs)
Parameters:
geom— Geometry. Accepts WKB/EWKB/WKT/EWKT.crs(STRING) — Target CRS. An authority string ('EPSG:4326','ESRI:54008') or an int-castable string / integer (32633,'32633'), which behaves likeST_SetSRID(geom, 32633).
Returns: BINARY — EWKB with the new SRID embedded. Coordinate values are preserved exactly, to the last decimal place — no reprojection and no rounding. (The output bytes are not identical to the input's: embedding the SRID is what makes it EWKB.)
Raises when crs has no integer authority code, because a geometry can store only an integer SRID. That covers:
-
authority-less definitions — a raw
PROJCS[...]WKT or a PROJ4 string such as'+proj=utm +zone=33 +datum=WGS84'. PROJ's fuzzy matcher would pair that PROJ4 string withEPSG:32633at partial confidence, but a geometry SRID is an exact identity claim — a guess is never silently written into one. Usegbx_st_transformcrsif you want the coordinates in that CRS.This distinction is not academic: a PROJ4 string that resembles a registry CRS is not necessarily equivalent to it. One that omits a datum shift (
+towgs84=0,0,0,0,0,0,0) can place coordinates hundreds of metres from the EPSG code it superficially matches. GeoBrix treats such a definition as its own CRS throughout — including when selecting the transformation used to reproject — rather than silently substituting the near-match. If you want the registry CRS, name it explicitly ('EPSG:28992'). -
non-numeric authority codes —
'OGC:CRS84','IGNF:LAMB93': real, resolvable CRSes whose code simply is not an integer.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_setcrs(geom, 'EPSG:4326') AS stamped FROM vector_geoms;
+--------+
|stamped |
+--------+
|[binary]|
+--------+
... (EWKB binary — coordinates preserved, SRID=4326 embedded)
from databricks.labs.gbx.pyvx import functions as vx
df = spark.table("vector_geoms")
result = df.select(vx.st_setcrs("geom", "EPSG:4326").alias("stamped")).first()
+---------+
|stamped |
+---------+
|[binary] |
+---------+
... (EWKB binary — coordinates preserved, SRID=4326 embedded)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("vector_geoms")
result = df.select(
vx.st_setcrs(f.col("geom"), "EPSG:4326").alias("stamped")
).first()
+---------+
|stamped |
+---------+
|[binary] |
+---------+
... (EWKB binary — coordinates preserved, SRID=4326 embedded)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("vector_geoms")
val result = df.select(vx.st_setcrs(col("geom"), "EPSG:4326").alias("stamped")).first()
result.getAs[Array[Byte]]("stamped")
+---------+
|stamped |
+---------+
|[binary] |
+---------+
... (EWKB binary — coordinates preserved, SRID=4326 embedded)
st_transformcrs
LightweightHeavyweightReprojects a geometry's coordinates into target_crs. The counterpart to the built-in ST_Transform, taking a CRS string instead of an integer — so an ESRI code, a WKT definition, or a PROJ4 string can be a target.
Signature: gbx_st_transformcrs(geom, target_crs [, source_crs])
Parameters:
geom— Geometry. Accepts WKB/EWKB/WKT/EWKT.target_crs(STRING) — CRS to reproject into: authority string, int-castable string/integer, WKT, or PROJ4.source_crs(STRING, optional) — CRS the input is in. Used only for a plain (SRID-less) geometry; a geometry that carries an embedded SRID ignores this argument, so a mixed column is safe.
Returns: BINARY — the reprojected geometry.
Which SRID comes out follows the target:
target_crs | Example | Output |
|---|---|---|
| Has an integer authority code | 'EPSG:32633', 'ESRI:54008', 32633 | EWKB with that SRID stamped — a plain input is upgraded to carry one |
| Has no integer authority code | raw PROJCS[...] WKT, '+proj=utm +zone=33 …', 'OGC:CRS84' | plain WKB: coordinates reprojected, and the now-stale source SRID cleared — leaving it would label the geometry with a CRS it is no longer in |
Source CRS resolution order: the geometry's embedded SRID first, then source_crs, and if neither resolves the geometry is returned unchanged.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_transformcrs(geom, 'EPSG:32633') AS utm33n FROM vector_geoms;
+--------+
|utm33n |
+--------+
|[binary]|
+--------+
... (EWKB binary — POINT(13, 42) reprojected from EPSG:4326 to EPSG:32633)
from databricks.labs.gbx.pyvx import functions as vx
df = spark.table("vector_geoms")
result = df.select(vx.st_transformcrs("geom", "EPSG:32633").alias("utm33n")).first()
+--------+
|utm33n |
+--------+
|[binary]|
+--------+
... (EWKB binary — POINT(13, 42) reprojected from EPSG:4326 to EPSG:32633)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx import functions as vx
df = spark.table("vector_geoms")
result = df.select(
vx.st_transformcrs(f.col("geom"), "EPSG:32633").alias("utm33n")
).first()
+--------+
|utm33n |
+--------+
|[binary]|
+--------+
... (EWKB binary — POINT(13, 42) reprojected from EPSG:4326 to EPSG:32633)
import com.databricks.labs.gbx.vectorx.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("vector_geoms")
val result = df.select(vx.st_transformcrs(col("geom"), lit("EPSG:32633")).alias("utm33n")).first()
result.getAs[Array[Byte]]("utm33n")
+--------+
|utm33n |
+--------+
|[binary]|
+--------+
... (EWKB binary — POINT(13, 42) reprojected from EPSG:4326 to EPSG:32633)
Geometry validity
Distributed shapely helpers — shapely/GEOS geometry operations running as Python UDFs across your Spark cluster — for diagnosing and repairing OGC-invalid geometries. Lightweight tier only (pyvx); no heavyweight counterpart exists. See Geometry Validity for the full conditional-repair workflow and invalidity reason codes.
st_makevalid
LightweightRepairs a geometry to OGC-SFS validity. The default level='linework' nodes self-intersections and applies orient_polygons so output passes the product's stricter ST_IsValid (topology + orientation). level='structure' uses a more conservative overlay method. level='ogc' applies OGC-only repair without orientation normalization.
Signature: gbx_st_makevalid(geom [, level])
Parameters:
geom— Geometry to repair. Accepts WKB/EWKB/WKT/EWKT.level(optional) — Repair strategy:'linework'(default),'structure', or'ogc'.
Returns: BINARY — WKB (or EWKB if input carried a SRID). The original CRS is preserved. Returns NULL for null or unparseable input.
See Geometry Validity.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_makevalid('POLYGON((0 0,1 1,1 0,0 1,0 0))') AS clean
+--------+
|clean |
+--------+
|[binary]|
+--------+
... (WKB binary — repaired geometry; bowtie becomes a valid multi-polygon)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
bowtie = from_wkt("POLYGON((0 0,1 1,1 0,0 1,0 0))")
df = spark.createDataFrame([(to_wkb(bowtie),)], ["geom"])
+--------+
|clean |
+--------+
|[binary]|
+--------+
... (WKB binary — repaired geometry; bowtie becomes a valid multi-polygon)
Not available in this tier.
Not available in this tier.
st_explainvalidity
LightweightDiagnoses geometry validity and returns a JSON string with four fields: valid (boolean), reason (GEOS reason string), code (stable integer violation class), and location (POINT(x y) WKT string of the violation site). code and location are null when GEOS does not provide them. Use ST_IsValid as a cheap boolean gate and call gbx_st_explainvalidity only on flagged rows.
Signature: gbx_st_explainvalidity(geom)
Parameters:
geom— Geometry to diagnose. Accepts WKB/EWKB/WKT/EWKT.
Returns: STRING — JSON {"valid": ..., "reason": ..., "code": ..., "location": ...}. Returns NULL for null or unparseable input.
See Geometry Validity.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_explainvalidity('POLYGON((0 0,1 1,1 0,0 1,0 0))') AS detail
+--------------------------------------------------------------------+
|detail |
+--------------------------------------------------------------------+
|{"valid": false, "reason": "Self-intersection[0.5 0.5]", "code": 10,|
+--------------------------------------------------------------------+
... (JSON string — {valid, reason, code, location} for SFS validity diagnosis)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
bowtie = from_wkt("POLYGON((0 0,1 1,1 0,0 1,0 0))")
df = spark.createDataFrame([(to_wkb(bowtie),)], ["geom"])
+----------------------------------------------------------------------+
|detail |
+----------------------------------------------------------------------+
|{"valid": false, "reason": "Self-intersection[0.5 0.5]", "code": 10,|
+----------------------------------------------------------------------+
... (JSON string — {valid, reason, code, location} for SFS validity diagnosis)
Not available in this tier.
Not available in this tier.
Coverage validity
Distributed shapely helpers — grouped-aggregate SQL UDFs and a Python DataFrame helper — for validating and simplifying polygon coverages (sets of polygons that share edges and together partition a region). Lightweight tier only (pyvx); no heavyweight counterpart exists. See Coverage Validity for the gap_width semantics, scale-ceiling notes, and the comparison with per-geometry validity.
st_coverageisvalid
Lightweight grouped-aggReturns true when the polygon group forms a valid coverage — no overlaps; no gaps thinner than gap_width. Use with GROUP BY to identify which coverage groups pass.
Signature: gbx_st_coverageisvalid(geom, gap_width)
Parameters:
geom— Geometry column. Accepts WKB/EWKB/WKT/EWKT. All members must be polygon types.gap_width(DOUBLE, required) — Gap tolerance. Overlaps always invalid; gaps narrower than this are also flagged. Pass0.0to detect only overlaps.
Returns: BOOLEAN. NULL when the group is empty.
See Coverage Validity.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT cov_id, gbx_st_coverageisvalid(geom, 0.0) AS is_valid
FROM coverage_parcels
GROUP BY cov_id
+------+--------+
|cov_id|is_valid|
+------+--------+
|1 |true |
+------+--------+
... (BOOLEAN — true when the polygon group has no overlaps and no slivers narrower than gap_width)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import to_wkb
from shapely.geometry import box
left = box(0.0, 0.0, 5.0, 5.0)
right = box(5.0, 0.0, 10.0, 5.0)
df = spark.createDataFrame(
[
(1, to_wkb(left)),
(1, to_wkb(right)),
],
["cov_id", "geom"],
)
result = df.groupBy("cov_id").agg(
vx.st_coverageisvalid("geom", 0.0).alias("is_valid")
)
+------+--------+
|cov_id|is_valid|
+------+--------+
| 1| true|
+------+--------+
... (BOOLEAN — true: the two adjacent squares share a clean edge with no overlap)
Not available in this tier.
Not available in this tier.
st_coverageinvalidedges
Lightweight grouped-aggReturns the union of the boundary segments that violate the coverage as BINARY (WKB/EWKB). Empty geometry when the coverage is clean; non-empty when overlaps or slivers exist. Use the output geometry to highlight problem zones on a map.
Signature: gbx_st_coverageinvalidedges(geom, gap_width)
Parameters:
geom— Geometry column. Accepts WKB/EWKB/WKT/EWKT. All members must be polygon types.gap_width(DOUBLE, required) — Gap tolerance. Same semantics asgbx_st_coverageisvalid.
Returns: BINARY — WKB/EWKB union of invalid edge segments. Empty geometry when clean. NULL when the group is empty.
See Coverage Validity.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT cov_id, gbx_st_coverageinvalidedges(geom, 0.0) AS bad_edges
FROM coverage_overlap
GROUP BY cov_id
+------+---------+
|cov_id|bad_edges|
+------+---------+
|1 |[binary] |
+------+---------+
... (BINARY — union of the invalid edge segments; empty geometry when the coverage is clean)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import to_wkb
from shapely.geometry import box
poly1 = box(0.0, 0.0, 6.0, 6.0)
poly2 = box(4.0, 4.0, 10.0, 10.0)
df = spark.createDataFrame(
[
(1, to_wkb(poly1)),
(1, to_wkb(poly2)),
],
["cov_id", "geom"],
)
result = df.groupBy("cov_id").agg(
vx.st_coverageinvalidedges("geom", 0.0).alias("bad_edges")
)
+------+---------+
|cov_id|bad_edges|
+------+---------+
| 1|[binary] |
+------+---------+
... (BINARY — union of the invalid edge segments; non-empty because the two squares overlap)
Not available in this tier.
Not available in this tier.
coverage_simplify
LightweightPython-API only — no SQL form. Topology-preserving simplification of a whole coverage. Unlike per-row simplification (gbx_st_simplifypreservetopology), this helper simplifies the coverage as a unit so shared edges stay aligned in both adjacent polygons after simplification.
Signature: vx.coverage_simplify(df, group_col, geom_col, tolerance, simplify_boundary=True, out_col="geom_simplified")
Parameters:
df— Input Spark DataFrame.group_col— Column name defining coverage groups (e.g."admin_level").geom_col— Column name of the geometry (WKB/EWKB/WKT/EWKT).tolerance(float) — Simplification tolerance in coordinate units.simplify_boundary(bool, defaultTrue) — Whether to simplify the outer boundary.out_col(str, default"geom_simplified") — Output column name.
Returns: A Spark DataFrame — same rows, all original columns, plus out_col (BINARY). N→N.
from databricks.labs.gbx.pyvx import functions as vx
vx.register(spark)
simplified_df = vx.coverage_simplify(
df, group_col="admin_level", geom_col="geom_wkb", tolerance=50.0
)
See Coverage Validity.
Geometry cleaning
Distributed shapely helpers — shapely/GEOS operations running as Python UDFs — for improving the quality of already-valid geometries. If your geometry is invalid, repair it first with gbx_st_makevalid, then clean it. Lightweight tier only (pyvx); no heavyweight counterpart exists. See Geometry Cleaning for usage guidance and the comparison with the product's built-in simplify.
st_simplifypreservetopology
LightweightSimplifies a geometry using the Douglas-Peucker algorithm with topology preservation (preserve_topology=True). Vertices that deviate less than tolerance from the simplified line are dropped, but the output geometry is guaranteed to be of the same topological class as the input and to remain valid. Use this instead of the product's st_simplify when ring connectivity must be maintained.
Signature: gbx_st_simplifypreservetopology(geom, tolerance)
Parameters:
geom— Geometry to simplify. Accepts WKB/EWKB/WKT/EWKT.tolerance— Simplification tolerance in the geometry's coordinate units.
Returns: BINARY — WKB (or EWKB if input carried a SRID). The original CRS is preserved. Returns NULL for null or unparseable input.
See Geometry Cleaning.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_simplifypreservetopology('POLYGON((0 0,0 5,0.001 8,0 10,10 10,10 0,0 0))', 1.0) AS simplified
+----------+
|simplified|
+----------+
|[binary] |
+----------+
... (WKB binary — simplified polygon with near-collinear vertex removed, topology preserved)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
from pyspark.sql import functions as f
geom = from_wkt("POLYGON((0 0,0 5,0.001 8,0 10,10 10,10 0,0 0))")
df = spark.createDataFrame([(to_wkb(geom),)], ["geom"])
return df.select(
vx.st_simplifypreservetopology("geom", f.lit(1.0)).alias("simplified")
).first()["simplified"]
+--------+
|simplified|
+--------+
|[binary]|
+--------+
... (WKB binary — simplified polygon with near-collinear vertex removed, topology preserved)
Not available in this tier.
Not available in this tier.
st_removerepeatedpoints
LightweightRemoves consecutive duplicate or near-duplicate vertices from a geometry. With the default tolerance=0.0, only exact duplicate consecutive coordinate pairs are removed. A positive tolerance collapses vertices within that distance of their predecessor into a single representative vertex.
Signature: gbx_st_removerepeatedpoints(geom [, tolerance])
Parameters:
geom— Geometry to deduplicate. Accepts WKB/EWKB/WKT/EWKT.tolerance(optional, default0.0) — Distance threshold for consecutive near-duplicate vertices.
Returns: BINARY — WKB (or EWKB if input carried a SRID). The original CRS is preserved. Returns NULL for null or unparseable input.
See Geometry Cleaning.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_removerepeatedpoints('LINESTRING(0 0,0 0,1 1,1 1,2 2)') AS deduped
+--------+
|deduped |
+--------+
|[binary]|
+--------+
... (WKB binary — linestring with duplicate consecutive vertices removed: LINESTRING(0 0,1 1,2 2))
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
geom = from_wkt("LINESTRING(0 0,0 0,1 1,1 1,2 2)")
df = spark.createDataFrame([(to_wkb(geom),)], ["geom"])
return df.select(
vx.st_removerepeatedpoints("geom").alias("deduped")
).first()["deduped"]
+--------+
|deduped |
+--------+
|[binary]|
+--------+
... (WKB binary — linestring with duplicate consecutive vertices removed: LINESTRING(0 0,1 1,2 2))
Not available in this tier.
Not available in this tier.
st_reduceprecision
LightweightSnaps coordinates to a precision grid of size grid_size, rounding each coordinate to the nearest multiple of grid_size. Useful for eliminating floating-point noise, aligning data from different sources, or reducing coordinate storage size. Uses GEOS set_precision with mode="valid_output" so the output remains valid even if snapping would otherwise collapse a ring.
Signature: gbx_st_reduceprecision(geom, grid_size)
Parameters:
geom— Geometry to snap to grid. Accepts WKB/EWKB/WKT/EWKT.grid_size— Precision grid size in the geometry's coordinate units (e.g.1.0for integer coordinates,0.001for 3 decimal places).
Returns: BINARY — WKB (or EWKB if input carried a SRID). The original CRS is preserved. Returns NULL for null or unparseable input.
See Geometry Cleaning.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_reduceprecision('POINT(1.234 5.678)', 1.0) AS snapped
+--------+
|snapped |
+--------+
|[binary]|
+--------+
... (WKB binary — POINT(1.0, 6.0): coordinates snapped to nearest 1.0 grid lines)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
from pyspark.sql import functions as f
geom = from_wkt("POINT(1.234 5.678)")
df = spark.createDataFrame([(to_wkb(geom),)], ["geom"])
return df.select(
vx.st_reduceprecision("geom", f.lit(1.0)).alias("snapped")
).first()["snapped"]
+--------+
|snapped |
+--------+
|[binary]|
+--------+
... (WKB binary — POINT(1.0, 6.0): coordinates snapped to nearest 1.0 grid lines)
Not available in this tier.
Not available in this tier.
st_node
LightweightNodes a geometry's linework by splitting at all self-intersections and pairwise intersections. The result is a MultiLineString (or LineString) where every segment endpoint is a valid node — no two segments cross without sharing an explicit endpoint. Noding is typically a prerequisite for polygonization, planar graph analysis, or building topologically clean networks from raw linework.
Signature: gbx_st_node(geom)
Parameters:
geom— Linework geometry to node. Accepts WKB/EWKB/WKT/EWKT. Best suited forLineStringandMultiLineStringinputs; polygon rings can also be noded.
Returns: BINARY — WKB (or EWKB if input carried a SRID). The original CRS is preserved. Returns NULL for null or unparseable input.
See Geometry Cleaning.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_node('LINESTRING(0 0,10 10,0 10,10 0)') AS noded
+--------+
|noded |
+--------+
|[binary]|
+--------+
... (WKB binary — MultiLineString: figure-eight split into clean segments at the self-intersection)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
geom = from_wkt("LINESTRING(0 0,10 10,0 10,10 0)")
df = spark.createDataFrame([(to_wkb(geom),)], ["geom"])
+--------+
|noded |
+--------+
|[binary]|
+--------+
... (WKB binary — MultiLineString: figure-eight split into clean segments at the self-intersection)
Not available in this tier.
Not available in this tier.
st_snap
LightweightSnaps geom's vertices onto reference where within tolerance. Any vertex in geom that lies within tolerance distance of a vertex or edge in reference is moved onto that nearest point. Useful for aligning features from different sources that should share boundaries but don't quite meet due to digitizing differences or floating-point noise.
Complements the product's ST_ClosestPoint (exact nearest point, no tolerance gate).
Signature: gbx_st_snap(geom, reference, tolerance)
Parameters:
geom— Geometry whose vertices will be snapped. Accepts WKB/EWKB/WKT/EWKT.reference— Reference geometry to snap onto. Accepts WKB/EWKB/WKT/EWKT.tolerance— Maximum snap distance in the geometry's coordinate units.
Returns: BINARY — WKB (or EWKB if input carried a SRID). The SRID of geom is preserved. Returns NULL if either input is null or unparseable.
See Geometry Cleaning.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_snap('LINESTRING(0 0.4,10 0.4)', 'LINESTRING(0 0,10 0)', 0.5) AS snapped
+--------+
|snapped |
+--------+
|[binary]|
+--------+
... (WKB binary — linestring with near-miss vertices snapped onto the reference at y=0)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
from pyspark.sql import functions as f
geom = from_wkt("LINESTRING(0 0.4,10 0.4)")
ref = from_wkt("LINESTRING(0 0,10 0)")
df = spark.createDataFrame([(to_wkb(geom), to_wkb(ref))], ["geom", "ref"])
return df.select(
vx.st_snap("geom", "ref", f.lit(0.5)).alias("snapped")
).first()["snapped"]
+--------+
|snapped |
+--------+
|[binary]|
+--------+
... (WKB binary — linestring with near-miss vertices snapped onto the reference at y=0)
Not available in this tier.
Not available in this tier.
Antimeridian handling
These three functions are distributed shapely helpers — shapely/GEOS geometry operations running as Python UDFs across your Spark cluster. They are lightweight tier only (pyvx); no heavyweight counterpart exists. See Antimeridian for the full composition pattern.
st_shiftlongitude
LightweightShifts all longitude (x) coordinates of a geometry from [-180, 180] space into [0, 360] space by adding 360 to any negative x value. Use this as the first step when normalizing a geometry that crosses the 180° antimeridian: after shifting, the crossing polygon becomes contiguous around x=180 and can be cleanly split there.
Signature: gbx_st_shiftlongitude(geom)
Parameters:
geom— Geometry. Accepts WKB/EWKB/WKT/EWKT.
Returns: BINARY — WKB with all negative x coordinates shifted by +360.
See Antimeridian.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_shiftlongitude('POLYGON((-170 -10, -150 -10, -150 10, -170 10, -170 -10))') AS shifted
+--------+
|shifted |
+--------+
|[binary]|
+--------+
... (WKB binary — polygon in [0,360] longitude space, x coordinates shifted to [190,210])
from databricks.labs.gbx.pyvx import functions as vx
from shapely import to_wkb
from shapely.geometry import Point
df = spark.createDataFrame([(to_wkb(Point(-170.0, 10.0)),)], ["geom"])
+--------+
|shifted |
+--------+
|[binary]|
+--------+
... (WKB binary — POINT(190.0, 10.0): x shifted from -170 to 190)
Not available in this tier.
Not available in this tier.
st_wrapx
LightweightWraps x coordinates that exceed a threshold back by a fixed offset. The primary use is the reverse of gbx_st_shiftlongitude: after splitting at x=180, apply gbx_st_wrapx(geom, 180, -360) to the eastern pieces to move them from [180, 360] back into [-180, 0]. Apply only to the pieces where ST_XMax(piece) > 180 — wrapping the western piece's shared 180° edge would produce a malformed geometry.
Signature: gbx_st_wrapx(geom, wrap_x_origin, wrap_direction)
Parameters:
geom— Geometry. Accepts WKB/EWKB/WKT/EWKT.wrap_x_origin(DOUBLE) — x threshold above which wrapping is applied (typically180).wrap_direction(DOUBLE) — offset to add to coordinates that exceed the threshold (typically-360).
Returns: BINARY — WKB with qualifying x coordinates shifted by wrap_direction.
See Antimeridian.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_wrapx('POINT(190 10)', 180, -360) AS wrapped
+--------+
|wrapped |
+--------+
|[binary]|
+--------+
... (WKB binary — POINT(-170, 10): x=190 wrapped back by -360)
from pyspark.sql import functions as f
from databricks.labs.gbx.pyvx import functions as vx
from shapely import to_wkb
from shapely.geometry import Point
df = spark.createDataFrame([(to_wkb(Point(190.0, 10.0)),)], ["geom"])
return df.select(
vx.st_wrapx("geom", f.lit(180.0), f.lit(-360.0)).alias("wrapped")
).first()["wrapped"]
+--------+
|wrapped |
+--------+
|[binary]|
+--------+
... (WKB binary — POINT(-170.0, 10.0): x=190 wrapped back by -360)
Not available in this tier.
Not available in this tier.
st_split
LightweightSplits a geometry by a blade geometry, returning a GEOMETRYCOLLECTION containing all resulting pieces. The primary use is cutting an antimeridian-crossing polygon at the 180° meridian: pass the polygon (already shifted into [0, 360] space by gbx_st_shiftlongitude) and the blade 'LINESTRING(180 -90, 180 90)'.
Signature: gbx_st_split(geom, blade)
Parameters:
geom— Geometry to split. Accepts WKB/EWKB/WKT/EWKT.blade— Cutting geometry. Accepts WKB/EWKB/WKT/EWKT.
Returns: BINARY — WKB GEOMETRYCOLLECTION of the resulting pieces.
See Antimeridian.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_split('POLYGON((170 -10, 190 -10, 190 10, 170 10, 170 -10))', 'LINESTRING(180 -90, 180 90)') AS pieces
+--------+
|pieces |
+--------+
|[binary]|
+--------+
... (WKB binary — GeometryCollection with 2 polygon pieces split at x=180)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import to_wkb
from shapely.geometry import LineString, Polygon
input_poly = Polygon([(170, -10), (190, -10), (190, 10), (170, 10), (170, -10)])
blade_line = LineString([(180, -90), (180, 90)])
df = spark.createDataFrame(
[(to_wkb(input_poly), to_wkb(blade_line))],
["input_geom", "blade_geom"],
)
return df.select(
vx.st_split("input_geom", "blade_geom").alias("pieces")
).first()["pieces"]
+--------+
|pieces |
+--------+
|[binary]|
+--------+
... (WKB binary — GeometryCollection with 2 polygon pieces split at x=180)
Not available in this tier.
Not available in this tier.
Legacy Mosaic conversion
st_legacyaswkb
LightweightHeavyweightMigrates a legacy DBLabs Mosaic geometry value to standard Well-Known Binary (WKB). Pass the raw legacy geometry column through st_legacyaswkb to obtain a WKB binary that all downstream ST_* functions accept — the practical first step when moving a Mosaic-era table onto the product's native GEOMETRY/GEOGRAPHY types.
A scalar function in both tiers (same registered name, same output bytes), so the migration query is a one-line tier swap.
Parameters: legacyGeometry — Column containing a legacy Mosaic geometry value (e.g. {1, [[[x, y]]], [[]]}).
Returns: BINARY — standard WKB.
Migration notes:
- Z values are preserved. 3D legacy geometries round-trip through
st_legacyaswkbwith their Z coordinate intact. - Polygon holes (interior rings) are preserved.
- SRID is applied separately at ingestion. The output is plain WKB and carries no SRID; assign the CRS when you read it back, e.g.
ST_GeomFromWKB(gbx_st_legacyaswkb(geom_legacy), 27700). - M (measure) values are out of scope for this conversion.
- SQL
- Python (light)
- Python (heavy)
- Scala
SELECT gbx_st_legacyaswkb(geom_legacy) AS wkb FROM legacy_geoms;
+--------+
|wkb |
+--------+
|[binary]|
+--------+
... (WKB binary)
from databricks.labs.gbx.pyvx import functions as vx
df = spark.table("legacy_geoms")
result = df.select(vx.st_legacyaswkb("geom_legacy").alias("wkb")).first()
+--------+
|wkb |
+--------+
|[binary]|
+--------+
... (WKB binary)
from pyspark.sql import functions as f
from databricks.labs.gbx.vectorx.jts.legacy import functions as vx
vx.register(spark)
df = spark.table("legacy_geoms")
result = df.select(vx.st_legacyaswkb(f.col("geom_legacy")).alias("wkb")).first()
+--------+
|wkb |
+--------+
|[binary]|
+--------+
... (WKB binary)
import com.databricks.labs.gbx.vectorx.jts.legacy.{functions => vx}
import org.apache.spark.sql.functions._
vx.register(spark)
val df = spark.table("legacy_geoms")
val result = df.select(vx.st_legacyaswkb(col("geom_legacy")).alias("wkb")).first()
result.getAs[Array[Byte]]("wkb")
+--------+
|wkb |
+--------+
|[binary]|
+--------+
... (WKB binary)
Next Steps
- Quick Start — Register and use VectorX with the legacy example
- Geometry Validity — diagnose and repair OGC-invalid geometries with
gbx_st_explainvalidityandgbx_st_makevalid - Coverage Validity — validate and simplify polygon coverages with
gbx_st_coverageisvalid,gbx_st_coverageinvalidedges, andcoverage_simplify - Geometry Cleaning — topology-safe simplification, vertex deduplication, precision reduction, linework noding, and feature snapping
- Antimeridian — antimeridian composition pattern with full example
- Choosing an Execution Tier — lightweight vs heavyweight comparison
- PMTiles Function Reference — Aggregate MVT tiles into a single
.pmtilesarchive - PMTiles Writer — DataSource for streaming large pyramids to a single
.pmtilesfile - Helios notebooks — worked end-to-end example:
gbx_st_asmvt+gbx_st_asmvt_pyramidencode San Francisco building footprints into a PMTiles archive (NB01). - Benchmarking — light-vs-heavy timing methodology
- API Overview — All GeoBrix APIs