diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index 4c1865acd871..4fffdb571b8b 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -1241,6 +1241,12 @@ has no relationship with the commit order of concurrent transactions.

| d e y_off | | y | \ 0 0 1 / \ 0 /

+st_angle(line1: geometry, line2: geometry) → float

Returns the clockwise angle between two LINESTRING geometries, treating them as vectors between their start- and endpoints. Returns NULL if any vectors have 0 length.

+
+st_angle(point1: geometry, point2: geometry, point3: geometry) → float

Returns the clockwise angle between the vectors formed by point2,point1 and point2,point3. The arguments must be POINT geometries. Returns NULL if any vectors have 0 length.

+
+st_angle(point1: geometry, point2: geometry, point3: geometry, point4: geometry) → float

Returns the clockwise angle between the vectors formed by point1,point2 and point3,point4. The arguments must be POINT geometries. Returns NULL if any vectors have 0 length.

+
st_area(geography: geography) → float

Returns the area of the given geography in meters^2. Uses a spheroid to perform the operation.

This function utilizes the GeographicLib library for spheroid calculations.

@@ -1377,6 +1383,9 @@ has no relationship with the commit order of concurrent transactions.

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

Returns the azimuth in radians of the segment defined by the given point geometries, or NULL if the two points are coincident.

The azimuth is angle is referenced from north, and is positive clockwise: North = 0; East = π/2; South = π; West = 3π/2.

+st_boundary(geometry: geometry) → geometry

Returns the closure of the combinatorial boundary of this Geometry.

+

This function utilizes the GEOS module.

+
st_box2dfromgeohash(geohash: string) → box2d

Return a Box2D from a GeoHash string with max precision.

st_box2dfromgeohash(geohash: string, precision: int) → box2d

Return a Box2D from a GeoHash string with supplied precision.

@@ -1549,6 +1558,9 @@ from the given Geometry.

st_dfullywithinexclusive(geometry_a: geometry, geometry_b: geometry, distance: float) → bool

Returns true if every pair of points comprising geometry_a and geometry_b are within distance units, exclusive. In other words, the ST_MaxDistance between geometry_a and geometry_b is less than distance units.

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

+st_difference(geometry_a: geometry, geometry_b: geometry) → geometry

Returns the difference of two Geometries.

+

This function utilizes the GEOS module.

+
st_dimension(geometry: geometry) → int

Returns the number of topological dimensions of a given Geometry.

st_disjoint(geometry_a: geometry, geometry_b: geometry) → bool

Returns true if geometry_a does not overlap, touch or is within geometry_b.

@@ -1641,6 +1653,12 @@ Bottom Left.

st_forcepolygoncw(geometry: geometry) → geometry

Returns a Geometry where all Polygon objects have exterior rings in the clockwise orientation and interior rings in the counter-clockwise orientation. Non-Polygon objects are unchanged.

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

Returns the Frechet distance between the given geometries.

+

This function utilizes the GEOS module.

+
+st_frechetdistance(geometry_a: geometry, geometry_b: geometry, densify_frac: float) → float

Returns the Frechet distance between the given geometries, with the given segment densification (range 0.0-1.0, -1 to disable).

+

This function utilizes the GEOS module.

+
st_geogfromewkb(val: bytes) → geography

Returns the Geography from an EWKB representation.

st_geogfromewkt(val: string) → geography

Returns the Geography from an EWKT representation.

@@ -1706,6 +1724,12 @@ Bottom Left.

st_geomfromwkb(val: bytes) → geometry

Returns the Geometry from a WKB (or EWKB) representation.

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

Returns the Hausdorff distance between the given geometries.

+

This function utilizes the GEOS module.

+
+st_hausdorffdistance(geometry_a: geometry, geometry_b: geometry, densify_frac: float) → float

Returns the Hausdorff distance between the given geometries, with the given segment densification (range 0.0-1.0).

+

This function utilizes the GEOS module.

+
st_interiorringn(geometry: geometry, n: int) → geometry

Returns the n-th (1-indexed) interior ring of a Polygon as a LineString. Returns NULL if the shape is not a Polygon, or the ring does not exist.

st_intersection(geography_a: geography, geography_b: geography) → geography

Returns the point intersections of the given geographies.

@@ -1838,6 +1862,10 @@ calculated, the result is transformed back into a Geography with SRID 4326.

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

Returns the maximum distance across every pair of points comprising the given geometries. Note if the geometries are the same, it will return the maximum distance between the geometry’s vertexes.

+st_minimumclearance(geometry: geometry) → float

Returns the minimum distance a vertex can move before producing an invalid geometry. Returns Infinity if no minimum clearance can be found (e.g. for a single point).

+
+st_minimumclearanceline(geometry: geometry) → geometry

Returns a LINESTRING spanning the minimum distance a vertex can move before producing an invalid geometry. If no minimum clearance can be found (e.g. for a single point), an empty LINESTRING is returned.

+
st_mlinefromtext(str: string, srid: int) → geometry

Returns the Geometry from a WKT or EWKT representation with an SRID. If the shape underneath is not MultiLineString, NULL is returned. If the SRID is present in both the EWKT and the argument, the argument value is used.

st_mlinefromtext(val: string) → geometry

Returns the Geometry from a WKT or EWKT representation. If the shape underneath is not MultiLineString, NULL is returned.

@@ -1969,6 +1997,8 @@ calculated, the result is transformed back into a Geography with SRID 4326.

st_polyfromwkb(wkb: bytes, srid: int) → geometry

Returns the Geometry from a WKB (or EWKB) representation with an SRID. If the shape underneath is not Polygon, NULL is returned.

+st_polygon(geometry: geometry, srid: int) → geometry

Returns a new Polygon from the given LineString and sets its SRID. It is equivalent to ST_MakePolygon with a single argument followed by ST_SetSRID.

+
st_polygonfromtext(str: string, srid: int) → geometry

Returns the Geometry from a WKT or EWKT representation with an SRID. If the shape underneath is not Polygon, NULL is returned. If the SRID is present in both the EWKT and the argument, the argument value is used.

st_polygonfromtext(val: string) → geometry

Returns the Geometry from a WKT or EWKT representation. If the shape underneath is not Polygon, NULL is returned.

@@ -1987,6 +2017,9 @@ Negative azimuth values and values greater than 2π (360 degrees) are supported. st_relate(geometry_a: geometry, geometry_b: geometry) → string

Returns the DE-9IM spatial relation between geometry_a and geometry_b.

This function utilizes the GEOS module.

+st_relate(geometry_a: geometry, geometry_b: geometry, bnr: int) → string

Returns the DE-9IM spatial relation between geometry_a and geometry_b using the given boundary node rule (1:OGC/MOD2, 2:Endpoint, 3:MultivalentEndpoint, 4:MonovalentEndpoint).

+

This function utilizes the GEOS module.

+
st_relate(geometry_a: geometry, geometry_b: geometry, pattern: string) → bool

Returns whether the DE-9IM spatial relation between geometry_a and geometry_b matches the DE-9IM pattern.

This function utilizes the GEOS module.

diff --git a/pkg/geo/geomfn/angle.go b/pkg/geo/geomfn/angle.go new file mode 100644 index 000000000000..36f1c5b9025f --- /dev/null +++ b/pkg/geo/geomfn/angle.go @@ -0,0 +1,114 @@ +// Copyright 2020 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package geomfn + +import ( + "math" + + "github.com/cockroachdb/cockroach/pkg/geo" + "github.com/cockroachdb/errors" + "github.com/twpayne/go-geom" +) + +// Angle calculates the clockwise angle between two vectors given by points +// p1,p2 and p3,p4. If p4 is an empty geometry (of any type, to follow PostGIS +// behavior), it instead calculates the clockwise angle between p2,p1 and p2,p3. +func Angle(g1, g2, g3, g4 geo.Geometry) (*float64, error) { + if g4.Empty() { + g1, g2, g3, g4 = g2, g1, g2, g3 + } + + if g1.SRID() != g2.SRID() { + return nil, geo.NewMismatchingSRIDsError(g1.SpatialObject(), g2.SpatialObject()) + } + if g1.SRID() != g3.SRID() { + return nil, geo.NewMismatchingSRIDsError(g1.SpatialObject(), g3.SpatialObject()) + } + if g1.SRID() != g4.SRID() { + return nil, geo.NewMismatchingSRIDsError(g1.SpatialObject(), g4.SpatialObject()) + } + + t1, err := g1.AsGeomT() + if err != nil { + return nil, err + } + t2, err := g2.AsGeomT() + if err != nil { + return nil, err + } + t3, err := g3.AsGeomT() + if err != nil { + return nil, err + } + t4, err := g4.AsGeomT() + if err != nil { + return nil, err + } + + p1, p1ok := t1.(*geom.Point) + p2, p2ok := t2.(*geom.Point) + p3, p3ok := t3.(*geom.Point) + p4, p4ok := t4.(*geom.Point) + + if !p1ok || !p2ok || !p3ok || !p4ok { + return nil, errors.New("arguments must be POINT geometries") + } + if p1.Empty() || p2.Empty() || p3.Empty() || p4.Empty() { + return nil, errors.New("received EMPTY geometry") + } + + return angleFromCoords(p1.Coords(), p2.Coords(), p3.Coords(), p4.Coords()), nil +} + +// AngleLineString calculates the clockwise angle between two linestrings, +// treating them as vectors between the start- and endpoints. Type conflicts +// or empty geometries return nil (as opposed to Angle which errors), to +// follow PostGIS behavior. +func AngleLineString(g1, g2 geo.Geometry) (*float64, error) { + if g1.SRID() != g2.SRID() { + return nil, geo.NewMismatchingSRIDsError(g1.SpatialObject(), g2.SpatialObject()) + } + t1, err := g1.AsGeomT() + if err != nil { + return nil, err + } + t2, err := g2.AsGeomT() + if err != nil { + return nil, err + } + l1, l1ok := t1.(*geom.LineString) + l2, l2ok := t2.(*geom.LineString) + if !l1ok || !l2ok || l1.Empty() || l2.Empty() { + return nil, nil // follow PostGIS behavior + } + return angleFromCoords( + l1.Coord(0), l1.Coord(l1.NumCoords()-1), l2.Coord(0), l2.Coord(l2.NumCoords()-1)), nil +} + +// angleFromCoords returns the clockwise angle between the vectors c1,c2 and +// c3,c4. For compatibility with PostGIS, it returns nil if any vectors have +// length 0. +func angleFromCoords(c1, c2, c3, c4 geom.Coord) *float64 { + a := coordSub(c2, c1) + b := coordSub(c4, c3) + if (a.X() == 0 && a.Y() == 0) || (b.X() == 0 && b.Y() == 0) { + return nil + } + // We want the clockwise angle, not the smallest interior angle, so can't use cosine formula. + angle := math.Atan2(-coordDet(a, b), coordDot(a, b)) + // We want the angle in the interval [0,2π), while Atan2 returns [-π,π] + if angle == -0.0 { + angle = 0.0 + } else if angle < 0 { + angle += 2 * math.Pi + } + return &angle +} diff --git a/pkg/geo/geomfn/angle_test.go b/pkg/geo/geomfn/angle_test.go new file mode 100644 index 000000000000..ff27db052d75 --- /dev/null +++ b/pkg/geo/geomfn/angle_test.go @@ -0,0 +1,148 @@ +// Copyright 2020 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package geomfn + +import ( + "fmt" + "math" + "testing" + + "github.com/cockroachdb/cockroach/pkg/geo" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" + "github.com/stretchr/testify/require" +) + +func TestAngle(t *testing.T) { + pf := func(f float64) *float64 { + return &f + } + testCases := []struct { + wkt1 string + wkt2 string + wkt3 string + wkt4 string + expected *float64 + }{ + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (0 1)", pf(0)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (1 1)", pf(1.0 / 4.0 * math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (1 0)", pf(2.0 / 4.0 * math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (1 -1)", pf(3.0 / 4.0 * math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (0 -1)", pf(math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (-1 -1)", pf(5.0 / 4.0 * math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (-1 0)", pf(6.0 / 4.0 * math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 0)", "POINT (-1 1)", pf(7.0 / 4.0 * math.Pi)}, + {"POINT (10 10)", "POINT (10 11)", "POINT (-100 -100)", "POINT (-99 -99)", pf(1.0 / 4.0 * math.Pi)}, + {"POINT (1.2 -3.4)", "POINT (-5.6 7.8)", "POINT (-0.12 0.34)", "POINT (0.56 -0.78)", pf(math.Pi)}, + {"POINT (56 76)", "POINT (-34 -71)", "POINT (-3 27)", "POINT (-81 -36)", pf(0.3420080366154973)}, + {"POINT (0 0)", "POINT (1 0)", "POINT (1 1)", "POINT EMPTY", pf(1.0 / 2.0 * math.Pi)}, + {"POINT (0 0)", "POINT (0 1)", "POINT (0 -1)", "POINT EMPTY", pf(0)}, + // Ignore type of final empty geometry, following PostGIS. + {"POINT (0 0)", "POINT (1 0)", "POINT (1 -1)", "MULTIPOLYGON EMPTY", pf(3.0 / 2.0 * math.Pi)}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%v, %v, %v, %v", tc.wkt1, tc.wkt2, tc.wkt3, tc.wkt4), func(t *testing.T) { + srid := geopb.SRID(4000) + g1, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt1), srid, true) + require.NoError(t, err) + g2, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt2), srid, true) + require.NoError(t, err) + g3, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt3), srid, true) + require.NoError(t, err) + g4, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt4), srid, true) + require.NoError(t, err) + + angle, err := Angle(g1, g2, g3, g4) + require.NoError(t, err) + if tc.expected != nil && angle != nil { + require.Equal(t, *tc.expected, *angle) + } else { + require.Equal(t, tc.expected, angle) + } + }) + } + + errorTestCases := []struct { + wkt1 string + wkt2 string + wkt3 string + wkt4 string + }{ + {"POINT EMPTY", "POINT EMPTY", "POINT EMPTY", "POINT EMPTY"}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 0 1)"}, + {"LINESTRING EMPTY", "LINESTRING EMPTY", "LINESTRING EMPTY", "LINESTRING EMPTY"}, + } + t.Run("errors on invalid arguments", func(t *testing.T) { + for _, tc := range errorTestCases { + t.Run(fmt.Sprintf("%v, %v, %v, %v", tc.wkt1, tc.wkt2, tc.wkt3, tc.wkt4), func(t *testing.T) { + srid := geopb.SRID(4000) + g1, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt1), srid, true) + require.NoError(t, err) + g2, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt2), srid, true) + require.NoError(t, err) + g3, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt3), srid, true) + require.NoError(t, err) + g4, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt4), srid, true) + require.NoError(t, err) + + _, err = Angle(g1, g2, g3, g4) + require.Error(t, err) + }) + } + }) +} + +func TestAngleLineString(t *testing.T) { + pf := func(f float64) *float64 { + return &f + } + testCases := []struct { + wkt1 string + wkt2 string + expected *float64 + }{ + {"LINESTRING (0 0, 0 0)", "LINESTRING (0 0, 0 1)", nil}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 0 0)", nil}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 0 1)", pf(0)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 1 1)", pf(1.0 / 4.0 * math.Pi)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 1 0)", pf(2.0 / 4.0 * math.Pi)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 1 -1)", pf(3.0 / 4.0 * math.Pi)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, 0 -1)", pf(math.Pi)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, -1 -1)", pf(5.0 / 4.0 * math.Pi)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, -1 0)", pf(6.0 / 4.0 * math.Pi)}, + {"LINESTRING (0 0, 0 1)", "LINESTRING (0 0, -1 1)", pf(7.0 / 4.0 * math.Pi)}, + {"LINESTRING (10 10, 10 11)", "LINESTRING (-100 -100, -99 -99)", pf(1.0 / 4.0 * math.Pi)}, + {"LINESTRING (1.2 -3.4, -5.6 7.8)", "LINESTRING (-0.12 0.34, 0.56 -0.78)", pf(math.Pi)}, + {"LINESTRING (56 76, -34 -71)", "LINESTRING (-3 27, -81 -36)", pf(0.3420080366154973)}, + // Returns nil on type errors, to follow PostGIS behavior. + {"LINESTRING EMPTY", "LINESTRING EMPTY", nil}, + {"GEOMETRYCOLLECTION EMPTY", "MULTIPOLYGON EMPTY", nil}, + {"POINT (0 1)", "POINT(1 0)", nil}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%v, %v", tc.wkt1, tc.wkt2), func(t *testing.T) { + srid := geopb.SRID(4000) + g1, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt1), srid, true) + require.NoError(t, err) + g2, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt2), srid, true) + require.NoError(t, err) + + angle, err := AngleLineString(g1, g2) + require.NoError(t, err) + if tc.expected != nil && angle != nil { + require.Equal(t, *tc.expected, *angle) + } else { + require.Equal(t, tc.expected, angle) + } + }) + } +} diff --git a/pkg/geo/geomfn/coord.go b/pkg/geo/geomfn/coord.go index e4b8fb1d768c..f68e083ac4fd 100644 --- a/pkg/geo/geomfn/coord.go +++ b/pkg/geo/geomfn/coord.go @@ -31,6 +31,11 @@ func coordMul(a geom.Coord, s float64) geom.Coord { return geom.Coord{a.X() * s, a.Y() * s} } +// coordDet returns the determinant of the 2x2 matrix formed by the vectors a and b. +func coordDet(a geom.Coord, b geom.Coord) float64 { + return a.X()*b.Y() - b.X()*a.Y() +} + // coordDot returns the dot product of two coords if the coord was a vector. func coordDot(a geom.Coord, b geom.Coord) float64 { return a.X()*b.X() + a.Y()*b.Y() diff --git a/pkg/geo/geomfn/de9im.go b/pkg/geo/geomfn/de9im.go index e3aa1d0ad094..40b4318d5e71 100644 --- a/pkg/geo/geomfn/de9im.go +++ b/pkg/geo/geomfn/de9im.go @@ -25,6 +25,15 @@ func Relate(a geo.Geometry, b geo.Geometry) (string, error) { return geos.Relate(a.EWKB(), b.EWKB()) } +// RelateBoundaryNodeRule returns the DE-9IM relation between A and B using +// the given boundary node rule (as specified by GEOS). +func RelateBoundaryNodeRule(a, b geo.Geometry, bnr int) (string, error) { + if a.SRID() != b.SRID() { + return "", geo.NewMismatchingSRIDsError(a.SpatialObject(), b.SpatialObject()) + } + return geos.RelateBoundaryNodeRule(a.EWKB(), b.EWKB(), bnr) +} + // RelatePattern returns whether the DE-9IM relation between A and B matches. func RelatePattern(a geo.Geometry, b geo.Geometry, pattern string) (bool, error) { if a.SRID() != b.SRID() { diff --git a/pkg/geo/geomfn/de9im_test.go b/pkg/geo/geomfn/de9im_test.go index 6849b900dec1..9203899a10c3 100644 --- a/pkg/geo/geomfn/de9im_test.go +++ b/pkg/geo/geomfn/de9im_test.go @@ -36,6 +36,40 @@ func TestRelate(t *testing.T) { } } +func TestRelateBoundaryNodeRule(t *testing.T) { + testCases := []struct { + a geo.Geometry + b geo.Geometry + bnr int + expected string + }{ + {leftRect, rightRect, 1, "FF2F11212"}, + {leftRect, rightRect, 2, "FF2F11212"}, + {leftRect, rightRect, 3, "1F2F002F2"}, + {leftRect, rightRect, 4, "FF2F11212"}, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("tc:%d", i), func(t *testing.T) { + ret, err := RelateBoundaryNodeRule(tc.a, tc.b, tc.bnr) + require.NoError(t, err) + require.Equal(t, tc.expected, ret) + }) + } + + t.Run("errors on invalid BNR", func(t *testing.T) { + _, err := RelateBoundaryNodeRule(leftRect, rightRect, 0) + require.Error(t, err) + _, err = RelateBoundaryNodeRule(leftRect, rightRect, 5) + require.Error(t, err) + }) + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := RelateBoundaryNodeRule(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB, 1) + requireMismatchingSRIDError(t, err) + }) +} + func TestMatchesDE9IM(t *testing.T) { testCases := []struct { str string diff --git a/pkg/geo/geomfn/distance.go b/pkg/geo/geomfn/distance.go index 83eb127f84b2..245999a50ba6 100644 --- a/pkg/geo/geomfn/distance.go +++ b/pkg/geo/geomfn/distance.go @@ -15,6 +15,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/geo" "github.com/cockroachdb/cockroach/pkg/geo/geodist" + "github.com/cockroachdb/cockroach/pkg/geo/geos" "github.com/cockroachdb/errors" "github.com/twpayne/go-geom" "github.com/twpayne/go-geom/xy/lineintersector" @@ -709,3 +710,68 @@ func (c *geomDistanceCalculator) ClosestPointToEdge( } return geodist.Point{GeomPoint: coordAdd(e.V0.GeomPoint, coordMul(ab, r))}, true } + +// FrechetDistance calculates the Frechet distance between two geometries. +func FrechetDistance(a, b geo.Geometry) (*float64, error) { + if a.Empty() || b.Empty() { + return nil, nil + } + if a.SRID() != b.SRID() { + return nil, geo.NewMismatchingSRIDsError(a.SpatialObject(), b.SpatialObject()) + } + distance, err := geos.FrechetDistance(a.EWKB(), b.EWKB()) + if err != nil { + return nil, err + } + return &distance, nil +} + +// FrechetDistanceDensify calculates the Frechet distance between two geometries. +func FrechetDistanceDensify(a, b geo.Geometry, densifyFrac float64) (*float64, error) { + // For Frechet distance, we take <= 0 to disable the densifyFrac parameter. + // This differs from HausdorffDistance, but follows PostGIS behavior. + if densifyFrac <= 0 { + return FrechetDistance(a, b) + } + if a.Empty() || b.Empty() { + return nil, nil + } + if a.SRID() != b.SRID() { + return nil, geo.NewMismatchingSRIDsError(a.SpatialObject(), b.SpatialObject()) + } + distance, err := geos.FrechetDistanceDensify(a.EWKB(), b.EWKB(), densifyFrac) + if err != nil { + return nil, err + } + return &distance, nil +} + +// HausdorffDistance calculates the Hausdorff distance between two geometries. +func HausdorffDistance(a, b geo.Geometry) (*float64, error) { + if a.Empty() || b.Empty() { + return nil, nil + } + if a.SRID() != b.SRID() { + return nil, geo.NewMismatchingSRIDsError(a.SpatialObject(), b.SpatialObject()) + } + distance, err := geos.HausdorffDistance(a.EWKB(), b.EWKB()) + if err != nil { + return nil, err + } + return &distance, nil +} + +// HausdorffDistanceDensify calculates the Hausdorff distance between two geometries. +func HausdorffDistanceDensify(a, b geo.Geometry, densifyFrac float64) (*float64, error) { + if a.Empty() || b.Empty() { + return nil, nil + } + if a.SRID() != b.SRID() { + return nil, geo.NewMismatchingSRIDsError(a.SpatialObject(), b.SpatialObject()) + } + distance, err := geos.HausdorffDistanceDensify(a.EWKB(), b.EWKB(), densifyFrac) + if err != nil { + return nil, err + } + return &distance, nil +} diff --git a/pkg/geo/geomfn/distance_test.go b/pkg/geo/geomfn/distance_test.go index 101de91cb501..8db6767f288f 100644 --- a/pkg/geo/geomfn/distance_test.go +++ b/pkg/geo/geomfn/distance_test.go @@ -700,3 +700,192 @@ func TestShortestLineString(t *testing.T) { requireMismatchingSRIDError(t, err) }) } + +func TestFrechetDistance(t *testing.T) { + pf := func(f float64) *float64 { return &f } + + testCases := []struct { + a string + b string + expected *float64 + }{ + {"LINESTRING EMPTY", "LINESTRING EMPTY", nil}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING EMPTY", nil}, + {"LINESTRING EMPTY", "LINESTRING (0 0, 9 1, 2 2)", nil}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", pf(7.615773105863909)}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%v %v", tc.a, tc.b), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + + ret, err := FrechetDistance(a, b) + require.NoError(t, err) + if tc.expected != nil && ret != nil { + require.Equal(t, *tc.expected, *ret) + } else { + require.Equal(t, tc.expected, ret) + } + }) + } + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := FrechetDistance(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB) + requireMismatchingSRIDError(t, err) + }) +} + +func TestFrechetDistanceDensify(t *testing.T) { + pf := func(f float64) *float64 { return &f } + + testCases := []struct { + a string + b string + densify float64 + expected *float64 + }{ + {"LINESTRING EMPTY", "LINESTRING EMPTY", 0.5, nil}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING EMPTY", 0.5, nil}, + {"LINESTRING EMPTY", "LINESTRING (0 0, 9 1, 2 2)", 0.5, nil}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", -1, pf(7.615773105863909)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", -0.1, pf(7.615773105863909)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", 0.0, pf(7.615773105863909)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", 0.2, pf(6.627216610312356)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", 0.4, pf(6.666666666666667)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", 0.6, pf(6.670832032063167)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", 0.8, pf(7.615773105863909)}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", 1.0, pf(7.615773105863909)}, + } + + for _, tc := range testCases { + tc := tc + t.Run(fmt.Sprintf("%v %v densify %v", tc.a, tc.b, tc.densify), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + + ret, err := FrechetDistanceDensify(a, b, tc.densify) + require.NoError(t, err) + if tc.expected != nil && ret != nil { + require.Equal(t, *tc.expected, *ret) + } else { + require.Equal(t, tc.expected, ret) + } + }) + } + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := FrechetDistanceDensify(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB, 0.5) + requireMismatchingSRIDError(t, err) + }) +} + +func TestHausdorffDistance(t *testing.T) { + pf := func(f float64) *float64 { return &f } + + testCases := []struct { + a string + b string + expected *float64 + }{ + {"LINESTRING EMPTY", "LINESTRING EMPTY", nil}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING EMPTY", nil}, + {"LINESTRING EMPTY", "LINESTRING (0 0, 9 1, 2 2)", nil}, + {"LINESTRING (0 0, 3 7, 5 5)", "LINESTRING (0 0, 9 1, 2 2)", pf(5.656854249492381)}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%v %v", tc.a, tc.b), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + + ret, err := HausdorffDistance(a, b) + require.NoError(t, err) + if tc.expected != nil && ret != nil { + require.Equal(t, *tc.expected, *ret) + } else { + require.Equal(t, tc.expected, ret) + } + }) + } + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := HausdorffDistance(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB) + requireMismatchingSRIDError(t, err) + }) +} + +func TestHausdorffDistanceDensify(t *testing.T) { + pf := func(f float64) *float64 { return &f } + + testCases := []struct { + a string + b string + densify float64 + expected *float64 + }{ + {"LINESTRING EMPTY", "LINESTRING EMPTY", 0.5, nil}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING EMPTY", 0.5, nil}, + {"LINESTRING EMPTY", "LINESTRING (10 10, 10 150, 130 10)", 0.5, nil}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 0.2, pf(66)}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 0.4, pf(56.66666666666667)}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 0.6, pf(70)}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 0.8, pf(14.142135623730951)}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 1.0, pf(14.142135623730951)}, + } + + for _, tc := range testCases { + tc := tc + t.Run(fmt.Sprintf("%v %v densify %v", tc.a, tc.b, tc.densify), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + + ret, err := HausdorffDistanceDensify(a, b, tc.densify) + require.NoError(t, err) + if tc.expected != nil && ret != nil { + require.Equal(t, *tc.expected, *ret) + } else { + require.Equal(t, tc.expected, ret) + } + }) + } + + errorTestCases := []struct { + a string + b string + densify float64 + }{ + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", -1}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", -0.1}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 0.0}, + {"LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 1.1}, + } + + t.Run("errors on invalid densify fraction", func(t *testing.T) { + for _, tc := range errorTestCases { + tc := tc + t.Run(fmt.Sprintf("%v %v densify %v", tc.a, tc.b, tc.densify), func(t *testing.T) { + a, err := geo.ParseGeometry(tc.a) + require.NoError(t, err) + b, err := geo.ParseGeometry(tc.b) + require.NoError(t, err) + + _, err = HausdorffDistanceDensify(a, b, tc.densify) + require.Error(t, err) + }) + } + }) + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := HausdorffDistanceDensify(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB, 0.5) + requireMismatchingSRIDError(t, err) + }) +} diff --git a/pkg/geo/geomfn/make_geometry.go b/pkg/geo/geomfn/make_geometry.go index e11551f48f08..0dcc1c04889d 100644 --- a/pkg/geo/geomfn/make_geometry.go +++ b/pkg/geo/geomfn/make_geometry.go @@ -12,6 +12,7 @@ package geomfn import ( "github.com/cockroachdb/cockroach/pkg/geo" + "github.com/cockroachdb/cockroach/pkg/geo/geopb" "github.com/cockroachdb/errors" "github.com/twpayne/go-geom" ) @@ -52,3 +53,17 @@ func MakePolygon(outer geo.Geometry, interior ...geo.Geometry) (geo.Geometry, er } return geo.MakeGeometryFromGeomT(polygon) } + +// MakePolygonWithSRID is like MakePolygon but also sets the SRID, like ST_Polygon. +func MakePolygonWithSRID(g geo.Geometry, srid int) (geo.Geometry, error) { + polygon, err := MakePolygon(g) + if err != nil { + return geo.Geometry{}, err + } + t, err := polygon.AsGeomT() + if err != nil { + return geo.Geometry{}, err + } + geo.AdjustGeomTSRID(t, geopb.SRID(srid)) + return geo.MakeGeometryFromGeomT(t) +} diff --git a/pkg/geo/geomfn/make_geometry_test.go b/pkg/geo/geomfn/make_geometry_test.go index 9882f085e049..ef6debafa614 100644 --- a/pkg/geo/geomfn/make_geometry_test.go +++ b/pkg/geo/geomfn/make_geometry_test.go @@ -263,3 +263,42 @@ func TestMakePolygon(t *testing.T) { }) } } + +func TestMakePolygonWithSRID(t *testing.T) { + testCases := []struct { + name string + g string + srid int + expected string + }{ + { + "Single input variant - 2D", + "LINESTRING(75 29,77 29,77 29, 75 29)", + int(geopb.DefaultGeometrySRID), + "SRID=0;POLYGON((75 29,77 29,77 29,75 29))", + }, + { + "Single input variant - 2D with SRID", + "LINESTRING(75 29,77 29,77 29, 75 29)", + 4326, + "SRID=4326;POLYGON((75 29,77 29,77 29,75 29))", + }, + { + "Single input variant - 2D square with SRID", + "LINESTRING(40 80, 80 80, 80 40, 40 40, 40 80)", + 4000, + "SRID=4000;POLYGON((40 80, 80 80, 80 40, 40 40, 40 80))", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := geo.MustParseGeometry(tc.g) + polygon, err := MakePolygonWithSRID(g, tc.srid) + require.NoError(t, err) + expected := geo.MustParseGeometry(tc.expected) + require.Equal(t, expected, polygon) + require.EqualValues(t, tc.srid, polygon.SRID()) + }) + } +} diff --git a/pkg/geo/geomfn/topology_operations.go b/pkg/geo/geomfn/topology_operations.go index 6254da502169..b7f20ad23a61 100644 --- a/pkg/geo/geomfn/topology_operations.go +++ b/pkg/geo/geomfn/topology_operations.go @@ -15,6 +15,19 @@ import ( "github.com/cockroachdb/cockroach/pkg/geo/geos" ) +// Boundary returns the boundary of a given Geometry. +func Boundary(g geo.Geometry) (geo.Geometry, error) { + // follow PostGIS behavior + if g.Empty() { + return g, nil + } + boundaryEWKB, err := geos.Boundary(g.EWKB()) + if err != nil { + return geo.Geometry{}, err + } + return geo.ParseGeometryFromEWKB(boundaryEWKB) +} + // Centroid returns the Centroid of a given Geometry. func Centroid(g geo.Geometry) (geo.Geometry, error) { centroidEWKB, err := geos.Centroid(g.EWKB()) @@ -45,6 +58,22 @@ func ConvexHull(g geo.Geometry) (geo.Geometry, error) { return geo.ParseGeometryFromEWKB(convexHullEWKB) } +// Difference returns the difference between two given Geometries. +func Difference(a, b geo.Geometry) (geo.Geometry, error) { + // follow PostGIS behavior + if a.Empty() || b.Empty() { + return a, nil + } + if a.SRID() != b.SRID() { + return geo.Geometry{}, geo.NewMismatchingSRIDsError(a.SpatialObject(), b.SpatialObject()) + } + diffEWKB, err := geos.Difference(a.EWKB(), b.EWKB()) + if err != nil { + return geo.Geometry{}, err + } + return geo.ParseGeometryFromEWKB(diffEWKB) +} + // Simplify returns a simplified Geometry. func Simplify(g geo.Geometry, tolerance float64) (geo.Geometry, error) { simplifiedEWKB, err := geos.Simplify(g.EWKB(), tolerance) diff --git a/pkg/geo/geomfn/topology_operations_test.go b/pkg/geo/geomfn/topology_operations_test.go index 0c250ab0672e..53a4da5d1f86 100644 --- a/pkg/geo/geomfn/topology_operations_test.go +++ b/pkg/geo/geomfn/topology_operations_test.go @@ -20,6 +20,36 @@ import ( "github.com/twpayne/go-geom" ) +func TestBoundary(t *testing.T) { + testCases := []struct { + wkt string + expected string + }{ + {"POINT EMPTY", "POINT EMPTY"}, + {"POINT (1 1)", "GEOMETRYCOLLECTION EMPTY"}, + {"LINESTRING EMPTY", "LINESTRING EMPTY"}, + {"LINESTRING (100 150, 50 60, 70 80, 160 170)", "MULTIPOINT (100 150, 160 170)"}, + {"SRID=4000;LINESTRING (100 150, 50 60, 70 80, 160 170)", "SRID=4000;MULTIPOINT (100 150, 160 170)"}, + { + "POLYGON ((10 130, 50 190, 110 190, 140 150, 150 80, 100 10, 20 40, 10 130), (70 40, 100 50, 120 80, 80 110, 50 90, 70 40))", + "MULTILINESTRING ((10 130, 50 190, 110 190, 140 150, 150 80, 100 10, 20 40, 10 130), (70 40, 100 50, 120 80, 80 110, 50 90, 70 40))", + }, + } + + for _, tc := range testCases { + t.Run(tc.wkt, func(t *testing.T) { + g, err := geo.ParseGeometry(tc.wkt) + require.NoError(t, err) + ret, err := Boundary(g) + require.NoError(t, err) + + wkt, err := geo.SpatialObjectToEWKT(ret.SpatialObject(), 0) + require.NoError(t, err) + require.EqualValues(t, tc.expected, wkt) + }) + } +} + func TestCentroid(t *testing.T) { testCases := []struct { wkt string @@ -93,6 +123,39 @@ func TestConvexHull(t *testing.T) { } } +func TestDifference(t *testing.T) { + testCases := []struct { + wkt1 string + wkt2 string + expected string + }{ + {"POINT EMPTY", "LINESTRING EMPTY", "POINT EMPTY"}, + {"LINESTRING EMPTY", "POINT EMPTY", "LINESTRING EMPTY"}, + {"LINESTRING (50 100, 50 200)", "LINESTRING(50 50, 50 150)", "LINESTRING (50 150, 50 200)"}, + {"SRID=4000;LINESTRING (50 100, 50 200)", "SRID=4000;LINESTRING(50 50, 50 150)", "SRID=4000;LINESTRING (50 150, 50 200)"}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%v - %v", tc.wkt2, tc.wkt1), func(t *testing.T) { + g1, err := geo.ParseGeometry(tc.wkt1) + require.NoError(t, err) + g2, err := geo.ParseGeometry(tc.wkt2) + require.NoError(t, err) + ret, err := Difference(g1, g2) + require.NoError(t, err) + + wkt, err := geo.SpatialObjectToEWKT(ret.SpatialObject(), 0) + require.NoError(t, err) + require.EqualValues(t, tc.expected, wkt) + }) + } + + t.Run("errors if SRIDs mismatch", func(t *testing.T) { + _, err := Difference(mismatchingSRIDGeometryA, mismatchingSRIDGeometryB) + requireMismatchingSRIDError(t, err) + }) +} + func TestSimplify(t *testing.T) { testCases := []struct { wkt string diff --git a/pkg/geo/geomfn/unary_operators.go b/pkg/geo/geomfn/unary_operators.go index 24827f2f14eb..e4f11be18c9b 100644 --- a/pkg/geo/geomfn/unary_operators.go +++ b/pkg/geo/geomfn/unary_operators.go @@ -215,3 +215,20 @@ func Normalize(g geo.Geometry) (geo.Geometry, error) { } return geo.ParseGeometryFromEWKB(retEWKB) } + +// MinimumClearance returns the minimum distance a vertex can move to produce +// an invalid geometry, or infinity if no clearance was found. +func MinimumClearance(g geo.Geometry) (float64, error) { + return geos.MinimumClearance(g.EWKB()) +} + +// MinimumClearanceLine returns the line spanning the minimum distance a vertex +// can move to produce an invalid geometry, or an empty line if no clearance was +// found. +func MinimumClearanceLine(g geo.Geometry) (geo.Geometry, error) { + retEWKB, err := geos.MinimumClearanceLine(g.EWKB()) + if err != nil { + return geo.Geometry{}, err + } + return geo.ParseGeometryFromEWKB(retEWKB) +} diff --git a/pkg/geo/geomfn/unary_operators_test.go b/pkg/geo/geomfn/unary_operators_test.go index d264856929d9..31df5235ae7e 100644 --- a/pkg/geo/geomfn/unary_operators_test.go +++ b/pkg/geo/geomfn/unary_operators_test.go @@ -12,6 +12,7 @@ package geomfn import ( "fmt" + "math" "testing" "github.com/cockroachdb/cockroach/pkg/geo" @@ -204,3 +205,55 @@ func TestNormalize(t *testing.T) { }) } } + +func TestMinimumClearance(t *testing.T) { + testCases := []struct { + wkt string + expected float64 + }{ + {"POINT (1 1)", math.Inf(1)}, + {"POLYGON EMPTY", math.Inf(1)}, + {"POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0))", 0.00032}, + {"GEOMETRYCOLLECTION (POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0)), POINT (1 1))", 0.00032}, + } + + for _, tc := range testCases { + t.Run(tc.wkt, func(t *testing.T) { + g, err := geo.ParseGeometry(tc.wkt) + require.NoError(t, err) + ret, err := MinimumClearance(g) + require.NoError(t, err) + require.Equal(t, tc.expected, ret) + }) + } +} + +func TestMinimumClearanceLine(t *testing.T) { + testCases := []struct { + wkt string + expected string + }{ + {"POINT (1 1)", "LINESTRING EMPTY"}, + {"POLYGON EMPTY", "LINESTRING EMPTY"}, + {"POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0))", "LINESTRING (0.5 0.00032, 0.5 0)"}, + { + "GEOMETRYCOLLECTION (POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0)), POINT (1 1))", + "LINESTRING (0.5 0.00032, 0.5 0)", + }, + } + + for _, tc := range testCases { + t.Run(tc.wkt, func(t *testing.T) { + srid := geopb.SRID(4000) + g, err := geo.ParseGeometryFromEWKT(geopb.EWKT(tc.wkt), srid, true) + require.NoError(t, err) + + result, err := MinimumClearanceLine(g) + require.NoError(t, err) + wkt, err := geo.SpatialObjectToWKT(result.SpatialObject(), 10) + require.NoError(t, err) + require.EqualValues(t, tc.expected, wkt) + require.EqualValues(t, srid, result.SRID()) + }) + } +} diff --git a/pkg/geo/geos/geos.cc b/pkg/geo/geos/geos.cc index d5918342c9a9..5192380ec071 100644 --- a/pkg/geo/geos/geos.cc +++ b/pkg/geo/geos/geos.cc @@ -94,11 +94,16 @@ typedef CR_GEOS_Geometry (*CR_GEOS_MakeValid_r)(CR_GEOS_Handle, CR_GEOS_Geometry 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 int (*CR_GEOS_MinimumClearance_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double*); +typedef CR_GEOS_Geometry (*CR_GEOS_MinimumClearanceLine_r)(CR_GEOS_Handle, CR_GEOS_Geometry); typedef int (*CR_GEOS_Normalize_r)(CR_GEOS_Handle, CR_GEOS_Geometry); typedef CR_GEOS_Geometry (*CR_GEOS_LineMerge_r)(CR_GEOS_Handle, CR_GEOS_Geometry); +typedef CR_GEOS_Geometry (*CR_GEOS_Boundary_r)(CR_GEOS_Handle, CR_GEOS_Geometry); 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_Difference_r)(CR_GEOS_Handle, CR_GEOS_Geometry, + CR_GEOS_Geometry); typedef CR_GEOS_Geometry (*CR_GEOS_Simplify_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double); typedef CR_GEOS_Geometry (*CR_GEOS_TopologyPreserveSimplify_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double); @@ -112,6 +117,14 @@ typedef CR_GEOS_Geometry (*CR_GEOS_PointOnSurface_r)(CR_GEOS_Handle, CR_GEOS_Geo typedef CR_GEOS_Geometry (*CR_GEOS_Interpolate_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double); typedef int (*CR_GEOS_Distance_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry, double*); +typedef int (*CR_GEOS_FrechetDistance_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry, + double*); +typedef int (*CR_GEOS_FrechetDistanceDensify_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry, + double, double*); +typedef int (*CR_GEOS_HausdorffDistance_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry, + double*); +typedef int (*CR_GEOS_HausdorffDistanceDensify_r)(CR_GEOS_Handle, CR_GEOS_Geometry, + CR_GEOS_Geometry, double, double*); typedef char (*CR_GEOS_Covers_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry); typedef char (*CR_GEOS_CoveredBy_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry); @@ -125,6 +138,8 @@ typedef char (*CR_GEOS_Touches_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geom typedef char (*CR_GEOS_Within_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry); typedef char* (*CR_GEOS_Relate_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry); +typedef char* (*CR_GEOS_RelateBoundaryNodeRule_r)(CR_GEOS_Handle, CR_GEOS_Geometry, + CR_GEOS_Geometry, int); typedef char (*CR_GEOS_RelatePattern_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry, const char*); @@ -187,11 +202,15 @@ struct CR_GEOS { CR_GEOS_Area_r GEOSArea_r; CR_GEOS_Length_r GEOSLength_r; + CR_GEOS_MinimumClearance_r GEOSMinimumClearance_r; + CR_GEOS_MinimumClearanceLine_r GEOSMinimumClearanceLine_r; CR_GEOS_Normalize_r GEOSNormalize_r; CR_GEOS_LineMerge_r GEOSLineMerge_r; + CR_GEOS_Boundary_r GEOSBoundary_r; CR_GEOS_Centroid_r GEOSGetCentroid_r; CR_GEOS_ConvexHull_r GEOSConvexHull_r; + CR_GEOS_Difference_r GEOSDifference_r; CR_GEOS_Simplify_r GEOSSimplify_r; CR_GEOS_TopologyPreserveSimplify_r GEOSTopologyPreserveSimplify_r; CR_GEOS_Union_r GEOSUnion_r; @@ -202,6 +221,10 @@ struct CR_GEOS { CR_GEOS_Interpolate_r GEOSInterpolate_r; CR_GEOS_Distance_r GEOSDistance_r; + CR_GEOS_FrechetDistance_r GEOSFrechetDistance_r; + CR_GEOS_FrechetDistanceDensify_r GEOSFrechetDistanceDensify_r; + CR_GEOS_HausdorffDistance_r GEOSHausdorffDistance_r; + CR_GEOS_HausdorffDistanceDensify_r GEOSHausdorffDistanceDensify_r; CR_GEOS_Covers_r GEOSCovers_r; CR_GEOS_CoveredBy_r GEOSCoveredBy_r; @@ -215,6 +238,7 @@ struct CR_GEOS { CR_GEOS_Within_r GEOSWithin_r; CR_GEOS_Relate_r GEOSRelate_r; + CR_GEOS_RelateBoundaryNodeRule_r GEOSRelateBoundaryNodeRule_r; CR_GEOS_RelatePattern_r GEOSRelatePattern_r; CR_GEOS_WKBWriter_create_r GEOSWKBWriter_create_r; @@ -273,9 +297,13 @@ struct CR_GEOS { INIT(GEOSMakeValid_r); INIT(GEOSArea_r); INIT(GEOSLength_r); + INIT(GEOSMinimumClearance_r); + INIT(GEOSMinimumClearanceLine_r); INIT(GEOSNormalize_r); INIT(GEOSLineMerge_r); INIT(GEOSisSimple_r); + INIT(GEOSBoundary_r); + INIT(GEOSDifference_r); INIT(GEOSGetCentroid_r); INIT(GEOSConvexHull_r); INIT(GEOSSimplify_r); @@ -286,6 +314,10 @@ struct CR_GEOS { INIT(GEOSSymDifference_r); INIT(GEOSInterpolate_r); INIT(GEOSDistance_r); + INIT(GEOSFrechetDistance_r); + INIT(GEOSFrechetDistanceDensify_r); + INIT(GEOSHausdorffDistance_r); + INIT(GEOSHausdorffDistanceDensify_r); INIT(GEOSCovers_r); INIT(GEOSCoveredBy_r); INIT(GEOSContains_r); @@ -297,6 +329,7 @@ struct CR_GEOS { INIT(GEOSTouches_r); INIT(GEOSWithin_r); INIT(GEOSRelate_r); + INIT(GEOSRelateBoundaryNodeRule_r); INIT(GEOSRelatePattern_r); INIT(GEOSSharedPaths_r); INIT(GEOSWKTReader_create_r); @@ -538,6 +571,42 @@ CR_GEOS_Status CR_GEOS_Length(CR_GEOS* lib, CR_GEOS_Slice a, double* ret) { return CR_GEOS_UnaryOperator(lib, lib->GEOSLength_r, a, ret); } +CR_GEOS_Status CR_GEOS_MinimumClearance(CR_GEOS* lib, CR_GEOS_Slice g, double* ret) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + auto geom = CR_GEOS_GeometryFromSlice(lib, handle, g); + *ret = 0; + if (geom != nullptr) { + auto r = lib->GEOSMinimumClearance_r(handle, geom, ret); + if (r == 2) { + if (error.length() == 0) { + error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE); + } + } + lib->GEOSGeom_destroy_r(handle, geom); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + +CR_GEOS_Status CR_GEOS_MinimumClearanceLine(CR_GEOS* lib, CR_GEOS_Slice g, + CR_GEOS_String* clearanceEWKB) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + *clearanceEWKB = {.data = NULL, .len = 0}; + + auto geom = CR_GEOS_GeometryFromSlice(lib, handle, g); + if (geom != nullptr) { + auto clearance = lib->GEOSMinimumClearanceLine_r(handle, geom); + auto srid = lib->GEOSGetSRID_r(handle, clearance); + CR_GEOS_writeGeomToEWKB(lib, handle, clearance, clearanceEWKB, srid); + lib->GEOSGeom_destroy_r(handle, geom); + } + + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + CR_GEOS_Status CR_GEOS_Normalize(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* normalizedEWKB) { std::string error; auto handle = initHandleWithErrorBuffer(lib, &error); @@ -705,6 +774,24 @@ CR_GEOS_Status CR_GEOS_MakeValid(CR_GEOS* lib, CR_GEOS_Slice g, CR_GEOS_String* // Topology operators. // +CR_GEOS_Status CR_GEOS_Boundary(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* boundaryEWKB) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a); + *boundaryEWKB = {.data = NULL, .len = 0}; + if (geom != nullptr) { + auto boundaryGeom = lib->GEOSBoundary_r(handle, geom); + if (boundaryGeom != nullptr) { + auto srid = lib->GEOSGetSRID_r(handle, geom); + CR_GEOS_writeGeomToEWKB(lib, handle, boundaryGeom, boundaryEWKB, srid); + lib->GEOSGeom_destroy_r(handle, boundaryGeom); + } + lib->GEOSGeom_destroy_r(handle, geom); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + CR_GEOS_Status CR_GEOS_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* centroidEWKB) { std::string error; auto handle = initHandleWithErrorBuffer(lib, &error); @@ -741,6 +828,31 @@ CR_GEOS_Status CR_GEOS_ConvexHull(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* return toGEOSString(error.data(), error.length()); } +CR_GEOS_Status CR_GEOS_Difference(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + CR_GEOS_String* diffEWKB) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + auto geomA = CR_GEOS_GeometryFromSlice(lib, handle, a); + auto geomB = CR_GEOS_GeometryFromSlice(lib, handle, b); + *diffEWKB = {.data = NULL, .len = 0}; + if (geomA != nullptr && geomB != nullptr) { + auto diffGeom = lib->GEOSDifference_r(handle, geomA, geomB); + if (diffGeom != nullptr) { + auto srid = lib->GEOSGetSRID_r(handle, geomA); + CR_GEOS_writeGeomToEWKB(lib, handle, diffGeom, diffEWKB, srid); + lib->GEOSGeom_destroy_r(handle, diffGeom); + } + } + if (geomA != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomA); + } + if (geomB != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomB); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + CR_GEOS_Status CR_GEOS_Simplify(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* simplifyEWKB, double tolerance) { std::string error; @@ -909,6 +1021,73 @@ CR_GEOS_Status CR_GEOS_Interpolate(CR_GEOS* lib, CR_GEOS_Slice a, double distanc CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double* ret) { return CR_GEOS_BinaryOperator(lib, lib->GEOSDistance_r, a, b, ret); } +CR_GEOS_Status CR_GEOS_FrechetDistance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double* ret) { + return CR_GEOS_BinaryOperator(lib, lib->GEOSFrechetDistance_r, a, b, ret); +} + +CR_GEOS_Status CR_GEOS_FrechetDistanceDensify(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double densifyFrac, double* ret) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + + auto wkbReader = lib->GEOSWKBReader_create_r(handle); + auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len); + auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len); + lib->GEOSWKBReader_destroy_r(handle, wkbReader); + + if (geomA != nullptr && geomB != nullptr) { + auto r = lib->GEOSFrechetDistanceDensify_r(handle, geomA, geomB, densifyFrac, ret); + // ret == 0 indicates an exception. + if (r == 0) { + if (error.length() == 0) { + error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE); + } + } + } + if (geomA != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomA); + } + if (geomB != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomB); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + +CR_GEOS_Status CR_GEOS_HausdorffDistance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double* ret) { + return CR_GEOS_BinaryOperator(lib, lib->GEOSHausdorffDistance_r, a, b, ret); +} + +CR_GEOS_Status CR_GEOS_HausdorffDistanceDensify(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double densifyFrac, double* ret) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + + auto wkbReader = lib->GEOSWKBReader_create_r(handle); + auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len); + auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len); + lib->GEOSWKBReader_destroy_r(handle, wkbReader); + + if (geomA != nullptr && geomB != nullptr) { + auto r = lib->GEOSHausdorffDistanceDensify_r(handle, geomA, geomB, densifyFrac, ret); + // ret == 0 indicates an exception. + if (r == 0) { + if (error.length() == 0) { + error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE); + } + } + } + if (geomA != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomA); + } + if (geomB != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomB); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} // // Binary predicates @@ -1017,6 +1196,33 @@ CR_GEOS_Status CR_GEOS_Relate(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR return toGEOSString(error.data(), error.length()); } +CR_GEOS_Status CR_GEOS_RelateBoundaryNodeRule(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + int bnr, CR_GEOS_String* ret) { + std::string error; + auto handle = initHandleWithErrorBuffer(lib, &error); + + auto wkbReader = lib->GEOSWKBReader_create_r(handle); + auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len); + auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len); + lib->GEOSWKBReader_destroy_r(handle, wkbReader); + + if (geomA != nullptr && geomB != nullptr) { + auto r = lib->GEOSRelateBoundaryNodeRule_r(handle, geomA, geomB, bnr); + if (r != NULL) { + *ret = toGEOSString(r, strlen(r)); + lib->GEOSFree_r(handle, r); + } + } + if (geomA != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomA); + } + if (geomB != nullptr) { + lib->GEOSGeom_destroy_r(handle, geomB); + } + lib->GEOS_finish_r(handle); + return toGEOSString(error.data(), error.length()); +} + CR_GEOS_Status CR_GEOS_RelatePattern(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_Slice pattern, char* ret) { std::string error; diff --git a/pkg/geo/geos/geos.go b/pkg/geo/geos/geos.go index dc3a79412818..87c41a51d486 100644 --- a/pkg/geo/geos/geos.go +++ b/pkg/geo/geos/geos.go @@ -326,6 +326,32 @@ func Area(ewkb geopb.EWKB) (float64, error) { return float64(area), nil } +// Boundary returns the boundary of an EWKB. +func Boundary(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_Boundary(g, goToCSlice(ewkb), &cEWKB)); err != nil { + return nil, err + } + return cStringToSafeGoBytes(cEWKB), nil +} + +// Difference returns the difference between two EWKB. +func Difference(ewkb1 geopb.EWKB, ewkb2 geopb.EWKB) (geopb.EWKB, error) { + g, err := ensureInitInternal() + if err != nil { + return nil, err + } + var diffEWKB C.CR_GEOS_String + if err := statusToError(C.CR_GEOS_Difference(g, goToCSlice(ewkb1), goToCSlice(ewkb2), &diffEWKB)); err != nil { + return nil, err + } + return cStringToSafeGoBytes(diffEWKB), nil +} + // Length returns the length of an EWKB. func Length(ewkb geopb.EWKB) (float64, error) { g, err := ensureInitInternal() @@ -519,6 +545,34 @@ func MinDistance(a geopb.EWKB, b geopb.EWKB) (float64, error) { return float64(distance), nil } +// MinimumClearance returns the minimum distance a vertex can move to result in an +// invalid geometry. +func MinimumClearance(ewkb geopb.EWKB) (float64, error) { + g, err := ensureInitInternal() + if err != nil { + return 0, err + } + var distance C.double + if err := statusToError(C.CR_GEOS_MinimumClearance(g, goToCSlice(ewkb), &distance)); err != nil { + return 0, err + } + return float64(distance), nil +} + +// MinimumClearanceLine returns the line spanning the minimum clearance a vertex can +// move before producing an invalid geometry. +func MinimumClearanceLine(ewkb geopb.EWKB) (geopb.EWKB, error) { + g, err := ensureInitInternal() + if err != nil { + return nil, err + } + var clearanceEWKB C.CR_GEOS_String + if err := statusToError(C.CR_GEOS_MinimumClearanceLine(g, goToCSlice(ewkb), &clearanceEWKB)); err != nil { + return nil, err + } + return cStringToSafeGoBytes(clearanceEWKB), nil +} + // ClipByRect clips a EWKB to the specified rectangle. func ClipByRect( ewkb geopb.EWKB, xMin float64, yMin float64, xMax float64, yMax float64, @@ -665,6 +719,66 @@ func Within(a geopb.EWKB, b geopb.EWKB) (bool, error) { return ret == 1, nil } +// FrechetDistance returns the Frechet distance between the geometries. +func FrechetDistance(a, b geopb.EWKB) (float64, error) { + g, err := ensureInitInternal() + if err != nil { + return 0, err + } + var distance C.double + if err := statusToError( + C.CR_GEOS_FrechetDistance(g, goToCSlice(a), goToCSlice(b), &distance), + ); err != nil { + return 0, err + } + return float64(distance), nil +} + +// FrechetDistanceDensify returns the Frechet distance between the geometries. +func FrechetDistanceDensify(a, b geopb.EWKB, densifyFrac float64) (float64, error) { + g, err := ensureInitInternal() + if err != nil { + return 0, err + } + var distance C.double + if err := statusToError( + C.CR_GEOS_FrechetDistanceDensify(g, goToCSlice(a), goToCSlice(b), C.double(densifyFrac), &distance), + ); err != nil { + return 0, err + } + return float64(distance), nil +} + +// HausdorffDistance returns the Hausdorff distance between the geometries. +func HausdorffDistance(a, b geopb.EWKB) (float64, error) { + g, err := ensureInitInternal() + if err != nil { + return 0, err + } + var distance C.double + if err := statusToError( + C.CR_GEOS_HausdorffDistance(g, goToCSlice(a), goToCSlice(b), &distance), + ); err != nil { + return 0, err + } + return float64(distance), nil +} + +// HausdorffDistanceDensify returns the Hausdorff distance between the geometries. +func HausdorffDistanceDensify(a, b geopb.EWKB, densifyFrac float64) (float64, error) { + g, err := ensureInitInternal() + if err != nil { + return 0, err + } + var distance C.double + if err := statusToError( + C.CR_GEOS_HausdorffDistanceDensify(g, goToCSlice(a), goToCSlice(b), C.double(densifyFrac), &distance), + ); err != nil { + return 0, err + } + return float64(distance), nil +} + // // DE-9IM related // @@ -685,6 +799,22 @@ func Relate(a geopb.EWKB, b geopb.EWKB) (string, error) { return string(cStringToSafeGoBytes(ret)), nil } +// RelateBoundaryNodeRule returns the DE-9IM relation between A and B given a boundary node rule. +func RelateBoundaryNodeRule(a geopb.EWKB, b geopb.EWKB, bnr int) (string, error) { + g, err := ensureInitInternal() + if err != nil { + return "", err + } + var ret C.CR_GEOS_String + if err := statusToError(C.CR_GEOS_RelateBoundaryNodeRule(g, goToCSlice(a), goToCSlice(b), C.int(bnr), &ret)); err != nil { + return "", err + } + if ret.data == nil { + return "", errors.Newf("expected DE-9IM string but found nothing") + } + return string(cStringToSafeGoBytes(ret)), nil +} + // RelatePattern whether A and B have a DE-9IM relation matching the given pattern. func RelatePattern(a geopb.EWKB, b geopb.EWKB, pattern string) (bool, error) { g, err := ensureInitInternal() diff --git a/pkg/geo/geos/geos.h b/pkg/geo/geos/geos.h index 80369af9ba65..0df66940767c 100644 --- a/pkg/geo/geos/geos.h +++ b/pkg/geo/geos/geos.h @@ -86,6 +86,9 @@ CR_GEOS_Status CR_GEOS_Area(CR_GEOS* lib, CR_GEOS_Slice a, double* ret); CR_GEOS_Status CR_GEOS_Length(CR_GEOS* lib, CR_GEOS_Slice a, double* ret); CR_GEOS_Status CR_GEOS_Normalize(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* normalizedEWKB); CR_GEOS_Status CR_GEOS_LineMerge(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* ewkb); +CR_GEOS_Status CR_GEOS_MinimumClearance(CR_GEOS* lib, CR_GEOS_Slice a, double* ret); +CR_GEOS_Status CR_GEOS_MinimumClearanceLine(CR_GEOS* lib, CR_GEOS_Slice a, + CR_GEOS_String* clearanceEWKB); // // Unary predicates. @@ -97,8 +100,11 @@ CR_GEOS_Status CR_GEOS_IsSimple(CR_GEOS* lib, CR_GEOS_Slice a, char* ret); // Topology operators. // +CR_GEOS_Status CR_GEOS_Boundary(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* boundaryEWKB); 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_Difference(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + CR_GEOS_String* diffEWKB); CR_GEOS_Status CR_GEOS_Simplify(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* simplifyEWKB, double tolerance); CR_GEOS_Status CR_GEOS_TopologyPreserveSimplify(CR_GEOS* lib, CR_GEOS_Slice a, @@ -125,6 +131,13 @@ CR_GEOS_Status CR_GEOS_Interpolate(CR_GEOS* lib, CR_GEOS_Slice a, double distanc // CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double* ret); +CR_GEOS_Status CR_GEOS_FrechetDistance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double* ret); +CR_GEOS_Status CR_GEOS_FrechetDistanceDensify(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double densifyFrac, double* ret); +CR_GEOS_Status CR_GEOS_HausdorffDistance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double* ret); +CR_GEOS_Status CR_GEOS_HausdorffDistanceDensify(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + double densifyFrac, double* ret); // // Binary predicates. @@ -146,6 +159,8 @@ CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, ch // CR_GEOS_Status CR_GEOS_Relate(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_String* ret); +CR_GEOS_Status CR_GEOS_RelateBoundaryNodeRule(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, + int bnr, CR_GEOS_String* ret); CR_GEOS_Status CR_GEOS_RelatePattern(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_Slice pattern, char* ret); diff --git a/pkg/sql/logictest/testdata/logic_test/geospatial b/pkg/sql/logictest/testdata/logic_test/geospatial index e2ae67332fd6..c9b394c9465d 100644 --- a/pkg/sql/logictest/testdata/logic_test/geospatial +++ b/pkg/sql/logictest/testdata/logic_test/geospatial @@ -211,6 +211,18 @@ SELECT ST_Azimuth(ST_Point(0, 0), ST_Point(0, 0)) ---- NULL +query RRRRRRR +SELECT + degrees(ST_Angle('POINT (0 0)', 'POINT (0 1)', 'POINT (0 0)', 'POINT (1 0)')), + degrees(ST_Angle('POINT (0 0)', 'POINT (0 1)', 'POINT (0 0)')), + degrees(ST_Angle('POINT (0 0)', 'POINT (0 1)', 'POINT (1 1)')), + degrees(ST_Angle('POINT (0 0)', 'POINT (0 0)', 'POINT (0 0)', 'POINT (0 0)')), + degrees(ST_Angle('LINESTRING (0 0, 0 1)', 'LINESTRING (0 0, 1 0)')), + degrees(ST_Angle('LINESTRING (0 0, 0 1)', 'LINESTRING (0 0, 0 1)')), + degrees(ST_Angle('LINESTRING (0 0, 0 0)', 'LINESTRING (0 0, 0 0)')) +---- +90 0 270 NULL 90 0 NULL + subtest json_test # All values here should be true. @@ -942,7 +954,7 @@ invalid polygon false false false Self-intersection[1.5 1.5] S self-intersecting polygon false false true Ring Self-intersection[14 20] Ring Self-intersection Valid Geometry POLYGON ((14 20, 8 45, 20 35, 14 20), (14 20, 16 30, 12 30, 14 20)) # Unary operators -query TRRRRRR +query TRRRRRRRT SELECT a.dsc, ST_Area(a.geom), @@ -950,22 +962,24 @@ SELECT ST_Length(a.geom), ST_Length2D(a.geom), ST_Perimeter(a.geom), - ST_Perimeter2D(a.geom) + ST_Perimeter2D(a.geom), + ST_MinimumClearance(a.geom), + ST_AsText(ST_MinimumClearanceLine(a.geom)) FROM geom_operators_test a ORDER BY a.dsc ---- -Empty GeometryCollection 0 0 0 0 0 0 -Empty LineString 0 0 0 0 0 0 -Empty Point 0 0 0 0 0 0 -Faraway point 0 0 0 0 0 0 -Line going through left and right square 0 0 1 1 0 0 -NULL NULL NULL NULL NULL NULL NULL -Nested Geometry Collection 0 0 0 0 0 0 -Point middle of Left Square 0 0 0 0 0 0 -Point middle of Right Square 0 0 0 0 0 0 -Square (left) 1 1 0 0 4 4 -Square (right) 1 1 0 0 4 4 -Square overlapping left and right square 1.1 1.1 0 0 4.2 4.2 +Empty GeometryCollection 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Empty LineString 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Empty Point 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Faraway point 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Line going through left and right square 0 0 1 1 0 0 1 LINESTRING (-0.5 0.5, 0.5 0.5) +NULL NULL NULL NULL NULL NULL NULL NULL NULL +Nested Geometry Collection 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Point middle of Left Square 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Point middle of Right Square 0 0 0 0 0 0 +Inf LINESTRING EMPTY +Square (left) 1 1 0 0 4 4 1 LINESTRING (-1 0, 0 0) +Square (right) 1 1 0 0 4 4 1 LINESTRING (0 0, 1 0) +Square overlapping left and right square 1.1 1.1 0 0 4.2 4.2 1 LINESTRING (-0.1 0, -0.1 1) # Unary predicates query TBBBB @@ -1190,6 +1204,24 @@ Square (left) POINT (-0.5 0.5) POINT (-0.5 0.5) PO 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)) +query TT +SELECT + a.dsc, + ST_AsEWKT(ST_Boundary(a.geom)) +FROM geom_operators_test a +WHERE NOT ST_IsCollection(a.geom) +ORDER BY a.dsc +---- +Empty LineString LINESTRING EMPTY +Empty Point POINT EMPTY +Faraway point GEOMETRYCOLLECTION EMPTY +Line going through left and right square MULTIPOINT (-0.5 0.5, 0.5 0.5) +Point middle of Left Square GEOMETRYCOLLECTION EMPTY +Point middle of Right Square GEOMETRYCOLLECTION EMPTY +Square (left) LINESTRING (-1 0, 0 0, 0 1, -1 1, -1 0) +Square (right) LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0) +Square overlapping left and right square LINESTRING (-0.1 0, 1 0, 1 1, -0.1 1, -0.1 0) + query TTT SELECT a.dsc, @@ -1789,6 +1821,317 @@ Square overlapping left and right square Square (left) Square overlapping left and right square Square (right) 0 0 Square overlapping left and right square Square overlapping left and right square 0 0 +query TTRRR +SELECT + a.dsc, + b.dsc, + ST_FrechetDistance(a.geom, b.geom), + ST_FrechetDistance(a.geom, b.geom, 0.2), + ST_FrechetDistance(a.geom, b.geom, -1) +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 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 NaN NaN NaN +Faraway point Line going through left and right square 6.36396103067893 6.95269731830748 6.36396103067893 +Faraway point NULL NULL NULL NULL +Faraway point Nested Geometry Collection NaN NaN NaN +Faraway point Point middle of Left Square NaN NaN NaN +Faraway point Point middle of Right Square NaN NaN NaN +Faraway point Square (left) 7.81024967590665 7.81024967590665 7.81024967590665 +Faraway point Square (right) 7.07106781186548 7.07106781186548 7.07106781186548 +Faraway point Square overlapping left and right square 7.14212853426764 7.14212853426764 7.14212853426764 +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 6.36396103067893 6.95269731830748 6.36396103067893 +Line going through left and right square Line going through left and right square 0 0 0 +Line going through left and right square NULL NULL NULL NULL +Line going through left and right square Nested Geometry Collection 0.707106781186548 0.707106781186548 0.707106781186548 +Line going through left and right square Point middle of Left Square 1 1 1 +Line going through left and right square Point middle of Right Square 0 0.8 0 +Line going through left and right square Square (left) 1.58113883008419 1.58113883008419 1.58113883008419 +Line going through left and right square Square (right) 0.707106781186548 0.707106781186548 0.707106781186548 +Line going through left and right square Square overlapping left and right square 0.781024967590665 0.781024967590665 0.781024967590665 +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 NaN NaN NaN +Nested Geometry Collection Line going through left and right square 0.707106781186548 0.707106781186548 0.707106781186548 +Nested Geometry Collection NULL NULL NULL NULL +Nested Geometry Collection Nested Geometry Collection NaN NaN NaN +Nested Geometry Collection Point middle of Left Square NaN NaN NaN +Nested Geometry Collection Point middle of Right Square NaN NaN NaN +Nested Geometry Collection Square (left) 1.4142135623731 1.4142135623731 1.4142135623731 +Nested Geometry Collection Square (right) 1.4142135623731 1.4142135623731 1.4142135623731 +Nested Geometry Collection Square overlapping left and right square 1.4142135623731 1.4142135623731 1.4142135623731 +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 NaN NaN NaN +Point middle of Left Square Line going through left and right square 1 1 1 +Point middle of Left Square NULL NULL NULL NULL +Point middle of Left Square Nested Geometry Collection NaN NaN NaN +Point middle of Left Square Point middle of Left Square NaN NaN NaN +Point middle of Left Square Point middle of Right Square NaN NaN NaN +Point middle of Left Square Square (left) 0.707106781186548 0.707106781186548 0.707106781186548 +Point middle of Left Square Square (right) 1.58113883008419 1.58113883008419 1.58113883008419 +Point middle of Left Square Square overlapping left and right square 1.58113883008419 1.58113883008419 1.58113883008419 +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 NaN NaN NaN +Point middle of Right Square Line going through left and right square 0 0.8 0 +Point middle of Right Square NULL NULL NULL NULL +Point middle of Right Square Nested Geometry Collection NaN NaN NaN +Point middle of Right Square Point middle of Left Square NaN NaN NaN +Point middle of Right Square Point middle of Right Square NaN NaN NaN +Point middle of Right Square Square (left) 1.58113883008419 1.58113883008419 1.58113883008419 +Point middle of Right Square Square (right) 0.707106781186548 0.707106781186548 0.707106781186548 +Point middle of Right Square Square overlapping left and right square 0.781024967590665 0.781024967590665 0.781024967590665 +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 7.81024967590665 7.81024967590665 7.81024967590665 +Square (left) Line going through left and right square 1.58113883008419 1.58113883008419 1.58113883008419 +Square (left) NULL NULL NULL NULL +Square (left) Nested Geometry Collection 1.4142135623731 1.4142135623731 1.4142135623731 +Square (left) Point middle of Left Square 0.707106781186548 0.707106781186548 0.707106781186548 +Square (left) Point middle of Right Square 1.58113883008419 1.58113883008419 1.58113883008419 +Square (left) Square (left) 0 0 0 +Square (left) Square (right) 1 1 1 +Square (left) Square overlapping left and right square 1 1 1 +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 7.07106781186548 7.07106781186548 7.07106781186548 +Square (right) Line going through left and right square 0.707106781186548 0.707106781186548 0.707106781186548 +Square (right) NULL NULL NULL NULL +Square (right) Nested Geometry Collection 1.4142135623731 1.4142135623731 1.4142135623731 +Square (right) Point middle of Left Square 1.58113883008419 1.58113883008419 1.58113883008419 +Square (right) Point middle of Right Square 0.707106781186548 0.707106781186548 0.707106781186548 +Square (right) Square (left) 1 1 1 +Square (right) Square (right) 0 0 0 +Square (right) Square overlapping left and right square 0.1 0.1 0.1 +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 7.14212853426764 7.14212853426764 7.14212853426764 +Square overlapping left and right square Line going through left and right square 0.781024967590665 0.781024967590665 0.781024967590665 +Square overlapping left and right square NULL NULL NULL NULL +Square overlapping left and right square Nested Geometry Collection 1.4142135623731 1.4142135623731 1.4142135623731 +Square overlapping left and right square Point middle of Left Square 1.58113883008419 1.58113883008419 1.58113883008419 +Square overlapping left and right square Point middle of Right Square 0.781024967590665 0.781024967590665 0.781024967590665 +Square overlapping left and right square Square (left) 1 1 1 +Square overlapping left and right square Square (right) 0.1 0.1 0.1 +Square overlapping left and right square Square overlapping left and right square 0 0 0 + +query TTRR +SELECT + a.dsc, + b.dsc, + ST_HausdorffDistance(a.geom, b.geom), + ST_HausdorffDistance(a.geom, b.geom, 0.4) +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 0 0 +Faraway point Line going through left and right square 7.10633520177595 7.10633520177595 +Faraway point NULL NULL NULL +Faraway point Nested Geometry Collection 7.07106781186548 7.07106781186548 +Faraway point Point middle of Left Square 7.10633520177595 7.10633520177595 +Faraway point Point middle of Right Square 6.36396103067893 6.36396103067893 +Faraway point Square (left) 7.81024967590665 7.81024967590665 +Faraway point Square (right) 7.07106781186548 7.07106781186548 +Faraway point Square overlapping left and right square 7.14212853426764 7.14212853426764 +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 7.10633520177595 7.10633520177595 +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 Nested Geometry Collection 0.707106781186548 0.707106781186548 +Line going through left and right square Point middle of Left Square 1 1 +Line going through left and right square Point middle of Right Square 1 1 +Line going through left and right square Square (left) 0.707106781186548 0.707106781186548 +Line going through left and right square Square (right) 0.707106781186548 0.707106781186548 +Line going through left and right square Square overlapping left and right square 0.707106781186548 0.707106781186548 +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 7.07106781186548 7.07106781186548 +Nested Geometry Collection Line going through left and right square 0.707106781186548 0.707106781186548 +Nested Geometry Collection NULL NULL NULL +Nested Geometry Collection Nested Geometry Collection 0 0 +Nested Geometry Collection Point middle of Left Square 0.707106781186548 0.707106781186548 +Nested Geometry Collection Point middle of Right Square 0.707106781186548 0.707106781186548 +Nested Geometry Collection Square (left) 1.4142135623731 1.4142135623731 +Nested Geometry Collection Square (right) 1.4142135623731 1.4142135623731 +Nested Geometry Collection Square overlapping left and right square 1.4142135623731 1.4142135623731 +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 7.10633520177595 7.10633520177595 +Point middle of Left Square Line going through left and right square 1 1 +Point middle of Left Square NULL NULL NULL +Point middle of Left Square Nested Geometry Collection 0.707106781186548 0.707106781186548 +Point middle of Left Square Point middle of Left Square 0 0 +Point middle of Left Square Point middle of Right Square 1 1 +Point middle of Left Square Square (left) 0.707106781186548 0.707106781186548 +Point middle of Left Square Square (right) 1.58113883008419 1.58113883008419 +Point middle of Left Square Square overlapping left and right square 1.58113883008419 1.58113883008419 +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 6.36396103067893 6.36396103067893 +Point middle of Right Square Line going through left and right square 1 1 +Point middle of Right Square NULL NULL NULL +Point middle of Right Square Nested Geometry Collection 0.707106781186548 0.707106781186548 +Point middle of Right Square Point middle of Left Square 1 1 +Point middle of Right Square Point middle of Right Square 0 0 +Point middle of Right Square Square (left) 1.58113883008419 1.58113883008419 +Point middle of Right Square Square (right) 0.707106781186548 0.707106781186548 +Point middle of Right Square Square overlapping left and right square 0.781024967590665 0.781024967590665 +Square (left) Empty GeometryCollection NULL NULL +Square (left) Empty LineString NULL NULL +Square (left) Empty Point NULL NULL +Square (left) Faraway point 7.81024967590665 7.81024967590665 +Square (left) Line going through left and right square 0.707106781186548 0.707106781186548 +Square (left) NULL NULL NULL +Square (left) Nested Geometry Collection 1.4142135623731 1.4142135623731 +Square (left) Point middle of Left Square 0.707106781186548 0.707106781186548 +Square (left) Point middle of Right Square 1.58113883008419 1.58113883008419 +Square (left) Square (left) 0 5.55111512312578e-17 +Square (left) Square (right) 1 1 +Square (left) Square overlapping left and right square 1 1 +Square (right) Empty GeometryCollection NULL NULL +Square (right) Empty LineString NULL NULL +Square (right) Empty Point NULL NULL +Square (right) Faraway point 7.07106781186548 7.07106781186548 +Square (right) Line going through left and right square 0.707106781186548 0.707106781186548 +Square (right) NULL NULL NULL +Square (right) Nested Geometry Collection 1.4142135623731 1.4142135623731 +Square (right) Point middle of Left Square 1.58113883008419 1.58113883008419 +Square (right) Point middle of Right Square 0.707106781186548 0.707106781186548 +Square (right) Square (left) 1 1 +Square (right) Square (right) 0 5.55111512312578e-17 +Square (right) Square overlapping left and right square 0.1 0.1 +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 7.14212853426764 7.14212853426764 +Square overlapping left and right square Line going through left and right square 0.707106781186548 0.707106781186548 +Square overlapping left and right square NULL NULL NULL +Square overlapping left and right square Nested Geometry Collection 1.4142135623731 1.4142135623731 +Square overlapping left and right square Point middle of Left Square 1.58113883008419 1.58113883008419 +Square overlapping left and right square Point middle of Right Square 0.781024967590665 0.781024967590665 +Square overlapping left and right square Square (left) 1 1 +Square overlapping left and right square Square (right) 0.1 0.1 +Square overlapping left and right square Square overlapping left and right square 0 5.55111512312578e-17 + # ST_LongestLine, ST_ShortestLine query TTTT SELECT @@ -1945,6 +2288,160 @@ Square overlapping left and right square Square (left) 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) +# ST_Difference +query TTT +SELECT + a.dsc, + b.dsc, + ST_AsText(ST_Difference(a.geom, b.geom)) +FROM geom_operators_test a, geom_operators_test b +ORDER BY a.dsc, b.dsc +---- +Empty GeometryCollection Empty GeometryCollection GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Empty LineString GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Empty Point GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Faraway point GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Line going through left and right square GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection NULL NULL +Empty GeometryCollection Nested Geometry Collection GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Point middle of Left Square GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Point middle of Right Square GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Square (left) GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Square (right) GEOMETRYCOLLECTION EMPTY +Empty GeometryCollection Square overlapping left and right square GEOMETRYCOLLECTION EMPTY +Empty LineString Empty GeometryCollection LINESTRING EMPTY +Empty LineString Empty LineString LINESTRING EMPTY +Empty LineString Empty Point LINESTRING EMPTY +Empty LineString Faraway point LINESTRING EMPTY +Empty LineString Line going through left and right square LINESTRING EMPTY +Empty LineString NULL NULL +Empty LineString Nested Geometry Collection LINESTRING EMPTY +Empty LineString Point middle of Left Square LINESTRING EMPTY +Empty LineString Point middle of Right Square LINESTRING EMPTY +Empty LineString Square (left) LINESTRING EMPTY +Empty LineString Square (right) LINESTRING EMPTY +Empty LineString Square overlapping left and right square LINESTRING EMPTY +Empty Point Empty GeometryCollection POINT EMPTY +Empty Point Empty LineString POINT EMPTY +Empty Point Empty Point POINT EMPTY +Empty Point Faraway point POINT EMPTY +Empty Point Line going through left and right square POINT EMPTY +Empty Point NULL NULL +Empty Point Nested Geometry Collection POINT EMPTY +Empty Point Point middle of Left Square POINT EMPTY +Empty Point Point middle of Right Square POINT EMPTY +Empty Point Square (left) POINT EMPTY +Empty Point Square (right) POINT EMPTY +Empty Point Square overlapping left and right square POINT EMPTY +Faraway point Empty GeometryCollection POINT (5 5) +Faraway point Empty LineString POINT (5 5) +Faraway point Empty Point POINT (5 5) +Faraway point Faraway point POINT EMPTY +Faraway point Line going through left and right square POINT (5 5) +Faraway point NULL NULL +Faraway point Nested Geometry Collection POINT (5 5) +Faraway point Point middle of Left Square POINT (5 5) +Faraway point Point middle of Right Square POINT (5 5) +Faraway point Square (left) POINT (5 5) +Faraway point Square (right) POINT (5 5) +Faraway point Square overlapping left and right square POINT (5 5) +Line going through left and right square Empty GeometryCollection LINESTRING (-0.5 0.5, 0.5 0.5) +Line going through left and right square Empty LineString LINESTRING (-0.5 0.5, 0.5 0.5) +Line going through left and right square Empty Point LINESTRING (-0.5 0.5, 0.5 0.5) +Line going through left and right square Faraway point LINESTRING (-0.5 0.5, 0.5 0.5) +Line going through left and right square Line going through left and right square LINESTRING EMPTY +Line going through left and right square NULL NULL +Line going through left and right square Nested Geometry Collection LINESTRING (-0.5 0.5, 0.5 0.5) +Line going through left and right square Point middle of Left Square 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) +Line going through left and right square Square (left) LINESTRING (0 0.5, 0.5 0.5) +Line going through left and right square Square (right) LINESTRING (-0.5 0.5, 0 0.5) +Line going through left and right square Square overlapping left and right square LINESTRING (-0.5 0.5, -0.1 0.5) +NULL Empty GeometryCollection NULL +NULL Empty LineString NULL +NULL Empty Point NULL +NULL Faraway point NULL +NULL Line going through left and right square NULL +NULL NULL NULL +NULL Nested Geometry Collection NULL +NULL Point middle of Left Square NULL +NULL Point middle of Right Square NULL +NULL Square (left) NULL +NULL Square (right) NULL +NULL Square overlapping left and right square NULL +Nested Geometry Collection Empty GeometryCollection GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 0))) +Nested Geometry Collection Empty LineString GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 0))) +Nested Geometry Collection Empty Point GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (0 0))) +Nested Geometry Collection Faraway point POINT (0 0) +Nested Geometry Collection Line going through left and right square POINT (0 0) +Nested Geometry Collection NULL NULL +Nested Geometry Collection Nested Geometry Collection POINT EMPTY +Nested Geometry Collection Point middle of Left Square POINT (0 0) +Nested Geometry Collection Point middle of Right Square POINT (0 0) +Nested Geometry Collection Square (left) POINT EMPTY +Nested Geometry Collection Square (right) POINT EMPTY +Nested Geometry Collection Square overlapping left and right square POINT EMPTY +Point middle of Left Square Empty GeometryCollection POINT (-0.5 0.5) +Point middle of Left Square Empty LineString POINT (-0.5 0.5) +Point middle of Left Square Empty Point POINT (-0.5 0.5) +Point middle of Left Square Faraway point POINT (-0.5 0.5) +Point middle of Left Square Line going through left and right square POINT EMPTY +Point middle of Left Square NULL NULL +Point middle of Left Square Nested Geometry Collection POINT (-0.5 0.5) +Point middle of Left Square Point middle of Left Square POINT EMPTY +Point middle of Left Square Point middle of Right Square POINT (-0.5 0.5) +Point middle of Left Square Square (left) POINT EMPTY +Point middle of Left Square Square (right) POINT (-0.5 0.5) +Point middle of Left Square Square overlapping left and right square POINT (-0.5 0.5) +Point middle of Right Square Empty GeometryCollection POINT (0.5 0.5) +Point middle of Right Square Empty LineString POINT (0.5 0.5) +Point middle of Right Square Empty Point POINT (0.5 0.5) +Point middle of Right Square Faraway point POINT (0.5 0.5) +Point middle of Right Square Line going through left and right square POINT EMPTY +Point middle of Right Square NULL NULL +Point middle of Right Square Nested Geometry Collection POINT (0.5 0.5) +Point middle of Right Square Point middle of Left Square POINT (0.5 0.5) +Point middle of Right Square Point middle of Right Square POINT EMPTY +Point middle of Right Square Square (left) POINT (0.5 0.5) +Point middle of Right Square Square (right) POINT EMPTY +Point middle of Right Square Square overlapping left and right square POINT EMPTY +Square (left) Empty GeometryCollection POLYGON ((-1 0, 0 0, 0 1, -1 1, -1 0)) +Square (left) Empty LineString POLYGON ((-1 0, 0 0, 0 1, -1 1, -1 0)) +Square (left) Empty Point POLYGON ((-1 0, 0 0, 0 1, -1 1, -1 0)) +Square (left) Faraway point POLYGON ((-1 0, -1 1, 0 1, 0 0, -1 0)) +Square (left) Line going through left and right square POLYGON ((0 0.5, 0 0, -1 0, -1 1, 0 1, 0 0.5)) +Square (left) NULL NULL +Square (left) Nested Geometry Collection POLYGON ((-1 0, -1 1, 0 1, 0 0, -1 0)) +Square (left) Point middle of Left Square POLYGON ((-1 0, -1 1, 0 1, 0 0, -1 0)) +Square (left) Point middle of Right Square POLYGON ((-1 0, -1 1, 0 1, 0 0, -1 0)) +Square (left) Square (left) POLYGON EMPTY +Square (left) Square (right) POLYGON ((0 0, -1 0, -1 1, 0 1, 0 0)) +Square (left) Square overlapping left and right square POLYGON ((-0.1 0, -1 0, -1 1, -0.1 1, -0.1 0)) +Square (right) Empty GeometryCollection POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)) +Square (right) Empty LineString POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)) +Square (right) Empty Point POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)) +Square (right) Faraway point POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0)) +Square (right) Line going through left and right square POLYGON ((0 0.5, 0 1, 1 1, 1 0, 0 0, 0 0.5)) +Square (right) NULL NULL +Square (right) Nested Geometry Collection POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0)) +Square (right) Point middle of Left Square POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0)) +Square (right) Point middle of Right Square POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0)) +Square (right) Square (left) POLYGON ((0 1, 1 1, 1 0, 0 0, 0 1)) +Square (right) Square (right) POLYGON EMPTY +Square (right) Square overlapping left and right square POLYGON EMPTY +Square overlapping left and right square Empty GeometryCollection POLYGON ((-0.1 0, 1 0, 1 1, -0.1 1, -0.1 0)) +Square overlapping left and right square Empty LineString POLYGON ((-0.1 0, 1 0, 1 1, -0.1 1, -0.1 0)) +Square overlapping left and right square Empty Point POLYGON ((-0.1 0, 1 0, 1 1, -0.1 1, -0.1 0)) +Square overlapping left and right square Faraway point POLYGON ((-0.1 0, -0.1 1, 1 1, 1 0, -0.1 0)) +Square overlapping left and right square Line going through left and right square POLYGON ((-0.1 0.5, -0.1 1, 1 1, 1 0, -0.1 0, -0.1 0.5)) +Square overlapping left and right square NULL NULL +Square overlapping left and right square Nested Geometry Collection POLYGON ((-0.1 0, -0.1 1, 1 1, 1 0, -0.1 0)) +Square overlapping left and right square Point middle of Left Square POLYGON ((-0.1 0, -0.1 1, 1 1, 1 0, -0.1 0)) +Square overlapping left and right square Point middle of Right Square POLYGON ((-0.1 0, -0.1 1, 1 1, 1 0, -0.1 0)) +Square overlapping left and right square Square (left) POLYGON ((0 1, 1 1, 1 0, 0 0, 0 1)) +Square overlapping left and right square Square (right) POLYGON ((0 0, -0.1 0, -0.1 1, 0 1, 0 0)) +Square overlapping left and right square Square overlapping left and right square POLYGON EMPTY + # Binary predicates query TTBBBBBBBBBBB SELECT @@ -2625,160 +3122,161 @@ FROM ( VALUES true false -query TTTB +query TTTTB SELECT a.dsc, b.dsc, ST_Relate(a.geom, b.geom), + ST_Relate(a.geom, b.geom, 3), ST_Relate(a.geom, b.geom, 'T**FF*FF*') FROM geom_operators_test a JOIN geom_operators_test b ON (1=1) ORDER BY a.dsc, b.dsc ---- -Empty GeometryCollection Empty GeometryCollection FFFFFFFF2 false -Empty GeometryCollection Empty LineString FFFFFFFF2 false -Empty GeometryCollection Empty Point FFFFFFFF2 false -Empty GeometryCollection Faraway point FFFFFF0F2 false -Empty GeometryCollection Line going through left and right square FFFFFF102 false -Empty GeometryCollection NULL NULL NULL -Empty GeometryCollection Nested Geometry Collection FFFFFF0F2 false -Empty GeometryCollection Point middle of Left Square FFFFFF0F2 false -Empty GeometryCollection Point middle of Right Square FFFFFF0F2 false -Empty GeometryCollection Square (left) FFFFFF212 false -Empty GeometryCollection Square (right) FFFFFF212 false -Empty GeometryCollection Square overlapping left and right square FFFFFF212 false -Empty LineString Empty GeometryCollection FFFFFFFF2 false -Empty LineString Empty LineString FFFFFFFF2 false -Empty LineString Empty Point FFFFFFFF2 false -Empty LineString Faraway point FFFFFF0F2 false -Empty LineString Line going through left and right square FFFFFF102 false -Empty LineString NULL NULL NULL -Empty LineString Nested Geometry Collection FFFFFF0F2 false -Empty LineString Point middle of Left Square FFFFFF0F2 false -Empty LineString Point middle of Right Square FFFFFF0F2 false -Empty LineString Square (left) FFFFFF212 false -Empty LineString Square (right) FFFFFF212 false -Empty LineString Square overlapping left and right square FFFFFF212 false -Empty Point Empty GeometryCollection FFFFFFFF2 false -Empty Point Empty LineString FFFFFFFF2 false -Empty Point Empty Point FFFFFFFF2 false -Empty Point Faraway point FFFFFF0F2 false -Empty Point Line going through left and right square FFFFFF102 false -Empty Point NULL NULL NULL -Empty Point Nested Geometry Collection FFFFFF0F2 false -Empty Point Point middle of Left Square FFFFFF0F2 false -Empty Point Point middle of Right Square FFFFFF0F2 false -Empty Point Square (left) FFFFFF212 false -Empty Point Square (right) FFFFFF212 false -Empty Point Square overlapping left and right square FFFFFF212 false -Faraway point Empty GeometryCollection FF0FFFFF2 false -Faraway point Empty LineString FF0FFFFF2 false -Faraway point Empty Point FF0FFFFF2 false -Faraway point Faraway point 0FFFFFFF2 true -Faraway point Line going through left and right square FF0FFF102 false -Faraway point NULL NULL NULL -Faraway point Nested Geometry Collection FF0FFF0F2 false -Faraway point Point middle of Left Square FF0FFF0F2 false -Faraway point Point middle of Right Square FF0FFF0F2 false -Faraway point Square (left) FF0FFF212 false -Faraway point Square (right) FF0FFF212 false -Faraway point Square overlapping left and right square FF0FFF212 false -Line going through left and right square Empty GeometryCollection FF1FF0FF2 false -Line going through left and right square Empty LineString FF1FF0FF2 false -Line going through left and right square Empty Point FF1FF0FF2 false -Line going through left and right square Faraway point FF1FF00F2 false -Line going through left and right square Line going through left and right square 1FFF0FFF2 false -Line going through left and right square NULL NULL NULL -Line going through left and right square Nested Geometry Collection FF1FF00F2 false -Line going through left and right square Point middle of Left Square FF10F0FF2 false -Line going through left and right square Point middle of Right Square FF10F0FF2 false -Line going through left and right square Square (left) 1010F0212 false -Line going through left and right square Square (right) 1010F0212 false -Line going through left and right square Square overlapping left and right square 1010F0212 false -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 FF0FFFFF2 false -Nested Geometry Collection Empty LineString FF0FFFFF2 false -Nested Geometry Collection Empty Point FF0FFFFF2 false -Nested Geometry Collection Faraway point FF0FFF0F2 false -Nested Geometry Collection Line going through left and right square FF0FFF102 false -Nested Geometry Collection NULL NULL NULL -Nested Geometry Collection Nested Geometry Collection 0FFFFFFF2 true -Nested Geometry Collection Point middle of Left Square FF0FFF0F2 false -Nested Geometry Collection Point middle of Right Square FF0FFF0F2 false -Nested Geometry Collection Square (left) F0FFFF212 false -Nested Geometry Collection Square (right) F0FFFF212 false -Nested Geometry Collection Square overlapping left and right square F0FFFF212 false -Point middle of Left Square Empty GeometryCollection FF0FFFFF2 false -Point middle of Left Square Empty LineString FF0FFFFF2 false -Point middle of Left Square Empty Point FF0FFFFF2 false -Point middle of Left Square Faraway point FF0FFF0F2 false -Point middle of Left Square Line going through left and right square F0FFFF102 false -Point middle of Left Square NULL NULL NULL -Point middle of Left Square Nested Geometry Collection FF0FFF0F2 false -Point middle of Left Square Point middle of Left Square 0FFFFFFF2 true -Point middle of Left Square Point middle of Right Square FF0FFF0F2 false -Point middle of Left Square Square (left) 0FFFFF212 false -Point middle of Left Square Square (right) FF0FFF212 false -Point middle of Left Square Square overlapping left and right square FF0FFF212 false -Point middle of Right Square Empty GeometryCollection FF0FFFFF2 false -Point middle of Right Square Empty LineString FF0FFFFF2 false -Point middle of Right Square Empty Point FF0FFFFF2 false -Point middle of Right Square Faraway point FF0FFF0F2 false -Point middle of Right Square Line going through left and right square F0FFFF102 false -Point middle of Right Square NULL NULL NULL -Point middle of Right Square Nested Geometry Collection FF0FFF0F2 false -Point middle of Right Square Point middle of Left Square FF0FFF0F2 false -Point middle of Right Square Point middle of Right Square 0FFFFFFF2 true -Point middle of Right Square Square (left) FF0FFF212 false -Point middle of Right Square Square (right) 0FFFFF212 false -Point middle of Right Square Square overlapping left and right square 0FFFFF212 false -Square (left) Empty GeometryCollection FF2FF1FF2 false -Square (left) Empty LineString FF2FF1FF2 false -Square (left) Empty Point FF2FF1FF2 false -Square (left) Faraway point FF2FF10F2 false -Square (left) Line going through left and right square 1020F1102 false -Square (left) NULL NULL NULL -Square (left) Nested Geometry Collection FF20F1FF2 false -Square (left) Point middle of Left Square 0F2FF1FF2 true -Square (left) Point middle of Right Square FF2FF10F2 false -Square (left) Square (left) 2FFF1FFF2 false -Square (left) Square (right) FF2F11212 false -Square (left) Square overlapping left and right square 212111212 false -Square (right) Empty GeometryCollection FF2FF1FF2 false -Square (right) Empty LineString FF2FF1FF2 false -Square (right) Empty Point FF2FF1FF2 false -Square (right) Faraway point FF2FF10F2 false -Square (right) Line going through left and right square 1020F1102 false -Square (right) NULL NULL NULL -Square (right) Nested Geometry Collection FF20F1FF2 false -Square (right) Point middle of Left Square FF2FF10F2 false -Square (right) Point middle of Right Square 0F2FF1FF2 true -Square (right) Square (left) FF2F11212 false -Square (right) Square (right) 2FFF1FFF2 false -Square (right) Square overlapping left and right square 2FF11F212 false -Square overlapping left and right square Empty GeometryCollection FF2FF1FF2 false -Square overlapping left and right square Empty LineString FF2FF1FF2 false -Square overlapping left and right square Empty Point FF2FF1FF2 false -Square overlapping left and right square Faraway point FF2FF10F2 false -Square overlapping left and right square Line going through left and right square 1020F1102 false -Square overlapping left and right square NULL NULL NULL -Square overlapping left and right square Nested Geometry Collection FF20F1FF2 false -Square overlapping left and right square Point middle of Left Square FF2FF10F2 false -Square overlapping left and right square Point middle of Right Square 0F2FF1FF2 true -Square overlapping left and right square Square (left) 212111212 false -Square overlapping left and right square Square (right) 212F11FF2 false -Square overlapping left and right square Square overlapping left and right square 2FFF1FFF2 false +Empty GeometryCollection Empty GeometryCollection FFFFFFFF2 FFFFFFFF2 false +Empty GeometryCollection Empty LineString FFFFFFFF2 FFFFFFFF2 false +Empty GeometryCollection Empty Point FFFFFFFF2 FFFFFFFF2 false +Empty GeometryCollection Faraway point FFFFFF0F2 FFFFFF0F2 false +Empty GeometryCollection Line going through left and right square FFFFFF102 FFFFFF102 false +Empty GeometryCollection NULL NULL NULL NULL +Empty GeometryCollection Nested Geometry Collection FFFFFF0F2 FFFFFF0F2 false +Empty GeometryCollection Point middle of Left Square FFFFFF0F2 FFFFFF0F2 false +Empty GeometryCollection Point middle of Right Square FFFFFF0F2 FFFFFF0F2 false +Empty GeometryCollection Square (left) FFFFFF212 FFFFFF212 false +Empty GeometryCollection Square (right) FFFFFF212 FFFFFF212 false +Empty GeometryCollection Square overlapping left and right square FFFFFF212 FFFFFF212 false +Empty LineString Empty GeometryCollection FFFFFFFF2 FFFFFFFF2 false +Empty LineString Empty LineString FFFFFFFF2 FFFFFFFF2 false +Empty LineString Empty Point FFFFFFFF2 FFFFFFFF2 false +Empty LineString Faraway point FFFFFF0F2 FFFFFF0F2 false +Empty LineString Line going through left and right square FFFFFF102 FFFFFF102 false +Empty LineString NULL NULL NULL NULL +Empty LineString Nested Geometry Collection FFFFFF0F2 FFFFFF0F2 false +Empty LineString Point middle of Left Square FFFFFF0F2 FFFFFF0F2 false +Empty LineString Point middle of Right Square FFFFFF0F2 FFFFFF0F2 false +Empty LineString Square (left) FFFFFF212 FFFFFF212 false +Empty LineString Square (right) FFFFFF212 FFFFFF212 false +Empty LineString Square overlapping left and right square FFFFFF212 FFFFFF212 false +Empty Point Empty GeometryCollection FFFFFFFF2 FFFFFFFF2 false +Empty Point Empty LineString FFFFFFFF2 FFFFFFFF2 false +Empty Point Empty Point FFFFFFFF2 FFFFFFFF2 false +Empty Point Faraway point FFFFFF0F2 FFFFFF0F2 false +Empty Point Line going through left and right square FFFFFF102 FFFFFF102 false +Empty Point NULL NULL NULL NULL +Empty Point Nested Geometry Collection FFFFFF0F2 FFFFFF0F2 false +Empty Point Point middle of Left Square FFFFFF0F2 FFFFFF0F2 false +Empty Point Point middle of Right Square FFFFFF0F2 FFFFFF0F2 false +Empty Point Square (left) FFFFFF212 FFFFFF212 false +Empty Point Square (right) FFFFFF212 FFFFFF212 false +Empty Point Square overlapping left and right square FFFFFF212 FFFFFF212 false +Faraway point Empty GeometryCollection FF0FFFFF2 FF0FFFFF2 false +Faraway point Empty LineString FF0FFFFF2 FF0FFFFF2 false +Faraway point Empty Point FF0FFFFF2 FF0FFFFF2 false +Faraway point Faraway point 0FFFFFFF2 0FFFFFFF2 true +Faraway point Line going through left and right square FF0FFF102 FF0FFF102 false +Faraway point NULL NULL NULL NULL +Faraway point Nested Geometry Collection FF0FFF0F2 FF0FFF0F2 false +Faraway point Point middle of Left Square FF0FFF0F2 FF0FFF0F2 false +Faraway point Point middle of Right Square FF0FFF0F2 FF0FFF0F2 false +Faraway point Square (left) FF0FFF212 FF0FFF212 false +Faraway point Square (right) FF0FFF212 FF0FFF212 false +Faraway point Square overlapping left and right square FF0FFF212 FF0FFF212 false +Line going through left and right square Empty GeometryCollection FF1FF0FF2 FF1FF0FF2 false +Line going through left and right square Empty LineString FF1FF0FF2 FF1FF0FF2 false +Line going through left and right square Empty Point FF1FF0FF2 FF1FF0FF2 false +Line going through left and right square Faraway point FF1FF00F2 FF1FF00F2 false +Line going through left and right square Line going through left and right square 1FFF0FFF2 1FFFFFFF2 false +Line going through left and right square NULL NULL NULL NULL +Line going through left and right square Nested Geometry Collection FF1FF00F2 FF1FF00F2 false +Line going through left and right square Point middle of Left Square FF10F0FF2 0F1FFFFF2 false +Line going through left and right square Point middle of Right Square FF10F0FF2 0F1FFFFF2 false +Line going through left and right square Square (left) 1010F0212 101FFF202 false +Line going through left and right square Square (right) 1010F0212 101FFF202 false +Line going through left and right square Square overlapping left and right square 1010F0212 101FFF202 false +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 FF0FFFFF2 FF0FFFFF2 false +Nested Geometry Collection Empty LineString FF0FFFFF2 FF0FFFFF2 false +Nested Geometry Collection Empty Point FF0FFFFF2 FF0FFFFF2 false +Nested Geometry Collection Faraway point FF0FFF0F2 FF0FFF0F2 false +Nested Geometry Collection Line going through left and right square FF0FFF102 FF0FFF102 false +Nested Geometry Collection NULL NULL NULL NULL +Nested Geometry Collection Nested Geometry Collection 0FFFFFFF2 0FFFFFFF2 true +Nested Geometry Collection Point middle of Left Square FF0FFF0F2 FF0FFF0F2 false +Nested Geometry Collection Point middle of Right Square FF0FFF0F2 FF0FFF0F2 false +Nested Geometry Collection Square (left) F0FFFF212 F0FFFF212 false +Nested Geometry Collection Square (right) F0FFFF212 F0FFFF212 false +Nested Geometry Collection Square overlapping left and right square F0FFFF212 F0FFFF212 false +Point middle of Left Square Empty GeometryCollection FF0FFFFF2 FF0FFFFF2 false +Point middle of Left Square Empty LineString FF0FFFFF2 FF0FFFFF2 false +Point middle of Left Square Empty Point FF0FFFFF2 FF0FFFFF2 false +Point middle of Left Square Faraway point FF0FFF0F2 FF0FFF0F2 false +Point middle of Left Square Line going through left and right square F0FFFF102 0FFFFF1F2 false +Point middle of Left Square NULL NULL NULL NULL +Point middle of Left Square Nested Geometry Collection FF0FFF0F2 FF0FFF0F2 false +Point middle of Left Square Point middle of Left Square 0FFFFFFF2 0FFFFFFF2 true +Point middle of Left Square Point middle of Right Square FF0FFF0F2 FF0FFF0F2 false +Point middle of Left Square Square (left) 0FFFFF212 0FFFFF212 false +Point middle of Left Square Square (right) FF0FFF212 FF0FFF212 false +Point middle of Left Square Square overlapping left and right square FF0FFF212 FF0FFF212 false +Point middle of Right Square Empty GeometryCollection FF0FFFFF2 FF0FFFFF2 false +Point middle of Right Square Empty LineString FF0FFFFF2 FF0FFFFF2 false +Point middle of Right Square Empty Point FF0FFFFF2 FF0FFFFF2 false +Point middle of Right Square Faraway point FF0FFF0F2 FF0FFF0F2 false +Point middle of Right Square Line going through left and right square F0FFFF102 0FFFFF1F2 false +Point middle of Right Square NULL NULL NULL NULL +Point middle of Right Square Nested Geometry Collection FF0FFF0F2 FF0FFF0F2 false +Point middle of Right Square Point middle of Left Square FF0FFF0F2 FF0FFF0F2 false +Point middle of Right Square Point middle of Right Square 0FFFFFFF2 0FFFFFFF2 true +Point middle of Right Square Square (left) FF0FFF212 FF0FFF212 false +Point middle of Right Square Square (right) 0FFFFF212 0FFFFF212 false +Point middle of Right Square Square overlapping left and right square 0FFFFF212 0FFFFF212 false +Square (left) Empty GeometryCollection FF2FF1FF2 FF2FF1FF2 false +Square (left) Empty LineString FF2FF1FF2 FF2FF1FF2 false +Square (left) Empty Point FF2FF1FF2 FF2FF1FF2 false +Square (left) Faraway point FF2FF10F2 FF2FF10F2 false +Square (left) Line going through left and right square 1020F1102 1F20F01F2 false +Square (left) NULL NULL NULL NULL +Square (left) Nested Geometry Collection FF20F1FF2 FF20F1FF2 false +Square (left) Point middle of Left Square 0F2FF1FF2 0F2FF1FF2 true +Square (left) Point middle of Right Square FF2FF10F2 FF2FF10F2 false +Square (left) Square (left) 2FFF1FFF2 2FFF0FFF2 false +Square (left) Square (right) FF2F11212 1F2F002F2 false +Square (left) Square overlapping left and right square 212111212 2F2F002F2 false +Square (right) Empty GeometryCollection FF2FF1FF2 FF2FF1FF2 false +Square (right) Empty LineString FF2FF1FF2 FF2FF1FF2 false +Square (right) Empty Point FF2FF1FF2 FF2FF1FF2 false +Square (right) Faraway point FF2FF10F2 FF2FF10F2 false +Square (right) Line going through left and right square 1020F1102 1F20F01F2 false +Square (right) NULL NULL NULL NULL +Square (right) Nested Geometry Collection FF20F1FF2 FF20F1FF2 false +Square (right) Point middle of Left Square FF2FF10F2 FF2FF10F2 false +Square (right) Point middle of Right Square 0F2FF1FF2 0F2FF1FF2 true +Square (right) Square (left) FF2F11212 1F2F0F202 false +Square (right) Square (right) 2FFF1FFF2 2FFF0FFF2 false +Square (right) Square overlapping left and right square 2FF11F212 2FFF0F202 false +Square overlapping left and right square Empty GeometryCollection FF2FF1FF2 FF2FF1FF2 false +Square overlapping left and right square Empty LineString FF2FF1FF2 FF2FF1FF2 false +Square overlapping left and right square Empty Point FF2FF1FF2 FF2FF1FF2 false +Square overlapping left and right square Faraway point FF2FF10F2 FF2FF10F2 false +Square overlapping left and right square Line going through left and right square 1020F1102 1F20F01F2 false +Square overlapping left and right square NULL NULL NULL NULL +Square overlapping left and right square Nested Geometry Collection FF20F1FF2 FF20F1FF2 false +Square overlapping left and right square Point middle of Left Square FF2FF10F2 FF2FF10F2 false +Square overlapping left and right square Point middle of Right Square 0F2FF1FF2 0F2FF1FF2 true +Square overlapping left and right square Square (left) 212111212 2F2F0F202 false +Square overlapping left and right square Square (right) 212F11FF2 2F2F00FF2 false +Square overlapping left and right square Square overlapping left and right square 2FFF1FFF2 2FFF0FFF2 false # ST_Envelope query TT @@ -3046,6 +3544,11 @@ SELECT ST_AsEWKT(ST_MakePolygon( ---- SRID=4326;POLYGON ((40 80, 80 80, 80 40, 40 40, 40 80), (50 70, 70 70, 70 50, 50 50, 50 70)) +query T +SELECT ST_AsEWKT(ST_Polygon( ST_GeomFromText('LINESTRING(75 29,77 29,77 29, 75 29)'), 4326)) +---- +SRID=4326;POLYGON ((75 29, 77 29, 77 29, 75 29)) + # Multi-Geometry related operations. query ITTT SELECT diff --git a/pkg/sql/sem/builtins/geo_builtins.go b/pkg/sql/sem/builtins/geo_builtins.go index 8a107c3922bd..adf6ecb8e9d8 100644 --- a/pkg/sql/sem/builtins/geo_builtins.go +++ b/pkg/sql/sem/builtins/geo_builtins.go @@ -596,6 +596,30 @@ var geoBuiltins = map[string]builtinDefinition{ Volatility: tree.VolatilityImmutable, }, ), + "st_polygon": makeBuiltin( + defProps(), + tree.Overload{ + Types: tree.ArgTypes{ + {"geometry", types.Geometry}, + {"srid", types.Int}, + }, + ReturnType: tree.FixedReturnType(types.Geometry), + Fn: func(ctx *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + g := tree.MustBeDGeometry(args[0]) + srid := tree.MustBeDInt(args[1]) + polygon, err := geomfn.MakePolygonWithSRID(g.Geometry, int(srid)) + if err != nil { + return nil, err + } + return tree.NewDGeometry(polygon), nil + }, + Info: infoBuilder{ + info: `Returns a new Polygon from the given LineString and sets its SRID. It is equivalent ` + + `to ST_MakePolygon with a single argument followed by ST_SetSRID.`, + }.String(), + Volatility: tree.VolatilityImmutable, + }, + ), "st_geomcollfromtext": geometryFromTextCheckShapeBuiltin(geopb.ShapeType_GeometryCollection), "st_geomcollfromwkb": geometryFromWKBCheckShapeBuiltin(geopb.ShapeType_GeometryCollection), @@ -1962,6 +1986,43 @@ Flags shown square brackets after the geometry type have the following meaning: Volatility: tree.VolatilityImmutable, }, ), + "st_minimumclearance": makeBuiltin( + defProps(), + geometryOverload1( + func(ctx *tree.EvalContext, g *tree.DGeometry) (tree.Datum, error) { + ret, err := geomfn.MinimumClearance(g.Geometry) + if err != nil { + return nil, err + } + return tree.NewDFloat(tree.DFloat(ret)), nil + }, + types.Float, + infoBuilder{ + info: `Returns the minimum distance a vertex can move before producing an invalid geometry. ` + + `Returns Infinity if no minimum clearance can be found (e.g. for a single point).`, + }, + tree.VolatilityImmutable, + ), + ), + "st_minimumclearanceline": makeBuiltin( + defProps(), + geometryOverload1( + func(ctx *tree.EvalContext, g *tree.DGeometry) (tree.Datum, error) { + ret, err := geomfn.MinimumClearanceLine(g.Geometry) + if err != nil { + return nil, err + } + return tree.NewDGeometry(ret), nil + }, + types.Geometry, + infoBuilder{ + info: `Returns a LINESTRING spanning the minimum distance a vertex can move before producing ` + + `an invalid geometry. If no minimum clearance can be found (e.g. for a single point), an ` + + `empty LINESTRING is returned.`, + }, + tree.VolatilityImmutable, + ), + ), "st_numinteriorrings": makeBuiltin( defProps(), geometryOverload1( @@ -2697,6 +2758,104 @@ The azimuth is angle is referenced from north, and is positive clockwise: North tree.VolatilityImmutable, ), ), + "st_frechetdistance": makeBuiltin( + defProps(), + geometryOverload2( + func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { + ret, err := geomfn.FrechetDistance(a.Geometry, b.Geometry) + if err != nil { + return nil, err + } + if ret == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*ret)), nil + }, + types.Float, + infoBuilder{ + info: `Returns the Frechet distance between the given geometries.`, + libraryUsage: usesGEOS, + }, + tree.VolatilityImmutable, + ), + tree.Overload{ + Types: tree.ArgTypes{ + {"geometry_a", types.Geometry}, + {"geometry_b", types.Geometry}, + {"densify_frac", types.Float}, + }, + ReturnType: tree.FixedReturnType(types.Float), + Fn: func(ctx *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + a := tree.MustBeDGeometry(args[0]) + b := tree.MustBeDGeometry(args[1]) + densifyFrac := tree.MustBeDFloat(args[2]) + + ret, err := geomfn.FrechetDistanceDensify(a.Geometry, b.Geometry, float64(densifyFrac)) + if err != nil { + return nil, err + } + if ret == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*ret)), nil + }, + Info: infoBuilder{ + info: `Returns the Frechet distance between the given geometries, with the given ` + + `segment densification (range 0.0-1.0, -1 to disable).`, + libraryUsage: usesGEOS, + }.String(), + Volatility: tree.VolatilityImmutable, + }, + ), + "st_hausdorffdistance": makeBuiltin( + defProps(), + geometryOverload2( + func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { + ret, err := geomfn.HausdorffDistance(a.Geometry, b.Geometry) + if err != nil { + return nil, err + } + if ret == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*ret)), nil + }, + types.Float, + infoBuilder{ + info: `Returns the Hausdorff distance between the given geometries.`, + libraryUsage: usesGEOS, + }, + tree.VolatilityImmutable, + ), + tree.Overload{ + Types: tree.ArgTypes{ + {"geometry_a", types.Geometry}, + {"geometry_b", types.Geometry}, + {"densify_frac", types.Float}, + }, + ReturnType: tree.FixedReturnType(types.Float), + Fn: func(ctx *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + a := tree.MustBeDGeometry(args[0]) + b := tree.MustBeDGeometry(args[1]) + densifyFrac := tree.MustBeDFloat(args[2]) + + ret, err := geomfn.HausdorffDistanceDensify(a.Geometry, b.Geometry, float64(densifyFrac)) + if err != nil { + return nil, err + } + if ret == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*ret)), nil + }, + Info: infoBuilder{ + info: `Returns the Hausdorff distance between the given geometries, with the given ` + + `segment densification (range 0.0-1.0).`, + libraryUsage: usesGEOS, + }.String(), + Volatility: tree.VolatilityImmutable, + }, + ), "st_maxdistance": makeBuiltin( defProps(), geometryOverload2( @@ -3001,6 +3160,30 @@ Note if geometries are the same, it will return the LineString with the minimum }.String(), Volatility: tree.VolatilityImmutable, }, + tree.Overload{ + Types: tree.ArgTypes{ + {"geometry_a", types.Geometry}, + {"geometry_b", types.Geometry}, + {"bnr", types.Int}, + }, + ReturnType: tree.FixedReturnType(types.String), + Fn: func(ctx *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + a := tree.MustBeDGeometry(args[0]) + b := tree.MustBeDGeometry(args[1]) + bnr := tree.MustBeDInt(args[2]) + ret, err := geomfn.RelateBoundaryNodeRule(a.Geometry, b.Geometry, int(bnr)) + if err != nil { + return nil, err + } + return tree.NewDString(ret), nil + }, + Info: infoBuilder{ + info: `Returns the DE-9IM spatial relation between geometry_a and geometry_b using the given ` + + `boundary node rule (1:OGC/MOD2, 2:Endpoint, 3:MultivalentEndpoint, 4:MonovalentEndpoint).`, + libraryUsage: usesGEOS, + }.String(), + Volatility: tree.VolatilityImmutable, + }, ), "st_relatematch": makeBuiltin( defProps(), @@ -3143,6 +3326,24 @@ For flags=1, validity considers self-intersecting rings forming holes as valid a // Topology operations // + "st_boundary": makeBuiltin( + defProps(), + geometryOverload1( + func(ctx *tree.EvalContext, g *tree.DGeometry) (tree.Datum, error) { + centroid, err := geomfn.Boundary(g.Geometry) + if err != nil { + return nil, err + } + return tree.NewDGeometry(centroid), err + }, + types.Geometry, + infoBuilder{ + info: "Returns the closure of the combinatorial boundary of this Geometry.", + libraryUsage: usesGEOS, + }, + tree.VolatilityImmutable, + ), + ), "st_centroid": makeBuiltin( defProps(), append( @@ -3215,6 +3416,24 @@ For flags=1, validity considers self-intersecting rings forming holes as valid a tree.VolatilityImmutable, ), ), + "st_difference": makeBuiltin( + defProps(), + geometryOverload2( + func(ctx *tree.EvalContext, a, b *tree.DGeometry) (tree.Datum, error) { + diff, err := geomfn.Difference(a.Geometry, b.Geometry) + if err != nil { + return nil, err + } + return tree.NewDGeometry(diff), err + }, + types.Geometry, + infoBuilder{ + info: "Returns the difference of two Geometries.", + libraryUsage: usesGEOS, + }, + tree.VolatilityImmutable, + ), + ), "st_pointonsurface": makeBuiltin( defProps(), geometryOverload1( @@ -4334,6 +4553,92 @@ Bottom Left.`, }, ), + "st_angle": makeBuiltin( + defProps(), + tree.Overload{ + Types: tree.ArgTypes{ + {"point1", types.Geometry}, + {"point2", types.Geometry}, + {"point3", types.Geometry}, + {"point4", types.Geometry}, + }, + ReturnType: tree.FixedReturnType(types.Float), + Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + g1 := tree.MustBeDGeometry(args[0]).Geometry + g2 := tree.MustBeDGeometry(args[1]).Geometry + g3 := tree.MustBeDGeometry(args[2]).Geometry + g4 := tree.MustBeDGeometry(args[3]).Geometry + angle, err := geomfn.Angle(g1, g2, g3, g4) + if err != nil { + return nil, err + } + if angle == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*angle)), nil + }, + Info: infoBuilder{ + info: `Returns the clockwise angle between the vectors formed by point1,point2 and point3,point4. ` + + `The arguments must be POINT geometries. Returns NULL if any vectors have 0 length.`, + }.String(), + Volatility: tree.VolatilityImmutable, + }, + tree.Overload{ + Types: tree.ArgTypes{ + {"point1", types.Geometry}, + {"point2", types.Geometry}, + {"point3", types.Geometry}, + }, + ReturnType: tree.FixedReturnType(types.Float), + Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + g1 := tree.MustBeDGeometry(args[0]).Geometry + g2 := tree.MustBeDGeometry(args[1]).Geometry + g3 := tree.MustBeDGeometry(args[2]).Geometry + g4, err := geo.MakeGeometryFromGeomT(geom.NewPointEmpty(geom.XY)) + if err != nil { + return nil, err + } + angle, err := geomfn.Angle(g1, g2, g3, g4) + if err != nil { + return nil, err + } + if angle == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*angle)), nil + }, + Info: infoBuilder{ + info: `Returns the clockwise angle between the vectors formed by point2,point1 and point2,point3. ` + + `The arguments must be POINT geometries. Returns NULL if any vectors have 0 length.`, + }.String(), + Volatility: tree.VolatilityImmutable, + }, + tree.Overload{ + Types: tree.ArgTypes{ + {"line1", types.Geometry}, + {"line2", types.Geometry}, + }, + ReturnType: tree.FixedReturnType(types.Float), + Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + g1 := tree.MustBeDGeometry(args[0]).Geometry + g2 := tree.MustBeDGeometry(args[1]).Geometry + angle, err := geomfn.AngleLineString(g1, g2) + if err != nil { + return nil, err + } + if angle == nil { + return tree.DNull, nil + } + return tree.NewDFloat(tree.DFloat(*angle)), nil + }, + Info: infoBuilder{ + info: `Returns the clockwise angle between two LINESTRING geometries, treating them as vectors ` + + `between their start- and endpoints. Returns NULL if any vectors have 0 length.`, + }.String(), + Volatility: tree.VolatilityImmutable, + }, + ), + // // BoundingBox // @@ -4788,13 +5093,11 @@ Bottom Left.`, // Unimplemented. // - "st_angle": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48866}), "st_asencodedpolyline": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48872}), "st_asgml": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48877}), "st_aslatlontext": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48882}), "st_assvg": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48883}), "st_astwkb": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48886}), - "st_boundary": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48888}), "st_boundingdiagonal": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48889}), "st_buildarea": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48892}), "st_chaikinsmoothing": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48894}), @@ -4806,14 +5109,11 @@ Bottom Left.`, "st_clusterwithin": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48901}), "st_concavehull": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48906}), "st_delaunaytriangles": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48915}), - "st_difference": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48917}), "st_dump": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49785}), "st_dumppoints": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49786}), "st_dumprings": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49787}), - "st_frechetdistance": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48940}), "st_generatepoints": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48941}), "st_geometricmedian": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48944}), - "st_hausdorffdistance": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48947}), "st_interpolatepoint": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48950}), "st_isvaliddetail": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48962}), "st_length2dspheroid": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48967}), @@ -4824,13 +5124,10 @@ Bottom Left.`, "st_memsize": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48985}), "st_minimumboundingcircle": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48987}), "st_minimumboundingradius": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48988}), - "st_minimumclearance": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48989}), - "st_minimumclearanceline": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48990}), "st_node": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48993}), "st_orderingequals": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49002}), "st_orientedenvelope": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49003}), "st_pointinsidecircle": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49007}), - "st_polygon": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49010}), "st_polygonize": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49011}), "st_quantizecoordinates": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49012}), "st_seteffectivearea": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49030}),