Coverage Validity
VectorX ships a set of geometry-processing helpers powered by shapely (GEOS under the hood), running as distributed Python UDFs across your Spark cluster. These functions fill gaps in the product's native ST_* surface — operations that are standard in shapely but have no direct built-in counterpart in Databricks Runtime.
The functions on this page are available in the lightweight (pyvx) tier. Register pyvx before using them in SQL or Python:
from databricks.labs.gbx.pyvx import functions as vx
vx.register(spark)
A coverage is a set of polygons that together partition a region: each polygon may be valid on its own (passes ST_IsValid), yet the set may still be invalid because polygons overlap, or because gaps appear between them. Coverage validity is a set-level question that single-geometry validity functions cannot answer.
The canonical use cases are polygon layers where precise boundary alignment matters: administrative boundaries, parcels, land-cover patches, building footprints, or any layer where features share edges. Gaps and overlaps in such layers cause double-counting, broken spatial joins, and tile-rendering seams.
GeoBrix provides two SQL grouped-aggregate functions plus one Python-API helper:
gbx_st_coverageisvalid— detect:GROUP BY-based boolean check.truewhen the coverage has no overlaps and no gaps thinner thangap_width.gbx_st_coverageinvalidedges— inspect: returns the union of the boundary segments that violate the coverage. Empty geometry when the coverage is clean.coverage_simplify— repair and simplify: Python-API only. Topology-preserving simplification of a whole coverage — shared edges stay shared after simplification.
What is gap_width?
Both gbx_st_coverageisvalid and gbx_st_coverageinvalidedges take a required gap_width argument:
- Overlaps are always invalid, regardless of
gap_width. Any two polygons in the group whose interiors intersect cause the coverage to fail. - Gaps thinner than
gap_widthare also flagged. A gap is a region between two adjacent polygons that does not belong to any polygon in the group.gap_width=0.0means only overlaps are detected (no gap tolerance). A positivegap_widthalso flags slivers — thin corridors between nearly-touching polygons that may be digitizing artefacts rather than intentional gaps.
Worked example:
| Coverage | gap_width=0.0 | gap_width=0.5 |
|---|---|---|
| Two squares sharing a clean edge (no gap, no overlap) | valid | valid |
| Two squares with a 0.3-unit gap between them | valid (gap ignored) | invalid (gap detected) |
| Two squares with a 0.3-unit overlap | invalid | invalid |
gbx_st_coverageisvalid
Lightweight grouped-aggCheck whether a group of polygons forms a valid coverage. Use GROUP BY to define which polygons belong to the same coverage — the aggregator evaluates the entire group at once and returns a single BOOLEAN per group.
Signature: gbx_st_coverageisvalid(geom, gap_width)
Parameters:
geom— Geometry column containing polygon or multipolygon values. Accepts WKB/EWKB/WKT/EWKT. Every member of the group must parse to a polygon type.gap_width(DOUBLE, required) — Gap tolerance. Overlaps are always invalid; gaps thinner than this threshold are also flagged. Pass0.0to detect only overlaps.
Returns: BOOLEAN — true when the coverage is valid (no overlaps; no gaps narrower than gap_width). NULL when the group is empty or all members are null.
gbx_st_coverageisvalid(geom) (one-argument form) does not resolve in SQL and will raise an UNRESOLVED_ROUTINE error. Always pass gap_width explicitly: gbx_st_coverageisvalid(geom, 0.0). The Python Column wrapper (vx.st_coverageisvalid) defaults gap_width to 0.0 and does not require it.
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)
gbx_st_coverageinvalidedges
Lightweight grouped-aggReturn the union of the boundary segments that violate the coverage. Like gbx_st_coverageisvalid, this is a grouped aggregate — it evaluates all polygons in the group together and returns one geometry per group.
Signature: gbx_st_coverageinvalidedges(geom, gap_width)
Parameters:
geom— Geometry column. Accepts WKB/EWKB/WKT/EWKT. Every member must parse to a polygon type.gap_width(DOUBLE, required) — Gap tolerance. Same semantics asgbx_st_coverageisvalid. Pass0.0to detect only overlap-zone edges.
Returns: BINARY — WKB (or EWKB if the input group carries a SRID): the union of the edges that violate the coverage. An empty MultiLineString is returned when the coverage is clean (no invalid edges). NULL when the group is empty.
The output geometry is useful for spatial debugging: join it back to a map layer to highlight exactly where the coverage breaks down.
Same constraint as gbx_st_coverageisvalid — always pass the second argument explicitly.
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)
coverage_simplify (Python API)
LightweightTopology-preserving simplification of a whole coverage. Unlike gbx_st_simplifypreservetopology (which simplifies each geometry independently), coverage_simplify simplifies the entire coverage as a unit — vertices on shared edges are dropped or kept together in both polygons, so boundaries that were shared before simplification remain shared after.
This is a Python-API only function. There is no SQL form; it operates directly on a Spark DataFrame.
Signature: coverage_simplify(df, group_col, geom_col, tolerance, simplify_boundary=True, out_col="geom_simplified")
Parameters:
df— Input Spark DataFrame.group_col— Column name that identifies coverage groups (e.g."cov_id"). All rows with the same value form one coverage and are simplified together.geom_col— Column name of the geometry (WKB/EWKB/WKT/EWKT column).tolerance(float) — Simplification tolerance in the geometry's coordinate units. Vertices whose removal would shift the boundary by less thantoleranceare candidates for removal.simplify_boundary(bool, defaultTrue) — Whether to simplify the outer boundary of the coverage (the edges that are not shared between polygons). Set toFalseto preserve the outer boundary exactly and simplify only interior shared edges.out_col(str, default"geom_simplified") — Name of the new column added to the output DataFrame for the simplified geometry.
Returns: A Spark DataFrame with the same rows and all original columns, plus out_col (BINARY WKB/EWKB). Row identity is preserved — the output has exactly the same number of rows as the input (N→N), in the same order within each group.
Import:
from databricks.labs.gbx.pyvx import functions as vx
vx.register(spark)
result = vx.coverage_simplify(df, "admin_level", "geom_wkb", tolerance=50.0)
from databricks.labs.gbx.pyvx import functions as vx
from shapely import from_wkt, to_wkb
left = from_wkt("POLYGON((0 0, 2.5 0.001, 5 0, 5 5, 0 5, 0 0))")
right = from_wkt("POLYGON((5 0, 7.5 0.001, 10 0, 10 5, 5 5, 5 0))")
df = spark.createDataFrame(
[
(1, to_wkb(left)),
(1, to_wkb(right)),
],
["cov_id", "geom"],
)
result = vx.coverage_simplify(df, "cov_id", "geom", 0.1)
rows = result.collect()
+------+--------+----------------+
|cov_id| geom|geom_simplified |
+------+--------+----------------+
| 1|[binary]| [binary] |
| 1|[binary]| [binary] |
+------+--------+----------------+
... (BINARY — 2 rows in, 2 rows out; near-collinear vertices dropped, shared edge preserved)
When to use coverage_simplify vs gbx_st_simplifypreservetopology
| Scenario | Recommendation |
|---|---|
| Simplify features in isolation (no shared edges) | gbx_st_simplifypreservetopology — simpler, per-row operation |
| Simplify a parcel/boundary layer where features share edges | coverage_simplify — shared edges stay aligned after simplification |
| Very large coverage that does not fit in one executor | See Scale ceiling below |
Scale ceiling
gbx_st_coverageisvalid, gbx_st_coverageinvalidedges, and coverage_simplify all require the entire coverage group to be resident on a single executor — GEOS needs to see all polygons in the group at once to evaluate shared edges. This is a fundamental constraint of coverage-level operations.
Practical limit: A coverage group that fits in one executor's Python worker (typically ~1–3 GB on Serverless, more on dedicated clusters) will process without issue. Large administrative boundary datasets with thousands of polygons and complex geometries may exceed this limit.
If your coverage exceeds executor memory:
- Partition the coverage spatially into sub-regions (e.g. by grid cell or administrative hierarchy level) and process sub-regions independently.
- Use
gbx_st_coverageinvalidedgeson candidate problem zones rather than on the full coverage. - For simplification of very large coverages, consider iterating over spatial tiles with an overlap buffer.
Future versions of GeoBrix will explore streaming coverage algorithms that relax the all-in-memory constraint.
Tier availability
| Function | Lightweight (pyvx) | Heavyweight (vectorx) |
|---|---|---|
gbx_st_coverageisvalid | Supported | — |
gbx_st_coverageinvalidedges | Supported | — |
coverage_simplify | Supported (Python API) | — |