From abe114e29e16d881f230a00d5cfcc47b804d6f74 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Tue, 14 Jul 2020 17:22:36 -0700 Subject: [PATCH 1/2] builtins: implement ST_ConvexHull Resolves #48909. Release note (sql change): Implement the ST_ConvexHull function on geometry types. --- docs/generated/sql/functions.md | 3 ++ pkg/geo/geomfn/topology_operations.go | 9 +++++ pkg/geo/geomfn/topology_operations_test.go | 34 +++++++++++++++++++ pkg/geo/geos/geos.cc | 21 ++++++++++++ pkg/geo/geos/geos.go | 13 +++++++ pkg/geo/geos/geos.h | 1 + .../logictest/testdata/logic_test/geospatial | 27 ++++++++------- pkg/sql/sem/builtins/geo_builtins.go | 18 ++++++++++ 8 files changed, 113 insertions(+), 13 deletions(-) diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index 9185f0867661..9771ecf7c0c7 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -974,6 +974,9 @@ given Geometry.

This function utilizes the GEOS module.

This function variant will attempt to utilize any available geospatial index.

+st_convexhull(geometry: geometry) → geometry

Returns a geometry that represents the Convex Hull of the given geometry.

+

This function utilizes the GEOS module.

+
st_coveredby(geography_a: geography, geography_b: geography) → bool

Returns true if no point in geography_a is outside geography_b.

The calculations performed are have a precision of 1cm.

This function utilizes the S2 library for spherical calculations.

diff --git a/pkg/geo/geomfn/topology_operations.go b/pkg/geo/geomfn/topology_operations.go index a8d7f90fd7e1..17fcb7868349 100644 --- a/pkg/geo/geomfn/topology_operations.go +++ b/pkg/geo/geomfn/topology_operations.go @@ -24,6 +24,15 @@ func Centroid(g *geo.Geometry) (*geo.Geometry, error) { return geo.ParseGeometryFromEWKB(centroidEWKB) } +// ConvexHull returns the convex hull of a given Geometry. +func ConvexHull(g *geo.Geometry) (*geo.Geometry, error) { + convexHullEWKB, err := geos.ConvexHull(g.EWKB()) + if err != nil { + return nil, err + } + return geo.ParseGeometryFromEWKB(convexHullEWKB) +} + // PointOnSurface returns the PointOnSurface of a given Geometry. func PointOnSurface(g *geo.Geometry) (*geo.Geometry, error) { pointOnSurfaceEWKB, err := geos.PointOnSurface(g.EWKB()) diff --git a/pkg/geo/geomfn/topology_operations_test.go b/pkg/geo/geomfn/topology_operations_test.go index 832685a05f84..343a56aeb23c 100644 --- a/pkg/geo/geomfn/topology_operations_test.go +++ b/pkg/geo/geomfn/topology_operations_test.go @@ -58,6 +58,40 @@ func TestCentroid(t *testing.T) { } } +func TestConvexHull(t *testing.T) { + testCases := []struct { + wkt string + expected string + }{ + { + "GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))", + "POLYGON((10 10,10 40,20 45,40 40,45 30,40 10,10 10))", + }, + { + "SRID=4326;GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))", + "SRID=4326;POLYGON((10 10,10 40,20 45,40 40,45 30,40 10,10 10))", + }, + { + "MULTILINESTRING((100 190,10 8),(150 10, 20 30))", + "POLYGON((10 8,20 30,100 190,150 10,10 8))", + }, + } + + for _, tc := range testCases { + t.Run(tc.wkt, func(t *testing.T) { + g, err := geo.ParseGeometry(tc.wkt) + require.NoError(t, err) + ret, err := ConvexHull(g) + require.NoError(t, err) + + expected, err := geo.ParseGeometry(tc.expected) + require.NoError(t, err) + + require.Equal(t, expected, ret) + }) + } +} + func TestPointOnSurface(t *testing.T) { testCases := []struct { wkt string diff --git a/pkg/geo/geos/geos.cc b/pkg/geo/geos/geos.cc index 9180e1399fa6..d6cc63744329 100644 --- a/pkg/geo/geos/geos.cc +++ b/pkg/geo/geos/geos.cc @@ -87,6 +87,7 @@ typedef int (*CR_GEOS_Area_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double*); typedef int (*CR_GEOS_Length_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double*); typedef CR_GEOS_Geometry (*CR_GEOS_Centroid_r)(CR_GEOS_Handle, CR_GEOS_Geometry); +typedef CR_GEOS_Geometry (*CR_GEOS_ConvexHull_r)(CR_GEOS_Handle, CR_GEOS_Geometry); typedef CR_GEOS_Geometry (*CR_GEOS_Union_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry); typedef CR_GEOS_Geometry (*CR_GEOS_Intersection_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry); @@ -162,6 +163,7 @@ struct CR_GEOS { CR_GEOS_Length_r GEOSLength_r; CR_GEOS_Centroid_r GEOSGetCentroid_r; + CR_GEOS_ConvexHull_r GEOSConvexHull_r; CR_GEOS_Union_r GEOSUnion_r; CR_GEOS_PointOnSurface_r GEOSPointOnSurface_r; CR_GEOS_Intersection_r GEOSIntersection_r; @@ -232,6 +234,7 @@ struct CR_GEOS { INIT(GEOSArea_r); INIT(GEOSLength_r); INIT(GEOSGetCentroid_r); + INIT(GEOSConvexHull_r); INIT(GEOSUnion_r); INIT(GEOSPointOnSurface_r); INIT(GEOSIntersection_r); @@ -535,6 +538,24 @@ CR_GEOS_Status CR_GEOS_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* c return toGEOSString(error.data(), error.length()); } +CR_GEOS_Status CR_GEOS_ConvexHull(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* convexHullEWKB) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a); + *convexHullEWKB = {.data = NULL, .len = 0}; + if (geom != nullptr) { + auto convexHullGeom = lib->GEOSConvexHull_r(handle, geom); + if (convexHullGeom != nullptr) { + auto srid = lib->GEOSGetSRID_r(handle, geom); + CR_GEOS_writeGeomToEWKB(lib, handle, convexHullGeom, convexHullEWKB, srid); + lib->GEOSGeom_destroy_r(handle, convexHullGeom); + } + lib->GEOSGeom_destroy_r(handle, geom); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + CR_GEOS_Status CR_GEOS_Union(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_String* unionEWKB) { std::string error; diff --git a/pkg/geo/geos/geos.go b/pkg/geo/geos/geos.go index 1327e34e1789..dcfec7fbe1a9 100644 --- a/pkg/geo/geos/geos.go +++ b/pkg/geo/geos/geos.go @@ -342,6 +342,19 @@ func Centroid(ewkb geopb.EWKB) (geopb.EWKB, error) { return cStringToSafeGoBytes(cEWKB), nil } +// ConvexHull returns an EWKB which returns the convex hull of the given EWKB. +func ConvexHull(ewkb geopb.EWKB) (geopb.EWKB, error) { + g, err := ensureInitInternal() + if err != nil { + return nil, err + } + var cEWKB C.CR_GEOS_String + if err := statusToError(C.CR_GEOS_ConvexHull(g, goToCSlice(ewkb), &cEWKB)); err != nil { + return nil, err + } + return cStringToSafeGoBytes(cEWKB), nil +} + // PointOnSurface returns an EWKB with a point that is on the surface of the given EWKB. func PointOnSurface(ewkb geopb.EWKB) (geopb.EWKB, error) { g, err := ensureInitInternal() diff --git a/pkg/geo/geos/geos.h b/pkg/geo/geos/geos.h index 6cb9c11063b0..24181a6fc01e 100644 --- a/pkg/geo/geos/geos.h +++ b/pkg/geo/geos/geos.h @@ -81,6 +81,7 @@ CR_GEOS_Status CR_GEOS_Length(CR_GEOS* lib, CR_GEOS_Slice a, double* ret); // CR_GEOS_Status CR_GEOS_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* centroidEWKB); +CR_GEOS_Status CR_GEOS_ConvexHull(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* convexHullEWKB); CR_GEOS_Status CR_GEOS_Union(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_String* unionEWKB); CR_GEOS_Status CR_GEOS_PointOnSurface(CR_GEOS* lib, CR_GEOS_Slice a, diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index f939bc934bc7..bdb032ebd0d5 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -867,25 +867,26 @@ Square overlapping left and right square Square (left) Square overlapping left and right square Square (right) POLYGON ((1 0, 0 0, 0 1, 1 1, 1 0)) Square overlapping left and right square Square overlapping left and right square POLYGON ((1 0, -0.1 0, -0.1 1, 1 1, 1 0)) -query TTT +query TTTT SELECT a.dsc, ST_AsEWKT(ST_Centroid(a.geom)), - ST_AsEWKT(ST_PointOnSurface(a.geom)) + ST_AsEWKT(ST_PointOnSurface(a.geom)), + ST_AsEWKT(ST_ConvexHull(a.geom)) FROM geom_operators_test a ORDER BY a.dsc ---- -Empty GeometryCollection POINT EMPTY POINT EMPTY -Empty LineString POINT EMPTY POINT EMPTY -Empty Point POINT EMPTY POINT EMPTY -Faraway point POINT (5 5) POINT (5 5) -Line going through left and right square POINT (0 0.5) POINT (-0.5 0.5) -NULL NULL NULL -Point middle of Left Square POINT (-0.5 0.5) POINT (-0.5 0.5) -Point middle of Right Square POINT (0.5 0.5) POINT (0.5 0.5) -Square (left) POINT (-0.5 0.5) POINT (-0.5 0.5) -Square (right) POINT (0.5 0.5) POINT (0.5 0.5) -Square overlapping left and right square POINT (0.45 0.5) POINT (0.45 0.5) +Empty GeometryCollection POINT EMPTY POINT EMPTY GEOMETRYCOLLECTION EMPTY +Empty LineString POINT EMPTY POINT EMPTY GEOMETRYCOLLECTION EMPTY +Empty Point POINT EMPTY POINT EMPTY GEOMETRYCOLLECTION EMPTY +Faraway point POINT (5 5) POINT (5 5) POINT (5 5) +Line going through left and right square POINT (0 0.5) POINT (-0.5 0.5) LINESTRING (-0.5 0.5, 0.5 0.5) +NULL NULL NULL NULL +Point middle of Left Square POINT (-0.5 0.5) POINT (-0.5 0.5) POINT (-0.5 0.5) +Point middle of Right Square POINT (0.5 0.5) POINT (0.5 0.5) POINT (0.5 0.5) +Square (left) POINT (-0.5 0.5) POINT (-0.5 0.5) POLYGON ((-1 0, -1 1, 0 1, 0 0, -1 0)) +Square (right) POINT (0.5 0.5) POINT (0.5 0.5) POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0)) +Square overlapping left and right square POINT (0.45 0.5) POINT (0.45 0.5) POLYGON ((-0.1 0, -0.1 1, 1 1, 1 0, -0.1 0)) # Functions which take in strings as input as well. query TT diff --git a/pkg/sql/sem/builtins/geo_builtins.go b/pkg/sql/sem/builtins/geo_builtins.go index a8c034d01f52..139dfb6bbe92 100644 --- a/pkg/sql/sem/builtins/geo_builtins.go +++ b/pkg/sql/sem/builtins/geo_builtins.go @@ -2386,6 +2386,24 @@ Note if geometries are the same, it will return the LineString with the minimum ), )..., ), + "st_convexhull": makeBuiltin( + defProps(), + geometryOverload1( + func(ctx *tree.EvalContext, g *tree.DGeometry) (tree.Datum, error) { + convexHull, err := geomfn.ConvexHull(g.Geometry) + if err != nil { + return nil, err + } + return tree.NewDGeometry(convexHull), err + }, + types.Geometry, + infoBuilder{ + info: "Returns a geometry that represents the Convex Hull of the given geometry.", + libraryUsage: usesGEOS, + }, + tree.VolatilityImmutable, + ), + ), "st_pointonsurface": makeBuiltin( defProps(), geometryOverload1( From 0a69e11fb56165970016d76358dbf3b9231e0d2f Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 15 Jul 2020 07:50:19 -0700 Subject: [PATCH 2/2] builtins: implement ST_DistanceSphere and ST_DistanceSpheroid Release note (sql change): Implement the ST_DistanceSphere and ST_DistanceSpheroid operators for geometry types. --- docs/generated/sql/functions.md | 7 + .../logictest/testdata/logic_test/geospatial | 133 ++++++++++++++++++ pkg/sql/sem/builtins/geo_builtins.go | 60 ++++++++ 3 files changed, 200 insertions(+) diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index 9185f0867661..dfdd52e03ab1 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -1007,6 +1007,13 @@ given Geometry.

st_distance(geometry_a: geometry, geometry_b: geometry) → float

Returns the distance between the given geometries.

+st_distancesphere(geometry_a: geometry, geometry_b: geometry) → float

Returns the distance in meters between geometry_a and geometry_b assuming the coordinates represent lng/lat points on a sphere.

+

This function utilizes the S2 library for spherical calculations.

+
+st_distancespheroid(geometry_a: geometry, geometry_b: geometry) → float

Returns the distance in meters between geometry_a and geometry_b assuming the coordinates represent lng/lat points on a spheroid."\n\nWhen operating on a spheroid, this function will use the sphere to calculate the closest two points using S2. The spheroid distance between these two points is calculated using GeographicLib. This follows observed PostGIS behavior.

+

This function utilizes the S2 library for spherical calculations.

+

This function utilizes the GeographicLib library for spheroid calculations.

+
st_dwithin(geography_a: geography, geography_b: geography, distance: float) → bool

Returns true if any of geography_a is within distance meters of geography_b. Uses a spheroid to perform the operation."\n\nWhen operating on a spheroid, this function will use the sphere to calculate the closest two points using S2. The spheroid distance between these two points is calculated using GeographicLib. This follows observed PostGIS behavior.

The calculations performed are have a precision of 1cm.

This function utilizes the GeographicLib library for spheroid calculations.

diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index f939bc934bc7..48952251ca6b 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -1040,6 +1040,139 @@ Square overlapping left and right square Square (left) Square overlapping left and right square Square (right) 0 1.48660687473185 Square overlapping left and right square Square overlapping left and right square 0 1.48660687473185 +# ST_DistanceSphere, ST_DistanceSpheroid +query TTRR +SELECT + a.dsc, + b.dsc, + ST_DistanceSphere(a.geom, b.geom), + ST_DistanceSpheroid(a.geom, b.geom) +FROM geom_operators_test a +JOIN geom_operators_test b ON (1=1) +ORDER BY a.dsc, b.dsc +---- +Empty GeometryCollection Empty GeometryCollection NULL NULL +Empty GeometryCollection Empty LineString NULL NULL +Empty GeometryCollection Empty Point NULL NULL +Empty GeometryCollection Faraway point NULL NULL +Empty GeometryCollection Line going through left and right square NULL NULL +Empty GeometryCollection NULL NULL NULL +Empty GeometryCollection Point middle of Left Square NULL NULL +Empty GeometryCollection Point middle of Right Square NULL NULL +Empty GeometryCollection Square (left) NULL NULL +Empty GeometryCollection Square (right) NULL NULL +Empty GeometryCollection Square overlapping left and right square NULL NULL +Empty LineString Empty GeometryCollection NULL NULL +Empty LineString Empty LineString NULL NULL +Empty LineString Empty Point NULL NULL +Empty LineString Faraway point NULL NULL +Empty LineString Line going through left and right square NULL NULL +Empty LineString NULL NULL NULL +Empty LineString Point middle of Left Square NULL NULL +Empty LineString Point middle of Right Square NULL NULL +Empty LineString Square (left) NULL NULL +Empty LineString Square (right) NULL NULL +Empty LineString Square overlapping left and right square NULL NULL +Empty Point Empty GeometryCollection NULL NULL +Empty Point Empty LineString NULL NULL +Empty Point Empty Point NULL NULL +Empty Point Faraway point NULL NULL +Empty Point Line going through left and right square NULL NULL +Empty Point NULL NULL NULL +Empty Point Point middle of Left Square NULL NULL +Empty Point Point middle of Right Square NULL NULL +Empty Point Square (left) NULL NULL +Empty Point Square (right) NULL NULL +Empty Point Square overlapping left and right square NULL NULL +Faraway point Empty GeometryCollection NULL NULL +Faraway point Empty LineString NULL NULL +Faraway point Empty Point NULL NULL +Faraway point Faraway point 0 0 +Faraway point Line going through left and right square 707142.162247235 705577.648328755 +Faraway point NULL NULL NULL +Faraway point Point middle of Left Square 789521.780520966 788297.405265007 +Faraway point Point middle of Right Square 707142.162247235 705577.648328755 +Faraway point Square (left) 711311.966664979 710260.975395327 +Faraway point Square (right) 628519.033787529 627129.502610754 +Faraway point Square overlapping left and right square 628519.033787529 627129.502610754 +Line going through left and right square Empty GeometryCollection NULL NULL +Line going through left and right square Empty LineString NULL NULL +Line going through left and right square Empty Point NULL NULL +Line going through left and right square Faraway point 707142.162247235 705577.648328755 +Line going through left and right square Line going through left and right square 0 0 +Line going through left and right square NULL NULL NULL +Line going through left and right square Point middle of Left Square 0 0 +Line going through left and right square Point middle of Right Square 0 0 +Line going through left and right square Square (left) 0 0 +Line going through left and right square Square (right) 0 0 +Line going through left and right square Square overlapping left and right square 0 0 +NULL Empty GeometryCollection NULL NULL +NULL Empty LineString NULL NULL +NULL Empty Point NULL NULL +NULL Faraway point NULL NULL +NULL Line going through left and right square NULL NULL +NULL NULL NULL NULL +NULL Point middle of Left Square NULL NULL +NULL Point middle of Right Square NULL NULL +NULL Square (left) NULL NULL +NULL Square (right) NULL NULL +NULL Square overlapping left and right square NULL NULL +Point middle of Left Square Empty GeometryCollection NULL NULL +Point middle of Left Square Empty LineString NULL NULL +Point middle of Left Square Empty Point NULL NULL +Point middle of Left Square Faraway point 789521.780520966 788297.405265007 +Point middle of Left Square Line going through left and right square 0 0 +Point middle of Left Square NULL NULL NULL +Point middle of Left Square Point middle of Left Square 0 0 +Point middle of Left Square Point middle of Right Square 111190.845659241 111315.280354463 +Point middle of Left Square Square (left) 0 0 +Point middle of Left Square Square (right) 55595.4228296203 55657.6401772334 +Point middle of Left Square Square overlapping left and right square 44476.338279173 44526.1121572803 +Point middle of Right Square Empty GeometryCollection NULL NULL +Point middle of Right Square Empty LineString NULL NULL +Point middle of Right Square Empty Point NULL NULL +Point middle of Right Square Faraway point 707142.162247235 705577.648328755 +Point middle of Right Square Line going through left and right square 0 0 +Point middle of Right Square NULL NULL NULL +Point middle of Right Square Point middle of Left Square 111190.845659241 111315.280354463 +Point middle of Right Square Point middle of Right Square 0 0 +Point middle of Right Square Square (left) 55595.4228296203 55657.6401772334 +Point middle of Right Square Square (right) 0 0 +Point middle of Right Square Square overlapping left and right square 0 0 +Square (left) Empty GeometryCollection NULL NULL +Square (left) Empty LineString NULL NULL +Square (left) Empty Point NULL NULL +Square (left) Faraway point 711311.966664979 710260.975395327 +Square (left) Line going through left and right square 0 0 +Square (left) NULL NULL NULL +Square (left) Point middle of Left Square 0 0 +Square (left) Point middle of Right Square 55595.4228296203 55657.6401772334 +Square (left) Square (left) 0 0 +Square (left) Square (right) 0 0 +Square (left) Square overlapping left and right square 0 0 +Square (right) Empty GeometryCollection NULL NULL +Square (right) Empty LineString NULL NULL +Square (right) Empty Point NULL NULL +Square (right) Faraway point 628519.033787529 627129.502610754 +Square (right) Line going through left and right square 0 0 +Square (right) NULL NULL NULL +Square (right) Point middle of Left Square 55595.4228296203 55657.6401772334 +Square (right) Point middle of Right Square 0 0 +Square (right) Square (left) 0 0 +Square (right) Square (right) 0 0 +Square (right) Square overlapping left and right square 0 0 +Square overlapping left and right square Empty GeometryCollection NULL NULL +Square overlapping left and right square Empty LineString NULL NULL +Square overlapping left and right square Empty Point NULL NULL +Square overlapping left and right square Faraway point 628519.033787529 627129.502610754 +Square overlapping left and right square Line going through left and right square 0 0 +Square overlapping left and right square NULL NULL NULL +Square overlapping left and right square Point middle of Left Square 44476.338279173 44526.1121572803 +Square overlapping left and right square Point middle of Right Square 0 0 +Square overlapping left and right square Square (left) 0 0 +Square overlapping left and right square Square (right) 0 0 +Square overlapping left and right square Square overlapping left and right square 0 0 + # ST_LongestLine, ST_ShortestLine query TTTT SELECT diff --git a/pkg/sql/sem/builtins/geo_builtins.go b/pkg/sql/sem/builtins/geo_builtins.go index a8c034d01f52..60da08f279e9 100644 --- a/pkg/sql/sem/builtins/geo_builtins.go +++ b/pkg/sql/sem/builtins/geo_builtins.go @@ -1979,6 +1979,66 @@ The azimuth is angle is referenced from north, and is positive clockwise: North Volatility: tree.VolatilityImmutable, }, ), + "st_distancesphere": makeBuiltin( + defProps(), + geometryOverload2( + func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { + aGeog, err := a.Geometry.AsGeography() + if err != nil { + return nil, err + } + bGeog, err := b.Geometry.AsGeography() + if err != nil { + return nil, err + } + ret, err := geogfn.Distance(aGeog, bGeog, geogfn.UseSphere) + if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } + return nil, err + } + return tree.NewDFloat(tree.DFloat(ret)), nil + }, + types.Float, + infoBuilder{ + info: "Returns the distance in meters between geometry_a and geometry_b assuming the coordinates " + + "represent lng/lat points on a sphere.", + libraryUsage: usesS2, + }, + tree.VolatilityImmutable, + ), + ), + "st_distancespheroid": makeBuiltin( + defProps(), + geometryOverload2( + func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { + aGeog, err := a.Geometry.AsGeography() + if err != nil { + return nil, err + } + bGeog, err := b.Geometry.AsGeography() + if err != nil { + return nil, err + } + ret, err := geogfn.Distance(aGeog, bGeog, geogfn.UseSpheroid) + if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } + return nil, err + } + return tree.NewDFloat(tree.DFloat(ret)), nil + }, + types.Float, + infoBuilder{ + info: "Returns the distance in meters between geometry_a and geometry_b assuming the coordinates " + + "represent lng/lat points on a spheroid." + spheroidDistanceMessage, + libraryUsage: usesGeographicLib | usesS2, + }, + tree.VolatilityImmutable, + ), + ), "st_maxdistance": makeBuiltin( defProps(), geometryOverload2(