Skip to content

Commit

Permalink
geo/geomfn: use GEOSRelatePattern for ST_ContainsProperly
Browse files Browse the repository at this point in the history
GEOSRelatePattern seems to avoid the checks for wildcards, making
ST_ContainsProperly faster.

Release note: None
  • Loading branch information
otan committed Jun 2, 2020
1 parent d652d65 commit 26d5952
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 46 deletions.
8 changes: 3 additions & 5 deletions pkg/geo/geomfn/binary_predicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,13 @@ func Contains(a *geo.Geometry, b *geo.Geometry) (bool, error) {

// ContainsProperly returns whether geometry A properly contains geometry B.
func ContainsProperly(a *geo.Geometry, b *geo.Geometry) (bool, error) {
// No GEOS CAPI to call ContainsProperly; fallback to Relate.
relate, err := Relate(a, b)
if err != nil {
return false, err
if a.SRID() != b.SRID() {
return false, geo.NewMismatchingSRIDsError(a, b)
}
if !a.BoundingBoxIntersects(b) {
return false, nil
}
return MatchesDE9IM(relate, "T**FF*FF*")
return geos.RelatePattern(a.EWKB(), b.EWKB(), "T**FF*FF*")
}

// Crosses returns whether geometry A crosses geometry B.
Expand Down
8 changes: 8 additions & 0 deletions pkg/geo/geomfn/de9im.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ func Relate(a *geo.Geometry, b *geo.Geometry) (string, error) {
return geos.Relate(a.EWKB(), b.EWKB())
}

// 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() {
return false, geo.NewMismatchingSRIDsError(a, b)
}
return geos.RelatePattern(a.EWKB(), b.EWKB(), pattern)
}

// MatchesDE9IM checks whether the given DE-9IM relation matches the DE-91M pattern.
// Assumes the relation has been computed, and such has no 'T' and '*' characters.
// See: https://en.wikipedia.org/wiki/DE-9IM.
Expand Down
83 changes: 61 additions & 22 deletions pkg/geo/geos/geos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@
#include <windows.h>
#else
#include <dlfcn.h>
#endif // #if _WIN32
#endif // #if _WIN32
#include <memory>
#include <stdlib.h>
#include <string>

#include "geos.h"

#if _WIN32
#define dlopen(x,y) LoadLibrary(x)
#define dlopen(x, y) LoadLibrary(x)
#define dlsym GetProcAddress
#define dlclose FreeLibrary
#define dlerror() ((char*) "failed to execute dlsym")
#define dlerror() ((char*)"failed to execute dlsym")
typedef HMODULE dlhandle;
#else
typedef void* dlhandle;
#endif // #if _WIN32
#endif // #if _WIN32

#define CR_GEOS_NO_ERROR_DEFINED_MESSAGE "geos: returned invalid result but error not populated"

Expand Down Expand Up @@ -80,6 +80,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_RelatePattern_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry,
const char*);

typedef CR_GEOS_WKBWriter (*CR_GEOS_WKBWriter_create_r)(CR_GEOS_Handle);
typedef char* (*CR_GEOS_WKBWriter_write_r)(CR_GEOS_Handle, CR_GEOS_WKBWriter, CR_GEOS_Geometry,
Expand Down Expand Up @@ -133,6 +135,7 @@ struct CR_GEOS {
CR_GEOS_Within_r GEOSWithin_r;

CR_GEOS_Relate_r GEOSRelate_r;
CR_GEOS_RelatePattern_r GEOSRelatePattern_r;

CR_GEOS_WKBWriter_create_r GEOSWKBWriter_create_r;
CR_GEOS_WKBWriter_destroy_r GEOSWKBWriter_destroy_r;
Expand Down Expand Up @@ -180,6 +183,7 @@ struct CR_GEOS {
INIT(GEOSTouches_r);
INIT(GEOSWithin_r);
INIT(GEOSRelate_r);
INIT(GEOSRelatePattern_r);
INIT(GEOSWKTReader_create_r);
INIT(GEOSWKTReader_destroy_r);
INIT(GEOSWKTReader_read_r);
Expand Down Expand Up @@ -251,7 +255,8 @@ CR_GEOS_Handle initHandleWithErrorBuffer(CR_GEOS* lib, std::string* buffer) {
return handle;
}

CR_GEOS_Geometry CR_GEOS_GeometryFromSlice(CR_GEOS* lib, CR_GEOS_Handle handle, CR_GEOS_Slice slice) {
CR_GEOS_Geometry CR_GEOS_GeometryFromSlice(CR_GEOS* lib, CR_GEOS_Handle handle,
CR_GEOS_Slice slice) {
auto wkbReader = lib->GEOSWKBReader_create_r(handle);
auto geom = lib->GEOSWKBReader_read_r(handle, wkbReader, slice.data, slice.len);
lib->GEOSWKBReader_destroy_r(handle, wkbReader);
Expand Down Expand Up @@ -314,7 +319,7 @@ CR_GEOS_Status CR_GEOS_ClipEWKBByRect(CR_GEOS* lib, CR_GEOS_Slice ewkb, double x
//

template <typename T, typename R>
CR_GEOS_Status CR_GEOS_UnaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, R *ret) {
CR_GEOS_Status CR_GEOS_UnaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, R* ret) {
std::string error;
auto handle = initHandleWithErrorBuffer(lib, &error);
auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a);
Expand All @@ -333,7 +338,8 @@ CR_GEOS_Status CR_GEOS_UnaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, R *ret
}

template <typename T, typename R>
CR_GEOS_Status CR_GEOS_BinaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GEOS_Slice b, R *ret) {
CR_GEOS_Status CR_GEOS_BinaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GEOS_Slice b,
R* ret) {
std::string error;
auto handle = initHandleWithErrorBuffer(lib, &error);
auto wkbReader = lib->GEOSWKBReader_create_r(handle);
Expand All @@ -359,15 +365,15 @@ CR_GEOS_Status CR_GEOS_BinaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GE
return toGEOSString(error.data(), error.length());
}

CR_GEOS_Status CR_GEOS_Area(CR_GEOS* lib, CR_GEOS_Slice a, double *ret) {
CR_GEOS_Status CR_GEOS_Area(CR_GEOS* lib, CR_GEOS_Slice a, double* ret) {
return CR_GEOS_UnaryOperator(lib, lib->GEOSArea_r, a, ret);
}

CR_GEOS_Status CR_GEOS_Length(CR_GEOS* lib, CR_GEOS_Slice a, double *ret) {
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_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String *centroidEWKB) {
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);
auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a);
Expand All @@ -385,7 +391,7 @@ CR_GEOS_Status CR_GEOS_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String *c
return toGEOSString(error.data(), error.length());
}

CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double *ret) {
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);
}

Expand All @@ -394,7 +400,8 @@ CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b,
//

template <typename T>
CR_GEOS_Status CR_GEOS_BinaryPredicate(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_BinaryPredicate(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GEOS_Slice b,
char* ret) {
std::string error;
auto handle = initHandleWithErrorBuffer(lib, &error);

Expand Down Expand Up @@ -424,39 +431,39 @@ CR_GEOS_Status CR_GEOS_BinaryPredicate(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_G
return toGEOSString(error.data(), error.length());
}

CR_GEOS_Status CR_GEOS_Covers(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Covers(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSCovers_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_CoveredBy(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_CoveredBy(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSCoveredBy_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Contains(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Contains(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSContains_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Crosses(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Crosses(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSCrosses_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Equals(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Equals(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSEquals_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Intersects(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Intersects(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSIntersects_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Overlaps(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Overlaps(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSOverlaps_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Touches(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Touches(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSTouches_r, a, b, ret);
}

CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret) {
CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
return CR_GEOS_BinaryPredicate(lib, lib->GEOSWithin_r, a, b, ret);
}

Expand All @@ -465,7 +472,7 @@ CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, ch
// See: https://en.wikipedia.org/wiki/DE-9IM.
//

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_Relate(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_String* ret) {
std::string error;
auto handle = initHandleWithErrorBuffer(lib, &error);

Expand All @@ -490,3 +497,35 @@ CR_GEOS_Status CR_GEOS_Relate(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR
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;
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);
auto p = std::string(pattern.data, pattern.len);
lib->GEOSWKBReader_destroy_r(handle, wkbReader);

if (geomA != nullptr && geomB != nullptr) {
auto r = lib->GEOSRelatePattern_r(handle, geomA, geomB, p.c_str());
// ret == 2 indicates an exception.
if (r == 2) {
if (error.length() == 0) {
error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE);
}
} else {
*ret = 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());
}
15 changes: 15 additions & 0 deletions pkg/geo/geos/geos.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,18 @@ func Relate(a geopb.EWKB, b geopb.EWKB) (string, error) {
}
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()
if err != nil {
return false, err
}
var ret C.char
if err := statusToError(
C.CR_GEOS_RelatePattern(g, goToCSlice(a), goToCSlice(b), goToCSlice([]byte(pattern)), &ret),
); err != nil {
return false, err
}
return ret == 1, nil
}
30 changes: 16 additions & 14 deletions pkg/geo/geos/geos.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,35 +61,37 @@ CR_GEOS_Status CR_GEOS_ClipEWKBByRect(CR_GEOS* lib, CR_GEOS_Slice wkb, double xm
// Unary operators.
//

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_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String *centroidEWKB);
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_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* centroidEWKB);

//
// Binary operators.
//

CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double *ret);
CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double* ret);

//
// Binary predicates.
//

CR_GEOS_Status CR_GEOS_Covers(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_CoveredBy(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Contains(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Crosses(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Equals(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Intersects(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Overlaps(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Touches(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char *ret);
CR_GEOS_Status CR_GEOS_Covers(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_CoveredBy(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Contains(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Crosses(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Equals(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Intersects(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Overlaps(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Touches(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);
CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret);

//
// DE-9IM related
//

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_Relate(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, 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);
#ifdef __cplusplus
} // extern "C"
#endif
6 changes: 1 addition & 5 deletions pkg/sql/sem/builtins/geo_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -1854,11 +1854,7 @@ Note ST_Perimeter is only valid for Polygon - use ST_Length for LineString.`,
a := args[0].(*tree.DGeometry)
b := args[1].(*tree.DGeometry)
pattern := args[2].(*tree.DString)
relation, err := geomfn.Relate(a.Geometry, b.Geometry)
if err != nil {
return nil, err
}
ret, err := geomfn.MatchesDE9IM(relation, string(*pattern))
ret, err := geomfn.RelatePattern(a.Geometry, b.Geometry, string(*pattern))
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 26d5952

Please sign in to comment.