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 }