Follow-up to the Box3D Phase 1 epic (#2973). Adds the 3D-aware distance predicate, matching PostGIS's ST_3DDWithin and extending the existing ST_DWithin Box overload pattern.
Scope
Two Java overloads on org.apache.sedona.common.Predicates:
dWithin3D(Geometry a, Geometry b, double distance) -> boolean — 3D Euclidean distance test on Geometry inputs. Uses JTS Distance3DOp (already imported in Functions.java::distance3d). Returns true iff the minimum 3D Euclidean distance is ≤ distance. Geometries without a Z dimension are treated as z = 0 per PostGIS.
dWithin3D(Box3D a, Box3D b, double distance) -> boolean — closed-interval 3D Euclidean distance test between two axis-aligned 3D boxes. Mirrors the existing dWithin(Box2D, Box2D, double) overload added in the Box2D Phase 1 work. Rejects inverted bounds (xmin > xmax / ymin > ymax / zmin > zmax) with IllegalArgumentException — same contract as the Box3D predicates from #3012.
Catalyst surface:
ST_3DDWithin case class wired through InferredExpression with inferrableFunction3((Geometry, Geometry, Double) => ...) and inferrableFunction3((Box3D, Box3D, Double) => ...) branches.
- Registered in
Catalog.predicateExpressions.
DataFrame API surface (mirroring the Box3D predicates PR #3014):
- Scala helpers in
st_predicates.scala: ST_3DDWithin(Column, Column, Column) / (String, String, String).
- Python helpers in
st_predicates.py with PostGIS-parity docstring.
Semantics
- Closed interval — distance equal to the threshold counts as within.
- NULL on null input.
- Inverted Box3D bounds →
IllegalArgumentException.
- Geometries without a Z dimension fold to
z = 0, matching ST_Box3D / ST_3DMakeBox semantics already in master.
- No spheroid option (matches PostGIS; planar 3D distance is what 3D geometries already imply).
Tests
- Java
PredicatesTest covering Geometry-Geometry (XY-only inputs fold to 0, XYZ inputs return true 3D distance, threshold edge inclusion), Box3D-Box3D (overlap, edge/face/corner touch, separated along Z axis only, threshold edge inclusion, inverted-bounds reject), and null/empty propagation.
- ScalaTest
Box3DDWithinSuite for the SQL surface and DataFrame API entries in dataFrameAPITestScala + test_dataframe_api.py.
Out of scope
- Spatial join planner integration for
ST_3DDWithin (tracked separately under the Box3D Phase 1 epic).
- Filter pushdown.
Follow-up to the Box3D Phase 1 epic (#2973). Adds the 3D-aware distance predicate, matching PostGIS's
ST_3DDWithinand extending the existingST_DWithinBox overload pattern.Scope
Two Java overloads on
org.apache.sedona.common.Predicates:dWithin3D(Geometry a, Geometry b, double distance) -> boolean— 3D Euclidean distance test on Geometry inputs. Uses JTSDistance3DOp(already imported inFunctions.java::distance3d). Returns true iff the minimum 3D Euclidean distance is ≤distance. Geometries without a Z dimension are treated asz = 0per PostGIS.dWithin3D(Box3D a, Box3D b, double distance) -> boolean— closed-interval 3D Euclidean distance test between two axis-aligned 3D boxes. Mirrors the existingdWithin(Box2D, Box2D, double)overload added in the Box2D Phase 1 work. Rejects inverted bounds (xmin > xmax/ymin > ymax/zmin > zmax) withIllegalArgumentException— same contract as the Box3D predicates from #3012.Catalyst surface:
ST_3DDWithincase class wired throughInferredExpressionwithinferrableFunction3((Geometry, Geometry, Double) => ...)andinferrableFunction3((Box3D, Box3D, Double) => ...)branches.Catalog.predicateExpressions.DataFrame API surface (mirroring the Box3D predicates PR #3014):
st_predicates.scala:ST_3DDWithin(Column, Column, Column)/(String, String, String).st_predicates.pywith PostGIS-parity docstring.Semantics
IllegalArgumentException.z = 0, matchingST_Box3D/ST_3DMakeBoxsemantics already in master.Tests
PredicatesTestcovering Geometry-Geometry (XY-only inputs fold to 0, XYZ inputs return true 3D distance, threshold edge inclusion), Box3D-Box3D (overlap, edge/face/corner touch, separated along Z axis only, threshold edge inclusion, inverted-bounds reject), and null/empty propagation.Box3DDWithinSuitefor the SQL surface and DataFrame API entries indataFrameAPITestScala+test_dataframe_api.py.Out of scope
ST_3DDWithin(tracked separately under the Box3D Phase 1 epic).