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 100 : static void OGRGEOSErrorHandler(const char *fmt, ...)
60 : {
61 : va_list args;
62 :
63 100 : va_start(args, fmt);
64 100 : CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
65 100 : va_end(args);
66 100 : }
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 11310 : int OGRWktOptions::getDefaultPrecision()
83 : {
84 11310 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
85 : }
86 :
87 11406 : bool OGRWktOptions::getDefaultRound()
88 : {
89 11406 : 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 1804320 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
107 1804320 : : poSRS(other.poSRS), flags(other.flags)
108 : {
109 1804320 : if (poSRS != nullptr)
110 81396 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
111 1804320 : }
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 25735600 : OGRGeometry::~OGRGeometry()
134 :
135 : {
136 12867800 : if (poSRS != nullptr)
137 3656380 : const_cast<OGRSpatialReference *>(poSRS)->Release();
138 12867800 : }
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 5664240 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
506 :
507 : {
508 : // Do in that order to properly handle poSR == poSRS
509 5664240 : if (poSR != nullptr)
510 3621440 : const_cast<OGRSpatialReference *>(poSR)->Reference();
511 5664240 : if (poSRS != nullptr)
512 46456 : const_cast<OGRSpatialReference *>(poSRS)->Release();
513 :
514 5664240 : poSRS = poSR;
515 5664240 : }
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 35 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
699 :
700 : {
701 35 : if (getSpatialReference() == nullptr)
702 : {
703 1 : CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
704 1 : return OGRERR_FAILURE;
705 : }
706 :
707 34 : 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 34 : OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
715 34 : if (poCT == nullptr)
716 0 : return OGRERR_FAILURE;
717 :
718 34 : const OGRErr eErr = transform(poCT);
719 :
720 34 : OGRCoordinateTransformation::DestroyCT(poCT);
721 :
722 34 : 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 609831 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
854 : {
855 609831 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
856 :
857 609831 : if (flags & OGR_G_3D)
858 213813 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
859 609831 : if (flags & OGR_G_MEASURED)
860 26025 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
861 :
862 609831 : 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 594412 : int OGRGeometry::getCoordinateDimension() const
963 :
964 : {
965 594412 : 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 37752 : int OGR_G_Is3D(OGRGeometryH hGeom)
1056 :
1057 : {
1058 37752 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1059 :
1060 37752 : 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 40156 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1076 :
1077 : {
1078 40156 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1079 :
1080 40156 : 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 68227 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1103 :
1104 : {
1105 68227 : if (nNewDimension == 2)
1106 67725 : flags &= ~OGR_G_3D;
1107 : else
1108 502 : flags |= OGR_G_3D;
1109 68227 : 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 1624880 : bool OGRGeometry::set3D(bool bIs3D)
1126 :
1127 : {
1128 1624880 : if (bIs3D)
1129 1614820 : flags |= OGR_G_3D;
1130 : else
1131 10055 : flags &= ~OGR_G_3D;
1132 1624880 : 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 419772 : bool OGRGeometry::setMeasured(bool bIsMeasured)
1150 :
1151 : {
1152 419772 : if (bIsMeasured)
1153 137732 : flags |= OGR_G_MEASURED;
1154 : else
1155 282040 : flags &= ~OGR_G_MEASURED;
1156 419772 : 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 28233 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1293 :
1294 : {
1295 28233 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1296 :
1297 28233 : 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 56466 : return OGRGeometry::FromHandle(hGeom)->Equals(
1305 56466 : 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 157808 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1597 : unsigned char *pabyData,
1598 : OGRwkbVariant eWkbVariant) const
1599 : {
1600 157808 : OGRwkbExportOptions sOptions;
1601 157808 : sOptions.eByteOrder = eByteOrder;
1602 157808 : sOptions.eWkbVariant = eWkbVariant;
1603 315616 : 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 123938 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1794 : int *pbHasM, bool *pbIsEmpty)
1795 : {
1796 123938 : const char *pszInput = *ppszInput;
1797 :
1798 : /* -------------------------------------------------------------------- */
1799 : /* Clear existing Geoms. */
1800 : /* -------------------------------------------------------------------- */
1801 123938 : empty();
1802 123938 : *pbIsEmpty = false;
1803 :
1804 : /* -------------------------------------------------------------------- */
1805 : /* Read and verify the type keyword, and ensure it matches the */
1806 : /* actual type of this container. */
1807 : /* -------------------------------------------------------------------- */
1808 123938 : bool bHasM = false;
1809 123938 : bool bHasZ = false;
1810 123938 : bool bAlreadyGotDimension = false;
1811 :
1812 123938 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1813 123938 : pszInput = OGRWktReadToken(pszInput, szToken);
1814 123938 : 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 123938 : const size_t nTokenLen = strlen(szToken);
1819 123938 : 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 123927 : 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 123938 : if (!EQUAL(szToken, getGeometryName()))
1841 0 : return OGRERR_CORRUPT_DATA;
1842 :
1843 : /* -------------------------------------------------------------------- */
1844 : /* Check for Z, M or ZM */
1845 : /* -------------------------------------------------------------------- */
1846 123938 : if (!bAlreadyGotDimension)
1847 : {
1848 123921 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1849 123921 : if (EQUAL(szToken, "Z"))
1850 : {
1851 1418 : pszInput = pszNewInput;
1852 1418 : bHasZ = true;
1853 : }
1854 122503 : else if (EQUAL(szToken, "M"))
1855 : {
1856 354 : pszInput = pszNewInput;
1857 354 : bHasM = true;
1858 : }
1859 122149 : else if (EQUAL(szToken, "ZM"))
1860 : {
1861 494 : pszInput = pszNewInput;
1862 494 : bHasZ = true;
1863 494 : bHasM = true;
1864 : }
1865 : }
1866 123938 : *pbHasZ = bHasZ;
1867 123938 : *pbHasM = bHasM;
1868 :
1869 : /* -------------------------------------------------------------------- */
1870 : /* Check for EMPTY ... */
1871 : /* -------------------------------------------------------------------- */
1872 123938 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1873 123938 : 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 122360 : if (!EQUAL(szToken, "("))
1885 35 : return OGRERR_CORRUPT_DATA;
1886 :
1887 122325 : if (!bHasZ && !bHasM)
1888 : {
1889 : // Test for old-style XXXXXXXXX(EMPTY).
1890 120222 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1891 120222 : 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 122281 : *ppszInput = pszInput;
1914 :
1915 122281 : 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 14573 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1931 : {
1932 14573 : std::string s(" ");
1933 :
1934 14573 : 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 14573 : if (s.size() > 1)
1942 2521 : s += " ";
1943 14573 : 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 8911 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1969 : {
1970 8911 : OGRWktOptions opts;
1971 8911 : opts.variant = variant;
1972 8911 : OGRErr err(OGRERR_NONE);
1973 :
1974 8911 : std::string wkt = exportToWkt(opts, &err);
1975 8911 : *ppszDstText = CPLStrdup(wkt.data());
1976 17822 : 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 2601 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
2004 :
2005 : {
2006 2601 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2007 :
2008 2601 : 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 131958 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2075 :
2076 : {
2077 131958 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2078 :
2079 131958 : 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 19018 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2111 :
2112 : {
2113 19018 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2114 :
2115 19018 : 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 3345 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2647 : {
2648 3345 : OGRwkbGeometryType eType = wkbUnknown;
2649 3345 : bool bConvertTo3D = false;
2650 3345 : bool bIsMeasured = false;
2651 3345 : if (*pszGeomType != '\0')
2652 : {
2653 3339 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2654 3339 : 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 3339 : if (ch == 'z' || ch == 'Z')
2661 : {
2662 34 : bConvertTo3D = true;
2663 : }
2664 : }
2665 :
2666 3345 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2667 971 : eType = wkbPoint;
2668 2374 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2669 186 : eType = wkbLineString;
2670 2188 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2671 375 : eType = wkbPolygon;
2672 1813 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2673 25 : eType = wkbMultiPoint;
2674 1788 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2675 15 : eType = wkbMultiLineString;
2676 1773 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2677 673 : eType = wkbMultiPolygon;
2678 1100 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2679 4 : eType = wkbGeometryCollection;
2680 1096 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2681 356 : eType = wkbCircularString;
2682 740 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2683 0 : eType = wkbCompoundCurve;
2684 740 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2685 16 : eType = wkbCurvePolygon;
2686 724 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2687 2 : eType = wkbMultiCurve;
2688 722 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2689 0 : eType = wkbMultiSurface;
2690 722 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2691 0 : eType = wkbTriangle;
2692 722 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2693 1 : eType = wkbPolyhedralSurface;
2694 721 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2695 5 : eType = wkbTIN;
2696 716 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2697 3 : eType = wkbCurve;
2698 713 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2699 3 : eType = wkbSurface;
2700 : else
2701 710 : eType = wkbUnknown;
2702 :
2703 3345 : if (bConvertTo3D)
2704 34 : eType = wkbSetZ(eType);
2705 3345 : if (bIsMeasured)
2706 2 : eType = wkbSetM(eType);
2707 :
2708 3345 : 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 3236 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2726 : bool bAddZM, bool bSpaceBeforeZM)
2727 : {
2728 3236 : const char *pszRet = "";
2729 3236 : switch (wkbFlatten(eGeomType))
2730 : {
2731 1724 : case wkbUnknown:
2732 1724 : pszRet = "Geometry";
2733 1724 : break;
2734 566 : case wkbPoint:
2735 566 : pszRet = "Point";
2736 566 : break;
2737 141 : case wkbLineString:
2738 141 : pszRet = "LineString";
2739 141 : break;
2740 186 : case wkbPolygon:
2741 186 : pszRet = "Polygon";
2742 186 : break;
2743 50 : case wkbMultiPoint:
2744 50 : pszRet = "MultiPoint";
2745 50 : break;
2746 85 : case wkbMultiLineString:
2747 85 : pszRet = "MultiLineString";
2748 85 : break;
2749 327 : case wkbMultiPolygon:
2750 327 : pszRet = "MultiPolygon";
2751 327 : 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 3236 : if (bAddZM)
2789 : {
2790 131 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2791 131 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2792 131 : if (bHasZ || bHasM)
2793 : {
2794 13 : if (bSpaceBeforeZM)
2795 1 : pszRet = CPLSPrintf("%s ", pszRet);
2796 13 : if (bHasZ)
2797 12 : pszRet = CPLSPrintf("%sZ", pszRet);
2798 13 : if (bHasM)
2799 5 : pszRet = CPLSPrintf("%sM", pszRet);
2800 : }
2801 : }
2802 3236 : if (!bCamelCase)
2803 3164 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2804 3236 : 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 86774 : 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 86774 : 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 84101 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
3362 : {
3363 : (void)hGEOSCtxt;
3364 : #ifdef HAVE_GEOS
3365 84101 : if (hGEOSCtxt != nullptr)
3366 : {
3367 84101 : finishGEOS_r(hGEOSCtxt);
3368 : }
3369 : #endif
3370 84101 : }
3371 : #ifdef HAVE_GEOS
3372 :
3373 : /************************************************************************/
3374 : /* canConvertToMultiPolygon() */
3375 : /************************************************************************/
3376 :
3377 157 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
3378 : {
3379 888 : for (const auto *poSubGeom : *poGC)
3380 : {
3381 : const OGRwkbGeometryType eSubGeomType =
3382 813 : wkbFlatten(poSubGeom->getGeometryType());
3383 813 : if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
3384 623 : eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
3385 : {
3386 82 : return false;
3387 : }
3388 : }
3389 :
3390 75 : 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 28 : while (poRing->getNumPoints() < MIN_RING_POINTS)
3507 : {
3508 20 : OGRPoint oPoint;
3509 10 : poRing->getPoint(0, &oPoint);
3510 10 : 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 122543 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3527 : const OGRGeometry *poGeom)
3528 : {
3529 122543 : GEOSGeom hGeom = nullptr;
3530 122543 : const size_t nDataSize = poGeom->WkbSize();
3531 : unsigned char *pabyData =
3532 122543 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3533 : #if GEOS_VERSION_MAJOR > 3 || \
3534 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3535 122543 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3536 : #else
3537 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3538 : #endif
3539 122543 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3540 : {
3541 121527 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3542 : }
3543 122543 : CPLFree(pabyData);
3544 :
3545 122543 : 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 122529 : 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 122529 : if (hGEOSCtxt == nullptr)
3580 0 : return nullptr;
3581 :
3582 122529 : 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 122529 : GEOSGeom hGeom = nullptr;
3593 :
3594 122529 : std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
3595 122529 : const OGRGeometry *poGeosInput = this;
3596 :
3597 122529 : const bool bHasZ = poGeosInput->Is3D();
3598 122529 : bool bHasM = poGeosInput->IsMeasured();
3599 :
3600 122529 : if (poGeosInput->hasCurveGeometry())
3601 : {
3602 868 : poModifiedInput.reset(poGeosInput->getLinearGeometry());
3603 868 : poGeosInput = poModifiedInput.get();
3604 : }
3605 :
3606 122529 : 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 122529 : if (eType == wkbTriangle)
3631 : {
3632 : poModifiedInput =
3633 98 : std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
3634 98 : poGeosInput = poModifiedInput.get();
3635 : }
3636 122431 : 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 121744 : else if (eType == wkbGeometryCollection &&
3649 157 : CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
3650 : {
3651 75 : if (!poModifiedInput)
3652 : {
3653 73 : 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 225 : poModifiedInput = OGRGeometryFactory::forceTo(
3660 75 : std::move(poModifiedInput),
3661 75 : OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
3662 225 : poModifiedInput = OGRGeometryFactory::forceTo(
3663 75 : std::move(poModifiedInput),
3664 75 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
3665 :
3666 75 : 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 122529 : if (bAddPointsIfNeeded)
3676 : {
3677 152 : oSilencer.emplace(hGEOSCtxt);
3678 : }
3679 :
3680 122529 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3681 : }
3682 :
3683 122529 : 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 122529 : 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 158386 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3726 : {
3727 158386 : 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 5108 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
4032 : const OGRGeometry *poOtherGeom,
4033 : OGRGeometry *poOGRProduct)
4034 : {
4035 10216 : if (poOGRProduct != nullptr &&
4036 10126 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
4037 5018 : (poGeom->hasCurveGeometry(true) ||
4038 3862 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
4039 : {
4040 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
4041 8 : delete poOGRProduct;
4042 8 : return poCurveGeom;
4043 : }
4044 5100 : return poOGRProduct;
4045 : }
4046 :
4047 : /************************************************************************/
4048 : /* BuildGeometryFromGEOS() */
4049 : /************************************************************************/
4050 :
4051 4959 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
4052 : GEOSGeom hGeosProduct,
4053 : const OGRGeometry *poSelf,
4054 : const OGRGeometry *poOtherGeom)
4055 : {
4056 4959 : OGRGeometry *poOGRProduct = nullptr;
4057 4959 : if (hGeosProduct != nullptr)
4058 : {
4059 : poOGRProduct =
4060 4956 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
4061 4956 : if (poOGRProduct != nullptr &&
4062 12730 : poSelf->getSpatialReference() != nullptr &&
4063 2818 : (poOtherGeom == nullptr ||
4064 2818 : (poOtherGeom->getSpatialReference() != nullptr &&
4065 2679 : poOtherGeom->getSpatialReference()->IsSame(
4066 : poSelf->getSpatialReference()))))
4067 : {
4068 2733 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
4069 : }
4070 : poOGRProduct =
4071 4956 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
4072 4956 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
4073 : }
4074 4959 : return poOGRProduct;
4075 : }
4076 :
4077 : /************************************************************************/
4078 : /* BuildGeometryFromTwoGeoms() */
4079 : /************************************************************************/
4080 :
4081 3940 : static OGRGeometry *BuildGeometryFromTwoGeoms(
4082 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4083 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
4084 : const GEOSGeometry *,
4085 : const GEOSGeometry *))
4086 : {
4087 3940 : OGRGeometry *poOGRProduct = nullptr;
4088 :
4089 3940 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4090 3940 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4091 3940 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4092 3940 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4093 : {
4094 : GEOSGeom hGeosProduct =
4095 3940 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
4096 :
4097 : poOGRProduct =
4098 3940 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
4099 : }
4100 3940 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4101 3940 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4102 3940 : poSelf->freeGEOSContext(hGEOSCtxt);
4103 :
4104 3940 : return poOGRProduct;
4105 : }
4106 :
4107 : /************************************************************************/
4108 : /* OGRGEOSBooleanPredicate() */
4109 : /************************************************************************/
4110 :
4111 22775 : static bool OGRGEOSBooleanPredicate(
4112 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4113 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
4114 : const GEOSGeometry *))
4115 : {
4116 22775 : bool bResult = false;
4117 :
4118 22775 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4119 22775 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4120 22775 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4121 22775 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4122 : {
4123 22220 : bResult =
4124 22220 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
4125 : }
4126 22775 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4127 22775 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4128 22775 : poSelf->freeGEOSContext(hGEOSCtxt);
4129 :
4130 22775 : 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 3113 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5319 :
5320 : {
5321 3113 : 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 3113 : 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 : OGRGeometry *
5652 638 : OGRGeometry::UnaryUnion(UNUSED_IF_NO_GEOS GDALProgressFunc pfnProgress,
5653 : UNUSED_IF_NO_GEOS void *pProgressData) const
5654 :
5655 : {
5656 : #ifndef HAVE_GEOS
5657 :
5658 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5659 : return nullptr;
5660 : #else
5661 :
5662 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5663 : if (IsEmpty())
5664 : {
5665 : // GEOS < 3.11 crashes on an empty geometry
5666 : auto poRet = new OGRGeometryCollection();
5667 : poRet->assignSpatialReference(getSpatialReference());
5668 : return poRet;
5669 : }
5670 : #endif
5671 638 : OGRGeometry *poOGRProduct = nullptr;
5672 :
5673 638 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5674 638 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5675 638 : if (hThisGeosGeom != nullptr)
5676 : {
5677 : GDALGEOSProgressReporter oReporter(hGEOSCtxt, pfnProgress,
5678 638 : pProgressData);
5679 638 : GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5680 638 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5681 :
5682 : poOGRProduct =
5683 638 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5684 : }
5685 638 : freeGEOSContext(hGEOSCtxt);
5686 :
5687 638 : return poOGRProduct;
5688 :
5689 : #endif // HAVE_GEOS
5690 : }
5691 :
5692 : /************************************************************************/
5693 : /* OGR_G_UnaryUnion() */
5694 : /************************************************************************/
5695 :
5696 : /**
5697 : * \brief Returns the union of all components of a single geometry.
5698 : *
5699 : * Usually used to convert a collection into the smallest set of polygons that
5700 : * cover the same area.
5701 : *
5702 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5703 : *
5704 : * Geometry validity is not checked. In case you are unsure of the validity
5705 : * of the input geometries, call IsValid() before, otherwise the result might
5706 : * be wrong.
5707 : *
5708 : * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5709 : *
5710 : * This function is built on the GEOS library, check it for the definition
5711 : * of the geometry operation.
5712 : * If OGR is built without the GEOS library, this function will always fail,
5713 : * issuing a CPLE_NotSupported error.
5714 : *
5715 : * @param hThis the geometry.
5716 : *
5717 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5718 : * or NULL if an error occurs.
5719 : *
5720 : * @since GDAL 3.7
5721 : */
5722 :
5723 3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5724 :
5725 : {
5726 3 : VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5727 :
5728 3 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5729 : }
5730 :
5731 : /************************************************************************/
5732 : /* Difference() */
5733 : /************************************************************************/
5734 :
5735 : /**
5736 : * \brief Compute difference.
5737 : *
5738 : * Generates a new geometry which is the region of this geometry with the
5739 : * region of the second geometry removed.
5740 : *
5741 : * Geometry validity is not checked. In case you are unsure of the validity
5742 : * of the input geometries, call IsValid() before, otherwise the result might
5743 : * be wrong.
5744 : *
5745 : * This method is the same as the C function OGR_G_Difference().
5746 : *
5747 : * This method is built on the GEOS library, check it for the definition
5748 : * of the geometry operation.
5749 : * If OGR is built without the GEOS library, this method will always fail,
5750 : * issuing a CPLE_NotSupported error.
5751 : *
5752 : * @param poOtherGeom the other geometry removed from "this" geometry.
5753 : *
5754 : * @return a new geometry to be freed by the caller, or NULL if the difference
5755 : * is empty or if an error occurs.
5756 : */
5757 :
5758 : OGRGeometry *
5759 752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5760 :
5761 : {
5762 752 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5763 : {
5764 : #ifndef HAVE_SFCGAL
5765 :
5766 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5767 0 : return nullptr;
5768 :
5769 : #else
5770 :
5771 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5772 : if (poThis == nullptr)
5773 : return nullptr;
5774 :
5775 : sfcgal_geometry_t *poOther =
5776 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5777 : if (poOther == nullptr)
5778 : {
5779 : sfcgal_geometry_delete(poThis);
5780 : return nullptr;
5781 : }
5782 :
5783 : sfcgal_geometry_t *poRes =
5784 : sfcgal_geometry_difference_3d(poThis, poOther);
5785 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5786 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5787 : poOtherGeom->getSpatialReference() != nullptr &&
5788 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5789 : h_prodGeom->assignSpatialReference(getSpatialReference());
5790 :
5791 : sfcgal_geometry_delete(poThis);
5792 : sfcgal_geometry_delete(poOther);
5793 : sfcgal_geometry_delete(poRes);
5794 :
5795 : return h_prodGeom;
5796 :
5797 : #endif
5798 : }
5799 :
5800 : else
5801 : {
5802 : #ifndef HAVE_GEOS
5803 :
5804 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5805 : return nullptr;
5806 :
5807 : #else
5808 752 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5809 : #endif /* HAVE_GEOS */
5810 : }
5811 : }
5812 :
5813 : /************************************************************************/
5814 : /* OGR_G_Difference() */
5815 : /************************************************************************/
5816 :
5817 : /**
5818 : * \brief Compute difference.
5819 : *
5820 : * Generates a new geometry which is the region of this geometry with the
5821 : * region of the other geometry removed.
5822 : *
5823 : * Geometry validity is not checked. In case you are unsure of the validity
5824 : * of the input geometries, call IsValid() before, otherwise the result might
5825 : * be wrong.
5826 : *
5827 : * This function is the same as the C++ method OGRGeometry::Difference().
5828 : *
5829 : * This function is built on the GEOS library, check it for the definition
5830 : * of the geometry operation.
5831 : * If OGR is built without the GEOS library, this function will always fail,
5832 : * issuing a CPLE_NotSupported error.
5833 : *
5834 : * @param hThis the geometry.
5835 : * @param hOther the other geometry.
5836 : *
5837 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5838 : * or NULL if the difference is empty or if an error occurs.
5839 : */
5840 :
5841 6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5842 :
5843 : {
5844 6 : VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5845 :
5846 12 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5847 12 : OGRGeometry::FromHandle(hOther)));
5848 : }
5849 :
5850 : /************************************************************************/
5851 : /* SymDifference() */
5852 : /************************************************************************/
5853 :
5854 : /**
5855 : * \brief Compute symmetric difference.
5856 : *
5857 : * Generates a new geometry which is the symmetric difference of this
5858 : * geometry and the second geometry passed into the method.
5859 : *
5860 : * Geometry validity is not checked. In case you are unsure of the validity
5861 : * of the input geometries, call IsValid() before, otherwise the result might
5862 : * be wrong.
5863 : *
5864 : * This method is the same as the C function OGR_G_SymDifference().
5865 : *
5866 : * This method is built on the GEOS library, check it for the definition
5867 : * of the geometry operation.
5868 : * If OGR is built without the GEOS library, this method will always fail,
5869 : * issuing a CPLE_NotSupported error.
5870 : *
5871 : * @param poOtherGeom the other geometry.
5872 : *
5873 : * @return a new geometry to be freed by the caller, or NULL if the difference
5874 : * is empty or if an error occurs.
5875 : *
5876 : */
5877 :
5878 7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
5879 :
5880 : {
5881 : (void)poOtherGeom;
5882 7 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5883 : {
5884 : #ifndef HAVE_SFCGAL
5885 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5886 0 : return nullptr;
5887 : #else
5888 : OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5889 : if (poFirstDifference == nullptr)
5890 : return nullptr;
5891 :
5892 : OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5893 : if (poOtherDifference == nullptr)
5894 : {
5895 : delete poFirstDifference;
5896 : return nullptr;
5897 : }
5898 :
5899 : OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5900 : delete poFirstDifference;
5901 : delete poOtherDifference;
5902 : return poSymDiff;
5903 : #endif
5904 : }
5905 :
5906 : #ifndef HAVE_GEOS
5907 :
5908 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5909 : return nullptr;
5910 :
5911 : #else
5912 7 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5913 : #endif // HAVE_GEOS
5914 : }
5915 :
5916 : //! @cond Doxygen_Suppress
5917 : /**
5918 : * \brief Compute symmetric difference (deprecated)
5919 : *
5920 : * @deprecated
5921 : *
5922 : * @see OGRGeometry::SymDifference()
5923 : */
5924 : OGRGeometry *
5925 0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5926 :
5927 : {
5928 0 : return SymDifference(poOtherGeom);
5929 : }
5930 :
5931 : //! @endcond
5932 :
5933 : /************************************************************************/
5934 : /* OGR_G_SymDifference() */
5935 : /************************************************************************/
5936 :
5937 : /**
5938 : * \brief Compute symmetric difference.
5939 : *
5940 : * Generates a new geometry which is the symmetric difference of this
5941 : * geometry and the other geometry.
5942 : *
5943 : * Geometry validity is not checked. In case you are unsure of the validity
5944 : * of the input geometries, call IsValid() before, otherwise the result might
5945 : * be wrong.
5946 : *
5947 : * This function is the same as the C++ method
5948 : * OGRGeometry::SymmetricDifference().
5949 : *
5950 : * This function is built on the GEOS library, check it for the definition
5951 : * of the geometry operation.
5952 : * If OGR is built without the GEOS library, this function will always fail,
5953 : * issuing a CPLE_NotSupported error.
5954 : *
5955 : * @param hThis the geometry.
5956 : * @param hOther the other geometry.
5957 : *
5958 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5959 : * or NULL if the difference is empty or if an error occurs.
5960 : *
5961 : */
5962 :
5963 7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5964 :
5965 : {
5966 7 : VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5967 :
5968 14 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5969 14 : OGRGeometry::FromHandle(hOther)));
5970 : }
5971 :
5972 : /**
5973 : * \brief Compute symmetric difference (deprecated)
5974 : *
5975 : * @deprecated
5976 : *
5977 : * @see OGR_G_SymmetricDifference()
5978 : */
5979 0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5980 :
5981 : {
5982 0 : VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5983 :
5984 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5985 0 : OGRGeometry::FromHandle(hOther)));
5986 : }
5987 :
5988 : /************************************************************************/
5989 : /* Disjoint() */
5990 : /************************************************************************/
5991 :
5992 : /**
5993 : * \brief Test for disjointness.
5994 : *
5995 : * Tests if this geometry and the other passed into the method are disjoint.
5996 : *
5997 : * Geometry validity is not checked. In case you are unsure of the validity
5998 : * of the input geometries, call IsValid() before, otherwise the result might
5999 : * be wrong.
6000 : *
6001 : * This method is the same as the C function OGR_G_Disjoint().
6002 : *
6003 : * This method is built on the GEOS library, check it for the definition
6004 : * of the geometry operation.
6005 : * If OGR is built without the GEOS library, this method will always fail,
6006 : * issuing a CPLE_NotSupported error.
6007 : *
6008 : * @param poOtherGeom the geometry to compare to this geometry.
6009 : *
6010 : * @return TRUE if they are disjoint, otherwise FALSE.
6011 : */
6012 :
6013 8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
6014 :
6015 : {
6016 : (void)poOtherGeom;
6017 : #ifndef HAVE_GEOS
6018 :
6019 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6020 : return FALSE;
6021 :
6022 : #else
6023 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
6024 : #endif // HAVE_GEOS
6025 : }
6026 :
6027 : /************************************************************************/
6028 : /* OGR_G_Disjoint() */
6029 : /************************************************************************/
6030 :
6031 : /**
6032 : * \brief Test for disjointness.
6033 : *
6034 : * Tests if this geometry and the other geometry are disjoint.
6035 : *
6036 : * Geometry validity is not checked. In case you are unsure of the validity
6037 : * of the input geometries, call IsValid() before, otherwise the result might
6038 : * be wrong.
6039 : *
6040 : * This function is the same as the C++ method OGRGeometry::Disjoint().
6041 : *
6042 : * This function is built on the GEOS library, check it for the definition
6043 : * of the geometry operation.
6044 : * If OGR is built without the GEOS library, this function will always fail,
6045 : * issuing a CPLE_NotSupported error.
6046 : *
6047 : * @param hThis the geometry to compare.
6048 : * @param hOther the other geometry to compare.
6049 : *
6050 : * @return TRUE if they are disjoint, otherwise FALSE.
6051 : */
6052 8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
6053 :
6054 : {
6055 8 : VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
6056 :
6057 16 : return OGRGeometry::FromHandle(hThis)->Disjoint(
6058 16 : OGRGeometry::FromHandle(hOther));
6059 : }
6060 :
6061 : /************************************************************************/
6062 : /* Touches() */
6063 : /************************************************************************/
6064 :
6065 : /**
6066 : * \brief Test for touching.
6067 : *
6068 : * Tests if this geometry and the other passed into the method are touching.
6069 : *
6070 : * Geometry validity is not checked. In case you are unsure of the validity
6071 : * of the input geometries, call IsValid() before, otherwise the result might
6072 : * be wrong.
6073 : *
6074 : * This method is the same as the C function OGR_G_Touches().
6075 : *
6076 : * This method is built on the GEOS library, check it for the definition
6077 : * of the geometry operation.
6078 : * If OGR is built without the GEOS library, this method will always fail,
6079 : * issuing a CPLE_NotSupported error.
6080 : *
6081 : * @param poOtherGeom the geometry to compare to this geometry.
6082 : *
6083 : * @return TRUE if they are touching, otherwise FALSE.
6084 : */
6085 :
6086 11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
6087 :
6088 : {
6089 : (void)poOtherGeom;
6090 : #ifndef HAVE_GEOS
6091 :
6092 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6093 : return FALSE;
6094 :
6095 : #else
6096 11 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
6097 : #endif // HAVE_GEOS
6098 : }
6099 :
6100 : /************************************************************************/
6101 : /* OGR_G_Touches() */
6102 : /************************************************************************/
6103 : /**
6104 : * \brief Test for touching.
6105 : *
6106 : * Tests if this geometry and the other geometry are touching.
6107 : *
6108 : * Geometry validity is not checked. In case you are unsure of the validity
6109 : * of the input geometries, call IsValid() before, otherwise the result might
6110 : * be wrong.
6111 : *
6112 : * This function is the same as the C++ method OGRGeometry::Touches().
6113 : *
6114 : * This function is built on the GEOS library, check it for the definition
6115 : * of the geometry operation.
6116 : * If OGR is built without the GEOS library, this function will always fail,
6117 : * issuing a CPLE_NotSupported error.
6118 : *
6119 : * @param hThis the geometry to compare.
6120 : * @param hOther the other geometry to compare.
6121 : *
6122 : * @return TRUE if they are touching, otherwise FALSE.
6123 : */
6124 :
6125 8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
6126 :
6127 : {
6128 8 : VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
6129 :
6130 16 : return OGRGeometry::FromHandle(hThis)->Touches(
6131 16 : OGRGeometry::FromHandle(hOther));
6132 : }
6133 :
6134 : /************************************************************************/
6135 : /* Crosses() */
6136 : /************************************************************************/
6137 :
6138 : /**
6139 : * \brief Test for crossing.
6140 : *
6141 : * Tests if this geometry and the other passed into the method are crossing.
6142 : *
6143 : * Geometry validity is not checked. In case you are unsure of the validity
6144 : * of the input geometries, call IsValid() before, otherwise the result might
6145 : * be wrong.
6146 : *
6147 : * This method is the same as the C function OGR_G_Crosses().
6148 : *
6149 : * This method is built on the GEOS library, check it for the definition
6150 : * of the geometry operation.
6151 : * If OGR is built without the GEOS library, this method will always fail,
6152 : * issuing a CPLE_NotSupported error.
6153 : *
6154 : * @param poOtherGeom the geometry to compare to this geometry.
6155 : *
6156 : * @return TRUE if they are crossing, otherwise FALSE.
6157 : */
6158 :
6159 8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
6160 :
6161 : {
6162 8 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
6163 : {
6164 : #ifndef HAVE_SFCGAL
6165 :
6166 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
6167 0 : return FALSE;
6168 :
6169 : #else
6170 :
6171 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
6172 : if (poThis == nullptr)
6173 : return FALSE;
6174 :
6175 : sfcgal_geometry_t *poOther =
6176 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
6177 : if (poOther == nullptr)
6178 : {
6179 : sfcgal_geometry_delete(poThis);
6180 : return FALSE;
6181 : }
6182 :
6183 : int res = sfcgal_geometry_intersects_3d(poThis, poOther);
6184 :
6185 : sfcgal_geometry_delete(poThis);
6186 : sfcgal_geometry_delete(poOther);
6187 :
6188 : return (res == 1) ? TRUE : FALSE;
6189 :
6190 : #endif
6191 : }
6192 :
6193 : else
6194 : {
6195 :
6196 : #ifndef HAVE_GEOS
6197 :
6198 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6199 : return FALSE;
6200 :
6201 : #else
6202 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
6203 : #endif /* HAVE_GEOS */
6204 : }
6205 : }
6206 :
6207 : /************************************************************************/
6208 : /* OGR_G_Crosses() */
6209 : /************************************************************************/
6210 : /**
6211 : * \brief Test for crossing.
6212 : *
6213 : * Tests if this geometry and the other geometry are crossing.
6214 : *
6215 : * Geometry validity is not checked. In case you are unsure of the validity
6216 : * of the input geometries, call IsValid() before, otherwise the result might
6217 : * be wrong.
6218 : *
6219 : * This function is the same as the C++ method OGRGeometry::Crosses().
6220 : *
6221 : * This function is built on the GEOS library, check it for the definition
6222 : * of the geometry operation.
6223 : * If OGR is built without the GEOS library, this function will always fail,
6224 : * issuing a CPLE_NotSupported error.
6225 : *
6226 : * @param hThis the geometry to compare.
6227 : * @param hOther the other geometry to compare.
6228 : *
6229 : * @return TRUE if they are crossing, otherwise FALSE.
6230 : */
6231 :
6232 8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
6233 :
6234 : {
6235 8 : VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
6236 :
6237 16 : return OGRGeometry::FromHandle(hThis)->Crosses(
6238 16 : OGRGeometry::FromHandle(hOther));
6239 : }
6240 :
6241 : /************************************************************************/
6242 : /* Within() */
6243 : /************************************************************************/
6244 :
6245 : /**
6246 : * \brief Test for containment.
6247 : *
6248 : * Tests if actual geometry object is within the passed geometry.
6249 : *
6250 : * Geometry validity is not checked. In case you are unsure of the validity
6251 : * of the input geometries, call IsValid() before, otherwise the result might
6252 : * be wrong.
6253 : *
6254 : * This method is the same as the C function OGR_G_Within().
6255 : *
6256 : * This method is built on the GEOS library, check it for the definition
6257 : * of the geometry operation.
6258 : * If OGR is built without the GEOS library, this method will always fail,
6259 : * issuing a CPLE_NotSupported error.
6260 : *
6261 : * @param poOtherGeom the geometry to compare to this geometry.
6262 : *
6263 : * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
6264 : */
6265 :
6266 22419 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
6267 :
6268 : {
6269 : (void)poOtherGeom;
6270 : #ifndef HAVE_GEOS
6271 :
6272 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6273 : return FALSE;
6274 :
6275 : #else
6276 22419 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
6277 : #endif // HAVE_GEOS
6278 : }
6279 :
6280 : /************************************************************************/
6281 : /* OGR_G_Within() */
6282 : /************************************************************************/
6283 :
6284 : /**
6285 : * \brief Test for containment.
6286 : *
6287 : * Tests if this geometry is within the other geometry.
6288 : *
6289 : * Geometry validity is not checked. In case you are unsure of the validity
6290 : * of the input geometries, call IsValid() before, otherwise the result might
6291 : * be wrong.
6292 : *
6293 : * This function is the same as the C++ method OGRGeometry::Within().
6294 : *
6295 : * This function is built on the GEOS library, check it for the definition
6296 : * of the geometry operation.
6297 : * If OGR is built without the GEOS library, this function will always fail,
6298 : * issuing a CPLE_NotSupported error.
6299 : *
6300 : * @param hThis the geometry to compare.
6301 : * @param hOther the other geometry to compare.
6302 : *
6303 : * @return TRUE if hThis is within hOther, otherwise FALSE.
6304 : */
6305 7376 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
6306 :
6307 : {
6308 7376 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
6309 :
6310 14752 : return OGRGeometry::FromHandle(hThis)->Within(
6311 14752 : OGRGeometry::FromHandle(hOther));
6312 : }
6313 :
6314 : /************************************************************************/
6315 : /* Contains() */
6316 : /************************************************************************/
6317 :
6318 : /**
6319 : * \brief Test for containment.
6320 : *
6321 : * Tests if actual geometry object contains the passed geometry.
6322 : *
6323 : * Geometry validity is not checked. In case you are unsure of the validity
6324 : * of the input geometries, call IsValid() before, otherwise the result might
6325 : * be wrong.
6326 : *
6327 : * This method is the same as the C function OGR_G_Contains().
6328 : *
6329 : * This method is built on the GEOS library, check it for the definition
6330 : * of the geometry operation.
6331 : * If OGR is built without the GEOS library, this method will always fail,
6332 : * issuing a CPLE_NotSupported error.
6333 : *
6334 : * @param poOtherGeom the geometry to compare to this geometry.
6335 : *
6336 : * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
6337 : */
6338 :
6339 322 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
6340 :
6341 : {
6342 : (void)poOtherGeom;
6343 : #ifndef HAVE_GEOS
6344 :
6345 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6346 : return FALSE;
6347 :
6348 : #else
6349 322 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
6350 : #endif // HAVE_GEOS
6351 : }
6352 :
6353 : /************************************************************************/
6354 : /* OGR_G_Contains() */
6355 : /************************************************************************/
6356 :
6357 : /**
6358 : * \brief Test for containment.
6359 : *
6360 : * Tests if this geometry contains the other geometry.
6361 : *
6362 : * Geometry validity is not checked. In case you are unsure of the validity
6363 : * of the input geometries, call IsValid() before, otherwise the result might
6364 : * be wrong.
6365 : *
6366 : * This function is the same as the C++ method OGRGeometry::Contains().
6367 : *
6368 : * This function is built on the GEOS library, check it for the definition
6369 : * of the geometry operation.
6370 : * If OGR is built without the GEOS library, this function will always fail,
6371 : * issuing a CPLE_NotSupported error.
6372 : *
6373 : * @param hThis the geometry to compare.
6374 : * @param hOther the other geometry to compare.
6375 : *
6376 : * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
6377 : */
6378 10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
6379 :
6380 : {
6381 10 : VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
6382 :
6383 20 : return OGRGeometry::FromHandle(hThis)->Contains(
6384 20 : OGRGeometry::FromHandle(hOther));
6385 : }
6386 :
6387 : /************************************************************************/
6388 : /* Overlaps() */
6389 : /************************************************************************/
6390 :
6391 : /**
6392 : * \brief Test for overlap.
6393 : *
6394 : * Tests if this geometry and the other passed into the method overlap, that is
6395 : * their intersection has a non-zero area.
6396 : *
6397 : * Geometry validity is not checked. In case you are unsure of the validity
6398 : * of the input geometries, call IsValid() before, otherwise the result might
6399 : * be wrong.
6400 : *
6401 : * This method is the same as the C function OGR_G_Overlaps().
6402 : *
6403 : * This method is built on the GEOS library, check it for the definition
6404 : * of the geometry operation.
6405 : * If OGR is built without the GEOS library, this method will always fail,
6406 : * issuing a CPLE_NotSupported error.
6407 : *
6408 : * @param poOtherGeom the geometry to compare to this geometry.
6409 : *
6410 : * @return TRUE if they are overlapping, otherwise FALSE.
6411 : */
6412 :
6413 7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
6414 :
6415 : {
6416 : (void)poOtherGeom;
6417 : #ifndef HAVE_GEOS
6418 :
6419 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6420 : return FALSE;
6421 :
6422 : #else
6423 7 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6424 : #endif // HAVE_GEOS
6425 : }
6426 :
6427 : /************************************************************************/
6428 : /* OGR_G_Overlaps() */
6429 : /************************************************************************/
6430 : /**
6431 : * \brief Test for overlap.
6432 : *
6433 : * Tests if this geometry and the other geometry overlap, that is their
6434 : * intersection has a non-zero area.
6435 : *
6436 : * Geometry validity is not checked. In case you are unsure of the validity
6437 : * of the input geometries, call IsValid() before, otherwise the result might
6438 : * be wrong.
6439 : *
6440 : * This function is the same as the C++ method OGRGeometry::Overlaps().
6441 : *
6442 : * This function is built on the GEOS library, check it for the definition
6443 : * of the geometry operation.
6444 : * If OGR is built without the GEOS library, this function will always fail,
6445 : * issuing a CPLE_NotSupported error.
6446 : *
6447 : * @param hThis the geometry to compare.
6448 : * @param hOther the other geometry to compare.
6449 : *
6450 : * @return TRUE if they are overlapping, otherwise FALSE.
6451 : */
6452 :
6453 7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6454 :
6455 : {
6456 7 : VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6457 :
6458 14 : return OGRGeometry::FromHandle(hThis)->Overlaps(
6459 14 : OGRGeometry::FromHandle(hOther));
6460 : }
6461 :
6462 : /************************************************************************/
6463 : /* closeRings() */
6464 : /************************************************************************/
6465 :
6466 : /**
6467 : * \brief Force rings to be closed.
6468 : *
6469 : * If this geometry, or any contained geometries has polygon rings that
6470 : * are not closed, they will be closed by adding the starting point at
6471 : * the end.
6472 : */
6473 :
6474 1264 : void OGRGeometry::closeRings()
6475 : {
6476 1264 : }
6477 :
6478 : /************************************************************************/
6479 : /* OGR_G_CloseRings() */
6480 : /************************************************************************/
6481 :
6482 : /**
6483 : * \brief Force rings to be closed.
6484 : *
6485 : * If this geometry, or any contained geometries has polygon rings that
6486 : * are not closed, they will be closed by adding the starting point at
6487 : * the end.
6488 : *
6489 : * @param hGeom handle to the geometry.
6490 : */
6491 :
6492 6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
6493 :
6494 : {
6495 6 : VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6496 :
6497 6 : OGRGeometry::FromHandle(hGeom)->closeRings();
6498 : }
6499 :
6500 : /************************************************************************/
6501 : /* Centroid() */
6502 : /************************************************************************/
6503 :
6504 : /**
6505 : * \brief Compute the geometry centroid.
6506 : *
6507 : * The centroid location is applied to the passed in OGRPoint object.
6508 : * The centroid is not necessarily within the geometry.
6509 : *
6510 : * This method relates to the SFCOM ISurface::get_Centroid() method
6511 : * however the current implementation based on GEOS can operate on other
6512 : * geometry types such as multipoint, linestring, geometrycollection such as
6513 : * multipolygons.
6514 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6515 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6516 : * (multipolygons).
6517 : *
6518 : * This function is the same as the C function OGR_G_Centroid().
6519 : *
6520 : * This function is built on the GEOS library, check it for the definition
6521 : * of the geometry operation.
6522 : * If OGR is built without the GEOS library, this function will always fail,
6523 : * issuing a CPLE_NotSupported error.
6524 : *
6525 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6526 : *
6527 : * to OGRPolygon)
6528 : */
6529 :
6530 5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6531 :
6532 : {
6533 5 : if (poPoint == nullptr)
6534 0 : return OGRERR_FAILURE;
6535 :
6536 : #ifndef HAVE_GEOS
6537 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6538 : return OGRERR_FAILURE;
6539 :
6540 : #else
6541 :
6542 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6543 : GEOSGeom hThisGeosGeom =
6544 5 : exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6545 :
6546 5 : if (hThisGeosGeom != nullptr)
6547 : {
6548 5 : GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6549 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6550 :
6551 5 : if (hOtherGeosGeom == nullptr)
6552 : {
6553 0 : freeGEOSContext(hGEOSCtxt);
6554 0 : return OGRERR_FAILURE;
6555 : }
6556 :
6557 : OGRGeometry *poCentroidGeom =
6558 5 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6559 :
6560 5 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6561 :
6562 5 : if (poCentroidGeom == nullptr)
6563 : {
6564 0 : freeGEOSContext(hGEOSCtxt);
6565 0 : return OGRERR_FAILURE;
6566 : }
6567 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6568 : {
6569 0 : delete poCentroidGeom;
6570 0 : freeGEOSContext(hGEOSCtxt);
6571 0 : return OGRERR_FAILURE;
6572 : }
6573 :
6574 5 : if (getSpatialReference() != nullptr)
6575 0 : poCentroidGeom->assignSpatialReference(getSpatialReference());
6576 :
6577 5 : OGRPoint *poCentroid = poCentroidGeom->toPoint();
6578 :
6579 5 : if (!poCentroid->IsEmpty())
6580 : {
6581 4 : poPoint->setX(poCentroid->getX());
6582 4 : poPoint->setY(poCentroid->getY());
6583 : }
6584 : else
6585 : {
6586 1 : poPoint->empty();
6587 : }
6588 :
6589 5 : delete poCentroidGeom;
6590 :
6591 5 : freeGEOSContext(hGEOSCtxt);
6592 5 : return OGRERR_NONE;
6593 : }
6594 : else
6595 : {
6596 0 : freeGEOSContext(hGEOSCtxt);
6597 0 : return OGRERR_FAILURE;
6598 : }
6599 :
6600 : #endif // HAVE_GEOS
6601 : }
6602 :
6603 : /************************************************************************/
6604 : /* OGR_G_Centroid() */
6605 : /************************************************************************/
6606 :
6607 : /**
6608 : * \brief Compute the geometry centroid.
6609 : *
6610 : * The centroid location is applied to the passed in OGRPoint object.
6611 : * The centroid is not necessarily within the geometry.
6612 : *
6613 : * This method relates to the SFCOM ISurface::get_Centroid() method
6614 : * however the current implementation based on GEOS can operate on other
6615 : * geometry types such as multipoint, linestring, geometrycollection such as
6616 : * multipolygons.
6617 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6618 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6619 : * (multipolygons).
6620 : *
6621 : * This function is the same as the C++ method OGRGeometry::Centroid().
6622 : *
6623 : * This function is built on the GEOS library, check it for the definition
6624 : * of the geometry operation.
6625 : * If OGR is built without the GEOS library, this function will always fail,
6626 : * issuing a CPLE_NotSupported error.
6627 : *
6628 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6629 : */
6630 :
6631 5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6632 :
6633 : {
6634 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6635 :
6636 5 : OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6637 5 : if (poCentroidGeom == nullptr)
6638 0 : return OGRERR_FAILURE;
6639 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6640 : {
6641 0 : CPLError(CE_Failure, CPLE_AppDefined,
6642 : "Passed wrong geometry type as centroid argument.");
6643 0 : return OGRERR_FAILURE;
6644 : }
6645 :
6646 5 : return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6647 : }
6648 :
6649 : /************************************************************************/
6650 : /* OGR_G_PointOnSurface() */
6651 : /************************************************************************/
6652 :
6653 : /**
6654 : * \brief Returns a point guaranteed to lie on the surface.
6655 : *
6656 : * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6657 : * however the current implementation based on GEOS can operate on other
6658 : * geometry types than the types that are supported by SQL/MM-Part 3 :
6659 : * surfaces (polygons) and multisurfaces (multipolygons).
6660 : *
6661 : * This method is built on the GEOS library, check it for the definition
6662 : * of the geometry operation.
6663 : * If OGR is built without the GEOS library, this method will always fail,
6664 : * issuing a CPLE_NotSupported error.
6665 : *
6666 : * @param hGeom the geometry to operate on.
6667 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6668 : * or NULL if an error occurs.
6669 : *
6670 : */
6671 :
6672 4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6673 :
6674 : {
6675 4 : VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6676 :
6677 : #ifndef HAVE_GEOS
6678 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6679 : return nullptr;
6680 : #else
6681 :
6682 4 : OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6683 :
6684 4 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6685 4 : GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6686 :
6687 4 : if (hThisGeosGeom != nullptr)
6688 : {
6689 : GEOSGeom hOtherGeosGeom =
6690 4 : GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6691 4 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6692 :
6693 4 : if (hOtherGeosGeom == nullptr)
6694 : {
6695 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6696 0 : return nullptr;
6697 : }
6698 :
6699 : OGRGeometry *poInsidePointGeom =
6700 4 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6701 :
6702 4 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6703 :
6704 4 : if (poInsidePointGeom == nullptr)
6705 : {
6706 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6707 0 : return nullptr;
6708 : }
6709 4 : if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6710 : {
6711 0 : delete poInsidePointGeom;
6712 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6713 0 : return nullptr;
6714 : }
6715 :
6716 4 : if (poThis->getSpatialReference() != nullptr)
6717 0 : poInsidePointGeom->assignSpatialReference(
6718 0 : poThis->getSpatialReference());
6719 :
6720 4 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6721 4 : return OGRGeometry::ToHandle(poInsidePointGeom);
6722 : }
6723 :
6724 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6725 0 : return nullptr;
6726 : #endif
6727 : }
6728 :
6729 : /************************************************************************/
6730 : /* PointOnSurfaceInternal() */
6731 : /************************************************************************/
6732 :
6733 : //! @cond Doxygen_Suppress
6734 0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6735 : {
6736 0 : if (poPoint == nullptr || poPoint->IsEmpty())
6737 0 : return OGRERR_FAILURE;
6738 :
6739 0 : OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6740 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6741 0 : if (hInsidePoint == nullptr)
6742 0 : return OGRERR_FAILURE;
6743 :
6744 0 : OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6745 0 : if (poInsidePoint->IsEmpty())
6746 : {
6747 0 : poPoint->empty();
6748 : }
6749 : else
6750 : {
6751 0 : poPoint->setX(poInsidePoint->getX());
6752 0 : poPoint->setY(poInsidePoint->getY());
6753 : }
6754 :
6755 0 : OGR_G_DestroyGeometry(hInsidePoint);
6756 :
6757 0 : return OGRERR_NONE;
6758 : }
6759 :
6760 : //! @endcond
6761 :
6762 : /************************************************************************/
6763 : /* Simplify() */
6764 : /************************************************************************/
6765 :
6766 : /**
6767 : * \brief Simplify the geometry.
6768 : *
6769 : * This function is the same as the C function OGR_G_Simplify().
6770 : *
6771 : * This function is built on the GEOS library, check it for the definition
6772 : * of the geometry operation.
6773 : * If OGR is built without the GEOS library, this function will always fail,
6774 : * issuing a CPLE_NotSupported error.
6775 : *
6776 : * @param dTolerance the distance tolerance for the simplification.
6777 : *
6778 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6779 : *
6780 : */
6781 :
6782 55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
6783 :
6784 : {
6785 : (void)dTolerance;
6786 : #ifndef HAVE_GEOS
6787 :
6788 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6789 : return nullptr;
6790 :
6791 : #else
6792 55 : OGRGeometry *poOGRProduct = nullptr;
6793 :
6794 55 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6795 55 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6796 55 : if (hThisGeosGeom != nullptr)
6797 : {
6798 : GEOSGeom hGeosProduct =
6799 55 : GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6800 55 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6801 : poOGRProduct =
6802 55 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6803 : }
6804 55 : freeGEOSContext(hGEOSCtxt);
6805 55 : return poOGRProduct;
6806 :
6807 : #endif // HAVE_GEOS
6808 : }
6809 :
6810 : /************************************************************************/
6811 : /* OGR_G_Simplify() */
6812 : /************************************************************************/
6813 :
6814 : /**
6815 : * \brief Compute a simplified geometry.
6816 : *
6817 : * This function is the same as the C++ method OGRGeometry::Simplify().
6818 : *
6819 : * This function is built on the GEOS library, check it for the definition
6820 : * of the geometry operation.
6821 : * If OGR is built without the GEOS library, this function will always fail,
6822 : * issuing a CPLE_NotSupported error.
6823 : *
6824 : * @param hThis the geometry.
6825 : * @param dTolerance the distance tolerance for the simplification.
6826 : *
6827 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6828 : * or NULL if an error occurs.
6829 : *
6830 : */
6831 :
6832 1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6833 :
6834 : {
6835 1 : VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6836 1 : return OGRGeometry::ToHandle(
6837 1 : OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6838 : }
6839 :
6840 : /************************************************************************/
6841 : /* SimplifyPreserveTopology() */
6842 : /************************************************************************/
6843 :
6844 : /**
6845 : * \brief Simplify the geometry while preserving topology.
6846 : *
6847 : * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6848 : *
6849 : * This function is built on the GEOS library, check it for the definition
6850 : * of the geometry operation.
6851 : * If OGR is built without the GEOS library, this function will always fail,
6852 : * issuing a CPLE_NotSupported error.
6853 : *
6854 : * @param dTolerance the distance tolerance for the simplification.
6855 : *
6856 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6857 : *
6858 : */
6859 :
6860 17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
6861 :
6862 : {
6863 : (void)dTolerance;
6864 : #ifndef HAVE_GEOS
6865 :
6866 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6867 : return nullptr;
6868 :
6869 : #else
6870 17 : OGRGeometry *poOGRProduct = nullptr;
6871 :
6872 17 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6873 17 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6874 17 : if (hThisGeosGeom != nullptr)
6875 : {
6876 17 : GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6877 : hGEOSCtxt, hThisGeosGeom, dTolerance);
6878 17 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6879 : poOGRProduct =
6880 17 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6881 : }
6882 17 : freeGEOSContext(hGEOSCtxt);
6883 17 : return poOGRProduct;
6884 :
6885 : #endif // HAVE_GEOS
6886 : }
6887 :
6888 : /************************************************************************/
6889 : /* OGR_G_SimplifyPreserveTopology() */
6890 : /************************************************************************/
6891 :
6892 : /**
6893 : * \brief Simplify the geometry while preserving topology.
6894 : *
6895 : * This function is the same as the C++ method
6896 : * OGRGeometry::SimplifyPreserveTopology().
6897 : *
6898 : * This function is built on the GEOS library, check it for the definition
6899 : * of the geometry operation.
6900 : * If OGR is built without the GEOS library, this function will always fail,
6901 : * issuing a CPLE_NotSupported error.
6902 : *
6903 : * @param hThis the geometry.
6904 : * @param dTolerance the distance tolerance for the simplification.
6905 : *
6906 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6907 : * or NULL if an error occurs.
6908 : *
6909 : */
6910 :
6911 1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6912 : double dTolerance)
6913 :
6914 : {
6915 1 : VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6916 1 : return OGRGeometry::ToHandle(
6917 1 : OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6918 : }
6919 :
6920 : /************************************************************************/
6921 : /* roundCoordinates() */
6922 : /************************************************************************/
6923 :
6924 : /** Round coordinates of the geometry to the specified precision.
6925 : *
6926 : * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6927 : * will return valid geometries, whereas roundCoordinates() does not make
6928 : * such guarantee and may return geometries with invalidities, if they are
6929 : * not compatible with the specified precision. roundCoordinates() supports
6930 : * curve geometries, whereas SetPrecision() does not currently.
6931 : *
6932 : * One use case for roundCoordinates() is to undo the effect of
6933 : * quantizeCoordinates().
6934 : *
6935 : * @param sPrecision Contains the precision requirements.
6936 : * @since GDAL 3.9
6937 : */
6938 39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6939 : {
6940 : struct Rounder : public OGRDefaultGeometryVisitor
6941 : {
6942 : const OGRGeomCoordinatePrecision &m_precision;
6943 : const double m_invXYResolution;
6944 : const double m_invZResolution;
6945 : const double m_invMResolution;
6946 :
6947 39 : explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6948 39 : : m_precision(sPrecisionIn),
6949 39 : m_invXYResolution(m_precision.dfXYResolution !=
6950 : OGRGeomCoordinatePrecision::UNKNOWN
6951 39 : ? 1.0 / m_precision.dfXYResolution
6952 : : 0.0),
6953 39 : m_invZResolution(m_precision.dfZResolution !=
6954 : OGRGeomCoordinatePrecision::UNKNOWN
6955 39 : ? 1.0 / m_precision.dfZResolution
6956 : : 0.0),
6957 39 : m_invMResolution(m_precision.dfMResolution !=
6958 : OGRGeomCoordinatePrecision::UNKNOWN
6959 39 : ? 1.0 / m_precision.dfMResolution
6960 117 : : 0.0)
6961 : {
6962 39 : }
6963 :
6964 : using OGRDefaultGeometryVisitor::visit;
6965 :
6966 379 : void visit(OGRPoint *poPoint) override
6967 : {
6968 379 : if (m_precision.dfXYResolution !=
6969 : OGRGeomCoordinatePrecision::UNKNOWN)
6970 : {
6971 379 : poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6972 379 : m_precision.dfXYResolution);
6973 379 : poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6974 379 : m_precision.dfXYResolution);
6975 : }
6976 758 : if (m_precision.dfZResolution !=
6977 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6978 4 : poPoint->Is3D())
6979 : {
6980 4 : poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6981 4 : m_precision.dfZResolution);
6982 : }
6983 758 : if (m_precision.dfMResolution !=
6984 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6985 4 : poPoint->IsMeasured())
6986 : {
6987 4 : poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6988 4 : m_precision.dfMResolution);
6989 : }
6990 379 : }
6991 : };
6992 :
6993 78 : Rounder rounder(sPrecision);
6994 39 : accept(&rounder);
6995 39 : }
6996 :
6997 : /************************************************************************/
6998 : /* SetPrecision() */
6999 : /************************************************************************/
7000 :
7001 : /** Set the geometry's precision, rounding all its coordinates to the precision
7002 : * grid, and making sure the geometry is still valid.
7003 : *
7004 : * This is a stronger version of roundCoordinates().
7005 : *
7006 : * Note that at time of writing GEOS does no supported curve geometries. So
7007 : * currently if this function is called on such a geometry, OGR will first call
7008 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7009 : * that it is unlikely to yield to the expected result.
7010 : *
7011 : * This function is the same as the C function OGR_G_SetPrecision().
7012 : *
7013 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7014 : * GEOS library. Check it for the definition of the geometry operation.
7015 : * If OGR is built without the GEOS library, this function will always fail,
7016 : * issuing a CPLE_NotSupported error.
7017 : *
7018 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7019 : * precision.
7020 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7021 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7022 : *
7023 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7024 : *
7025 : * @since GDAL 3.9
7026 : */
7027 :
7028 6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
7029 : {
7030 : (void)dfGridSize;
7031 : (void)nFlags;
7032 : #ifndef HAVE_GEOS
7033 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7034 : return nullptr;
7035 :
7036 : #else
7037 6 : OGRGeometry *poOGRProduct = nullptr;
7038 :
7039 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7040 6 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7041 6 : if (hThisGeosGeom != nullptr)
7042 : {
7043 6 : GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
7044 : hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
7045 6 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7046 : poOGRProduct =
7047 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7048 : }
7049 6 : freeGEOSContext(hGEOSCtxt);
7050 6 : return poOGRProduct;
7051 :
7052 : #endif // HAVE_GEOS
7053 : }
7054 :
7055 : /************************************************************************/
7056 : /* OGR_G_SetPrecision() */
7057 : /************************************************************************/
7058 :
7059 : /** Set the geometry's precision, rounding all its coordinates to the precision
7060 : * grid, and making sure the geometry is still valid.
7061 : *
7062 : * This is a stronger version of roundCoordinates().
7063 : *
7064 : * Note that at time of writing GEOS does no supported curve geometries. So
7065 : * currently if this function is called on such a geometry, OGR will first call
7066 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7067 : * that it is unlikely to yield to the expected result.
7068 : *
7069 : * This function is the same as the C++ method OGRGeometry::SetPrecision().
7070 : *
7071 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7072 : * GEOS library. Check it for the definition of the geometry operation.
7073 : * If OGR is built without the GEOS library, this function will always fail,
7074 : * issuing a CPLE_NotSupported error.
7075 : *
7076 : * @param hThis the geometry.
7077 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7078 : * precision.
7079 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7080 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7081 : *
7082 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7083 : * or NULL if an error occurs.
7084 : *
7085 : * @since GDAL 3.9
7086 : */
7087 1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
7088 : int nFlags)
7089 : {
7090 1 : VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
7091 1 : return OGRGeometry::ToHandle(
7092 1 : OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
7093 : }
7094 :
7095 : /************************************************************************/
7096 : /* DelaunayTriangulation() */
7097 : /************************************************************************/
7098 :
7099 : /**
7100 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7101 : *
7102 : * This function is the same as the C function OGR_G_DelaunayTriangulation().
7103 : *
7104 : * This function is built on the GEOS library, v3.4 or above.
7105 : * If OGR is built without the GEOS library, this function will always fail,
7106 : * issuing a CPLE_NotSupported error.
7107 : *
7108 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7109 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7110 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7111 : *
7112 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7113 : */
7114 :
7115 : #ifndef HAVE_GEOS
7116 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
7117 : int /*bOnlyEdges*/) const
7118 : {
7119 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7120 : return nullptr;
7121 : }
7122 : #else
7123 1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
7124 : int bOnlyEdges) const
7125 : {
7126 1 : OGRGeometry *poOGRProduct = nullptr;
7127 :
7128 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7129 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7130 1 : if (hThisGeosGeom != nullptr)
7131 : {
7132 1 : GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
7133 : hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
7134 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7135 : poOGRProduct =
7136 1 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7137 : }
7138 1 : freeGEOSContext(hGEOSCtxt);
7139 1 : return poOGRProduct;
7140 : }
7141 : #endif
7142 :
7143 : /************************************************************************/
7144 : /* OGR_G_DelaunayTriangulation() */
7145 : /************************************************************************/
7146 :
7147 : /**
7148 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7149 : *
7150 : * This function is the same as the C++ method
7151 : * OGRGeometry::DelaunayTriangulation().
7152 : *
7153 : * This function is built on the GEOS library, v3.4 or above.
7154 : * If OGR is built without the GEOS library, this function will always fail,
7155 : * issuing a CPLE_NotSupported error.
7156 : *
7157 : * @param hThis the geometry.
7158 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7159 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7160 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7161 : *
7162 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7163 : * or NULL if an error occurs.
7164 : */
7165 :
7166 1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
7167 : int bOnlyEdges)
7168 :
7169 : {
7170 1 : VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
7171 :
7172 1 : return OGRGeometry::ToHandle(
7173 : OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
7174 1 : bOnlyEdges));
7175 : }
7176 :
7177 : /************************************************************************/
7178 : /* ConstrainedDelaunayTriangulation() */
7179 : /************************************************************************/
7180 :
7181 : /**
7182 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7183 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7184 : * geometry collection.
7185 : *
7186 : * This function is the same as the C function
7187 : * OGR_G_ConstrainedDelaunayTriangulation().
7188 : *
7189 : * This function is built on the GEOS library, v3.10 or above.
7190 : * If OGR is built without the GEOS library, this function will always fail,
7191 : * issuing a CPLE_NotSupported error.
7192 : *
7193 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7194 : *
7195 : * @since OGR 3.12
7196 : */
7197 :
7198 3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
7199 : {
7200 : #ifndef HAVE_GEOS
7201 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7202 : return nullptr;
7203 : #elif !(GEOS_VERSION_MAJOR > 3 || \
7204 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
7205 : CPLError(
7206 : CE_Failure, CPLE_NotSupported,
7207 : "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
7208 : return nullptr;
7209 : #else
7210 :
7211 3 : OGRGeometry *poOGRProduct = nullptr;
7212 :
7213 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7214 3 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7215 3 : if (hThisGeosGeom != nullptr)
7216 : {
7217 : GEOSGeom hGeosProduct =
7218 3 : GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
7219 3 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7220 : poOGRProduct =
7221 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7222 : }
7223 3 : freeGEOSContext(hGEOSCtxt);
7224 3 : return poOGRProduct;
7225 : #endif
7226 : }
7227 :
7228 : /************************************************************************/
7229 : /* OGR_G_ConstrainedDelaunayTriangulation() */
7230 : /************************************************************************/
7231 :
7232 : /**
7233 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7234 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7235 : * geometry collection.
7236 : *
7237 : * This function is the same as the C++ method
7238 : * OGRGeometry::ConstrainedDelaunayTriangulation().
7239 : *
7240 : * This function is built on the GEOS library, v3.10 or above.
7241 : * If OGR is built without the GEOS library, this function will always fail,
7242 : * issuing a CPLE_NotSupported error.
7243 : *
7244 : * @param hThis the geometry.
7245 : *
7246 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7247 : * or NULL if an error occurs.
7248 : *
7249 : * @since OGR 3.12
7250 : */
7251 :
7252 3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
7253 : {
7254 3 : VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
7255 :
7256 3 : return OGRGeometry::ToHandle(
7257 3 : OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
7258 : }
7259 :
7260 : /************************************************************************/
7261 : /* Polygonize() */
7262 : /************************************************************************/
7263 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
7264 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
7265 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
7266 : /* AMBIENTALE */
7267 : /************************************************************************/
7268 :
7269 : /**
7270 : * \brief Polygonizes a set of sparse edges.
7271 : *
7272 : * A new geometry object is created and returned containing a collection
7273 : * of reassembled Polygons: NULL will be returned if the input collection
7274 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7275 : * into Polygons is impossible due to topological inconsistencies.
7276 : *
7277 : * This method is the same as the C function OGR_G_Polygonize().
7278 : *
7279 : * This method is built on the GEOS library, check it for the definition
7280 : * of the geometry operation.
7281 : * If OGR is built without the GEOS library, this method will always fail,
7282 : * issuing a CPLE_NotSupported error.
7283 : *
7284 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7285 : *
7286 : */
7287 :
7288 117 : OGRGeometry *OGRGeometry::Polygonize() const
7289 :
7290 : {
7291 : #ifndef HAVE_GEOS
7292 :
7293 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7294 : return nullptr;
7295 :
7296 : #else
7297 :
7298 117 : const OGRGeometryCollection *poColl = nullptr;
7299 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
7300 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
7301 116 : poColl = toGeometryCollection();
7302 : else
7303 1 : return nullptr;
7304 :
7305 116 : const int nCount = poColl->getNumGeometries();
7306 :
7307 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
7308 116 : bool bError = false;
7309 :
7310 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7311 :
7312 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
7313 747 : for (int ig = 0; ig < nCount; ig++)
7314 : {
7315 631 : GEOSGeom hGeosGeom = nullptr;
7316 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
7317 1262 : if (poChild == nullptr ||
7318 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
7319 1 : bError = true;
7320 : else
7321 : {
7322 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
7323 630 : if (hGeosGeom == nullptr)
7324 0 : bError = true;
7325 : }
7326 631 : pahGeosGeomList[ig] = hGeosGeom;
7327 : }
7328 :
7329 116 : if (!bError)
7330 : {
7331 : GEOSGeom hGeosPolygs =
7332 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
7333 :
7334 : poPolygsOGRGeom =
7335 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7336 : }
7337 :
7338 747 : for (int ig = 0; ig < nCount; ig++)
7339 : {
7340 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
7341 631 : if (hGeosGeom != nullptr)
7342 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
7343 : }
7344 116 : delete[] pahGeosGeomList;
7345 116 : freeGEOSContext(hGEOSCtxt);
7346 :
7347 116 : return poPolygsOGRGeom;
7348 :
7349 : #endif // HAVE_GEOS
7350 : }
7351 :
7352 : /************************************************************************/
7353 : /* OGR_G_Polygonize() */
7354 : /************************************************************************/
7355 : /**
7356 : * \brief Polygonizes a set of sparse edges.
7357 : *
7358 : * A new geometry object is created and returned containing a collection
7359 : * of reassembled Polygons: NULL will be returned if the input collection
7360 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7361 : * into Polygons is impossible due to topological inconsistencies.
7362 : *
7363 : * This function is the same as the C++ method OGRGeometry::Polygonize().
7364 : *
7365 : * This function is built on the GEOS library, check it for the definition
7366 : * of the geometry operation.
7367 : * If OGR is built without the GEOS library, this function will always fail,
7368 : * issuing a CPLE_NotSupported error.
7369 : *
7370 : * @param hTarget The Geometry to be polygonized.
7371 : *
7372 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7373 : * or NULL if an error occurs.
7374 : *
7375 : */
7376 :
7377 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
7378 :
7379 : {
7380 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
7381 :
7382 3 : return OGRGeometry::ToHandle(
7383 3 : OGRGeometry::FromHandle(hTarget)->Polygonize());
7384 : }
7385 :
7386 : /************************************************************************/
7387 : /* BuildArea() */
7388 : /************************************************************************/
7389 :
7390 : /**
7391 : * \brief Polygonize a linework assuming inner polygons are holes.
7392 : *
7393 : * This method is the same as the C function OGR_G_BuildArea().
7394 : *
7395 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
7396 : * Additionally, holes are dropped and the result is unified producing
7397 : * a single Polygon or a MultiPolygon.
7398 : *
7399 : * A new geometry object is created and returned: NULL on failure,
7400 : * empty GeometryCollection if the input geometry cannot be polygonized,
7401 : * Polygon or MultiPolygon on success.
7402 : *
7403 : * This method is built on the GEOSBuildArea_r() function of the GEOS
7404 : * library, check it for the definition of the geometry operation.
7405 : * If OGR is built without the GEOS library, this method will always fail,
7406 : * issuing a CPLE_NotSupported error.
7407 : *
7408 : * @return a newly allocated geometry now owned by the caller,
7409 : * or NULL on failure.
7410 : *
7411 : * @since OGR 3.11
7412 : */
7413 :
7414 30 : OGRGeometry *OGRGeometry::BuildArea() const
7415 :
7416 : {
7417 : #ifndef HAVE_GEOS
7418 :
7419 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7420 : return nullptr;
7421 :
7422 : #else
7423 :
7424 30 : OGRGeometry *poPolygsOGRGeom = nullptr;
7425 :
7426 30 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7427 30 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7428 30 : if (hThisGeosGeom != nullptr)
7429 : {
7430 30 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7431 : poPolygsOGRGeom =
7432 30 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7433 30 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7434 : }
7435 30 : freeGEOSContext(hGEOSCtxt);
7436 :
7437 30 : return poPolygsOGRGeom;
7438 :
7439 : #endif // HAVE_GEOS
7440 : }
7441 :
7442 : /************************************************************************/
7443 : /* OGR_G_BuildArea() */
7444 : /************************************************************************/
7445 :
7446 : /**
7447 : * \brief Polygonize a linework assuming inner polygons are holes.
7448 : *
7449 : * This function is the same as the C++ method OGRGeometry::BuildArea().
7450 : *
7451 : * Polygonization is performed similarly to OGR_G_Polygonize().
7452 : * Additionally, holes are dropped and the result is unified producing
7453 : * a single Polygon or a MultiPolygon.
7454 : *
7455 : * A new geometry object is created and returned: NULL on failure,
7456 : * empty GeometryCollection if the input geometry cannot be polygonized,
7457 : * Polygon or MultiPolygon on success.
7458 : *
7459 : * This function is built on the GEOSBuildArea_r() function of the GEOS
7460 : * library, check it for the definition of the geometry operation.
7461 : * If OGR is built without the GEOS library, this function will always fail,
7462 : * issuing a CPLE_NotSupported error.
7463 : *
7464 : * @param hGeom handle on the geometry to polygonize.
7465 : *
7466 : * @return a handle on newly allocated geometry now owned by the caller,
7467 : * or NULL on failure.
7468 : *
7469 : * @since OGR 3.11
7470 : */
7471 :
7472 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7473 :
7474 : {
7475 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7476 :
7477 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7478 : }
7479 :
7480 : /************************************************************************/
7481 : /* swapXY() */
7482 : /************************************************************************/
7483 :
7484 : /**
7485 : * \brief Swap x and y coordinates.
7486 : *
7487 : */
7488 :
7489 0 : void OGRGeometry::swapXY()
7490 :
7491 : {
7492 0 : }
7493 :
7494 : /************************************************************************/
7495 : /* swapXY() */
7496 : /************************************************************************/
7497 :
7498 : /**
7499 : * \brief Swap x and y coordinates.
7500 : *
7501 : * @param hGeom geometry.
7502 : */
7503 :
7504 32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7505 : {
7506 32 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7507 :
7508 32 : OGRGeometry::FromHandle(hGeom)->swapXY();
7509 : }
7510 :
7511 : /************************************************************************/
7512 : /* Prepared geometry API */
7513 : /************************************************************************/
7514 :
7515 : #if defined(HAVE_GEOS)
7516 : struct _OGRPreparedGeometry
7517 : {
7518 : GEOSContextHandle_t hGEOSCtxt;
7519 : GEOSGeom hGEOSGeom;
7520 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7521 : };
7522 : #endif
7523 :
7524 : /************************************************************************/
7525 : /* OGRHasPreparedGeometrySupport() */
7526 : /************************************************************************/
7527 :
7528 : /** Returns if GEOS has prepared geometry support.
7529 : * @return TRUE or FALSE
7530 : */
7531 1 : int OGRHasPreparedGeometrySupport()
7532 : {
7533 : #if defined(HAVE_GEOS)
7534 1 : return TRUE;
7535 : #else
7536 : return FALSE;
7537 : #endif
7538 : }
7539 :
7540 : /************************************************************************/
7541 : /* OGRCreatePreparedGeometry() */
7542 : /************************************************************************/
7543 :
7544 : /** Creates a prepared geometry.
7545 : *
7546 : * To free with OGRDestroyPreparedGeometry()
7547 : *
7548 : * @param hGeom input geometry to prepare.
7549 : * @return handle to a prepared geometry.
7550 : * @since GDAL 3.3
7551 : */
7552 53089 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
7553 : {
7554 : (void)hGeom;
7555 : #if defined(HAVE_GEOS)
7556 53089 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7557 53089 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7558 53089 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7559 53089 : if (hGEOSGeom == nullptr)
7560 : {
7561 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7562 0 : return nullptr;
7563 : }
7564 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7565 53089 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7566 53089 : if (poPreparedGEOSGeom == nullptr)
7567 : {
7568 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7569 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7570 0 : return nullptr;
7571 : }
7572 :
7573 53089 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7574 53089 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7575 53089 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7576 53089 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7577 :
7578 53089 : return poPreparedGeom;
7579 : #else
7580 : return nullptr;
7581 : #endif
7582 : }
7583 :
7584 : /************************************************************************/
7585 : /* OGRDestroyPreparedGeometry() */
7586 : /************************************************************************/
7587 :
7588 : /** Destroys a prepared geometry.
7589 : * @param hPreparedGeom prepared geometry.
7590 : * @since GDAL 3.3
7591 : */
7592 53135 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
7593 : {
7594 : (void)hPreparedGeom;
7595 : #if defined(HAVE_GEOS)
7596 53135 : if (hPreparedGeom != nullptr)
7597 : {
7598 53089 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7599 : hPreparedGeom->poPreparedGEOSGeom);
7600 53089 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7601 53089 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7602 53089 : delete hPreparedGeom;
7603 : }
7604 : #endif
7605 53135 : }
7606 :
7607 : /************************************************************************/
7608 : /* OGRPreparedGeometryIntersects() */
7609 : /************************************************************************/
7610 :
7611 : /** Returns whether a prepared geometry intersects with a geometry.
7612 : * @param hPreparedGeom prepared geometry.
7613 : * @param hOtherGeom other geometry.
7614 : * @return TRUE or FALSE.
7615 : * @since GDAL 3.3
7616 : */
7617 5626 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
7618 : const OGRGeometryH hOtherGeom)
7619 : {
7620 : (void)hPreparedGeom;
7621 : (void)hOtherGeom;
7622 : #if defined(HAVE_GEOS)
7623 5626 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7624 5626 : if (hPreparedGeom == nullptr ||
7625 : poOtherGeom == nullptr
7626 : // The check for IsEmpty() is for buggy GEOS versions.
7627 : // See https://github.com/libgeos/geos/pull/423
7628 11252 : || poOtherGeom->IsEmpty())
7629 : {
7630 1 : return FALSE;
7631 : }
7632 :
7633 : #if GEOS_VERSION_MAJOR > 3 || \
7634 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
7635 5625 : if (wkbFlatten(OGR_G_GetGeometryType(hOtherGeom)) == wkbPoint)
7636 : {
7637 560 : const OGRPoint *poPoint = cpl::down_cast<const OGRPoint *>(
7638 : OGRGeometry::FromHandle(hOtherGeom));
7639 560 : return 1 ==
7640 560 : GEOSPreparedIntersectsXY_r(hPreparedGeom->hGEOSCtxt,
7641 : hPreparedGeom->poPreparedGEOSGeom,
7642 560 : poPoint->getX(), poPoint->getY());
7643 : }
7644 : #endif
7645 : GEOSGeom hGEOSOtherGeom =
7646 5065 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7647 5065 : if (hGEOSOtherGeom == nullptr)
7648 0 : return FALSE;
7649 :
7650 : const bool bRet =
7651 5065 : GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
7652 : hPreparedGeom->poPreparedGEOSGeom,
7653 5065 : hGEOSOtherGeom) == 1;
7654 5065 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7655 :
7656 5065 : return bRet;
7657 : #else
7658 : return FALSE;
7659 : #endif
7660 : }
7661 :
7662 : /** Returns whether a prepared geometry contains a geometry.
7663 : * @param hPreparedGeom prepared geometry.
7664 : * @param hOtherGeom other geometry.
7665 : * @return TRUE or FALSE.
7666 : */
7667 120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
7668 : const OGRGeometryH hOtherGeom)
7669 : {
7670 : (void)hPreparedGeom;
7671 : (void)hOtherGeom;
7672 : #if defined(HAVE_GEOS)
7673 120516 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7674 120516 : if (hPreparedGeom == nullptr ||
7675 : poOtherGeom == nullptr
7676 : // The check for IsEmpty() is for buggy GEOS versions.
7677 : // See https://github.com/libgeos/geos/pull/423
7678 241032 : || poOtherGeom->IsEmpty())
7679 : {
7680 1 : return FALSE;
7681 : }
7682 :
7683 : #if GEOS_VERSION_MAJOR > 3 || \
7684 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
7685 120515 : if (wkbFlatten(OGR_G_GetGeometryType(hOtherGeom)) == wkbPoint)
7686 : {
7687 120515 : const OGRPoint *poPoint = cpl::down_cast<const OGRPoint *>(
7688 : OGRGeometry::FromHandle(hOtherGeom));
7689 120515 : return 1 == GEOSPreparedContainsXY_r(hPreparedGeom->hGEOSCtxt,
7690 : hPreparedGeom->poPreparedGEOSGeom,
7691 120515 : poPoint->getX(), poPoint->getY());
7692 : }
7693 : #endif
7694 : GEOSGeom hGEOSOtherGeom =
7695 0 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7696 0 : if (hGEOSOtherGeom == nullptr)
7697 0 : return FALSE;
7698 :
7699 0 : const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
7700 : hPreparedGeom->poPreparedGEOSGeom,
7701 0 : hGEOSOtherGeom) == 1;
7702 0 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7703 :
7704 0 : return bRet;
7705 : #else
7706 : return FALSE;
7707 : #endif
7708 : }
7709 :
7710 : /************************************************************************/
7711 : /* OGRGeometryFromEWKB() */
7712 : /************************************************************************/
7713 :
7714 1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7715 : int bIsPostGIS1_EWKB)
7716 :
7717 : {
7718 1445 : OGRGeometry *poGeometry = nullptr;
7719 :
7720 1445 : size_t nWKBSize = 0;
7721 1445 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7722 1445 : if (pabyWKB == nullptr)
7723 0 : return nullptr;
7724 :
7725 : /* -------------------------------------------------------------------- */
7726 : /* Try to ingest the geometry. */
7727 : /* -------------------------------------------------------------------- */
7728 1445 : (void)OGRGeometryFactory::createFromWkb(
7729 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7730 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7731 :
7732 1445 : return poGeometry;
7733 : }
7734 :
7735 : /************************************************************************/
7736 : /* OGRGeometryFromHexEWKB() */
7737 : /************************************************************************/
7738 :
7739 1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7740 : int bIsPostGIS1_EWKB)
7741 :
7742 : {
7743 1443 : if (pszBytea == nullptr)
7744 0 : return nullptr;
7745 :
7746 1443 : int nWKBLength = 0;
7747 1443 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7748 :
7749 : OGRGeometry *poGeometry =
7750 1443 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7751 :
7752 1443 : CPLFree(pabyWKB);
7753 :
7754 1443 : return poGeometry;
7755 : }
7756 :
7757 : /************************************************************************/
7758 : /* OGRGeometryToHexEWKB() */
7759 : /************************************************************************/
7760 :
7761 1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
7762 : int nPostGISMajor, int nPostGISMinor)
7763 : {
7764 1071 : const size_t nWkbSize = poGeometry->WkbSize();
7765 1071 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7766 1071 : if (pabyWKB == nullptr)
7767 0 : return CPLStrdup("");
7768 :
7769 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7770 1815 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7771 626 : poGeometry->IsEmpty())
7772 : {
7773 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7774 : OGRERR_NONE)
7775 : {
7776 0 : CPLFree(pabyWKB);
7777 0 : return CPLStrdup("");
7778 : }
7779 : }
7780 1069 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7781 : (nPostGISMajor < 2)
7782 : ? wkbVariantPostGIS1
7783 1069 : : wkbVariantOldOgc) != OGRERR_NONE)
7784 : {
7785 0 : CPLFree(pabyWKB);
7786 0 : return CPLStrdup("");
7787 : }
7788 :
7789 : // When converting to hex, each byte takes 2 hex characters. In addition
7790 : // we add in 8 characters to represent the SRID integer in hex, and
7791 : // one for a null terminator.
7792 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7793 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7794 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7795 1071 : if (nWkbSize >
7796 1071 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7797 : {
7798 0 : CPLFree(pabyWKB);
7799 0 : return CPLStrdup("");
7800 : }
7801 1071 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7802 1071 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7803 1071 : if (pszTextBuf == nullptr)
7804 : {
7805 0 : CPLFree(pabyWKB);
7806 0 : return CPLStrdup("");
7807 : }
7808 1071 : char *pszTextBufCurrent = pszTextBuf;
7809 :
7810 : // Convert the 1st byte, which is the endianness flag, to hex.
7811 1071 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7812 1071 : strcpy(pszTextBufCurrent, pszHex);
7813 1071 : CPLFree(pszHex);
7814 1071 : pszTextBufCurrent += 2;
7815 :
7816 : // Next, get the geom type which is bytes 2 through 5.
7817 : GUInt32 geomType;
7818 1071 : memcpy(&geomType, pabyWKB + 1, 4);
7819 :
7820 : // Now add the SRID flag if an SRID is provided.
7821 1071 : if (nSRSId > 0)
7822 : {
7823 : // Change the flag to wkbNDR (little) endianness.
7824 541 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7825 541 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7826 : // Apply the flag.
7827 541 : geomType = geomType | nGSrsFlag;
7828 : }
7829 :
7830 : // Now write the geom type which is 4 bytes.
7831 1071 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7832 1071 : strcpy(pszTextBufCurrent, pszHex);
7833 1071 : CPLFree(pszHex);
7834 1071 : pszTextBufCurrent += 8;
7835 :
7836 : // Now include SRID if provided.
7837 1071 : if (nSRSId > 0)
7838 : {
7839 : // Force the srsid to wkbNDR (little) endianness.
7840 541 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7841 541 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7842 : reinterpret_cast<const GByte *>(&nGSRSId));
7843 541 : strcpy(pszTextBufCurrent, pszHex);
7844 541 : CPLFree(pszHex);
7845 541 : pszTextBufCurrent += 8;
7846 : }
7847 :
7848 : // Copy the rest of the data over - subtract
7849 : // 5 since we already copied 5 bytes above.
7850 1071 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7851 1071 : CPLFree(pabyWKB);
7852 1071 : if (!pszHex || pszHex[0] == 0)
7853 : {
7854 0 : CPLFree(pszTextBuf);
7855 0 : return pszHex;
7856 : }
7857 1071 : strcpy(pszTextBufCurrent, pszHex);
7858 1071 : CPLFree(pszHex);
7859 :
7860 1071 : return pszTextBuf;
7861 : }
7862 :
7863 : /************************************************************************/
7864 : /* importPreambleFromWkb() */
7865 : /************************************************************************/
7866 :
7867 : //! @cond Doxygen_Suppress
7868 160217 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7869 : size_t nSize,
7870 : OGRwkbByteOrder &eByteOrder,
7871 : OGRwkbVariant eWkbVariant)
7872 : {
7873 160217 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7874 0 : return OGRERR_NOT_ENOUGH_DATA;
7875 :
7876 : /* -------------------------------------------------------------------- */
7877 : /* Get the byte order byte. */
7878 : /* -------------------------------------------------------------------- */
7879 160217 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7880 160217 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7881 0 : return OGRERR_CORRUPT_DATA;
7882 160217 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7883 :
7884 : /* -------------------------------------------------------------------- */
7885 : /* Get the geometry feature type. */
7886 : /* -------------------------------------------------------------------- */
7887 : OGRwkbGeometryType eGeometryType;
7888 : const OGRErr err =
7889 160217 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7890 160217 : if (wkbHasZ(eGeometryType))
7891 62309 : flags |= OGR_G_3D;
7892 160217 : if (wkbHasM(eGeometryType))
7893 59692 : flags |= OGR_G_MEASURED;
7894 :
7895 160217 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7896 0 : return OGRERR_CORRUPT_DATA;
7897 :
7898 160217 : return OGRERR_NONE;
7899 : }
7900 :
7901 : /************************************************************************/
7902 : /* importPreambleOfCollectionFromWkb() */
7903 : /* */
7904 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7905 : /* OGRCurvePolygon and OGRGeometryCollection. */
7906 : /************************************************************************/
7907 :
7908 76832 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7909 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7910 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7911 : OGRwkbVariant eWkbVariant)
7912 : {
7913 76832 : nGeomCount = 0;
7914 :
7915 : OGRErr eErr =
7916 76832 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7917 76832 : if (eErr != OGRERR_NONE)
7918 0 : return eErr;
7919 :
7920 : /* -------------------------------------------------------------------- */
7921 : /* Clear existing Geoms. */
7922 : /* -------------------------------------------------------------------- */
7923 76832 : int _flags = flags; // flags set in importPreambleFromWkb
7924 76832 : empty(); // may reset flags etc.
7925 :
7926 : // restore
7927 76832 : if (_flags & OGR_G_3D)
7928 59265 : set3D(TRUE);
7929 76832 : if (_flags & OGR_G_MEASURED)
7930 56768 : setMeasured(TRUE);
7931 :
7932 : /* -------------------------------------------------------------------- */
7933 : /* Get the sub-geometry count. */
7934 : /* -------------------------------------------------------------------- */
7935 76832 : memcpy(&nGeomCount, pabyData + 5, 4);
7936 :
7937 76832 : if (OGR_SWAP(eByteOrder))
7938 386 : nGeomCount = CPL_SWAP32(nGeomCount);
7939 :
7940 153530 : if (nGeomCount < 0 ||
7941 76698 : static_cast<size_t>(nGeomCount) >
7942 76698 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7943 : {
7944 134 : nGeomCount = 0;
7945 134 : return OGRERR_CORRUPT_DATA;
7946 : }
7947 76698 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7948 :
7949 : // Each ring has a minimum of nMinSubGeomSize bytes.
7950 76698 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7951 : {
7952 910 : CPLError(CE_Failure, CPLE_AppDefined,
7953 : "Length of input WKB is too small");
7954 910 : nGeomCount = 0;
7955 910 : return OGRERR_NOT_ENOUGH_DATA;
7956 : }
7957 :
7958 75788 : nDataOffset = 9;
7959 75788 : if (nSize != static_cast<size_t>(-1))
7960 : {
7961 75768 : CPLAssert(nSize >= nDataOffset);
7962 75768 : nSize -= nDataOffset;
7963 : }
7964 :
7965 75788 : return OGRERR_NONE;
7966 : }
7967 :
7968 : /************************************************************************/
7969 : /* importCurveCollectionFromWkt() */
7970 : /* */
7971 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7972 : /* OGRMultiCurve. */
7973 : /************************************************************************/
7974 :
7975 1464 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7976 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7977 : int bAllowCurve, int bAllowCompoundCurve,
7978 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7979 :
7980 : {
7981 1464 : int bHasZ = FALSE;
7982 1464 : int bHasM = FALSE;
7983 1464 : bool bIsEmpty = false;
7984 1464 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7985 1464 : flags = 0;
7986 1464 : if (eErr != OGRERR_NONE)
7987 14 : return eErr;
7988 1450 : if (bHasZ)
7989 206 : flags |= OGR_G_3D;
7990 1450 : if (bHasM)
7991 132 : flags |= OGR_G_MEASURED;
7992 1450 : if (bIsEmpty)
7993 111 : return OGRERR_NONE;
7994 :
7995 : char szToken[OGR_WKT_TOKEN_MAX];
7996 1339 : const char *pszInput = *ppszInput;
7997 1339 : eErr = OGRERR_NONE;
7998 :
7999 : // Skip first '('.
8000 1339 : pszInput = OGRWktReadToken(pszInput, szToken);
8001 :
8002 : /* ==================================================================== */
8003 : /* Read each curve in turn. Note that we try to reuse the same */
8004 : /* point list buffer from curve to curve to cut down on */
8005 : /* allocate/deallocate overhead. */
8006 : /* ==================================================================== */
8007 1339 : OGRRawPoint *paoPoints = nullptr;
8008 1339 : int nMaxPoints = 0;
8009 1339 : double *padfZ = nullptr;
8010 :
8011 656 : do
8012 : {
8013 :
8014 : /* --------------------------------------------------------------------
8015 : */
8016 : /* Get the first token, which should be the geometry type. */
8017 : /* --------------------------------------------------------------------
8018 : */
8019 1995 : const char *pszInputBefore = pszInput;
8020 1995 : pszInput = OGRWktReadToken(pszInput, szToken);
8021 :
8022 : /* --------------------------------------------------------------------
8023 : */
8024 : /* Do the import. */
8025 : /* --------------------------------------------------------------------
8026 : */
8027 1995 : OGRCurve *poCurve = nullptr;
8028 1995 : if (EQUAL(szToken, "("))
8029 : {
8030 1441 : OGRLineString *poLine = new OGRLineString();
8031 1441 : poCurve = poLine;
8032 1441 : pszInput = pszInputBefore;
8033 1441 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
8034 : paoPoints, nMaxPoints, padfZ);
8035 : }
8036 554 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
8037 : {
8038 16 : poCurve = new OGRLineString();
8039 : }
8040 : // Accept LINESTRING(), but this is an extension to the BNF, also
8041 : // accepted by PostGIS.
8042 538 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
8043 523 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
8044 523 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
8045 1235 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
8046 159 : (bAllowCompoundCurve &&
8047 159 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
8048 : {
8049 500 : OGRGeometry *poGeom = nullptr;
8050 500 : pszInput = pszInputBefore;
8051 : eErr =
8052 500 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
8053 500 : if (poGeom == nullptr)
8054 : {
8055 1 : eErr = OGRERR_CORRUPT_DATA;
8056 : }
8057 : else
8058 : {
8059 499 : poCurve = poGeom->toCurve();
8060 : }
8061 : }
8062 : else
8063 : {
8064 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
8065 : szToken);
8066 38 : eErr = OGRERR_CORRUPT_DATA;
8067 : }
8068 :
8069 : // If this has M it is an error if poGeom does not have M.
8070 1995 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
8071 0 : eErr = OGRERR_CORRUPT_DATA;
8072 :
8073 1995 : if (eErr == OGRERR_NONE)
8074 1950 : eErr = pfnAddCurveDirectly(this, poCurve);
8075 1995 : if (eErr != OGRERR_NONE)
8076 : {
8077 55 : delete poCurve;
8078 55 : break;
8079 : }
8080 :
8081 : /* --------------------------------------------------------------------
8082 : */
8083 : /* Read the delimiter following the surface. */
8084 : /* --------------------------------------------------------------------
8085 : */
8086 1940 : pszInput = OGRWktReadToken(pszInput, szToken);
8087 1940 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
8088 :
8089 1339 : CPLFree(paoPoints);
8090 1339 : CPLFree(padfZ);
8091 :
8092 : /* -------------------------------------------------------------------- */
8093 : /* freak if we don't get a closing bracket. */
8094 : /* -------------------------------------------------------------------- */
8095 :
8096 1339 : if (eErr != OGRERR_NONE)
8097 55 : return eErr;
8098 :
8099 1284 : if (szToken[0] != ')')
8100 9 : return OGRERR_CORRUPT_DATA;
8101 :
8102 1275 : *ppszInput = pszInput;
8103 1275 : return OGRERR_NONE;
8104 : }
8105 :
8106 : //! @endcond
8107 :
8108 : /************************************************************************/
8109 : /* OGR_GT_Flatten() */
8110 : /************************************************************************/
8111 : /**
8112 : * \brief Returns the 2D geometry type corresponding to the passed geometry
8113 : * type.
8114 : *
8115 : * This function is intended to work with geometry types as old-style 99-402
8116 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
8117 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
8118 : *
8119 : * @param eType Input geometry type
8120 : *
8121 : * @return 2D geometry type corresponding to the passed geometry type.
8122 : *
8123 : */
8124 :
8125 7920920 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
8126 : {
8127 7920920 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
8128 7920920 : if (eType >= 1000 && eType < 2000) // ISO Z.
8129 2776960 : return static_cast<OGRwkbGeometryType>(eType - 1000);
8130 5143960 : if (eType >= 2000 && eType < 3000) // ISO M.
8131 6121 : return static_cast<OGRwkbGeometryType>(eType - 2000);
8132 5137840 : if (eType >= 3000 && eType < 4000) // ISO ZM.
8133 136120 : return static_cast<OGRwkbGeometryType>(eType - 3000);
8134 5001720 : return eType;
8135 : }
8136 :
8137 : /************************************************************************/
8138 : /* OGR_GT_HasZ() */
8139 : /************************************************************************/
8140 : /**
8141 : * \brief Return if the geometry type is a 3D geometry type.
8142 : *
8143 : * @param eType Input geometry type
8144 : *
8145 : * @return TRUE if the geometry type is a 3D geometry type.
8146 : *
8147 : */
8148 :
8149 2094530 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
8150 : {
8151 2094530 : if (eType & wkb25DBitInternalUse)
8152 157221 : return TRUE;
8153 1937310 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
8154 264 : return TRUE;
8155 1937040 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8156 121359 : return TRUE;
8157 1815680 : return FALSE;
8158 : }
8159 :
8160 : /************************************************************************/
8161 : /* OGR_GT_HasM() */
8162 : /************************************************************************/
8163 : /**
8164 : * \brief Return if the geometry type is a measured type.
8165 : *
8166 : * @param eType Input geometry type
8167 : *
8168 : * @return TRUE if the geometry type is a measured type.
8169 : *
8170 : */
8171 :
8172 2155850 : int OGR_GT_HasM(OGRwkbGeometryType eType)
8173 : {
8174 2155850 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
8175 2600 : return TRUE;
8176 2153250 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8177 121015 : return TRUE;
8178 2032230 : return FALSE;
8179 : }
8180 :
8181 : /************************************************************************/
8182 : /* OGR_GT_SetZ() */
8183 : /************************************************************************/
8184 : /**
8185 : * \brief Returns the 3D geometry type corresponding to the passed geometry
8186 : * type.
8187 : *
8188 : * @param eType Input geometry type
8189 : *
8190 : * @return 3D geometry type corresponding to the passed geometry type.
8191 : *
8192 : */
8193 :
8194 5669 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
8195 : {
8196 5669 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
8197 497 : return eType;
8198 5172 : if (eType <= wkbGeometryCollection)
8199 5070 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
8200 : else
8201 102 : return static_cast<OGRwkbGeometryType>(eType + 1000);
8202 : }
8203 :
8204 : /************************************************************************/
8205 : /* OGR_GT_SetM() */
8206 : /************************************************************************/
8207 : /**
8208 : * \brief Returns the measured geometry type corresponding to the passed
8209 : * geometry type.
8210 : *
8211 : * @param eType Input geometry type
8212 : *
8213 : * @return measured geometry type corresponding to the passed geometry type.
8214 : *
8215 : */
8216 :
8217 2023 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
8218 : {
8219 2023 : if (OGR_GT_HasM(eType) || eType == wkbNone)
8220 262 : return eType;
8221 1761 : if (eType & wkb25DBitInternalUse)
8222 : {
8223 720 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
8224 720 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
8225 : }
8226 1761 : return static_cast<OGRwkbGeometryType>(eType + 2000);
8227 : }
8228 :
8229 : /************************************************************************/
8230 : /* OGR_GT_SetModifier() */
8231 : /************************************************************************/
8232 : /**
8233 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
8234 : *
8235 : * @param eType Input geometry type
8236 : * @param bHasZ TRUE if the output geometry type must be 3D.
8237 : * @param bHasM TRUE if the output geometry type must be measured.
8238 : *
8239 : * @return Output geometry type.
8240 : *
8241 : */
8242 :
8243 5403 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
8244 : int bHasM)
8245 : {
8246 5403 : if (bHasZ && bHasM)
8247 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
8248 5061 : else if (bHasM)
8249 334 : return OGR_GT_SetM(wkbFlatten(eType));
8250 4727 : else if (bHasZ)
8251 1927 : return OGR_GT_SetZ(wkbFlatten(eType));
8252 : else
8253 2800 : return wkbFlatten(eType);
8254 : }
8255 :
8256 : /************************************************************************/
8257 : /* OGR_GT_IsSubClassOf) */
8258 : /************************************************************************/
8259 : /**
8260 : * \brief Returns if a type is a subclass of another one
8261 : *
8262 : * @param eType Type.
8263 : * @param eSuperType Super type
8264 : *
8265 : * @return TRUE if eType is a subclass of eSuperType.
8266 : *
8267 : */
8268 :
8269 139122 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
8270 : {
8271 139122 : eSuperType = wkbFlatten(eSuperType);
8272 139122 : eType = wkbFlatten(eType);
8273 :
8274 139122 : if (eSuperType == eType || eSuperType == wkbUnknown)
8275 21486 : return TRUE;
8276 :
8277 117636 : if (eSuperType == wkbGeometryCollection)
8278 34361 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
8279 70767 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
8280 36406 : eType == wkbMultiSurface;
8281 :
8282 81230 : if (eSuperType == wkbCurvePolygon)
8283 22258 : return eType == wkbPolygon || eType == wkbTriangle;
8284 :
8285 58972 : if (eSuperType == wkbMultiCurve)
8286 249 : return eType == wkbMultiLineString;
8287 :
8288 58723 : if (eSuperType == wkbMultiSurface)
8289 288 : return eType == wkbMultiPolygon;
8290 :
8291 58435 : if (eSuperType == wkbCurve)
8292 23333 : return eType == wkbLineString || eType == wkbCircularString ||
8293 23333 : eType == wkbCompoundCurve;
8294 :
8295 35102 : if (eSuperType == wkbSurface)
8296 3553 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
8297 7252 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
8298 3699 : eType == wkbTIN;
8299 :
8300 31403 : if (eSuperType == wkbPolygon)
8301 221 : return eType == wkbTriangle;
8302 :
8303 31182 : if (eSuperType == wkbPolyhedralSurface)
8304 16071 : return eType == wkbTIN;
8305 :
8306 15111 : return FALSE;
8307 : }
8308 :
8309 : /************************************************************************/
8310 : /* OGR_GT_GetCollection() */
8311 : /************************************************************************/
8312 : /**
8313 : * \brief Returns the collection type that can contain the passed geometry type
8314 : *
8315 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
8316 : * wkbLineString->wkbMultiLineString,
8317 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
8318 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
8319 : * wkbCurvePolygon->wkbMultiSurface.
8320 : * In other cases, wkbUnknown is returned
8321 : *
8322 : * Passed Z, M, ZM flag is preserved.
8323 : *
8324 : *
8325 : * @param eType Input geometry type
8326 : *
8327 : * @return the collection type that can contain the passed geometry type or
8328 : * wkbUnknown
8329 : *
8330 : */
8331 :
8332 8843 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
8333 : {
8334 8843 : const bool bHasZ = wkbHasZ(eType);
8335 8843 : const bool bHasM = wkbHasM(eType);
8336 8843 : if (eType == wkbNone)
8337 1 : return wkbNone;
8338 8842 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8339 8842 : if (eFGType == wkbPoint)
8340 73 : eType = wkbMultiPoint;
8341 :
8342 8769 : else if (eFGType == wkbLineString)
8343 1776 : eType = wkbMultiLineString;
8344 :
8345 6993 : else if (eFGType == wkbPolygon)
8346 5544 : eType = wkbMultiPolygon;
8347 :
8348 1449 : else if (eFGType == wkbTriangle)
8349 7 : eType = wkbTIN;
8350 :
8351 1442 : else if (OGR_GT_IsCurve(eFGType))
8352 189 : eType = wkbMultiCurve;
8353 :
8354 1253 : else if (OGR_GT_IsSurface(eFGType))
8355 951 : eType = wkbMultiSurface;
8356 :
8357 : else
8358 302 : return wkbUnknown;
8359 :
8360 8540 : if (bHasZ)
8361 108 : eType = wkbSetZ(eType);
8362 8540 : if (bHasM)
8363 12 : eType = wkbSetM(eType);
8364 :
8365 8540 : return eType;
8366 : }
8367 :
8368 : /************************************************************************/
8369 : /* OGR_GT_GetSingle() */
8370 : /************************************************************************/
8371 : /**
8372 : * \brief Returns the non-collection type that be contained in the passed
8373 : * geometry type.
8374 : *
8375 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
8376 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
8377 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
8378 : * wkbGeometryCollection -> wkbUnknown
8379 : * In other cases, the original geometry is returned.
8380 : *
8381 : * Passed Z, M, ZM flag is preserved.
8382 : *
8383 : *
8384 : * @param eType Input geometry type
8385 : *
8386 : * @return the the non-collection type that be contained in the passed geometry
8387 : * type or wkbUnknown
8388 : *
8389 : * @since GDAL 3.11
8390 : */
8391 :
8392 473 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
8393 : {
8394 473 : const bool bHasZ = wkbHasZ(eType);
8395 473 : const bool bHasM = wkbHasM(eType);
8396 473 : if (eType == wkbNone)
8397 1 : return wkbNone;
8398 472 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
8399 472 : if (eFGType == wkbMultiPoint)
8400 17 : eType = wkbPoint;
8401 :
8402 455 : else if (eFGType == wkbMultiLineString)
8403 6 : eType = wkbLineString;
8404 :
8405 449 : else if (eFGType == wkbMultiPolygon)
8406 7 : eType = wkbPolygon;
8407 :
8408 442 : else if (eFGType == wkbMultiCurve)
8409 2 : eType = wkbCompoundCurve;
8410 :
8411 440 : else if (eFGType == wkbMultiSurface)
8412 2 : eType = wkbCurvePolygon;
8413 :
8414 438 : else if (eFGType == wkbGeometryCollection)
8415 4 : return wkbUnknown;
8416 :
8417 468 : if (bHasZ)
8418 3 : eType = wkbSetZ(eType);
8419 468 : if (bHasM)
8420 2 : eType = wkbSetM(eType);
8421 :
8422 468 : return eType;
8423 : }
8424 :
8425 : /************************************************************************/
8426 : /* OGR_GT_GetCurve() */
8427 : /************************************************************************/
8428 : /**
8429 : * \brief Returns the curve geometry type that can contain the passed geometry
8430 : * type
8431 : *
8432 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8433 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8434 : * and wkbMultiLineString->wkbMultiCurve.
8435 : * In other cases, the passed geometry is returned.
8436 : *
8437 : * Passed Z, M, ZM flag is preserved.
8438 : *
8439 : * @param eType Input geometry type
8440 : *
8441 : * @return the curve type that can contain the passed geometry type
8442 : *
8443 : */
8444 :
8445 32 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8446 : {
8447 32 : const bool bHasZ = wkbHasZ(eType);
8448 32 : const bool bHasM = wkbHasM(eType);
8449 32 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8450 :
8451 32 : if (eFGType == wkbLineString)
8452 3 : eType = wkbCompoundCurve;
8453 :
8454 29 : else if (eFGType == wkbPolygon)
8455 1 : eType = wkbCurvePolygon;
8456 :
8457 28 : else if (eFGType == wkbTriangle)
8458 0 : eType = wkbCurvePolygon;
8459 :
8460 28 : else if (eFGType == wkbMultiLineString)
8461 3 : eType = wkbMultiCurve;
8462 :
8463 25 : else if (eFGType == wkbMultiPolygon)
8464 4 : eType = wkbMultiSurface;
8465 :
8466 32 : if (bHasZ)
8467 4 : eType = wkbSetZ(eType);
8468 32 : if (bHasM)
8469 4 : eType = wkbSetM(eType);
8470 :
8471 32 : return eType;
8472 : }
8473 :
8474 : /************************************************************************/
8475 : /* OGR_GT_GetLinear() */
8476 : /************************************************************************/
8477 : /**
8478 : * \brief Returns the non-curve geometry type that can contain the passed
8479 : * geometry type
8480 : *
8481 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8482 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8483 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8484 : * In other cases, the passed geometry is returned.
8485 : *
8486 : * Passed Z, M, ZM flag is preserved.
8487 : *
8488 : * @param eType Input geometry type
8489 : *
8490 : * @return the non-curve type that can contain the passed geometry type
8491 : *
8492 : */
8493 :
8494 786 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8495 : {
8496 786 : const bool bHasZ = wkbHasZ(eType);
8497 786 : const bool bHasM = wkbHasM(eType);
8498 786 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8499 :
8500 786 : if (OGR_GT_IsCurve(eFGType))
8501 56 : eType = wkbLineString;
8502 :
8503 730 : else if (OGR_GT_IsSurface(eFGType))
8504 42 : eType = wkbPolygon;
8505 :
8506 688 : else if (eFGType == wkbMultiCurve)
8507 187 : eType = wkbMultiLineString;
8508 :
8509 501 : else if (eFGType == wkbMultiSurface)
8510 167 : eType = wkbMultiPolygon;
8511 :
8512 786 : if (bHasZ)
8513 154 : eType = wkbSetZ(eType);
8514 786 : if (bHasM)
8515 101 : eType = wkbSetM(eType);
8516 :
8517 786 : return eType;
8518 : }
8519 :
8520 : /************************************************************************/
8521 : /* OGR_GT_IsCurve() */
8522 : /************************************************************************/
8523 :
8524 : /**
8525 : * \brief Return if a geometry type is an instance of Curve
8526 : *
8527 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8528 : * and their Z/M/ZM variant.
8529 : *
8530 : * @param eGeomType the geometry type
8531 : * @return TRUE if the geometry type is an instance of Curve
8532 : *
8533 : */
8534 :
8535 23324 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8536 : {
8537 23324 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8538 : }
8539 :
8540 : /************************************************************************/
8541 : /* OGR_GT_IsSurface() */
8542 : /************************************************************************/
8543 :
8544 : /**
8545 : * \brief Return if a geometry type is an instance of Surface
8546 : *
8547 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8548 : * and their Z/M/ZM variant.
8549 : *
8550 : * @param eGeomType the geometry type
8551 : * @return TRUE if the geometry type is an instance of Surface
8552 : *
8553 : */
8554 :
8555 3693 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8556 : {
8557 3693 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8558 : }
8559 :
8560 : /************************************************************************/
8561 : /* OGR_GT_IsNonLinear() */
8562 : /************************************************************************/
8563 :
8564 : /**
8565 : * \brief Return if a geometry type is a non-linear geometry type.
8566 : *
8567 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8568 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8569 : * Z/M variants.
8570 : *
8571 : * @param eGeomType the geometry type
8572 : * @return TRUE if the geometry type is a non-linear geometry type.
8573 : *
8574 : */
8575 :
8576 120357 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8577 : {
8578 120357 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8579 120349 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8580 120276 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8581 240706 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8582 120357 : eFGeomType == wkbMultiSurface;
8583 : }
8584 :
8585 : /************************************************************************/
8586 : /* CastToError() */
8587 : /************************************************************************/
8588 :
8589 : //! @cond Doxygen_Suppress
8590 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8591 : {
8592 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8593 0 : poGeom->getGeometryName());
8594 0 : delete poGeom;
8595 0 : return nullptr;
8596 : }
8597 :
8598 : //! @endcond
8599 :
8600 : /************************************************************************/
8601 : /* OGRexportToSFCGAL() */
8602 : /************************************************************************/
8603 :
8604 : //! @cond Doxygen_Suppress
8605 : sfcgal_geometry_t *
8606 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8607 : {
8608 : #ifdef HAVE_SFCGAL
8609 :
8610 : sfcgal_init();
8611 : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8612 :
8613 : const auto exportToSFCGALViaWKB =
8614 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8615 : {
8616 : if (!geom)
8617 : return nullptr;
8618 :
8619 : // Get WKB size and allocate buffer
8620 : size_t nSize = geom->WkbSize();
8621 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8622 :
8623 : // Set export options with NDR byte order
8624 : OGRwkbExportOptions oOptions;
8625 : oOptions.eByteOrder = wkbNDR;
8626 : // and ISO to avoid wkb25DBit for Z geometries
8627 : oOptions.eWkbVariant = wkbVariantIso;
8628 :
8629 : // Export to WKB
8630 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8631 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8632 : {
8633 : sfcgalGeom = sfcgal_io_read_wkb(
8634 : reinterpret_cast<const char *>(pabyWkb), nSize);
8635 : }
8636 :
8637 : CPLFree(pabyWkb);
8638 : return sfcgalGeom;
8639 : };
8640 :
8641 : // Handle special cases
8642 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8643 : {
8644 : std::unique_ptr<OGRLineString> poLS(
8645 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8646 : return exportToSFCGALViaWKB(poLS.get());
8647 : }
8648 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8649 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8650 : {
8651 : std::unique_ptr<OGRLineString> poLS(
8652 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8653 : ->toLineString());
8654 : return exportToSFCGALViaWKB(poLS.get());
8655 : }
8656 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8657 : {
8658 : std::unique_ptr<OGRPolygon> poPolygon(
8659 : OGRGeometryFactory::forceToPolygon(
8660 : poGeom->clone()->toCurvePolygon())
8661 : ->toPolygon());
8662 : return exportToSFCGALViaWKB(poPolygon.get());
8663 : }
8664 : else
8665 : {
8666 : // Default case - direct export
8667 : return exportToSFCGALViaWKB(poGeom);
8668 : }
8669 : #else
8670 : char *buffer = nullptr;
8671 :
8672 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8673 : // Polygon
8674 :
8675 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8676 : {
8677 : // cast it to LineString and get the WKT
8678 : std::unique_ptr<OGRLineString> poLS(
8679 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8680 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8681 : {
8682 : sfcgal_geometry_t *_geometry =
8683 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8684 : CPLFree(buffer);
8685 : return _geometry;
8686 : }
8687 : else
8688 : {
8689 : CPLFree(buffer);
8690 : return nullptr;
8691 : }
8692 : }
8693 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8694 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8695 : {
8696 : // convert it to LineString and get the WKT
8697 : std::unique_ptr<OGRLineString> poLS(
8698 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8699 : ->toLineString());
8700 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8701 : {
8702 : sfcgal_geometry_t *_geometry =
8703 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8704 : CPLFree(buffer);
8705 : return _geometry;
8706 : }
8707 : else
8708 : {
8709 : CPLFree(buffer);
8710 : return nullptr;
8711 : }
8712 : }
8713 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8714 : {
8715 : // convert it to Polygon and get the WKT
8716 : std::unique_ptr<OGRPolygon> poPolygon(
8717 : OGRGeometryFactory::forceToPolygon(
8718 : poGeom->clone()->toCurvePolygon())
8719 : ->toPolygon());
8720 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8721 : {
8722 : sfcgal_geometry_t *_geometry =
8723 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8724 : CPLFree(buffer);
8725 : return _geometry;
8726 : }
8727 : else
8728 : {
8729 : CPLFree(buffer);
8730 : return nullptr;
8731 : }
8732 : }
8733 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8734 : {
8735 : sfcgal_geometry_t *_geometry =
8736 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8737 : CPLFree(buffer);
8738 : return _geometry;
8739 : }
8740 : else
8741 : {
8742 : CPLFree(buffer);
8743 : return nullptr;
8744 : }
8745 : #endif
8746 : #else
8747 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8748 0 : return nullptr;
8749 : #endif
8750 : }
8751 :
8752 : //! @endcond
8753 :
8754 : /************************************************************************/
8755 : /* SFCGALexportToOGR() */
8756 : /************************************************************************/
8757 :
8758 : //! @cond Doxygen_Suppress
8759 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8760 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8761 : {
8762 : #ifdef HAVE_SFCGAL
8763 : if (geometry == nullptr)
8764 : return nullptr;
8765 :
8766 : sfcgal_init();
8767 : char *pabySFCGAL = nullptr;
8768 : size_t nLength = 0;
8769 : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8770 :
8771 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8772 :
8773 : if (pabySFCGAL == nullptr || nLength == 0)
8774 : return nullptr;
8775 :
8776 : OGRGeometry *poGeom = nullptr;
8777 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8778 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8779 : nLength);
8780 :
8781 : free(pabySFCGAL);
8782 :
8783 : if (eErr == OGRERR_NONE)
8784 : {
8785 : return poGeom;
8786 : }
8787 : else
8788 : {
8789 : return nullptr;
8790 : }
8791 : #else
8792 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8793 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8794 : memcpy(pszWKT, pabySFCGAL, nLength);
8795 : pszWKT[nLength] = 0;
8796 : free(pabySFCGAL);
8797 :
8798 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8799 :
8800 : OGRGeometry *poGeom = nullptr;
8801 : if (geom_type == SFCGAL_TYPE_POINT)
8802 : {
8803 : poGeom = new OGRPoint();
8804 : }
8805 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8806 : {
8807 : poGeom = new OGRLineString();
8808 : }
8809 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8810 : {
8811 : poGeom = new OGRPolygon();
8812 : }
8813 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8814 : {
8815 : poGeom = new OGRMultiPoint();
8816 : }
8817 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8818 : {
8819 : poGeom = new OGRMultiLineString();
8820 : }
8821 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8822 : {
8823 : poGeom = new OGRMultiPolygon();
8824 : }
8825 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8826 : {
8827 : poGeom = new OGRGeometryCollection();
8828 : }
8829 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8830 : {
8831 : poGeom = new OGRTriangle();
8832 : }
8833 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8834 : {
8835 : poGeom = new OGRPolyhedralSurface();
8836 : }
8837 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8838 : {
8839 : poGeom = new OGRTriangulatedSurface();
8840 : }
8841 : else
8842 : {
8843 : CPLFree(pszWKT);
8844 : return nullptr;
8845 : }
8846 :
8847 : const char *pszWKTTmp = pszWKT;
8848 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8849 : {
8850 : CPLFree(pszWKT);
8851 : return poGeom;
8852 : }
8853 : else
8854 : {
8855 : delete poGeom;
8856 : CPLFree(pszWKT);
8857 : return nullptr;
8858 : }
8859 : #endif
8860 : #else
8861 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8862 0 : return nullptr;
8863 : #endif
8864 : }
8865 :
8866 : //! @endcond
8867 :
8868 : //! @cond Doxygen_Suppress
8869 11082 : bool OGRGeometry::IsSFCGALCompatible() const
8870 : {
8871 11082 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8872 11082 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8873 : eGType == wkbTIN)
8874 : {
8875 2 : return TRUE;
8876 : }
8877 11080 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8878 : {
8879 14 : const OGRGeometryCollection *poGC = toGeometryCollection();
8880 14 : bool bIsSFCGALCompatible = false;
8881 14 : for (auto &&poSubGeom : *poGC)
8882 : {
8883 : OGRwkbGeometryType eSubGeomType =
8884 14 : wkbFlatten(poSubGeom->getGeometryType());
8885 14 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8886 : {
8887 0 : bIsSFCGALCompatible = true;
8888 : }
8889 14 : else if (eSubGeomType != wkbMultiPolygon)
8890 : {
8891 14 : bIsSFCGALCompatible = false;
8892 14 : break;
8893 : }
8894 : }
8895 14 : return bIsSFCGALCompatible;
8896 : }
8897 11066 : return FALSE;
8898 : }
8899 :
8900 : //! @endcond
8901 :
8902 : /************************************************************************/
8903 : /* roundCoordinatesIEEE754() */
8904 : /************************************************************************/
8905 :
8906 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8907 : * double-precision binary representation.
8908 : *
8909 : * Determines the number of bits (N) required to represent a coordinate value
8910 : * with a specified number of digits after the decimal point, and then sets all
8911 : * but the N most significant bits to zero. The resulting coordinate value will
8912 : * still round to the original value (e.g. after roundCoordinates()), but will
8913 : * have improved compressiblity.
8914 : *
8915 : * @param options Contains the precision requirements.
8916 : * @since GDAL 3.9
8917 : */
8918 1 : void OGRGeometry::roundCoordinatesIEEE754(
8919 : const OGRGeomCoordinateBinaryPrecision &options)
8920 : {
8921 : struct Quantizer : public OGRDefaultGeometryVisitor
8922 : {
8923 : const OGRGeomCoordinateBinaryPrecision &m_options;
8924 :
8925 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8926 1 : : m_options(optionsIn)
8927 : {
8928 1 : }
8929 :
8930 : using OGRDefaultGeometryVisitor::visit;
8931 :
8932 3 : void visit(OGRPoint *poPoint) override
8933 : {
8934 3 : if (m_options.nXYBitPrecision != INT_MIN)
8935 : {
8936 : uint64_t i;
8937 : double d;
8938 3 : d = poPoint->getX();
8939 3 : memcpy(&i, &d, sizeof(i));
8940 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8941 3 : memcpy(&d, &i, sizeof(i));
8942 3 : poPoint->setX(d);
8943 3 : d = poPoint->getY();
8944 3 : memcpy(&i, &d, sizeof(i));
8945 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8946 3 : memcpy(&d, &i, sizeof(i));
8947 3 : poPoint->setY(d);
8948 : }
8949 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8950 : {
8951 : uint64_t i;
8952 : double d;
8953 3 : d = poPoint->getZ();
8954 3 : memcpy(&i, &d, sizeof(i));
8955 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8956 3 : memcpy(&d, &i, sizeof(i));
8957 3 : poPoint->setZ(d);
8958 : }
8959 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8960 : {
8961 : uint64_t i;
8962 : double d;
8963 3 : d = poPoint->getM();
8964 3 : memcpy(&i, &d, sizeof(i));
8965 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8966 3 : memcpy(&d, &i, sizeof(i));
8967 3 : poPoint->setM(d);
8968 : }
8969 3 : }
8970 : };
8971 :
8972 2 : Quantizer quantizer(options);
8973 1 : accept(&quantizer);
8974 1 : }
8975 :
8976 : /************************************************************************/
8977 : /* visit() */
8978 : /************************************************************************/
8979 :
8980 105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8981 : {
8982 1248 : for (auto &&oPoint : *poGeom)
8983 : {
8984 1143 : oPoint.accept(this);
8985 : }
8986 105 : }
8987 :
8988 104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8989 : {
8990 104 : _visit(poGeom);
8991 104 : }
8992 :
8993 80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8994 : {
8995 80 : visit(poGeom->toUpperClass());
8996 80 : }
8997 :
8998 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8999 : {
9000 1 : _visit(poGeom);
9001 1 : }
9002 :
9003 78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
9004 : {
9005 159 : for (auto &&poSubGeom : *poGeom)
9006 81 : poSubGeom->accept(this);
9007 78 : }
9008 :
9009 77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
9010 : {
9011 77 : visit(poGeom->toUpperClass());
9012 77 : }
9013 :
9014 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
9015 : {
9016 1 : visit(poGeom->toUpperClass());
9017 1 : }
9018 :
9019 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
9020 : {
9021 8 : visit(poGeom->toUpperClass());
9022 8 : }
9023 :
9024 44 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
9025 : {
9026 44 : visit(poGeom->toUpperClass());
9027 44 : }
9028 :
9029 56 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
9030 : {
9031 135 : for (auto &&poSubGeom : *poGeom)
9032 79 : poSubGeom->accept(this);
9033 56 : }
9034 :
9035 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
9036 : {
9037 2 : for (auto &&poSubGeom : *poGeom)
9038 1 : poSubGeom->accept(this);
9039 1 : }
9040 :
9041 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
9042 : {
9043 1 : visit(poGeom->toUpperClass());
9044 1 : }
9045 :
9046 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
9047 : {
9048 1 : visit(poGeom->toUpperClass());
9049 1 : }
9050 :
9051 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
9052 : {
9053 2 : visit(poGeom->toUpperClass());
9054 2 : }
9055 :
9056 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
9057 : {
9058 4 : for (auto &&poSubGeom : *poGeom)
9059 2 : poSubGeom->accept(this);
9060 2 : }
9061 :
9062 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
9063 : {
9064 1 : visit(poGeom->toUpperClass());
9065 1 : }
9066 :
9067 127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
9068 : {
9069 2988 : for (auto &&oPoint : *poGeom)
9070 : {
9071 2861 : oPoint.accept(this);
9072 : }
9073 127 : }
9074 :
9075 121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
9076 : {
9077 121 : _visit(poGeom);
9078 121 : }
9079 :
9080 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
9081 : {
9082 110 : visit(poGeom->toUpperClass());
9083 110 : }
9084 :
9085 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
9086 : {
9087 6 : _visit(poGeom);
9088 6 : }
9089 :
9090 112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
9091 : {
9092 225 : for (auto &&poSubGeom : *poGeom)
9093 113 : poSubGeom->accept(this);
9094 112 : }
9095 :
9096 109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
9097 : {
9098 109 : visit(poGeom->toUpperClass());
9099 109 : }
9100 :
9101 64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
9102 : {
9103 64 : visit(poGeom->toUpperClass());
9104 64 : }
9105 :
9106 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
9107 : {
9108 1 : visit(poGeom->toUpperClass());
9109 1 : }
9110 :
9111 83 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
9112 : {
9113 83 : visit(poGeom->toUpperClass());
9114 83 : }
9115 :
9116 151 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
9117 : {
9118 489 : for (auto &&poSubGeom : *poGeom)
9119 338 : poSubGeom->accept(this);
9120 151 : }
9121 :
9122 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
9123 : {
9124 14 : for (auto &&poSubGeom : *poGeom)
9125 11 : poSubGeom->accept(this);
9126 3 : }
9127 :
9128 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
9129 : {
9130 1 : visit(poGeom->toUpperClass());
9131 1 : }
9132 :
9133 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
9134 : {
9135 1 : visit(poGeom->toUpperClass());
9136 1 : }
9137 :
9138 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
9139 : {
9140 2 : visit(poGeom->toUpperClass());
9141 2 : }
9142 :
9143 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
9144 : {
9145 4 : for (auto &&poSubGeom : *poGeom)
9146 2 : poSubGeom->accept(this);
9147 2 : }
9148 :
9149 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
9150 : {
9151 1 : visit(poGeom->toUpperClass());
9152 1 : }
9153 :
9154 : /************************************************************************/
9155 : /* OGRGeometryUniquePtrDeleter */
9156 : /************************************************************************/
9157 :
9158 : //! @cond Doxygen_Suppress
9159 0 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
9160 : {
9161 0 : delete poGeom;
9162 0 : }
9163 :
9164 : //! @endcond
9165 :
9166 : /************************************************************************/
9167 : /* OGRPreparedGeometryUniquePtrDeleter */
9168 : /************************************************************************/
9169 :
9170 : //! @cond Doxygen_Suppress
9171 158 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
9172 : OGRPreparedGeometry *poPreparedGeom) const
9173 : {
9174 158 : OGRDestroyPreparedGeometry(poPreparedGeom);
9175 158 : }
9176 :
9177 : //! @endcond
9178 :
9179 : /************************************************************************/
9180 : /* HomogenizeDimensionalityWith() */
9181 : /************************************************************************/
9182 :
9183 : //! @cond Doxygen_Suppress
9184 3351720 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
9185 : {
9186 3351720 : if (poOtherGeom->Is3D() && !Is3D())
9187 1331020 : set3D(TRUE);
9188 :
9189 3351720 : if (poOtherGeom->IsMeasured() && !IsMeasured())
9190 854 : setMeasured(TRUE);
9191 :
9192 3351720 : if (!poOtherGeom->Is3D() && Is3D())
9193 298 : poOtherGeom->set3D(TRUE);
9194 :
9195 3351720 : if (!poOtherGeom->IsMeasured() && IsMeasured())
9196 41 : poOtherGeom->setMeasured(TRUE);
9197 3351720 : }
9198 :
9199 : //! @endcond
9200 :
9201 : /************************************************************************/
9202 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
9203 : /************************************************************************/
9204 :
9205 : /** Set binary precision options from resolution.
9206 : *
9207 : * @since GDAL 3.9
9208 : */
9209 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
9210 : const OGRGeomCoordinatePrecision &prec)
9211 : {
9212 16 : if (prec.dfXYResolution != 0)
9213 : {
9214 16 : nXYBitPrecision =
9215 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
9216 : }
9217 16 : if (prec.dfZResolution != 0)
9218 : {
9219 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
9220 : }
9221 16 : if (prec.dfMResolution != 0)
9222 : {
9223 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
9224 : }
9225 16 : }
9226 :
9227 : /************************************************************************/
9228 : /* OGRwkbExportOptionsCreate() */
9229 : /************************************************************************/
9230 :
9231 : /**
9232 : * \brief Create geometry WKB export options.
9233 : *
9234 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
9235 : *
9236 : * @return object to be freed with OGRwkbExportOptionsDestroy().
9237 : * @since GDAL 3.9
9238 : */
9239 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
9240 : {
9241 2 : return new OGRwkbExportOptions;
9242 : }
9243 :
9244 : /************************************************************************/
9245 : /* OGRwkbExportOptionsDestroy() */
9246 : /************************************************************************/
9247 :
9248 : /**
9249 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
9250 : *
9251 : * @param psOptions WKB export options
9252 : * @since GDAL 3.9
9253 : */
9254 :
9255 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
9256 : {
9257 2 : delete psOptions;
9258 2 : }
9259 :
9260 : /************************************************************************/
9261 : /* OGRwkbExportOptionsSetByteOrder() */
9262 : /************************************************************************/
9263 :
9264 : /**
9265 : * \brief Set the WKB byte order.
9266 : *
9267 : * @param psOptions WKB export options
9268 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
9269 : * Intel)
9270 : * @since GDAL 3.9
9271 : */
9272 :
9273 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
9274 : OGRwkbByteOrder eByteOrder)
9275 : {
9276 1 : psOptions->eByteOrder = eByteOrder;
9277 1 : }
9278 :
9279 : /************************************************************************/
9280 : /* OGRwkbExportOptionsSetVariant() */
9281 : /************************************************************************/
9282 :
9283 : /**
9284 : * \brief Set the WKB variant
9285 : *
9286 : * @param psOptions WKB export options
9287 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
9288 : * wkbVariantPostGIS1
9289 : * @since GDAL 3.9
9290 : */
9291 :
9292 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
9293 : OGRwkbVariant eWkbVariant)
9294 : {
9295 1 : psOptions->eWkbVariant = eWkbVariant;
9296 1 : }
9297 :
9298 : /************************************************************************/
9299 : /* OGRwkbExportOptionsSetPrecision() */
9300 : /************************************************************************/
9301 :
9302 : /**
9303 : * \brief Set precision options
9304 : *
9305 : * @param psOptions WKB export options
9306 : * @param hPrecisionOptions Precision options (might be null to reset them)
9307 : * @since GDAL 3.9
9308 : */
9309 :
9310 1 : void OGRwkbExportOptionsSetPrecision(
9311 : OGRwkbExportOptions *psOptions,
9312 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
9313 : {
9314 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
9315 1 : if (hPrecisionOptions)
9316 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
9317 1 : }
9318 :
9319 : /************************************************************************/
9320 : /* IsRectangle() */
9321 : /************************************************************************/
9322 :
9323 : /**
9324 : * \brief Returns whether the geometry is a polygon with 4 corners forming
9325 : * a rectangle.
9326 : *
9327 : * @since GDAL 3.10
9328 : */
9329 53086 : bool OGRGeometry::IsRectangle() const
9330 : {
9331 53086 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
9332 576 : return false;
9333 :
9334 52510 : const OGRPolygon *poPoly = toPolygon();
9335 :
9336 52510 : if (poPoly->getNumInteriorRings() != 0)
9337 27 : return false;
9338 :
9339 52483 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
9340 52483 : if (!poRing)
9341 4 : return false;
9342 :
9343 52479 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
9344 33 : return false;
9345 :
9346 : // If the ring has 5 points, the last should be the first.
9347 104835 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
9348 52389 : poRing->getY(0) != poRing->getY(4)))
9349 1 : return false;
9350 :
9351 : // Polygon with first segment in "y" direction.
9352 104173 : if (poRing->getX(0) == poRing->getX(1) &&
9353 103455 : poRing->getY(1) == poRing->getY(2) &&
9354 155900 : poRing->getX(2) == poRing->getX(3) &&
9355 51727 : poRing->getY(3) == poRing->getY(0))
9356 51727 : return true;
9357 :
9358 : // Polygon with first segment in "x" direction.
9359 1373 : if (poRing->getY(0) == poRing->getY(1) &&
9360 1310 : poRing->getX(1) == poRing->getX(2) &&
9361 2028 : poRing->getY(2) == poRing->getY(3) &&
9362 655 : poRing->getX(3) == poRing->getX(0))
9363 655 : return true;
9364 :
9365 63 : return false;
9366 : }
9367 :
9368 : /************************************************************************/
9369 : /* hasEmptyParts() */
9370 : /************************************************************************/
9371 :
9372 : /**
9373 : * \brief Returns whether a geometry has empty parts/rings.
9374 : *
9375 : * Returns true if removeEmptyParts() will modify the geometry.
9376 : *
9377 : * This is different from IsEmpty().
9378 : *
9379 : * @since GDAL 3.10
9380 : */
9381 103 : bool OGRGeometry::hasEmptyParts() const
9382 : {
9383 103 : return false;
9384 : }
9385 :
9386 : /************************************************************************/
9387 : /* removeEmptyParts() */
9388 : /************************************************************************/
9389 :
9390 : /**
9391 : * \brief Remove empty parts/rings from this geometry.
9392 : *
9393 : * @since GDAL 3.10
9394 : */
9395 17 : void OGRGeometry::removeEmptyParts()
9396 : {
9397 17 : }
9398 :
9399 : /************************************************************************/
9400 : /* ~IOGRGeometryVisitor() */
9401 : /************************************************************************/
9402 :
9403 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
9404 :
9405 : /************************************************************************/
9406 : /* ~IOGRConstGeometryVisitor() */
9407 : /************************************************************************/
9408 :
9409 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
9410 :
9411 : /************************************************************************/
9412 : /* GDALGEOSProgressReporter() */
9413 : /************************************************************************/
9414 :
9415 : #if HAVE_GEOS
9416 642 : GDALGEOSProgressReporter::GDALGEOSProgressReporter(
9417 : GEOSContextHandle_t hGEOSCtxt, GDALProgressFunc pfnProgress,
9418 642 : void *pProgressData)
9419 : : m_context(hGEOSCtxt), m_userData{
9420 : pfnProgress,
9421 : pProgressData,
9422 642 : }
9423 : {
9424 : #if GEOS_VERSION_MAJOR > 3 || \
9425 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15)
9426 642 : GEOSContext_setProgressCallback_r(m_context, GDALGEOSProgress, &m_userData);
9427 : // Although GEOS accepts interrupt callbacks starting with version 3.14,
9428 : // GDAL has no way to request an interrupt in the absence of a progress
9429 : // update (available since GEOS 3.15 only.)
9430 642 : GEOSContext_setInterruptCallback_r(m_context, GDALGEOSCheckInterrupt,
9431 642 : &m_userData);
9432 : #endif
9433 642 : }
9434 :
9435 1284 : GDALGEOSProgressReporter::~GDALGEOSProgressReporter()
9436 : {
9437 : #if GEOS_VERSION_MAJOR > 3 || \
9438 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15)
9439 642 : GEOSContext_setProgressCallback_r(m_context, nullptr, nullptr);
9440 642 : GEOSContext_setInterruptCallback_r(m_context, nullptr, nullptr);
9441 : #endif
9442 642 : }
9443 : #endif
9444 :
9445 : /************************************************************************/
9446 : /* GDALGEOSProgress() */
9447 : /************************************************************************/
9448 :
9449 : #if HAVE_GEOS
9450 : /** Callback invoked by GEOS to report progress. Progress will be relayed on
9451 : * to a GDALProgressFunc. If the GDALProgressFunc returns false, a GEOS
9452 : * interrupt will be requested at the next opportunity. */
9453 29775 : void GDALGEOSProgress(double frac, const char *message, void *userData)
9454 : {
9455 29775 : GDALGEOSProgressUserData *pData =
9456 : static_cast<GDALGEOSProgressUserData *>(userData);
9457 29775 : if (!pData->m_pfnProgress)
9458 : {
9459 29668 : return;
9460 : }
9461 :
9462 107 : if (!pData->m_pfnProgress(frac, message, pData->m_pProgressData))
9463 : {
9464 3 : pData->m_bRequestedInterrupt = true;
9465 : };
9466 : }
9467 : #endif
9468 :
9469 : /************************************************************************/
9470 : /* GDALGEOSCheckInterrupt() */
9471 : /************************************************************************/
9472 :
9473 : #if HAVE_GEOS
9474 : /** Callback invoked by GEOS to check whether the a previous invocation of a
9475 : * GDALProgressFunc has requested processing be interrupted. */
9476 131827 : int GDALGEOSCheckInterrupt(void *userData)
9477 : {
9478 131827 : GDALGEOSProgressUserData *pData =
9479 : static_cast<GDALGEOSProgressUserData *>(userData);
9480 131827 : return pData->m_bRequestedInterrupt;
9481 : }
9482 : #endif
|