26 #define SUPPRESS_NULL_LOGGER_DEPRECATION_WARNINGS
34 #define GEOS_USE_ONLY_R_API
37 using namespace Geospatial;
39 using WKB = std::vector<uint8_t>;
41 #define MAX_GEOS_MESSAGE_LEN 200
43 static std::mutex geos_log_info_mutex;
44 static std::mutex geos_log_error_mutex;
48 void operator()(uint8_t* p) { free(p); }
50 using WkbUniquePtr = std::unique_ptr<uint8_t, FreeDeleter>;
54 static void geos_notice_handler(
const char* fmt, ...) {
55 char buffer[MAX_GEOS_MESSAGE_LEN];
58 vsnprintf(buffer, MAX_GEOS_MESSAGE_LEN, fmt, args);
61 std::lock_guard<std::mutex> guard(geos_log_info_mutex);
62 LOG(
INFO) <<
"GEOS Notice: " << std::string(buffer);
67 static void geos_error_handler(
const char* fmt, ...) {
69 char buffer[MAX_GEOS_MESSAGE_LEN];
71 vsnprintf(buffer, MAX_GEOS_MESSAGE_LEN, fmt, args);
74 std::lock_guard<std::mutex> guard(geos_log_error_mutex);
75 LOG(
ERROR) <<
"GEOS Error: " << std::string(buffer);
79 GEOSContextHandle_t create_context() {
80 #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 5
81 GEOSContextHandle_t context = initGEOS_r(geos_notice_handler, geos_error_handler);
85 GEOSContextHandle_t context = GEOS_init_r();
87 GEOSContext_setNoticeHandler_r(context, geos_notice_handler);
88 GEOSContext_setErrorHandler_r(context, geos_error_handler);
93 void destroy_context(GEOSContextHandle_t context) {
95 #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 5
96 finishGEOS_r(context);
98 GEOS_finish_r(context);
113 int32_t* best_planar_srid_ptr) {
116 auto execute_transform = (srid_in > 0 && srid_out > 0 && srid_in != srid_out);
117 if (static_cast<SQLTypes>(type) ==
kPOINT) {
119 if (execute_transform && !point.transform(srid_in, srid_out)) {
122 if (best_planar_srid_ptr) {
125 *best_planar_srid_ptr = point.getBestPlanarSRID();
126 if (!point.transform(4326, *best_planar_srid_ptr)) {
130 return point.getWkb(wkb);
134 if (execute_transform && !multipoint.transform(srid_in, srid_out)) {
137 if (best_planar_srid_ptr) {
140 *best_planar_srid_ptr = multipoint.getBestPlanarSRID();
141 if (!multipoint.transform(4326, *best_planar_srid_ptr)) {
145 return multipoint.getWkb(wkb);
149 if (execute_transform && !linestring.transform(srid_in, srid_out)) {
152 if (best_planar_srid_ptr) {
155 *best_planar_srid_ptr = linestring.getBestPlanarSRID();
156 if (!linestring.transform(4326, *best_planar_srid_ptr)) {
160 return linestring.getWkb(wkb);
162 std::vector<int32_t> meta1v(meta1, meta1 + meta1_size);
165 if (execute_transform && !multilinestring.transform(srid_in, srid_out)) {
168 if (best_planar_srid_ptr) {
171 *best_planar_srid_ptr = multilinestring.getBestPlanarSRID();
172 if (!multilinestring.transform(4326, *best_planar_srid_ptr)) {
176 return multilinestring.getWkb(wkb);
178 if (static_cast<SQLTypes>(type) ==
kPOLYGON) {
180 if (execute_transform && !poly.transform(srid_in, srid_out)) {
183 if (best_planar_srid_ptr) {
186 *best_planar_srid_ptr = poly.getBestPlanarSRID();
187 if (!poly.transform(4326, *best_planar_srid_ptr)) {
191 return poly.getWkb(wkb);
193 std::vector<int32_t> meta2v(meta2, meta2 + meta2_size);
198 if (meta1_size == 1 && meta2_size == 1) {
199 const std::vector<double> ecv = {0.0, 0.0, 0.00000012345, 0.0, 0.0, 0.00000012345};
202 return empty.getWkb(wkb);
206 if (execute_transform && !mpoly.transform(srid_in, srid_out)) {
209 if (best_planar_srid_ptr) {
212 *best_planar_srid_ptr = mpoly.getBestPlanarSRID();
213 if (!mpoly.transform(4326, *best_planar_srid_ptr)) {
217 return mpoly.getWkb(wkb);
224 bool fromWkb(
WkbView const wkb_view,
226 int8_t** result_coords,
227 int64_t* result_coords_size,
228 int32_t** result_meta1,
229 int64_t* result_meta1_size,
230 int32_t** result_meta2,
231 int64_t* result_meta2_size,
232 int32_t result_srid_in,
233 int32_t result_srid_out,
234 int32_t* best_planar_srid_ptr) {
237 if (best_planar_srid_ptr) {
240 if (!
result->transform(*best_planar_srid_ptr, 4326)) {
244 if (result_srid_in > 0 && result_srid_out > 0 and result_srid_in != result_srid_out) {
245 if (!
result->transform(result_srid_in, result_srid_out)) {
252 std::vector<double> coords{};
253 std::vector<int32_t> ring_sizes{};
254 std::vector<int32_t> poly_rings{};
255 std::vector<double> bounds{};
262 coords = {0.0, 0.0, 0.00000012345, 0.0, 0.0, 0.00000012345};
263 ring_sizes.push_back(3);
264 poly_rings.push_back(1);
265 }
else if (
auto result_point = dynamic_cast<GeoPoint*>(
result.get())) {
266 result_point->getColumns(coords);
268 coords.push_back(coords[0] + 0.0000001);
269 coords.push_back(coords[1]);
270 coords.push_back(coords[0]);
271 coords.push_back(coords[1] + 0.0000001);
272 ring_sizes.push_back(3);
273 poly_rings.push_back(ring_sizes.size());
274 }
else if (
auto result_poly = dynamic_cast<GeoPolygon*>(
result.get())) {
275 result_poly->getColumns(coords, ring_sizes, bounds);
277 poly_rings.push_back(ring_sizes.size());
278 }
else if (
auto result_mpoly = dynamic_cast<GeoMultiPolygon*>(
result.get())) {
279 result_mpoly->getColumns(coords, ring_sizes, poly_rings, bounds);
289 *result_coords =
nullptr;
290 int64_t size = coords.size() *
sizeof(double);
292 auto buf = malloc(size);
295 std::memcpy(buf, coords.data(), size);
296 *result_coords =
reinterpret_cast<int8_t*
>(buf);
298 *result_coords_size = size;
300 *result_meta1 =
nullptr;
301 size = ring_sizes.size() *
sizeof(int32_t);
303 auto buf = malloc(size);
306 std::memcpy(buf, ring_sizes.data(), size);
307 *result_meta1 =
reinterpret_cast<int32_t*
>(buf);
309 *result_meta1_size = ring_sizes.size();
311 *result_meta2 =
nullptr;
312 size = poly_rings.size() *
sizeof(int32_t);
314 auto buf = malloc(size);
317 std::memcpy(buf, poly_rings.data(), size);
318 *result_meta2 =
reinterpret_cast<int32_t*
>(buf);
320 *result_meta2_size = poly_rings.size();
325 GEOSGeometry* postprocess(GEOSContextHandle_t context, GEOSGeometry* g) {
326 if (g && GEOSisEmpty_r(context, g) == 0) {
327 auto type = GEOSGeomTypeId_r(context, g);
329 if (type != GEOS_POINT && type != GEOS_POLYGON && type != GEOS_MULTIPOLYGON) {
331 double tiny_distance = 0.000000001;
332 auto ng = GEOSBuffer_r(context, g, tiny_distance, quadsegs);
333 GEOSGeom_destroy_r(context, g);
346 int64_t arg1_coords_size,
348 int64_t arg1_meta1_size,
350 int64_t arg1_meta2_size,
353 int32_t arg1_srid_in,
354 int32_t arg1_srid_out,
357 int64_t arg2_coords_size,
359 int64_t arg2_meta1_size,
361 int64_t arg2_meta2_size,
364 int32_t arg2_srid_in,
365 int32_t arg2_srid_out,
367 int32_t* result_type,
368 int8_t** result_coords,
369 int64_t* result_coords_size,
370 int32_t** result_meta1,
371 int64_t* result_meta1_size,
372 int32_t** result_meta2,
373 int64_t* result_meta2_size,
375 int32_t result_srid_out) {
382 int32_t best_planar_srid;
383 int32_t* best_planar_srid_ptr =
nullptr;
384 if (arg1_srid_out == 4326 &&
403 best_planar_srid_ptr)) {
418 best_planar_srid_ptr)) {
422 auto context = create_context();
426 auto* g1 = GEOSGeomFromWKB_buf_r(context, wkb1.data(), wkb1.size());
428 auto* g2 = GEOSGeomFromWKB_buf_r(context, wkb2.data(), wkb2.size());
430 GEOSGeometry* g =
nullptr;
432 g = GEOSIntersection_r(context, g1, g2);
434 g = GEOSDifference_r(context, g1, g2);
436 g = GEOSUnion_r(context, g1, g2);
438 g = postprocess(context, g);
440 size_t wkb_size = 0ULL;
441 WkbUniquePtr wkb_unique_ptr(GEOSGeomToWKB_buf_r(context, g, &wkb_size));
442 WkbView wkb_view{wkb_unique_ptr.get(), wkb_size};
444 status = fromWkb(wkb_view,
454 best_planar_srid_ptr);
456 GEOSGeom_destroy_r(context, g);
458 GEOSGeom_destroy_r(context, g2);
460 GEOSGeom_destroy_r(context, g1);
462 destroy_context(context);
473 int64_t arg1_coords_size,
475 int64_t arg1_meta1_size,
477 int64_t arg1_meta2_size,
480 int32_t arg1_srid_in,
481 int32_t arg1_srid_out,
484 int64_t arg2_coords_size,
486 int64_t arg2_meta1_size,
488 int64_t arg2_meta2_size,
491 int32_t arg2_srid_in,
492 int32_t arg2_srid_out,
526 auto context = create_context();
530 auto* g1 = GEOSGeomFromWKB_buf_r(context, wkb1.data(), wkb1.size());
532 auto* g2 = GEOSGeomFromWKB_buf_r(context, wkb2.data(), wkb2.size());
535 if (arg1_ic != arg2_ic &&
539 *result = GEOSEquals_r(context, g1, g2);
543 GEOSGeom_destroy_r(context, g2);
545 GEOSGeom_destroy_r(context, g1);
547 destroy_context(context);
558 int64_t arg1_coords_size,
560 int64_t arg1_meta1_size,
562 int64_t arg1_meta2_size,
565 int32_t arg1_srid_in,
566 int32_t arg1_srid_out,
570 int8_t** result_coords,
571 int64_t* result_coords_size,
572 int32_t** result_meta1,
573 int64_t* result_meta1_size,
574 int32_t** result_meta2,
575 int64_t* result_meta2_size,
577 int32_t result_srid_out) {
579 int32_t best_planar_srid;
580 int32_t* best_planar_srid_ptr =
nullptr;
581 if (arg1_srid_out == 4326 &&
585 best_planar_srid_ptr = &best_planar_srid;
599 best_planar_srid_ptr)) {
604 auto context = create_context();
608 auto* g1 = GEOSGeomFromWKB_buf_r(context, wkb1.data(), wkb1.size());
610 GEOSGeometry* g =
nullptr;
614 g = GEOSBuffer_r(context, g1, arg2, quadsegs);
616 g = GEOSGeom_clone_r(context, g1);
619 #if (GEOS_VERSION_MAJOR > 3) || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11)
620 constexpr
bool allow_holes =
false;
621 g = GEOSConcaveHull_r(context, g1, arg2, allow_holes);
624 g = GEOSConvexHull_r(context, g1);
626 g = postprocess(context, g);
628 size_t wkb_size = 0ULL;
629 WkbUniquePtr wkb_unique_ptr(GEOSGeomToWKB_buf_r(context, g, &wkb_size));
630 WkbView wkb_view{wkb_unique_ptr.get(), wkb_size};
633 status = fromWkb(wkb_view,
643 best_planar_srid_ptr);
645 GEOSGeom_destroy_r(context, g);
647 GEOSGeom_destroy_r(context, g1);
649 destroy_context(context);
660 int64_t arg_coords_size,
662 int64_t arg_meta1_size,
664 int64_t arg_meta2_size,
668 int32_t arg_srid_out,
672 if (!result || !toWkb(wkb1,
688 auto context = create_context();
692 auto* g1 = GEOSGeomFromWKB_buf_r(context, wkb1.data(), wkb1.size());
695 *result = GEOSisEmpty_r(context, g1);
698 *result = GEOSisValid_r(context, g1);
701 GEOSGeom_destroy_r(context, g1);
703 destroy_context(context);
RUNTIME_EXPORT bool Geos_Wkb_double(int op, int arg1_type, int8_t *arg1_coords, int64_t arg1_coords_size, int32_t *arg1_meta1, int64_t arg1_meta1_size, int32_t *arg1_meta2, int64_t arg1_meta2_size, int32_t arg1_ic, int32_t arg1_srid_in, int32_t arg1_srid_out, double arg2, int *result_type, int8_t **result_coords, int64_t *result_coords_size, int32_t **result_meta1, int64_t *result_meta1_size, int32_t **result_meta2, int64_t *result_meta2_size, int32_t result_srid_out)
#define TOLERANCE_GEOINT32
RUNTIME_EXPORT bool Geos_Wkb_Wkb(int op, int arg1_type, int8_t *arg1_coords, int64_t arg1_coords_size, int32_t *arg1_meta1, int64_t arg1_meta1_size, int32_t *arg1_meta2, int64_t arg1_meta2_size, int32_t arg1_ic, int32_t arg1_srid_in, int32_t arg1_srid_out, int arg2_type, int8_t *arg2_coords, int64_t arg2_coords_size, int32_t *arg2_meta1, int64_t arg2_meta1_size, int32_t *arg2_meta2, int64_t arg2_meta2_size, int32_t arg2_ic, int32_t arg2_srid_in, int32_t arg2_srid_out, int *result_type, int8_t **result_coords, int64_t *result_coords_size, int32_t **result_meta1, int64_t *result_meta1_size, int32_t **result_meta2, int64_t *result_meta2_size, int32_t result_srid_out)
static std::unique_ptr< GeoBase > createGeoType(const std::string &wkt_or_wkb_hex, const bool validate_with_geos_if_available)
std::shared_ptr< std::vector< double > > decompress_coords< double, int32_t >(const int32_t &ic, const int8_t *coords, const size_t coords_sz)
RUNTIME_EXPORT bool Geos_Wkb(int op, int arg_type, int8_t *arg_coords, int64_t arg_coords_size, int32_t *arg_meta1, int64_t arg_meta1_size, int32_t *arg_meta2, int64_t arg_meta2_size, int32_t arg_ic, int32_t arg_srid_in, int32_t arg_srid_out, bool *result)
RUNTIME_EXPORT bool Geos_Wkb_Wkb_Predicate(int op, int arg1_type, int8_t *arg1_coords, int64_t arg1_coords_size, int32_t *arg1_meta1, int64_t arg1_meta1_size, int32_t *arg1_meta2, int64_t arg1_meta2_size, int32_t arg1_ic, int32_t arg1_srid_in, int32_t arg1_srid_out, int arg2_type, int8_t *arg2_coords, int64_t arg2_coords_size, int32_t *arg2_meta1, int64_t arg2_meta1_size, int32_t *arg2_meta2, int64_t arg2_meta2_size, int32_t arg2_ic, int32_t arg2_srid_in, int32_t arg2_srid_out, bool *result)
#define COMPRESSION_GEOINT32