Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
57697: geo: add st_orientedenvelope builtin r=otan a=kakshay21

Resolves cockroachdb#49003
Release note (sql change): New builtin method to calculate oriented envelope

Co-authored-by: Kumar Akshay <[email protected]>
  • Loading branch information
craig[bot] and kakshay21 committed Dec 14, 2020
2 parents b5d618c + 89dc433 commit cea9423
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 4 deletions.
4 changes: 4 additions & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1995,6 +1995,10 @@ calculated, the result is transformed back into a Geography with SRID 4326.</p>
</span></td></tr>
<tr><td><a name="st_orderingequals"></a><code>st_orderingequals(geometry_a: geometry, geometry_b: geometry) &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>Returns true if geometry_a is exactly equal to geometry_b, having all coordinates in the same order, as well as the same type, SRID, bounding box, and so on.</p>
</span></td></tr>
<tr><td><a name="st_orientedenvelope"></a><code>st_orientedenvelope(geometry: geometry) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a minimum rotated rectangle enclosing a geometry.
Note that more than one minimum rotated rectangle may exist.
May return a Point or LineString in the case of degenerate inputs.</p>
</span></td></tr>
<tr><td><a name="st_overlaps"></a><code>st_overlaps(geometry_a: geometry, geometry_b: geometry) &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>Returns true if geometry_a intersects but does not completely contain geometry_b, or vice versa. “Does not completely” implies ST_Within(geometry_a, geometry_b) = ST_Within(geometry_b, geometry_a) = false.</p>
<p>This function utilizes the GEOS module.</p>
<p>This function variant will attempt to utilize any available spatial index.</p>
Expand Down
13 changes: 13 additions & 0 deletions pkg/geo/geomfn/topology_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,16 @@ func SharedPaths(a geo.Geometry, b geo.Geometry) (geo.Geometry, error) {
}
return gm, nil
}

// MinimumRotatedRectangle Returns a minimum rotated rectangle enclosing a geometry
func MinimumRotatedRectangle(g geo.Geometry) (geo.Geometry, error) {
paths, err := geos.MinimumRotatedRectangle(g.EWKB())
if err != nil {
return geo.Geometry{}, err
}
gm, err := geo.ParseGeometryFromEWKB(paths)
if err != nil {
return geo.Geometry{}, err
}
return gm, nil
}
66 changes: 66 additions & 0 deletions pkg/geo/geomfn/topology_operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,69 @@ func TestUnaryUnion(t *testing.T) {
})
}
}

func TestMinimumRotatedRectangle(t *testing.T) {
tests := []struct {
name string
arg geo.Geometry
want geo.Geometry
}{
{
"empty multipoint",
geo.MustParseGeometry("MULTIPOINT EMPTY"),
geo.MustParseGeometry("POLYGON EMPTY"),
},
{
"multipoint, must return the valid polygon",
geo.MustParseGeometry("MULTIPOINT ((0 0), (-1 -1), (3 2))"),
geo.MustParseGeometry("POLYGON((3 2,2.88 2.16,-1.12 -0.84,-1 -1,3 2))"),
},
{
"multipoint, must give linestring in case of degenerate input",
geo.MustParseGeometry("MULTIPOINT ((0 0), (-2 0), (1 0))"),
geo.MustParseGeometry("LINESTRING (-2 0, 1 0)"),
},
{
"multipoint, must give point in case of degenerate input",
geo.MustParseGeometry("MULTIPOINT ((0 0), (0 0), (0 0))"),
geo.MustParseGeometry("POINT (0 0)"),
},
{
"point, must return the valid point",
geo.MustParseGeometry("POINT (1 1)"),
geo.MustParseGeometry("POINT (1 1)"),
},
{
"linestring, must return the valid polygon",
geo.MustParseGeometry("LINESTRING (0 0, 50 200, 100 0)"),
geo.MustParseGeometry("POLYGON ((0 0,94.1176470588235 -23.5294117647059,144.117647058824 176.470588235294,50 200,0 0))"),
},
{
"polygon, must return the valid polygon",
geo.MustParseGeometry("POLYGON ((0 0,1 -2,1 1,5 2,0 0))"),
geo.MustParseGeometry("POLYGON ((-0.5 -0.5,1 -2,5 2,3.5 3.5,-0.5 -0.5))"),
},
{
"multilinestring, must give linestring in case of degenerate input",
geo.MustParseGeometry("MULTILINESTRING ((1 1, 2 2))"),
geo.MustParseGeometry("LINESTRING (1 1,2 2)"),
},
{
"multipolygon, must give linestring in case of degenerate input",
geo.MustParseGeometry("MULTIPOLYGON (((1 2, 3 4, 5 6, 1 2)))"),
geo.MustParseGeometry("LINESTRING (1 2,5 6)"),
},
{
"multilinestring, must give linestring in case of degenerate input",
geo.MustParseGeometry("GEOMETRYCOLLECTION (MULTIPOINT (1 1, 2 2))"),
geo.MustParseGeometry("LINESTRING (1 1,2 2)"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MinimumRotatedRectangle(tt.arg)
require.NoError(t, err)
require.Equal(t, true, EqualsExact(got, tt.want, 1e-6))
})
}
}
6 changes: 3 additions & 3 deletions pkg/geo/geomfn/voronoi.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ func VoronoiDiagram(
if err != nil {
return geo.Geometry{}, err
}
gm, gmErr := geo.ParseGeometryFromEWKB(paths)
if gmErr != nil {
return geo.Geometry{}, gmErr
gm, err := geo.ParseGeometryFromEWKB(paths)
if err != nil {
return geo.Geometry{}, err
}
return gm, nil
}
23 changes: 23 additions & 0 deletions pkg/geo/geos/geos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ typedef CR_GEOS_Geometry (*CR_GEOS_VoronoiDiagram_r)(CR_GEOS_Handle, CR_GEOS_Geo
typedef char (*CR_GEOS_EqualsExact_r)(CR_GEOS_Handle, CR_GEOS_Geometry,
CR_GEOS_Geometry, double);

typedef CR_GEOS_Geometry (*CR_GEOS_MinimumRotatedRectangle_r)(CR_GEOS_Handle, CR_GEOS_Geometry);

std::string ToString(CR_GEOS_Slice slice) { return std::string(slice.data, slice.len); }

} // namespace
Expand Down Expand Up @@ -267,6 +269,7 @@ struct CR_GEOS {
CR_GEOS_SharedPaths_r GEOSSharedPaths_r;
CR_GEOS_VoronoiDiagram_r GEOSVoronoiDiagram_r;
CR_GEOS_EqualsExact_r GEOSEqualsExact_r;
CR_GEOS_MinimumRotatedRectangle_r GEOSMinimumRotatedRectangle_r;

CR_GEOS_Node_r GEOSNode_r;

Expand Down Expand Up @@ -351,6 +354,7 @@ struct CR_GEOS {
INIT(GEOSRelate_r);
INIT(GEOSVoronoiDiagram_r);
INIT(GEOSEqualsExact_r);
INIT(GEOSMinimumRotatedRectangle_r);
INIT(GEOSRelateBoundaryNodeRule_r);
INIT(GEOSRelatePattern_r);
INIT(GEOSSharedPaths_r);
Expand Down Expand Up @@ -1419,3 +1423,22 @@ CR_GEOS_Status CR_GEOS_EqualsExact(CR_GEOS* lib, CR_GEOS_Slice lhs, CR_GEOS_Slic
lib->GEOS_finish_r(handle);
return toGEOSString(error.data(), error.length());
}

CR_GEOS_Status CR_GEOS_MinimumRotatedRectangle(CR_GEOS* lib, CR_GEOS_Slice g, CR_GEOS_String* ret) {
std::string error;
auto handle = initHandleWithErrorBuffer(lib, &error);
auto gGeom = CR_GEOS_GeometryFromSlice(lib, handle, g);
*ret = {.data = NULL, .len = 0};
if (gGeom != nullptr) {
auto r = lib->GEOSMinimumRotatedRectangle_r(handle, gGeom);
if (r != NULL) {
auto srid = lib->GEOSGetSRID_r(handle, r);
CR_GEOS_writeGeomToEWKB(lib, handle, r, ret, srid);
lib->GEOSGeom_destroy_r(handle, r);
}
lib->GEOSGeom_destroy_r(handle, gGeom);
}

lib->GEOS_finish_r(handle);
return toGEOSString(error.data(), error.length());
}
15 changes: 15 additions & 0 deletions pkg/geo/geos/geos.go
Original file line number Diff line number Diff line change
Expand Up @@ -1017,3 +1017,18 @@ func VoronoiDiagram(a, env geopb.EWKB, tolerance float64, onlyEdges bool) (geopb
}
return cStringToSafeGoBytes(cEWKB), nil
}

// MinimumRotatedRectangle Returns a minimum rotated rectangle enclosing a geometry
func MinimumRotatedRectangle(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_MinimumRotatedRectangle(g, goToCSlice(ewkb), &cEWKB),
); err != nil {
return nil, err
}
return cStringToSafeGoBytes(cEWKB), nil
}
2 changes: 2 additions & 0 deletions pkg/geo/geos/geos.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ CR_GEOS_Status CR_GEOS_Node(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* ret);

CR_GEOS_Status CR_GEOS_MinimumBoundingCircle(CR_GEOS* lib, CR_GEOS_Slice a, double* radius,
CR_GEOS_String* centerEWKB, CR_GEOS_String* polygonEWKB);

CR_GEOS_Status CR_GEOS_MinimumRotatedRectangle(CR_GEOS* lib, CR_GEOS_Slice g, CR_GEOS_String* ret);
//
// Linear reference.
//
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/geospatial
Original file line number Diff line number Diff line change
Expand Up @@ -5671,3 +5671,10 @@ query T
SELECT ST_AsText(ST_VoronoiLines(ST_GeomFromText('MULTIPOINT(50 30, 60 30, 100 100,10 150, 110 120)')));
----
MULTILINESTRING ((100.5 270, 59.347826086956523 132.826086956521749), (59.347826086956523 132.826086956521749, 36.81818181818182 92.272727272727266), (36.81818181818182 92.272727272727266, -110 43.333333333333321), (36.81818181818182 92.272727272727266, 55 79.285714285714278), (55 79.285714285714278, 55 -90), (59.347826086956523 132.826086956521749, 230 47.5), (230 -20.714285714285733, 55 79.285714285714278))

subtest st_orientedenvelope

query T
SELECT ST_AsText(ST_OrientedEnvelope(ST_GeomFromText('MULTIPOINT ((0 0), (-1 -1), (3 2))')));
----
POLYGON ((3 2, 2.88 2.16, -1.12 -0.84, -1 -1, 3 2))
24 changes: 23 additions & 1 deletion pkg/sql/sem/builtins/geo_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -5830,6 +5830,29 @@ See http://developers.google.com/maps/documentation/utilities/polylinealgorithm`
Volatility: tree.VolatilityImmutable,
},
),
"st_orientedenvelope": makeBuiltin(
defProps(),
tree.Overload{
Types: tree.ArgTypes{
{"geometry", types.Geometry},
},
ReturnType: tree.FixedReturnType(types.Geometry),
Fn: func(ctx *tree.EvalContext, args tree.Datums) (tree.Datum, error) {
g := tree.MustBeDGeometry(args[0])
ret, err := geomfn.MinimumRotatedRectangle(g.Geometry)
if err != nil {
return nil, err
}
return tree.NewDGeometry(ret), nil
},
Info: infoBuilder{
info: `Returns a minimum rotated rectangle enclosing a geometry.
Note that more than one minimum rotated rectangle may exist.
May return a Point or LineString in the case of degenerate inputs.`,
}.String(),
Volatility: tree.VolatilityImmutable,
},
),

//
// Unimplemented.
Expand Down Expand Up @@ -5860,7 +5883,6 @@ See http://developers.google.com/maps/documentation/utilities/polylinealgorithm`
"st_lengthspheroid": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48968}),
"st_linecrossingdirection": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48969}),
"st_linesubstring": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 48975}),
"st_orientedenvelope": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49003}),
"st_polygonize": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49011}),
"st_quantizecoordinates": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49012}),
"st_seteffectivearea": makeBuiltin(tree.FunctionProperties{UnsupportedWithIssue: 49030}),
Expand Down

0 comments on commit cea9423

Please sign in to comment.