diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index 43cf3f441af0..1a24be108fec 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -1502,6 +1502,8 @@ from the given Geometry.
st_clipbybox2d(geometry: geometry, box2d: box2d) → geometry
Clips the geometry to conform to the bounding box specified by box2d.
st_closestpoint(geometry_a: geometry, geometry_b: geometry) → geometry
Returns the 2-dimensional point on geometry_a that is closest to geometry_b. This is the first point of the shortest line.
+st_collectionextract(geometry: geometry, type: int) → geometry
Given a collection, returns a multitype consisting only of elements of the specified type. If there are no elements of the given type, an EMPTY geometry is returned. Types are specified as 1=POINT, 2=LINESTRING, 3=POLYGON - other types are not supported.
st_collectionhomogenize(geometry: geometry) → geometry
Returns the “simplest” representation of a collection’s contents. Collections of a single type will be returned as an appopriate multitype, or a singleton if it only contains a single geometry.
diff --git a/pkg/geo/geomfn/distance.go b/pkg/geo/geomfn/distance.go index 245999a50ba6..fab17fcd430a 100644 --- a/pkg/geo/geomfn/distance.go +++ b/pkg/geo/geomfn/distance.go @@ -775,3 +775,22 @@ func HausdorffDistanceDensify(a, b geo.Geometry, densifyFrac float64) (*float64, } return &distance, nil } + +// ClosestPoint returns the first point located on geometry A on the shortest line between the geometries. +func ClosestPoint(a, b geo.Geometry) (geo.Geometry, error) { + shortestLine, err := ShortestLineString(a, b) + if err != nil { + return geo.Geometry{}, err + } + shortestLineT, err := shortestLine.AsGeomT() + if err != nil { + return geo.Geometry{}, err + } + closestPoint, err := geo.MakeGeometryFromPointCoords( + shortestLineT.(*geom.LineString).Coord(0).X(), + shortestLineT.(*geom.LineString).Coord(0).Y()) + if err != nil { + return geo.Geometry{}, err + } + return closestPoint, nil +} diff --git a/pkg/geo/geomfn/distance_test.go b/pkg/geo/geomfn/distance_test.go index 8db6767f288f..99887bf81832 100644 --- a/pkg/geo/geomfn/distance_test.go +++ b/pkg/geo/geomfn/distance_test.go @@ -18,6 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/geo" "github.com/cockroachdb/cockroach/pkg/geo/geos" "github.com/stretchr/testify/require" + "github.com/twpayne/go-geom" ) var distanceTestCases = []struct { @@ -889,3 +890,114 @@ func TestHausdorffDistanceDensify(t *testing.T) { requireMismatchingSRIDError(t, err) }) } + +func TestClosestPoint(t *testing.T) { + + testCases := []struct { + name string + geomA string + geomB string + expected string + }{ + {"Closest point between a POINT and LINESTRING", + "POINT(100 100)", + "LINESTRING(20 80, 98 190, 110 180, 50 75 )", + "POINT(100 100)", + }, + {"Closest point between a LINESTRING and POINT", + "LINESTRING(20 80, 98 190, 110 180, 50 75 )", + "POINT(100 100)", + "POINT(73.0769230769231 115.384615384615)", + }, + {"Closest point between 2 POLYGONS", + "POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))", + "POLYGON((15 50, 2 4, 5 6, 12 10, 15 50))", + "POINT(20 40)", + }, + {"Closest point between overlapping POLYGONS", + "POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))", + "POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))", + "POINT(175 150)", + }, + {"Closest point between partially-overlapping POLYGONS", + "POLYGON((10 10, 14 14, 20 14, 20 10, 10 10))", + "POLYGON((12 12, 16 12, 16 8, 12 8, 12 12))", + "POINT(12 12)", + }, + {"Closest point between MULTILINESTRING and POLYGON", + "MULTILINESTRING((0 0, 1 1, 2 2),(3 3, 4 4, 5 5))", + "POLYGON((10 10, 11 11, 14 11, 14 10, 10 10))", + "POINT(5 5)", + }, + {"Closest point between MULTILINESTRING and MULTIPOINT", + "MULTILINESTRING((0 0, 1 1, 2 2),(3 3, 4 4, 5 5))", + "MULTIPOINT((2 1),(10 10))", + "POINT(1.5 1.5)", + }, + {"Closest point between MULTIPOLYGON and MULTIPOINT", + "MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))", + "MULTIPOINT((20 10),(10 10))", + "POINT(4 4)", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gA, err := geo.ParseGeometry(tc.geomA) + require.NoError(t, err) + gB, err := geo.ParseGeometry(tc.geomB) + require.NoError(t, err) + + ret, err := ClosestPoint(gA, gB) + require.NoError(t, err) + retAsGeomT, err := ret.AsGeomT() + require.NoError(t, err) + + expected, err := geo.ParseGeometry(tc.expected) + require.NoError(t, err) + expectedAsGeomT, err := expected.AsGeomT() + require.NoError(t, err) + + require.InEpsilon(t, expectedAsGeomT.(*geom.Point).X(), retAsGeomT.(*geom.Point).X(), 2e-10) + require.InEpsilon(t, expectedAsGeomT.(*geom.Point).Y(), retAsGeomT.(*geom.Point).Y(), 2e-10) + }) + } + + testCasesEmpty := []struct { + name string + geomA string + geomB string + }{ + {"Closest point when both geometries are empty", + "LINESTRING EMPTY", + "LINESTRING EMPTY", + }, + {"Closest point when first geometry is empty", + "LINESTRING EMPTY", + "POINT(100 100)", + }, + {"Closest point when second geometry is empty", + "POINT(100 100)", + "LINESTRING EMPTY", + }, + } + + t.Run("errors for EMPTY geometries", func(t *testing.T) { + for _, tc := range testCasesEmpty { + t.Run(tc.name, func(t *testing.T) { + a, err := geo.ParseGeometry(tc.geomA) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.geomB) + require.NoError(t, err) + _, err = ClosestPoint(a, b) + require.Error(t, err) + require.True(t, geo.IsEmptyGeometryError(err)) + }) + } + }) + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := ClosestPoint(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB) + requireMismatchingSRIDError(t, err) + }) +} diff --git a/pkg/geo/geopb/geopb.go b/pkg/geo/geopb/geopb.go index 622a49129e79..3c70bd617fb9 100644 --- a/pkg/geo/geopb/geopb.go +++ b/pkg/geo/geopb/geopb.go @@ -10,13 +10,25 @@ package geopb -import "fmt" +import ( + "fmt" + "unsafe" +) // EWKBHex returns the EWKB-hex version of this data type func (b *SpatialObject) EWKBHex() string { return fmt.Sprintf("%X", b.EWKB) } +// MemSize returns the size of the spatial object in memory. +func (b *SpatialObject) MemSize() uintptr { + var bboxSize uintptr + if bbox := b.BoundingBox; bbox != nil { + bboxSize = unsafe.Sizeof(*bbox) + } + return unsafe.Sizeof(*b) + bboxSize + uintptr(len(b.EWKB)) +} + // MultiType returns the corresponding multi-type for a shape type, or unset // if there is no multi-type. func (s ShapeType) MultiType() ShapeType { diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index f31300d4e7cb..6e709edfc3e8 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -2133,160 +2133,161 @@ Square overlapping left and right square Square (right) Square overlapping left and right square Square overlapping left and right square 0 5.55111512312578e-17 # ST_LongestLine, ST_ShortestLine -query TTTT +query TTTTT SELECT a.dsc, b.dsc, ST_AsText(ST_LongestLine(a.geom, b.geom)), - ST_AsText(ST_ShortestLine(a.geom, b.geom)) + ST_AsText(ST_ShortestLine(a.geom, b.geom)), + ST_AsText(ST_ClosestPoint(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 Nested Geometry Collection 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 Nested Geometry Collection 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 Nested Geometry Collection 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 LINESTRING (5 5, 5 5) LINESTRING (5 5, 5 5) -Faraway point Line going through left and right square LINESTRING (5 5, -0.5 0.5) LINESTRING (5 5, 0.5 0.5) -Faraway point NULL NULL NULL -Faraway point Nested Geometry Collection LINESTRING (5 5, 0 0) LINESTRING (5 5, 0 0) -Faraway point Point middle of Left Square LINESTRING (5 5, -0.5 0.5) LINESTRING (5 5, -0.5 0.5) -Faraway point Point middle of Right Square LINESTRING (5 5, 0.5 0.5) LINESTRING (5 5, 0.5 0.5) -Faraway point Square (left) LINESTRING (5 5, -1 0) LINESTRING (5 5, 0 1) -Faraway point Square (right) LINESTRING (5 5, 0 0) LINESTRING (5 5, 1 1) -Faraway point Square overlapping left and right square LINESTRING (5 5, -0.1 0) LINESTRING (5 5, 1 1) -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 LINESTRING (-0.5 0.5, 5 5) LINESTRING (0.5 0.5, 5 5) -Line going through left and right square Line going through left and right square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) -Line going through left and right square NULL NULL NULL -Line going through left and right square Nested Geometry Collection LINESTRING (-0.5 0.5, 0 0) LINESTRING (0 0.5, 0 0) -Line going through left and right square Point middle of Left Square LINESTRING (0.5 0.5, -0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) -Line going through left and right square Point middle of Right Square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) -Line going through left and right square Square (left) LINESTRING (0.5 0.5, -1 0) LINESTRING (-0.5 0.5, -0.5 0.5) -Line going through left and right square Square (right) LINESTRING (-0.5 0.5, 1 0) LINESTRING (0 0.5, 0 0.5) -Line going through left and right square Square overlapping left and right square LINESTRING (-0.5 0.5, 1 0) LINESTRING (-0.1 0.5, -0.1 0.5) -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 Nested Geometry Collection 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 -Nested Geometry Collection Empty GeometryCollection NULL NULL -Nested Geometry Collection Empty LineString NULL NULL -Nested Geometry Collection Empty Point NULL NULL -Nested Geometry Collection Faraway point LINESTRING (0 0, 5 5) LINESTRING (0 0, 5 5) -Nested Geometry Collection Line going through left and right square LINESTRING (0 0, -0.5 0.5) LINESTRING (0 0, 0 0.5) -Nested Geometry Collection NULL NULL NULL -Nested Geometry Collection Nested Geometry Collection LINESTRING (0 0, 0 0) LINESTRING (0 0, 0 0) -Nested Geometry Collection Point middle of Left Square LINESTRING (0 0, -0.5 0.5) LINESTRING (0 0, -0.5 0.5) -Nested Geometry Collection Point middle of Right Square LINESTRING (0 0, 0.5 0.5) LINESTRING (0 0, 0.5 0.5) -Nested Geometry Collection Square (left) LINESTRING (0 0, -1 1) LINESTRING (0 0, 0 0) -Nested Geometry Collection Square (right) LINESTRING (0 0, 1 1) LINESTRING (0 0, 0 0) -Nested Geometry Collection Square overlapping left and right square LINESTRING (0 0, 1 1) LINESTRING (0 0, 0 0) -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 LINESTRING (-0.5 0.5, 5 5) LINESTRING (-0.5 0.5, 5 5) -Point middle of Left Square Line going through left and right square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) -Point middle of Left Square NULL NULL NULL -Point middle of Left Square Nested Geometry Collection LINESTRING (-0.5 0.5, 0 0) LINESTRING (-0.5 0.5, 0 0) -Point middle of Left Square Point middle of Left Square LINESTRING (-0.5 0.5, -0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) -Point middle of Left Square Point middle of Right Square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (-0.5 0.5, 0.5 0.5) -Point middle of Left Square Square (left) LINESTRING (-0.5 0.5, 0 0) LINESTRING (-0.5 0.5, -0.5 0.5) -Point middle of Left Square Square (right) LINESTRING (-0.5 0.5, 1 0) LINESTRING (-0.5 0.5, 0 0.5) -Point middle of Left Square Square overlapping left and right square LINESTRING (-0.5 0.5, 1 0) LINESTRING (-0.5 0.5, -0.1 0.5) -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 LINESTRING (0.5 0.5, 5 5) LINESTRING (0.5 0.5, 5 5) -Point middle of Right Square Line going through left and right square LINESTRING (0.5 0.5, -0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) -Point middle of Right Square NULL NULL NULL -Point middle of Right Square Nested Geometry Collection LINESTRING (0.5 0.5, 0 0) LINESTRING (0.5 0.5, 0 0) -Point middle of Right Square Point middle of Left Square LINESTRING (0.5 0.5, -0.5 0.5) LINESTRING (0.5 0.5, -0.5 0.5) -Point middle of Right Square Point middle of Right Square LINESTRING (0.5 0.5, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) -Point middle of Right Square Square (left) LINESTRING (0.5 0.5, -1 1) LINESTRING (0.5 0.5, 0 0.5) -Point middle of Right Square Square (right) LINESTRING (0.5 0.5, 1 0) LINESTRING (0.5 0.5, 0.5 0.5) -Point middle of Right Square Square overlapping left and right square LINESTRING (0.5 0.5, -0.1 1) LINESTRING (0.5 0.5, 0.5 0.5) -Square (left) Empty GeometryCollection NULL NULL -Square (left) Empty LineString NULL NULL -Square (left) Empty Point NULL NULL -Square (left) Faraway point LINESTRING (-1 0, 5 5) LINESTRING (0 1, 5 5) -Square (left) Line going through left and right square LINESTRING (-1 0, 0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) -Square (left) NULL NULL NULL -Square (left) Nested Geometry Collection LINESTRING (-1 1, 0 0) LINESTRING (0 0, 0 0) -Square (left) Point middle of Left Square LINESTRING (0 0, -0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) -Square (left) Point middle of Right Square LINESTRING (-1 1, 0.5 0.5) LINESTRING (0 0.5, 0.5 0.5) -Square (left) Square (left) LINESTRING (-1 0, 0 1) LINESTRING (-1 0, -1 0) -Square (left) Square (right) LINESTRING (-1 0, 1 1) LINESTRING (0 0, 0 0) -Square (left) Square overlapping left and right square LINESTRING (-1 0, 1 1) LINESTRING (-0.1 0, -0.1 0) -Square (right) Empty GeometryCollection NULL NULL -Square (right) Empty LineString NULL NULL -Square (right) Empty Point NULL NULL -Square (right) Faraway point LINESTRING (0 0, 5 5) LINESTRING (1 1, 5 5) -Square (right) Line going through left and right square LINESTRING (1 0, -0.5 0.5) LINESTRING (0 0.5, 0 0.5) -Square (right) NULL NULL NULL -Square (right) Nested Geometry Collection LINESTRING (1 1, 0 0) LINESTRING (0 0, 0 0) -Square (right) Point middle of Left Square LINESTRING (1 0, -0.5 0.5) LINESTRING (0 0.5, -0.5 0.5) -Square (right) Point middle of Right Square LINESTRING (1 0, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) -Square (right) Square (left) LINESTRING (1 0, -1 1) LINESTRING (0 0, 0 0) -Square (right) Square (right) LINESTRING (0 0, 1 1) LINESTRING (0 0, 0 0) -Square (right) Square overlapping left and right square LINESTRING (1 0, -0.1 1) LINESTRING (0 0, 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 LINESTRING (-0.1 0, 5 5) LINESTRING (1 1, 5 5) -Square overlapping left and right square Line going through left and right square LINESTRING (1 0, -0.5 0.5) LINESTRING (-0.1 0.5, -0.1 0.5) -Square overlapping left and right square NULL NULL NULL -Square overlapping left and right square Nested Geometry Collection LINESTRING (1 1, 0 0) LINESTRING (0 0, 0 0) -Square overlapping left and right square Point middle of Left Square LINESTRING (1 0, -0.5 0.5) LINESTRING (-0.1 0.5, -0.5 0.5) -Square overlapping left and right square Point middle of Right Square LINESTRING (-0.1 1, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) -Square overlapping left and right square Square (left) LINESTRING (1 0, -1 1) LINESTRING (-0.1 0, -0.1 0) -Square overlapping left and right square Square (right) LINESTRING (-0.1 0, 1 1) LINESTRING (0 0, 0 0) -Square overlapping left and right square Square overlapping left and right square LINESTRING (-0.1 0, 1 1) LINESTRING (-0.1 0, -0.1 0) +Empty GeometryCollection Empty GeometryCollection NULL NULL NULL +Empty GeometryCollection Empty LineString NULL NULL NULL +Empty GeometryCollection Empty Point NULL NULL NULL +Empty GeometryCollection Faraway point NULL NULL NULL +Empty GeometryCollection Line going through left and right square NULL NULL NULL +Empty GeometryCollection NULL NULL NULL NULL +Empty GeometryCollection Nested Geometry Collection NULL NULL NULL +Empty GeometryCollection Point middle of Left Square NULL NULL NULL +Empty GeometryCollection Point middle of Right Square NULL NULL NULL +Empty GeometryCollection Square (left) NULL NULL NULL +Empty GeometryCollection Square (right) NULL NULL NULL +Empty GeometryCollection Square overlapping left and right square NULL NULL NULL +Empty LineString Empty GeometryCollection NULL NULL NULL +Empty LineString Empty LineString NULL NULL NULL +Empty LineString Empty Point NULL NULL NULL +Empty LineString Faraway point NULL NULL NULL +Empty LineString Line going through left and right square NULL NULL NULL +Empty LineString NULL NULL NULL NULL +Empty LineString Nested Geometry Collection NULL NULL NULL +Empty LineString Point middle of Left Square NULL NULL NULL +Empty LineString Point middle of Right Square NULL NULL NULL +Empty LineString Square (left) NULL NULL NULL +Empty LineString Square (right) NULL NULL NULL +Empty LineString Square overlapping left and right square NULL NULL NULL +Empty Point Empty GeometryCollection NULL NULL NULL +Empty Point Empty LineString NULL NULL NULL +Empty Point Empty Point NULL NULL NULL +Empty Point Faraway point NULL NULL NULL +Empty Point Line going through left and right square NULL NULL NULL +Empty Point NULL NULL NULL NULL +Empty Point Nested Geometry Collection NULL NULL NULL +Empty Point Point middle of Left Square NULL NULL NULL +Empty Point Point middle of Right Square NULL NULL NULL +Empty Point Square (left) NULL NULL NULL +Empty Point Square (right) NULL NULL NULL +Empty Point Square overlapping left and right square NULL NULL NULL +Faraway point Empty GeometryCollection NULL NULL NULL +Faraway point Empty LineString NULL NULL NULL +Faraway point Empty Point NULL NULL NULL +Faraway point Faraway point LINESTRING (5 5, 5 5) LINESTRING (5 5, 5 5) POINT (5 5) +Faraway point Line going through left and right square LINESTRING (5 5, -0.5 0.5) LINESTRING (5 5, 0.5 0.5) POINT (5 5) +Faraway point NULL NULL NULL NULL +Faraway point Nested Geometry Collection LINESTRING (5 5, 0 0) LINESTRING (5 5, 0 0) POINT (5 5) +Faraway point Point middle of Left Square LINESTRING (5 5, -0.5 0.5) LINESTRING (5 5, -0.5 0.5) POINT (5 5) +Faraway point Point middle of Right Square LINESTRING (5 5, 0.5 0.5) LINESTRING (5 5, 0.5 0.5) POINT (5 5) +Faraway point Square (left) LINESTRING (5 5, -1 0) LINESTRING (5 5, 0 1) POINT (5 5) +Faraway point Square (right) LINESTRING (5 5, 0 0) LINESTRING (5 5, 1 1) POINT (5 5) +Faraway point Square overlapping left and right square LINESTRING (5 5, -0.1 0) LINESTRING (5 5, 1 1) POINT (5 5) +Line going through left and right square Empty GeometryCollection NULL NULL NULL +Line going through left and right square Empty LineString NULL NULL NULL +Line going through left and right square Empty Point NULL NULL NULL +Line going through left and right square Faraway point LINESTRING (-0.5 0.5, 5 5) LINESTRING (0.5 0.5, 5 5) POINT (0.5 0.5) +Line going through left and right square Line going through left and right square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Line going through left and right square NULL NULL NULL NULL +Line going through left and right square Nested Geometry Collection LINESTRING (-0.5 0.5, 0 0) LINESTRING (0 0.5, 0 0) POINT (0 0.5) +Line going through left and right square Point middle of Left Square LINESTRING (0.5 0.5, -0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Line going through left and right square Point middle of Right Square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Line going through left and right square Square (left) LINESTRING (0.5 0.5, -1 0) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Line going through left and right square Square (right) LINESTRING (-0.5 0.5, 1 0) LINESTRING (0 0.5, 0 0.5) POINT (0 0.5) +Line going through left and right square Square overlapping left and right square LINESTRING (-0.5 0.5, 1 0) LINESTRING (-0.1 0.5, -0.1 0.5) POINT (-0.1 0.5) +NULL Empty GeometryCollection NULL NULL NULL +NULL Empty LineString NULL NULL NULL +NULL Empty Point NULL NULL NULL +NULL Faraway point NULL NULL NULL +NULL Line going through left and right square NULL NULL NULL +NULL NULL NULL NULL NULL +NULL Nested Geometry Collection NULL NULL NULL +NULL Point middle of Left Square NULL NULL NULL +NULL Point middle of Right Square NULL NULL NULL +NULL Square (left) NULL NULL NULL +NULL Square (right) NULL NULL NULL +NULL Square overlapping left and right square NULL NULL NULL +Nested Geometry Collection Empty GeometryCollection NULL NULL NULL +Nested Geometry Collection Empty LineString NULL NULL NULL +Nested Geometry Collection Empty Point NULL NULL NULL +Nested Geometry Collection Faraway point LINESTRING (0 0, 5 5) LINESTRING (0 0, 5 5) POINT (0 0) +Nested Geometry Collection Line going through left and right square LINESTRING (0 0, -0.5 0.5) LINESTRING (0 0, 0 0.5) POINT (0 0) +Nested Geometry Collection NULL NULL NULL NULL +Nested Geometry Collection Nested Geometry Collection LINESTRING (0 0, 0 0) LINESTRING (0 0, 0 0) POINT (0 0) +Nested Geometry Collection Point middle of Left Square LINESTRING (0 0, -0.5 0.5) LINESTRING (0 0, -0.5 0.5) POINT (0 0) +Nested Geometry Collection Point middle of Right Square LINESTRING (0 0, 0.5 0.5) LINESTRING (0 0, 0.5 0.5) POINT (0 0) +Nested Geometry Collection Square (left) LINESTRING (0 0, -1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Nested Geometry Collection Square (right) LINESTRING (0 0, 1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Nested Geometry Collection Square overlapping left and right square LINESTRING (0 0, 1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Point middle of Left Square Empty GeometryCollection NULL NULL NULL +Point middle of Left Square Empty LineString NULL NULL NULL +Point middle of Left Square Empty Point NULL NULL NULL +Point middle of Left Square Faraway point LINESTRING (-0.5 0.5, 5 5) LINESTRING (-0.5 0.5, 5 5) POINT (-0.5 0.5) +Point middle of Left Square Line going through left and right square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Point middle of Left Square NULL NULL NULL NULL +Point middle of Left Square Nested Geometry Collection LINESTRING (-0.5 0.5, 0 0) LINESTRING (-0.5 0.5, 0 0) POINT (-0.5 0.5) +Point middle of Left Square Point middle of Left Square LINESTRING (-0.5 0.5, -0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Point middle of Left Square Point middle of Right Square LINESTRING (-0.5 0.5, 0.5 0.5) LINESTRING (-0.5 0.5, 0.5 0.5) POINT (-0.5 0.5) +Point middle of Left Square Square (left) LINESTRING (-0.5 0.5, 0 0) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Point middle of Left Square Square (right) LINESTRING (-0.5 0.5, 1 0) LINESTRING (-0.5 0.5, 0 0.5) POINT (-0.5 0.5) +Point middle of Left Square Square overlapping left and right square LINESTRING (-0.5 0.5, 1 0) LINESTRING (-0.5 0.5, -0.1 0.5) POINT (-0.5 0.5) +Point middle of Right Square Empty GeometryCollection NULL NULL NULL +Point middle of Right Square Empty LineString NULL NULL NULL +Point middle of Right Square Empty Point NULL NULL NULL +Point middle of Right Square Faraway point LINESTRING (0.5 0.5, 5 5) LINESTRING (0.5 0.5, 5 5) POINT (0.5 0.5) +Point middle of Right Square Line going through left and right square LINESTRING (0.5 0.5, -0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Point middle of Right Square NULL NULL NULL NULL +Point middle of Right Square Nested Geometry Collection LINESTRING (0.5 0.5, 0 0) LINESTRING (0.5 0.5, 0 0) POINT (0.5 0.5) +Point middle of Right Square Point middle of Left Square LINESTRING (0.5 0.5, -0.5 0.5) LINESTRING (0.5 0.5, -0.5 0.5) POINT (0.5 0.5) +Point middle of Right Square Point middle of Right Square LINESTRING (0.5 0.5, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Point middle of Right Square Square (left) LINESTRING (0.5 0.5, -1 1) LINESTRING (0.5 0.5, 0 0.5) POINT (0.5 0.5) +Point middle of Right Square Square (right) LINESTRING (0.5 0.5, 1 0) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Point middle of Right Square Square overlapping left and right square LINESTRING (0.5 0.5, -0.1 1) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Square (left) Empty GeometryCollection NULL NULL NULL +Square (left) Empty LineString NULL NULL NULL +Square (left) Empty Point NULL NULL NULL +Square (left) Faraway point LINESTRING (-1 0, 5 5) LINESTRING (0 1, 5 5) POINT (0 1) +Square (left) Line going through left and right square LINESTRING (-1 0, 0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Square (left) NULL NULL NULL NULL +Square (left) Nested Geometry Collection LINESTRING (-1 1, 0 0) LINESTRING (0 0, 0 0) POINT (0 0) +Square (left) Point middle of Left Square LINESTRING (0 0, -0.5 0.5) LINESTRING (-0.5 0.5, -0.5 0.5) POINT (-0.5 0.5) +Square (left) Point middle of Right Square LINESTRING (-1 1, 0.5 0.5) LINESTRING (0 0.5, 0.5 0.5) POINT (0 0.5) +Square (left) Square (left) LINESTRING (-1 0, 0 1) LINESTRING (-1 0, -1 0) POINT (-1 0) +Square (left) Square (right) LINESTRING (-1 0, 1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Square (left) Square overlapping left and right square LINESTRING (-1 0, 1 1) LINESTRING (-0.1 0, -0.1 0) POINT (-0.1 0) +Square (right) Empty GeometryCollection NULL NULL NULL +Square (right) Empty LineString NULL NULL NULL +Square (right) Empty Point NULL NULL NULL +Square (right) Faraway point LINESTRING (0 0, 5 5) LINESTRING (1 1, 5 5) POINT (1 1) +Square (right) Line going through left and right square LINESTRING (1 0, -0.5 0.5) LINESTRING (0 0.5, 0 0.5) POINT (0 0.5) +Square (right) NULL NULL NULL NULL +Square (right) Nested Geometry Collection LINESTRING (1 1, 0 0) LINESTRING (0 0, 0 0) POINT (0 0) +Square (right) Point middle of Left Square LINESTRING (1 0, -0.5 0.5) LINESTRING (0 0.5, -0.5 0.5) POINT (0 0.5) +Square (right) Point middle of Right Square LINESTRING (1 0, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Square (right) Square (left) LINESTRING (1 0, -1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Square (right) Square (right) LINESTRING (0 0, 1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Square (right) Square overlapping left and right square LINESTRING (1 0, -0.1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Square overlapping left and right square Empty GeometryCollection NULL NULL NULL +Square overlapping left and right square Empty LineString NULL NULL NULL +Square overlapping left and right square Empty Point NULL NULL NULL +Square overlapping left and right square Faraway point LINESTRING (-0.1 0, 5 5) LINESTRING (1 1, 5 5) POINT (1 1) +Square overlapping left and right square Line going through left and right square LINESTRING (1 0, -0.5 0.5) LINESTRING (-0.1 0.5, -0.1 0.5) POINT (-0.1 0.5) +Square overlapping left and right square NULL NULL NULL NULL +Square overlapping left and right square Nested Geometry Collection LINESTRING (1 1, 0 0) LINESTRING (0 0, 0 0) POINT (0 0) +Square overlapping left and right square Point middle of Left Square LINESTRING (1 0, -0.5 0.5) LINESTRING (-0.1 0.5, -0.5 0.5) POINT (-0.1 0.5) +Square overlapping left and right square Point middle of Right Square LINESTRING (-0.1 1, 0.5 0.5) LINESTRING (0.5 0.5, 0.5 0.5) POINT (0.5 0.5) +Square overlapping left and right square Square (left) LINESTRING (1 0, -1 1) LINESTRING (-0.1 0, -0.1 0) POINT (-0.1 0) +Square overlapping left and right square Square (right) LINESTRING (-0.1 0, 1 1) LINESTRING (0 0, 0 0) POINT (0 0) +Square overlapping left and right square Square overlapping left and right square LINESTRING (-0.1 0, 1 1) LINESTRING (-0.1 0, -0.1 0) POINT (-0.1 0) # ST_Difference query TTT diff --git a/pkg/sql/sem/builtins/aggregate_builtins.go b/pkg/sql/sem/builtins/aggregate_builtins.go index ae5f0475a6b5..de976922d790 100644 --- a/pkg/sql/sem/builtins/aggregate_builtins.go +++ b/pkg/sql/sem/builtins/aggregate_builtins.go @@ -372,7 +372,9 @@ var aggregates = map[string]builtinDefinition{ func( params []*types.T, evalCtx *tree.EvalContext, arguments tree.Datums, ) tree.AggregateFunc { - return &stMakeLineAgg{} + return &stMakeLineAgg{ + acc: evalCtx.Mon.MakeBoundAccount(), + } }, infoBuilder{ info: "Forms a LineString from Point, MultiPoint or LineStrings. Other shapes will be ignored.", @@ -627,7 +629,9 @@ func makeSTUnionBuiltin() builtinDefinition { func( params []*types.T, evalCtx *tree.EvalContext, arguments tree.Datums, ) tree.AggregateFunc { - return &stUnionAgg{} + return &stUnionAgg{ + acc: evalCtx.Mon.MakeBoundAccount(), + } }, infoBuilder{ info: "Applies a spatial union to the geometries provided.", @@ -640,11 +644,12 @@ func makeSTUnionBuiltin() builtinDefinition { type stMakeLineAgg struct { flatCoords []float64 layout geom.Layout + acc mon.BoundAccount } // Add implements the AggregateFunc interface. func (agg *stMakeLineAgg) Add( - _ context.Context, firstArg tree.Datum, otherArgs ...tree.Datum, + ctx context.Context, firstArg tree.Datum, otherArgs ...tree.Datum, ) error { if firstArg == tree.DNull { return nil @@ -667,6 +672,9 @@ func (agg *stMakeLineAgg) Add( } switch g.(type) { case *geom.Point, *geom.LineString, *geom.MultiPoint: + if err := agg.acc.Grow(ctx, int64(len(g.FlatCoords())*8)); err != nil { + return err + } agg.flatCoords = append(agg.flatCoords, g.FlatCoords()...) } return nil @@ -685,12 +693,15 @@ func (agg *stMakeLineAgg) Result() (tree.Datum, error) { } // Reset implements the AggregateFunc interface. -func (agg *stMakeLineAgg) Reset(context.Context) { +func (agg *stMakeLineAgg) Reset(ctx context.Context) { agg.flatCoords = agg.flatCoords[:0] + agg.acc.Empty(ctx) } // Close implements the AggregateFunc interface. -func (agg *stMakeLineAgg) Close(context.Context) {} +func (agg *stMakeLineAgg) Close(ctx context.Context) { + agg.acc.Close(ctx) +} // Size implements the AggregateFunc interface. func (agg *stMakeLineAgg) Size() int64 { @@ -701,11 +712,14 @@ type stUnionAgg struct { srid geopb.SRID // TODO(#geo): store the current union object in C memory, to avoid the EWKB round trips. ewkb geopb.EWKB + acc mon.BoundAccount set bool } // Add implements the AggregateFunc interface. -func (agg *stUnionAgg) Add(_ context.Context, firstArg tree.Datum, otherArgs ...tree.Datum) error { +func (agg *stUnionAgg) Add( + ctx context.Context, firstArg tree.Datum, otherArgs ...tree.Datum, +) error { if firstArg == tree.DNull { return nil } @@ -723,12 +737,18 @@ func (agg *stUnionAgg) Add(_ context.Context, firstArg tree.Datum, otherArgs ... } return geo.NewMismatchingSRIDsError(geomArg.Geometry.SpatialObject(), c.SpatialObject()) } + if err := agg.acc.Grow(ctx, int64(len(geomArg.EWKB()))); err != nil { + return err + } var err error // TODO(#geo):We are allocating a slice for the result each time we // call geos.Union in cStringToSafeGoBytes. // We could change geos.Union to accept the existing slice. agg.ewkb, err = geos.Union(agg.ewkb, geomArg.EWKB()) - return err + if err != nil { + return err + } + return agg.acc.ResizeTo(ctx, int64(len(agg.ewkb))) } // Result implements the AggregateFunc interface. @@ -744,13 +764,16 @@ func (agg *stUnionAgg) Result() (tree.Datum, error) { } // Reset implements the AggregateFunc interface. -func (agg *stUnionAgg) Reset(context.Context) { +func (agg *stUnionAgg) Reset(ctx context.Context) { agg.ewkb = nil agg.set = false + agg.acc.Empty(ctx) } // Close implements the AggregateFunc interface. -func (agg *stUnionAgg) Close(context.Context) {} +func (agg *stUnionAgg) Close(ctx context.Context) { + agg.acc.Close(ctx) +} // Size implements the AggregateFunc interface. func (agg *stUnionAgg) Size() int64 { diff --git a/pkg/sql/sem/builtins/geo_builtins.go b/pkg/sql/sem/builtins/geo_builtins.go index 565b1cd1fbb8..4d9e4ed457c4 100644 --- a/pkg/sql/sem/builtins/geo_builtins.go +++ b/pkg/sql/sem/builtins/geo_builtins.go @@ -3671,6 +3671,27 @@ The paths themselves are given in the direction of the first geometry.`, tree.VolatilityImmutable, ), ), + "st_closestpoint": makeBuiltin( + defProps(), + geometryOverload2( + func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { + ret, err := geomfn.ClosestPoint(a.Geometry, b.Geometry) + if err != nil { + if geo.IsEmptyGeometryError(err) { + return tree.DNull, nil + } + return nil, err + } + geom := tree.NewDGeometry(ret) + return geom, nil + }, + types.Geometry, + infoBuilder{ + info: `Returns the 2-dimensional point on geometry_a that is closest to geometry_b. This is the first point of the shortest line.`, + }, + tree.VolatilityImmutable, + ), + ), "st_symdifference": makeBuiltin( defProps(), geometryOverload2( @@ -5269,7 +5290,6 @@ The swap_ordinate_string parameter is a 2-character string naming the ordinates "st_buildarea": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48892}), "st_chaikinsmoothing": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48894}), "st_cleangeometry": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48895}), - "st_closestpoint": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48896}), "st_clusterdbscan": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48898}), "st_clusterintersecting": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48899}), "st_clusterkmeans": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48900}), diff --git a/pkg/sql/sem/tree/datum.go b/pkg/sql/sem/tree/datum.go index c4ca26c41f0f..1f96f24bcbaa 100644 --- a/pkg/sql/sem/tree/datum.go +++ b/pkg/sql/sem/tree/datum.go @@ -2826,7 +2826,7 @@ func (d *DGeography) Format(ctx *FmtCtx) { // Size implements the Datum interface. func (d *DGeography) Size() uintptr { - return unsafe.Sizeof(*d) + return d.Geography.SpatialObjectRef().MemSize() } // DGeometry is the Geometry Datum. @@ -2934,7 +2934,7 @@ func (d *DGeometry) Format(ctx *FmtCtx) { // Size implements the Datum interface. func (d *DGeometry) Size() uintptr { - return unsafe.Sizeof(*d) + return d.Geometry.SpatialObjectRef().MemSize() } // DBox2D is the Datum representation of the Box2D type. diff --git a/pkg/sql/sem/tree/datum_test.go b/pkg/sql/sem/tree/datum_test.go index 2e5f1413e415..5f1d7f7cb86d 100644 --- a/pkg/sql/sem/tree/datum_test.go +++ b/pkg/sql/sem/tree/datum_test.go @@ -1128,3 +1128,29 @@ var _ tree.ParseTimeContext = testParseTimeContext{} func (t testParseTimeContext) GetRelativeParseTime() time.Time { return time.Time(t) } + +func TestGeospatialSize(t *testing.T) { + defer leaktest.AfterTest(t)() + testCases := []struct { + wkt string + expected uintptr + }{ + {"SRID=4004;POINT EMPTY", 73}, + {"SRID=4326;LINESTRING(0 0, 10 0)", 125}, + } + + for _, tc := range testCases { + t.Run(tc.wkt, func(t *testing.T) { + t.Run("geometry", func(t *testing.T) { + g, err := tree.ParseDGeometry(tc.wkt) + require.NoError(t, err) + require.Equal(t, tc.expected, g.Size()) + }) + t.Run("geography", func(t *testing.T) { + g, err := tree.ParseDGeography(tc.wkt) + require.NoError(t, err) + require.Equal(t, tc.expected, g.Size()) + }) + }) + } +}