github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geos/geos.cc (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  #include <cstdarg>
    12  #include <cstring>
    13  #if _WIN32
    14  #include <windows.h>
    15  #else
    16  #include <dlfcn.h>
    17  #endif  // #if _WIN32
    18  #include <memory>
    19  #include <stdlib.h>
    20  #include <string>
    21  
    22  #include "geos.h"
    23  
    24  #if _WIN32
    25  #define dlopen(x, y) LoadLibrary(x)
    26  #define dlsym GetProcAddress
    27  #define dlclose FreeLibrary
    28  #define dlerror() ((char*)"failed to execute dlsym")
    29  typedef HMODULE dlhandle;
    30  #else
    31  typedef void* dlhandle;
    32  #endif  // #if _WIN32
    33  
    34  #define CR_GEOS_NO_ERROR_DEFINED_MESSAGE "geos: returned invalid result but error not populated"
    35  
    36  namespace {
    37  
    38  // Data Types adapted from `capi/geos_c.h.in` in GEOS.
    39  typedef void* CR_GEOS_Handle;
    40  typedef void (*CR_GEOS_MessageHandler)(const char*, void*);
    41  typedef void* CR_GEOS_WKTReader;
    42  typedef void* CR_GEOS_WKBReader;
    43  typedef void* CR_GEOS_WKBWriter;
    44  typedef void* CR_GEOS_BufferParams;
    45  
    46  // Function declarations from `capi/geos_c.h.in` in GEOS.
    47  typedef CR_GEOS_Handle (*CR_GEOS_init_r)();
    48  typedef void (*CR_GEOS_finish_r)(CR_GEOS_Handle);
    49  typedef CR_GEOS_MessageHandler (*CR_GEOS_Context_setErrorMessageHandler_r)(CR_GEOS_Handle,
    50                                                                             CR_GEOS_MessageHandler,
    51                                                                             void*);
    52  typedef void (*CR_GEOS_Free_r)(CR_GEOS_Handle, void* buffer);
    53  typedef void (*CR_GEOS_SetSRID_r)(CR_GEOS_Handle, CR_GEOS_Geometry, int);
    54  typedef int (*CR_GEOS_GetSRID_r)(CR_GEOS_Handle, CR_GEOS_Geometry);
    55  typedef void (*CR_GEOS_GeomDestroy_r)(CR_GEOS_Handle, CR_GEOS_Geometry);
    56  
    57  typedef CR_GEOS_WKTReader (*CR_GEOS_WKTReader_create_r)(CR_GEOS_Handle);
    58  typedef CR_GEOS_Geometry (*CR_GEOS_WKTReader_read_r)(CR_GEOS_Handle, CR_GEOS_WKTReader,
    59                                                       const char*);
    60  typedef void (*CR_GEOS_WKTReader_destroy_r)(CR_GEOS_Handle, CR_GEOS_WKTReader);
    61  
    62  typedef CR_GEOS_WKBReader (*CR_GEOS_WKBReader_create_r)(CR_GEOS_Handle);
    63  typedef CR_GEOS_Geometry (*CR_GEOS_WKBReader_read_r)(CR_GEOS_Handle, CR_GEOS_WKBReader, const char*,
    64                                                       size_t);
    65  typedef void (*CR_GEOS_WKBReader_destroy_r)(CR_GEOS_Handle, CR_GEOS_WKBReader);
    66  
    67  typedef CR_GEOS_BufferParams (*CR_GEOS_BufferParams_create_r)(CR_GEOS_Handle);
    68  typedef void (*CR_GEOS_GEOSBufferParams_destroy_r)(CR_GEOS_Handle, CR_GEOS_BufferParams);
    69  typedef int (*CR_GEOS_BufferParams_setEndCapStyle_r)(CR_GEOS_Handle, CR_GEOS_BufferParams,
    70                                                       int endCapStyle);
    71  typedef int (*CR_GEOS_BufferParams_setJoinStyle_r)(CR_GEOS_Handle, CR_GEOS_BufferParams,
    72                                                     int joinStyle);
    73  typedef int (*CR_GEOS_BufferParams_setMitreLimit_r)(CR_GEOS_Handle, CR_GEOS_BufferParams,
    74                                                      double mitreLimit);
    75  typedef int (*CR_GEOS_BufferParams_setQuadrantSegments_r)(CR_GEOS_Handle, CR_GEOS_BufferParams,
    76                                                            int quadrantSegments);
    77  typedef int (*CR_GEOS_BufferParams_setSingleSided_r)(CR_GEOS_Handle, CR_GEOS_BufferParams,
    78                                                       int singleSided);
    79  typedef CR_GEOS_Geometry (*CR_GEOS_BufferWithParams_r)(CR_GEOS_Handle, CR_GEOS_Geometry,
    80                                                         CR_GEOS_BufferParams, double width);
    81  
    82  typedef int (*CR_GEOS_Area_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double*);
    83  typedef int (*CR_GEOS_Length_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double*);
    84  typedef CR_GEOS_Geometry (*CR_GEOS_Centroid_r)(CR_GEOS_Handle, CR_GEOS_Geometry);
    85  
    86  typedef CR_GEOS_Geometry (*CR_GEOS_Interpolate_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double);
    87  
    88  typedef int (*CR_GEOS_Distance_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry, double*);
    89  
    90  typedef char (*CR_GEOS_Covers_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    91  typedef char (*CR_GEOS_CoveredBy_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    92  typedef char (*CR_GEOS_Contains_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    93  typedef char (*CR_GEOS_Crosses_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    94  typedef char (*CR_GEOS_Equals_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    95  typedef char (*CR_GEOS_Intersects_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    96  typedef char (*CR_GEOS_Overlaps_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    97  typedef char (*CR_GEOS_Touches_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    98  typedef char (*CR_GEOS_Within_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
    99  
   100  typedef char* (*CR_GEOS_Relate_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry);
   101  typedef char (*CR_GEOS_RelatePattern_r)(CR_GEOS_Handle, CR_GEOS_Geometry, CR_GEOS_Geometry,
   102                                          const char*);
   103  
   104  typedef CR_GEOS_WKBWriter (*CR_GEOS_WKBWriter_create_r)(CR_GEOS_Handle);
   105  typedef char* (*CR_GEOS_WKBWriter_write_r)(CR_GEOS_Handle, CR_GEOS_WKBWriter, CR_GEOS_Geometry,
   106                                             size_t*);
   107  typedef void (*CR_GEOS_WKBWriter_setByteOrder_r)(CR_GEOS_Handle, CR_GEOS_WKBWriter, int);
   108  typedef void (*CR_GEOS_WKBWriter_destroy_r)(CR_GEOS_Handle, CR_GEOS_WKBWriter);
   109  typedef void (*CR_GEOS_WKBWriter_setIncludeSRID_r)(CR_GEOS_Handle, CR_GEOS_WKBWriter, const char);
   110  typedef CR_GEOS_Geometry (*CR_GEOS_ClipByRect_r)(CR_GEOS_Handle, CR_GEOS_Geometry, double, double,
   111                                                   double, double);
   112  
   113  std::string ToString(CR_GEOS_Slice slice) { return std::string(slice.data, slice.len); }
   114  
   115  const char* dlopenFailError = "failed to execute dlopen";
   116  
   117  }  // namespace
   118  
   119  struct CR_GEOS {
   120    dlhandle dlHandle;
   121  
   122    CR_GEOS_init_r GEOS_init_r;
   123    CR_GEOS_finish_r GEOS_finish_r;
   124    CR_GEOS_Context_setErrorMessageHandler_r GEOSContext_setErrorMessageHandler_r;
   125    CR_GEOS_Free_r GEOSFree_r;
   126  
   127    CR_GEOS_BufferParams_create_r GEOSBufferParams_create_r;
   128    CR_GEOS_GEOSBufferParams_destroy_r GEOSBufferParams_destroy_r;
   129    CR_GEOS_BufferParams_setEndCapStyle_r GEOSBufferParams_setEndCapStyle_r;
   130    CR_GEOS_BufferParams_setJoinStyle_r GEOSBufferParams_setJoinStyle_r;
   131    CR_GEOS_BufferParams_setMitreLimit_r GEOSBufferParams_setMitreLimit_r;
   132    CR_GEOS_BufferParams_setQuadrantSegments_r GEOSBufferParams_setQuadrantSegments_r;
   133    CR_GEOS_BufferParams_setSingleSided_r GEOSBufferParams_setSingleSided_r;
   134    CR_GEOS_BufferWithParams_r GEOSBufferWithParams_r;
   135  
   136    CR_GEOS_SetSRID_r GEOSSetSRID_r;
   137    CR_GEOS_GetSRID_r GEOSGetSRID_r;
   138    CR_GEOS_GeomDestroy_r GEOSGeom_destroy_r;
   139  
   140    CR_GEOS_WKTReader_create_r GEOSWKTReader_create_r;
   141    CR_GEOS_WKTReader_destroy_r GEOSWKTReader_destroy_r;
   142    CR_GEOS_WKTReader_read_r GEOSWKTReader_read_r;
   143  
   144    CR_GEOS_WKBReader_create_r GEOSWKBReader_create_r;
   145    CR_GEOS_WKBReader_destroy_r GEOSWKBReader_destroy_r;
   146    CR_GEOS_WKBReader_read_r GEOSWKBReader_read_r;
   147  
   148    CR_GEOS_Area_r GEOSArea_r;
   149    CR_GEOS_Length_r GEOSLength_r;
   150    CR_GEOS_Centroid_r GEOSGetCentroid_r;
   151  
   152    CR_GEOS_Interpolate_r GEOSInterpolate_r;
   153  
   154    CR_GEOS_Distance_r GEOSDistance_r;
   155  
   156    CR_GEOS_Covers_r GEOSCovers_r;
   157    CR_GEOS_CoveredBy_r GEOSCoveredBy_r;
   158    CR_GEOS_Contains_r GEOSContains_r;
   159    CR_GEOS_Crosses_r GEOSCrosses_r;
   160    CR_GEOS_Equals_r GEOSEquals_r;
   161    CR_GEOS_Intersects_r GEOSIntersects_r;
   162    CR_GEOS_Overlaps_r GEOSOverlaps_r;
   163    CR_GEOS_Touches_r GEOSTouches_r;
   164    CR_GEOS_Within_r GEOSWithin_r;
   165  
   166    CR_GEOS_Relate_r GEOSRelate_r;
   167    CR_GEOS_RelatePattern_r GEOSRelatePattern_r;
   168  
   169    CR_GEOS_WKBWriter_create_r GEOSWKBWriter_create_r;
   170    CR_GEOS_WKBWriter_destroy_r GEOSWKBWriter_destroy_r;
   171    CR_GEOS_WKBWriter_setByteOrder_r GEOSWKBWriter_setByteOrder_r;
   172    CR_GEOS_WKBWriter_setIncludeSRID_r GEOSWKBWriter_setIncludeSRID_r;
   173    CR_GEOS_WKBWriter_write_r GEOSWKBWriter_write_r;
   174  
   175    CR_GEOS_ClipByRect_r GEOSClipByRect_r;
   176  
   177    CR_GEOS(dlhandle h) : dlHandle(h) {}
   178  
   179    ~CR_GEOS() {
   180      if (dlHandle != NULL) {
   181        dlclose(dlHandle);
   182      }
   183    }
   184  
   185    char* Init() {
   186  #define INIT(x)                                                                                    \
   187    do {                                                                                             \
   188      auto error = InitSym(&x, #x);                                                                  \
   189      if (error != nullptr) {                                                                        \
   190        return error;                                                                                \
   191      }                                                                                              \
   192    } while (0)
   193  
   194      INIT(GEOS_init_r);
   195      INIT(GEOS_finish_r);
   196      INIT(GEOSFree_r);
   197      INIT(GEOSContext_setErrorMessageHandler_r);
   198      INIT(GEOSGeom_destroy_r);
   199      INIT(GEOSBufferParams_create_r);
   200      INIT(GEOSBufferParams_destroy_r);
   201      INIT(GEOSBufferParams_setEndCapStyle_r);
   202      INIT(GEOSBufferParams_setJoinStyle_r);
   203      INIT(GEOSBufferParams_setMitreLimit_r);
   204      INIT(GEOSBufferParams_setQuadrantSegments_r);
   205      INIT(GEOSBufferParams_setSingleSided_r);
   206      INIT(GEOSBufferWithParams_r);
   207      INIT(GEOSSetSRID_r);
   208      INIT(GEOSGetSRID_r);
   209      INIT(GEOSArea_r);
   210      INIT(GEOSLength_r);
   211      INIT(GEOSGetCentroid_r);
   212      INIT(GEOSInterpolate_r);
   213      INIT(GEOSDistance_r);
   214      INIT(GEOSCovers_r);
   215      INIT(GEOSCoveredBy_r);
   216      INIT(GEOSContains_r);
   217      INIT(GEOSCrosses_r);
   218      INIT(GEOSEquals_r);
   219      INIT(GEOSIntersects_r);
   220      INIT(GEOSOverlaps_r);
   221      INIT(GEOSTouches_r);
   222      INIT(GEOSWithin_r);
   223      INIT(GEOSRelate_r);
   224      INIT(GEOSRelatePattern_r);
   225      INIT(GEOSWKTReader_create_r);
   226      INIT(GEOSWKTReader_destroy_r);
   227      INIT(GEOSWKTReader_read_r);
   228      INIT(GEOSWKBReader_create_r);
   229      INIT(GEOSWKBReader_destroy_r);
   230      INIT(GEOSWKBReader_read_r);
   231      INIT(GEOSWKBWriter_create_r);
   232      INIT(GEOSWKBWriter_destroy_r);
   233      INIT(GEOSWKBWriter_setByteOrder_r);
   234      INIT(GEOSWKBWriter_setIncludeSRID_r);
   235      INIT(GEOSWKBWriter_write_r);
   236      INIT(GEOSClipByRect_r);
   237      return nullptr;
   238  
   239  #undef INIT
   240    }
   241  
   242    template <typename T> char* InitSym(T* ptr, const char* symbol) {
   243      *ptr = reinterpret_cast<T>(dlsym(dlHandle, symbol));
   244      if (ptr == nullptr) {
   245        return dlerror();
   246      }
   247      return nullptr;
   248    }
   249  };
   250  
   251  CR_GEOS_Slice cStringToSlice(char* cstr) {
   252    CR_GEOS_Slice slice = {.data = cstr, .len = strlen(cstr)};
   253    return slice;
   254  }
   255  
   256  // Given data that will be deallocated on return, with length
   257  // equal to len, returns a CR_GEOS_String to return to Go.
   258  CR_GEOS_String toGEOSString(const char* data, size_t len) {
   259    CR_GEOS_String result = {.data = nullptr, .len = len};
   260    if (len == 0) {
   261      return result;
   262    }
   263    result.data = static_cast<char*>(malloc(len));
   264    memcpy(result.data, data, len);
   265    return result;
   266  }
   267  
   268  CR_GEOS_Slice CR_GEOS_Init(CR_GEOS_Slice loc, CR_GEOS** lib) {
   269    auto locStr = ToString(loc);
   270    dlhandle dlHandle = dlopen(locStr.c_str(), RTLD_LAZY);
   271    if (!dlHandle) {
   272      return cStringToSlice((char*)dlopenFailError);
   273    }
   274  
   275    std::unique_ptr<CR_GEOS> ret(new CR_GEOS(dlHandle));
   276    auto error = ret->Init();
   277    if (error != nullptr) {
   278      return cStringToSlice(error);
   279    }
   280  
   281    *lib = ret.release();
   282    return CR_GEOS_Slice{.data = NULL, .len = 0};
   283  }
   284  
   285  void errorHandler(const char* msg, void* buffer) {
   286    std::string* str = static_cast<std::string*>(buffer);
   287    *str = std::string("geos error: ") + msg;
   288  }
   289  
   290  CR_GEOS_Handle initHandleWithErrorBuffer(CR_GEOS* lib, std::string* buffer) {
   291    auto handle = lib->GEOS_init_r();
   292    lib->GEOSContext_setErrorMessageHandler_r(handle, errorHandler, buffer);
   293    return handle;
   294  }
   295  
   296  CR_GEOS_Geometry CR_GEOS_GeometryFromSlice(CR_GEOS* lib, CR_GEOS_Handle handle,
   297                                             CR_GEOS_Slice slice) {
   298    auto wkbReader = lib->GEOSWKBReader_create_r(handle);
   299    auto geom = lib->GEOSWKBReader_read_r(handle, wkbReader, slice.data, slice.len);
   300    lib->GEOSWKBReader_destroy_r(handle, wkbReader);
   301    return geom;
   302  }
   303  
   304  void CR_GEOS_writeGeomToEWKB(CR_GEOS* lib, CR_GEOS_Handle handle, CR_GEOS_Geometry geom,
   305                               CR_GEOS_String* ewkb, int srid) {
   306    auto wkbWriter = lib->GEOSWKBWriter_create_r(handle);
   307    lib->GEOSWKBWriter_setByteOrder_r(handle, wkbWriter, 1);
   308    if (srid != 0) {
   309      lib->GEOSSetSRID_r(handle, geom, srid);
   310    }
   311    lib->GEOSWKBWriter_setIncludeSRID_r(handle, wkbWriter, true);
   312    ewkb->data = lib->GEOSWKBWriter_write_r(handle, wkbWriter, geom, &ewkb->len);
   313    lib->GEOSWKBWriter_destroy_r(handle, wkbWriter);
   314  }
   315  
   316  CR_GEOS_Status CR_GEOS_WKTToEWKB(CR_GEOS* lib, CR_GEOS_Slice wkt, int srid, CR_GEOS_String* ewkb) {
   317    std::string error;
   318    auto handle = initHandleWithErrorBuffer(lib, &error);
   319  
   320    auto wktReader = lib->GEOSWKTReader_create_r(handle);
   321    auto wktStr = ToString(wkt);
   322    auto geom = lib->GEOSWKTReader_read_r(handle, wktReader, wktStr.c_str());
   323    lib->GEOSWKTReader_destroy_r(handle, wktReader);
   324  
   325    if (geom != NULL) {
   326      CR_GEOS_writeGeomToEWKB(lib, handle, geom, ewkb, srid);
   327      lib->GEOSGeom_destroy_r(handle, geom);
   328    }
   329  
   330    lib->GEOS_finish_r(handle);
   331    return toGEOSString(error.data(), error.length());
   332  }
   333  
   334  CR_GEOS_Status CR_GEOS_ClipEWKBByRect(CR_GEOS* lib, CR_GEOS_Slice ewkb, double xmin, double ymin,
   335                                        double xmax, double ymax, CR_GEOS_String* clippedEWKB) {
   336    std::string error;
   337    auto handle = initHandleWithErrorBuffer(lib, &error);
   338    *clippedEWKB = {.data = NULL, .len = 0};
   339  
   340    auto geom = CR_GEOS_GeometryFromSlice(lib, handle, ewkb);
   341    if (geom != nullptr) {
   342      auto clippedGeom = lib->GEOSClipByRect_r(handle, geom, xmin, ymin, xmax, ymax);
   343      if (clippedGeom != nullptr) {
   344        auto srid = lib->GEOSGetSRID_r(handle, geom);
   345        CR_GEOS_writeGeomToEWKB(lib, handle, clippedGeom, clippedEWKB, srid);
   346        lib->GEOSGeom_destroy_r(handle, clippedGeom);
   347      }
   348      lib->GEOSGeom_destroy_r(handle, geom);
   349    }
   350  
   351    lib->GEOS_finish_r(handle);
   352    return toGEOSString(error.data(), error.length());
   353  }
   354  
   355  CR_GEOS_Status CR_GEOS_Buffer(CR_GEOS* lib, CR_GEOS_Slice ewkb, CR_GEOS_BufferParamsInput params,
   356                                double distance, CR_GEOS_String* ret) {
   357    std::string error;
   358    auto handle = initHandleWithErrorBuffer(lib, &error);
   359    *ret = {.data = NULL, .len = 0};
   360  
   361    auto geom = CR_GEOS_GeometryFromSlice(lib, handle, ewkb);
   362    if (geom != nullptr) {
   363      auto gParams = lib->GEOSBufferParams_create_r(handle);
   364      if (gParams != nullptr) {
   365        if (lib->GEOSBufferParams_setEndCapStyle_r(handle, gParams, params.endCapStyle) &&
   366            lib->GEOSBufferParams_setJoinStyle_r(handle, gParams, params.joinStyle) &&
   367            lib->GEOSBufferParams_setMitreLimit_r(handle, gParams, params.mitreLimit) &&
   368            lib->GEOSBufferParams_setQuadrantSegments_r(handle, gParams, params.quadrantSegments) &&
   369            lib->GEOSBufferParams_setSingleSided_r(handle, gParams, params.singleSided)) {
   370          auto bufferedGeom = lib->GEOSBufferWithParams_r(handle, geom, gParams, distance);
   371          if (bufferedGeom != nullptr) {
   372            auto srid = lib->GEOSGetSRID_r(handle, geom);
   373            CR_GEOS_writeGeomToEWKB(lib, handle, bufferedGeom, ret, srid);
   374            lib->GEOSGeom_destroy_r(handle, bufferedGeom);
   375          }
   376        }
   377        lib->GEOSBufferParams_destroy_r(handle, gParams);
   378      }
   379      lib->GEOSGeom_destroy_r(handle, geom);
   380    }
   381  
   382    lib->GEOS_finish_r(handle);
   383    return toGEOSString(error.data(), error.length());
   384  }
   385  
   386  //
   387  // Unary operators
   388  //
   389  
   390  template <typename T, typename R>
   391  CR_GEOS_Status CR_GEOS_UnaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, R* ret) {
   392    std::string error;
   393    auto handle = initHandleWithErrorBuffer(lib, &error);
   394    auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a);
   395    if (geom != nullptr) {
   396      auto r = fn(handle, geom, ret);
   397      // ret == 0 indicates an exception.
   398      if (r == 0) {
   399        if (error.length() == 0) {
   400          error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE);
   401        }
   402      }
   403      lib->GEOSGeom_destroy_r(handle, geom);
   404    }
   405    lib->GEOS_finish_r(handle);
   406    return toGEOSString(error.data(), error.length());
   407  }
   408  
   409  template <typename T, typename R>
   410  CR_GEOS_Status CR_GEOS_BinaryOperator(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GEOS_Slice b,
   411                                        R* ret) {
   412    std::string error;
   413    auto handle = initHandleWithErrorBuffer(lib, &error);
   414    auto wkbReader = lib->GEOSWKBReader_create_r(handle);
   415    auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len);
   416    auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len);
   417    lib->GEOSWKBReader_destroy_r(handle, wkbReader);
   418    if (geomA != nullptr && geomB != nullptr) {
   419      auto r = fn(handle, geomA, geomB, ret);
   420      // ret == 0 indicates an exception.
   421      if (r == 0) {
   422        if (error.length() == 0) {
   423          error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE);
   424        }
   425      }
   426    }
   427    if (geomA != nullptr) {
   428      lib->GEOSGeom_destroy_r(handle, geomA);
   429    }
   430    if (geomB != nullptr) {
   431      lib->GEOSGeom_destroy_r(handle, geomB);
   432    }
   433    lib->GEOS_finish_r(handle);
   434    return toGEOSString(error.data(), error.length());
   435  }
   436  
   437  CR_GEOS_Status CR_GEOS_Area(CR_GEOS* lib, CR_GEOS_Slice a, double* ret) {
   438    return CR_GEOS_UnaryOperator(lib, lib->GEOSArea_r, a, ret);
   439  }
   440  
   441  CR_GEOS_Status CR_GEOS_Length(CR_GEOS* lib, CR_GEOS_Slice a, double* ret) {
   442    return CR_GEOS_UnaryOperator(lib, lib->GEOSLength_r, a, ret);
   443  }
   444  
   445  CR_GEOS_Status CR_GEOS_Centroid(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_String* centroidEWKB) {
   446    std::string error;
   447    auto handle = initHandleWithErrorBuffer(lib, &error);
   448    auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a);
   449    *centroidEWKB = {.data = NULL, .len = 0};
   450    if (geom != nullptr) {
   451      auto centroidGeom = lib->GEOSGetCentroid_r(handle, geom);
   452      if (centroidGeom != nullptr) {
   453        auto srid = lib->GEOSGetSRID_r(handle, geom);
   454        CR_GEOS_writeGeomToEWKB(lib, handle, centroidGeom, centroidEWKB, srid);
   455        lib->GEOSGeom_destroy_r(handle, centroidGeom);
   456      }
   457      lib->GEOSGeom_destroy_r(handle, geom);
   458    }
   459    lib->GEOS_finish_r(handle);
   460    return toGEOSString(error.data(), error.length());
   461  }
   462  
   463  //
   464  // Linear Reference
   465  //
   466  
   467  CR_GEOS_Status CR_GEOS_Interpolate(CR_GEOS* lib, CR_GEOS_Slice a, double distance,
   468                                     CR_GEOS_String* interpolatedPointEWKB) {
   469     std::string error;
   470     auto handle = initHandleWithErrorBuffer(lib, &error);
   471     auto geom = CR_GEOS_GeometryFromSlice(lib, handle, a);
   472     *interpolatedPointEWKB = {.data = NULL, .len = 0};
   473     if (geom != nullptr) {
   474       auto interpolatedPoint = lib->GEOSInterpolate_r(handle, geom, distance);
   475       if (interpolatedPoint != nullptr) {
   476         auto srid = lib->GEOSGetSRID_r(handle, geom);
   477         CR_GEOS_writeGeomToEWKB(lib, handle, interpolatedPoint, interpolatedPointEWKB, srid);
   478         lib->GEOSGeom_destroy_r(handle, interpolatedPoint);
   479       }
   480       lib->GEOSGeom_destroy_r(handle, geom);
   481     }
   482     lib->GEOS_finish_r(handle);
   483     return toGEOSString(error.data(), error.length());
   484  }
   485  
   486  //
   487  // Binary operators
   488  //
   489  
   490  CR_GEOS_Status CR_GEOS_Distance(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, double *ret) {
   491    return CR_GEOS_BinaryOperator(lib, lib->GEOSDistance_r, a, b, ret);
   492  }
   493  
   494  //
   495  // Binary predicates
   496  //
   497  
   498  template <typename T>
   499  CR_GEOS_Status CR_GEOS_BinaryPredicate(CR_GEOS* lib, T fn, CR_GEOS_Slice a, CR_GEOS_Slice b,
   500                                         char* ret) {
   501    std::string error;
   502    auto handle = initHandleWithErrorBuffer(lib, &error);
   503  
   504    auto wkbReader = lib->GEOSWKBReader_create_r(handle);
   505    auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len);
   506    auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len);
   507    lib->GEOSWKBReader_destroy_r(handle, wkbReader);
   508  
   509    if (geomA != nullptr && geomB != nullptr) {
   510      auto r = fn(handle, geomA, geomB);
   511      // ret == 2 indicates an exception.
   512      if (r == 2) {
   513        if (error.length() == 0) {
   514          error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE);
   515        }
   516      } else {
   517        *ret = r;
   518      }
   519    }
   520    if (geomA != nullptr) {
   521      lib->GEOSGeom_destroy_r(handle, geomA);
   522    }
   523    if (geomB != nullptr) {
   524      lib->GEOSGeom_destroy_r(handle, geomB);
   525    }
   526    lib->GEOS_finish_r(handle);
   527    return toGEOSString(error.data(), error.length());
   528  }
   529  
   530  CR_GEOS_Status CR_GEOS_Covers(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   531    return CR_GEOS_BinaryPredicate(lib, lib->GEOSCovers_r, a, b, ret);
   532  }
   533  
   534  CR_GEOS_Status CR_GEOS_CoveredBy(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   535    return CR_GEOS_BinaryPredicate(lib, lib->GEOSCoveredBy_r, a, b, ret);
   536  }
   537  
   538  CR_GEOS_Status CR_GEOS_Contains(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   539    return CR_GEOS_BinaryPredicate(lib, lib->GEOSContains_r, a, b, ret);
   540  }
   541  
   542  CR_GEOS_Status CR_GEOS_Crosses(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   543    return CR_GEOS_BinaryPredicate(lib, lib->GEOSCrosses_r, a, b, ret);
   544  }
   545  
   546  CR_GEOS_Status CR_GEOS_Equals(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   547    return CR_GEOS_BinaryPredicate(lib, lib->GEOSEquals_r, a, b, ret);
   548  }
   549  
   550  CR_GEOS_Status CR_GEOS_Intersects(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   551    return CR_GEOS_BinaryPredicate(lib, lib->GEOSIntersects_r, a, b, ret);
   552  }
   553  
   554  CR_GEOS_Status CR_GEOS_Overlaps(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   555    return CR_GEOS_BinaryPredicate(lib, lib->GEOSOverlaps_r, a, b, ret);
   556  }
   557  
   558  CR_GEOS_Status CR_GEOS_Touches(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   559    return CR_GEOS_BinaryPredicate(lib, lib->GEOSTouches_r, a, b, ret);
   560  }
   561  
   562  CR_GEOS_Status CR_GEOS_Within(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, char* ret) {
   563    return CR_GEOS_BinaryPredicate(lib, lib->GEOSWithin_r, a, b, ret);
   564  }
   565  
   566  //
   567  // DE-9IM related
   568  // See: https://en.wikipedia.org/wiki/DE-9IM.
   569  //
   570  
   571  CR_GEOS_Status CR_GEOS_Relate(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b, CR_GEOS_String* ret) {
   572    std::string error;
   573    auto handle = initHandleWithErrorBuffer(lib, &error);
   574  
   575    auto wkbReader = lib->GEOSWKBReader_create_r(handle);
   576    auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len);
   577    auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len);
   578    lib->GEOSWKBReader_destroy_r(handle, wkbReader);
   579  
   580    if (geomA != nullptr && geomB != nullptr) {
   581      auto r = lib->GEOSRelate_r(handle, geomA, geomB);
   582      if (r != NULL) {
   583        *ret = toGEOSString(r, strlen(r));
   584        lib->GEOSFree_r(handle, r);
   585      }
   586    }
   587    if (geomA != nullptr) {
   588      lib->GEOSGeom_destroy_r(handle, geomA);
   589    }
   590    if (geomB != nullptr) {
   591      lib->GEOSGeom_destroy_r(handle, geomB);
   592    }
   593    lib->GEOS_finish_r(handle);
   594    return toGEOSString(error.data(), error.length());
   595  }
   596  
   597  CR_GEOS_Status CR_GEOS_RelatePattern(CR_GEOS* lib, CR_GEOS_Slice a, CR_GEOS_Slice b,
   598                                       CR_GEOS_Slice pattern, char* ret) {
   599    std::string error;
   600    auto handle = initHandleWithErrorBuffer(lib, &error);
   601  
   602    auto wkbReader = lib->GEOSWKBReader_create_r(handle);
   603    auto geomA = lib->GEOSWKBReader_read_r(handle, wkbReader, a.data, a.len);
   604    auto geomB = lib->GEOSWKBReader_read_r(handle, wkbReader, b.data, b.len);
   605    auto p = std::string(pattern.data, pattern.len);
   606    lib->GEOSWKBReader_destroy_r(handle, wkbReader);
   607  
   608    if (geomA != nullptr && geomB != nullptr) {
   609      auto r = lib->GEOSRelatePattern_r(handle, geomA, geomB, p.c_str());
   610      // ret == 2 indicates an exception.
   611      if (r == 2) {
   612        if (error.length() == 0) {
   613          error.assign(CR_GEOS_NO_ERROR_DEFINED_MESSAGE);
   614        }
   615      } else {
   616        *ret = r;
   617      }
   618    }
   619    if (geomA != nullptr) {
   620      lib->GEOSGeom_destroy_r(handle, geomA);
   621    }
   622    if (geomB != nullptr) {
   623      lib->GEOSGeom_destroy_r(handle, geomB);
   624    }
   625    lib->GEOS_finish_r(handle);
   626    return toGEOSString(error.data(), error.length());
   627  }