Skip to content

ST_3DDWithin: 3D distance-within predicate #3024

@jiayuasu

Description

@jiayuasu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions