Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements a few base methods on OGRGeometry.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_geometry.h"
16 :
17 : #include <climits>
18 : #include <cstdarg>
19 : #include <cstddef>
20 : #include <cstdio>
21 : #include <cstdlib>
22 : #include <cstring>
23 : #include <limits>
24 : #include <memory>
25 : #include <optional>
26 : #include <stdexcept>
27 : #include <string>
28 :
29 : #include "cpl_conv.h"
30 : #include "cpl_error.h"
31 : #include "cpl_error_internal.h"
32 : #include "cpl_multiproc.h"
33 : #include "cpl_string.h"
34 : #include "ogr_api.h"
35 : #include "ogr_core.h"
36 : #include "ogr_geos.h"
37 : #include "ogr_sfcgal.h"
38 : #include "ogr_libs.h"
39 : #include "ogr_p.h"
40 : #include "ogr_spatialref.h"
41 : #include "ogr_srs_api.h"
42 : #include "ogr_wkb.h"
43 :
44 : #ifndef SFCGAL_MAKE_VERSION
45 : #define SFCGAL_MAKE_VERSION(major, minor, patch) \
46 : ((major) * 10000 + (minor) * 100 + (patch))
47 : #endif
48 : #ifndef SFCGAL_VERSION_NUM
49 : #define SFCGAL_VERSION_NUM \
50 : SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR, \
51 : SFCGAL_VERSION_PATCH)
52 : #endif
53 :
54 : //! @cond Doxygen_Suppress
55 : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
56 : //! @endcond
57 :
58 : #ifdef HAVE_GEOS
59 99 : static void OGRGEOSErrorHandler(const char *fmt, ...)
60 : {
61 : va_list args;
62 :
63 99 : va_start(args, fmt);
64 99 : CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
65 99 : va_end(args);
66 99 : }
67 :
68 111 : static void OGRGEOSWarningHandler(const char *fmt, ...)
69 : {
70 : va_list args;
71 :
72 111 : va_start(args, fmt);
73 111 : CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
74 111 : va_end(args);
75 111 : }
76 : #endif
77 :
78 : /************************************************************************/
79 : /* OGRWktOptions() */
80 : /************************************************************************/
81 :
82 11308 : int OGRWktOptions::getDefaultPrecision()
83 : {
84 11308 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
85 : }
86 :
87 11404 : bool OGRWktOptions::getDefaultRound()
88 : {
89 11404 : return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
90 : }
91 :
92 : /************************************************************************/
93 : /* OGRGeometry() */
94 : /************************************************************************/
95 :
96 : OGRGeometry::OGRGeometry() = default;
97 :
98 : /************************************************************************/
99 : /* OGRGeometry( const OGRGeometry& ) */
100 : /************************************************************************/
101 :
102 : /**
103 : * \brief Copy constructor.
104 : */
105 :
106 1803940 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
107 1803940 : : poSRS(other.poSRS), flags(other.flags)
108 : {
109 1803940 : if (poSRS != nullptr)
110 81294 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
111 1803940 : }
112 :
113 : /************************************************************************/
114 : /* OGRGeometry( OGRGeometry&& ) */
115 : /************************************************************************/
116 :
117 : /**
118 : * \brief Move constructor.
119 : *
120 : * @since GDAL 3.11
121 : */
122 :
123 156330 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
124 156330 : : poSRS(other.poSRS), flags(other.flags)
125 : {
126 156330 : other.poSRS = nullptr;
127 156330 : }
128 :
129 : /************************************************************************/
130 : /* ~OGRGeometry() */
131 : /************************************************************************/
132 :
133 25798500 : OGRGeometry::~OGRGeometry()
134 :
135 : {
136 12899200 : if (poSRS != nullptr)
137 3655700 : const_cast<OGRSpatialReference *>(poSRS)->Release();
138 12899200 : }
139 :
140 : /************************************************************************/
141 : /* operator=( const OGRGeometry&) */
142 : /************************************************************************/
143 :
144 : /**
145 : * \brief Assignment operator.
146 : */
147 :
148 1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
149 : {
150 1211 : if (this != &other)
151 : {
152 1211 : empty();
153 1211 : assignSpatialReference(other.getSpatialReference());
154 1211 : flags = other.flags;
155 : }
156 1211 : return *this;
157 : }
158 :
159 : /************************************************************************/
160 : /* operator=( OGRGeometry&&) */
161 : /************************************************************************/
162 :
163 : /**
164 : * \brief Move assignment operator.
165 : *
166 : * @since GDAL 3.11
167 : */
168 :
169 103938 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
170 : {
171 103938 : if (this != &other)
172 : {
173 103938 : poSRS = other.poSRS;
174 103938 : other.poSRS = nullptr;
175 103938 : flags = other.flags;
176 : }
177 103938 : return *this;
178 : }
179 :
180 : /************************************************************************/
181 : /* dumpReadable() */
182 : /************************************************************************/
183 :
184 : /**
185 : * \brief Dump geometry in well known text format to indicated output file.
186 : *
187 : * A few options can be defined to change the default dump :
188 : * <ul>
189 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
190 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
191 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
192 : * </ul>
193 : *
194 : * This method is the same as the C function OGR_G_DumpReadable().
195 : *
196 : * @param fp the text file to write the geometry to.
197 : * @param pszPrefix the prefix to put on each line of output.
198 : * @param papszOptions NULL terminated list of options (may be NULL)
199 : */
200 :
201 0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
202 : CSLConstList papszOptions) const
203 :
204 : {
205 0 : if (fp == nullptr)
206 0 : fp = stdout;
207 :
208 0 : const auto osStr = dumpReadable(pszPrefix, papszOptions);
209 0 : fprintf(fp, "%s", osStr.c_str());
210 0 : }
211 :
212 : /************************************************************************/
213 : /* dumpReadable() */
214 : /************************************************************************/
215 :
216 : /**
217 : * \brief Dump geometry in well known text format to indicated output file.
218 : *
219 : * A few options can be defined to change the default dump :
220 : * <ul>
221 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
222 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
223 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
224 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
225 : * in WKT (added in GDAL 3.9)</li>
226 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
227 : * WKT (added in GDAL 3.9)</li>
228 : * </ul>
229 : *
230 : * @param pszPrefix the prefix to put on each line of output.
231 : * @param papszOptions NULL terminated list of options (may be NULL)
232 : * @return a string with the geometry representation.
233 : * @since GDAL 3.7
234 : */
235 :
236 317 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
237 : CSLConstList papszOptions) const
238 :
239 : {
240 317 : if (pszPrefix == nullptr)
241 306 : pszPrefix = "";
242 :
243 317 : std::string osRet;
244 :
245 : const auto exportToWktWithOpts =
246 2044 : [this, pszPrefix, papszOptions, &osRet](bool bIso)
247 : {
248 292 : OGRErr err(OGRERR_NONE);
249 292 : OGRWktOptions opts;
250 292 : if (const char *pszXYPrecision =
251 292 : CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
252 : {
253 1 : opts.format = OGRWktFormat::F;
254 1 : opts.xyPrecision = atoi(pszXYPrecision);
255 : }
256 292 : if (const char *pszZPrecision =
257 292 : CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
258 : {
259 1 : opts.format = OGRWktFormat::F;
260 1 : opts.zPrecision = atoi(pszZPrecision);
261 : }
262 292 : if (bIso)
263 292 : opts.variant = wkbVariantIso;
264 584 : std::string wkt = exportToWkt(opts, &err);
265 292 : if (err == OGRERR_NONE)
266 : {
267 292 : osRet = pszPrefix;
268 292 : osRet += wkt.data();
269 292 : osRet += '\n';
270 : }
271 292 : };
272 :
273 : const char *pszDisplayGeometry =
274 317 : CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
275 317 : if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
276 : {
277 25 : osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
278 25 : switch (getGeometryType())
279 : {
280 1 : case wkbUnknown:
281 : case wkbNone:
282 : case wkbPoint:
283 : case wkbPoint25D:
284 : case wkbPointM:
285 : case wkbPointZM:
286 1 : break;
287 0 : case wkbPolyhedralSurface:
288 : case wkbTIN:
289 : case wkbPolyhedralSurfaceZ:
290 : case wkbTINZ:
291 : case wkbPolyhedralSurfaceM:
292 : case wkbTINM:
293 : case wkbPolyhedralSurfaceZM:
294 : case wkbTINZM:
295 : {
296 0 : const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
297 : osRet +=
298 0 : CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
299 0 : for (auto &&poSubGeom : *poPS)
300 : {
301 0 : osRet += pszPrefix;
302 0 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
303 : }
304 0 : break;
305 : }
306 0 : case wkbLineString:
307 : case wkbLineString25D:
308 : case wkbLineStringM:
309 : case wkbLineStringZM:
310 : case wkbCircularString:
311 : case wkbCircularStringZ:
312 : case wkbCircularStringM:
313 : case wkbCircularStringZM:
314 : {
315 0 : const OGRSimpleCurve *poSC = toSimpleCurve();
316 0 : osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
317 0 : break;
318 : }
319 11 : case wkbPolygon:
320 : case wkbTriangle:
321 : case wkbTriangleZ:
322 : case wkbTriangleM:
323 : case wkbTriangleZM:
324 : case wkbPolygon25D:
325 : case wkbPolygonM:
326 : case wkbPolygonZM:
327 : case wkbCurvePolygon:
328 : case wkbCurvePolygonZ:
329 : case wkbCurvePolygonM:
330 : case wkbCurvePolygonZM:
331 : {
332 11 : const OGRCurvePolygon *poPoly = toCurvePolygon();
333 11 : const OGRCurve *poRing = poPoly->getExteriorRingCurve();
334 11 : const int nRings = poPoly->getNumInteriorRings();
335 11 : if (poRing == nullptr)
336 : {
337 0 : osRet += "empty";
338 : }
339 : else
340 : {
341 11 : osRet += CPLOPrintf("%d points", poRing->getNumPoints());
342 11 : if (wkbFlatten(poRing->getGeometryType()) ==
343 : wkbCompoundCurve)
344 : {
345 0 : osRet += " (";
346 0 : osRet += poRing->dumpReadable(nullptr, papszOptions);
347 0 : osRet += ")";
348 : }
349 11 : if (nRings)
350 : {
351 1 : osRet += CPLOPrintf(", %d inner rings (", nRings);
352 8 : for (int ir = 0; ir < nRings; ir++)
353 : {
354 7 : poRing = poPoly->getInteriorRingCurve(ir);
355 7 : if (ir)
356 6 : osRet += ", ";
357 : osRet +=
358 7 : CPLOPrintf("%d points", poRing->getNumPoints());
359 7 : if (wkbFlatten(poRing->getGeometryType()) ==
360 : wkbCompoundCurve)
361 : {
362 2 : osRet += " (";
363 : osRet +=
364 2 : poRing->dumpReadable(nullptr, papszOptions);
365 2 : osRet += ")";
366 : }
367 : }
368 1 : osRet += ")";
369 : }
370 : }
371 11 : osRet += "\n";
372 11 : break;
373 : }
374 2 : case wkbCompoundCurve:
375 : case wkbCompoundCurveZ:
376 : case wkbCompoundCurveM:
377 : case wkbCompoundCurveZM:
378 : {
379 2 : const OGRCompoundCurve *poCC = toCompoundCurve();
380 2 : if (poCC->getNumCurves() == 0)
381 : {
382 0 : osRet += "empty";
383 : }
384 : else
385 : {
386 6 : for (int i = 0; i < poCC->getNumCurves(); i++)
387 : {
388 4 : if (i)
389 2 : osRet += ", ";
390 : osRet +=
391 8 : CPLOPrintf("%s (%d points)",
392 4 : poCC->getCurve(i)->getGeometryName(),
393 8 : poCC->getCurve(i)->getNumPoints());
394 : }
395 : }
396 2 : break;
397 : }
398 :
399 11 : case wkbMultiPoint:
400 : case wkbMultiLineString:
401 : case wkbMultiPolygon:
402 : case wkbMultiCurve:
403 : case wkbMultiSurface:
404 : case wkbGeometryCollection:
405 : case wkbMultiPoint25D:
406 : case wkbMultiLineString25D:
407 : case wkbMultiPolygon25D:
408 : case wkbMultiCurveZ:
409 : case wkbMultiSurfaceZ:
410 : case wkbGeometryCollection25D:
411 : case wkbMultiPointM:
412 : case wkbMultiLineStringM:
413 : case wkbMultiPolygonM:
414 : case wkbMultiCurveM:
415 : case wkbMultiSurfaceM:
416 : case wkbGeometryCollectionM:
417 : case wkbMultiPointZM:
418 : case wkbMultiLineStringZM:
419 : case wkbMultiPolygonZM:
420 : case wkbMultiCurveZM:
421 : case wkbMultiSurfaceZM:
422 : case wkbGeometryCollectionZM:
423 : {
424 11 : const OGRGeometryCollection *poColl = toGeometryCollection();
425 : osRet +=
426 11 : CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
427 22 : for (auto &&poSubGeom : *poColl)
428 : {
429 11 : osRet += pszPrefix;
430 11 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
431 : }
432 11 : break;
433 : }
434 0 : case wkbLinearRing:
435 : case wkbCurve:
436 : case wkbSurface:
437 : case wkbCurveZ:
438 : case wkbSurfaceZ:
439 : case wkbCurveM:
440 : case wkbSurfaceM:
441 : case wkbCurveZM:
442 : case wkbSurfaceZM:
443 0 : break;
444 25 : }
445 : }
446 292 : else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
447 : {
448 0 : exportToWktWithOpts(/* bIso=*/false);
449 : }
450 292 : else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
451 0 : EQUAL(pszDisplayGeometry, "ISO_WKT"))
452 : {
453 292 : exportToWktWithOpts(/* bIso=*/true);
454 : }
455 :
456 634 : return osRet;
457 : }
458 :
459 : /************************************************************************/
460 : /* OGR_G_DumpReadable() */
461 : /************************************************************************/
462 : /**
463 : * \brief Dump geometry in well known text format to indicated output file.
464 : *
465 : * This method is the same as the CPP method OGRGeometry::dumpReadable.
466 : *
467 : * @param hGeom handle on the geometry to dump.
468 : * @param fp the text file to write the geometry to.
469 : * @param pszPrefix the prefix to put on each line of output.
470 : */
471 :
472 0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
473 :
474 : {
475 0 : VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
476 :
477 0 : OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
478 : }
479 :
480 : /************************************************************************/
481 : /* assignSpatialReference() */
482 : /************************************************************************/
483 :
484 : /**
485 : * \brief Assign spatial reference to this object.
486 : *
487 : * Any existing spatial reference
488 : * is replaced, but under no circumstances does this result in the object
489 : * being reprojected. It is just changing the interpretation of the existing
490 : * geometry. Note that assigning a spatial reference increments the
491 : * reference count on the OGRSpatialReference, but does not copy it.
492 : *
493 : * This will also assign the spatial reference to
494 : * potential sub-geometries of the geometry (OGRGeometryCollection,
495 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
496 : * derived classes).
497 : *
498 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
499 : *
500 : * This method is the same as the C function OGR_G_AssignSpatialReference().
501 : *
502 : * @param poSR new spatial reference system to apply.
503 : */
504 :
505 5662600 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
506 :
507 : {
508 : // Do in that order to properly handle poSR == poSRS
509 5662600 : if (poSR != nullptr)
510 3620580 : const_cast<OGRSpatialReference *>(poSR)->Reference();
511 5662600 : if (poSRS != nullptr)
512 46168 : const_cast<OGRSpatialReference *>(poSRS)->Release();
513 :
514 5662600 : poSRS = poSR;
515 5662600 : }
516 :
517 : /************************************************************************/
518 : /* OGR_G_AssignSpatialReference() */
519 : /************************************************************************/
520 : /**
521 : * \brief Assign spatial reference to this object.
522 : *
523 : * Any existing spatial reference
524 : * is replaced, but under no circumstances does this result in the object
525 : * being reprojected. It is just changing the interpretation of the existing
526 : * geometry. Note that assigning a spatial reference increments the
527 : * reference count on the OGRSpatialReference, but does not copy it.
528 : *
529 : * This will also assign the spatial reference to
530 : * potential sub-geometries of the geometry (OGRGeometryCollection,
531 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
532 : * derived classes).
533 : *
534 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
535 : *
536 : * This function is the same as the CPP method
537 : * OGRGeometry::assignSpatialReference.
538 : *
539 : * @param hGeom handle on the geometry to apply the new spatial reference
540 : * system.
541 : * @param hSRS handle on the new spatial reference system to apply.
542 : */
543 :
544 80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
545 :
546 : {
547 80 : VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
548 :
549 160 : OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
550 80 : OGRSpatialReference::FromHandle(hSRS));
551 : }
552 :
553 : /************************************************************************/
554 : /* Intersects() */
555 : /************************************************************************/
556 :
557 : /**
558 : * \brief Do these features intersect?
559 : *
560 : * Determines whether two geometries intersect. If GEOS is enabled, then
561 : * this is done in rigorous fashion otherwise TRUE is returned if the
562 : * envelopes (bounding boxes) of the two geometries overlap.
563 : *
564 : * The poOtherGeom argument may be safely NULL, but in this case the method
565 : * will always return TRUE. That is, a NULL geometry is treated as being
566 : * everywhere.
567 : *
568 : * This method is the same as the C function OGR_G_Intersects().
569 : *
570 : * @param poOtherGeom the other geometry to test against.
571 : *
572 : * @return TRUE if the geometries intersect, otherwise FALSE.
573 : */
574 :
575 44 : bool OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
576 :
577 : {
578 44 : if (poOtherGeom == nullptr)
579 0 : return TRUE;
580 :
581 44 : OGREnvelope oEnv1;
582 44 : getEnvelope(&oEnv1);
583 :
584 44 : OGREnvelope oEnv2;
585 44 : poOtherGeom->getEnvelope(&oEnv2);
586 :
587 44 : if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
588 26 : oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
589 18 : return FALSE;
590 :
591 : #ifndef HAVE_GEOS
592 : // Without GEOS we assume that envelope overlap is equivalent to
593 : // actual intersection.
594 : return TRUE;
595 : #else
596 :
597 26 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
598 26 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
599 26 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
600 :
601 26 : bool bResult = false;
602 26 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
603 : {
604 26 : bResult =
605 26 : GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
606 : }
607 :
608 26 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
609 26 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
610 26 : freeGEOSContext(hGEOSCtxt);
611 :
612 26 : return bResult;
613 : #endif // HAVE_GEOS
614 : }
615 :
616 : // Old API compatibility function.
617 :
618 : //! @cond Doxygen_Suppress
619 0 : bool OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
620 :
621 : {
622 0 : return Intersects(poOtherGeom);
623 : }
624 :
625 : //! @endcond
626 :
627 : /************************************************************************/
628 : /* OGR_G_Intersects() */
629 : /************************************************************************/
630 : /**
631 : * \brief Do these features intersect?
632 : *
633 : * Determines whether two geometries intersect. If GEOS is enabled, then
634 : * this is done in rigorous fashion otherwise TRUE is returned if the
635 : * envelopes (bounding boxes) of the two geometries overlap.
636 : *
637 : * This function is the same as the CPP method OGRGeometry::Intersects.
638 : *
639 : * @param hGeom handle on the first geometry.
640 : * @param hOtherGeom handle on the other geometry to test against.
641 : *
642 : * @return TRUE if the geometries intersect, otherwise FALSE.
643 : */
644 :
645 11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
646 :
647 : {
648 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
649 11 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
650 :
651 22 : return OGRGeometry::FromHandle(hGeom)->Intersects(
652 22 : OGRGeometry::FromHandle(hOtherGeom));
653 : }
654 :
655 : //! @cond Doxygen_Suppress
656 0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
657 :
658 : {
659 0 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
660 0 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
661 :
662 0 : return OGRGeometry::FromHandle(hGeom)->Intersects(
663 0 : OGRGeometry::FromHandle(hOtherGeom));
664 : }
665 :
666 : //! @endcond
667 :
668 : /************************************************************************/
669 : /* transformTo() */
670 : /************************************************************************/
671 :
672 : /**
673 : * \brief Transform geometry to new spatial reference system.
674 : *
675 : * This method will transform the coordinates of a geometry from
676 : * their current spatial reference system to a new target spatial
677 : * reference system. Normally this means reprojecting the vectors,
678 : * but it could include datum shifts, and changes of units.
679 : *
680 : * This method will only work if the geometry already has an assigned
681 : * spatial reference system, and if it is transformable to the target
682 : * coordinate system.
683 : *
684 : * Because this method requires internal creation and initialization of an
685 : * OGRCoordinateTransformation object it is significantly more expensive to
686 : * use this method to transform many geometries than it is to create the
687 : * OGRCoordinateTransformation in advance, and call transform() with that
688 : * transformation. This method exists primarily for convenience when only
689 : * transforming a single geometry.
690 : *
691 : * This method is the same as the C function OGR_G_TransformTo().
692 : *
693 : * @param poSR spatial reference system to transform to.
694 : *
695 : * @return OGRERR_NONE on success, or an error code.
696 : */
697 :
698 33 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
699 :
700 : {
701 33 : if (getSpatialReference() == nullptr)
702 : {
703 1 : CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
704 1 : return OGRERR_FAILURE;
705 : }
706 :
707 32 : if (poSR == nullptr)
708 : {
709 0 : CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
710 0 : return OGRERR_FAILURE;
711 : }
712 :
713 : OGRCoordinateTransformation *poCT =
714 32 : OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
715 32 : if (poCT == nullptr)
716 0 : return OGRERR_FAILURE;
717 :
718 32 : const OGRErr eErr = transform(poCT);
719 :
720 32 : OGRCoordinateTransformation::DestroyCT(poCT);
721 :
722 32 : return eErr;
723 : }
724 :
725 : /************************************************************************/
726 : /* OGR_G_TransformTo() */
727 : /************************************************************************/
728 : /**
729 : * \brief Transform geometry to new spatial reference system.
730 : *
731 : * This function will transform the coordinates of a geometry from
732 : * their current spatial reference system to a new target spatial
733 : * reference system. Normally this means reprojecting the vectors,
734 : * but it could include datum shifts, and changes of units.
735 : *
736 : * This function will only work if the geometry already has an assigned
737 : * spatial reference system, and if it is transformable to the target
738 : * coordinate system.
739 : *
740 : * Because this function requires internal creation and initialization of an
741 : * OGRCoordinateTransformation object it is significantly more expensive to
742 : * use this function to transform many geometries than it is to create the
743 : * OGRCoordinateTransformation in advance, and call transform() with that
744 : * transformation. This function exists primarily for convenience when only
745 : * transforming a single geometry.
746 : *
747 : * This function is the same as the CPP method OGRGeometry::transformTo.
748 : *
749 : * @param hGeom handle on the geometry to apply the transform to.
750 : * @param hSRS handle on the spatial reference system to apply.
751 : *
752 : * @return OGRERR_NONE on success, or an error code.
753 : */
754 :
755 9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
756 :
757 : {
758 9 : VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
759 :
760 18 : return OGRGeometry::FromHandle(hGeom)->transformTo(
761 18 : OGRSpatialReference::FromHandle(hSRS));
762 : }
763 :
764 : /**
765 : * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
766 : *
767 : * \brief Apply arbitrary coordinate transformation to geometry.
768 : *
769 : * This method will transform the coordinates of a geometry from
770 : * their current spatial reference system to a new target spatial
771 : * reference system. Normally this means reprojecting the vectors,
772 : * but it could include datum shifts, and changes of units.
773 : *
774 : * Note that this method does not require that the geometry already
775 : * have a spatial reference system. It will be assumed that they can
776 : * be treated as having the source spatial reference system of the
777 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
778 : * will be ignored. On successful completion the output OGRSpatialReference
779 : * of the OGRCoordinateTransformation will be assigned to the geometry.
780 : *
781 : * This method only does reprojection on a point-by-point basis. It does not
782 : * include advanced logic to deal with discontinuities at poles or antimeridian.
783 : * For that, use the OGRGeometryFactory::transformWithOptions() method.
784 : *
785 : * This method is the same as the C function OGR_G_Transform().
786 : *
787 : * @param poCT the transformation to apply.
788 : *
789 : * @return OGRERR_NONE on success or an error code.
790 : */
791 :
792 : /************************************************************************/
793 : /* OGR_G_Transform() */
794 : /************************************************************************/
795 : /**
796 : * \brief Apply arbitrary coordinate transformation to geometry.
797 : *
798 : * This function will transform the coordinates of a geometry from
799 : * their current spatial reference system to a new target spatial
800 : * reference system. Normally this means reprojecting the vectors,
801 : * but it could include datum shifts, and changes of units.
802 : *
803 : * Note that this function does not require that the geometry already
804 : * have a spatial reference system. It will be assumed that they can
805 : * be treated as having the source spatial reference system of the
806 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
807 : * will be ignored. On successful completion the output OGRSpatialReference
808 : * of the OGRCoordinateTransformation will be assigned to the geometry.
809 : *
810 : * This function only does reprojection on a point-by-point basis. It does not
811 : * include advanced logic to deal with discontinuities at poles or antimeridian.
812 : * For that, use the OGR_GeomTransformer_Create() and
813 : * OGR_GeomTransformer_Transform() functions.
814 : *
815 : * This function is the same as the CPP method OGRGeometry::transform.
816 : *
817 : * @param hGeom handle on the geometry to apply the transform to.
818 : * @param hTransform handle on the transformation to apply.
819 : *
820 : * @return OGRERR_NONE on success or an error code.
821 : */
822 :
823 11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
824 : OGRCoordinateTransformationH hTransform)
825 :
826 : {
827 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
828 :
829 22 : return OGRGeometry::FromHandle(hGeom)->transform(
830 11 : OGRCoordinateTransformation::FromHandle(hTransform));
831 : }
832 :
833 : /**
834 : * \fn int OGRGeometry::getDimension() const;
835 : *
836 : * \brief Get the dimension of this object.
837 : *
838 : * This method corresponds to the SFCOM IGeometry::GetDimension() method.
839 : * It indicates the dimension of the object, but does not indicate the
840 : * dimension of the underlying space (as indicated by
841 : * OGRGeometry::getCoordinateDimension()).
842 : *
843 : * This method is the same as the C function OGR_G_GetDimension().
844 : *
845 : * @return 0 for points, 1 for lines and 2 for surfaces.
846 : */
847 :
848 : /**
849 : * \brief Get the geometry type that conforms with ISO SQL/MM Part3
850 : *
851 : * @return the geometry type that conforms with ISO SQL/MM Part3
852 : */
853 730587 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
854 : {
855 730587 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
856 :
857 730587 : if (flags & OGR_G_3D)
858 214265 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
859 730587 : if (flags & OGR_G_MEASURED)
860 26025 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
861 :
862 730587 : return nGType;
863 : }
864 :
865 : /************************************************************************/
866 : /* OGRGeometry::segmentize() */
867 : /************************************************************************/
868 : /**
869 : *
870 : * \brief Modify the geometry such it has no segment longer then the
871 : * given distance.
872 : *
873 : * This method modifies the geometry to add intermediate vertices if necessary
874 : * so that the maximum length between 2 consecutive vertices is lower than
875 : * dfMaxLength.
876 : *
877 : * Interpolated points will have Z and M values (if needed) set to 0.
878 : * Distance computation is performed in 2d only
879 : *
880 : * This function is the same as the C function OGR_G_Segmentize()
881 : *
882 : * @param dfMaxLength the maximum distance between 2 points after segmentization
883 : * @return (since 3.10) true in case of success, false in case of error.
884 : */
885 :
886 0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
887 : {
888 : // Do nothing.
889 0 : return true;
890 : }
891 :
892 : /************************************************************************/
893 : /* OGR_G_Segmentize() */
894 : /************************************************************************/
895 :
896 : /**
897 : *
898 : * \brief Modify the geometry such it has no segment longer then the given
899 : * distance.
900 : *
901 : * Interpolated points will have Z and M values (if needed) set to 0.
902 : * Distance computation is performed in 2d only.
903 : *
904 : * This function is the same as the CPP method OGRGeometry::segmentize().
905 : *
906 : * @param hGeom handle on the geometry to segmentize
907 : * @param dfMaxLength the maximum distance between 2 points after segmentization
908 : */
909 :
910 24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
911 : {
912 24 : VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
913 :
914 24 : if (dfMaxLength <= 0)
915 : {
916 0 : CPLError(CE_Failure, CPLE_AppDefined,
917 : "dfMaxLength must be strictly positive");
918 0 : return;
919 : }
920 24 : OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
921 : }
922 :
923 : /************************************************************************/
924 : /* OGR_G_GetDimension() */
925 : /************************************************************************/
926 : /**
927 : *
928 : * \brief Get the dimension of this geometry.
929 : *
930 : * This function corresponds to the SFCOM IGeometry::GetDimension() method.
931 : * It indicates the dimension of the geometry, but does not indicate the
932 : * dimension of the underlying space (as indicated by
933 : * OGR_G_GetCoordinateDimension() function).
934 : *
935 : * This function is the same as the CPP method OGRGeometry::getDimension().
936 : *
937 : * @param hGeom handle on the geometry to get the dimension from.
938 : * @return 0 for points, 1 for lines and 2 for surfaces.
939 : */
940 :
941 21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
942 :
943 : {
944 21 : VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
945 :
946 21 : return OGRGeometry::FromHandle(hGeom)->getDimension();
947 : }
948 :
949 : /************************************************************************/
950 : /* getCoordinateDimension() */
951 : /************************************************************************/
952 : /**
953 : * \brief Get the dimension of the coordinates in this object.
954 : *
955 : * This method is the same as the C function OGR_G_GetCoordinateDimension().
956 : *
957 : * @deprecated use CoordinateDimension().
958 : *
959 : * @return this will return 2 or 3.
960 : */
961 :
962 593649 : int OGRGeometry::getCoordinateDimension() const
963 :
964 : {
965 593649 : return (flags & OGR_G_3D) ? 3 : 2;
966 : }
967 :
968 : /************************************************************************/
969 : /* CoordinateDimension() */
970 : /************************************************************************/
971 : /**
972 : * \brief Get the dimension of the coordinates in this object.
973 : *
974 : * This method is the same as the C function OGR_G_CoordinateDimension().
975 : *
976 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
977 : *
978 : */
979 :
980 29843 : int OGRGeometry::CoordinateDimension() const
981 :
982 : {
983 29843 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
984 7375 : return 4;
985 22468 : else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
986 6861 : return 3;
987 : else
988 15607 : return 2;
989 : }
990 :
991 : /************************************************************************/
992 : /* OGR_G_GetCoordinateDimension() */
993 : /************************************************************************/
994 : /**
995 : *
996 : * \brief Get the dimension of the coordinates in this geometry.
997 : *
998 : * This function is the same as the CPP method
999 : * OGRGeometry::getCoordinateDimension().
1000 : *
1001 : * @param hGeom handle on the geometry to get the dimension of the
1002 : * coordinates from.
1003 : *
1004 : * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
1005 : * OGR_G_IsMeasured().
1006 : *
1007 : * @return this will return 2 or 3.
1008 : */
1009 :
1010 724 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
1011 :
1012 : {
1013 724 : VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
1014 :
1015 724 : return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
1016 : }
1017 :
1018 : /************************************************************************/
1019 : /* OGR_G_CoordinateDimension() */
1020 : /************************************************************************/
1021 : /**
1022 : *
1023 : * \brief Get the dimension of the coordinates in this geometry.
1024 : *
1025 : * This function is the same as the CPP method
1026 : * OGRGeometry::CoordinateDimension().
1027 : *
1028 : * @param hGeom handle on the geometry to get the dimension of the
1029 : * coordinates from.
1030 : *
1031 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
1032 : *
1033 : */
1034 :
1035 4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
1036 :
1037 : {
1038 4 : VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
1039 :
1040 4 : return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
1041 : }
1042 :
1043 : /**
1044 : *
1045 : * \brief See whether this geometry has Z coordinates.
1046 : *
1047 : * This function is the same as the CPP method
1048 : * OGRGeometry::Is3D().
1049 : *
1050 : * @param hGeom handle on the geometry to check whether it has Z coordinates.
1051 : *
1052 : * @return TRUE if the geometry has Z coordinates.
1053 : */
1054 :
1055 37749 : int OGR_G_Is3D(OGRGeometryH hGeom)
1056 :
1057 : {
1058 37749 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1059 :
1060 37749 : return OGRGeometry::FromHandle(hGeom)->Is3D();
1061 : }
1062 :
1063 : /**
1064 : *
1065 : * \brief See whether this geometry is measured.
1066 : *
1067 : * This function is the same as the CPP method
1068 : * OGRGeometry::IsMeasured().
1069 : *
1070 : * @param hGeom handle on the geometry to check whether it is measured.
1071 : *
1072 : * @return TRUE if the geometry has M coordinates.
1073 : */
1074 :
1075 40153 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1076 :
1077 : {
1078 40153 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1079 :
1080 40153 : return OGRGeometry::FromHandle(hGeom)->IsMeasured();
1081 : }
1082 :
1083 : /************************************************************************/
1084 : /* setCoordinateDimension() */
1085 : /************************************************************************/
1086 :
1087 : /**
1088 : * \brief Set the coordinate dimension.
1089 : *
1090 : * This method sets the explicit coordinate dimension. Setting the coordinate
1091 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1092 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1093 : * will affect the children geometries.
1094 : * This will also remove the M dimension if present before this call.
1095 : *
1096 : * @deprecated use set3D() or setMeasured().
1097 : *
1098 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1099 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1100 : */
1101 :
1102 68188 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1103 :
1104 : {
1105 68188 : if (nNewDimension == 2)
1106 67686 : flags &= ~OGR_G_3D;
1107 : else
1108 502 : flags |= OGR_G_3D;
1109 68188 : return setMeasured(FALSE);
1110 : }
1111 :
1112 : /**
1113 : * \brief Add or remove the Z coordinate dimension.
1114 : *
1115 : * This method adds or removes the explicit Z coordinate dimension.
1116 : * Removing the Z coordinate dimension of a geometry will remove any
1117 : * existing Z values. Adding the Z dimension to a geometry
1118 : * collection, a compound curve, a polygon, etc. will affect the
1119 : * children geometries.
1120 : *
1121 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1122 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1123 : */
1124 :
1125 1624260 : bool OGRGeometry::set3D(bool bIs3D)
1126 :
1127 : {
1128 1624260 : if (bIs3D)
1129 1614820 : flags |= OGR_G_3D;
1130 : else
1131 9440 : flags &= ~OGR_G_3D;
1132 1624260 : return true;
1133 : }
1134 :
1135 : /**
1136 : * \brief Add or remove the M coordinate dimension.
1137 : *
1138 : * This method adds or removes the explicit M coordinate dimension.
1139 : * Removing the M coordinate dimension of a geometry will remove any
1140 : * existing M values. Adding the M dimension to a geometry
1141 : * collection, a compound curve, a polygon, etc. will affect the
1142 : * children geometries.
1143 : *
1144 : * @param bIsMeasured Should the geometry have a M dimension, either
1145 : * TRUE or FALSE.
1146 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1147 : */
1148 :
1149 419113 : bool OGRGeometry::setMeasured(bool bIsMeasured)
1150 :
1151 : {
1152 419113 : if (bIsMeasured)
1153 137732 : flags |= OGR_G_MEASURED;
1154 : else
1155 281381 : flags &= ~OGR_G_MEASURED;
1156 419113 : return true;
1157 : }
1158 :
1159 : /************************************************************************/
1160 : /* OGR_G_SetCoordinateDimension() */
1161 : /************************************************************************/
1162 :
1163 : /**
1164 : * \brief Set the coordinate dimension.
1165 : *
1166 : * This method sets the explicit coordinate dimension. Setting the coordinate
1167 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1168 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1169 : * will affect the children geometries.
1170 : * This will also remove the M dimension if present before this call.
1171 : *
1172 : * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
1173 : *
1174 : * @param hGeom handle on the geometry to set the dimension of the
1175 : * coordinates.
1176 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1177 : */
1178 :
1179 56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
1180 :
1181 : {
1182 56 : VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
1183 :
1184 56 : OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
1185 : }
1186 :
1187 : /************************************************************************/
1188 : /* OGR_G_Set3D() */
1189 : /************************************************************************/
1190 :
1191 : /**
1192 : * \brief Add or remove the Z coordinate dimension.
1193 : *
1194 : * This method adds or removes the explicit Z coordinate dimension.
1195 : * Removing the Z coordinate dimension of a geometry will remove any
1196 : * existing Z values. Adding the Z dimension to a geometry
1197 : * collection, a compound curve, a polygon, etc. will affect the
1198 : * children geometries.
1199 : *
1200 : * @param hGeom handle on the geometry to set or unset the Z dimension.
1201 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1202 : */
1203 :
1204 154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
1205 :
1206 : {
1207 154 : VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
1208 :
1209 154 : OGRGeometry::FromHandle(hGeom)->set3D(CPL_TO_BOOL(bIs3D));
1210 : }
1211 :
1212 : /************************************************************************/
1213 : /* OGR_G_SetMeasured() */
1214 : /************************************************************************/
1215 :
1216 : /**
1217 : * \brief Add or remove the M coordinate dimension.
1218 : *
1219 : * This method adds or removes the explicit M coordinate dimension.
1220 : * Removing the M coordinate dimension of a geometry will remove any
1221 : * existing M values. Adding the M dimension to a geometry
1222 : * collection, a compound curve, a polygon, etc. will affect the
1223 : * children geometries.
1224 : *
1225 : * @param hGeom handle on the geometry to set or unset the M dimension.
1226 : * @param bIsMeasured Should the geometry have a M dimension, either
1227 : * TRUE or FALSE.
1228 : */
1229 :
1230 154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
1231 :
1232 : {
1233 154 : VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
1234 :
1235 154 : OGRGeometry::FromHandle(hGeom)->setMeasured(CPL_TO_BOOL(bIsMeasured));
1236 : }
1237 :
1238 : /**
1239 : * \fn bool OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
1240 : *
1241 : * \brief Returns TRUE if two geometries are equivalent.
1242 : *
1243 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1244 : *
1245 : * The comparison is done in a structural way, that is to say that the geometry
1246 : * types must be identical, as well as the number and ordering of sub-geometries
1247 : * and vertices.
1248 : * Or equivalently, two geometries are considered equal by this method if their
1249 : * WKT/WKB representation is equal.
1250 : * Note: this must be distinguished for equality in a spatial way (which is
1251 : * the purpose of the ST_Equals() operation).
1252 : *
1253 : * This method is the same as the C function OGR_G_Equals().
1254 : *
1255 : * @return TRUE if equivalent or FALSE otherwise.
1256 : */
1257 :
1258 : // Backward compatibility method.
1259 :
1260 : //! @cond Doxygen_Suppress
1261 0 : bool OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
1262 : {
1263 0 : return Equals(poOtherGeom);
1264 : }
1265 :
1266 : //! @endcond
1267 :
1268 : /************************************************************************/
1269 : /* OGR_G_Equals() */
1270 : /************************************************************************/
1271 :
1272 : /**
1273 : * \brief Returns TRUE if two geometries are equivalent.
1274 : *
1275 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1276 : *
1277 : * The comparison is done in a structural way, that is to say that the geometry
1278 : * types must be identical, as well as the number and ordering of sub-geometries
1279 : * and vertices.
1280 : * Or equivalently, two geometries are considered equal by this method if their
1281 : * WKT/WKB representation is equal.
1282 : * Note: this must be distinguished for equality in a spatial way (which is
1283 : * the purpose of the ST_Equals() operation).
1284 : *
1285 : * This function is the same as the CPP method OGRGeometry::Equals() method.
1286 : *
1287 : * @param hGeom handle on the first geometry.
1288 : * @param hOther handle on the other geometry to test against.
1289 : * @return TRUE if equivalent or FALSE otherwise.
1290 : */
1291 :
1292 28120 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1293 :
1294 : {
1295 28120 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1296 :
1297 28120 : if (hOther == nullptr)
1298 : {
1299 0 : CPLError(CE_Failure, CPLE_ObjectNull,
1300 : "hOther was NULL in OGR_G_Equals");
1301 0 : return 0;
1302 : }
1303 :
1304 56240 : return OGRGeometry::FromHandle(hGeom)->Equals(
1305 56240 : OGRGeometry::FromHandle(hOther));
1306 : }
1307 :
1308 : //! @cond Doxygen_Suppress
1309 0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
1310 :
1311 : {
1312 0 : if (hGeom == nullptr)
1313 : {
1314 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
1315 0 : return 0;
1316 : }
1317 :
1318 0 : if (hOther == nullptr)
1319 : {
1320 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
1321 0 : return 0;
1322 : }
1323 :
1324 0 : return OGRGeometry::FromHandle(hGeom)->Equals(
1325 0 : OGRGeometry::FromHandle(hOther));
1326 : }
1327 :
1328 : //! @endcond
1329 :
1330 : /**
1331 : * \fn int OGRGeometry::WkbSize() const;
1332 : *
1333 : * \brief Returns size of related binary representation.
1334 : *
1335 : * This method returns the exact number of bytes required to hold the
1336 : * well known binary representation of this geometry object. Its computation
1337 : * may be slightly expensive for complex geometries.
1338 : *
1339 : * This method relates to the SFCOM IWks::WkbSize() method.
1340 : *
1341 : * This method is the same as the C function OGR_G_WkbSize().
1342 : *
1343 : * @return size of binary representation in bytes.
1344 : */
1345 :
1346 : /************************************************************************/
1347 : /* OGR_G_WkbSize() */
1348 : /************************************************************************/
1349 : /**
1350 : * \brief Returns size of related binary representation.
1351 : *
1352 : * This function returns the exact number of bytes required to hold the
1353 : * well known binary representation of this geometry object. Its computation
1354 : * may be slightly expensive for complex geometries.
1355 : *
1356 : * This function relates to the SFCOM IWks::WkbSize() method.
1357 : *
1358 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1359 : *
1360 : * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
1361 : *
1362 : * @param hGeom handle on the geometry to get the binary size from.
1363 : * @return size of binary representation in bytes.
1364 : */
1365 :
1366 1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
1367 :
1368 : {
1369 1 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
1370 :
1371 1 : const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
1372 1 : if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
1373 : {
1374 0 : CPLError(CE_Failure, CPLE_AppDefined,
1375 : "OGR_G_WkbSize() would return a value beyond int range. "
1376 : "Use OGR_G_WkbSizeEx() instead");
1377 0 : return 0;
1378 : }
1379 1 : return static_cast<int>(nSize);
1380 : }
1381 :
1382 : /************************************************************************/
1383 : /* OGR_G_WkbSizeEx() */
1384 : /************************************************************************/
1385 : /**
1386 : * \brief Returns size of related binary representation.
1387 : *
1388 : * This function returns the exact number of bytes required to hold the
1389 : * well known binary representation of this geometry object. Its computation
1390 : * may be slightly expensive for complex geometries.
1391 : *
1392 : * This function relates to the SFCOM IWks::WkbSize() method.
1393 : *
1394 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1395 : *
1396 : * @param hGeom handle on the geometry to get the binary size from.
1397 : * @return size of binary representation in bytes.
1398 : * @since GDAL 3.3
1399 : */
1400 :
1401 10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
1402 :
1403 : {
1404 10679 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
1405 :
1406 10679 : return OGRGeometry::FromHandle(hGeom)->WkbSize();
1407 : }
1408 :
1409 : /**
1410 : * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
1411 : *
1412 : * \brief Computes and returns the bounding envelope for this geometry
1413 : * in the passed psEnvelope structure.
1414 : *
1415 : * This method is the same as the C function OGR_G_GetEnvelope().
1416 : *
1417 : * @param psEnvelope the structure in which to place the results.
1418 : */
1419 :
1420 : /************************************************************************/
1421 : /* OGR_G_GetEnvelope() */
1422 : /************************************************************************/
1423 : /**
1424 : * \brief Computes and returns the bounding envelope for this geometry
1425 : * in the passed psEnvelope structure.
1426 : *
1427 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1428 : *
1429 : * @param hGeom handle of the geometry to get envelope from.
1430 : * @param psEnvelope the structure in which to place the results.
1431 : */
1432 :
1433 13371 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
1434 :
1435 : {
1436 13371 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1437 :
1438 13371 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1439 : }
1440 :
1441 : /**
1442 : * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
1443 : *
1444 : * \brief Computes and returns the bounding envelope (3D) for this
1445 : * geometry in the passed psEnvelope structure.
1446 : *
1447 : * This method is the same as the C function OGR_G_GetEnvelope3D().
1448 : *
1449 : * @param psEnvelope the structure in which to place the results.
1450 : *
1451 : */
1452 :
1453 : /************************************************************************/
1454 : /* OGR_G_GetEnvelope3D() */
1455 : /************************************************************************/
1456 : /**
1457 : * \brief Computes and returns the bounding envelope (3D) for this
1458 : * geometry in the passed psEnvelope structure.
1459 : *
1460 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1461 : *
1462 : * @param hGeom handle of the geometry to get envelope from.
1463 : * @param psEnvelope the structure in which to place the results.
1464 : *
1465 : */
1466 :
1467 10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
1468 :
1469 : {
1470 10 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
1471 :
1472 10 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* importFromWkb() */
1477 : /************************************************************************/
1478 :
1479 : /**
1480 : * \brief Assign geometry from well known binary data.
1481 : *
1482 : * The object must have already been instantiated as the correct derived
1483 : * type of geometry object to match the binaries type. This method is used
1484 : * by the OGRGeometryFactory class, but not normally called by application
1485 : * code.
1486 : *
1487 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1488 : *
1489 : * This method is the same as the C function OGR_G_ImportFromWkb().
1490 : *
1491 : * @param pabyData the binary input data.
1492 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1493 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1494 : * done for curve geometries code
1495 : *
1496 : * @return OGRERR_NONE if all goes well, otherwise any of
1497 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1498 : * OGRERR_CORRUPT_DATA may be returned.
1499 : */
1500 :
1501 492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
1502 : OGRwkbVariant eWkbVariant)
1503 : {
1504 492 : size_t nBytesConsumedOutIgnored = 0;
1505 492 : return importFromWkb(pabyData, nSize, eWkbVariant,
1506 984 : nBytesConsumedOutIgnored);
1507 : }
1508 :
1509 : /**
1510 : * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
1511 : * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
1512 : *
1513 : * \brief Assign geometry from well known binary data.
1514 : *
1515 : * The object must have already been instantiated as the correct derived
1516 : * type of geometry object to match the binaries type. This method is used
1517 : * by the OGRGeometryFactory class, but not normally called by application
1518 : * code.
1519 : *
1520 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1521 : *
1522 : * This method is the same as the C function OGR_G_ImportFromWkb().
1523 : *
1524 : * @param pabyData the binary input data.
1525 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1526 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1527 : * done for curve geometries code
1528 : * @param nBytesConsumedOut output parameter. Number of bytes consumed.
1529 : *
1530 : * @return OGRERR_NONE if all goes well, otherwise any of
1531 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1532 : * OGRERR_CORRUPT_DATA may be returned.
1533 : *
1534 : */
1535 :
1536 : /************************************************************************/
1537 : /* OGR_G_ImportFromWkb() */
1538 : /************************************************************************/
1539 : /**
1540 : * \brief Assign geometry from well known binary data.
1541 : *
1542 : * The object must have already been instantiated as the correct derived
1543 : * type of geometry object to match the binaries type.
1544 : *
1545 : * This function relates to the SFCOM IWks::ImportFromWKB() method.
1546 : *
1547 : * This function is the same as the CPP method OGRGeometry::importFromWkb().
1548 : *
1549 : * @param hGeom handle on the geometry to assign the well know binary data to.
1550 : * @param pabyData the binary input data.
1551 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1552 : *
1553 : * @return OGRERR_NONE if all goes well, otherwise any of
1554 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1555 : * OGRERR_CORRUPT_DATA may be returned.
1556 : */
1557 :
1558 0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
1559 :
1560 : {
1561 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
1562 :
1563 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkb(
1564 0 : static_cast<const GByte *>(pabyData), nSize);
1565 : }
1566 :
1567 : /************************************************************************/
1568 : /* OGRGeometry::exportToWkb() */
1569 : /************************************************************************/
1570 :
1571 : /* clang-format off */
1572 : /**
1573 : * \brief Convert a geometry into well known binary format.
1574 : *
1575 : * This method relates to the SFCOM IWks::ExportToWKB() method.
1576 : *
1577 : * This method is the same as the C function OGR_G_ExportToWkb() or
1578 : * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
1579 : *
1580 : * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1581 : * respectively.
1582 : * @param pabyData a buffer into which the binary representation is
1583 : * written. This buffer must be at least
1584 : * OGRGeometry::WkbSize() byte in size.
1585 : * @param eWkbVariant What standard to use when exporting geometries
1586 : * with three dimensions (or more). The default
1587 : * wkbVariantOldOgc is the historical OGR
1588 : * variant. wkbVariantIso is the variant defined
1589 : * in ISO SQL/MM and adopted by OGC for SFSQL
1590 : * 1.2.
1591 : *
1592 : * @return Currently OGRERR_NONE is always returned.
1593 : */
1594 : /* clang-format on */
1595 :
1596 278818 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1597 : unsigned char *pabyData,
1598 : OGRwkbVariant eWkbVariant) const
1599 : {
1600 278818 : OGRwkbExportOptions sOptions;
1601 278818 : sOptions.eByteOrder = eByteOrder;
1602 278818 : sOptions.eWkbVariant = eWkbVariant;
1603 557636 : return exportToWkb(pabyData, &sOptions);
1604 : }
1605 :
1606 : /************************************************************************/
1607 : /* OGR_G_ExportToWkb() */
1608 : /************************************************************************/
1609 : /**
1610 : * \brief Convert a geometry well known binary format
1611 : *
1612 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1613 : *
1614 : * For backward compatibility purposes, it exports the Old-style 99-402
1615 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1616 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1617 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
1618 : *
1619 : * This function is the same as the CPP method
1620 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
1621 : * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
1622 : *
1623 : * @param hGeom handle on the geometry to convert to a well know binary
1624 : * data from.
1625 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1626 : * respectively.
1627 : * @param pabyDstBuffer a buffer into which the binary representation is
1628 : * written. This buffer must be at least
1629 : * OGR_G_WkbSize() byte in size.
1630 : *
1631 : * @return Currently OGRERR_NONE is always returned.
1632 : */
1633 :
1634 109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1635 : unsigned char *pabyDstBuffer)
1636 :
1637 : {
1638 109 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
1639 :
1640 109 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
1641 : }
1642 :
1643 : /************************************************************************/
1644 : /* OGR_G_ExportToIsoWkb() */
1645 : /************************************************************************/
1646 : /**
1647 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
1648 : * binary format
1649 : *
1650 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1651 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
1652 : * types.
1653 : *
1654 : * This function is the same as the CPP method
1655 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
1656 : * with eWkbVariant = wkbVariantIso.
1657 : *
1658 : * @param hGeom handle on the geometry to convert to a well know binary
1659 : * data from.
1660 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1661 : * respectively.
1662 : * @param pabyDstBuffer a buffer into which the binary representation is
1663 : * written. This buffer must be at least
1664 : * OGR_G_WkbSize() byte in size.
1665 : *
1666 : * @return Currently OGRERR_NONE is always returned.
1667 : *
1668 : */
1669 :
1670 10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1671 : unsigned char *pabyDstBuffer)
1672 :
1673 : {
1674 10571 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
1675 :
1676 10571 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
1677 10571 : wkbVariantIso);
1678 : }
1679 :
1680 : /************************************************************************/
1681 : /* OGR_G_ExportToWkbEx() */
1682 : /************************************************************************/
1683 :
1684 : /* clang-format off */
1685 : /**
1686 : * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
1687 : *
1688 : * \brief Convert a geometry into well known binary format
1689 : *
1690 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1691 : *
1692 : * This function is the same as the C function OGR_G_ExportToWkbEx().
1693 : *
1694 : * @param pabyDstBuffer a buffer into which the binary representation is
1695 : * written. This buffer must be at least
1696 : * OGR_G_WkbSize() byte in size.
1697 : * @param psOptions WKB export options.
1698 :
1699 : * @return Currently OGRERR_NONE is always returned.
1700 : *
1701 : * @since GDAL 3.9
1702 : */
1703 : /* clang-format on */
1704 :
1705 : /**
1706 : * \brief Convert a geometry into well known binary format
1707 : *
1708 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1709 : *
1710 : * This function is the same as the CPP method
1711 : * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
1712 : *
1713 : * @param hGeom handle on the geometry to convert to a well know binary
1714 : * data from.
1715 : * @param pabyDstBuffer a buffer into which the binary representation is
1716 : * written. This buffer must be at least
1717 : * OGR_G_WkbSize() byte in size.
1718 : * @param psOptions WKB export options.
1719 :
1720 : * @return Currently OGRERR_NONE is always returned.
1721 : *
1722 : * @since GDAL 3.9
1723 : */
1724 :
1725 2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
1726 : const OGRwkbExportOptions *psOptions)
1727 : {
1728 2 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
1729 :
1730 4 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
1731 2 : psOptions);
1732 : }
1733 :
1734 : /**
1735 : * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
1736 : *
1737 : * \brief Assign geometry from well known text data.
1738 : *
1739 : * The object must have already been instantiated as the correct derived
1740 : * type of geometry object to match the text type. This method is used
1741 : * by the OGRGeometryFactory class, but not normally called by application
1742 : * code.
1743 : *
1744 : * This method relates to the SFCOM IWks::ImportFromWKT() method.
1745 : *
1746 : * This method is the same as the C function OGR_G_ImportFromWkt().
1747 : *
1748 : * @param ppszInput pointer to a pointer to the source text. The pointer is
1749 : * updated to pointer after the consumed text.
1750 : *
1751 : * @return OGRERR_NONE if all goes well, otherwise any of
1752 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1753 : * OGRERR_CORRUPT_DATA may be returned.
1754 : */
1755 :
1756 : /************************************************************************/
1757 : /* OGR_G_ImportFromWkt() */
1758 : /************************************************************************/
1759 : /**
1760 : * \brief Assign geometry from well known text data.
1761 : *
1762 : * The object must have already been instantiated as the correct derived
1763 : * type of geometry object to match the text type.
1764 : *
1765 : * This function relates to the SFCOM IWks::ImportFromWKT() method.
1766 : *
1767 : * This function is the same as the CPP method OGRGeometry::importFromWkt().
1768 : *
1769 : * @param hGeom handle on the geometry to assign well know text data to.
1770 : * @param ppszSrcText pointer to a pointer to the source text. The pointer is
1771 : * updated to pointer after the consumed text.
1772 : *
1773 : * @return OGRERR_NONE if all goes well, otherwise any of
1774 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1775 : * OGRERR_CORRUPT_DATA may be returned.
1776 : */
1777 :
1778 0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
1779 :
1780 : {
1781 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
1782 :
1783 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkt(
1784 0 : const_cast<const char **>(ppszSrcText));
1785 : }
1786 :
1787 : /************************************************************************/
1788 : /* importPreambleFromWkt() */
1789 : /************************************************************************/
1790 :
1791 : // Returns -1 if processing must continue.
1792 : //! @cond Doxygen_Suppress
1793 123910 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1794 : int *pbHasM, bool *pbIsEmpty)
1795 : {
1796 123910 : const char *pszInput = *ppszInput;
1797 :
1798 : /* -------------------------------------------------------------------- */
1799 : /* Clear existing Geoms. */
1800 : /* -------------------------------------------------------------------- */
1801 123910 : empty();
1802 123910 : *pbIsEmpty = false;
1803 :
1804 : /* -------------------------------------------------------------------- */
1805 : /* Read and verify the type keyword, and ensure it matches the */
1806 : /* actual type of this container. */
1807 : /* -------------------------------------------------------------------- */
1808 123910 : bool bHasM = false;
1809 123910 : bool bHasZ = false;
1810 123910 : bool bAlreadyGotDimension = false;
1811 :
1812 123910 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1813 123910 : pszInput = OGRWktReadToken(pszInput, szToken);
1814 123910 : if (szToken[0] != '\0')
1815 : {
1816 : // Postgis EWKT: POINTM instead of POINT M.
1817 : // Current QGIS versions (at least <= 3.38) also export POINTZ.
1818 123910 : const size_t nTokenLen = strlen(szToken);
1819 123910 : if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
1820 : {
1821 11 : szToken[nTokenLen - 1] = '\0';
1822 11 : bHasM = true;
1823 11 : bAlreadyGotDimension = true;
1824 :
1825 11 : if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
1826 9 : szToken[nTokenLen - 2] == 'z'))
1827 : {
1828 4 : bHasZ = true;
1829 4 : szToken[nTokenLen - 2] = '\0';
1830 : }
1831 : }
1832 123899 : else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
1833 : {
1834 6 : szToken[nTokenLen - 1] = '\0';
1835 6 : bHasZ = true;
1836 6 : bAlreadyGotDimension = true;
1837 : }
1838 : }
1839 :
1840 123910 : if (!EQUAL(szToken, getGeometryName()))
1841 0 : return OGRERR_CORRUPT_DATA;
1842 :
1843 : /* -------------------------------------------------------------------- */
1844 : /* Check for Z, M or ZM */
1845 : /* -------------------------------------------------------------------- */
1846 123910 : if (!bAlreadyGotDimension)
1847 : {
1848 123893 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1849 123893 : if (EQUAL(szToken, "Z"))
1850 : {
1851 1418 : pszInput = pszNewInput;
1852 1418 : bHasZ = true;
1853 : }
1854 122475 : else if (EQUAL(szToken, "M"))
1855 : {
1856 354 : pszInput = pszNewInput;
1857 354 : bHasM = true;
1858 : }
1859 122121 : else if (EQUAL(szToken, "ZM"))
1860 : {
1861 494 : pszInput = pszNewInput;
1862 494 : bHasZ = true;
1863 494 : bHasM = true;
1864 : }
1865 : }
1866 123910 : *pbHasZ = bHasZ;
1867 123910 : *pbHasM = bHasM;
1868 :
1869 : /* -------------------------------------------------------------------- */
1870 : /* Check for EMPTY ... */
1871 : /* -------------------------------------------------------------------- */
1872 123910 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1873 123910 : if (EQUAL(szToken, "EMPTY"))
1874 : {
1875 1578 : *ppszInput = pszNewInput;
1876 1578 : *pbIsEmpty = true;
1877 1578 : if (bHasZ)
1878 137 : set3D(TRUE);
1879 1578 : if (bHasM)
1880 84 : setMeasured(TRUE);
1881 1578 : return OGRERR_NONE;
1882 : }
1883 :
1884 122332 : if (!EQUAL(szToken, "("))
1885 35 : return OGRERR_CORRUPT_DATA;
1886 :
1887 122297 : if (!bHasZ && !bHasM)
1888 : {
1889 : // Test for old-style XXXXXXXXX(EMPTY).
1890 120194 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1891 120194 : if (EQUAL(szToken, "EMPTY"))
1892 : {
1893 69 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1894 :
1895 69 : if (EQUAL(szToken, ","))
1896 : {
1897 : // This is OK according to SFSQL SPEC.
1898 : }
1899 44 : else if (!EQUAL(szToken, ")"))
1900 : {
1901 9 : return OGRERR_CORRUPT_DATA;
1902 : }
1903 : else
1904 : {
1905 35 : *ppszInput = pszNewInput;
1906 35 : empty();
1907 35 : *pbIsEmpty = true;
1908 35 : return OGRERR_NONE;
1909 : }
1910 : }
1911 : }
1912 :
1913 122253 : *ppszInput = pszInput;
1914 :
1915 122253 : return OGRERR_NONE;
1916 : }
1917 :
1918 : //! @endcond
1919 :
1920 : /************************************************************************/
1921 : /* wktTypeString() */
1922 : /************************************************************************/
1923 :
1924 : //! @cond Doxygen_Suppress
1925 : /** Get a type string for WKT, padded with a space at the end.
1926 : *
1927 : * @param variant OGR type variant
1928 : * @return "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
1929 : */
1930 14571 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1931 : {
1932 14571 : std::string s(" ");
1933 :
1934 14571 : if (variant == wkbVariantIso)
1935 : {
1936 9546 : if (flags & OGR_G_3D)
1937 1957 : s += "Z";
1938 9546 : if (flags & OGR_G_MEASURED)
1939 1203 : s += "M";
1940 : }
1941 14571 : if (s.size() > 1)
1942 2521 : s += " ";
1943 14571 : return s;
1944 : }
1945 :
1946 : //! @endcond
1947 :
1948 : /**
1949 : * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
1950 : * OGRwkbVariant variant = wkbVariantOldOgc ) const;
1951 : *
1952 : * \brief Convert a geometry into well known text format.
1953 : *
1954 : * This method relates to the SFCOM IWks::ExportToWKT() method.
1955 : *
1956 : * This method is the same as the C function OGR_G_ExportToWkt().
1957 : *
1958 : * @param ppszDstText a text buffer is allocated by the program, and assigned
1959 : * to the passed pointer. After use, *ppszDstText should be
1960 : * freed with CPLFree().
1961 : * @param variant the specification that must be conformed too :
1962 : * - wkbVariantOgc for old-style 99-402 extended
1963 : * dimension (Z) WKB types
1964 : * - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
1965 : *
1966 : * @return Currently OGRERR_NONE is always returned.
1967 : */
1968 8909 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1969 : {
1970 8909 : OGRWktOptions opts;
1971 8909 : opts.variant = variant;
1972 8909 : OGRErr err(OGRERR_NONE);
1973 :
1974 8909 : std::string wkt = exportToWkt(opts, &err);
1975 8909 : *ppszDstText = CPLStrdup(wkt.data());
1976 17818 : return err;
1977 : }
1978 :
1979 : /************************************************************************/
1980 : /* OGR_G_ExportToWkt() */
1981 : /************************************************************************/
1982 :
1983 : /**
1984 : * \brief Convert a geometry into well known text format.
1985 : *
1986 : * This function relates to the SFCOM IWks::ExportToWKT() method.
1987 : *
1988 : * For backward compatibility purposes, it exports the Old-style 99-402
1989 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1990 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1991 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
1992 : *
1993 : * This function is the same as the CPP method OGRGeometry::exportToWkt().
1994 : *
1995 : * @param hGeom handle on the geometry to convert to a text format from.
1996 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
1997 : * to the passed pointer. After use, *ppszDstText should be
1998 : * freed with CPLFree().
1999 : *
2000 : * @return Currently OGRERR_NONE is always returned.
2001 : */
2002 :
2003 2599 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
2004 :
2005 : {
2006 2599 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2007 :
2008 2599 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
2009 : }
2010 :
2011 : /************************************************************************/
2012 : /* OGR_G_ExportToIsoWkt() */
2013 : /************************************************************************/
2014 :
2015 : /**
2016 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
2017 : * known text format.
2018 : *
2019 : * This function relates to the SFCOM IWks::ExportToWKT() method.
2020 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
2021 : * (Z&M) WKB types.
2022 : *
2023 : * This function is the same as the CPP method
2024 : * OGRGeometry::exportToWkt(wkbVariantIso).
2025 : *
2026 : * @param hGeom handle on the geometry to convert to a text format from.
2027 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
2028 : * to the passed pointer. After use, *ppszDstText should be
2029 : * freed with CPLFree().
2030 : *
2031 : * @return Currently OGRERR_NONE is always returned.
2032 : *
2033 : */
2034 :
2035 5499 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
2036 :
2037 : {
2038 5499 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2039 :
2040 5499 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2041 5499 : wkbVariantIso);
2042 : }
2043 :
2044 : /**
2045 : * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
2046 : *
2047 : * \brief Fetch geometry type.
2048 : *
2049 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2050 : * flattened version of the geometry type apply the wkbFlatten() macro
2051 : * to the return result.
2052 : *
2053 : * This method is the same as the C function OGR_G_GetGeometryType().
2054 : *
2055 : * @return the geometry type code.
2056 : */
2057 :
2058 : /************************************************************************/
2059 : /* OGR_G_GetGeometryType() */
2060 : /************************************************************************/
2061 : /**
2062 : * \brief Fetch geometry type.
2063 : *
2064 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2065 : * flattened version of the geometry type apply the wkbFlatten() macro
2066 : * to the return result.
2067 : *
2068 : * This function is the same as the CPP method OGRGeometry::getGeometryType().
2069 : *
2070 : * @param hGeom handle on the geometry to get type from.
2071 : * @return the geometry type code.
2072 : */
2073 :
2074 5818 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2075 :
2076 : {
2077 5818 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2078 :
2079 5818 : return OGRGeometry::FromHandle(hGeom)->getGeometryType();
2080 : }
2081 :
2082 : /**
2083 : * \fn const char * OGRGeometry::getGeometryName() const;
2084 : *
2085 : * \brief Fetch WKT name for geometry type.
2086 : *
2087 : * There is no SFCOM analog to this method.
2088 : *
2089 : * This method is the same as the C function OGR_G_GetGeometryName().
2090 : *
2091 : * @return name used for this geometry type in well known text format. The
2092 : * returned pointer is to a static internal string and should not be modified
2093 : * or freed.
2094 : */
2095 :
2096 : /************************************************************************/
2097 : /* OGR_G_GetGeometryName() */
2098 : /************************************************************************/
2099 : /**
2100 : * \brief Fetch WKT name for geometry type.
2101 : *
2102 : * There is no SFCOM analog to this function.
2103 : *
2104 : * This function is the same as the CPP method OGRGeometry::getGeometryName().
2105 : *
2106 : * @param hGeom handle on the geometry to get name from.
2107 : * @return name used for this geometry type in well known text format.
2108 : */
2109 :
2110 19014 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2111 :
2112 : {
2113 19014 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2114 :
2115 19014 : return OGRGeometry::FromHandle(hGeom)->getGeometryName();
2116 : }
2117 :
2118 : /**
2119 : * \fn OGRGeometry *OGRGeometry::clone() const;
2120 : *
2121 : * \brief Make a copy of this object.
2122 : *
2123 : * This method relates to the SFCOM IGeometry::clone() method.
2124 : *
2125 : * This method is the same as the C function OGR_G_Clone().
2126 : *
2127 : * @return a new object instance with the same geometry, and spatial
2128 : * reference system as the original.
2129 : */
2130 :
2131 : /************************************************************************/
2132 : /* OGR_G_Clone() */
2133 : /************************************************************************/
2134 : /**
2135 : * \brief Make a copy of this object.
2136 : *
2137 : * This function relates to the SFCOM IGeometry::clone() method.
2138 : *
2139 : * This function is the same as the CPP method OGRGeometry::clone().
2140 : *
2141 : * @param hGeom handle on the geometry to clone from.
2142 : * @return a handle on the copy of the geometry with the spatial
2143 : * reference system as the original.
2144 : */
2145 :
2146 13575 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
2147 :
2148 : {
2149 13575 : VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
2150 :
2151 13575 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
2152 : }
2153 :
2154 : /**
2155 : * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
2156 : *
2157 : * \brief Returns spatial reference system for object.
2158 : *
2159 : * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
2160 : *
2161 : * This method is the same as the C function OGR_G_GetSpatialReference().
2162 : *
2163 : * @return a reference to the spatial reference object. The object may be
2164 : * shared with many geometry objects, and should not be modified.
2165 : */
2166 :
2167 : /************************************************************************/
2168 : /* OGR_G_GetSpatialReference() */
2169 : /************************************************************************/
2170 : /**
2171 : * \brief Returns spatial reference system for geometry.
2172 : *
2173 : * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
2174 : *
2175 : * This function is the same as the CPP method
2176 : * OGRGeometry::getSpatialReference().
2177 : *
2178 : * @param hGeom handle on the geometry to get spatial reference from.
2179 : * @return a reference to the spatial reference geometry, which should not be
2180 : * modified.
2181 : */
2182 :
2183 126 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
2184 :
2185 : {
2186 126 : VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
2187 :
2188 126 : return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
2189 252 : OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
2190 : }
2191 :
2192 : /**
2193 : * \fn void OGRGeometry::empty();
2194 : *
2195 : * \brief Clear geometry information.
2196 : * This restores the geometry to its initial
2197 : * state after construction, and before assignment of actual geometry.
2198 : *
2199 : * This method relates to the SFCOM IGeometry::Empty() method.
2200 : *
2201 : * This method is the same as the C function OGR_G_Empty().
2202 : */
2203 :
2204 : /************************************************************************/
2205 : /* OGR_G_Empty() */
2206 : /************************************************************************/
2207 : /**
2208 : * \brief Clear geometry information.
2209 : * This restores the geometry to its initial
2210 : * state after construction, and before assignment of actual geometry.
2211 : *
2212 : * This function relates to the SFCOM IGeometry::Empty() method.
2213 : *
2214 : * This function is the same as the CPP method OGRGeometry::empty().
2215 : *
2216 : * @param hGeom handle on the geometry to empty.
2217 : */
2218 :
2219 4 : void OGR_G_Empty(OGRGeometryH hGeom)
2220 :
2221 : {
2222 4 : VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
2223 :
2224 4 : OGRGeometry::FromHandle(hGeom)->empty();
2225 : }
2226 :
2227 : /**
2228 : * \fn bool OGRGeometry::IsEmpty() const;
2229 : *
2230 : * \brief Returns TRUE (non-zero) if the object has no points.
2231 : *
2232 : * Normally this
2233 : * returns FALSE except between when an object is instantiated and points
2234 : * have been assigned.
2235 : *
2236 : * This method relates to the SFCOM IGeometry::IsEmpty() method.
2237 : *
2238 : * @return TRUE if object is empty, otherwise FALSE.
2239 : */
2240 :
2241 : /************************************************************************/
2242 : /* OGR_G_IsEmpty() */
2243 : /************************************************************************/
2244 :
2245 : /**
2246 : * \brief Test if the geometry is empty.
2247 : *
2248 : * This method is the same as the CPP method OGRGeometry::IsEmpty().
2249 : *
2250 : * @param hGeom The Geometry to test.
2251 : *
2252 : * @return TRUE if the geometry has no points, otherwise FALSE.
2253 : */
2254 :
2255 2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
2256 :
2257 : {
2258 2391 : VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
2259 :
2260 2391 : return OGRGeometry::FromHandle(hGeom)->IsEmpty();
2261 : }
2262 :
2263 : /************************************************************************/
2264 : /* IsValid() */
2265 : /************************************************************************/
2266 :
2267 : /**
2268 : * \brief Test if the geometry is valid.
2269 : *
2270 : * This method is the same as the C functions OGR_G_IsValid() and
2271 : * OGR_G_GetInvalidityReason().
2272 : *
2273 : * This method is built on the GEOS library, check it for the definition
2274 : * of the geometry operation.
2275 : * If OGR is built without the GEOS library, this method will always return
2276 : * FALSE.
2277 : *
2278 : * @param[out] posReason (since 3.13) Pointer to a string to receive the reason
2279 : * for invalidity, or nullptr. When nullptr, invalidity
2280 : * reasons are emitted as CPL warnings.
2281 : * @return TRUE if the geometry has no points, otherwise FALSE.
2282 : */
2283 :
2284 2977 : bool OGRGeometry::IsValid(std::string *posReason) const
2285 :
2286 : {
2287 2977 : if (posReason)
2288 851 : posReason->clear();
2289 :
2290 2977 : if (IsSFCGALCompatible())
2291 : {
2292 : #ifndef HAVE_SFCGAL
2293 :
2294 : #ifdef HAVE_GEOS
2295 2 : if (wkbFlatten(getGeometryType()) == wkbTriangle)
2296 : {
2297 : // go on
2298 : }
2299 : else
2300 : #endif
2301 : {
2302 0 : CPLError(CE_Failure, CPLE_NotSupported,
2303 : "SFCGAL support not enabled.");
2304 0 : return FALSE;
2305 : }
2306 : #else
2307 : sfcgal_init();
2308 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
2309 : if (poThis == nullptr)
2310 : {
2311 : CPLError(CE_Failure, CPLE_IllegalArg,
2312 : "SFCGAL geometry returned is NULL");
2313 : return FALSE;
2314 : }
2315 :
2316 : const int res = sfcgal_geometry_is_valid(poThis);
2317 : if (res != 1 && posReason)
2318 : {
2319 : char *pszReason = nullptr;
2320 : sfcgal_geometry_is_valid_detail(poThis, &pszReason, nullptr);
2321 : if (pszReason)
2322 : {
2323 : *posReason = pszReason;
2324 : free(pszReason);
2325 : }
2326 : else
2327 : *posReason = "unknown reason";
2328 : }
2329 : sfcgal_geometry_delete(poThis);
2330 : return res == 1;
2331 : #endif
2332 : }
2333 :
2334 : {
2335 : #ifndef HAVE_GEOS
2336 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2337 : return FALSE;
2338 :
2339 : #else
2340 2977 : bool bResult = false;
2341 :
2342 : // Some invalid geometries, such as lines with one point, or
2343 : // rings that do not close, cannot be converted to GEOS.
2344 : // For validity checking we initialize the GEOS context with
2345 : // the warning handler as the error handler to avoid emitting
2346 : // CE_Failure when a geometry cannot be converted to GEOS.
2347 : GEOSContextHandle_t hGEOSCtxt =
2348 2977 : initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
2349 :
2350 : GEOSGeom hThisGeosGeom;
2351 2977 : if (posReason)
2352 : {
2353 1702 : CPLErrorAccumulator oAccumulator;
2354 : {
2355 851 : auto oContext = oAccumulator.InstallForCurrentScope();
2356 851 : CPL_IGNORE_RET_VAL(oContext);
2357 851 : hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2358 : }
2359 851 : if (!hThisGeosGeom && oAccumulator.GetErrors().size() == 1)
2360 : {
2361 2 : std::string msg = oAccumulator.GetErrors()[0].msg;
2362 :
2363 : // Trim GEOS exception name
2364 1 : const auto subMsgPos = msg.find(": ");
2365 1 : if (subMsgPos != std::string::npos)
2366 : {
2367 1 : msg = msg.substr(subMsgPos + strlen(": "));
2368 : }
2369 :
2370 : // Trim newline from end of GEOS exception message
2371 1 : if (!msg.empty() && msg.back() == '\n')
2372 : {
2373 1 : msg.pop_back();
2374 : }
2375 :
2376 1 : *posReason = std::move(msg);
2377 : }
2378 : }
2379 : else
2380 : {
2381 2126 : hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2382 : }
2383 :
2384 2977 : if (hThisGeosGeom != nullptr)
2385 : {
2386 2976 : if (posReason)
2387 : {
2388 1700 : CPLErrorAccumulator oAccumulator;
2389 : {
2390 850 : auto oContext = oAccumulator.InstallForCurrentScope();
2391 850 : CPL_IGNORE_RET_VAL(oContext);
2392 850 : bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
2393 : }
2394 850 : if (!bResult && oAccumulator.GetErrors().size() == 1)
2395 : {
2396 27 : *posReason = oAccumulator.GetErrors()[0].msg;
2397 : }
2398 : }
2399 : else
2400 : {
2401 2126 : bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
2402 : }
2403 : #ifdef DEBUG_VERBOSE
2404 : if (!bResult && !posReason)
2405 : {
2406 : char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
2407 : CPLDebug("OGR", "%s", pszReason);
2408 : GEOSFree_r(hGEOSCtxt, pszReason);
2409 : }
2410 : #endif
2411 2976 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2412 : }
2413 2977 : freeGEOSContext(hGEOSCtxt);
2414 :
2415 2977 : return bResult;
2416 :
2417 : #endif // HAVE_GEOS
2418 : }
2419 : }
2420 :
2421 : /************************************************************************/
2422 : /* OGR_G_IsValid() */
2423 : /************************************************************************/
2424 :
2425 : /**
2426 : * \brief Test if the geometry is valid.
2427 : *
2428 : * This function is the same as the C++ method OGRGeometry::IsValid().
2429 : *
2430 : * This function is built on the GEOS library, check it for the definition
2431 : * of the geometry operation.
2432 : * If OGR is built without the GEOS library, this function will always return
2433 : * FALSE.
2434 : *
2435 : * If the geometry is invalid, the reason for its invalidity is emitted as a
2436 : * CPL warning. To get it in a string instead, use OGR_G_GetInvalidityReason()
2437 : *
2438 : * @param hGeom The Geometry to test.
2439 : *
2440 : * @return TRUE if the geometry is valid, otherwise FALSE.
2441 : */
2442 :
2443 21 : int OGR_G_IsValid(OGRGeometryH hGeom)
2444 :
2445 : {
2446 21 : VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
2447 :
2448 21 : return OGRGeometry::FromHandle(hGeom)->IsValid();
2449 : }
2450 :
2451 : /************************************************************************/
2452 : /* OGR_G_GetInvalidityReason() */
2453 : /************************************************************************/
2454 :
2455 : /**
2456 : * \brief Test if the geometry is valid and, if not, return the invalidity reason.
2457 : *
2458 : * This function is the same as the C++ method OGRGeometry::IsValid().
2459 : *
2460 : * This function is built on the GEOS library, check it for the definition
2461 : * of the geometry operation.
2462 : * If OGR is built without the GEOS library, this function will always return
2463 : * FALSE.
2464 : *
2465 : * @param hGeom The Geometry to test.
2466 : * @return a string with the invalidity reason, to free with CPLFree(),
2467 : * if the geometry is invalid, or nullptr if the geometry is valid.
2468 : *
2469 : * @since 3.13
2470 : */
2471 :
2472 3 : char *OGR_G_GetInvalidityReason(OGRGeometryH hGeom)
2473 :
2474 : {
2475 3 : VALIDATE_POINTER1(hGeom, "OGR_G_GetInvalidityReason", nullptr);
2476 :
2477 6 : std::string osReason;
2478 3 : const int nRet = OGRGeometry::FromHandle(hGeom)->IsValid(&osReason);
2479 3 : if (osReason.empty())
2480 : {
2481 1 : if (!nRet)
2482 : {
2483 : // not sure if that can happen
2484 0 : return CPLStrdup("unknown reason");
2485 : }
2486 : else
2487 1 : return nullptr;
2488 : }
2489 : else
2490 : {
2491 2 : return CPLStrdup(osReason.c_str());
2492 : }
2493 : }
2494 :
2495 : /************************************************************************/
2496 : /* IsSimple() */
2497 : /************************************************************************/
2498 :
2499 : /**
2500 : * \brief Test if the geometry is simple.
2501 : *
2502 : * This method is the same as the C function OGR_G_IsSimple().
2503 : *
2504 : * This method is built on the GEOS library, check it for the definition
2505 : * of the geometry operation.
2506 : * If OGR is built without the GEOS library, this method will always return
2507 : * FALSE.
2508 : *
2509 : *
2510 : * @return TRUE if the geometry has no points, otherwise FALSE.
2511 : */
2512 :
2513 5 : bool OGRGeometry::IsSimple() const
2514 :
2515 : {
2516 : #ifndef HAVE_GEOS
2517 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2518 : return FALSE;
2519 :
2520 : #else
2521 :
2522 5 : bool bResult = false;
2523 :
2524 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2525 5 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2526 :
2527 5 : if (hThisGeosGeom != nullptr)
2528 : {
2529 5 : bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
2530 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2531 : }
2532 5 : freeGEOSContext(hGEOSCtxt);
2533 :
2534 5 : return bResult;
2535 :
2536 : #endif // HAVE_GEOS
2537 : }
2538 :
2539 : /**
2540 : * \brief Returns TRUE if the geometry is simple.
2541 : *
2542 : * Returns TRUE if the geometry has no anomalous geometric points, such
2543 : * as self intersection or self tangency. The description of each
2544 : * instantiable geometric class will include the specific conditions that
2545 : * cause an instance of that class to be classified as not simple.
2546 : *
2547 : * This function is the same as the C++ method OGRGeometry::IsSimple() method.
2548 : *
2549 : * If OGR is built without the GEOS library, this function will always return
2550 : * FALSE.
2551 : *
2552 : * @param hGeom The Geometry to test.
2553 : *
2554 : * @return TRUE if object is simple, otherwise FALSE.
2555 : */
2556 :
2557 5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
2558 :
2559 : {
2560 5 : VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
2561 :
2562 5 : return OGRGeometry::FromHandle(hGeom)->IsSimple();
2563 : }
2564 :
2565 : /************************************************************************/
2566 : /* IsRing() */
2567 : /************************************************************************/
2568 :
2569 : /**
2570 : * \brief Test if the geometry is a ring
2571 : *
2572 : * This method is the same as the C function OGR_G_IsRing().
2573 : *
2574 : * This method is built on the GEOS library, check it for the definition
2575 : * of the geometry operation.
2576 : * If OGR is built without the GEOS library, this method will always return
2577 : * FALSE.
2578 : *
2579 : *
2580 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2581 : * length and closure (self-intersection is not checked), otherwise FALSE.
2582 : */
2583 :
2584 1 : bool OGRGeometry::IsRing() const
2585 :
2586 : {
2587 : #ifndef HAVE_GEOS
2588 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2589 : return FALSE;
2590 :
2591 : #else
2592 :
2593 1 : bool bResult = false;
2594 :
2595 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2596 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2597 :
2598 1 : if (hThisGeosGeom != nullptr)
2599 : {
2600 1 : bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
2601 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2602 : }
2603 1 : freeGEOSContext(hGEOSCtxt);
2604 :
2605 1 : return bResult;
2606 :
2607 : #endif // HAVE_GEOS
2608 : }
2609 :
2610 : /************************************************************************/
2611 : /* OGR_G_IsRing() */
2612 : /************************************************************************/
2613 :
2614 : /**
2615 : * \brief Test if the geometry is a ring
2616 : *
2617 : * This function is the same as the C++ method OGRGeometry::IsRing().
2618 : *
2619 : * This function is built on the GEOS library, check it for the definition
2620 : * of the geometry operation.
2621 : * If OGR is built without the GEOS library, this function will always return
2622 : * FALSE.
2623 : *
2624 : * @param hGeom The Geometry to test.
2625 : *
2626 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2627 : * length and closure (self-intersection is not checked), otherwise FALSE.
2628 : */
2629 :
2630 1 : int OGR_G_IsRing(OGRGeometryH hGeom)
2631 :
2632 : {
2633 1 : VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
2634 :
2635 1 : return OGRGeometry::FromHandle(hGeom)->IsRing();
2636 : }
2637 :
2638 : /************************************************************************/
2639 : /* OGRFromOGCGeomType() */
2640 : /************************************************************************/
2641 :
2642 : /** Map OGC geometry format type to corresponding OGR constants.
2643 : * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
2644 : * @return OGR constant.
2645 : */
2646 3272 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2647 : {
2648 3272 : OGRwkbGeometryType eType = wkbUnknown;
2649 3272 : bool bConvertTo3D = false;
2650 3272 : bool bIsMeasured = false;
2651 3272 : if (*pszGeomType != '\0')
2652 : {
2653 3266 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2654 3266 : if (ch == 'm' || ch == 'M')
2655 : {
2656 2 : bIsMeasured = true;
2657 2 : if (strlen(pszGeomType) > 1)
2658 2 : ch = pszGeomType[strlen(pszGeomType) - 2];
2659 : }
2660 3266 : if (ch == 'z' || ch == 'Z')
2661 : {
2662 34 : bConvertTo3D = true;
2663 : }
2664 : }
2665 :
2666 3272 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2667 971 : eType = wkbPoint;
2668 2301 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2669 186 : eType = wkbLineString;
2670 2115 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2671 375 : eType = wkbPolygon;
2672 1740 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2673 24 : eType = wkbMultiPoint;
2674 1716 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2675 15 : eType = wkbMultiLineString;
2676 1701 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2677 651 : eType = wkbMultiPolygon;
2678 1050 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2679 4 : eType = wkbGeometryCollection;
2680 1046 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2681 356 : eType = wkbCircularString;
2682 690 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2683 0 : eType = wkbCompoundCurve;
2684 690 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2685 16 : eType = wkbCurvePolygon;
2686 674 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2687 2 : eType = wkbMultiCurve;
2688 672 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2689 0 : eType = wkbMultiSurface;
2690 672 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2691 0 : eType = wkbTriangle;
2692 672 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2693 1 : eType = wkbPolyhedralSurface;
2694 671 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2695 5 : eType = wkbTIN;
2696 666 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2697 3 : eType = wkbCurve;
2698 663 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2699 3 : eType = wkbSurface;
2700 : else
2701 660 : eType = wkbUnknown;
2702 :
2703 3272 : if (bConvertTo3D)
2704 34 : eType = wkbSetZ(eType);
2705 3272 : if (bIsMeasured)
2706 2 : eType = wkbSetM(eType);
2707 :
2708 3272 : return eType;
2709 : }
2710 :
2711 : /************************************************************************/
2712 : /* OGRToOGCGeomType() */
2713 : /************************************************************************/
2714 :
2715 : /** Map OGR geometry format constants to corresponding OGC geometry type.
2716 : * @param eGeomType OGR geometry type
2717 : * @param bCamelCase Whether the return should be like "MultiPoint"
2718 : * (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
2719 : * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
2720 : * Default is false.
2721 : * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
2722 : * suffix. Default is false.
2723 : * @return string with OGC geometry type (without dimensionality)
2724 : */
2725 3172 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2726 : bool bAddZM, bool bSpaceBeforeZM)
2727 : {
2728 3172 : const char *pszRet = "";
2729 3172 : switch (wkbFlatten(eGeomType))
2730 : {
2731 1709 : case wkbUnknown:
2732 1709 : pszRet = "Geometry";
2733 1709 : break;
2734 562 : case wkbPoint:
2735 562 : pszRet = "Point";
2736 562 : break;
2737 140 : case wkbLineString:
2738 140 : pszRet = "LineString";
2739 140 : break;
2740 186 : case wkbPolygon:
2741 186 : pszRet = "Polygon";
2742 186 : break;
2743 49 : case wkbMultiPoint:
2744 49 : pszRet = "MultiPoint";
2745 49 : break;
2746 76 : case wkbMultiLineString:
2747 76 : pszRet = "MultiLineString";
2748 76 : break;
2749 293 : case wkbMultiPolygon:
2750 293 : pszRet = "MultiPolygon";
2751 293 : break;
2752 54 : case wkbGeometryCollection:
2753 54 : pszRet = "GeometryCollection";
2754 54 : break;
2755 9 : case wkbCircularString:
2756 9 : pszRet = "CircularString";
2757 9 : break;
2758 3 : case wkbCompoundCurve:
2759 3 : pszRet = "CompoundCurve";
2760 3 : break;
2761 12 : case wkbCurvePolygon:
2762 12 : pszRet = "CurvePolygon";
2763 12 : break;
2764 2 : case wkbMultiCurve:
2765 2 : pszRet = "MultiCurve";
2766 2 : break;
2767 3 : case wkbMultiSurface:
2768 3 : pszRet = "MultiSurface";
2769 3 : break;
2770 3 : case wkbTriangle:
2771 3 : pszRet = "Triangle";
2772 3 : break;
2773 5 : case wkbPolyhedralSurface:
2774 5 : pszRet = "PolyhedralSurface";
2775 5 : break;
2776 1 : case wkbTIN:
2777 1 : pszRet = "Tin";
2778 1 : break;
2779 3 : case wkbCurve:
2780 3 : pszRet = "Curve";
2781 3 : break;
2782 3 : case wkbSurface:
2783 3 : pszRet = "Surface";
2784 3 : break;
2785 59 : default:
2786 59 : break;
2787 : }
2788 3172 : if (bAddZM)
2789 : {
2790 71 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2791 71 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2792 71 : if (bHasZ || bHasM)
2793 : {
2794 10 : if (bSpaceBeforeZM)
2795 1 : pszRet = CPLSPrintf("%s ", pszRet);
2796 10 : if (bHasZ)
2797 9 : pszRet = CPLSPrintf("%sZ", pszRet);
2798 10 : if (bHasM)
2799 5 : pszRet = CPLSPrintf("%sM", pszRet);
2800 : }
2801 : }
2802 3172 : if (!bCamelCase)
2803 3100 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2804 3172 : return pszRet;
2805 : }
2806 :
2807 : /************************************************************************/
2808 : /* OGRGeometryTypeToName() */
2809 : /************************************************************************/
2810 :
2811 : /**
2812 : * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
2813 : * value. The returned value should not be modified, or freed by the
2814 : * application.
2815 : *
2816 : * This function is C callable.
2817 : *
2818 : * @param eType the geometry type.
2819 : *
2820 : * @return internal human readable string, or NULL on failure.
2821 : */
2822 :
2823 399 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2824 :
2825 : {
2826 399 : bool b3D = wkbHasZ(eType);
2827 399 : bool bMeasured = wkbHasM(eType);
2828 :
2829 399 : switch (wkbFlatten(eType))
2830 : {
2831 34 : case wkbUnknown:
2832 34 : if (b3D && bMeasured)
2833 0 : return "3D Measured Unknown (any)";
2834 34 : else if (b3D)
2835 1 : return "3D Unknown (any)";
2836 33 : else if (bMeasured)
2837 0 : return "Measured Unknown (any)";
2838 : else
2839 33 : return "Unknown (any)";
2840 :
2841 59 : case wkbPoint:
2842 59 : if (b3D && bMeasured)
2843 3 : return "3D Measured Point";
2844 56 : else if (b3D)
2845 12 : return "3D Point";
2846 44 : else if (bMeasured)
2847 5 : return "Measured Point";
2848 : else
2849 39 : return "Point";
2850 :
2851 21 : case wkbLineString:
2852 21 : if (b3D && bMeasured)
2853 0 : return "3D Measured Line String";
2854 21 : else if (b3D)
2855 9 : return "3D Line String";
2856 12 : else if (bMeasured)
2857 0 : return "Measured Line String";
2858 : else
2859 12 : return "Line String";
2860 :
2861 24 : case wkbPolygon:
2862 24 : if (b3D && bMeasured)
2863 0 : return "3D Measured Polygon";
2864 24 : else if (b3D)
2865 8 : return "3D Polygon";
2866 16 : else if (bMeasured)
2867 0 : return "Measured Polygon";
2868 : else
2869 16 : return "Polygon";
2870 :
2871 21 : case wkbMultiPoint:
2872 21 : if (b3D && bMeasured)
2873 0 : return "3D Measured Multi Point";
2874 21 : else if (b3D)
2875 9 : return "3D Multi Point";
2876 12 : else if (bMeasured)
2877 0 : return "Measured Multi Point";
2878 : else
2879 12 : return "Multi Point";
2880 :
2881 32 : case wkbMultiLineString:
2882 32 : if (b3D && bMeasured)
2883 0 : return "3D Measured Multi Line String";
2884 32 : else if (b3D)
2885 6 : return "3D Multi Line String";
2886 26 : else if (bMeasured)
2887 0 : return "Measured Multi Line String";
2888 : else
2889 26 : return "Multi Line String";
2890 :
2891 59 : case wkbMultiPolygon:
2892 59 : if (b3D && bMeasured)
2893 0 : return "3D Measured Multi Polygon";
2894 59 : else if (b3D)
2895 8 : return "3D Multi Polygon";
2896 51 : else if (bMeasured)
2897 0 : return "Measured Multi Polygon";
2898 : else
2899 51 : return "Multi Polygon";
2900 :
2901 25 : case wkbGeometryCollection:
2902 25 : if (b3D && bMeasured)
2903 0 : return "3D Measured Geometry Collection";
2904 25 : else if (b3D)
2905 10 : return "3D Geometry Collection";
2906 15 : else if (bMeasured)
2907 0 : return "Measured Geometry Collection";
2908 : else
2909 15 : return "Geometry Collection";
2910 :
2911 0 : case wkbCircularString:
2912 0 : if (b3D && bMeasured)
2913 0 : return "3D Measured Circular String";
2914 0 : else if (b3D)
2915 0 : return "3D Circular String";
2916 0 : else if (bMeasured)
2917 0 : return "Measured Circular String";
2918 : else
2919 0 : return "Circular String";
2920 :
2921 1 : case wkbCompoundCurve:
2922 1 : if (b3D && bMeasured)
2923 0 : return "3D Measured Compound Curve";
2924 1 : else if (b3D)
2925 0 : return "3D Compound Curve";
2926 1 : else if (bMeasured)
2927 0 : return "Measured Compound Curve";
2928 : else
2929 1 : return "Compound Curve";
2930 :
2931 0 : case wkbCurvePolygon:
2932 0 : if (b3D && bMeasured)
2933 0 : return "3D Measured Curve Polygon";
2934 0 : else if (b3D)
2935 0 : return "3D Curve Polygon";
2936 0 : else if (bMeasured)
2937 0 : return "Measured Curve Polygon";
2938 : else
2939 0 : return "Curve Polygon";
2940 :
2941 0 : case wkbMultiCurve:
2942 0 : if (b3D && bMeasured)
2943 0 : return "3D Measured Multi Curve";
2944 0 : else if (b3D)
2945 0 : return "3D Multi Curve";
2946 0 : else if (bMeasured)
2947 0 : return "Measured Multi Curve";
2948 : else
2949 0 : return "Multi Curve";
2950 :
2951 0 : case wkbMultiSurface:
2952 0 : if (b3D && bMeasured)
2953 0 : return "3D Measured Multi Surface";
2954 0 : else if (b3D)
2955 0 : return "3D Multi Surface";
2956 0 : else if (bMeasured)
2957 0 : return "Measured Multi Surface";
2958 : else
2959 0 : return "Multi Surface";
2960 :
2961 4 : case wkbCurve:
2962 4 : if (b3D && bMeasured)
2963 1 : return "3D Measured Curve";
2964 3 : else if (b3D)
2965 1 : return "3D Curve";
2966 2 : else if (bMeasured)
2967 1 : return "Measured Curve";
2968 : else
2969 1 : return "Curve";
2970 :
2971 4 : case wkbSurface:
2972 4 : if (b3D && bMeasured)
2973 1 : return "3D Measured Surface";
2974 3 : else if (b3D)
2975 1 : return "3D Surface";
2976 2 : else if (bMeasured)
2977 1 : return "Measured Surface";
2978 : else
2979 1 : return "Surface";
2980 :
2981 0 : case wkbTriangle:
2982 0 : if (b3D && bMeasured)
2983 0 : return "3D Measured Triangle";
2984 0 : else if (b3D)
2985 0 : return "3D Triangle";
2986 0 : else if (bMeasured)
2987 0 : return "Measured Triangle";
2988 : else
2989 0 : return "Triangle";
2990 :
2991 0 : case wkbPolyhedralSurface:
2992 0 : if (b3D && bMeasured)
2993 0 : return "3D Measured PolyhedralSurface";
2994 0 : else if (b3D)
2995 0 : return "3D PolyhedralSurface";
2996 0 : else if (bMeasured)
2997 0 : return "Measured PolyhedralSurface";
2998 : else
2999 0 : return "PolyhedralSurface";
3000 :
3001 2 : case wkbTIN:
3002 2 : if (b3D && bMeasured)
3003 0 : return "3D Measured TIN";
3004 2 : else if (b3D)
3005 0 : return "3D TIN";
3006 2 : else if (bMeasured)
3007 0 : return "Measured TIN";
3008 : else
3009 2 : return "TIN";
3010 :
3011 112 : case wkbNone:
3012 112 : return "None";
3013 :
3014 1 : default:
3015 : {
3016 1 : return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
3017 : }
3018 : }
3019 : }
3020 :
3021 : /************************************************************************/
3022 : /* OGRMergeGeometryTypes() */
3023 : /************************************************************************/
3024 :
3025 : /**
3026 : * \brief Find common geometry type.
3027 : *
3028 : * Given two geometry types, find the most specific common
3029 : * type. Normally used repeatedly with the geometries in a
3030 : * layer to try and establish the most specific geometry type
3031 : * that can be reported for the layer.
3032 : *
3033 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
3034 : * geometry types with nothing in common but the base geometry
3035 : * type. wkbNone should be used to indicate that no geometries
3036 : * have been encountered yet, and means the first geometry
3037 : * encountered will establish the preliminary type.
3038 : *
3039 : * @param eMain the first input geometry type.
3040 : * @param eExtra the second input geometry type.
3041 : *
3042 : * @return the merged geometry type.
3043 : */
3044 :
3045 0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
3046 : OGRwkbGeometryType eExtra)
3047 :
3048 : {
3049 0 : return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
3050 : }
3051 :
3052 : /**
3053 : * \brief Find common geometry type.
3054 : *
3055 : * Given two geometry types, find the most specific common
3056 : * type. Normally used repeatedly with the geometries in a
3057 : * layer to try and establish the most specific geometry type
3058 : * that can be reported for the layer.
3059 : *
3060 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
3061 : * geometry types with nothing in common but the base geometry
3062 : * type. wkbNone should be used to indicate that no geometries
3063 : * have been encountered yet, and means the first geometry
3064 : * encountered will establish the preliminary type.
3065 : *
3066 : * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
3067 : * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
3068 : * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
3069 : * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
3070 : *
3071 : * @param eMain the first input geometry type.
3072 : * @param eExtra the second input geometry type.
3073 : * @param bAllowPromotingToCurves determine if promotion to curve type
3074 : * must be done.
3075 : *
3076 : * @return the merged geometry type.
3077 : *
3078 : */
3079 :
3080 585 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
3081 : OGRwkbGeometryType eExtra,
3082 : int bAllowPromotingToCurves)
3083 :
3084 : {
3085 585 : OGRwkbGeometryType eFMain = wkbFlatten(eMain);
3086 585 : OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
3087 :
3088 585 : const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
3089 585 : const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
3090 :
3091 585 : if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
3092 17 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3093 :
3094 568 : if (eFMain == wkbNone)
3095 2 : return eExtra;
3096 :
3097 566 : if (eFExtra == wkbNone)
3098 0 : return eMain;
3099 :
3100 566 : if (eFMain == eFExtra)
3101 : {
3102 544 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3103 : }
3104 :
3105 22 : if (bAllowPromotingToCurves)
3106 : {
3107 22 : if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
3108 5 : return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
3109 : }
3110 :
3111 : // One is subclass of the other one
3112 17 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3113 : {
3114 4 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3115 : }
3116 13 : else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3117 : {
3118 6 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3119 : }
3120 :
3121 7 : if (OGR_GT_GetSingle(eFMain) == eFExtra)
3122 : {
3123 4 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3124 : }
3125 3 : else if (OGR_GT_GetSingle(eFExtra) == eFMain)
3126 : {
3127 1 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3128 : }
3129 :
3130 : // Nothing apparently in common.
3131 2 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3132 : }
3133 :
3134 : /**
3135 : * \fn void OGRGeometry::flattenTo2D();
3136 : *
3137 : * \brief Convert geometry to strictly 2D.
3138 : * In a sense this converts all Z coordinates
3139 : * to 0.0.
3140 : *
3141 : * This method is the same as the C function OGR_G_FlattenTo2D().
3142 : */
3143 :
3144 : /************************************************************************/
3145 : /* OGR_G_FlattenTo2D() */
3146 : /************************************************************************/
3147 : /**
3148 : * \brief Convert geometry to strictly 2D.
3149 : * In a sense this converts all Z coordinates
3150 : * to 0.0.
3151 : *
3152 : * This function is the same as the CPP method OGRGeometry::flattenTo2D().
3153 : *
3154 : * @param hGeom handle on the geometry to convert.
3155 : */
3156 :
3157 31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
3158 :
3159 : {
3160 31 : OGRGeometry::FromHandle(hGeom)->flattenTo2D();
3161 31 : }
3162 :
3163 : /************************************************************************/
3164 : /* exportToGML() */
3165 : /************************************************************************/
3166 :
3167 : /**
3168 : * \fn char *OGRGeometry::exportToGML( const char* const *
3169 : * papszOptions = NULL ) const;
3170 : *
3171 : * \brief Convert a geometry into GML format.
3172 : *
3173 : * The GML geometry is expressed directly in terms of GML basic data
3174 : * types assuming the this is available in the gml namespace. The returned
3175 : * string should be freed with CPLFree() when no longer required.
3176 : *
3177 : * The supported options are :
3178 : * <ul>
3179 : * <li> FORMAT=GML2/GML3/GML32.
3180 : * If not set, it will default to GML 2.1.2 output.
3181 : * </li>
3182 : * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
3183 : * To use gml:Curve element for linestrings.
3184 : * Otherwise gml:LineString will be used .
3185 : * </li>
3186 : * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
3187 : * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES.
3188 : * If YES, SRS with EPSG authority will be written with the
3189 : * "urn:ogc:def:crs:EPSG::" prefix.
3190 : * In the case the SRS should be treated as lat/long or
3191 : * northing/easting, then the function will take care of coordinate order
3192 : * swapping if the data axis to CRS axis mapping indicates it.
3193 : * If set to NO, SRS with EPSG authority will be written with the "EPSG:"
3194 : * prefix, even if they are in lat/long order.
3195 : * </li>
3196 : * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
3197 : * Defaults to OGC_URN. If SHORT, then srsName will be in
3198 : * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
3199 : * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
3200 : * then srsName will be in the form
3201 : * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
3202 : * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
3203 : * or northing/easting, then the function will take care of coordinate
3204 : * order swapping if the data axis to CRS axis mapping indicates it.
3205 : * </li>
3206 : * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
3207 : * top-level geometry element with the provided value.
3208 : * Required for GML 3.2 compatibility.
3209 : * </li>
3210 : * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
3211 : * FORMAT=GML3/GML32) Default to POSLIST.
3212 : * For 2.5D geometries, define the location where to attach the
3213 : * srsDimension attribute.
3214 : * There are diverging implementations. Some put in on the
3215 : * <gml:posList> element, other on the top geometry element.
3216 : * </li>
3217 : * <li> NAMESPACE_DECL=YES/NO. If set to YES,
3218 : * xmlns:gml="http://www.opengis.net/gml" will be added to the root node
3219 : * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
3220 : * </li>
3221 : * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
3222 : * Resolution for the coordinate precision of the X and Y coordinates.
3223 : * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
3224 : * to 5 decimal digits. 0 for the default behavior.
3225 : * </li>
3226 : * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
3227 : * Resolution for the coordinate precision of the Z coordinates.
3228 : * Expressed in the units of the Z axis of the SRS.
3229 : * 0 for the default behavior.
3230 : * </li>
3231 : * </ul>
3232 : *
3233 : * This method is the same as the C function OGR_G_ExportToGMLEx().
3234 : *
3235 : * @param papszOptions NULL-terminated list of options.
3236 : * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
3237 : */
3238 :
3239 265 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
3240 : {
3241 265 : return OGR_G_ExportToGMLEx(
3242 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3243 265 : const_cast<char **>(papszOptions));
3244 : }
3245 :
3246 : /************************************************************************/
3247 : /* exportToKML() */
3248 : /************************************************************************/
3249 :
3250 : /**
3251 : * \fn char *OGRGeometry::exportToKML() const;
3252 : *
3253 : * \brief Convert a geometry into KML format.
3254 : *
3255 : * The returned string should be freed with CPLFree() when no longer required.
3256 : *
3257 : * This method is the same as the C function OGR_G_ExportToKML().
3258 : *
3259 : * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
3260 : */
3261 :
3262 0 : char *OGRGeometry::exportToKML() const
3263 : {
3264 0 : return OGR_G_ExportToKML(
3265 0 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
3266 : }
3267 :
3268 : /************************************************************************/
3269 : /* exportToJson() */
3270 : /************************************************************************/
3271 :
3272 : /**
3273 : * \fn char *OGRGeometry::exportToJson() const;
3274 : *
3275 : * \brief Convert a geometry into GeoJSON format.
3276 : *
3277 : * The returned string should be freed with CPLFree() when no longer required.
3278 : *
3279 : * The following options are supported :
3280 : * <ul>
3281 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
3282 : * (added in GDAL 3.9)</li>
3283 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
3284 : * (added in GDAL 3.9)</li>
3285 : * </ul>
3286 : *
3287 : * This method is the same as the C function OGR_G_ExportToJson().
3288 : *
3289 : * @param papszOptions Null terminated list of options, or null (added in 3.9)
3290 : * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
3291 : */
3292 :
3293 43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
3294 : {
3295 43 : OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3296 43 : return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3297 43 : const_cast<char **>(papszOptions));
3298 : }
3299 :
3300 : /************************************************************************/
3301 : /* OGRSetGenerate_DB2_V72_BYTE_ORDER() */
3302 : /************************************************************************/
3303 :
3304 : /**
3305 : * \brief Special entry point to enable the hack for generating DB2 V7.2 style
3306 : * WKB.
3307 : *
3308 : * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
3309 : * order in WKB. This entry point is used to turn on or off the generation of
3310 : * such WKB.
3311 : */
3312 4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
3313 :
3314 : {
3315 : #if defined(HACK_FOR_IBM_DB2_V72)
3316 4 : OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
3317 4 : return OGRERR_NONE;
3318 : #else
3319 : if (bGenerate_DB2_V72_BYTE_ORDER)
3320 : return OGRERR_FAILURE;
3321 : else
3322 : return OGRERR_NONE;
3323 : #endif
3324 : }
3325 :
3326 : /************************************************************************/
3327 : /* OGRGetGenerate_DB2_V72_BYTE_ORDER() */
3328 : /* */
3329 : /* This is a special entry point to get the value of static flag */
3330 : /* OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER. */
3331 : /************************************************************************/
3332 0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
3333 : {
3334 0 : return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
3335 : }
3336 :
3337 : /************************************************************************/
3338 : /* createGEOSContext() */
3339 : /************************************************************************/
3340 :
3341 : /** Create a new GEOS context.
3342 : * @return a new GEOS context (to be freed with freeGEOSContext())
3343 : */
3344 86741 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
3345 : {
3346 : #ifndef HAVE_GEOS
3347 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3348 : return nullptr;
3349 : #else
3350 86741 : return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
3351 : #endif
3352 : }
3353 :
3354 : /************************************************************************/
3355 : /* freeGEOSContext() */
3356 : /************************************************************************/
3357 :
3358 : /** Destroy a GEOS context.
3359 : * @param hGEOSCtxt GEOS context
3360 : */
3361 84068 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
3362 : {
3363 : (void)hGEOSCtxt;
3364 : #ifdef HAVE_GEOS
3365 84068 : if (hGEOSCtxt != nullptr)
3366 : {
3367 84068 : finishGEOS_r(hGEOSCtxt);
3368 : }
3369 : #endif
3370 84068 : }
3371 : #ifdef HAVE_GEOS
3372 :
3373 : /************************************************************************/
3374 : /* canConvertToMultiPolygon() */
3375 : /************************************************************************/
3376 :
3377 155 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
3378 : {
3379 686 : for (const auto *poSubGeom : *poGC)
3380 : {
3381 : const OGRwkbGeometryType eSubGeomType =
3382 613 : wkbFlatten(poSubGeom->getGeometryType());
3383 613 : if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
3384 423 : eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
3385 : {
3386 82 : return false;
3387 : }
3388 : }
3389 :
3390 73 : return true;
3391 : }
3392 :
3393 : /************************************************************************/
3394 : /* GEOSWarningSilencer */
3395 : /************************************************************************/
3396 :
3397 : /** Class that can be used to silence GEOS messages while in-scope. */
3398 : class GEOSWarningSilencer
3399 : {
3400 : public:
3401 152 : explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
3402 152 : : m_poContext(poContext)
3403 : {
3404 152 : GEOSContext_setErrorHandler_r(m_poContext, nullptr);
3405 152 : GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
3406 152 : }
3407 :
3408 152 : ~GEOSWarningSilencer()
3409 152 : {
3410 152 : GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
3411 152 : GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
3412 152 : }
3413 :
3414 : CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
3415 :
3416 : private:
3417 : GEOSContextHandle_t m_poContext{nullptr};
3418 : };
3419 :
3420 : /************************************************************************/
3421 : /* repairForGEOS() */
3422 : /************************************************************************/
3423 :
3424 : /** Modify an OGRGeometry so that it can be converted into GEOS.
3425 : * Modifications include closing unclosed rings and adding redundant vertices
3426 : * to reach minimum point limits in GEOS.
3427 : *
3428 : * It is assumed that the input is a non-curved type that can be
3429 : * represented in GEOS.
3430 : *
3431 : * @param poGeom the geometry to modify
3432 : * @return an OGRGeometry that can be converted to GEOS using WKB
3433 : */
3434 22 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
3435 : {
3436 : #if GEOS_VERSION_MAJOR >= 3 || \
3437 : (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
3438 : static constexpr int MIN_RING_POINTS = 3;
3439 : #else
3440 : static constexpr int MIN_RING_POINTS = 4;
3441 : #endif
3442 :
3443 22 : const auto eType = wkbFlatten(poGeom->getGeometryType());
3444 :
3445 22 : if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
3446 : {
3447 4 : std::unique_ptr<OGRGeometryCollection> poRet;
3448 4 : if (eType == wkbGeometryCollection)
3449 : {
3450 2 : poRet = std::make_unique<OGRGeometryCollection>();
3451 : }
3452 2 : else if (eType == wkbMultiPolygon)
3453 : {
3454 2 : poRet = std::make_unique<OGRMultiPolygon>();
3455 : }
3456 0 : else if (eType == wkbMultiLineString)
3457 : {
3458 0 : poRet = std::make_unique<OGRMultiLineString>();
3459 : }
3460 0 : else if (eType == wkbMultiPoint)
3461 : {
3462 0 : poRet = std::make_unique<OGRMultiPoint>();
3463 : }
3464 : else
3465 : {
3466 0 : CPLError(CE_Failure, CPLE_AppDefined,
3467 : "Unexpected geometry type: %s",
3468 : OGRGeometryTypeToName(eType));
3469 0 : return nullptr;
3470 : }
3471 :
3472 4 : const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
3473 12 : for (const auto *poSubGeomIn : *poColl)
3474 : {
3475 8 : std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
3476 8 : poRet->addGeometry(std::move(poSubGeom));
3477 : }
3478 :
3479 4 : return poRet;
3480 : }
3481 :
3482 18 : if (eType == wkbPoint)
3483 : {
3484 0 : return std::unique_ptr<OGRGeometry>(poGeom->clone());
3485 : }
3486 18 : if (eType == wkbLineString)
3487 : {
3488 : std::unique_ptr<OGRLineString> poLineString(
3489 4 : poGeom->toLineString()->clone());
3490 2 : if (poLineString->getNumPoints() == 1)
3491 : {
3492 4 : OGRPoint oPoint;
3493 2 : poLineString->getPoint(0, &oPoint);
3494 2 : poLineString->addPoint(&oPoint);
3495 : }
3496 2 : return poLineString;
3497 : }
3498 16 : if (eType == wkbPolygon)
3499 : {
3500 32 : std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
3501 16 : poPolygon->closeRings();
3502 :
3503 : // make sure rings have enough points
3504 34 : for (auto *poRing : *poPolygon)
3505 : {
3506 22 : while (poRing->getNumPoints() < MIN_RING_POINTS)
3507 : {
3508 8 : OGRPoint oPoint;
3509 4 : poRing->getPoint(0, &oPoint);
3510 4 : poRing->addPoint(&oPoint);
3511 : }
3512 : }
3513 :
3514 16 : return poPolygon;
3515 : }
3516 :
3517 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
3518 : OGRGeometryTypeToName(eType));
3519 0 : return nullptr;
3520 : }
3521 :
3522 : /************************************************************************/
3523 : /* convertToGEOSGeom() */
3524 : /************************************************************************/
3525 :
3526 243553 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3527 : const OGRGeometry *poGeom)
3528 : {
3529 243553 : GEOSGeom hGeom = nullptr;
3530 243553 : const size_t nDataSize = poGeom->WkbSize();
3531 : unsigned char *pabyData =
3532 243553 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3533 : #if GEOS_VERSION_MAJOR > 3 || \
3534 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3535 243553 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3536 : #else
3537 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3538 : #endif
3539 243553 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3540 : {
3541 242537 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3542 : }
3543 243553 : CPLFree(pabyData);
3544 :
3545 243553 : return hGeom;
3546 : }
3547 : #endif
3548 :
3549 : /************************************************************************/
3550 : /* exportToGEOS() */
3551 : /************************************************************************/
3552 :
3553 : /** Returns a GEOSGeom object corresponding to the geometry.
3554 : *
3555 : * @param hGEOSCtxt GEOS context
3556 : * @param bRemoveEmptyParts Whether empty parts of the geometry should be
3557 : * removed before exporting to GEOS (GDAL >= 3.10)
3558 : * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
3559 : * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
3560 : * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
3561 : * @return a GEOSGeom object corresponding to the geometry (to be freed with
3562 : * GEOSGeom_destroy_r()), or NULL in case of error
3563 : */
3564 243539 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
3565 : bool bRemoveEmptyParts,
3566 : bool bAddPointsIfNeeded) const
3567 : {
3568 : (void)hGEOSCtxt;
3569 : (void)bRemoveEmptyParts;
3570 : (void)bAddPointsIfNeeded;
3571 :
3572 : #ifndef HAVE_GEOS
3573 :
3574 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3575 : return nullptr;
3576 :
3577 : #else
3578 :
3579 243539 : if (hGEOSCtxt == nullptr)
3580 0 : return nullptr;
3581 :
3582 243539 : const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
3583 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3584 : // POINT EMPTY is exported to WKB as if it were POINT(0 0),
3585 : // so that particular case is necessary.
3586 : if (eType == wkbPoint && IsEmpty())
3587 : {
3588 : return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
3589 : }
3590 : #endif
3591 :
3592 243539 : GEOSGeom hGeom = nullptr;
3593 :
3594 243539 : std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
3595 243539 : const OGRGeometry *poGeosInput = this;
3596 :
3597 243539 : const bool bHasZ = poGeosInput->Is3D();
3598 243539 : bool bHasM = poGeosInput->IsMeasured();
3599 :
3600 243539 : if (poGeosInput->hasCurveGeometry())
3601 : {
3602 868 : poModifiedInput.reset(poGeosInput->getLinearGeometry());
3603 868 : poGeosInput = poModifiedInput.get();
3604 : }
3605 :
3606 243539 : if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
3607 : {
3608 1 : if (!poModifiedInput)
3609 : {
3610 1 : poModifiedInput.reset(poGeosInput->clone());
3611 1 : poGeosInput = poModifiedInput.get();
3612 : }
3613 1 : poModifiedInput->removeEmptyParts();
3614 : }
3615 :
3616 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3617 : // GEOS < 3.12 doesn't support M dimension
3618 : if (bHasM)
3619 : {
3620 : if (!poModifiedInput)
3621 : {
3622 : poModifiedInput.reset(poGeosInput->clone());
3623 : poGeosInput = poModifiedInput.get();
3624 : }
3625 : poModifiedInput->setMeasured(false);
3626 : bHasM = false;
3627 : }
3628 : #endif
3629 :
3630 243539 : if (eType == wkbTriangle)
3631 : {
3632 : poModifiedInput =
3633 98 : std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
3634 98 : poGeosInput = poModifiedInput.get();
3635 : }
3636 243441 : else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3637 : {
3638 844 : if (!poModifiedInput)
3639 : {
3640 844 : poModifiedInput.reset(poGeosInput->clone());
3641 : }
3642 :
3643 2532 : poModifiedInput = OGRGeometryFactory::forceTo(
3644 844 : std::move(poModifiedInput),
3645 844 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
3646 844 : poGeosInput = poModifiedInput.get();
3647 : }
3648 242752 : else if (eType == wkbGeometryCollection &&
3649 155 : CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
3650 : {
3651 73 : if (!poModifiedInput)
3652 : {
3653 71 : poModifiedInput.reset(poGeosInput->clone());
3654 : }
3655 :
3656 : // Force into a MultiPolygon, then back to a GeometryCollection.
3657 : // This gets rid of fancy types like TIN and PolyhedralSurface that
3658 : // GEOS doesn't understand and flattens nested collections.
3659 219 : poModifiedInput = OGRGeometryFactory::forceTo(
3660 73 : std::move(poModifiedInput),
3661 73 : OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
3662 219 : poModifiedInput = OGRGeometryFactory::forceTo(
3663 73 : std::move(poModifiedInput),
3664 73 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
3665 :
3666 73 : poGeosInput = poModifiedInput.get();
3667 : }
3668 :
3669 : {
3670 : // Rather than check for conditions that would prevent conversion to
3671 : // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
3672 : // conversion as-is. If the conversion fails, we don't want any
3673 : // warnings emitted; we'll repair the input and try again.
3674 0 : std::optional<GEOSWarningSilencer> oSilencer;
3675 243539 : if (bAddPointsIfNeeded)
3676 : {
3677 152 : oSilencer.emplace(hGEOSCtxt);
3678 : }
3679 :
3680 243539 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3681 : }
3682 :
3683 243539 : if (hGeom == nullptr && bAddPointsIfNeeded)
3684 : {
3685 14 : poModifiedInput = repairForGEOS(poGeosInput);
3686 14 : poGeosInput = poModifiedInput.get();
3687 :
3688 14 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3689 : }
3690 :
3691 243539 : return hGeom;
3692 :
3693 : #endif // HAVE_GEOS
3694 : }
3695 :
3696 : /************************************************************************/
3697 : /* hasCurveGeometry() */
3698 : /************************************************************************/
3699 :
3700 : /**
3701 : * \brief Returns if this geometry is or has curve geometry.
3702 : *
3703 : * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
3704 : * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
3705 : *
3706 : * If bLookForNonLinear is set to TRUE, it will be actually looked if
3707 : * the geometry or its subgeometries are or contain a non-linear
3708 : * geometry in them. In which case, if the method returns TRUE, it
3709 : * means that getLinearGeometry() would return an approximate version
3710 : * of the geometry. Otherwise, getLinearGeometry() would do a
3711 : * conversion, but with just converting container type, like
3712 : * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
3713 : * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
3714 : * conversion.
3715 : *
3716 : * This method is the same as the C function OGR_G_HasCurveGeometry().
3717 : *
3718 : * @param bLookForNonLinear set it to TRUE to check if the geometry is
3719 : * or contains a CIRCULARSTRING.
3720 : *
3721 : * @return TRUE if this geometry is or has curve geometry.
3722 : *
3723 : */
3724 :
3725 279419 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3726 : {
3727 279419 : return FALSE;
3728 : }
3729 :
3730 : /************************************************************************/
3731 : /* getLinearGeometry() */
3732 : /************************************************************************/
3733 :
3734 : /**
3735 : * \brief Return, possibly approximate, non-curve version of this geometry.
3736 : *
3737 : * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
3738 : * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
3739 : *
3740 : * The ownership of the returned geometry belongs to the caller.
3741 : *
3742 : * The reverse method is OGRGeometry::getCurveGeometry().
3743 : *
3744 : * This method is the same as the C function OGR_G_GetLinearGeometry().
3745 : *
3746 : * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
3747 : * arc, zero to use the default setting.
3748 : * @param papszOptions options as a null-terminated list of strings.
3749 : * See OGRGeometryFactory::curveToLineString() for
3750 : * valid options.
3751 : *
3752 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3753 : *
3754 : */
3755 :
3756 : OGRGeometry *
3757 90 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3758 : CPL_UNUSED const char *const *papszOptions) const
3759 : {
3760 90 : return clone();
3761 : }
3762 :
3763 : /************************************************************************/
3764 : /* getCurveGeometry() */
3765 : /************************************************************************/
3766 :
3767 : /**
3768 : * \brief Return curve version of this geometry.
3769 : *
3770 : * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
3771 : * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
3772 : * curve geometries.
3773 : *
3774 : * If the geometry has no curve portion, the returned geometry will be a clone
3775 : * of it.
3776 : *
3777 : * The ownership of the returned geometry belongs to the caller.
3778 : *
3779 : * The reverse method is OGRGeometry::getLinearGeometry().
3780 : *
3781 : * This function is the same as C function OGR_G_GetCurveGeometry().
3782 : *
3783 : * @param papszOptions options as a null-terminated list of strings.
3784 : * Unused for now. Must be set to NULL.
3785 : *
3786 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3787 : *
3788 : */
3789 :
3790 : OGRGeometry *
3791 5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
3792 : {
3793 5 : return clone();
3794 : }
3795 :
3796 : /************************************************************************/
3797 : /* Distance() */
3798 : /************************************************************************/
3799 :
3800 : /**
3801 : * \brief Compute distance between two geometries.
3802 : *
3803 : * Returns the shortest distance between the two geometries. The distance is
3804 : * expressed into the same unit as the coordinates of the geometries.
3805 : *
3806 : * This method is the same as the C function OGR_G_Distance().
3807 : *
3808 : * This method is built on the GEOS library, check it for the definition
3809 : * of the geometry operation.
3810 : * If OGR is built without the GEOS library, this method will always fail,
3811 : * issuing a CPLE_NotSupported error.
3812 : *
3813 : * @param poOtherGeom the other geometry to compare against.
3814 : *
3815 : * @return the distance between the geometries or -1 if an error occurs.
3816 : */
3817 :
3818 25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
3819 :
3820 : {
3821 25 : if (nullptr == poOtherGeom)
3822 : {
3823 0 : CPLDebug("OGR",
3824 : "OGRGeometry::Distance called with NULL geometry pointer");
3825 0 : return -1.0;
3826 : }
3827 :
3828 25 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
3829 : {
3830 : #ifndef HAVE_SFCGAL
3831 :
3832 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3833 0 : return -1.0;
3834 :
3835 : #else
3836 :
3837 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3838 : if (poThis == nullptr)
3839 : return -1.0;
3840 :
3841 : sfcgal_geometry_t *poOther =
3842 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3843 : if (poOther == nullptr)
3844 : {
3845 : sfcgal_geometry_delete(poThis);
3846 : return -1.0;
3847 : }
3848 :
3849 : const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
3850 :
3851 : sfcgal_geometry_delete(poThis);
3852 : sfcgal_geometry_delete(poOther);
3853 :
3854 : return dfDistance > 0.0 ? dfDistance : -1.0;
3855 :
3856 : #endif
3857 : }
3858 :
3859 : else
3860 : {
3861 : #ifndef HAVE_GEOS
3862 :
3863 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3864 : return -1.0;
3865 :
3866 : #else
3867 :
3868 25 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3869 : // GEOSGeom is a pointer
3870 25 : GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
3871 25 : GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
3872 :
3873 25 : int bIsErr = 0;
3874 25 : double dfDistance = 0.0;
3875 :
3876 25 : if (hThis != nullptr && hOther != nullptr)
3877 : {
3878 25 : bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
3879 : }
3880 :
3881 25 : GEOSGeom_destroy_r(hGEOSCtxt, hThis);
3882 25 : GEOSGeom_destroy_r(hGEOSCtxt, hOther);
3883 25 : freeGEOSContext(hGEOSCtxt);
3884 :
3885 25 : if (bIsErr > 0)
3886 : {
3887 25 : return dfDistance;
3888 : }
3889 :
3890 : /* Calculations error */
3891 0 : return -1.0;
3892 :
3893 : #endif /* HAVE_GEOS */
3894 : }
3895 : }
3896 :
3897 : /************************************************************************/
3898 : /* OGR_G_Distance() */
3899 : /************************************************************************/
3900 : /**
3901 : * \brief Compute distance between two geometries.
3902 : *
3903 : * Returns the shortest distance between the two geometries. The distance is
3904 : * expressed into the same unit as the coordinates of the geometries.
3905 : *
3906 : * This function is the same as the C++ method OGRGeometry::Distance().
3907 : *
3908 : * This function is built on the GEOS library, check it for the definition
3909 : * of the geometry operation.
3910 : * If OGR is built without the GEOS library, this function will always fail,
3911 : * issuing a CPLE_NotSupported error.
3912 : *
3913 : * @param hFirst the first geometry to compare against.
3914 : * @param hOther the other geometry to compare against.
3915 : *
3916 : * @return the distance between the geometries or -1 if an error occurs.
3917 : */
3918 :
3919 2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
3920 :
3921 : {
3922 2 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
3923 :
3924 4 : return OGRGeometry::FromHandle(hFirst)->Distance(
3925 4 : OGRGeometry::FromHandle(hOther));
3926 : }
3927 :
3928 : /************************************************************************/
3929 : /* Distance3D() */
3930 : /************************************************************************/
3931 :
3932 : /**
3933 : * \brief Returns the 3D distance between two geometries
3934 : *
3935 : * The distance is expressed into the same unit as the coordinates of the
3936 : * geometries.
3937 : *
3938 : * This method is built on the SFCGAL library, check it for the definition
3939 : * of the geometry operation.
3940 : * If OGR is built without the SFCGAL library, this method will always return
3941 : * -1.0
3942 : *
3943 : * This function is the same as the C function OGR_G_Distance3D().
3944 : *
3945 : * @return distance between the two geometries
3946 : */
3947 :
3948 1 : double OGRGeometry::Distance3D(
3949 : UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
3950 : {
3951 1 : if (poOtherGeom == nullptr)
3952 : {
3953 0 : CPLDebug("OGR",
3954 : "OGRTriangle::Distance3D called with NULL geometry pointer");
3955 0 : return -1.0;
3956 : }
3957 :
3958 1 : if (!(poOtherGeom->Is3D() && Is3D()))
3959 : {
3960 0 : CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
3961 : "geometry(geometries)");
3962 0 : return -1.0;
3963 : }
3964 :
3965 : #ifndef HAVE_SFCGAL
3966 :
3967 1 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3968 1 : return -1.0;
3969 :
3970 : #else
3971 :
3972 : sfcgal_init();
3973 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3974 : if (poThis == nullptr)
3975 : return -1.0;
3976 :
3977 : sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3978 : if (poOther == nullptr)
3979 : {
3980 : sfcgal_geometry_delete(poThis);
3981 : return -1.0;
3982 : }
3983 :
3984 : const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
3985 :
3986 : sfcgal_geometry_delete(poThis);
3987 : sfcgal_geometry_delete(poOther);
3988 :
3989 : return dfDistance > 0 ? dfDistance : -1.0;
3990 :
3991 : #endif
3992 : }
3993 :
3994 : /************************************************************************/
3995 : /* OGR_G_Distance3D() */
3996 : /************************************************************************/
3997 : /**
3998 : * \brief Returns the 3D distance between two geometries
3999 : *
4000 : * The distance is expressed into the same unit as the coordinates of the
4001 : * geometries.
4002 : *
4003 : * This method is built on the SFCGAL library, check it for the definition
4004 : * of the geometry operation.
4005 : * If OGR is built without the SFCGAL library, this method will always return
4006 : * -1.0
4007 : *
4008 : * This function is the same as the C++ method OGRGeometry::Distance3D().
4009 : *
4010 : * @param hFirst the first geometry to compare against.
4011 : * @param hOther the other geometry to compare against.
4012 : * @return distance between the two geometries
4013 : *
4014 : * @return the distance between the geometries or -1 if an error occurs.
4015 : */
4016 :
4017 1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
4018 :
4019 : {
4020 1 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
4021 :
4022 2 : return OGRGeometry::FromHandle(hFirst)->Distance3D(
4023 2 : OGRGeometry::FromHandle(hOther));
4024 : }
4025 :
4026 : /************************************************************************/
4027 : /* OGRGeometryRebuildCurves() */
4028 : /************************************************************************/
4029 :
4030 : #ifdef HAVE_GEOS
4031 5099 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
4032 : const OGRGeometry *poOtherGeom,
4033 : OGRGeometry *poOGRProduct)
4034 : {
4035 10198 : if (poOGRProduct != nullptr &&
4036 10112 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
4037 5013 : (poGeom->hasCurveGeometry(true) ||
4038 3858 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
4039 : {
4040 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
4041 8 : delete poOGRProduct;
4042 8 : return poCurveGeom;
4043 : }
4044 5091 : return poOGRProduct;
4045 : }
4046 :
4047 : /************************************************************************/
4048 : /* BuildGeometryFromGEOS() */
4049 : /************************************************************************/
4050 :
4051 4949 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
4052 : GEOSGeom hGeosProduct,
4053 : const OGRGeometry *poSelf,
4054 : const OGRGeometry *poOtherGeom)
4055 : {
4056 4949 : OGRGeometry *poOGRProduct = nullptr;
4057 4949 : if (hGeosProduct != nullptr)
4058 : {
4059 : poOGRProduct =
4060 4947 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
4061 4947 : if (poOGRProduct != nullptr &&
4062 12708 : poSelf->getSpatialReference() != nullptr &&
4063 2814 : (poOtherGeom == nullptr ||
4064 2814 : (poOtherGeom->getSpatialReference() != nullptr &&
4065 2677 : poOtherGeom->getSpatialReference()->IsSame(
4066 : poSelf->getSpatialReference()))))
4067 : {
4068 2733 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
4069 : }
4070 : poOGRProduct =
4071 4947 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
4072 4947 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
4073 : }
4074 4949 : return poOGRProduct;
4075 : }
4076 :
4077 : /************************************************************************/
4078 : /* BuildGeometryFromTwoGeoms() */
4079 : /************************************************************************/
4080 :
4081 3932 : static OGRGeometry *BuildGeometryFromTwoGeoms(
4082 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4083 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
4084 : const GEOSGeometry *,
4085 : const GEOSGeometry *))
4086 : {
4087 3932 : OGRGeometry *poOGRProduct = nullptr;
4088 :
4089 3932 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4090 3932 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4091 3932 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4092 3932 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4093 : {
4094 : GEOSGeom hGeosProduct =
4095 3932 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
4096 :
4097 : poOGRProduct =
4098 3932 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
4099 : }
4100 3932 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4101 3932 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4102 3932 : poSelf->freeGEOSContext(hGEOSCtxt);
4103 :
4104 3932 : return poOGRProduct;
4105 : }
4106 :
4107 : /************************************************************************/
4108 : /* OGRGEOSBooleanPredicate() */
4109 : /************************************************************************/
4110 :
4111 22773 : static bool OGRGEOSBooleanPredicate(
4112 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4113 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
4114 : const GEOSGeometry *))
4115 : {
4116 22773 : bool bResult = false;
4117 :
4118 22773 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4119 22773 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4120 22773 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4121 22773 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4122 : {
4123 22218 : bResult =
4124 22218 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
4125 : }
4126 22773 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4127 22773 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4128 22773 : poSelf->freeGEOSContext(hGEOSCtxt);
4129 :
4130 22773 : return bResult;
4131 : }
4132 :
4133 : #endif // HAVE_GEOS
4134 :
4135 : /************************************************************************/
4136 : /* MakeValid() */
4137 : /************************************************************************/
4138 :
4139 : /**
4140 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4141 : *
4142 : * Already-valid geometries are cloned without further intervention
4143 : * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
4144 : * may be subject to non-significant transformations, such as duplicated point
4145 : * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
4146 : * geometry collections could be returned a single geometry. GDAL 3.10
4147 : * returns the same type of geometry).
4148 : *
4149 : * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
4150 : * post-processing step is often desired.
4151 : *
4152 : * This method is the same as the C function OGR_G_MakeValid().
4153 : *
4154 : * This function is built on the GEOS >= 3.8 library, check it for the
4155 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4156 : * library, this function will return a clone of the input geometry if it is
4157 : * valid, or NULL if it is invalid.
4158 : *
4159 : * Certain geometries cannot be read using GEOS, for example if Polygon rings
4160 : * are not closed or do not contain enough vertices. If a geometry cannot be
4161 : * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
4162 : * attempt to modify these geometries such that they can be read and
4163 : * repaired by GEOS.
4164 : *
4165 : * @param papszOptions NULL terminated list of options, or NULL. The following
4166 : * options are available:
4167 : * <ul>
4168 : * <li>METHOD=LINEWORK/STRUCTURE.
4169 : * LINEWORK is the default method, which combines all rings into a set of
4170 : * noded lines and then extracts valid polygons from that linework.
4171 : * The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
4172 : * all rings valid, then merges shells and
4173 : * subtracts holes from shells to generate valid result. Assumes that
4174 : * holes and shells are correctly categorized.</li>
4175 : * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
4176 : * NO (default): collapses are converted to empty geometries
4177 : * YES: collapses are converted to a valid geometry of lower dimension.</li>
4178 : * </ul>
4179 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4180 : *
4181 : * @since GDAL 3.0
4182 : */
4183 153 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
4184 : {
4185 : (void)papszOptions;
4186 : #ifndef HAVE_GEOS
4187 : if (IsValid())
4188 : return clone();
4189 :
4190 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4191 : return nullptr;
4192 : #else
4193 153 : if (IsSFCGALCompatible())
4194 : {
4195 0 : if (IsValid())
4196 0 : return clone();
4197 : }
4198 153 : else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
4199 : {
4200 3 : GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
4201 3 : bool bIsValid = false;
4202 3 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4203 3 : if (hGeosGeom)
4204 : {
4205 3 : bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
4206 3 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4207 : }
4208 3 : freeGEOSContext(hGEOSCtxt);
4209 3 : if (bIsValid)
4210 1 : return clone();
4211 : }
4212 :
4213 152 : const bool bStructureMethod = EQUAL(
4214 : CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
4215 152 : CPL_IGNORE_RET_VAL(bStructureMethod);
4216 : #if !(GEOS_VERSION_MAJOR > 3 || \
4217 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
4218 : if (bStructureMethod)
4219 : {
4220 : CPLError(CE_Failure, CPLE_NotSupported,
4221 : "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
4222 : return nullptr;
4223 : }
4224 : #endif
4225 :
4226 152 : OGRGeometry *poOGRProduct = nullptr;
4227 :
4228 152 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4229 152 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
4230 152 : if (hGeosGeom != nullptr)
4231 : {
4232 : GEOSGeom hGEOSRet;
4233 : #if GEOS_VERSION_MAJOR > 3 || \
4234 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4235 152 : if (bStructureMethod)
4236 : {
4237 : GEOSMakeValidParams *params =
4238 15 : GEOSMakeValidParams_create_r(hGEOSCtxt);
4239 15 : CPLAssert(params);
4240 15 : GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
4241 : GEOS_MAKE_VALID_STRUCTURE);
4242 15 : GEOSMakeValidParams_setKeepCollapsed_r(
4243 : hGEOSCtxt, params,
4244 15 : CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
4245 15 : hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
4246 15 : GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
4247 : }
4248 : else
4249 : #endif
4250 : {
4251 137 : hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
4252 : }
4253 152 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4254 :
4255 152 : if (hGEOSRet != nullptr)
4256 : {
4257 : poOGRProduct =
4258 152 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
4259 152 : if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
4260 6 : poOGRProduct->assignSpatialReference(getSpatialReference());
4261 : poOGRProduct =
4262 152 : OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
4263 152 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
4264 :
4265 : #if GEOS_VERSION_MAJOR > 3 || \
4266 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4267 : // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
4268 : // if the input is a multiple geometry
4269 152 : if (poOGRProduct && bStructureMethod &&
4270 310 : OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
4271 6 : !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
4272 : wkbGeometryCollection))
4273 : {
4274 6 : poOGRProduct = OGRGeometryFactory::forceTo(
4275 6 : std::unique_ptr<OGRGeometry>(poOGRProduct),
4276 3 : getGeometryType())
4277 3 : .release();
4278 : }
4279 : #endif
4280 : }
4281 : }
4282 152 : freeGEOSContext(hGEOSCtxt);
4283 :
4284 152 : return poOGRProduct;
4285 : #endif
4286 : }
4287 :
4288 : /************************************************************************/
4289 : /* OGR_G_MakeValid() */
4290 : /************************************************************************/
4291 :
4292 : /**
4293 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4294 : *
4295 : * Already-valid geometries are cloned without further intervention.
4296 : *
4297 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4298 : *
4299 : * This function is built on the GEOS >= 3.8 library, check it for the
4300 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4301 : * library, this function will return a clone of the input geometry if it is
4302 : * valid, or NULL if it is invalid
4303 : *
4304 : * @param hGeom The Geometry to make valid.
4305 : *
4306 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4307 : * or NULL if an error occurs.
4308 : *
4309 : * @since GDAL 3.0
4310 : */
4311 :
4312 0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
4313 :
4314 : {
4315 0 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
4316 :
4317 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
4318 : }
4319 :
4320 : /************************************************************************/
4321 : /* OGR_G_MakeValidEx() */
4322 : /************************************************************************/
4323 :
4324 : /**
4325 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4326 : *
4327 : * Already-valid geometries are cloned without further intervention.
4328 : *
4329 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4330 : *
4331 : * See documentation of that method for possible options.
4332 : *
4333 : * @param hGeom The Geometry to make valid.
4334 : * @param papszOptions Options.
4335 : *
4336 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4337 : * or NULL if an error occurs.
4338 : *
4339 : * @since GDAL 3.4
4340 : */
4341 :
4342 25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
4343 :
4344 : {
4345 25 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
4346 :
4347 25 : return OGRGeometry::ToHandle(
4348 50 : OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
4349 : }
4350 :
4351 : /************************************************************************/
4352 : /* Normalize() */
4353 : /************************************************************************/
4354 :
4355 : /**
4356 : * \brief Attempts to bring geometry into normalized/canonical form.
4357 : *
4358 : * This method is the same as the C function OGR_G_Normalize().
4359 : *
4360 : * This function is built on the GEOS library; check it for the definition
4361 : * of the geometry operation.
4362 : * If OGR is built without the GEOS library, this function will always fail,
4363 : * issuing a CPLE_NotSupported error.
4364 : *
4365 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4366 : *
4367 : * @since GDAL 3.3
4368 : */
4369 51 : OGRGeometry *OGRGeometry::Normalize() const
4370 : {
4371 : #ifndef HAVE_GEOS
4372 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4373 : return nullptr;
4374 : #else
4375 51 : OGRGeometry *poOGRProduct = nullptr;
4376 :
4377 51 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4378 51 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4379 51 : if (hGeosGeom != nullptr)
4380 : {
4381 :
4382 51 : int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
4383 :
4384 51 : if (hGEOSRet == 0)
4385 : {
4386 : poOGRProduct =
4387 51 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
4388 : }
4389 : else
4390 : {
4391 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4392 : }
4393 : }
4394 51 : freeGEOSContext(hGEOSCtxt);
4395 :
4396 51 : return poOGRProduct;
4397 : #endif
4398 : }
4399 :
4400 : /************************************************************************/
4401 : /* OGR_G_Normalize() */
4402 : /************************************************************************/
4403 :
4404 : /**
4405 : * \brief Attempts to bring geometry into normalized/canonical form.
4406 : *
4407 : * This function is the same as the C++ method OGRGeometry::Normalize().
4408 : *
4409 : * This function is built on the GEOS library; check it for the definition
4410 : * of the geometry operation.
4411 : * If OGR is built without the GEOS library, this function will always fail,
4412 : * issuing a CPLE_NotSupported error.
4413 : * @param hGeom The Geometry to normalize.
4414 : *
4415 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4416 : * or NULL if an error occurs.
4417 : *
4418 : * @since GDAL 3.3
4419 : */
4420 :
4421 21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
4422 :
4423 : {
4424 21 : VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
4425 :
4426 21 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
4427 : }
4428 :
4429 : /************************************************************************/
4430 : /* ConvexHull() */
4431 : /************************************************************************/
4432 :
4433 : /**
4434 : * \brief Compute convex hull.
4435 : *
4436 : * A new geometry object is created and returned containing the convex
4437 : * hull of the geometry on which the method is invoked.
4438 : *
4439 : * This method is the same as the C function OGR_G_ConvexHull().
4440 : *
4441 : * This method is built on the GEOS library, check it for the definition
4442 : * of the geometry operation.
4443 : * If OGR is built without the GEOS library, this method will always fail,
4444 : * issuing a CPLE_NotSupported error.
4445 : *
4446 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4447 : */
4448 :
4449 6 : OGRGeometry *OGRGeometry::ConvexHull() const
4450 :
4451 : {
4452 6 : if (IsSFCGALCompatible())
4453 : {
4454 : #ifndef HAVE_SFCGAL
4455 :
4456 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4457 0 : return nullptr;
4458 :
4459 : #else
4460 :
4461 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4462 : if (poThis == nullptr)
4463 : return nullptr;
4464 :
4465 : sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
4466 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4467 : if (h_prodGeom)
4468 : h_prodGeom->assignSpatialReference(getSpatialReference());
4469 :
4470 : sfcgal_geometry_delete(poThis);
4471 : sfcgal_geometry_delete(poRes);
4472 :
4473 : return h_prodGeom;
4474 :
4475 : #endif
4476 : }
4477 :
4478 : else
4479 : {
4480 : #ifndef HAVE_GEOS
4481 :
4482 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4483 : return nullptr;
4484 :
4485 : #else
4486 :
4487 6 : OGRGeometry *poOGRProduct = nullptr;
4488 :
4489 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4490 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4491 6 : if (hGeosGeom != nullptr)
4492 : {
4493 6 : GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
4494 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4495 :
4496 : poOGRProduct =
4497 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4498 : }
4499 6 : freeGEOSContext(hGEOSCtxt);
4500 :
4501 6 : return poOGRProduct;
4502 :
4503 : #endif /* HAVE_GEOS */
4504 : }
4505 : }
4506 :
4507 : /************************************************************************/
4508 : /* OGR_G_ConvexHull() */
4509 : /************************************************************************/
4510 : /**
4511 : * \brief Compute convex hull.
4512 : *
4513 : * A new geometry object is created and returned containing the convex
4514 : * hull of the geometry on which the method is invoked.
4515 : *
4516 : * This function is the same as the C++ method OGRGeometry::ConvexHull().
4517 : *
4518 : * This function is built on the GEOS library, check it for the definition
4519 : * of the geometry operation.
4520 : * If OGR is built without the GEOS library, this function will always fail,
4521 : * issuing a CPLE_NotSupported error.
4522 : *
4523 : * @param hTarget The Geometry to calculate the convex hull of.
4524 : *
4525 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4526 : * or NULL if an error occurs.
4527 : */
4528 :
4529 1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
4530 :
4531 : {
4532 1 : VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
4533 :
4534 1 : return OGRGeometry::ToHandle(
4535 1 : OGRGeometry::FromHandle(hTarget)->ConvexHull());
4536 : }
4537 :
4538 : /************************************************************************/
4539 : /* ConcaveHull() */
4540 : /************************************************************************/
4541 :
4542 : /**
4543 : * \brief Compute the concave hull of a geometry.
4544 : *
4545 : * The concave hull is fully contained within the convex hull and also
4546 : * contains all the points of the input, but in a smaller area.
4547 : * The area ratio is the ratio of the area of the convex hull and the concave
4548 : * hull. Frequently used to convert a multi-point into a polygonal area.
4549 : * that contains all the points in the input Geometry.
4550 : *
4551 : * A new geometry object is created and returned containing the concave
4552 : * hull of the geometry on which the method is invoked.
4553 : *
4554 : * This method is the same as the C function OGR_G_ConcaveHull().
4555 : *
4556 : * This method is built on the GEOS >= 3.11 library
4557 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4558 : * fail, issuing a CPLE_NotSupported error.
4559 : *
4560 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4561 : * @param bAllowHoles Whether holes are allowed.
4562 : *
4563 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4564 : *
4565 : * @since GDAL 3.6
4566 : * @see OGRGeometry::ConcaveHullOfPolygons()
4567 : */
4568 :
4569 8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
4570 : {
4571 : #ifndef HAVE_GEOS
4572 : (void)dfRatio;
4573 : (void)bAllowHoles;
4574 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4575 : return nullptr;
4576 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4577 : (void)dfRatio;
4578 : (void)bAllowHoles;
4579 : CPLError(CE_Failure, CPLE_NotSupported,
4580 : "GEOS 3.11 or later needed for ConcaveHull.");
4581 : return nullptr;
4582 : #else
4583 8 : OGRGeometry *poOGRProduct = nullptr;
4584 :
4585 8 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4586 8 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4587 8 : if (hGeosGeom != nullptr)
4588 : {
4589 : GEOSGeom hGeosHull =
4590 8 : GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
4591 8 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4592 :
4593 : poOGRProduct =
4594 8 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4595 : }
4596 8 : freeGEOSContext(hGEOSCtxt);
4597 :
4598 8 : return poOGRProduct;
4599 : #endif /* HAVE_GEOS */
4600 : }
4601 :
4602 : /************************************************************************/
4603 : /* OGR_G_ConcaveHull() */
4604 : /************************************************************************/
4605 : /**
4606 : * \brief Compute the concave hull of a geometry.
4607 : *
4608 : * The concave hull is fully contained within the convex hull and also
4609 : * contains all the points of the input, but in a smaller area.
4610 : * The area ratio is the ratio of the area of the convex hull and the concave
4611 : * hull. Frequently used to convert a multi-point into a polygonal area.
4612 : * that contains all the points in the input Geometry.
4613 : *
4614 : * A new geometry object is created and returned containing the convex
4615 : * hull of the geometry on which the function is invoked.
4616 : *
4617 : * This function is the same as the C++ method OGRGeometry::ConcaveHull().
4618 : *
4619 : * This function is built on the GEOS >= 3.11 library
4620 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4621 : * fail, issuing a CPLE_NotSupported error.
4622 : *
4623 : * @param hTarget The Geometry to calculate the concave hull of.
4624 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4625 : * @param bAllowHoles Whether holes are allowed.
4626 : *
4627 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4628 : * or NULL if an error occurs.
4629 : *
4630 : * @since GDAL 3.6
4631 : * @see OGR_G_ConcaveHullOfPolygons()
4632 : */
4633 :
4634 2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
4635 : bool bAllowHoles)
4636 :
4637 : {
4638 2 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
4639 :
4640 2 : return OGRGeometry::ToHandle(
4641 2 : OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
4642 : }
4643 :
4644 : /************************************************************************/
4645 : /* ConcaveHullOfPolygons() */
4646 : /************************************************************************/
4647 :
4648 : /**
4649 : * \brief Compute the concave hull of a set of polygons, respecting
4650 : * the polygons as constraints.
4651 : *
4652 : * A concave hull is a (possibly) non-convex polygon containing all the input
4653 : * polygons.
4654 : * The computed hull "fills the gap" between the polygons,
4655 : * and does not intersect their interior.
4656 : * A set of polygons has a sequence of hulls of increasing concaveness,
4657 : * determined by a numeric target parameter.
4658 : *
4659 : * The concave hull is constructed by removing the longest outer edges
4660 : * of the Delaunay Triangulation of the space between the polygons,
4661 : * until the target criterion parameter is reached.
4662 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4663 : * between polygons to be no larger than this value.
4664 : * This can be expressed as a ratio between the lengths of the longest and
4665 : * shortest edges.
4666 : *
4667 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4668 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4669 : * for more details.
4670 : *
4671 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4672 : * be non-overlapping).
4673 : *
4674 : * A new geometry object is created and returned containing the concave
4675 : * hull of the geometry on which the method is invoked.
4676 : *
4677 : * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
4678 : *
4679 : * This method is built on the GEOS >= 3.11 library
4680 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4681 : * fail, issuing a CPLE_NotSupported error.
4682 : *
4683 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4684 : * difference between the longest and shortest edge lengths
4685 : * between the polygons.
4686 : * This normalizes the Maximum Edge Length to be scale-free.
4687 : * A value of 1 produces the convex hull; a value of 0 produces
4688 : * the original polygons.
4689 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4690 : * polygons.
4691 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4692 : *
4693 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4694 : *
4695 : * @since GDAL 3.13
4696 : * @see OGRGeometry::ConcaveHull()
4697 : */
4698 :
4699 13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
4700 : bool bIsTight,
4701 : bool bAllowHoles) const
4702 : {
4703 : #ifndef HAVE_GEOS
4704 : (void)dfLengthRatio;
4705 : (void)bIsTight;
4706 : (void)bAllowHoles;
4707 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4708 : return nullptr;
4709 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4710 : (void)dfLengthRatio;
4711 : (void)bIsTight;
4712 : (void)bAllowHoles;
4713 : CPLError(CE_Failure, CPLE_NotSupported,
4714 : "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
4715 : return nullptr;
4716 : #else
4717 13 : OGRGeometry *poOGRProduct = nullptr;
4718 :
4719 13 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4720 13 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4721 13 : if (hGeosGeom != nullptr)
4722 : {
4723 13 : GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
4724 : hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
4725 13 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4726 :
4727 : poOGRProduct =
4728 13 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4729 : }
4730 13 : freeGEOSContext(hGEOSCtxt);
4731 :
4732 13 : return poOGRProduct;
4733 : #endif /* HAVE_GEOS */
4734 : }
4735 :
4736 : /************************************************************************/
4737 : /* OGR_G_ConcaveHullOfPolygons() */
4738 : /************************************************************************/
4739 : /**
4740 : * \brief Compute the concave hull of a set of polygons, respecting
4741 : * the polygons as constraints.
4742 : *
4743 : * A concave hull is a (possibly) non-convex polygon containing all the input
4744 : * polygons.
4745 : * The computed hull "fills the gap" between the polygons,
4746 : * and does not intersect their interior.
4747 : * A set of polygons has a sequence of hulls of increasing concaveness,
4748 : * determined by a numeric target parameter.
4749 : *
4750 : * The concave hull is constructed by removing the longest outer edges
4751 : * of the Delaunay Triangulation of the space between the polygons,
4752 : * until the target criterion parameter is reached.
4753 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4754 : * between polygons to be no larger than this value.
4755 : * This can be expressed as a ratio between the lengths of the longest and
4756 : * shortest edges.
4757 : *
4758 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4759 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4760 : * for more details.
4761 : *
4762 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4763 : * be non-overlapping).
4764 : *
4765 : * A new geometry object is created and returned containing the concave
4766 : * hull of the geometry on which the method is invoked.
4767 : *
4768 : * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
4769 : *
4770 : * This function is built on the GEOS >= 3.11 library
4771 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4772 : * fail, issuing a CPLE_NotSupported error.
4773 : *
4774 : * @param hTarget The Geometry to calculate the concave hull of.
4775 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4776 : * difference between the longest and shortest edge lengths
4777 : * between the polygons.
4778 : * This normalizes the Maximum Edge Length to be scale-free.
4779 : * A value of 1 produces the convex hull; a value of 0 produces
4780 : * the original polygons.
4781 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4782 : * polygons.
4783 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4784 : *
4785 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4786 : * or NULL if an error occurs.
4787 : *
4788 : * @since GDAL 3.13
4789 : * @see OGR_G_ConcaveHull()
4790 : */
4791 :
4792 7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
4793 : double dfLengthRatio, bool bIsTight,
4794 : bool bAllowHoles)
4795 :
4796 : {
4797 7 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
4798 :
4799 7 : return OGRGeometry::ToHandle(
4800 : OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
4801 7 : dfLengthRatio, bIsTight, bAllowHoles));
4802 : }
4803 :
4804 : /************************************************************************/
4805 : /* Boundary() */
4806 : /************************************************************************/
4807 :
4808 : /**
4809 : * \brief Compute boundary.
4810 : *
4811 : * A new geometry object is created and returned containing the boundary
4812 : * of the geometry on which the method is invoked.
4813 : *
4814 : * This method is the same as the C function OGR_G_Boundary().
4815 : *
4816 : * This method is built on the GEOS library, check it for the definition
4817 : * of the geometry operation.
4818 : * If OGR is built without the GEOS library, this method will always fail,
4819 : * issuing a CPLE_NotSupported error.
4820 : *
4821 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4822 : *
4823 : */
4824 :
4825 6 : OGRGeometry *OGRGeometry::Boundary() const
4826 :
4827 : {
4828 : #ifndef HAVE_GEOS
4829 :
4830 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4831 : return nullptr;
4832 :
4833 : #else
4834 :
4835 6 : OGRGeometry *poOGRProduct = nullptr;
4836 :
4837 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4838 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4839 6 : if (hGeosGeom != nullptr)
4840 : {
4841 6 : GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
4842 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4843 :
4844 : poOGRProduct =
4845 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4846 : }
4847 6 : freeGEOSContext(hGEOSCtxt);
4848 :
4849 6 : return poOGRProduct;
4850 :
4851 : #endif // HAVE_GEOS
4852 : }
4853 :
4854 : //! @cond Doxygen_Suppress
4855 : /**
4856 : * \brief Compute boundary (deprecated)
4857 : *
4858 : * @deprecated
4859 : *
4860 : * @see Boundary()
4861 : */
4862 0 : OGRGeometry *OGRGeometry::getBoundary() const
4863 :
4864 : {
4865 0 : return Boundary();
4866 : }
4867 :
4868 : //! @endcond
4869 :
4870 : /************************************************************************/
4871 : /* OGR_G_Boundary() */
4872 : /************************************************************************/
4873 : /**
4874 : * \brief Compute boundary.
4875 : *
4876 : * A new geometry object is created and returned containing the boundary
4877 : * of the geometry on which the method is invoked.
4878 : *
4879 : * This function is the same as the C++ method OGR_G_Boundary().
4880 : *
4881 : * This function is built on the GEOS library, check it for the definition
4882 : * of the geometry operation.
4883 : * If OGR is built without the GEOS library, this function will always fail,
4884 : * issuing a CPLE_NotSupported error.
4885 : *
4886 : * @param hTarget The Geometry to calculate the boundary of.
4887 : *
4888 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4889 : * or NULL if an error occurs.
4890 : *
4891 : */
4892 6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
4893 :
4894 : {
4895 6 : VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
4896 :
4897 6 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4898 : }
4899 :
4900 : /**
4901 : * \brief Compute boundary (deprecated)
4902 : *
4903 : * @deprecated
4904 : *
4905 : * @see OGR_G_Boundary()
4906 : */
4907 0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
4908 :
4909 : {
4910 0 : VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
4911 :
4912 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4913 : }
4914 :
4915 : /************************************************************************/
4916 : /* Buffer() */
4917 : /************************************************************************/
4918 :
4919 : /**
4920 : * \brief Compute buffer of geometry.
4921 : *
4922 : * Builds a new geometry containing the buffer region around the geometry
4923 : * on which it is invoked. The buffer is a polygon containing the region within
4924 : * the buffer distance of the original geometry.
4925 : *
4926 : * Some buffer sections are properly described as curves, but are converted to
4927 : * approximate polygons. The nQuadSegs parameter can be used to control how
4928 : * many segments should be used to define a 90 degree curve - a quadrant of a
4929 : * circle. A value of 30 is a reasonable default. Large values result in
4930 : * large numbers of vertices in the resulting buffer geometry while small
4931 : * numbers reduce the accuracy of the result.
4932 : *
4933 : * This method is the same as the C function OGR_G_Buffer().
4934 : *
4935 : * This method is built on the GEOS library, check it for the definition
4936 : * of the geometry operation.
4937 : * If OGR is built without the GEOS library, this method will always fail,
4938 : * issuing a CPLE_NotSupported error.
4939 : *
4940 : * @param dfDist the buffer distance to be applied. Should be expressed into
4941 : * the same unit as the coordinates of the geometry.
4942 : *
4943 : * @param nQuadSegs the number of segments used to approximate a 90
4944 : * degree (quadrant) of curvature.
4945 : *
4946 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4947 : */
4948 :
4949 42 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
4950 :
4951 : {
4952 : (void)dfDist;
4953 : (void)nQuadSegs;
4954 : #ifndef HAVE_GEOS
4955 :
4956 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4957 : return nullptr;
4958 :
4959 : #else
4960 :
4961 42 : OGRGeometry *poOGRProduct = nullptr;
4962 :
4963 42 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4964 42 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4965 42 : if (hGeosGeom != nullptr)
4966 : {
4967 : GEOSGeom hGeosProduct =
4968 42 : GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
4969 42 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4970 :
4971 : poOGRProduct =
4972 42 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4973 : }
4974 42 : freeGEOSContext(hGEOSCtxt);
4975 :
4976 42 : return poOGRProduct;
4977 :
4978 : #endif // HAVE_GEOS
4979 : }
4980 :
4981 : /************************************************************************/
4982 : /* OGR_G_Buffer() */
4983 : /************************************************************************/
4984 :
4985 : /**
4986 : * \brief Compute buffer of geometry.
4987 : *
4988 : * Builds a new geometry containing the buffer region around the geometry
4989 : * on which it is invoked. The buffer is a polygon containing the region within
4990 : * the buffer distance of the original geometry.
4991 : *
4992 : * Some buffer sections are properly described as curves, but are converted to
4993 : * approximate polygons. The nQuadSegs parameter can be used to control how
4994 : * many segments should be used to define a 90 degree curve - a quadrant of a
4995 : * circle. A value of 30 is a reasonable default. Large values result in
4996 : * large numbers of vertices in the resulting buffer geometry while small
4997 : * numbers reduce the accuracy of the result.
4998 : *
4999 : * This function is the same as the C++ method OGRGeometry::Buffer().
5000 : *
5001 : * This function is built on the GEOS library, check it for the definition
5002 : * of the geometry operation.
5003 : * If OGR is built without the GEOS library, this function will always fail,
5004 : * issuing a CPLE_NotSupported error.
5005 : *
5006 : * @param hTarget the geometry.
5007 : * @param dfDist the buffer distance to be applied. Should be expressed into
5008 : * the same unit as the coordinates of the geometry.
5009 : *
5010 : * @param nQuadSegs the number of segments used to approximate a 90 degree
5011 : * (quadrant) of curvature.
5012 : *
5013 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5014 : * or NULL if an error occurs.
5015 : */
5016 :
5017 42 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
5018 :
5019 : {
5020 42 : VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
5021 :
5022 42 : return OGRGeometry::ToHandle(
5023 42 : OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
5024 : }
5025 :
5026 : /**
5027 : * \brief Compute buffer of geometry.
5028 : *
5029 : * Builds a new geometry containing the buffer region around the geometry
5030 : * on which it is invoked. The buffer is a polygon containing the region within
5031 : * the buffer distance of the original geometry.
5032 : *
5033 : * This function is built on the GEOS library, check it for the definition
5034 : * of the geometry operation.
5035 : * If OGR is built without the GEOS library, this function will always fail,
5036 : * issuing a CPLE_NotSupported error.
5037 : *
5038 : * The following options are supported. See the GEOS library for more detailed
5039 : * descriptions.
5040 : *
5041 : * <ul>
5042 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5043 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5044 : * <li>MITRE_LIMIT=double</li>
5045 : * <li>QUADRANT_SEGMENTS=int</li>
5046 : * <li>SINGLE_SIDED=YES/NO</li>
5047 : * </ul>
5048 : *
5049 : * This function is the same as the C function OGR_G_BufferEx().
5050 : *
5051 : * @param dfDist the buffer distance to be applied. Should be expressed into
5052 : * the same unit as the coordinates of the geometry.
5053 : * @param papszOptions NULL terminated list of options (may be NULL)
5054 : *
5055 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5056 : *
5057 : * @since GDAL 3.10
5058 : */
5059 :
5060 35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
5061 : CSLConstList papszOptions) const
5062 : {
5063 : (void)dfDist;
5064 : (void)papszOptions;
5065 : #ifndef HAVE_GEOS
5066 :
5067 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5068 : return nullptr;
5069 :
5070 : #else
5071 35 : OGRGeometry *poOGRProduct = nullptr;
5072 35 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5073 :
5074 35 : auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
5075 35 : bool bParamsAreValid = true;
5076 :
5077 166 : for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
5078 : {
5079 131 : if (EQUAL(pszParam, "ENDCAP_STYLE"))
5080 : {
5081 : int nStyle;
5082 25 : if (EQUAL(pszValue, "ROUND"))
5083 : {
5084 22 : nStyle = GEOSBUF_CAP_ROUND;
5085 : }
5086 3 : else if (EQUAL(pszValue, "FLAT"))
5087 : {
5088 1 : nStyle = GEOSBUF_CAP_FLAT;
5089 : }
5090 2 : else if (EQUAL(pszValue, "SQUARE"))
5091 : {
5092 1 : nStyle = GEOSBUF_CAP_SQUARE;
5093 : }
5094 : else
5095 : {
5096 1 : bParamsAreValid = false;
5097 1 : CPLError(CE_Failure, CPLE_NotSupported,
5098 : "Invalid value for ENDCAP_STYLE: %s", pszValue);
5099 2 : break;
5100 : }
5101 :
5102 24 : if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
5103 : {
5104 0 : bParamsAreValid = false;
5105 : }
5106 : }
5107 106 : else if (EQUAL(pszParam, "JOIN_STYLE"))
5108 : {
5109 : int nStyle;
5110 25 : if (EQUAL(pszValue, "ROUND"))
5111 : {
5112 21 : nStyle = GEOSBUF_JOIN_ROUND;
5113 : }
5114 4 : else if (EQUAL(pszValue, "MITRE"))
5115 : {
5116 3 : nStyle = GEOSBUF_JOIN_MITRE;
5117 : }
5118 1 : else if (EQUAL(pszValue, "BEVEL"))
5119 : {
5120 0 : nStyle = GEOSBUF_JOIN_BEVEL;
5121 : }
5122 : else
5123 : {
5124 1 : bParamsAreValid = false;
5125 1 : CPLError(CE_Failure, CPLE_NotSupported,
5126 : "Invalid value for JOIN_STYLE: %s", pszValue);
5127 1 : break;
5128 : }
5129 :
5130 24 : if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
5131 : {
5132 0 : bParamsAreValid = false;
5133 0 : break;
5134 : }
5135 : }
5136 81 : else if (EQUAL(pszParam, "MITRE_LIMIT"))
5137 : {
5138 : try
5139 : {
5140 : std::size_t end;
5141 30 : double dfLimit = std::stod(pszValue, &end);
5142 :
5143 24 : if (end != strlen(pszValue))
5144 : {
5145 0 : throw std::invalid_argument("");
5146 : }
5147 :
5148 24 : if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
5149 : dfLimit))
5150 : {
5151 0 : bParamsAreValid = false;
5152 0 : break;
5153 : }
5154 : }
5155 4 : catch (const std::invalid_argument &)
5156 : {
5157 2 : bParamsAreValid = false;
5158 2 : CPLError(CE_Failure, CPLE_IllegalArg,
5159 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5160 : }
5161 0 : catch (const std::out_of_range &)
5162 : {
5163 0 : bParamsAreValid = false;
5164 0 : CPLError(CE_Failure, CPLE_IllegalArg,
5165 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5166 : }
5167 : }
5168 55 : else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
5169 : {
5170 : try
5171 : {
5172 : std::size_t end;
5173 38 : int nQuadSegs = std::stoi(pszValue, &end, 10);
5174 :
5175 26 : if (end != strlen(pszValue))
5176 : {
5177 0 : throw std::invalid_argument("");
5178 : }
5179 :
5180 26 : if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
5181 : nQuadSegs))
5182 : {
5183 0 : bParamsAreValid = false;
5184 0 : break;
5185 : }
5186 : }
5187 6 : catch (const std::invalid_argument &)
5188 : {
5189 3 : bParamsAreValid = false;
5190 3 : CPLError(CE_Failure, CPLE_IllegalArg,
5191 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5192 : }
5193 2 : catch (const std::out_of_range &)
5194 : {
5195 1 : bParamsAreValid = false;
5196 1 : CPLError(CE_Failure, CPLE_IllegalArg,
5197 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5198 : }
5199 : }
5200 25 : else if (EQUAL(pszParam, "SINGLE_SIDED"))
5201 : {
5202 24 : bool bSingleSided = CPLTestBool(pszValue);
5203 :
5204 24 : if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
5205 : bSingleSided))
5206 : {
5207 0 : bParamsAreValid = false;
5208 0 : break;
5209 : }
5210 : }
5211 : else
5212 : {
5213 1 : bParamsAreValid = false;
5214 1 : CPLError(CE_Failure, CPLE_NotSupported,
5215 : "Unsupported buffer option: %s", pszValue);
5216 : }
5217 : }
5218 :
5219 35 : if (bParamsAreValid)
5220 : {
5221 26 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
5222 26 : if (hGeosGeom != nullptr)
5223 : {
5224 : GEOSGeom hGeosProduct =
5225 26 : GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
5226 26 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
5227 :
5228 26 : if (hGeosProduct != nullptr)
5229 : {
5230 26 : poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
5231 : this, nullptr);
5232 : }
5233 : }
5234 : }
5235 :
5236 35 : GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
5237 35 : freeGEOSContext(hGEOSCtxt);
5238 35 : return poOGRProduct;
5239 : #endif
5240 : }
5241 :
5242 : /**
5243 : * \brief Compute buffer of geometry.
5244 : *
5245 : * Builds a new geometry containing the buffer region around the geometry
5246 : * on which it is invoked. The buffer is a polygon containing the region within
5247 : * the buffer distance of the original geometry.
5248 : *
5249 : * This function is built on the GEOS library, check it for the definition
5250 : * of the geometry operation.
5251 : * If OGR is built without the GEOS library, this function will always fail,
5252 : * issuing a CPLE_NotSupported error.
5253 : *
5254 : * The following options are supported. See the GEOS library for more detailed
5255 : * descriptions.
5256 : *
5257 : * <ul>
5258 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5259 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5260 : * <li>MITRE_LIMIT=double</li>
5261 : * <li>QUADRANT_SEGMENTS=int</li>
5262 : * <li>SINGLE_SIDED=YES/NO</li>
5263 : * </ul>
5264 : *
5265 : * This function is the same as the C++ method OGRGeometry::BufferEx().
5266 : *
5267 : * @param hTarget the geometry.
5268 : * @param dfDist the buffer distance to be applied. Should be expressed into
5269 : * the same unit as the coordinates of the geometry.
5270 : * @param papszOptions NULL terminated list of options (may be NULL)
5271 : *
5272 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5273 : * or NULL if an error occurs.
5274 : *
5275 : * @since GDAL 3.10
5276 : */
5277 :
5278 12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
5279 : CSLConstList papszOptions)
5280 :
5281 : {
5282 12 : VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
5283 :
5284 12 : return OGRGeometry::ToHandle(
5285 12 : OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
5286 : }
5287 :
5288 : /************************************************************************/
5289 : /* Intersection() */
5290 : /************************************************************************/
5291 :
5292 : /**
5293 : * \brief Compute intersection.
5294 : *
5295 : * Generates a new geometry which is the region of intersection of the
5296 : * two geometries operated on. The Intersects() method can be used to test if
5297 : * two geometries intersect.
5298 : *
5299 : * Geometry validity is not checked. In case you are unsure of the validity
5300 : * of the input geometries, call IsValid() before, otherwise the result might
5301 : * be wrong.
5302 : *
5303 : * This method is the same as the C function OGR_G_Intersection().
5304 : *
5305 : * This method is built on the GEOS library, check it for the definition
5306 : * of the geometry operation.
5307 : * If OGR is built without the GEOS library, this method will always fail,
5308 : * issuing a CPLE_NotSupported error.
5309 : *
5310 : * @param poOtherGeom the other geometry intersected with "this" geometry.
5311 : *
5312 : * @return a new geometry to be freed by the caller, or NULL if there is no
5313 : * intersection or if an error occurs.
5314 : *
5315 : */
5316 :
5317 : OGRGeometry *
5318 3105 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5319 :
5320 : {
5321 3105 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5322 : {
5323 : #ifndef HAVE_SFCGAL
5324 :
5325 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5326 0 : return nullptr;
5327 :
5328 : #else
5329 :
5330 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5331 : if (poThis == nullptr)
5332 : return nullptr;
5333 :
5334 : sfcgal_geometry_t *poOther =
5335 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5336 : if (poOther == nullptr)
5337 : {
5338 : sfcgal_geometry_delete(poThis);
5339 : return nullptr;
5340 : }
5341 :
5342 : sfcgal_geometry_t *poRes =
5343 : sfcgal_geometry_intersection_3d(poThis, poOther);
5344 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
5345 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5346 : poOtherGeom->getSpatialReference() != nullptr &&
5347 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5348 : h_prodGeom->assignSpatialReference(getSpatialReference());
5349 :
5350 : sfcgal_geometry_delete(poThis);
5351 : sfcgal_geometry_delete(poOther);
5352 : sfcgal_geometry_delete(poRes);
5353 :
5354 : return h_prodGeom;
5355 :
5356 : #endif
5357 : }
5358 :
5359 : else
5360 : {
5361 : #ifndef HAVE_GEOS
5362 :
5363 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5364 : return nullptr;
5365 :
5366 : #else
5367 3105 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
5368 : #endif /* HAVE_GEOS */
5369 : }
5370 : }
5371 :
5372 : /************************************************************************/
5373 : /* OGR_G_Intersection() */
5374 : /************************************************************************/
5375 :
5376 : /**
5377 : * \brief Compute intersection.
5378 : *
5379 : * Generates a new geometry which is the region of intersection of the
5380 : * two geometries operated on. The OGR_G_Intersects() function can be used to
5381 : * test if two geometries intersect.
5382 : *
5383 : * Geometry validity is not checked. In case you are unsure of the validity
5384 : * of the input geometries, call IsValid() before, otherwise the result might
5385 : * be wrong.
5386 : *
5387 : * This function is the same as the C++ method OGRGeometry::Intersection().
5388 : *
5389 : * This function is built on the GEOS library, check it for the definition
5390 : * of the geometry operation.
5391 : * If OGR is built without the GEOS library, this function will always fail,
5392 : * issuing a CPLE_NotSupported error.
5393 : *
5394 : * @param hThis the geometry.
5395 : * @param hOther the other geometry.
5396 : *
5397 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5398 : * or NULL if there is not intersection of if an error occurs.
5399 : */
5400 :
5401 12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
5402 :
5403 : {
5404 12 : VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
5405 :
5406 24 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
5407 24 : OGRGeometry::FromHandle(hOther)));
5408 : }
5409 :
5410 : /************************************************************************/
5411 : /* Union() */
5412 : /************************************************************************/
5413 :
5414 : /**
5415 : * \brief Compute union.
5416 : *
5417 : * Generates a new geometry which is the region of union of the
5418 : * two geometries operated on.
5419 : *
5420 : * Geometry validity is not checked. In case you are unsure of the validity
5421 : * of the input geometries, call IsValid() before, otherwise the result might
5422 : * be wrong.
5423 : *
5424 : * This method is the same as the C function OGR_G_Union().
5425 : *
5426 : * This method is built on the GEOS library, check it for the definition
5427 : * of the geometry operation.
5428 : * If OGR is built without the GEOS library, this method will always fail,
5429 : * issuing a CPLE_NotSupported error.
5430 : *
5431 : * @param poOtherGeom the other geometry unioned with "this" geometry.
5432 : *
5433 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5434 : */
5435 :
5436 : OGRGeometry *
5437 68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5438 :
5439 : {
5440 68 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5441 : {
5442 : #ifndef HAVE_SFCGAL
5443 :
5444 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5445 0 : return nullptr;
5446 :
5447 : #else
5448 :
5449 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5450 : if (poThis == nullptr)
5451 : return nullptr;
5452 :
5453 : sfcgal_geometry_t *poOther =
5454 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5455 : if (poOther == nullptr)
5456 : {
5457 : sfcgal_geometry_delete(poThis);
5458 : return nullptr;
5459 : }
5460 :
5461 : sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
5462 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5463 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5464 : poOtherGeom->getSpatialReference() != nullptr &&
5465 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5466 : h_prodGeom->assignSpatialReference(getSpatialReference());
5467 :
5468 : sfcgal_geometry_delete(poThis);
5469 : sfcgal_geometry_delete(poOther);
5470 : sfcgal_geometry_delete(poRes);
5471 :
5472 : return h_prodGeom;
5473 :
5474 : #endif
5475 : }
5476 :
5477 : else
5478 : {
5479 : #ifndef HAVE_GEOS
5480 :
5481 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5482 : return nullptr;
5483 :
5484 : #else
5485 68 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
5486 : #endif /* HAVE_GEOS */
5487 : }
5488 : }
5489 :
5490 : /************************************************************************/
5491 : /* OGR_G_Union() */
5492 : /************************************************************************/
5493 :
5494 : /**
5495 : * \brief Compute union.
5496 : *
5497 : * Generates a new geometry which is the region of union of the
5498 : * two geometries operated on.
5499 : *
5500 : * Geometry validity is not checked. In case you are unsure of the validity
5501 : * of the input geometries, call IsValid() before, otherwise the result might
5502 : * be wrong.
5503 : *
5504 : * This function is the same as the C++ method OGRGeometry::Union().
5505 : *
5506 : * This function is built on the GEOS library, check it for the definition
5507 : * of the geometry operation.
5508 : * If OGR is built without the GEOS library, this function will always fail,
5509 : * issuing a CPLE_NotSupported error.
5510 : *
5511 : * @param hThis the geometry.
5512 : * @param hOther the other geometry.
5513 : *
5514 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5515 : * or NULL if an error occurs.
5516 : */
5517 :
5518 10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
5519 :
5520 : {
5521 10 : VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
5522 :
5523 20 : return OGRGeometry::ToHandle(
5524 20 : OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
5525 : }
5526 :
5527 : /************************************************************************/
5528 : /* UnionCascaded() */
5529 : /************************************************************************/
5530 :
5531 : /**
5532 : * \brief Compute union using cascading.
5533 : *
5534 : * Geometry validity is not checked. In case you are unsure of the validity
5535 : * of the input geometries, call IsValid() before, otherwise the result might
5536 : * be wrong.
5537 : *
5538 : * The input geometry must be a MultiPolygon.
5539 : *
5540 : * This method is the same as the C function OGR_G_UnionCascaded().
5541 : *
5542 : * This method is built on the GEOS library, check it for the definition
5543 : * of the geometry operation.
5544 : * If OGR is built without the GEOS library, this method will always fail,
5545 : * issuing a CPLE_NotSupported error.
5546 : *
5547 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5548 : *
5549 : *
5550 : * @deprecated Use UnaryUnion() instead
5551 : */
5552 :
5553 2 : OGRGeometry *OGRGeometry::UnionCascaded() const
5554 :
5555 : {
5556 : #ifndef HAVE_GEOS
5557 :
5558 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5559 : return nullptr;
5560 : #else
5561 :
5562 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5563 : if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
5564 : {
5565 : // GEOS < 3.11 crashes on an empty multipolygon input
5566 : auto poRet = new OGRGeometryCollection();
5567 : poRet->assignSpatialReference(getSpatialReference());
5568 : return poRet;
5569 : }
5570 : #endif
5571 2 : OGRGeometry *poOGRProduct = nullptr;
5572 :
5573 2 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5574 2 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5575 2 : if (hThisGeosGeom != nullptr)
5576 : {
5577 2 : GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
5578 2 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5579 :
5580 : poOGRProduct =
5581 2 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5582 : }
5583 2 : freeGEOSContext(hGEOSCtxt);
5584 :
5585 2 : return poOGRProduct;
5586 :
5587 : #endif // HAVE_GEOS
5588 : }
5589 :
5590 : /************************************************************************/
5591 : /* OGR_G_UnionCascaded() */
5592 : /************************************************************************/
5593 :
5594 : /**
5595 : * \brief Compute union using cascading.
5596 : *
5597 : * Geometry validity is not checked. In case you are unsure of the validity
5598 : * of the input geometries, call IsValid() before, otherwise the result might
5599 : * be wrong.
5600 : *
5601 : * The input geometry must be a MultiPolygon.
5602 : *
5603 : * This function is the same as the C++ method OGRGeometry::UnionCascaded().
5604 : *
5605 : * This function is built on the GEOS library, check it for the definition
5606 : * of the geometry operation.
5607 : * If OGR is built without the GEOS library, this function will always fail,
5608 : * issuing a CPLE_NotSupported error.
5609 : *
5610 : * @param hThis the geometry.
5611 : *
5612 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5613 : * or NULL if an error occurs.
5614 : *
5615 : * @deprecated Use OGR_G_UnaryUnion() instead
5616 : */
5617 :
5618 2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
5619 :
5620 : {
5621 2 : VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
5622 :
5623 2 : return OGRGeometry::ToHandle(
5624 2 : OGRGeometry::FromHandle(hThis)->UnionCascaded());
5625 : }
5626 :
5627 : /************************************************************************/
5628 : /* UnaryUnion() */
5629 : /************************************************************************/
5630 :
5631 : /**
5632 : * \brief Returns the union of all components of a single geometry.
5633 : *
5634 : * Usually used to convert a collection into the smallest set of polygons that
5635 : * cover the same area.
5636 : *
5637 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5638 : *
5639 : * This method is the same as the C function OGR_G_UnaryUnion().
5640 : *
5641 : * This method is built on the GEOS library, check it for the definition
5642 : * of the geometry operation.
5643 : * If OGR is built without the GEOS library, this method will always fail,
5644 : * issuing a CPLE_NotSupported error.
5645 : *
5646 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5647 : *
5648 : * @since GDAL 3.7
5649 : */
5650 :
5651 636 : OGRGeometry *OGRGeometry::UnaryUnion() const
5652 :
5653 : {
5654 : #ifndef HAVE_GEOS
5655 :
5656 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5657 : return nullptr;
5658 : #else
5659 :
5660 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5661 : if (IsEmpty())
5662 : {
5663 : // GEOS < 3.11 crashes on an empty geometry
5664 : auto poRet = new OGRGeometryCollection();
5665 : poRet->assignSpatialReference(getSpatialReference());
5666 : return poRet;
5667 : }
5668 : #endif
5669 636 : OGRGeometry *poOGRProduct = nullptr;
5670 :
5671 636 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5672 636 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5673 636 : if (hThisGeosGeom != nullptr)
5674 : {
5675 636 : GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5676 636 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5677 :
5678 : poOGRProduct =
5679 636 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5680 : }
5681 636 : freeGEOSContext(hGEOSCtxt);
5682 :
5683 636 : return poOGRProduct;
5684 :
5685 : #endif // HAVE_GEOS
5686 : }
5687 :
5688 : /************************************************************************/
5689 : /* OGR_G_UnaryUnion() */
5690 : /************************************************************************/
5691 :
5692 : /**
5693 : * \brief Returns the union of all components of a single geometry.
5694 : *
5695 : * Usually used to convert a collection into the smallest set of polygons that
5696 : * cover the same area.
5697 : *
5698 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5699 : *
5700 : * Geometry validity is not checked. In case you are unsure of the validity
5701 : * of the input geometries, call IsValid() before, otherwise the result might
5702 : * be wrong.
5703 : *
5704 : * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5705 : *
5706 : * This function is built on the GEOS library, check it for the definition
5707 : * of the geometry operation.
5708 : * If OGR is built without the GEOS library, this function will always fail,
5709 : * issuing a CPLE_NotSupported error.
5710 : *
5711 : * @param hThis the geometry.
5712 : *
5713 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5714 : * or NULL if an error occurs.
5715 : *
5716 : * @since GDAL 3.7
5717 : */
5718 :
5719 3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5720 :
5721 : {
5722 3 : VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5723 :
5724 3 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5725 : }
5726 :
5727 : /************************************************************************/
5728 : /* Difference() */
5729 : /************************************************************************/
5730 :
5731 : /**
5732 : * \brief Compute difference.
5733 : *
5734 : * Generates a new geometry which is the region of this geometry with the
5735 : * region of the second geometry removed.
5736 : *
5737 : * Geometry validity is not checked. In case you are unsure of the validity
5738 : * of the input geometries, call IsValid() before, otherwise the result might
5739 : * be wrong.
5740 : *
5741 : * This method is the same as the C function OGR_G_Difference().
5742 : *
5743 : * This method is built on the GEOS library, check it for the definition
5744 : * of the geometry operation.
5745 : * If OGR is built without the GEOS library, this method will always fail,
5746 : * issuing a CPLE_NotSupported error.
5747 : *
5748 : * @param poOtherGeom the other geometry removed from "this" geometry.
5749 : *
5750 : * @return a new geometry to be freed by the caller, or NULL if the difference
5751 : * is empty or if an error occurs.
5752 : */
5753 :
5754 : OGRGeometry *
5755 752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5756 :
5757 : {
5758 752 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5759 : {
5760 : #ifndef HAVE_SFCGAL
5761 :
5762 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5763 0 : return nullptr;
5764 :
5765 : #else
5766 :
5767 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5768 : if (poThis == nullptr)
5769 : return nullptr;
5770 :
5771 : sfcgal_geometry_t *poOther =
5772 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5773 : if (poOther == nullptr)
5774 : {
5775 : sfcgal_geometry_delete(poThis);
5776 : return nullptr;
5777 : }
5778 :
5779 : sfcgal_geometry_t *poRes =
5780 : sfcgal_geometry_difference_3d(poThis, poOther);
5781 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5782 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5783 : poOtherGeom->getSpatialReference() != nullptr &&
5784 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5785 : h_prodGeom->assignSpatialReference(getSpatialReference());
5786 :
5787 : sfcgal_geometry_delete(poThis);
5788 : sfcgal_geometry_delete(poOther);
5789 : sfcgal_geometry_delete(poRes);
5790 :
5791 : return h_prodGeom;
5792 :
5793 : #endif
5794 : }
5795 :
5796 : else
5797 : {
5798 : #ifndef HAVE_GEOS
5799 :
5800 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5801 : return nullptr;
5802 :
5803 : #else
5804 752 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5805 : #endif /* HAVE_GEOS */
5806 : }
5807 : }
5808 :
5809 : /************************************************************************/
5810 : /* OGR_G_Difference() */
5811 : /************************************************************************/
5812 :
5813 : /**
5814 : * \brief Compute difference.
5815 : *
5816 : * Generates a new geometry which is the region of this geometry with the
5817 : * region of the other geometry removed.
5818 : *
5819 : * Geometry validity is not checked. In case you are unsure of the validity
5820 : * of the input geometries, call IsValid() before, otherwise the result might
5821 : * be wrong.
5822 : *
5823 : * This function is the same as the C++ method OGRGeometry::Difference().
5824 : *
5825 : * This function is built on the GEOS library, check it for the definition
5826 : * of the geometry operation.
5827 : * If OGR is built without the GEOS library, this function will always fail,
5828 : * issuing a CPLE_NotSupported error.
5829 : *
5830 : * @param hThis the geometry.
5831 : * @param hOther the other geometry.
5832 : *
5833 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5834 : * or NULL if the difference is empty or if an error occurs.
5835 : */
5836 :
5837 6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5838 :
5839 : {
5840 6 : VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5841 :
5842 12 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5843 12 : OGRGeometry::FromHandle(hOther)));
5844 : }
5845 :
5846 : /************************************************************************/
5847 : /* SymDifference() */
5848 : /************************************************************************/
5849 :
5850 : /**
5851 : * \brief Compute symmetric difference.
5852 : *
5853 : * Generates a new geometry which is the symmetric difference of this
5854 : * geometry and the second geometry passed into the method.
5855 : *
5856 : * Geometry validity is not checked. In case you are unsure of the validity
5857 : * of the input geometries, call IsValid() before, otherwise the result might
5858 : * be wrong.
5859 : *
5860 : * This method is the same as the C function OGR_G_SymDifference().
5861 : *
5862 : * This method is built on the GEOS library, check it for the definition
5863 : * of the geometry operation.
5864 : * If OGR is built without the GEOS library, this method will always fail,
5865 : * issuing a CPLE_NotSupported error.
5866 : *
5867 : * @param poOtherGeom the other geometry.
5868 : *
5869 : * @return a new geometry to be freed by the caller, or NULL if the difference
5870 : * is empty or if an error occurs.
5871 : *
5872 : */
5873 :
5874 7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
5875 :
5876 : {
5877 : (void)poOtherGeom;
5878 7 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5879 : {
5880 : #ifndef HAVE_SFCGAL
5881 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5882 0 : return nullptr;
5883 : #else
5884 : OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5885 : if (poFirstDifference == nullptr)
5886 : return nullptr;
5887 :
5888 : OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5889 : if (poOtherDifference == nullptr)
5890 : {
5891 : delete poFirstDifference;
5892 : return nullptr;
5893 : }
5894 :
5895 : OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5896 : delete poFirstDifference;
5897 : delete poOtherDifference;
5898 : return poSymDiff;
5899 : #endif
5900 : }
5901 :
5902 : #ifndef HAVE_GEOS
5903 :
5904 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5905 : return nullptr;
5906 :
5907 : #else
5908 7 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5909 : #endif // HAVE_GEOS
5910 : }
5911 :
5912 : //! @cond Doxygen_Suppress
5913 : /**
5914 : * \brief Compute symmetric difference (deprecated)
5915 : *
5916 : * @deprecated
5917 : *
5918 : * @see OGRGeometry::SymDifference()
5919 : */
5920 : OGRGeometry *
5921 0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5922 :
5923 : {
5924 0 : return SymDifference(poOtherGeom);
5925 : }
5926 :
5927 : //! @endcond
5928 :
5929 : /************************************************************************/
5930 : /* OGR_G_SymDifference() */
5931 : /************************************************************************/
5932 :
5933 : /**
5934 : * \brief Compute symmetric difference.
5935 : *
5936 : * Generates a new geometry which is the symmetric difference of this
5937 : * geometry and the other geometry.
5938 : *
5939 : * Geometry validity is not checked. In case you are unsure of the validity
5940 : * of the input geometries, call IsValid() before, otherwise the result might
5941 : * be wrong.
5942 : *
5943 : * This function is the same as the C++ method
5944 : * OGRGeometry::SymmetricDifference().
5945 : *
5946 : * This function is built on the GEOS library, check it for the definition
5947 : * of the geometry operation.
5948 : * If OGR is built without the GEOS library, this function will always fail,
5949 : * issuing a CPLE_NotSupported error.
5950 : *
5951 : * @param hThis the geometry.
5952 : * @param hOther the other geometry.
5953 : *
5954 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5955 : * or NULL if the difference is empty or if an error occurs.
5956 : *
5957 : */
5958 :
5959 7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5960 :
5961 : {
5962 7 : VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5963 :
5964 14 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5965 14 : OGRGeometry::FromHandle(hOther)));
5966 : }
5967 :
5968 : /**
5969 : * \brief Compute symmetric difference (deprecated)
5970 : *
5971 : * @deprecated
5972 : *
5973 : * @see OGR_G_SymmetricDifference()
5974 : */
5975 0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5976 :
5977 : {
5978 0 : VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5979 :
5980 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5981 0 : OGRGeometry::FromHandle(hOther)));
5982 : }
5983 :
5984 : /************************************************************************/
5985 : /* Disjoint() */
5986 : /************************************************************************/
5987 :
5988 : /**
5989 : * \brief Test for disjointness.
5990 : *
5991 : * Tests if this geometry and the other passed into the method are disjoint.
5992 : *
5993 : * Geometry validity is not checked. In case you are unsure of the validity
5994 : * of the input geometries, call IsValid() before, otherwise the result might
5995 : * be wrong.
5996 : *
5997 : * This method is the same as the C function OGR_G_Disjoint().
5998 : *
5999 : * This method is built on the GEOS library, check it for the definition
6000 : * of the geometry operation.
6001 : * If OGR is built without the GEOS library, this method will always fail,
6002 : * issuing a CPLE_NotSupported error.
6003 : *
6004 : * @param poOtherGeom the geometry to compare to this geometry.
6005 : *
6006 : * @return TRUE if they are disjoint, otherwise FALSE.
6007 : */
6008 :
6009 8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
6010 :
6011 : {
6012 : (void)poOtherGeom;
6013 : #ifndef HAVE_GEOS
6014 :
6015 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6016 : return FALSE;
6017 :
6018 : #else
6019 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
6020 : #endif // HAVE_GEOS
6021 : }
6022 :
6023 : /************************************************************************/
6024 : /* OGR_G_Disjoint() */
6025 : /************************************************************************/
6026 :
6027 : /**
6028 : * \brief Test for disjointness.
6029 : *
6030 : * Tests if this geometry and the other geometry are disjoint.
6031 : *
6032 : * Geometry validity is not checked. In case you are unsure of the validity
6033 : * of the input geometries, call IsValid() before, otherwise the result might
6034 : * be wrong.
6035 : *
6036 : * This function is the same as the C++ method OGRGeometry::Disjoint().
6037 : *
6038 : * This function is built on the GEOS library, check it for the definition
6039 : * of the geometry operation.
6040 : * If OGR is built without the GEOS library, this function will always fail,
6041 : * issuing a CPLE_NotSupported error.
6042 : *
6043 : * @param hThis the geometry to compare.
6044 : * @param hOther the other geometry to compare.
6045 : *
6046 : * @return TRUE if they are disjoint, otherwise FALSE.
6047 : */
6048 8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
6049 :
6050 : {
6051 8 : VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
6052 :
6053 16 : return OGRGeometry::FromHandle(hThis)->Disjoint(
6054 16 : OGRGeometry::FromHandle(hOther));
6055 : }
6056 :
6057 : /************************************************************************/
6058 : /* Touches() */
6059 : /************************************************************************/
6060 :
6061 : /**
6062 : * \brief Test for touching.
6063 : *
6064 : * Tests if this geometry and the other passed into the method are touching.
6065 : *
6066 : * Geometry validity is not checked. In case you are unsure of the validity
6067 : * of the input geometries, call IsValid() before, otherwise the result might
6068 : * be wrong.
6069 : *
6070 : * This method is the same as the C function OGR_G_Touches().
6071 : *
6072 : * This method is built on the GEOS library, check it for the definition
6073 : * of the geometry operation.
6074 : * If OGR is built without the GEOS library, this method will always fail,
6075 : * issuing a CPLE_NotSupported error.
6076 : *
6077 : * @param poOtherGeom the geometry to compare to this geometry.
6078 : *
6079 : * @return TRUE if they are touching, otherwise FALSE.
6080 : */
6081 :
6082 11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
6083 :
6084 : {
6085 : (void)poOtherGeom;
6086 : #ifndef HAVE_GEOS
6087 :
6088 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6089 : return FALSE;
6090 :
6091 : #else
6092 11 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
6093 : #endif // HAVE_GEOS
6094 : }
6095 :
6096 : /************************************************************************/
6097 : /* OGR_G_Touches() */
6098 : /************************************************************************/
6099 : /**
6100 : * \brief Test for touching.
6101 : *
6102 : * Tests if this geometry and the other geometry are touching.
6103 : *
6104 : * Geometry validity is not checked. In case you are unsure of the validity
6105 : * of the input geometries, call IsValid() before, otherwise the result might
6106 : * be wrong.
6107 : *
6108 : * This function is the same as the C++ method OGRGeometry::Touches().
6109 : *
6110 : * This function is built on the GEOS library, check it for the definition
6111 : * of the geometry operation.
6112 : * If OGR is built without the GEOS library, this function will always fail,
6113 : * issuing a CPLE_NotSupported error.
6114 : *
6115 : * @param hThis the geometry to compare.
6116 : * @param hOther the other geometry to compare.
6117 : *
6118 : * @return TRUE if they are touching, otherwise FALSE.
6119 : */
6120 :
6121 8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
6122 :
6123 : {
6124 8 : VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
6125 :
6126 16 : return OGRGeometry::FromHandle(hThis)->Touches(
6127 16 : OGRGeometry::FromHandle(hOther));
6128 : }
6129 :
6130 : /************************************************************************/
6131 : /* Crosses() */
6132 : /************************************************************************/
6133 :
6134 : /**
6135 : * \brief Test for crossing.
6136 : *
6137 : * Tests if this geometry and the other passed into the method are crossing.
6138 : *
6139 : * Geometry validity is not checked. In case you are unsure of the validity
6140 : * of the input geometries, call IsValid() before, otherwise the result might
6141 : * be wrong.
6142 : *
6143 : * This method is the same as the C function OGR_G_Crosses().
6144 : *
6145 : * This method is built on the GEOS library, check it for the definition
6146 : * of the geometry operation.
6147 : * If OGR is built without the GEOS library, this method will always fail,
6148 : * issuing a CPLE_NotSupported error.
6149 : *
6150 : * @param poOtherGeom the geometry to compare to this geometry.
6151 : *
6152 : * @return TRUE if they are crossing, otherwise FALSE.
6153 : */
6154 :
6155 8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
6156 :
6157 : {
6158 8 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
6159 : {
6160 : #ifndef HAVE_SFCGAL
6161 :
6162 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
6163 0 : return FALSE;
6164 :
6165 : #else
6166 :
6167 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
6168 : if (poThis == nullptr)
6169 : return FALSE;
6170 :
6171 : sfcgal_geometry_t *poOther =
6172 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
6173 : if (poOther == nullptr)
6174 : {
6175 : sfcgal_geometry_delete(poThis);
6176 : return FALSE;
6177 : }
6178 :
6179 : int res = sfcgal_geometry_intersects_3d(poThis, poOther);
6180 :
6181 : sfcgal_geometry_delete(poThis);
6182 : sfcgal_geometry_delete(poOther);
6183 :
6184 : return (res == 1) ? TRUE : FALSE;
6185 :
6186 : #endif
6187 : }
6188 :
6189 : else
6190 : {
6191 :
6192 : #ifndef HAVE_GEOS
6193 :
6194 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6195 : return FALSE;
6196 :
6197 : #else
6198 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
6199 : #endif /* HAVE_GEOS */
6200 : }
6201 : }
6202 :
6203 : /************************************************************************/
6204 : /* OGR_G_Crosses() */
6205 : /************************************************************************/
6206 : /**
6207 : * \brief Test for crossing.
6208 : *
6209 : * Tests if this geometry and the other geometry are crossing.
6210 : *
6211 : * Geometry validity is not checked. In case you are unsure of the validity
6212 : * of the input geometries, call IsValid() before, otherwise the result might
6213 : * be wrong.
6214 : *
6215 : * This function is the same as the C++ method OGRGeometry::Crosses().
6216 : *
6217 : * This function is built on the GEOS library, check it for the definition
6218 : * of the geometry operation.
6219 : * If OGR is built without the GEOS library, this function will always fail,
6220 : * issuing a CPLE_NotSupported error.
6221 : *
6222 : * @param hThis the geometry to compare.
6223 : * @param hOther the other geometry to compare.
6224 : *
6225 : * @return TRUE if they are crossing, otherwise FALSE.
6226 : */
6227 :
6228 8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
6229 :
6230 : {
6231 8 : VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
6232 :
6233 16 : return OGRGeometry::FromHandle(hThis)->Crosses(
6234 16 : OGRGeometry::FromHandle(hOther));
6235 : }
6236 :
6237 : /************************************************************************/
6238 : /* Within() */
6239 : /************************************************************************/
6240 :
6241 : /**
6242 : * \brief Test for containment.
6243 : *
6244 : * Tests if actual geometry object is within the passed geometry.
6245 : *
6246 : * Geometry validity is not checked. In case you are unsure of the validity
6247 : * of the input geometries, call IsValid() before, otherwise the result might
6248 : * be wrong.
6249 : *
6250 : * This method is the same as the C function OGR_G_Within().
6251 : *
6252 : * This method is built on the GEOS library, check it for the definition
6253 : * of the geometry operation.
6254 : * If OGR is built without the GEOS library, this method will always fail,
6255 : * issuing a CPLE_NotSupported error.
6256 : *
6257 : * @param poOtherGeom the geometry to compare to this geometry.
6258 : *
6259 : * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
6260 : */
6261 :
6262 22417 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
6263 :
6264 : {
6265 : (void)poOtherGeom;
6266 : #ifndef HAVE_GEOS
6267 :
6268 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6269 : return FALSE;
6270 :
6271 : #else
6272 22417 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
6273 : #endif // HAVE_GEOS
6274 : }
6275 :
6276 : /************************************************************************/
6277 : /* OGR_G_Within() */
6278 : /************************************************************************/
6279 :
6280 : /**
6281 : * \brief Test for containment.
6282 : *
6283 : * Tests if this geometry is within the other geometry.
6284 : *
6285 : * Geometry validity is not checked. In case you are unsure of the validity
6286 : * of the input geometries, call IsValid() before, otherwise the result might
6287 : * be wrong.
6288 : *
6289 : * This function is the same as the C++ method OGRGeometry::Within().
6290 : *
6291 : * This function is built on the GEOS library, check it for the definition
6292 : * of the geometry operation.
6293 : * If OGR is built without the GEOS library, this function will always fail,
6294 : * issuing a CPLE_NotSupported error.
6295 : *
6296 : * @param hThis the geometry to compare.
6297 : * @param hOther the other geometry to compare.
6298 : *
6299 : * @return TRUE if hThis is within hOther, otherwise FALSE.
6300 : */
6301 7374 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
6302 :
6303 : {
6304 7374 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
6305 :
6306 14748 : return OGRGeometry::FromHandle(hThis)->Within(
6307 14748 : OGRGeometry::FromHandle(hOther));
6308 : }
6309 :
6310 : /************************************************************************/
6311 : /* Contains() */
6312 : /************************************************************************/
6313 :
6314 : /**
6315 : * \brief Test for containment.
6316 : *
6317 : * Tests if actual geometry object contains the passed geometry.
6318 : *
6319 : * Geometry validity is not checked. In case you are unsure of the validity
6320 : * of the input geometries, call IsValid() before, otherwise the result might
6321 : * be wrong.
6322 : *
6323 : * This method is the same as the C function OGR_G_Contains().
6324 : *
6325 : * This method is built on the GEOS library, check it for the definition
6326 : * of the geometry operation.
6327 : * If OGR is built without the GEOS library, this method will always fail,
6328 : * issuing a CPLE_NotSupported error.
6329 : *
6330 : * @param poOtherGeom the geometry to compare to this geometry.
6331 : *
6332 : * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
6333 : */
6334 :
6335 322 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
6336 :
6337 : {
6338 : (void)poOtherGeom;
6339 : #ifndef HAVE_GEOS
6340 :
6341 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6342 : return FALSE;
6343 :
6344 : #else
6345 322 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
6346 : #endif // HAVE_GEOS
6347 : }
6348 :
6349 : /************************************************************************/
6350 : /* OGR_G_Contains() */
6351 : /************************************************************************/
6352 :
6353 : /**
6354 : * \brief Test for containment.
6355 : *
6356 : * Tests if this geometry contains the other geometry.
6357 : *
6358 : * Geometry validity is not checked. In case you are unsure of the validity
6359 : * of the input geometries, call IsValid() before, otherwise the result might
6360 : * be wrong.
6361 : *
6362 : * This function is the same as the C++ method OGRGeometry::Contains().
6363 : *
6364 : * This function is built on the GEOS library, check it for the definition
6365 : * of the geometry operation.
6366 : * If OGR is built without the GEOS library, this function will always fail,
6367 : * issuing a CPLE_NotSupported error.
6368 : *
6369 : * @param hThis the geometry to compare.
6370 : * @param hOther the other geometry to compare.
6371 : *
6372 : * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
6373 : */
6374 10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
6375 :
6376 : {
6377 10 : VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
6378 :
6379 20 : return OGRGeometry::FromHandle(hThis)->Contains(
6380 20 : OGRGeometry::FromHandle(hOther));
6381 : }
6382 :
6383 : /************************************************************************/
6384 : /* Overlaps() */
6385 : /************************************************************************/
6386 :
6387 : /**
6388 : * \brief Test for overlap.
6389 : *
6390 : * Tests if this geometry and the other passed into the method overlap, that is
6391 : * their intersection has a non-zero area.
6392 : *
6393 : * Geometry validity is not checked. In case you are unsure of the validity
6394 : * of the input geometries, call IsValid() before, otherwise the result might
6395 : * be wrong.
6396 : *
6397 : * This method is the same as the C function OGR_G_Overlaps().
6398 : *
6399 : * This method is built on the GEOS library, check it for the definition
6400 : * of the geometry operation.
6401 : * If OGR is built without the GEOS library, this method will always fail,
6402 : * issuing a CPLE_NotSupported error.
6403 : *
6404 : * @param poOtherGeom the geometry to compare to this geometry.
6405 : *
6406 : * @return TRUE if they are overlapping, otherwise FALSE.
6407 : */
6408 :
6409 7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
6410 :
6411 : {
6412 : (void)poOtherGeom;
6413 : #ifndef HAVE_GEOS
6414 :
6415 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6416 : return FALSE;
6417 :
6418 : #else
6419 7 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6420 : #endif // HAVE_GEOS
6421 : }
6422 :
6423 : /************************************************************************/
6424 : /* OGR_G_Overlaps() */
6425 : /************************************************************************/
6426 : /**
6427 : * \brief Test for overlap.
6428 : *
6429 : * Tests if this geometry and the other geometry overlap, that is their
6430 : * intersection has a non-zero area.
6431 : *
6432 : * Geometry validity is not checked. In case you are unsure of the validity
6433 : * of the input geometries, call IsValid() before, otherwise the result might
6434 : * be wrong.
6435 : *
6436 : * This function is the same as the C++ method OGRGeometry::Overlaps().
6437 : *
6438 : * This function is built on the GEOS library, check it for the definition
6439 : * of the geometry operation.
6440 : * If OGR is built without the GEOS library, this function will always fail,
6441 : * issuing a CPLE_NotSupported error.
6442 : *
6443 : * @param hThis the geometry to compare.
6444 : * @param hOther the other geometry to compare.
6445 : *
6446 : * @return TRUE if they are overlapping, otherwise FALSE.
6447 : */
6448 :
6449 7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6450 :
6451 : {
6452 7 : VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6453 :
6454 14 : return OGRGeometry::FromHandle(hThis)->Overlaps(
6455 14 : OGRGeometry::FromHandle(hOther));
6456 : }
6457 :
6458 : /************************************************************************/
6459 : /* closeRings() */
6460 : /************************************************************************/
6461 :
6462 : /**
6463 : * \brief Force rings to be closed.
6464 : *
6465 : * If this geometry, or any contained geometries has polygon rings that
6466 : * are not closed, they will be closed by adding the starting point at
6467 : * the end.
6468 : */
6469 :
6470 1264 : void OGRGeometry::closeRings()
6471 : {
6472 1264 : }
6473 :
6474 : /************************************************************************/
6475 : /* OGR_G_CloseRings() */
6476 : /************************************************************************/
6477 :
6478 : /**
6479 : * \brief Force rings to be closed.
6480 : *
6481 : * If this geometry, or any contained geometries has polygon rings that
6482 : * are not closed, they will be closed by adding the starting point at
6483 : * the end.
6484 : *
6485 : * @param hGeom handle to the geometry.
6486 : */
6487 :
6488 6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
6489 :
6490 : {
6491 6 : VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6492 :
6493 6 : OGRGeometry::FromHandle(hGeom)->closeRings();
6494 : }
6495 :
6496 : /************************************************************************/
6497 : /* Centroid() */
6498 : /************************************************************************/
6499 :
6500 : /**
6501 : * \brief Compute the geometry centroid.
6502 : *
6503 : * The centroid location is applied to the passed in OGRPoint object.
6504 : * The centroid is not necessarily within the geometry.
6505 : *
6506 : * This method relates to the SFCOM ISurface::get_Centroid() method
6507 : * however the current implementation based on GEOS can operate on other
6508 : * geometry types such as multipoint, linestring, geometrycollection such as
6509 : * multipolygons.
6510 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6511 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6512 : * (multipolygons).
6513 : *
6514 : * This function is the same as the C function OGR_G_Centroid().
6515 : *
6516 : * This function is built on the GEOS library, check it for the definition
6517 : * of the geometry operation.
6518 : * If OGR is built without the GEOS library, this function will always fail,
6519 : * issuing a CPLE_NotSupported error.
6520 : *
6521 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6522 : *
6523 : * to OGRPolygon)
6524 : */
6525 :
6526 5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6527 :
6528 : {
6529 5 : if (poPoint == nullptr)
6530 0 : return OGRERR_FAILURE;
6531 :
6532 : #ifndef HAVE_GEOS
6533 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6534 : return OGRERR_FAILURE;
6535 :
6536 : #else
6537 :
6538 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6539 : GEOSGeom hThisGeosGeom =
6540 5 : exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6541 :
6542 5 : if (hThisGeosGeom != nullptr)
6543 : {
6544 5 : GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6545 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6546 :
6547 5 : if (hOtherGeosGeom == nullptr)
6548 : {
6549 0 : freeGEOSContext(hGEOSCtxt);
6550 0 : return OGRERR_FAILURE;
6551 : }
6552 :
6553 : OGRGeometry *poCentroidGeom =
6554 5 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6555 :
6556 5 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6557 :
6558 5 : if (poCentroidGeom == nullptr)
6559 : {
6560 0 : freeGEOSContext(hGEOSCtxt);
6561 0 : return OGRERR_FAILURE;
6562 : }
6563 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6564 : {
6565 0 : delete poCentroidGeom;
6566 0 : freeGEOSContext(hGEOSCtxt);
6567 0 : return OGRERR_FAILURE;
6568 : }
6569 :
6570 5 : if (getSpatialReference() != nullptr)
6571 0 : poCentroidGeom->assignSpatialReference(getSpatialReference());
6572 :
6573 5 : OGRPoint *poCentroid = poCentroidGeom->toPoint();
6574 :
6575 5 : if (!poCentroid->IsEmpty())
6576 : {
6577 4 : poPoint->setX(poCentroid->getX());
6578 4 : poPoint->setY(poCentroid->getY());
6579 : }
6580 : else
6581 : {
6582 1 : poPoint->empty();
6583 : }
6584 :
6585 5 : delete poCentroidGeom;
6586 :
6587 5 : freeGEOSContext(hGEOSCtxt);
6588 5 : return OGRERR_NONE;
6589 : }
6590 : else
6591 : {
6592 0 : freeGEOSContext(hGEOSCtxt);
6593 0 : return OGRERR_FAILURE;
6594 : }
6595 :
6596 : #endif // HAVE_GEOS
6597 : }
6598 :
6599 : /************************************************************************/
6600 : /* OGR_G_Centroid() */
6601 : /************************************************************************/
6602 :
6603 : /**
6604 : * \brief Compute the geometry centroid.
6605 : *
6606 : * The centroid location is applied to the passed in OGRPoint object.
6607 : * The centroid is not necessarily within the geometry.
6608 : *
6609 : * This method relates to the SFCOM ISurface::get_Centroid() method
6610 : * however the current implementation based on GEOS can operate on other
6611 : * geometry types such as multipoint, linestring, geometrycollection such as
6612 : * multipolygons.
6613 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6614 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6615 : * (multipolygons).
6616 : *
6617 : * This function is the same as the C++ method OGRGeometry::Centroid().
6618 : *
6619 : * This function is built on the GEOS library, check it for the definition
6620 : * of the geometry operation.
6621 : * If OGR is built without the GEOS library, this function will always fail,
6622 : * issuing a CPLE_NotSupported error.
6623 : *
6624 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6625 : */
6626 :
6627 5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6628 :
6629 : {
6630 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6631 :
6632 5 : OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6633 5 : if (poCentroidGeom == nullptr)
6634 0 : return OGRERR_FAILURE;
6635 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6636 : {
6637 0 : CPLError(CE_Failure, CPLE_AppDefined,
6638 : "Passed wrong geometry type as centroid argument.");
6639 0 : return OGRERR_FAILURE;
6640 : }
6641 :
6642 5 : return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6643 : }
6644 :
6645 : /************************************************************************/
6646 : /* OGR_G_PointOnSurface() */
6647 : /************************************************************************/
6648 :
6649 : /**
6650 : * \brief Returns a point guaranteed to lie on the surface.
6651 : *
6652 : * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6653 : * however the current implementation based on GEOS can operate on other
6654 : * geometry types than the types that are supported by SQL/MM-Part 3 :
6655 : * surfaces (polygons) and multisurfaces (multipolygons).
6656 : *
6657 : * This method is built on the GEOS library, check it for the definition
6658 : * of the geometry operation.
6659 : * If OGR is built without the GEOS library, this method will always fail,
6660 : * issuing a CPLE_NotSupported error.
6661 : *
6662 : * @param hGeom the geometry to operate on.
6663 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6664 : * or NULL if an error occurs.
6665 : *
6666 : */
6667 :
6668 4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6669 :
6670 : {
6671 4 : VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6672 :
6673 : #ifndef HAVE_GEOS
6674 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6675 : return nullptr;
6676 : #else
6677 :
6678 4 : OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6679 :
6680 4 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6681 4 : GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6682 :
6683 4 : if (hThisGeosGeom != nullptr)
6684 : {
6685 : GEOSGeom hOtherGeosGeom =
6686 4 : GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6687 4 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6688 :
6689 4 : if (hOtherGeosGeom == nullptr)
6690 : {
6691 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6692 0 : return nullptr;
6693 : }
6694 :
6695 : OGRGeometry *poInsidePointGeom =
6696 4 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6697 :
6698 4 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6699 :
6700 4 : if (poInsidePointGeom == nullptr)
6701 : {
6702 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6703 0 : return nullptr;
6704 : }
6705 4 : if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6706 : {
6707 0 : delete poInsidePointGeom;
6708 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6709 0 : return nullptr;
6710 : }
6711 :
6712 4 : if (poThis->getSpatialReference() != nullptr)
6713 0 : poInsidePointGeom->assignSpatialReference(
6714 0 : poThis->getSpatialReference());
6715 :
6716 4 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6717 4 : return OGRGeometry::ToHandle(poInsidePointGeom);
6718 : }
6719 :
6720 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6721 0 : return nullptr;
6722 : #endif
6723 : }
6724 :
6725 : /************************************************************************/
6726 : /* PointOnSurfaceInternal() */
6727 : /************************************************************************/
6728 :
6729 : //! @cond Doxygen_Suppress
6730 0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6731 : {
6732 0 : if (poPoint == nullptr || poPoint->IsEmpty())
6733 0 : return OGRERR_FAILURE;
6734 :
6735 0 : OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6736 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6737 0 : if (hInsidePoint == nullptr)
6738 0 : return OGRERR_FAILURE;
6739 :
6740 0 : OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6741 0 : if (poInsidePoint->IsEmpty())
6742 : {
6743 0 : poPoint->empty();
6744 : }
6745 : else
6746 : {
6747 0 : poPoint->setX(poInsidePoint->getX());
6748 0 : poPoint->setY(poInsidePoint->getY());
6749 : }
6750 :
6751 0 : OGR_G_DestroyGeometry(hInsidePoint);
6752 :
6753 0 : return OGRERR_NONE;
6754 : }
6755 :
6756 : //! @endcond
6757 :
6758 : /************************************************************************/
6759 : /* Simplify() */
6760 : /************************************************************************/
6761 :
6762 : /**
6763 : * \brief Simplify the geometry.
6764 : *
6765 : * This function is the same as the C function OGR_G_Simplify().
6766 : *
6767 : * This function is built on the GEOS library, check it for the definition
6768 : * of the geometry operation.
6769 : * If OGR is built without the GEOS library, this function will always fail,
6770 : * issuing a CPLE_NotSupported error.
6771 : *
6772 : * @param dTolerance the distance tolerance for the simplification.
6773 : *
6774 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6775 : *
6776 : */
6777 :
6778 55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
6779 :
6780 : {
6781 : (void)dTolerance;
6782 : #ifndef HAVE_GEOS
6783 :
6784 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6785 : return nullptr;
6786 :
6787 : #else
6788 55 : OGRGeometry *poOGRProduct = nullptr;
6789 :
6790 55 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6791 55 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6792 55 : if (hThisGeosGeom != nullptr)
6793 : {
6794 : GEOSGeom hGeosProduct =
6795 55 : GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6796 55 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6797 : poOGRProduct =
6798 55 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6799 : }
6800 55 : freeGEOSContext(hGEOSCtxt);
6801 55 : return poOGRProduct;
6802 :
6803 : #endif // HAVE_GEOS
6804 : }
6805 :
6806 : /************************************************************************/
6807 : /* OGR_G_Simplify() */
6808 : /************************************************************************/
6809 :
6810 : /**
6811 : * \brief Compute a simplified geometry.
6812 : *
6813 : * This function is the same as the C++ method OGRGeometry::Simplify().
6814 : *
6815 : * This function is built on the GEOS library, check it for the definition
6816 : * of the geometry operation.
6817 : * If OGR is built without the GEOS library, this function will always fail,
6818 : * issuing a CPLE_NotSupported error.
6819 : *
6820 : * @param hThis the geometry.
6821 : * @param dTolerance the distance tolerance for the simplification.
6822 : *
6823 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6824 : * or NULL if an error occurs.
6825 : *
6826 : */
6827 :
6828 1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6829 :
6830 : {
6831 1 : VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6832 1 : return OGRGeometry::ToHandle(
6833 1 : OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6834 : }
6835 :
6836 : /************************************************************************/
6837 : /* SimplifyPreserveTopology() */
6838 : /************************************************************************/
6839 :
6840 : /**
6841 : * \brief Simplify the geometry while preserving topology.
6842 : *
6843 : * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6844 : *
6845 : * This function is built on the GEOS library, check it for the definition
6846 : * of the geometry operation.
6847 : * If OGR is built without the GEOS library, this function will always fail,
6848 : * issuing a CPLE_NotSupported error.
6849 : *
6850 : * @param dTolerance the distance tolerance for the simplification.
6851 : *
6852 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6853 : *
6854 : */
6855 :
6856 17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
6857 :
6858 : {
6859 : (void)dTolerance;
6860 : #ifndef HAVE_GEOS
6861 :
6862 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6863 : return nullptr;
6864 :
6865 : #else
6866 17 : OGRGeometry *poOGRProduct = nullptr;
6867 :
6868 17 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6869 17 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6870 17 : if (hThisGeosGeom != nullptr)
6871 : {
6872 17 : GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6873 : hGEOSCtxt, hThisGeosGeom, dTolerance);
6874 17 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6875 : poOGRProduct =
6876 17 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6877 : }
6878 17 : freeGEOSContext(hGEOSCtxt);
6879 17 : return poOGRProduct;
6880 :
6881 : #endif // HAVE_GEOS
6882 : }
6883 :
6884 : /************************************************************************/
6885 : /* OGR_G_SimplifyPreserveTopology() */
6886 : /************************************************************************/
6887 :
6888 : /**
6889 : * \brief Simplify the geometry while preserving topology.
6890 : *
6891 : * This function is the same as the C++ method
6892 : * OGRGeometry::SimplifyPreserveTopology().
6893 : *
6894 : * This function is built on the GEOS library, check it for the definition
6895 : * of the geometry operation.
6896 : * If OGR is built without the GEOS library, this function will always fail,
6897 : * issuing a CPLE_NotSupported error.
6898 : *
6899 : * @param hThis the geometry.
6900 : * @param dTolerance the distance tolerance for the simplification.
6901 : *
6902 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6903 : * or NULL if an error occurs.
6904 : *
6905 : */
6906 :
6907 1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6908 : double dTolerance)
6909 :
6910 : {
6911 1 : VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6912 1 : return OGRGeometry::ToHandle(
6913 1 : OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6914 : }
6915 :
6916 : /************************************************************************/
6917 : /* roundCoordinates() */
6918 : /************************************************************************/
6919 :
6920 : /** Round coordinates of the geometry to the specified precision.
6921 : *
6922 : * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6923 : * will return valid geometries, whereas roundCoordinates() does not make
6924 : * such guarantee and may return geometries with invalidities, if they are
6925 : * not compatible with the specified precision. roundCoordinates() supports
6926 : * curve geometries, whereas SetPrecision() does not currently.
6927 : *
6928 : * One use case for roundCoordinates() is to undo the effect of
6929 : * quantizeCoordinates().
6930 : *
6931 : * @param sPrecision Contains the precision requirements.
6932 : * @since GDAL 3.9
6933 : */
6934 39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6935 : {
6936 : struct Rounder : public OGRDefaultGeometryVisitor
6937 : {
6938 : const OGRGeomCoordinatePrecision &m_precision;
6939 : const double m_invXYResolution;
6940 : const double m_invZResolution;
6941 : const double m_invMResolution;
6942 :
6943 39 : explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6944 39 : : m_precision(sPrecisionIn),
6945 39 : m_invXYResolution(m_precision.dfXYResolution !=
6946 : OGRGeomCoordinatePrecision::UNKNOWN
6947 39 : ? 1.0 / m_precision.dfXYResolution
6948 : : 0.0),
6949 39 : m_invZResolution(m_precision.dfZResolution !=
6950 : OGRGeomCoordinatePrecision::UNKNOWN
6951 39 : ? 1.0 / m_precision.dfZResolution
6952 : : 0.0),
6953 39 : m_invMResolution(m_precision.dfMResolution !=
6954 : OGRGeomCoordinatePrecision::UNKNOWN
6955 39 : ? 1.0 / m_precision.dfMResolution
6956 117 : : 0.0)
6957 : {
6958 39 : }
6959 :
6960 : using OGRDefaultGeometryVisitor::visit;
6961 :
6962 379 : void visit(OGRPoint *poPoint) override
6963 : {
6964 379 : if (m_precision.dfXYResolution !=
6965 : OGRGeomCoordinatePrecision::UNKNOWN)
6966 : {
6967 379 : poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6968 379 : m_precision.dfXYResolution);
6969 379 : poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6970 379 : m_precision.dfXYResolution);
6971 : }
6972 758 : if (m_precision.dfZResolution !=
6973 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6974 4 : poPoint->Is3D())
6975 : {
6976 4 : poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6977 4 : m_precision.dfZResolution);
6978 : }
6979 758 : if (m_precision.dfMResolution !=
6980 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6981 4 : poPoint->IsMeasured())
6982 : {
6983 4 : poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6984 4 : m_precision.dfMResolution);
6985 : }
6986 379 : }
6987 : };
6988 :
6989 78 : Rounder rounder(sPrecision);
6990 39 : accept(&rounder);
6991 39 : }
6992 :
6993 : /************************************************************************/
6994 : /* SetPrecision() */
6995 : /************************************************************************/
6996 :
6997 : /** Set the geometry's precision, rounding all its coordinates to the precision
6998 : * grid, and making sure the geometry is still valid.
6999 : *
7000 : * This is a stronger version of roundCoordinates().
7001 : *
7002 : * Note that at time of writing GEOS does no supported curve geometries. So
7003 : * currently if this function is called on such a geometry, OGR will first call
7004 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7005 : * that it is unlikely to yield to the expected result.
7006 : *
7007 : * This function is the same as the C function OGR_G_SetPrecision().
7008 : *
7009 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7010 : * GEOS library. Check it for the definition of the geometry operation.
7011 : * If OGR is built without the GEOS library, this function will always fail,
7012 : * issuing a CPLE_NotSupported error.
7013 : *
7014 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7015 : * precision.
7016 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7017 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7018 : *
7019 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7020 : *
7021 : * @since GDAL 3.9
7022 : */
7023 :
7024 6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
7025 : {
7026 : (void)dfGridSize;
7027 : (void)nFlags;
7028 : #ifndef HAVE_GEOS
7029 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7030 : return nullptr;
7031 :
7032 : #else
7033 6 : OGRGeometry *poOGRProduct = nullptr;
7034 :
7035 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7036 6 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7037 6 : if (hThisGeosGeom != nullptr)
7038 : {
7039 6 : GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
7040 : hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
7041 6 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7042 : poOGRProduct =
7043 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7044 : }
7045 6 : freeGEOSContext(hGEOSCtxt);
7046 6 : return poOGRProduct;
7047 :
7048 : #endif // HAVE_GEOS
7049 : }
7050 :
7051 : /************************************************************************/
7052 : /* OGR_G_SetPrecision() */
7053 : /************************************************************************/
7054 :
7055 : /** Set the geometry's precision, rounding all its coordinates to the precision
7056 : * grid, and making sure the geometry is still valid.
7057 : *
7058 : * This is a stronger version of roundCoordinates().
7059 : *
7060 : * Note that at time of writing GEOS does no supported curve geometries. So
7061 : * currently if this function is called on such a geometry, OGR will first call
7062 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7063 : * that it is unlikely to yield to the expected result.
7064 : *
7065 : * This function is the same as the C++ method OGRGeometry::SetPrecision().
7066 : *
7067 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7068 : * GEOS library. Check it for the definition of the geometry operation.
7069 : * If OGR is built without the GEOS library, this function will always fail,
7070 : * issuing a CPLE_NotSupported error.
7071 : *
7072 : * @param hThis the geometry.
7073 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7074 : * precision.
7075 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7076 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7077 : *
7078 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7079 : * or NULL if an error occurs.
7080 : *
7081 : * @since GDAL 3.9
7082 : */
7083 1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
7084 : int nFlags)
7085 : {
7086 1 : VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
7087 1 : return OGRGeometry::ToHandle(
7088 1 : OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
7089 : }
7090 :
7091 : /************************************************************************/
7092 : /* DelaunayTriangulation() */
7093 : /************************************************************************/
7094 :
7095 : /**
7096 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7097 : *
7098 : * This function is the same as the C function OGR_G_DelaunayTriangulation().
7099 : *
7100 : * This function is built on the GEOS library, v3.4 or above.
7101 : * If OGR is built without the GEOS library, this function will always fail,
7102 : * issuing a CPLE_NotSupported error.
7103 : *
7104 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7105 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7106 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7107 : *
7108 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7109 : */
7110 :
7111 : #ifndef HAVE_GEOS
7112 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
7113 : int /*bOnlyEdges*/) const
7114 : {
7115 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7116 : return nullptr;
7117 : }
7118 : #else
7119 1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
7120 : int bOnlyEdges) const
7121 : {
7122 1 : OGRGeometry *poOGRProduct = nullptr;
7123 :
7124 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7125 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7126 1 : if (hThisGeosGeom != nullptr)
7127 : {
7128 1 : GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
7129 : hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
7130 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7131 : poOGRProduct =
7132 1 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7133 : }
7134 1 : freeGEOSContext(hGEOSCtxt);
7135 1 : return poOGRProduct;
7136 : }
7137 : #endif
7138 :
7139 : /************************************************************************/
7140 : /* OGR_G_DelaunayTriangulation() */
7141 : /************************************************************************/
7142 :
7143 : /**
7144 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7145 : *
7146 : * This function is the same as the C++ method
7147 : * OGRGeometry::DelaunayTriangulation().
7148 : *
7149 : * This function is built on the GEOS library, v3.4 or above.
7150 : * If OGR is built without the GEOS library, this function will always fail,
7151 : * issuing a CPLE_NotSupported error.
7152 : *
7153 : * @param hThis the geometry.
7154 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7155 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7156 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7157 : *
7158 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7159 : * or NULL if an error occurs.
7160 : */
7161 :
7162 1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
7163 : int bOnlyEdges)
7164 :
7165 : {
7166 1 : VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
7167 :
7168 1 : return OGRGeometry::ToHandle(
7169 : OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
7170 1 : bOnlyEdges));
7171 : }
7172 :
7173 : /************************************************************************/
7174 : /* ConstrainedDelaunayTriangulation() */
7175 : /************************************************************************/
7176 :
7177 : /**
7178 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7179 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7180 : * geometry collection.
7181 : *
7182 : * This function is the same as the C function
7183 : * OGR_G_ConstrainedDelaunayTriangulation().
7184 : *
7185 : * This function is built on the GEOS library, v3.10 or above.
7186 : * If OGR is built without the GEOS library, this function will always fail,
7187 : * issuing a CPLE_NotSupported error.
7188 : *
7189 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7190 : *
7191 : * @since OGR 3.12
7192 : */
7193 :
7194 3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
7195 : {
7196 : #ifndef HAVE_GEOS
7197 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7198 : return nullptr;
7199 : #elif !(GEOS_VERSION_MAJOR > 3 || \
7200 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
7201 : CPLError(
7202 : CE_Failure, CPLE_NotSupported,
7203 : "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
7204 : return nullptr;
7205 : #else
7206 :
7207 3 : OGRGeometry *poOGRProduct = nullptr;
7208 :
7209 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7210 3 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7211 3 : if (hThisGeosGeom != nullptr)
7212 : {
7213 : GEOSGeom hGeosProduct =
7214 3 : GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
7215 3 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7216 : poOGRProduct =
7217 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7218 : }
7219 3 : freeGEOSContext(hGEOSCtxt);
7220 3 : return poOGRProduct;
7221 : #endif
7222 : }
7223 :
7224 : /************************************************************************/
7225 : /* OGR_G_ConstrainedDelaunayTriangulation() */
7226 : /************************************************************************/
7227 :
7228 : /**
7229 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7230 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7231 : * geometry collection.
7232 : *
7233 : * This function is the same as the C++ method
7234 : * OGRGeometry::ConstrainedDelaunayTriangulation().
7235 : *
7236 : * This function is built on the GEOS library, v3.10 or above.
7237 : * If OGR is built without the GEOS library, this function will always fail,
7238 : * issuing a CPLE_NotSupported error.
7239 : *
7240 : * @param hThis the geometry.
7241 : *
7242 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7243 : * or NULL if an error occurs.
7244 : *
7245 : * @since OGR 3.12
7246 : */
7247 :
7248 3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
7249 : {
7250 3 : VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
7251 :
7252 3 : return OGRGeometry::ToHandle(
7253 3 : OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
7254 : }
7255 :
7256 : /************************************************************************/
7257 : /* Polygonize() */
7258 : /************************************************************************/
7259 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
7260 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
7261 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
7262 : /* AMBIENTALE */
7263 : /************************************************************************/
7264 :
7265 : /**
7266 : * \brief Polygonizes a set of sparse edges.
7267 : *
7268 : * A new geometry object is created and returned containing a collection
7269 : * of reassembled Polygons: NULL will be returned if the input collection
7270 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7271 : * into Polygons is impossible due to topological inconsistencies.
7272 : *
7273 : * This method is the same as the C function OGR_G_Polygonize().
7274 : *
7275 : * This method is built on the GEOS library, check it for the definition
7276 : * of the geometry operation.
7277 : * If OGR is built without the GEOS library, this method will always fail,
7278 : * issuing a CPLE_NotSupported error.
7279 : *
7280 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7281 : *
7282 : */
7283 :
7284 117 : OGRGeometry *OGRGeometry::Polygonize() const
7285 :
7286 : {
7287 : #ifndef HAVE_GEOS
7288 :
7289 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7290 : return nullptr;
7291 :
7292 : #else
7293 :
7294 117 : const OGRGeometryCollection *poColl = nullptr;
7295 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
7296 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
7297 116 : poColl = toGeometryCollection();
7298 : else
7299 1 : return nullptr;
7300 :
7301 116 : const int nCount = poColl->getNumGeometries();
7302 :
7303 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
7304 116 : bool bError = false;
7305 :
7306 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7307 :
7308 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
7309 747 : for (int ig = 0; ig < nCount; ig++)
7310 : {
7311 631 : GEOSGeom hGeosGeom = nullptr;
7312 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
7313 1262 : if (poChild == nullptr ||
7314 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
7315 1 : bError = true;
7316 : else
7317 : {
7318 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
7319 630 : if (hGeosGeom == nullptr)
7320 0 : bError = true;
7321 : }
7322 631 : pahGeosGeomList[ig] = hGeosGeom;
7323 : }
7324 :
7325 116 : if (!bError)
7326 : {
7327 : GEOSGeom hGeosPolygs =
7328 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
7329 :
7330 : poPolygsOGRGeom =
7331 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7332 : }
7333 :
7334 747 : for (int ig = 0; ig < nCount; ig++)
7335 : {
7336 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
7337 631 : if (hGeosGeom != nullptr)
7338 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
7339 : }
7340 116 : delete[] pahGeosGeomList;
7341 116 : freeGEOSContext(hGEOSCtxt);
7342 :
7343 116 : return poPolygsOGRGeom;
7344 :
7345 : #endif // HAVE_GEOS
7346 : }
7347 :
7348 : /************************************************************************/
7349 : /* OGR_G_Polygonize() */
7350 : /************************************************************************/
7351 : /**
7352 : * \brief Polygonizes a set of sparse edges.
7353 : *
7354 : * A new geometry object is created and returned containing a collection
7355 : * of reassembled Polygons: NULL will be returned if the input collection
7356 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7357 : * into Polygons is impossible due to topological inconsistencies.
7358 : *
7359 : * This function is the same as the C++ method OGRGeometry::Polygonize().
7360 : *
7361 : * This function is built on the GEOS library, check it for the definition
7362 : * of the geometry operation.
7363 : * If OGR is built without the GEOS library, this function will always fail,
7364 : * issuing a CPLE_NotSupported error.
7365 : *
7366 : * @param hTarget The Geometry to be polygonized.
7367 : *
7368 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7369 : * or NULL if an error occurs.
7370 : *
7371 : */
7372 :
7373 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
7374 :
7375 : {
7376 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
7377 :
7378 3 : return OGRGeometry::ToHandle(
7379 3 : OGRGeometry::FromHandle(hTarget)->Polygonize());
7380 : }
7381 :
7382 : /************************************************************************/
7383 : /* BuildArea() */
7384 : /************************************************************************/
7385 :
7386 : /**
7387 : * \brief Polygonize a linework assuming inner polygons are holes.
7388 : *
7389 : * This method is the same as the C function OGR_G_BuildArea().
7390 : *
7391 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
7392 : * Additionally, holes are dropped and the result is unified producing
7393 : * a single Polygon or a MultiPolygon.
7394 : *
7395 : * A new geometry object is created and returned: NULL on failure,
7396 : * empty GeometryCollection if the input geometry cannot be polygonized,
7397 : * Polygon or MultiPolygon on success.
7398 : *
7399 : * This method is built on the GEOSBuildArea_r() function of the GEOS
7400 : * library, check it for the definition of the geometry operation.
7401 : * If OGR is built without the GEOS library, this method will always fail,
7402 : * issuing a CPLE_NotSupported error.
7403 : *
7404 : * @return a newly allocated geometry now owned by the caller,
7405 : * or NULL on failure.
7406 : *
7407 : * @since OGR 3.11
7408 : */
7409 :
7410 30 : OGRGeometry *OGRGeometry::BuildArea() const
7411 :
7412 : {
7413 : #ifndef HAVE_GEOS
7414 :
7415 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7416 : return nullptr;
7417 :
7418 : #else
7419 :
7420 30 : OGRGeometry *poPolygsOGRGeom = nullptr;
7421 :
7422 30 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7423 30 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7424 30 : if (hThisGeosGeom != nullptr)
7425 : {
7426 30 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7427 : poPolygsOGRGeom =
7428 30 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7429 30 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7430 : }
7431 30 : freeGEOSContext(hGEOSCtxt);
7432 :
7433 30 : return poPolygsOGRGeom;
7434 :
7435 : #endif // HAVE_GEOS
7436 : }
7437 :
7438 : /************************************************************************/
7439 : /* OGR_G_BuildArea() */
7440 : /************************************************************************/
7441 :
7442 : /**
7443 : * \brief Polygonize a linework assuming inner polygons are holes.
7444 : *
7445 : * This function is the same as the C++ method OGRGeometry::BuildArea().
7446 : *
7447 : * Polygonization is performed similarly to OGR_G_Polygonize().
7448 : * Additionally, holes are dropped and the result is unified producing
7449 : * a single Polygon or a MultiPolygon.
7450 : *
7451 : * A new geometry object is created and returned: NULL on failure,
7452 : * empty GeometryCollection if the input geometry cannot be polygonized,
7453 : * Polygon or MultiPolygon on success.
7454 : *
7455 : * This function is built on the GEOSBuildArea_r() function of the GEOS
7456 : * library, check it for the definition of the geometry operation.
7457 : * If OGR is built without the GEOS library, this function will always fail,
7458 : * issuing a CPLE_NotSupported error.
7459 : *
7460 : * @param hGeom handle on the geometry to polygonize.
7461 : *
7462 : * @return a handle on newly allocated geometry now owned by the caller,
7463 : * or NULL on failure.
7464 : *
7465 : * @since OGR 3.11
7466 : */
7467 :
7468 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7469 :
7470 : {
7471 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7472 :
7473 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7474 : }
7475 :
7476 : /************************************************************************/
7477 : /* swapXY() */
7478 : /************************************************************************/
7479 :
7480 : /**
7481 : * \brief Swap x and y coordinates.
7482 : *
7483 : */
7484 :
7485 0 : void OGRGeometry::swapXY()
7486 :
7487 : {
7488 0 : }
7489 :
7490 : /************************************************************************/
7491 : /* swapXY() */
7492 : /************************************************************************/
7493 :
7494 : /**
7495 : * \brief Swap x and y coordinates.
7496 : *
7497 : * @param hGeom geometry.
7498 : */
7499 :
7500 32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7501 : {
7502 32 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7503 :
7504 32 : OGRGeometry::FromHandle(hGeom)->swapXY();
7505 : }
7506 :
7507 : /************************************************************************/
7508 : /* Prepared geometry API */
7509 : /************************************************************************/
7510 :
7511 : #if defined(HAVE_GEOS)
7512 : struct _OGRPreparedGeometry
7513 : {
7514 : GEOSContextHandle_t hGEOSCtxt;
7515 : GEOSGeom hGEOSGeom;
7516 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7517 : };
7518 : #endif
7519 :
7520 : /************************************************************************/
7521 : /* OGRHasPreparedGeometrySupport() */
7522 : /************************************************************************/
7523 :
7524 : /** Returns if GEOS has prepared geometry support.
7525 : * @return TRUE or FALSE
7526 : */
7527 1 : int OGRHasPreparedGeometrySupport()
7528 : {
7529 : #if defined(HAVE_GEOS)
7530 1 : return TRUE;
7531 : #else
7532 : return FALSE;
7533 : #endif
7534 : }
7535 :
7536 : /************************************************************************/
7537 : /* OGRCreatePreparedGeometry() */
7538 : /************************************************************************/
7539 :
7540 : /** Creates a prepared geometry.
7541 : *
7542 : * To free with OGRDestroyPreparedGeometry()
7543 : *
7544 : * @param hGeom input geometry to prepare.
7545 : * @return handle to a prepared geometry.
7546 : * @since GDAL 3.3
7547 : */
7548 53068 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
7549 : {
7550 : (void)hGeom;
7551 : #if defined(HAVE_GEOS)
7552 53068 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7553 53068 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7554 53068 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7555 53068 : if (hGEOSGeom == nullptr)
7556 : {
7557 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7558 0 : return nullptr;
7559 : }
7560 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7561 53068 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7562 53068 : if (poPreparedGEOSGeom == nullptr)
7563 : {
7564 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7565 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7566 0 : return nullptr;
7567 : }
7568 :
7569 53068 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7570 53068 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7571 53068 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7572 53068 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7573 :
7574 53068 : return poPreparedGeom;
7575 : #else
7576 : return nullptr;
7577 : #endif
7578 : }
7579 :
7580 : /************************************************************************/
7581 : /* OGRDestroyPreparedGeometry() */
7582 : /************************************************************************/
7583 :
7584 : /** Destroys a prepared geometry.
7585 : * @param hPreparedGeom prepared geometry.
7586 : * @since GDAL 3.3
7587 : */
7588 53114 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
7589 : {
7590 : (void)hPreparedGeom;
7591 : #if defined(HAVE_GEOS)
7592 53114 : if (hPreparedGeom != nullptr)
7593 : {
7594 53068 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7595 : hPreparedGeom->poPreparedGEOSGeom);
7596 53068 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7597 53068 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7598 53068 : delete hPreparedGeom;
7599 : }
7600 : #endif
7601 53114 : }
7602 :
7603 : /************************************************************************/
7604 : /* OGRPreparedGeometryIntersects() */
7605 : /************************************************************************/
7606 :
7607 : /** Returns whether a prepared geometry intersects with a geometry.
7608 : * @param hPreparedGeom prepared geometry.
7609 : * @param hOtherGeom other geometry.
7610 : * @return TRUE or FALSE.
7611 : * @since GDAL 3.3
7612 : */
7613 5604 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
7614 : const OGRGeometryH hOtherGeom)
7615 : {
7616 : (void)hPreparedGeom;
7617 : (void)hOtherGeom;
7618 : #if defined(HAVE_GEOS)
7619 5604 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7620 5604 : if (hPreparedGeom == nullptr ||
7621 : poOtherGeom == nullptr
7622 : // The check for IsEmpty() is for buggy GEOS versions.
7623 : // See https://github.com/libgeos/geos/pull/423
7624 11208 : || poOtherGeom->IsEmpty())
7625 : {
7626 1 : return FALSE;
7627 : }
7628 :
7629 : GEOSGeom hGEOSOtherGeom =
7630 5603 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7631 5603 : if (hGEOSOtherGeom == nullptr)
7632 0 : return FALSE;
7633 :
7634 : const bool bRet =
7635 5603 : GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
7636 : hPreparedGeom->poPreparedGEOSGeom,
7637 5603 : hGEOSOtherGeom) == 1;
7638 5603 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7639 :
7640 5603 : return bRet;
7641 : #else
7642 : return FALSE;
7643 : #endif
7644 : }
7645 :
7646 : /** Returns whether a prepared geometry contains a geometry.
7647 : * @param hPreparedGeom prepared geometry.
7648 : * @param hOtherGeom other geometry.
7649 : * @return TRUE or FALSE.
7650 : */
7651 120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
7652 : const OGRGeometryH hOtherGeom)
7653 : {
7654 : (void)hPreparedGeom;
7655 : (void)hOtherGeom;
7656 : #if defined(HAVE_GEOS)
7657 120516 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7658 120516 : if (hPreparedGeom == nullptr ||
7659 : poOtherGeom == nullptr
7660 : // The check for IsEmpty() is for buggy GEOS versions.
7661 : // See https://github.com/libgeos/geos/pull/423
7662 241032 : || poOtherGeom->IsEmpty())
7663 : {
7664 1 : return FALSE;
7665 : }
7666 :
7667 : GEOSGeom hGEOSOtherGeom =
7668 120515 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7669 120515 : if (hGEOSOtherGeom == nullptr)
7670 0 : return FALSE;
7671 :
7672 120515 : const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
7673 : hPreparedGeom->poPreparedGEOSGeom,
7674 120515 : hGEOSOtherGeom) == 1;
7675 120515 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7676 :
7677 120515 : return bRet;
7678 : #else
7679 : return FALSE;
7680 : #endif
7681 : }
7682 :
7683 : /************************************************************************/
7684 : /* OGRGeometryFromEWKB() */
7685 : /************************************************************************/
7686 :
7687 1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7688 : int bIsPostGIS1_EWKB)
7689 :
7690 : {
7691 1445 : OGRGeometry *poGeometry = nullptr;
7692 :
7693 1445 : size_t nWKBSize = 0;
7694 1445 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7695 1445 : if (pabyWKB == nullptr)
7696 0 : return nullptr;
7697 :
7698 : /* -------------------------------------------------------------------- */
7699 : /* Try to ingest the geometry. */
7700 : /* -------------------------------------------------------------------- */
7701 1445 : (void)OGRGeometryFactory::createFromWkb(
7702 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7703 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7704 :
7705 1445 : return poGeometry;
7706 : }
7707 :
7708 : /************************************************************************/
7709 : /* OGRGeometryFromHexEWKB() */
7710 : /************************************************************************/
7711 :
7712 1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7713 : int bIsPostGIS1_EWKB)
7714 :
7715 : {
7716 1443 : if (pszBytea == nullptr)
7717 0 : return nullptr;
7718 :
7719 1443 : int nWKBLength = 0;
7720 1443 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7721 :
7722 : OGRGeometry *poGeometry =
7723 1443 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7724 :
7725 1443 : CPLFree(pabyWKB);
7726 :
7727 1443 : return poGeometry;
7728 : }
7729 :
7730 : /************************************************************************/
7731 : /* OGRGeometryToHexEWKB() */
7732 : /************************************************************************/
7733 :
7734 1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
7735 : int nPostGISMajor, int nPostGISMinor)
7736 : {
7737 1071 : const size_t nWkbSize = poGeometry->WkbSize();
7738 1071 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7739 1071 : if (pabyWKB == nullptr)
7740 0 : return CPLStrdup("");
7741 :
7742 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7743 1815 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7744 626 : poGeometry->IsEmpty())
7745 : {
7746 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7747 : OGRERR_NONE)
7748 : {
7749 0 : CPLFree(pabyWKB);
7750 0 : return CPLStrdup("");
7751 : }
7752 : }
7753 1069 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7754 : (nPostGISMajor < 2)
7755 : ? wkbVariantPostGIS1
7756 1069 : : wkbVariantOldOgc) != OGRERR_NONE)
7757 : {
7758 0 : CPLFree(pabyWKB);
7759 0 : return CPLStrdup("");
7760 : }
7761 :
7762 : // When converting to hex, each byte takes 2 hex characters. In addition
7763 : // we add in 8 characters to represent the SRID integer in hex, and
7764 : // one for a null terminator.
7765 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7766 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7767 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7768 1071 : if (nWkbSize >
7769 1071 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7770 : {
7771 0 : CPLFree(pabyWKB);
7772 0 : return CPLStrdup("");
7773 : }
7774 1071 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7775 1071 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7776 1071 : if (pszTextBuf == nullptr)
7777 : {
7778 0 : CPLFree(pabyWKB);
7779 0 : return CPLStrdup("");
7780 : }
7781 1071 : char *pszTextBufCurrent = pszTextBuf;
7782 :
7783 : // Convert the 1st byte, which is the endianness flag, to hex.
7784 1071 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7785 1071 : strcpy(pszTextBufCurrent, pszHex);
7786 1071 : CPLFree(pszHex);
7787 1071 : pszTextBufCurrent += 2;
7788 :
7789 : // Next, get the geom type which is bytes 2 through 5.
7790 : GUInt32 geomType;
7791 1071 : memcpy(&geomType, pabyWKB + 1, 4);
7792 :
7793 : // Now add the SRID flag if an SRID is provided.
7794 1071 : if (nSRSId > 0)
7795 : {
7796 : // Change the flag to wkbNDR (little) endianness.
7797 541 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7798 541 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7799 : // Apply the flag.
7800 541 : geomType = geomType | nGSrsFlag;
7801 : }
7802 :
7803 : // Now write the geom type which is 4 bytes.
7804 1071 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7805 1071 : strcpy(pszTextBufCurrent, pszHex);
7806 1071 : CPLFree(pszHex);
7807 1071 : pszTextBufCurrent += 8;
7808 :
7809 : // Now include SRID if provided.
7810 1071 : if (nSRSId > 0)
7811 : {
7812 : // Force the srsid to wkbNDR (little) endianness.
7813 541 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7814 541 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7815 : reinterpret_cast<const GByte *>(&nGSRSId));
7816 541 : strcpy(pszTextBufCurrent, pszHex);
7817 541 : CPLFree(pszHex);
7818 541 : pszTextBufCurrent += 8;
7819 : }
7820 :
7821 : // Copy the rest of the data over - subtract
7822 : // 5 since we already copied 5 bytes above.
7823 1071 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7824 1071 : CPLFree(pabyWKB);
7825 1071 : if (!pszHex || pszHex[0] == 0)
7826 : {
7827 0 : CPLFree(pszTextBuf);
7828 0 : return pszHex;
7829 : }
7830 1071 : strcpy(pszTextBufCurrent, pszHex);
7831 1071 : CPLFree(pszHex);
7832 :
7833 1071 : return pszTextBuf;
7834 : }
7835 :
7836 : /************************************************************************/
7837 : /* importPreambleFromWkb() */
7838 : /************************************************************************/
7839 :
7840 : //! @cond Doxygen_Suppress
7841 160168 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7842 : size_t nSize,
7843 : OGRwkbByteOrder &eByteOrder,
7844 : OGRwkbVariant eWkbVariant)
7845 : {
7846 160168 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7847 0 : return OGRERR_NOT_ENOUGH_DATA;
7848 :
7849 : /* -------------------------------------------------------------------- */
7850 : /* Get the byte order byte. */
7851 : /* -------------------------------------------------------------------- */
7852 160168 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7853 160168 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7854 0 : return OGRERR_CORRUPT_DATA;
7855 160168 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7856 :
7857 : /* -------------------------------------------------------------------- */
7858 : /* Get the geometry feature type. */
7859 : /* -------------------------------------------------------------------- */
7860 : OGRwkbGeometryType eGeometryType;
7861 : const OGRErr err =
7862 160168 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7863 160168 : if (wkbHasZ(eGeometryType))
7864 62309 : flags |= OGR_G_3D;
7865 160168 : if (wkbHasM(eGeometryType))
7866 59692 : flags |= OGR_G_MEASURED;
7867 :
7868 160168 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7869 0 : return OGRERR_CORRUPT_DATA;
7870 :
7871 160168 : return OGRERR_NONE;
7872 : }
7873 :
7874 : /************************************************************************/
7875 : /* importPreambleOfCollectionFromWkb() */
7876 : /* */
7877 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7878 : /* OGRCurvePolygon and OGRGeometryCollection. */
7879 : /************************************************************************/
7880 :
7881 76787 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7882 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7883 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7884 : OGRwkbVariant eWkbVariant)
7885 : {
7886 76787 : nGeomCount = 0;
7887 :
7888 : OGRErr eErr =
7889 76787 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7890 76787 : if (eErr != OGRERR_NONE)
7891 0 : return eErr;
7892 :
7893 : /* -------------------------------------------------------------------- */
7894 : /* Clear existing Geoms. */
7895 : /* -------------------------------------------------------------------- */
7896 76787 : int _flags = flags; // flags set in importPreambleFromWkb
7897 76787 : empty(); // may reset flags etc.
7898 :
7899 : // restore
7900 76787 : if (_flags & OGR_G_3D)
7901 59265 : set3D(TRUE);
7902 76787 : if (_flags & OGR_G_MEASURED)
7903 56768 : setMeasured(TRUE);
7904 :
7905 : /* -------------------------------------------------------------------- */
7906 : /* Get the sub-geometry count. */
7907 : /* -------------------------------------------------------------------- */
7908 76787 : memcpy(&nGeomCount, pabyData + 5, 4);
7909 :
7910 76787 : if (OGR_SWAP(eByteOrder))
7911 386 : nGeomCount = CPL_SWAP32(nGeomCount);
7912 :
7913 153440 : if (nGeomCount < 0 ||
7914 76653 : static_cast<size_t>(nGeomCount) >
7915 76653 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7916 : {
7917 134 : nGeomCount = 0;
7918 134 : return OGRERR_CORRUPT_DATA;
7919 : }
7920 76653 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7921 :
7922 : // Each ring has a minimum of nMinSubGeomSize bytes.
7923 76653 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7924 : {
7925 910 : CPLError(CE_Failure, CPLE_AppDefined,
7926 : "Length of input WKB is too small");
7927 910 : nGeomCount = 0;
7928 910 : return OGRERR_NOT_ENOUGH_DATA;
7929 : }
7930 :
7931 75743 : nDataOffset = 9;
7932 75743 : if (nSize != static_cast<size_t>(-1))
7933 : {
7934 75723 : CPLAssert(nSize >= nDataOffset);
7935 75723 : nSize -= nDataOffset;
7936 : }
7937 :
7938 75743 : return OGRERR_NONE;
7939 : }
7940 :
7941 : /************************************************************************/
7942 : /* importCurveCollectionFromWkt() */
7943 : /* */
7944 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7945 : /* OGRMultiCurve. */
7946 : /************************************************************************/
7947 :
7948 1464 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7949 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7950 : int bAllowCurve, int bAllowCompoundCurve,
7951 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7952 :
7953 : {
7954 1464 : int bHasZ = FALSE;
7955 1464 : int bHasM = FALSE;
7956 1464 : bool bIsEmpty = false;
7957 1464 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7958 1464 : flags = 0;
7959 1464 : if (eErr != OGRERR_NONE)
7960 14 : return eErr;
7961 1450 : if (bHasZ)
7962 206 : flags |= OGR_G_3D;
7963 1450 : if (bHasM)
7964 132 : flags |= OGR_G_MEASURED;
7965 1450 : if (bIsEmpty)
7966 111 : return OGRERR_NONE;
7967 :
7968 : char szToken[OGR_WKT_TOKEN_MAX];
7969 1339 : const char *pszInput = *ppszInput;
7970 1339 : eErr = OGRERR_NONE;
7971 :
7972 : // Skip first '('.
7973 1339 : pszInput = OGRWktReadToken(pszInput, szToken);
7974 :
7975 : /* ==================================================================== */
7976 : /* Read each curve in turn. Note that we try to reuse the same */
7977 : /* point list buffer from curve to curve to cut down on */
7978 : /* allocate/deallocate overhead. */
7979 : /* ==================================================================== */
7980 1339 : OGRRawPoint *paoPoints = nullptr;
7981 1339 : int nMaxPoints = 0;
7982 1339 : double *padfZ = nullptr;
7983 :
7984 656 : do
7985 : {
7986 :
7987 : /* --------------------------------------------------------------------
7988 : */
7989 : /* Get the first token, which should be the geometry type. */
7990 : /* --------------------------------------------------------------------
7991 : */
7992 1995 : const char *pszInputBefore = pszInput;
7993 1995 : pszInput = OGRWktReadToken(pszInput, szToken);
7994 :
7995 : /* --------------------------------------------------------------------
7996 : */
7997 : /* Do the import. */
7998 : /* --------------------------------------------------------------------
7999 : */
8000 1995 : OGRCurve *poCurve = nullptr;
8001 1995 : if (EQUAL(szToken, "("))
8002 : {
8003 1441 : OGRLineString *poLine = new OGRLineString();
8004 1441 : poCurve = poLine;
8005 1441 : pszInput = pszInputBefore;
8006 1441 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
8007 : paoPoints, nMaxPoints, padfZ);
8008 : }
8009 554 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
8010 : {
8011 16 : poCurve = new OGRLineString();
8012 : }
8013 : // Accept LINESTRING(), but this is an extension to the BNF, also
8014 : // accepted by PostGIS.
8015 538 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
8016 523 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
8017 523 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
8018 1235 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
8019 159 : (bAllowCompoundCurve &&
8020 159 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
8021 : {
8022 500 : OGRGeometry *poGeom = nullptr;
8023 500 : pszInput = pszInputBefore;
8024 : eErr =
8025 500 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
8026 500 : if (poGeom == nullptr)
8027 : {
8028 1 : eErr = OGRERR_CORRUPT_DATA;
8029 : }
8030 : else
8031 : {
8032 499 : poCurve = poGeom->toCurve();
8033 : }
8034 : }
8035 : else
8036 : {
8037 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
8038 : szToken);
8039 38 : eErr = OGRERR_CORRUPT_DATA;
8040 : }
8041 :
8042 : // If this has M it is an error if poGeom does not have M.
8043 1995 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
8044 0 : eErr = OGRERR_CORRUPT_DATA;
8045 :
8046 1995 : if (eErr == OGRERR_NONE)
8047 1950 : eErr = pfnAddCurveDirectly(this, poCurve);
8048 1995 : if (eErr != OGRERR_NONE)
8049 : {
8050 55 : delete poCurve;
8051 55 : break;
8052 : }
8053 :
8054 : /* --------------------------------------------------------------------
8055 : */
8056 : /* Read the delimiter following the surface. */
8057 : /* --------------------------------------------------------------------
8058 : */
8059 1940 : pszInput = OGRWktReadToken(pszInput, szToken);
8060 1940 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
8061 :
8062 1339 : CPLFree(paoPoints);
8063 1339 : CPLFree(padfZ);
8064 :
8065 : /* -------------------------------------------------------------------- */
8066 : /* freak if we don't get a closing bracket. */
8067 : /* -------------------------------------------------------------------- */
8068 :
8069 1339 : if (eErr != OGRERR_NONE)
8070 55 : return eErr;
8071 :
8072 1284 : if (szToken[0] != ')')
8073 9 : return OGRERR_CORRUPT_DATA;
8074 :
8075 1275 : *ppszInput = pszInput;
8076 1275 : return OGRERR_NONE;
8077 : }
8078 :
8079 : //! @endcond
8080 :
8081 : /************************************************************************/
8082 : /* OGR_GT_Flatten() */
8083 : /************************************************************************/
8084 : /**
8085 : * \brief Returns the 2D geometry type corresponding to the passed geometry
8086 : * type.
8087 : *
8088 : * This function is intended to work with geometry types as old-style 99-402
8089 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
8090 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
8091 : *
8092 : * @param eType Input geometry type
8093 : *
8094 : * @return 2D geometry type corresponding to the passed geometry type.
8095 : *
8096 : */
8097 :
8098 8033540 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
8099 : {
8100 8033540 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
8101 8033540 : if (eType >= 1000 && eType < 2000) // ISO Z.
8102 2776760 : return static_cast<OGRwkbGeometryType>(eType - 1000);
8103 5256780 : if (eType >= 2000 && eType < 3000) // ISO M.
8104 6121 : return static_cast<OGRwkbGeometryType>(eType - 2000);
8105 5250660 : if (eType >= 3000 && eType < 4000) // ISO ZM.
8106 136119 : return static_cast<OGRwkbGeometryType>(eType - 3000);
8107 5114540 : return eType;
8108 : }
8109 :
8110 : /************************************************************************/
8111 : /* OGR_GT_HasZ() */
8112 : /************************************************************************/
8113 : /**
8114 : * \brief Return if the geometry type is a 3D geometry type.
8115 : *
8116 : * @param eType Input geometry type
8117 : *
8118 : * @return TRUE if the geometry type is a 3D geometry type.
8119 : *
8120 : */
8121 :
8122 2093170 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
8123 : {
8124 2093170 : if (eType & wkb25DBitInternalUse)
8125 157218 : return TRUE;
8126 1935950 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
8127 264 : return TRUE;
8128 1935690 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8129 121358 : return TRUE;
8130 1814330 : return FALSE;
8131 : }
8132 :
8133 : /************************************************************************/
8134 : /* OGR_GT_HasM() */
8135 : /************************************************************************/
8136 : /**
8137 : * \brief Return if the geometry type is a measured type.
8138 : *
8139 : * @param eType Input geometry type
8140 : *
8141 : * @return TRUE if the geometry type is a measured type.
8142 : *
8143 : */
8144 :
8145 2154430 : int OGR_GT_HasM(OGRwkbGeometryType eType)
8146 : {
8147 2154430 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
8148 2600 : return TRUE;
8149 2151830 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8150 121014 : return TRUE;
8151 2030820 : return FALSE;
8152 : }
8153 :
8154 : /************************************************************************/
8155 : /* OGR_GT_SetZ() */
8156 : /************************************************************************/
8157 : /**
8158 : * \brief Returns the 3D geometry type corresponding to the passed geometry
8159 : * type.
8160 : *
8161 : * @param eType Input geometry type
8162 : *
8163 : * @return 3D geometry type corresponding to the passed geometry type.
8164 : *
8165 : */
8166 :
8167 5668 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
8168 : {
8169 5668 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
8170 497 : return eType;
8171 5171 : if (eType <= wkbGeometryCollection)
8172 5069 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
8173 : else
8174 102 : return static_cast<OGRwkbGeometryType>(eType + 1000);
8175 : }
8176 :
8177 : /************************************************************************/
8178 : /* OGR_GT_SetM() */
8179 : /************************************************************************/
8180 : /**
8181 : * \brief Returns the measured geometry type corresponding to the passed
8182 : * geometry type.
8183 : *
8184 : * @param eType Input geometry type
8185 : *
8186 : * @return measured geometry type corresponding to the passed geometry type.
8187 : *
8188 : */
8189 :
8190 2023 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
8191 : {
8192 2023 : if (OGR_GT_HasM(eType) || eType == wkbNone)
8193 262 : return eType;
8194 1761 : if (eType & wkb25DBitInternalUse)
8195 : {
8196 720 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
8197 720 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
8198 : }
8199 1761 : return static_cast<OGRwkbGeometryType>(eType + 2000);
8200 : }
8201 :
8202 : /************************************************************************/
8203 : /* OGR_GT_SetModifier() */
8204 : /************************************************************************/
8205 : /**
8206 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
8207 : *
8208 : * @param eType Input geometry type
8209 : * @param bHasZ TRUE if the output geometry type must be 3D.
8210 : * @param bHasM TRUE if the output geometry type must be measured.
8211 : *
8212 : * @return Output geometry type.
8213 : *
8214 : */
8215 :
8216 5401 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
8217 : int bHasM)
8218 : {
8219 5401 : if (bHasZ && bHasM)
8220 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
8221 5059 : else if (bHasM)
8222 334 : return OGR_GT_SetM(wkbFlatten(eType));
8223 4725 : else if (bHasZ)
8224 1926 : return OGR_GT_SetZ(wkbFlatten(eType));
8225 : else
8226 2799 : return wkbFlatten(eType);
8227 : }
8228 :
8229 : /************************************************************************/
8230 : /* OGR_GT_IsSubClassOf) */
8231 : /************************************************************************/
8232 : /**
8233 : * \brief Returns if a type is a subclass of another one
8234 : *
8235 : * @param eType Type.
8236 : * @param eSuperType Super type
8237 : *
8238 : * @return TRUE if eType is a subclass of eSuperType.
8239 : *
8240 : */
8241 :
8242 138754 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
8243 : {
8244 138754 : eSuperType = wkbFlatten(eSuperType);
8245 138754 : eType = wkbFlatten(eType);
8246 :
8247 138754 : if (eSuperType == eType || eSuperType == wkbUnknown)
8248 21356 : return TRUE;
8249 :
8250 117398 : if (eSuperType == wkbGeometryCollection)
8251 34145 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
8252 70336 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
8253 36191 : eType == wkbMultiSurface;
8254 :
8255 81207 : if (eSuperType == wkbCurvePolygon)
8256 22252 : return eType == wkbPolygon || eType == wkbTriangle;
8257 :
8258 58955 : if (eSuperType == wkbMultiCurve)
8259 249 : return eType == wkbMultiLineString;
8260 :
8261 58706 : if (eSuperType == wkbMultiSurface)
8262 288 : return eType == wkbMultiPolygon;
8263 :
8264 58418 : if (eSuperType == wkbCurve)
8265 23329 : return eType == wkbLineString || eType == wkbCircularString ||
8266 23329 : eType == wkbCompoundCurve;
8267 :
8268 35089 : if (eSuperType == wkbSurface)
8269 3553 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
8270 7252 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
8271 3699 : eType == wkbTIN;
8272 :
8273 31390 : if (eSuperType == wkbPolygon)
8274 221 : return eType == wkbTriangle;
8275 :
8276 31169 : if (eSuperType == wkbPolyhedralSurface)
8277 16061 : return eType == wkbTIN;
8278 :
8279 15108 : return FALSE;
8280 : }
8281 :
8282 : /************************************************************************/
8283 : /* OGR_GT_GetCollection() */
8284 : /************************************************************************/
8285 : /**
8286 : * \brief Returns the collection type that can contain the passed geometry type
8287 : *
8288 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
8289 : * wkbLineString->wkbMultiLineString,
8290 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
8291 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
8292 : * wkbCurvePolygon->wkbMultiSurface.
8293 : * In other cases, wkbUnknown is returned
8294 : *
8295 : * Passed Z, M, ZM flag is preserved.
8296 : *
8297 : *
8298 : * @param eType Input geometry type
8299 : *
8300 : * @return the collection type that can contain the passed geometry type or
8301 : * wkbUnknown
8302 : *
8303 : */
8304 :
8305 8702 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
8306 : {
8307 8702 : const bool bHasZ = wkbHasZ(eType);
8308 8702 : const bool bHasM = wkbHasM(eType);
8309 8702 : if (eType == wkbNone)
8310 1 : return wkbNone;
8311 8701 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8312 8701 : if (eFGType == wkbPoint)
8313 69 : eType = wkbMultiPoint;
8314 :
8315 8632 : else if (eFGType == wkbLineString)
8316 1774 : eType = wkbMultiLineString;
8317 :
8318 6858 : else if (eFGType == wkbPolygon)
8319 5409 : eType = wkbMultiPolygon;
8320 :
8321 1449 : else if (eFGType == wkbTriangle)
8322 7 : eType = wkbTIN;
8323 :
8324 1442 : else if (OGR_GT_IsCurve(eFGType))
8325 189 : eType = wkbMultiCurve;
8326 :
8327 1253 : else if (OGR_GT_IsSurface(eFGType))
8328 951 : eType = wkbMultiSurface;
8329 :
8330 : else
8331 302 : return wkbUnknown;
8332 :
8333 8399 : if (bHasZ)
8334 108 : eType = wkbSetZ(eType);
8335 8399 : if (bHasM)
8336 12 : eType = wkbSetM(eType);
8337 :
8338 8399 : return eType;
8339 : }
8340 :
8341 : /************************************************************************/
8342 : /* OGR_GT_GetSingle() */
8343 : /************************************************************************/
8344 : /**
8345 : * \brief Returns the non-collection type that be contained in the passed
8346 : * geometry type.
8347 : *
8348 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
8349 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
8350 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
8351 : * wkbGeometryCollection -> wkbUnknown
8352 : * In other cases, the original geometry is returned.
8353 : *
8354 : * Passed Z, M, ZM flag is preserved.
8355 : *
8356 : *
8357 : * @param eType Input geometry type
8358 : *
8359 : * @return the the non-collection type that be contained in the passed geometry
8360 : * type or wkbUnknown
8361 : *
8362 : * @since GDAL 3.11
8363 : */
8364 :
8365 473 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
8366 : {
8367 473 : const bool bHasZ = wkbHasZ(eType);
8368 473 : const bool bHasM = wkbHasM(eType);
8369 473 : if (eType == wkbNone)
8370 1 : return wkbNone;
8371 472 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
8372 472 : if (eFGType == wkbMultiPoint)
8373 17 : eType = wkbPoint;
8374 :
8375 455 : else if (eFGType == wkbMultiLineString)
8376 6 : eType = wkbLineString;
8377 :
8378 449 : else if (eFGType == wkbMultiPolygon)
8379 7 : eType = wkbPolygon;
8380 :
8381 442 : else if (eFGType == wkbMultiCurve)
8382 2 : eType = wkbCompoundCurve;
8383 :
8384 440 : else if (eFGType == wkbMultiSurface)
8385 2 : eType = wkbCurvePolygon;
8386 :
8387 438 : else if (eFGType == wkbGeometryCollection)
8388 4 : return wkbUnknown;
8389 :
8390 468 : if (bHasZ)
8391 3 : eType = wkbSetZ(eType);
8392 468 : if (bHasM)
8393 2 : eType = wkbSetM(eType);
8394 :
8395 468 : return eType;
8396 : }
8397 :
8398 : /************************************************************************/
8399 : /* OGR_GT_GetCurve() */
8400 : /************************************************************************/
8401 : /**
8402 : * \brief Returns the curve geometry type that can contain the passed geometry
8403 : * type
8404 : *
8405 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8406 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8407 : * and wkbMultiLineString->wkbMultiCurve.
8408 : * In other cases, the passed geometry is returned.
8409 : *
8410 : * Passed Z, M, ZM flag is preserved.
8411 : *
8412 : * @param eType Input geometry type
8413 : *
8414 : * @return the curve type that can contain the passed geometry type
8415 : *
8416 : */
8417 :
8418 32 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8419 : {
8420 32 : const bool bHasZ = wkbHasZ(eType);
8421 32 : const bool bHasM = wkbHasM(eType);
8422 32 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8423 :
8424 32 : if (eFGType == wkbLineString)
8425 3 : eType = wkbCompoundCurve;
8426 :
8427 29 : else if (eFGType == wkbPolygon)
8428 1 : eType = wkbCurvePolygon;
8429 :
8430 28 : else if (eFGType == wkbTriangle)
8431 0 : eType = wkbCurvePolygon;
8432 :
8433 28 : else if (eFGType == wkbMultiLineString)
8434 3 : eType = wkbMultiCurve;
8435 :
8436 25 : else if (eFGType == wkbMultiPolygon)
8437 4 : eType = wkbMultiSurface;
8438 :
8439 32 : if (bHasZ)
8440 4 : eType = wkbSetZ(eType);
8441 32 : if (bHasM)
8442 4 : eType = wkbSetM(eType);
8443 :
8444 32 : return eType;
8445 : }
8446 :
8447 : /************************************************************************/
8448 : /* OGR_GT_GetLinear() */
8449 : /************************************************************************/
8450 : /**
8451 : * \brief Returns the non-curve geometry type that can contain the passed
8452 : * geometry type
8453 : *
8454 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8455 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8456 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8457 : * In other cases, the passed geometry is returned.
8458 : *
8459 : * Passed Z, M, ZM flag is preserved.
8460 : *
8461 : * @param eType Input geometry type
8462 : *
8463 : * @return the non-curve type that can contain the passed geometry type
8464 : *
8465 : */
8466 :
8467 786 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8468 : {
8469 786 : const bool bHasZ = wkbHasZ(eType);
8470 786 : const bool bHasM = wkbHasM(eType);
8471 786 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8472 :
8473 786 : if (OGR_GT_IsCurve(eFGType))
8474 56 : eType = wkbLineString;
8475 :
8476 730 : else if (OGR_GT_IsSurface(eFGType))
8477 42 : eType = wkbPolygon;
8478 :
8479 688 : else if (eFGType == wkbMultiCurve)
8480 187 : eType = wkbMultiLineString;
8481 :
8482 501 : else if (eFGType == wkbMultiSurface)
8483 167 : eType = wkbMultiPolygon;
8484 :
8485 786 : if (bHasZ)
8486 154 : eType = wkbSetZ(eType);
8487 786 : if (bHasM)
8488 101 : eType = wkbSetM(eType);
8489 :
8490 786 : return eType;
8491 : }
8492 :
8493 : /************************************************************************/
8494 : /* OGR_GT_IsCurve() */
8495 : /************************************************************************/
8496 :
8497 : /**
8498 : * \brief Return if a geometry type is an instance of Curve
8499 : *
8500 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8501 : * and their Z/M/ZM variant.
8502 : *
8503 : * @param eGeomType the geometry type
8504 : * @return TRUE if the geometry type is an instance of Curve
8505 : *
8506 : */
8507 :
8508 23320 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8509 : {
8510 23320 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8511 : }
8512 :
8513 : /************************************************************************/
8514 : /* OGR_GT_IsSurface() */
8515 : /************************************************************************/
8516 :
8517 : /**
8518 : * \brief Return if a geometry type is an instance of Surface
8519 : *
8520 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8521 : * and their Z/M/ZM variant.
8522 : *
8523 : * @param eGeomType the geometry type
8524 : * @return TRUE if the geometry type is an instance of Surface
8525 : *
8526 : */
8527 :
8528 3693 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8529 : {
8530 3693 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8531 : }
8532 :
8533 : /************************************************************************/
8534 : /* OGR_GT_IsNonLinear() */
8535 : /************************************************************************/
8536 :
8537 : /**
8538 : * \brief Return if a geometry type is a non-linear geometry type.
8539 : *
8540 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8541 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8542 : * Z/M variants.
8543 : *
8544 : * @param eGeomType the geometry type
8545 : * @return TRUE if the geometry type is a non-linear geometry type.
8546 : *
8547 : */
8548 :
8549 120233 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8550 : {
8551 120233 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8552 120225 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8553 120152 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8554 240458 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8555 120233 : eFGeomType == wkbMultiSurface;
8556 : }
8557 :
8558 : /************************************************************************/
8559 : /* CastToError() */
8560 : /************************************************************************/
8561 :
8562 : //! @cond Doxygen_Suppress
8563 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8564 : {
8565 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8566 0 : poGeom->getGeometryName());
8567 0 : delete poGeom;
8568 0 : return nullptr;
8569 : }
8570 :
8571 : //! @endcond
8572 :
8573 : /************************************************************************/
8574 : /* OGRexportToSFCGAL() */
8575 : /************************************************************************/
8576 :
8577 : //! @cond Doxygen_Suppress
8578 : sfcgal_geometry_t *
8579 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8580 : {
8581 : #ifdef HAVE_SFCGAL
8582 :
8583 : sfcgal_init();
8584 : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8585 :
8586 : const auto exportToSFCGALViaWKB =
8587 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8588 : {
8589 : if (!geom)
8590 : return nullptr;
8591 :
8592 : // Get WKB size and allocate buffer
8593 : size_t nSize = geom->WkbSize();
8594 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8595 :
8596 : // Set export options with NDR byte order
8597 : OGRwkbExportOptions oOptions;
8598 : oOptions.eByteOrder = wkbNDR;
8599 : // and ISO to avoid wkb25DBit for Z geometries
8600 : oOptions.eWkbVariant = wkbVariantIso;
8601 :
8602 : // Export to WKB
8603 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8604 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8605 : {
8606 : sfcgalGeom = sfcgal_io_read_wkb(
8607 : reinterpret_cast<const char *>(pabyWkb), nSize);
8608 : }
8609 :
8610 : CPLFree(pabyWkb);
8611 : return sfcgalGeom;
8612 : };
8613 :
8614 : // Handle special cases
8615 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8616 : {
8617 : std::unique_ptr<OGRLineString> poLS(
8618 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8619 : return exportToSFCGALViaWKB(poLS.get());
8620 : }
8621 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8622 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8623 : {
8624 : std::unique_ptr<OGRLineString> poLS(
8625 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8626 : ->toLineString());
8627 : return exportToSFCGALViaWKB(poLS.get());
8628 : }
8629 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8630 : {
8631 : std::unique_ptr<OGRPolygon> poPolygon(
8632 : OGRGeometryFactory::forceToPolygon(
8633 : poGeom->clone()->toCurvePolygon())
8634 : ->toPolygon());
8635 : return exportToSFCGALViaWKB(poPolygon.get());
8636 : }
8637 : else
8638 : {
8639 : // Default case - direct export
8640 : return exportToSFCGALViaWKB(poGeom);
8641 : }
8642 : #else
8643 : char *buffer = nullptr;
8644 :
8645 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8646 : // Polygon
8647 :
8648 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8649 : {
8650 : // cast it to LineString and get the WKT
8651 : std::unique_ptr<OGRLineString> poLS(
8652 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8653 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8654 : {
8655 : sfcgal_geometry_t *_geometry =
8656 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8657 : CPLFree(buffer);
8658 : return _geometry;
8659 : }
8660 : else
8661 : {
8662 : CPLFree(buffer);
8663 : return nullptr;
8664 : }
8665 : }
8666 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8667 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8668 : {
8669 : // convert it to LineString and get the WKT
8670 : std::unique_ptr<OGRLineString> poLS(
8671 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8672 : ->toLineString());
8673 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8674 : {
8675 : sfcgal_geometry_t *_geometry =
8676 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8677 : CPLFree(buffer);
8678 : return _geometry;
8679 : }
8680 : else
8681 : {
8682 : CPLFree(buffer);
8683 : return nullptr;
8684 : }
8685 : }
8686 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8687 : {
8688 : // convert it to Polygon and get the WKT
8689 : std::unique_ptr<OGRPolygon> poPolygon(
8690 : OGRGeometryFactory::forceToPolygon(
8691 : poGeom->clone()->toCurvePolygon())
8692 : ->toPolygon());
8693 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8694 : {
8695 : sfcgal_geometry_t *_geometry =
8696 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8697 : CPLFree(buffer);
8698 : return _geometry;
8699 : }
8700 : else
8701 : {
8702 : CPLFree(buffer);
8703 : return nullptr;
8704 : }
8705 : }
8706 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8707 : {
8708 : sfcgal_geometry_t *_geometry =
8709 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8710 : CPLFree(buffer);
8711 : return _geometry;
8712 : }
8713 : else
8714 : {
8715 : CPLFree(buffer);
8716 : return nullptr;
8717 : }
8718 : #endif
8719 : #else
8720 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8721 0 : return nullptr;
8722 : #endif
8723 : }
8724 :
8725 : //! @endcond
8726 :
8727 : /************************************************************************/
8728 : /* SFCGALexportToOGR() */
8729 : /************************************************************************/
8730 :
8731 : //! @cond Doxygen_Suppress
8732 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8733 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8734 : {
8735 : #ifdef HAVE_SFCGAL
8736 : if (geometry == nullptr)
8737 : return nullptr;
8738 :
8739 : sfcgal_init();
8740 : char *pabySFCGAL = nullptr;
8741 : size_t nLength = 0;
8742 : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8743 :
8744 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8745 :
8746 : if (pabySFCGAL == nullptr || nLength == 0)
8747 : return nullptr;
8748 :
8749 : OGRGeometry *poGeom = nullptr;
8750 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8751 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8752 : nLength);
8753 :
8754 : free(pabySFCGAL);
8755 :
8756 : if (eErr == OGRERR_NONE)
8757 : {
8758 : return poGeom;
8759 : }
8760 : else
8761 : {
8762 : return nullptr;
8763 : }
8764 : #else
8765 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8766 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8767 : memcpy(pszWKT, pabySFCGAL, nLength);
8768 : pszWKT[nLength] = 0;
8769 : free(pabySFCGAL);
8770 :
8771 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8772 :
8773 : OGRGeometry *poGeom = nullptr;
8774 : if (geom_type == SFCGAL_TYPE_POINT)
8775 : {
8776 : poGeom = new OGRPoint();
8777 : }
8778 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8779 : {
8780 : poGeom = new OGRLineString();
8781 : }
8782 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8783 : {
8784 : poGeom = new OGRPolygon();
8785 : }
8786 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8787 : {
8788 : poGeom = new OGRMultiPoint();
8789 : }
8790 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8791 : {
8792 : poGeom = new OGRMultiLineString();
8793 : }
8794 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8795 : {
8796 : poGeom = new OGRMultiPolygon();
8797 : }
8798 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8799 : {
8800 : poGeom = new OGRGeometryCollection();
8801 : }
8802 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8803 : {
8804 : poGeom = new OGRTriangle();
8805 : }
8806 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8807 : {
8808 : poGeom = new OGRPolyhedralSurface();
8809 : }
8810 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8811 : {
8812 : poGeom = new OGRTriangulatedSurface();
8813 : }
8814 : else
8815 : {
8816 : CPLFree(pszWKT);
8817 : return nullptr;
8818 : }
8819 :
8820 : const char *pszWKTTmp = pszWKT;
8821 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8822 : {
8823 : CPLFree(pszWKT);
8824 : return poGeom;
8825 : }
8826 : else
8827 : {
8828 : delete poGeom;
8829 : CPLFree(pszWKT);
8830 : return nullptr;
8831 : }
8832 : #endif
8833 : #else
8834 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8835 0 : return nullptr;
8836 : #endif
8837 : }
8838 :
8839 : //! @endcond
8840 :
8841 : //! @cond Doxygen_Suppress
8842 11066 : bool OGRGeometry::IsSFCGALCompatible() const
8843 : {
8844 11066 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8845 11066 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8846 : eGType == wkbTIN)
8847 : {
8848 2 : return TRUE;
8849 : }
8850 11064 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8851 : {
8852 14 : const OGRGeometryCollection *poGC = toGeometryCollection();
8853 14 : bool bIsSFCGALCompatible = false;
8854 14 : for (auto &&poSubGeom : *poGC)
8855 : {
8856 : OGRwkbGeometryType eSubGeomType =
8857 14 : wkbFlatten(poSubGeom->getGeometryType());
8858 14 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8859 : {
8860 0 : bIsSFCGALCompatible = true;
8861 : }
8862 14 : else if (eSubGeomType != wkbMultiPolygon)
8863 : {
8864 14 : bIsSFCGALCompatible = false;
8865 14 : break;
8866 : }
8867 : }
8868 14 : return bIsSFCGALCompatible;
8869 : }
8870 11050 : return FALSE;
8871 : }
8872 :
8873 : //! @endcond
8874 :
8875 : /************************************************************************/
8876 : /* roundCoordinatesIEEE754() */
8877 : /************************************************************************/
8878 :
8879 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8880 : * double-precision binary representation.
8881 : *
8882 : * Determines the number of bits (N) required to represent a coordinate value
8883 : * with a specified number of digits after the decimal point, and then sets all
8884 : * but the N most significant bits to zero. The resulting coordinate value will
8885 : * still round to the original value (e.g. after roundCoordinates()), but will
8886 : * have improved compressiblity.
8887 : *
8888 : * @param options Contains the precision requirements.
8889 : * @since GDAL 3.9
8890 : */
8891 1 : void OGRGeometry::roundCoordinatesIEEE754(
8892 : const OGRGeomCoordinateBinaryPrecision &options)
8893 : {
8894 : struct Quantizer : public OGRDefaultGeometryVisitor
8895 : {
8896 : const OGRGeomCoordinateBinaryPrecision &m_options;
8897 :
8898 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8899 1 : : m_options(optionsIn)
8900 : {
8901 1 : }
8902 :
8903 : using OGRDefaultGeometryVisitor::visit;
8904 :
8905 3 : void visit(OGRPoint *poPoint) override
8906 : {
8907 3 : if (m_options.nXYBitPrecision != INT_MIN)
8908 : {
8909 : uint64_t i;
8910 : double d;
8911 3 : d = poPoint->getX();
8912 3 : memcpy(&i, &d, sizeof(i));
8913 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8914 3 : memcpy(&d, &i, sizeof(i));
8915 3 : poPoint->setX(d);
8916 3 : d = poPoint->getY();
8917 3 : memcpy(&i, &d, sizeof(i));
8918 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8919 3 : memcpy(&d, &i, sizeof(i));
8920 3 : poPoint->setY(d);
8921 : }
8922 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8923 : {
8924 : uint64_t i;
8925 : double d;
8926 3 : d = poPoint->getZ();
8927 3 : memcpy(&i, &d, sizeof(i));
8928 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8929 3 : memcpy(&d, &i, sizeof(i));
8930 3 : poPoint->setZ(d);
8931 : }
8932 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8933 : {
8934 : uint64_t i;
8935 : double d;
8936 3 : d = poPoint->getM();
8937 3 : memcpy(&i, &d, sizeof(i));
8938 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8939 3 : memcpy(&d, &i, sizeof(i));
8940 3 : poPoint->setM(d);
8941 : }
8942 3 : }
8943 : };
8944 :
8945 2 : Quantizer quantizer(options);
8946 1 : accept(&quantizer);
8947 1 : }
8948 :
8949 : /************************************************************************/
8950 : /* visit() */
8951 : /************************************************************************/
8952 :
8953 105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8954 : {
8955 1248 : for (auto &&oPoint : *poGeom)
8956 : {
8957 1143 : oPoint.accept(this);
8958 : }
8959 105 : }
8960 :
8961 104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8962 : {
8963 104 : _visit(poGeom);
8964 104 : }
8965 :
8966 80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8967 : {
8968 80 : visit(poGeom->toUpperClass());
8969 80 : }
8970 :
8971 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8972 : {
8973 1 : _visit(poGeom);
8974 1 : }
8975 :
8976 78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8977 : {
8978 159 : for (auto &&poSubGeom : *poGeom)
8979 81 : poSubGeom->accept(this);
8980 78 : }
8981 :
8982 77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8983 : {
8984 77 : visit(poGeom->toUpperClass());
8985 77 : }
8986 :
8987 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8988 : {
8989 1 : visit(poGeom->toUpperClass());
8990 1 : }
8991 :
8992 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8993 : {
8994 8 : visit(poGeom->toUpperClass());
8995 8 : }
8996 :
8997 44 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8998 : {
8999 44 : visit(poGeom->toUpperClass());
9000 44 : }
9001 :
9002 56 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
9003 : {
9004 135 : for (auto &&poSubGeom : *poGeom)
9005 79 : poSubGeom->accept(this);
9006 56 : }
9007 :
9008 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
9009 : {
9010 2 : for (auto &&poSubGeom : *poGeom)
9011 1 : poSubGeom->accept(this);
9012 1 : }
9013 :
9014 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
9015 : {
9016 1 : visit(poGeom->toUpperClass());
9017 1 : }
9018 :
9019 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
9020 : {
9021 1 : visit(poGeom->toUpperClass());
9022 1 : }
9023 :
9024 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
9025 : {
9026 2 : visit(poGeom->toUpperClass());
9027 2 : }
9028 :
9029 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
9030 : {
9031 4 : for (auto &&poSubGeom : *poGeom)
9032 2 : poSubGeom->accept(this);
9033 2 : }
9034 :
9035 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
9036 : {
9037 1 : visit(poGeom->toUpperClass());
9038 1 : }
9039 :
9040 127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
9041 : {
9042 2988 : for (auto &&oPoint : *poGeom)
9043 : {
9044 2861 : oPoint.accept(this);
9045 : }
9046 127 : }
9047 :
9048 121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
9049 : {
9050 121 : _visit(poGeom);
9051 121 : }
9052 :
9053 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
9054 : {
9055 110 : visit(poGeom->toUpperClass());
9056 110 : }
9057 :
9058 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
9059 : {
9060 6 : _visit(poGeom);
9061 6 : }
9062 :
9063 112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
9064 : {
9065 225 : for (auto &&poSubGeom : *poGeom)
9066 113 : poSubGeom->accept(this);
9067 112 : }
9068 :
9069 109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
9070 : {
9071 109 : visit(poGeom->toUpperClass());
9072 109 : }
9073 :
9074 64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
9075 : {
9076 64 : visit(poGeom->toUpperClass());
9077 64 : }
9078 :
9079 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
9080 : {
9081 1 : visit(poGeom->toUpperClass());
9082 1 : }
9083 :
9084 83 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
9085 : {
9086 83 : visit(poGeom->toUpperClass());
9087 83 : }
9088 :
9089 151 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
9090 : {
9091 489 : for (auto &&poSubGeom : *poGeom)
9092 338 : poSubGeom->accept(this);
9093 151 : }
9094 :
9095 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
9096 : {
9097 14 : for (auto &&poSubGeom : *poGeom)
9098 11 : poSubGeom->accept(this);
9099 3 : }
9100 :
9101 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
9102 : {
9103 1 : visit(poGeom->toUpperClass());
9104 1 : }
9105 :
9106 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
9107 : {
9108 1 : visit(poGeom->toUpperClass());
9109 1 : }
9110 :
9111 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
9112 : {
9113 2 : visit(poGeom->toUpperClass());
9114 2 : }
9115 :
9116 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
9117 : {
9118 4 : for (auto &&poSubGeom : *poGeom)
9119 2 : poSubGeom->accept(this);
9120 2 : }
9121 :
9122 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
9123 : {
9124 1 : visit(poGeom->toUpperClass());
9125 1 : }
9126 :
9127 : /************************************************************************/
9128 : /* OGRGeometryUniquePtrDeleter */
9129 : /************************************************************************/
9130 :
9131 : //! @cond Doxygen_Suppress
9132 1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
9133 : {
9134 1333 : delete poGeom;
9135 1333 : }
9136 :
9137 : //! @endcond
9138 :
9139 : /************************************************************************/
9140 : /* OGRPreparedGeometryUniquePtrDeleter */
9141 : /************************************************************************/
9142 :
9143 : //! @cond Doxygen_Suppress
9144 150 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
9145 : OGRPreparedGeometry *poPreparedGeom) const
9146 : {
9147 150 : OGRDestroyPreparedGeometry(poPreparedGeom);
9148 150 : }
9149 :
9150 : //! @endcond
9151 :
9152 : /************************************************************************/
9153 : /* HomogenizeDimensionalityWith() */
9154 : /************************************************************************/
9155 :
9156 : //! @cond Doxygen_Suppress
9157 3350380 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
9158 : {
9159 3350380 : if (poOtherGeom->Is3D() && !Is3D())
9160 1331020 : set3D(TRUE);
9161 :
9162 3350380 : if (poOtherGeom->IsMeasured() && !IsMeasured())
9163 854 : setMeasured(TRUE);
9164 :
9165 3350380 : if (!poOtherGeom->Is3D() && Is3D())
9166 298 : poOtherGeom->set3D(TRUE);
9167 :
9168 3350380 : if (!poOtherGeom->IsMeasured() && IsMeasured())
9169 41 : poOtherGeom->setMeasured(TRUE);
9170 3350380 : }
9171 :
9172 : //! @endcond
9173 :
9174 : /************************************************************************/
9175 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
9176 : /************************************************************************/
9177 :
9178 : /** Set binary precision options from resolution.
9179 : *
9180 : * @since GDAL 3.9
9181 : */
9182 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
9183 : const OGRGeomCoordinatePrecision &prec)
9184 : {
9185 16 : if (prec.dfXYResolution != 0)
9186 : {
9187 16 : nXYBitPrecision =
9188 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
9189 : }
9190 16 : if (prec.dfZResolution != 0)
9191 : {
9192 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
9193 : }
9194 16 : if (prec.dfMResolution != 0)
9195 : {
9196 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
9197 : }
9198 16 : }
9199 :
9200 : /************************************************************************/
9201 : /* OGRwkbExportOptionsCreate() */
9202 : /************************************************************************/
9203 :
9204 : /**
9205 : * \brief Create geometry WKB export options.
9206 : *
9207 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
9208 : *
9209 : * @return object to be freed with OGRwkbExportOptionsDestroy().
9210 : * @since GDAL 3.9
9211 : */
9212 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
9213 : {
9214 2 : return new OGRwkbExportOptions;
9215 : }
9216 :
9217 : /************************************************************************/
9218 : /* OGRwkbExportOptionsDestroy() */
9219 : /************************************************************************/
9220 :
9221 : /**
9222 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
9223 : *
9224 : * @param psOptions WKB export options
9225 : * @since GDAL 3.9
9226 : */
9227 :
9228 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
9229 : {
9230 2 : delete psOptions;
9231 2 : }
9232 :
9233 : /************************************************************************/
9234 : /* OGRwkbExportOptionsSetByteOrder() */
9235 : /************************************************************************/
9236 :
9237 : /**
9238 : * \brief Set the WKB byte order.
9239 : *
9240 : * @param psOptions WKB export options
9241 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
9242 : * Intel)
9243 : * @since GDAL 3.9
9244 : */
9245 :
9246 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
9247 : OGRwkbByteOrder eByteOrder)
9248 : {
9249 1 : psOptions->eByteOrder = eByteOrder;
9250 1 : }
9251 :
9252 : /************************************************************************/
9253 : /* OGRwkbExportOptionsSetVariant() */
9254 : /************************************************************************/
9255 :
9256 : /**
9257 : * \brief Set the WKB variant
9258 : *
9259 : * @param psOptions WKB export options
9260 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
9261 : * wkbVariantPostGIS1
9262 : * @since GDAL 3.9
9263 : */
9264 :
9265 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
9266 : OGRwkbVariant eWkbVariant)
9267 : {
9268 1 : psOptions->eWkbVariant = eWkbVariant;
9269 1 : }
9270 :
9271 : /************************************************************************/
9272 : /* OGRwkbExportOptionsSetPrecision() */
9273 : /************************************************************************/
9274 :
9275 : /**
9276 : * \brief Set precision options
9277 : *
9278 : * @param psOptions WKB export options
9279 : * @param hPrecisionOptions Precision options (might be null to reset them)
9280 : * @since GDAL 3.9
9281 : */
9282 :
9283 1 : void OGRwkbExportOptionsSetPrecision(
9284 : OGRwkbExportOptions *psOptions,
9285 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
9286 : {
9287 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
9288 1 : if (hPrecisionOptions)
9289 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
9290 1 : }
9291 :
9292 : /************************************************************************/
9293 : /* IsRectangle() */
9294 : /************************************************************************/
9295 :
9296 : /**
9297 : * \brief Returns whether the geometry is a polygon with 4 corners forming
9298 : * a rectangle.
9299 : *
9300 : * @since GDAL 3.10
9301 : */
9302 53073 : bool OGRGeometry::IsRectangle() const
9303 : {
9304 53073 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
9305 572 : return false;
9306 :
9307 52501 : const OGRPolygon *poPoly = toPolygon();
9308 :
9309 52501 : if (poPoly->getNumInteriorRings() != 0)
9310 27 : return false;
9311 :
9312 52474 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
9313 52474 : if (!poRing)
9314 4 : return false;
9315 :
9316 52470 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
9317 33 : return false;
9318 :
9319 : // If the ring has 5 points, the last should be the first.
9320 104817 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
9321 52380 : poRing->getY(0) != poRing->getY(4)))
9322 1 : return false;
9323 :
9324 : // Polygon with first segment in "y" direction.
9325 104161 : if (poRing->getX(0) == poRing->getX(1) &&
9326 103449 : poRing->getY(1) == poRing->getY(2) &&
9327 155885 : poRing->getX(2) == poRing->getX(3) &&
9328 51724 : poRing->getY(3) == poRing->getY(0))
9329 51724 : return true;
9330 :
9331 : // Polygon with first segment in "x" direction.
9332 1363 : if (poRing->getY(0) == poRing->getY(1) &&
9333 1302 : poRing->getX(1) == poRing->getX(2) &&
9334 2014 : poRing->getY(2) == poRing->getY(3) &&
9335 651 : poRing->getX(3) == poRing->getX(0))
9336 651 : return true;
9337 :
9338 61 : return false;
9339 : }
9340 :
9341 : /************************************************************************/
9342 : /* hasEmptyParts() */
9343 : /************************************************************************/
9344 :
9345 : /**
9346 : * \brief Returns whether a geometry has empty parts/rings.
9347 : *
9348 : * Returns true if removeEmptyParts() will modify the geometry.
9349 : *
9350 : * This is different from IsEmpty().
9351 : *
9352 : * @since GDAL 3.10
9353 : */
9354 103 : bool OGRGeometry::hasEmptyParts() const
9355 : {
9356 103 : return false;
9357 : }
9358 :
9359 : /************************************************************************/
9360 : /* removeEmptyParts() */
9361 : /************************************************************************/
9362 :
9363 : /**
9364 : * \brief Remove empty parts/rings from this geometry.
9365 : *
9366 : * @since GDAL 3.10
9367 : */
9368 17 : void OGRGeometry::removeEmptyParts()
9369 : {
9370 17 : }
9371 :
9372 : /************************************************************************/
9373 : /* ~IOGRGeometryVisitor() */
9374 : /************************************************************************/
9375 :
9376 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
9377 :
9378 : /************************************************************************/
9379 : /* ~IOGRConstGeometryVisitor() */
9380 : /************************************************************************/
9381 :
9382 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
|