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 103 : static void OGRGEOSErrorHandler(const char *fmt, ...)
60 : {
61 : va_list args;
62 :
63 103 : va_start(args, fmt);
64 103 : CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
65 103 : va_end(args);
66 103 : }
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 11230 : int OGRWktOptions::getDefaultPrecision()
83 : {
84 11230 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
85 : }
86 :
87 11326 : bool OGRWktOptions::getDefaultRound()
88 : {
89 11326 : 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 1785140 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
107 1785140 : : poSRS(other.poSRS), flags(other.flags)
108 : {
109 1785140 : if (poSRS != nullptr)
110 75752 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
111 1785140 : }
112 :
113 : /************************************************************************/
114 : /* OGRGeometry( OGRGeometry&& ) */
115 : /************************************************************************/
116 :
117 : /**
118 : * \brief Move constructor.
119 : *
120 : * @since GDAL 3.11
121 : */
122 :
123 156286 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
124 156286 : : poSRS(other.poSRS), flags(other.flags)
125 : {
126 156286 : other.poSRS = nullptr;
127 156286 : }
128 :
129 : /************************************************************************/
130 : /* ~OGRGeometry() */
131 : /************************************************************************/
132 :
133 25604900 : OGRGeometry::~OGRGeometry()
134 :
135 : {
136 12802400 : if (poSRS != nullptr)
137 3632470 : const_cast<OGRSpatialReference *>(poSRS)->Release();
138 12802400 : }
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 103914 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
170 : {
171 103914 : if (this != &other)
172 : {
173 103914 : poSRS = other.poSRS;
174 103914 : other.poSRS = nullptr;
175 103914 : flags = other.flags;
176 : }
177 103914 : 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 307 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
237 : CSLConstList papszOptions) const
238 :
239 : {
240 307 : if (pszPrefix == nullptr)
241 306 : pszPrefix = "";
242 :
243 307 : 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 307 : CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
275 307 : if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
276 : {
277 15 : osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
278 15 : 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 1 : 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 1 : const OGRGeometryCollection *poColl = toGeometryCollection();
425 : osRet +=
426 1 : CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
427 2 : for (auto &&poSubGeom : *poColl)
428 : {
429 1 : osRet += pszPrefix;
430 1 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
431 : }
432 1 : 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 15 : }
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 614 : 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 5608960 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
506 :
507 : {
508 : // Do in that order to properly handle poSR == poSRS
509 5608960 : if (poSR != nullptr)
510 3597650 : const_cast<OGRSpatialReference *>(poSR)->Reference();
511 5608960 : if (poSRS != nullptr)
512 40926 : const_cast<OGRSpatialReference *>(poSRS)->Release();
513 :
514 5608960 : poSRS = poSR;
515 5608960 : }
516 :
517 : /************************************************************************/
518 : /* OGR_G_AssignSpatialReference() */
519 : /************************************************************************/
520 : /**
521 : * \brief Assign spatial reference to this object.
522 : *
523 : * Any existing spatial reference
524 : * is replaced, but under no circumstances does this result in the object
525 : * being reprojected. It is just changing the interpretation of the existing
526 : * geometry. Note that assigning a spatial reference increments the
527 : * reference count on the OGRSpatialReference, but does not copy it.
528 : *
529 : * This will also assign the spatial reference to
530 : * potential sub-geometries of the geometry (OGRGeometryCollection,
531 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
532 : * derived classes).
533 : *
534 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
535 : *
536 : * This function is the same as the CPP method
537 : * OGRGeometry::assignSpatialReference.
538 : *
539 : * @param hGeom handle on the geometry to apply the new spatial reference
540 : * system.
541 : * @param hSRS handle on the new spatial reference system to apply.
542 : */
543 :
544 80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
545 :
546 : {
547 80 : VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
548 :
549 160 : OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
550 80 : OGRSpatialReference::FromHandle(hSRS));
551 : }
552 :
553 : /************************************************************************/
554 : /* Intersects() */
555 : /************************************************************************/
556 :
557 : /**
558 : * \brief Do these features intersect?
559 : *
560 : * Determines whether two geometries intersect. If GEOS is enabled, then
561 : * this is done in rigorous fashion otherwise TRUE is returned if the
562 : * envelopes (bounding boxes) of the two geometries overlap.
563 : *
564 : * The poOtherGeom argument may be safely NULL, but in this case the method
565 : * will always return TRUE. That is, a NULL geometry is treated as being
566 : * everywhere.
567 : *
568 : * This method is the same as the C function OGR_G_Intersects().
569 : *
570 : * @param poOtherGeom the other geometry to test against.
571 : *
572 : * @return TRUE if the geometries intersect, otherwise FALSE.
573 : */
574 :
575 44 : bool OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
576 :
577 : {
578 44 : if (poOtherGeom == nullptr)
579 0 : return TRUE;
580 :
581 44 : OGREnvelope oEnv1;
582 44 : getEnvelope(&oEnv1);
583 :
584 44 : OGREnvelope oEnv2;
585 44 : poOtherGeom->getEnvelope(&oEnv2);
586 :
587 44 : if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
588 26 : oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
589 18 : return FALSE;
590 :
591 : #ifndef HAVE_GEOS
592 : // Without GEOS we assume that envelope overlap is equivalent to
593 : // actual intersection.
594 : return TRUE;
595 : #else
596 :
597 26 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
598 26 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
599 26 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
600 :
601 26 : bool bResult = false;
602 26 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
603 : {
604 26 : bResult =
605 26 : GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
606 : }
607 :
608 26 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
609 26 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
610 26 : freeGEOSContext(hGEOSCtxt);
611 :
612 26 : return bResult;
613 : #endif // HAVE_GEOS
614 : }
615 :
616 : // Old API compatibility function.
617 :
618 : //! @cond Doxygen_Suppress
619 0 : bool OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
620 :
621 : {
622 0 : return Intersects(poOtherGeom);
623 : }
624 :
625 : //! @endcond
626 :
627 : /************************************************************************/
628 : /* OGR_G_Intersects() */
629 : /************************************************************************/
630 : /**
631 : * \brief Do these features intersect?
632 : *
633 : * Determines whether two geometries intersect. If GEOS is enabled, then
634 : * this is done in rigorous fashion otherwise TRUE is returned if the
635 : * envelopes (bounding boxes) of the two geometries overlap.
636 : *
637 : * This function is the same as the CPP method OGRGeometry::Intersects.
638 : *
639 : * @param hGeom handle on the first geometry.
640 : * @param hOtherGeom handle on the other geometry to test against.
641 : *
642 : * @return TRUE if the geometries intersect, otherwise FALSE.
643 : */
644 :
645 11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
646 :
647 : {
648 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
649 11 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
650 :
651 22 : return OGRGeometry::FromHandle(hGeom)->Intersects(
652 22 : OGRGeometry::FromHandle(hOtherGeom));
653 : }
654 :
655 : //! @cond Doxygen_Suppress
656 0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
657 :
658 : {
659 0 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
660 0 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
661 :
662 0 : return OGRGeometry::FromHandle(hGeom)->Intersects(
663 0 : OGRGeometry::FromHandle(hOtherGeom));
664 : }
665 :
666 : //! @endcond
667 :
668 : /************************************************************************/
669 : /* transformTo() */
670 : /************************************************************************/
671 :
672 : /**
673 : * \brief Transform geometry to new spatial reference system.
674 : *
675 : * This method will transform the coordinates of a geometry from
676 : * their current spatial reference system to a new target spatial
677 : * reference system. Normally this means reprojecting the vectors,
678 : * but it could include datum shifts, and changes of units.
679 : *
680 : * This method will only work if the geometry already has an assigned
681 : * spatial reference system, and if it is transformable to the target
682 : * coordinate system.
683 : *
684 : * Because this method requires internal creation and initialization of an
685 : * OGRCoordinateTransformation object it is significantly more expensive to
686 : * use this method to transform many geometries than it is to create the
687 : * OGRCoordinateTransformation in advance, and call transform() with that
688 : * transformation. This method exists primarily for convenience when only
689 : * transforming a single geometry.
690 : *
691 : * This method is the same as the C function OGR_G_TransformTo().
692 : *
693 : * @param poSR spatial reference system to transform to.
694 : *
695 : * @return OGRERR_NONE on success, or an error code.
696 : */
697 :
698 33 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
699 :
700 : {
701 33 : if (getSpatialReference() == nullptr)
702 : {
703 1 : CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
704 1 : return OGRERR_FAILURE;
705 : }
706 :
707 32 : if (poSR == nullptr)
708 : {
709 0 : CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
710 0 : return OGRERR_FAILURE;
711 : }
712 :
713 : OGRCoordinateTransformation *poCT =
714 32 : OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
715 32 : if (poCT == nullptr)
716 0 : return OGRERR_FAILURE;
717 :
718 32 : const OGRErr eErr = transform(poCT);
719 :
720 32 : delete poCT;
721 :
722 32 : return eErr;
723 : }
724 :
725 : /************************************************************************/
726 : /* OGR_G_TransformTo() */
727 : /************************************************************************/
728 : /**
729 : * \brief Transform geometry to new spatial reference system.
730 : *
731 : * This function will transform the coordinates of a geometry from
732 : * their current spatial reference system to a new target spatial
733 : * reference system. Normally this means reprojecting the vectors,
734 : * but it could include datum shifts, and changes of units.
735 : *
736 : * This function will only work if the geometry already has an assigned
737 : * spatial reference system, and if it is transformable to the target
738 : * coordinate system.
739 : *
740 : * Because this function requires internal creation and initialization of an
741 : * OGRCoordinateTransformation object it is significantly more expensive to
742 : * use this function to transform many geometries than it is to create the
743 : * OGRCoordinateTransformation in advance, and call transform() with that
744 : * transformation. This function exists primarily for convenience when only
745 : * transforming a single geometry.
746 : *
747 : * This function is the same as the CPP method OGRGeometry::transformTo.
748 : *
749 : * @param hGeom handle on the geometry to apply the transform to.
750 : * @param hSRS handle on the spatial reference system to apply.
751 : *
752 : * @return OGRERR_NONE on success, or an error code.
753 : */
754 :
755 9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
756 :
757 : {
758 9 : VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
759 :
760 18 : return OGRGeometry::FromHandle(hGeom)->transformTo(
761 18 : OGRSpatialReference::FromHandle(hSRS));
762 : }
763 :
764 : /**
765 : * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
766 : *
767 : * \brief Apply arbitrary coordinate transformation to geometry.
768 : *
769 : * This method will transform the coordinates of a geometry from
770 : * their current spatial reference system to a new target spatial
771 : * reference system. Normally this means reprojecting the vectors,
772 : * but it could include datum shifts, and changes of units.
773 : *
774 : * Note that this method does not require that the geometry already
775 : * have a spatial reference system. It will be assumed that they can
776 : * be treated as having the source spatial reference system of the
777 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
778 : * will be ignored. On successful completion the output OGRSpatialReference
779 : * of the OGRCoordinateTransformation will be assigned to the geometry.
780 : *
781 : * This method only does reprojection on a point-by-point basis. It does not
782 : * include advanced logic to deal with discontinuities at poles or antimeridian.
783 : * For that, use the OGRGeometryFactory::transformWithOptions() method.
784 : *
785 : * This method is the same as the C function OGR_G_Transform().
786 : *
787 : * @param poCT the transformation to apply.
788 : *
789 : * @return OGRERR_NONE on success or an error code.
790 : */
791 :
792 : /************************************************************************/
793 : /* OGR_G_Transform() */
794 : /************************************************************************/
795 : /**
796 : * \brief Apply arbitrary coordinate transformation to geometry.
797 : *
798 : * This function will transform the coordinates of a geometry from
799 : * their current spatial reference system to a new target spatial
800 : * reference system. Normally this means reprojecting the vectors,
801 : * but it could include datum shifts, and changes of units.
802 : *
803 : * Note that this function does not require that the geometry already
804 : * have a spatial reference system. It will be assumed that they can
805 : * be treated as having the source spatial reference system of the
806 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
807 : * will be ignored. On successful completion the output OGRSpatialReference
808 : * of the OGRCoordinateTransformation will be assigned to the geometry.
809 : *
810 : * This function only does reprojection on a point-by-point basis. It does not
811 : * include advanced logic to deal with discontinuities at poles or antimeridian.
812 : * For that, use the OGR_GeomTransformer_Create() and
813 : * OGR_GeomTransformer_Transform() functions.
814 : *
815 : * This function is the same as the CPP method OGRGeometry::transform.
816 : *
817 : * @param hGeom handle on the geometry to apply the transform to.
818 : * @param hTransform handle on the transformation to apply.
819 : *
820 : * @return OGRERR_NONE on success or an error code.
821 : */
822 :
823 11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
824 : OGRCoordinateTransformationH hTransform)
825 :
826 : {
827 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
828 :
829 22 : return OGRGeometry::FromHandle(hGeom)->transform(
830 11 : OGRCoordinateTransformation::FromHandle(hTransform));
831 : }
832 :
833 : /**
834 : * \fn int OGRGeometry::getDimension() const;
835 : *
836 : * \brief Get the dimension of this object.
837 : *
838 : * This method corresponds to the SFCOM IGeometry::GetDimension() method.
839 : * It indicates the dimension of the object, but does not indicate the
840 : * dimension of the underlying space (as indicated by
841 : * OGRGeometry::getCoordinateDimension()).
842 : *
843 : * This method is the same as the C function OGR_G_GetDimension().
844 : *
845 : * @return 0 for points, 1 for lines and 2 for surfaces.
846 : */
847 :
848 : /**
849 : * \brief Get the geometry type that conforms with ISO SQL/MM Part3
850 : *
851 : * @return the geometry type that conforms with ISO SQL/MM Part3
852 : */
853 720652 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
854 : {
855 720652 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
856 :
857 720652 : if (flags & OGR_G_3D)
858 214269 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
859 720652 : if (flags & OGR_G_MEASURED)
860 26025 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
861 :
862 720652 : 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 573474 : int OGRGeometry::getCoordinateDimension() const
963 :
964 : {
965 573474 : 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 30434 : int OGRGeometry::CoordinateDimension() const
981 :
982 : {
983 30434 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
984 7375 : return 4;
985 23059 : else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
986 6869 : return 3;
987 : else
988 16190 : 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 37776 : int OGR_G_Is3D(OGRGeometryH hGeom)
1056 :
1057 : {
1058 37776 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1059 :
1060 37776 : 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 40185 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1076 :
1077 : {
1078 40185 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1079 :
1080 40185 : 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 68170 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1103 :
1104 : {
1105 68170 : if (nNewDimension == 2)
1106 67678 : flags &= ~OGR_G_3D;
1107 : else
1108 492 : flags |= OGR_G_3D;
1109 68170 : 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 1618800 : bool OGRGeometry::set3D(bool bIs3D)
1126 :
1127 : {
1128 1618800 : if (bIs3D)
1129 1612740 : flags |= OGR_G_3D;
1130 : else
1131 6053 : flags &= ~OGR_G_3D;
1132 1618800 : 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 415491 : bool OGRGeometry::setMeasured(bool bIsMeasured)
1150 :
1151 : {
1152 415491 : if (bIsMeasured)
1153 137729 : flags |= OGR_G_MEASURED;
1154 : else
1155 277762 : flags &= ~OGR_G_MEASURED;
1156 415491 : 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 28138 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1293 :
1294 : {
1295 28138 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1296 :
1297 28138 : 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 56276 : return OGRGeometry::FromHandle(hGeom)->Equals(
1305 56276 : 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 13363 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
1434 :
1435 : {
1436 13363 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1437 :
1438 13363 : 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 278673 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1597 : unsigned char *pabyData,
1598 : OGRwkbVariant eWkbVariant) const
1599 : {
1600 278673 : OGRwkbExportOptions sOptions;
1601 278673 : sOptions.eByteOrder = eByteOrder;
1602 278673 : sOptions.eWkbVariant = eWkbVariant;
1603 557346 : 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 123857 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1794 : int *pbHasM, bool *pbIsEmpty)
1795 : {
1796 123857 : const char *pszInput = *ppszInput;
1797 :
1798 : /* -------------------------------------------------------------------- */
1799 : /* Clear existing Geoms. */
1800 : /* -------------------------------------------------------------------- */
1801 123857 : empty();
1802 123857 : *pbIsEmpty = false;
1803 :
1804 : /* -------------------------------------------------------------------- */
1805 : /* Read and verify the type keyword, and ensure it matches the */
1806 : /* actual type of this container. */
1807 : /* -------------------------------------------------------------------- */
1808 123857 : bool bHasM = false;
1809 123857 : bool bHasZ = false;
1810 123857 : bool bAlreadyGotDimension = false;
1811 :
1812 123857 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1813 123857 : pszInput = OGRWktReadToken(pszInput, szToken);
1814 123857 : 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 123857 : const size_t nTokenLen = strlen(szToken);
1819 123857 : 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 123846 : 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 123857 : if (!EQUAL(szToken, getGeometryName()))
1841 0 : return OGRERR_CORRUPT_DATA;
1842 :
1843 : /* -------------------------------------------------------------------- */
1844 : /* Check for Z, M or ZM */
1845 : /* -------------------------------------------------------------------- */
1846 123857 : if (!bAlreadyGotDimension)
1847 : {
1848 123840 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1849 123840 : if (EQUAL(szToken, "Z"))
1850 : {
1851 1418 : pszInput = pszNewInput;
1852 1418 : bHasZ = true;
1853 : }
1854 122422 : else if (EQUAL(szToken, "M"))
1855 : {
1856 353 : pszInput = pszNewInput;
1857 353 : bHasM = true;
1858 : }
1859 122069 : else if (EQUAL(szToken, "ZM"))
1860 : {
1861 494 : pszInput = pszNewInput;
1862 494 : bHasZ = true;
1863 494 : bHasM = true;
1864 : }
1865 : }
1866 123857 : *pbHasZ = bHasZ;
1867 123857 : *pbHasM = bHasM;
1868 :
1869 : /* -------------------------------------------------------------------- */
1870 : /* Check for EMPTY ... */
1871 : /* -------------------------------------------------------------------- */
1872 123857 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1873 123857 : 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 122279 : if (!EQUAL(szToken, "("))
1885 35 : return OGRERR_CORRUPT_DATA;
1886 :
1887 122244 : if (!bHasZ && !bHasM)
1888 : {
1889 : // Test for old-style XXXXXXXXX(EMPTY).
1890 120142 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1891 120142 : 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 122200 : *ppszInput = pszInput;
1914 :
1915 122200 : 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 14263 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1931 : {
1932 14263 : std::string s(" ");
1933 :
1934 14263 : if (variant == wkbVariantIso)
1935 : {
1936 9352 : if (flags & OGR_G_3D)
1937 1957 : s += "Z";
1938 9352 : if (flags & OGR_G_MEASURED)
1939 1200 : s += "M";
1940 : }
1941 14263 : if (s.size() > 1)
1942 2518 : s += " ";
1943 14263 : 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 8883 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1969 : {
1970 8883 : OGRWktOptions opts;
1971 8883 : opts.variant = variant;
1972 8883 : OGRErr err(OGRERR_NONE);
1973 :
1974 8883 : std::string wkt = exportToWkt(opts, &err);
1975 8883 : *ppszDstText = CPLStrdup(wkt.data());
1976 17766 : 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 2551 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
2004 :
2005 : {
2006 2551 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2007 :
2008 2551 : 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 5500 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
2036 :
2037 : {
2038 5500 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2039 :
2040 5500 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2041 5500 : 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 5794 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2075 :
2076 : {
2077 5794 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2078 :
2079 5794 : 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 18986 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2111 :
2112 : {
2113 18986 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2114 :
2115 18986 : 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 13574 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
2147 :
2148 : {
2149 13574 : VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
2150 :
2151 13574 : 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 2971 : bool OGRGeometry::IsValid(std::string *posReason) const
2285 :
2286 : {
2287 2971 : if (posReason)
2288 851 : posReason->clear();
2289 :
2290 2971 : if (IsSFCGALCompatible())
2291 : {
2292 : #ifndef HAVE_SFCGAL
2293 :
2294 : #ifdef HAVE_GEOS
2295 3 : if (wkbFlatten(getGeometryType()) == wkbTriangle)
2296 : {
2297 : // go on
2298 : }
2299 : else
2300 : #endif
2301 : {
2302 1 : CPLError(CE_Failure, CPLE_NotSupported,
2303 : "SFCGAL support not enabled.");
2304 1 : 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 2970 : 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 2970 : initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
2349 :
2350 : GEOSGeom hThisGeosGeom;
2351 2970 : 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 2119 : hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2382 : }
2383 :
2384 2970 : if (hThisGeosGeom != nullptr)
2385 : {
2386 2969 : 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 2119 : 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 2969 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2412 : }
2413 2970 : freeGEOSContext(hGEOSCtxt);
2414 :
2415 2970 : 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 22 : int OGR_G_IsValid(OGRGeometryH hGeom)
2444 :
2445 : {
2446 22 : VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
2447 :
2448 22 : 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 3267 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2647 : {
2648 3267 : OGRwkbGeometryType eType = wkbUnknown;
2649 3267 : bool bConvertTo3D = false;
2650 3267 : bool bIsMeasured = false;
2651 3267 : if (*pszGeomType != '\0')
2652 : {
2653 3261 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2654 3261 : 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 3261 : if (ch == 'z' || ch == 'Z')
2661 : {
2662 34 : bConvertTo3D = true;
2663 : }
2664 : }
2665 :
2666 3267 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2667 971 : eType = wkbPoint;
2668 2296 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2669 184 : eType = wkbLineString;
2670 2112 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2671 943 : eType = wkbPolygon;
2672 1169 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2673 24 : eType = wkbMultiPoint;
2674 1145 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2675 13 : eType = wkbMultiLineString;
2676 1132 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2677 82 : eType = wkbMultiPolygon;
2678 1050 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2679 4 : eType = wkbGeometryCollection;
2680 1046 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2681 356 : eType = wkbCircularString;
2682 690 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2683 0 : eType = wkbCompoundCurve;
2684 690 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2685 16 : eType = wkbCurvePolygon;
2686 674 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2687 2 : eType = wkbMultiCurve;
2688 672 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2689 0 : eType = wkbMultiSurface;
2690 672 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2691 0 : eType = wkbTriangle;
2692 672 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2693 1 : eType = wkbPolyhedralSurface;
2694 671 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2695 5 : eType = wkbTIN;
2696 666 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2697 3 : eType = wkbCurve;
2698 663 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2699 3 : eType = wkbSurface;
2700 : else
2701 660 : eType = wkbUnknown;
2702 :
2703 3267 : if (bConvertTo3D)
2704 34 : eType = wkbSetZ(eType);
2705 3267 : if (bIsMeasured)
2706 2 : eType = wkbSetM(eType);
2707 :
2708 3267 : 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 3159 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2726 : bool bAddZM, bool bSpaceBeforeZM)
2727 : {
2728 3159 : const char *pszRet = "";
2729 3159 : switch (wkbFlatten(eGeomType))
2730 : {
2731 1699 : case wkbUnknown:
2732 1699 : pszRet = "Geometry";
2733 1699 : break;
2734 562 : case wkbPoint:
2735 562 : pszRet = "Point";
2736 562 : break;
2737 159 : case wkbLineString:
2738 159 : pszRet = "LineString";
2739 159 : break;
2740 404 : case wkbPolygon:
2741 404 : pszRet = "Polygon";
2742 404 : break;
2743 48 : case wkbMultiPoint:
2744 48 : pszRet = "MultiPoint";
2745 48 : break;
2746 56 : case wkbMultiLineString:
2747 56 : pszRet = "MultiLineString";
2748 56 : break;
2749 74 : case wkbMultiPolygon:
2750 74 : pszRet = "MultiPolygon";
2751 74 : 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 3159 : if (bAddZM)
2789 : {
2790 71 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2791 71 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2792 71 : if (bHasZ || bHasM)
2793 : {
2794 10 : if (bSpaceBeforeZM)
2795 1 : pszRet = CPLSPrintf("%s ", pszRet);
2796 10 : if (bHasZ)
2797 9 : pszRet = CPLSPrintf("%sZ", pszRet);
2798 10 : if (bHasM)
2799 5 : pszRet = CPLSPrintf("%sM", pszRet);
2800 : }
2801 : }
2802 3159 : if (!bCamelCase)
2803 3087 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2804 3159 : 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 393 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2824 :
2825 : {
2826 393 : bool b3D = wkbHasZ(eType);
2827 393 : bool bMeasured = wkbHasM(eType);
2828 :
2829 393 : 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 37 : case wkbLineString:
2852 37 : if (b3D && bMeasured)
2853 0 : return "3D Measured Line String";
2854 37 : else if (b3D)
2855 9 : return "3D Line String";
2856 28 : else if (bMeasured)
2857 0 : return "Measured Line String";
2858 : else
2859 28 : return "Line String";
2860 :
2861 62 : case wkbPolygon:
2862 62 : if (b3D && bMeasured)
2863 0 : return "3D Measured Polygon";
2864 62 : else if (b3D)
2865 8 : return "3D Polygon";
2866 54 : else if (bMeasured)
2867 0 : return "Measured Polygon";
2868 : else
2869 54 : return "Polygon";
2870 :
2871 19 : case wkbMultiPoint:
2872 19 : if (b3D && bMeasured)
2873 0 : return "3D Measured Multi Point";
2874 19 : else if (b3D)
2875 9 : return "3D Multi Point";
2876 10 : else if (bMeasured)
2877 0 : return "Measured Multi Point";
2878 : else
2879 10 : return "Multi Point";
2880 :
2881 14 : case wkbMultiLineString:
2882 14 : if (b3D && bMeasured)
2883 0 : return "3D Measured Multi Line String";
2884 14 : else if (b3D)
2885 6 : return "3D Multi Line String";
2886 8 : else if (bMeasured)
2887 0 : return "Measured Multi Line String";
2888 : else
2889 8 : return "Multi Line String";
2890 :
2891 19 : case wkbMultiPolygon:
2892 19 : if (b3D && bMeasured)
2893 0 : return "3D Measured Multi Polygon";
2894 19 : else if (b3D)
2895 8 : return "3D Multi Polygon";
2896 11 : else if (bMeasured)
2897 0 : return "Measured Multi Polygon";
2898 : else
2899 11 : 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 575 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
3081 : OGRwkbGeometryType eExtra,
3082 : int bAllowPromotingToCurves)
3083 :
3084 : {
3085 575 : OGRwkbGeometryType eFMain = wkbFlatten(eMain);
3086 575 : OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
3087 :
3088 575 : const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
3089 575 : const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
3090 :
3091 575 : if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
3092 17 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3093 :
3094 558 : if (eFMain == wkbNone)
3095 2 : return eExtra;
3096 :
3097 556 : if (eFExtra == wkbNone)
3098 0 : return eMain;
3099 :
3100 556 : if (eFMain == eFExtra)
3101 : {
3102 539 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3103 : }
3104 :
3105 17 : if (bAllowPromotingToCurves)
3106 : {
3107 17 : if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
3108 4 : return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
3109 :
3110 13 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3111 3 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3112 :
3113 10 : if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3114 6 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3115 : }
3116 :
3117 : // One is subclass of the other one
3118 4 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3119 : {
3120 0 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3121 : }
3122 4 : else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3123 : {
3124 0 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3125 : }
3126 :
3127 : // Nothing apparently in common.
3128 4 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3129 : }
3130 :
3131 : /**
3132 : * \fn void OGRGeometry::flattenTo2D();
3133 : *
3134 : * \brief Convert geometry to strictly 2D.
3135 : * In a sense this converts all Z coordinates
3136 : * to 0.0.
3137 : *
3138 : * This method is the same as the C function OGR_G_FlattenTo2D().
3139 : */
3140 :
3141 : /************************************************************************/
3142 : /* OGR_G_FlattenTo2D() */
3143 : /************************************************************************/
3144 : /**
3145 : * \brief Convert geometry to strictly 2D.
3146 : * In a sense this converts all Z coordinates
3147 : * to 0.0.
3148 : *
3149 : * This function is the same as the CPP method OGRGeometry::flattenTo2D().
3150 : *
3151 : * @param hGeom handle on the geometry to convert.
3152 : */
3153 :
3154 31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
3155 :
3156 : {
3157 31 : OGRGeometry::FromHandle(hGeom)->flattenTo2D();
3158 31 : }
3159 :
3160 : /************************************************************************/
3161 : /* exportToGML() */
3162 : /************************************************************************/
3163 :
3164 : /**
3165 : * \fn char *OGRGeometry::exportToGML( const char* const *
3166 : * papszOptions = NULL ) const;
3167 : *
3168 : * \brief Convert a geometry into GML format.
3169 : *
3170 : * The GML geometry is expressed directly in terms of GML basic data
3171 : * types assuming the this is available in the gml namespace. The returned
3172 : * string should be freed with CPLFree() when no longer required.
3173 : *
3174 : * The supported options are :
3175 : * <ul>
3176 : * <li> FORMAT=GML2/GML3/GML32.
3177 : * If not set, it will default to GML 2.1.2 output.
3178 : * </li>
3179 : * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
3180 : * To use gml:Curve element for linestrings.
3181 : * Otherwise gml:LineString will be used .
3182 : * </li>
3183 : * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
3184 : * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES.
3185 : * If YES, SRS with EPSG authority will be written with the
3186 : * "urn:ogc:def:crs:EPSG::" prefix.
3187 : * In the case the SRS should be treated as lat/long or
3188 : * northing/easting, then the function will take care of coordinate order
3189 : * swapping if the data axis to CRS axis mapping indicates it.
3190 : * If set to NO, SRS with EPSG authority will be written with the "EPSG:"
3191 : * prefix, even if they are in lat/long order.
3192 : * </li>
3193 : * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
3194 : * Defaults to OGC_URN. If SHORT, then srsName will be in
3195 : * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
3196 : * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
3197 : * then srsName will be in the form
3198 : * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
3199 : * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
3200 : * or northing/easting, then the function will take care of coordinate
3201 : * order swapping if the data axis to CRS axis mapping indicates it.
3202 : * </li>
3203 : * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
3204 : * top-level geometry element with the provided value.
3205 : * Required for GML 3.2 compatibility.
3206 : * </li>
3207 : * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
3208 : * FORMAT=GML3/GML32) Default to POSLIST.
3209 : * For 2.5D geometries, define the location where to attach the
3210 : * srsDimension attribute.
3211 : * There are diverging implementations. Some put in on the
3212 : * <gml:posList> element, other on the top geometry element.
3213 : * </li>
3214 : * <li> NAMESPACE_DECL=YES/NO. If set to YES,
3215 : * xmlns:gml="http://www.opengis.net/gml" will be added to the root node
3216 : * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
3217 : * </li>
3218 : * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
3219 : * Resolution for the coordinate precision of the X and Y coordinates.
3220 : * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
3221 : * to 5 decimal digits. 0 for the default behavior.
3222 : * </li>
3223 : * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
3224 : * Resolution for the coordinate precision of the Z coordinates.
3225 : * Expressed in the units of the Z axis of the SRS.
3226 : * 0 for the default behavior.
3227 : * </li>
3228 : * </ul>
3229 : *
3230 : * This method is the same as the C function OGR_G_ExportToGMLEx().
3231 : *
3232 : * @param papszOptions NULL-terminated list of options.
3233 : * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
3234 : */
3235 :
3236 251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
3237 : {
3238 251 : return OGR_G_ExportToGMLEx(
3239 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3240 251 : const_cast<char **>(papszOptions));
3241 : }
3242 :
3243 : /************************************************************************/
3244 : /* exportToKML() */
3245 : /************************************************************************/
3246 :
3247 : /**
3248 : * \fn char *OGRGeometry::exportToKML() const;
3249 : *
3250 : * \brief Convert a geometry into KML format.
3251 : *
3252 : * The returned string should be freed with CPLFree() when no longer required.
3253 : *
3254 : * This method is the same as the C function OGR_G_ExportToKML().
3255 : *
3256 : * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
3257 : */
3258 :
3259 0 : char *OGRGeometry::exportToKML() const
3260 : {
3261 0 : return OGR_G_ExportToKML(
3262 0 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
3263 : }
3264 :
3265 : /************************************************************************/
3266 : /* exportToJson() */
3267 : /************************************************************************/
3268 :
3269 : /**
3270 : * \fn char *OGRGeometry::exportToJson() const;
3271 : *
3272 : * \brief Convert a geometry into GeoJSON format.
3273 : *
3274 : * The returned string should be freed with CPLFree() when no longer required.
3275 : *
3276 : * The following options are supported :
3277 : * <ul>
3278 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
3279 : * (added in GDAL 3.9)</li>
3280 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
3281 : * (added in GDAL 3.9)</li>
3282 : * </ul>
3283 : *
3284 : * This method is the same as the C function OGR_G_ExportToJson().
3285 : *
3286 : * @param papszOptions Null terminated list of options, or null (added in 3.9)
3287 : * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
3288 : */
3289 :
3290 43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
3291 : {
3292 43 : OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3293 43 : return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3294 43 : const_cast<char **>(papszOptions));
3295 : }
3296 :
3297 : /************************************************************************/
3298 : /* OGRSetGenerate_DB2_V72_BYTE_ORDER() */
3299 : /************************************************************************/
3300 :
3301 : /**
3302 : * \brief Special entry point to enable the hack for generating DB2 V7.2 style
3303 : * WKB.
3304 : *
3305 : * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
3306 : * order in WKB. This entry point is used to turn on or off the generation of
3307 : * such WKB.
3308 : */
3309 4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
3310 :
3311 : {
3312 : #if defined(HACK_FOR_IBM_DB2_V72)
3313 4 : OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
3314 4 : return OGRERR_NONE;
3315 : #else
3316 : if (bGenerate_DB2_V72_BYTE_ORDER)
3317 : return OGRERR_FAILURE;
3318 : else
3319 : return OGRERR_NONE;
3320 : #endif
3321 : }
3322 :
3323 : /************************************************************************/
3324 : /* OGRGetGenerate_DB2_V72_BYTE_ORDER() */
3325 : /* */
3326 : /* This is a special entry point to get the value of static flag */
3327 : /* OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER. */
3328 : /************************************************************************/
3329 0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
3330 : {
3331 0 : return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
3332 : }
3333 :
3334 : /************************************************************************/
3335 : /* createGEOSContext() */
3336 : /************************************************************************/
3337 :
3338 : /** Create a new GEOS context.
3339 : * @return a new GEOS context (to be freed with freeGEOSContext())
3340 : */
3341 86694 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
3342 : {
3343 : #ifndef HAVE_GEOS
3344 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3345 : return nullptr;
3346 : #else
3347 86694 : return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
3348 : #endif
3349 : }
3350 :
3351 : /************************************************************************/
3352 : /* freeGEOSContext() */
3353 : /************************************************************************/
3354 :
3355 : /** Destroy a GEOS context.
3356 : * @param hGEOSCtxt GEOS context
3357 : */
3358 84014 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
3359 : {
3360 : (void)hGEOSCtxt;
3361 : #ifdef HAVE_GEOS
3362 84014 : if (hGEOSCtxt != nullptr)
3363 : {
3364 84014 : finishGEOS_r(hGEOSCtxt);
3365 : }
3366 : #endif
3367 84014 : }
3368 : #ifdef HAVE_GEOS
3369 :
3370 : /************************************************************************/
3371 : /* canConvertToMultiPolygon() */
3372 : /************************************************************************/
3373 :
3374 155 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
3375 : {
3376 686 : for (const auto *poSubGeom : *poGC)
3377 : {
3378 : const OGRwkbGeometryType eSubGeomType =
3379 613 : wkbFlatten(poSubGeom->getGeometryType());
3380 613 : if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
3381 505 : eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
3382 : {
3383 82 : return false;
3384 : }
3385 : }
3386 :
3387 73 : return true;
3388 : }
3389 :
3390 : /************************************************************************/
3391 : /* GEOSWarningSilencer */
3392 : /************************************************************************/
3393 :
3394 : /** Class that can be used to silence GEOS messages while in-scope. */
3395 : class GEOSWarningSilencer
3396 : {
3397 : public:
3398 152 : explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
3399 152 : : m_poContext(poContext)
3400 : {
3401 152 : GEOSContext_setErrorHandler_r(m_poContext, nullptr);
3402 152 : GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
3403 152 : }
3404 :
3405 152 : ~GEOSWarningSilencer()
3406 152 : {
3407 152 : GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
3408 152 : GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
3409 152 : }
3410 :
3411 : CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
3412 :
3413 : private:
3414 : GEOSContextHandle_t m_poContext{nullptr};
3415 : };
3416 :
3417 : /************************************************************************/
3418 : /* repairForGEOS() */
3419 : /************************************************************************/
3420 :
3421 : /** Modify an OGRGeometry so that it can be converted into GEOS.
3422 : * Modifications include closing unclosed rings and adding redundant vertices
3423 : * to reach minimum point limits in GEOS.
3424 : *
3425 : * It is assumed that the input is a non-curved type that can be
3426 : * represented in GEOS.
3427 : *
3428 : * @param poGeom the geometry to modify
3429 : * @return an OGRGeometry that can be converted to GEOS using WKB
3430 : */
3431 22 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
3432 : {
3433 : #if GEOS_VERSION_MAJOR >= 3 || \
3434 : (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
3435 : static constexpr int MIN_RING_POINTS = 3;
3436 : #else
3437 : static constexpr int MIN_RING_POINTS = 4;
3438 : #endif
3439 :
3440 22 : const auto eType = wkbFlatten(poGeom->getGeometryType());
3441 :
3442 22 : if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
3443 : {
3444 4 : std::unique_ptr<OGRGeometryCollection> poRet;
3445 4 : if (eType == wkbGeometryCollection)
3446 : {
3447 2 : poRet = std::make_unique<OGRGeometryCollection>();
3448 : }
3449 2 : else if (eType == wkbMultiPolygon)
3450 : {
3451 2 : poRet = std::make_unique<OGRMultiPolygon>();
3452 : }
3453 0 : else if (eType == wkbMultiLineString)
3454 : {
3455 0 : poRet = std::make_unique<OGRMultiLineString>();
3456 : }
3457 0 : else if (eType == wkbMultiPoint)
3458 : {
3459 0 : poRet = std::make_unique<OGRMultiPoint>();
3460 : }
3461 : else
3462 : {
3463 0 : CPLError(CE_Failure, CPLE_AppDefined,
3464 : "Unexpected geometry type: %s",
3465 : OGRGeometryTypeToName(eType));
3466 0 : return nullptr;
3467 : }
3468 :
3469 4 : const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
3470 12 : for (const auto *poSubGeomIn : *poColl)
3471 : {
3472 8 : std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
3473 8 : poRet->addGeometry(std::move(poSubGeom));
3474 : }
3475 :
3476 4 : return poRet;
3477 : }
3478 :
3479 18 : if (eType == wkbPoint)
3480 : {
3481 0 : return std::unique_ptr<OGRGeometry>(poGeom->clone());
3482 : }
3483 18 : if (eType == wkbLineString)
3484 : {
3485 : std::unique_ptr<OGRLineString> poLineString(
3486 4 : poGeom->toLineString()->clone());
3487 2 : if (poLineString->getNumPoints() == 1)
3488 : {
3489 4 : OGRPoint oPoint;
3490 2 : poLineString->getPoint(0, &oPoint);
3491 2 : poLineString->addPoint(&oPoint);
3492 : }
3493 2 : return poLineString;
3494 : }
3495 16 : if (eType == wkbPolygon)
3496 : {
3497 32 : std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
3498 16 : poPolygon->closeRings();
3499 :
3500 : // make sure rings have enough points
3501 34 : for (auto *poRing : *poPolygon)
3502 : {
3503 22 : while (poRing->getNumPoints() < MIN_RING_POINTS)
3504 : {
3505 8 : OGRPoint oPoint;
3506 4 : poRing->getPoint(0, &oPoint);
3507 4 : poRing->addPoint(&oPoint);
3508 : }
3509 : }
3510 :
3511 16 : return poPolygon;
3512 : }
3513 :
3514 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
3515 : OGRGeometryTypeToName(eType));
3516 0 : return nullptr;
3517 : }
3518 :
3519 : /************************************************************************/
3520 : /* convertToGEOSGeom() */
3521 : /************************************************************************/
3522 :
3523 243470 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3524 : const OGRGeometry *poGeom)
3525 : {
3526 243470 : GEOSGeom hGeom = nullptr;
3527 243470 : const size_t nDataSize = poGeom->WkbSize();
3528 : unsigned char *pabyData =
3529 243470 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3530 : #if GEOS_VERSION_MAJOR > 3 || \
3531 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3532 243470 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3533 : #else
3534 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3535 : #endif
3536 243470 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3537 : {
3538 242450 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3539 : }
3540 243470 : CPLFree(pabyData);
3541 :
3542 243470 : return hGeom;
3543 : }
3544 : #endif
3545 :
3546 : /************************************************************************/
3547 : /* exportToGEOS() */
3548 : /************************************************************************/
3549 :
3550 : /** Returns a GEOSGeom object corresponding to the geometry.
3551 : *
3552 : * @param hGEOSCtxt GEOS context
3553 : * @param bRemoveEmptyParts Whether empty parts of the geometry should be
3554 : * removed before exporting to GEOS (GDAL >= 3.10)
3555 : * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
3556 : * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
3557 : * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
3558 : * @return a GEOSGeom object corresponding to the geometry (to be freed with
3559 : * GEOSGeom_destroy_r()), or NULL in case of error
3560 : */
3561 243456 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
3562 : bool bRemoveEmptyParts,
3563 : bool bAddPointsIfNeeded) const
3564 : {
3565 : (void)hGEOSCtxt;
3566 : (void)bRemoveEmptyParts;
3567 : (void)bAddPointsIfNeeded;
3568 :
3569 : #ifndef HAVE_GEOS
3570 :
3571 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3572 : return nullptr;
3573 :
3574 : #else
3575 :
3576 243456 : if (hGEOSCtxt == nullptr)
3577 0 : return nullptr;
3578 :
3579 243456 : const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
3580 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3581 : // POINT EMPTY is exported to WKB as if it were POINT(0 0),
3582 : // so that particular case is necessary.
3583 : if (eType == wkbPoint && IsEmpty())
3584 : {
3585 : return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
3586 : }
3587 : #endif
3588 :
3589 243456 : GEOSGeom hGeom = nullptr;
3590 :
3591 243456 : std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
3592 243456 : const OGRGeometry *poGeosInput = this;
3593 :
3594 243456 : const bool bHasZ = poGeosInput->Is3D();
3595 243456 : bool bHasM = poGeosInput->IsMeasured();
3596 :
3597 243456 : if (poGeosInput->hasCurveGeometry())
3598 : {
3599 864 : poModifiedInput.reset(poGeosInput->getLinearGeometry());
3600 864 : poGeosInput = poModifiedInput.get();
3601 : }
3602 :
3603 243456 : if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
3604 : {
3605 1 : if (!poModifiedInput)
3606 : {
3607 1 : poModifiedInput.reset(poGeosInput->clone());
3608 1 : poGeosInput = poModifiedInput.get();
3609 : }
3610 1 : poModifiedInput->removeEmptyParts();
3611 : }
3612 :
3613 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3614 : // GEOS < 3.12 doesn't support M dimension
3615 : if (bHasM)
3616 : {
3617 : if (!poModifiedInput)
3618 : {
3619 : poModifiedInput.reset(poGeosInput->clone());
3620 : poGeosInput = poModifiedInput.get();
3621 : }
3622 : poModifiedInput->setMeasured(false);
3623 : bHasM = false;
3624 : }
3625 : #endif
3626 :
3627 243456 : if (eType == wkbTriangle)
3628 : {
3629 : poModifiedInput =
3630 98 : std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
3631 98 : poGeosInput = poModifiedInput.get();
3632 : }
3633 243358 : else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3634 : {
3635 844 : if (!poModifiedInput)
3636 : {
3637 844 : poModifiedInput.reset(poGeosInput->clone());
3638 : }
3639 :
3640 2532 : poModifiedInput = OGRGeometryFactory::forceTo(
3641 844 : std::move(poModifiedInput),
3642 844 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
3643 844 : poGeosInput = poModifiedInput.get();
3644 : }
3645 242669 : else if (eType == wkbGeometryCollection &&
3646 155 : CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
3647 : {
3648 73 : if (!poModifiedInput)
3649 : {
3650 71 : poModifiedInput.reset(poGeosInput->clone());
3651 : }
3652 :
3653 : // Force into a MultiPolygon, then back to a GeometryCollection.
3654 : // This gets rid of fancy types like TIN and PolyhedralSurface that
3655 : // GEOS doesn't understand and flattens nested collections.
3656 219 : poModifiedInput = OGRGeometryFactory::forceTo(
3657 73 : std::move(poModifiedInput),
3658 73 : OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
3659 219 : poModifiedInput = OGRGeometryFactory::forceTo(
3660 73 : std::move(poModifiedInput),
3661 73 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
3662 :
3663 73 : poGeosInput = poModifiedInput.get();
3664 : }
3665 :
3666 : {
3667 : // Rather than check for conditions that would prevent conversion to
3668 : // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
3669 : // conversion as-is. If the conversion fails, we don't want any
3670 : // warnings emitted; we'll repair the input and try again.
3671 0 : std::optional<GEOSWarningSilencer> oSilencer;
3672 243456 : if (bAddPointsIfNeeded)
3673 : {
3674 152 : oSilencer.emplace(hGEOSCtxt);
3675 : }
3676 :
3677 243456 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3678 : }
3679 :
3680 243456 : if (hGeom == nullptr && bAddPointsIfNeeded)
3681 : {
3682 14 : poModifiedInput = repairForGEOS(poGeosInput);
3683 14 : poGeosInput = poModifiedInput.get();
3684 :
3685 14 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3686 : }
3687 :
3688 243456 : return hGeom;
3689 :
3690 : #endif // HAVE_GEOS
3691 : }
3692 :
3693 : /************************************************************************/
3694 : /* hasCurveGeometry() */
3695 : /************************************************************************/
3696 :
3697 : /**
3698 : * \brief Returns if this geometry is or has curve geometry.
3699 : *
3700 : * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
3701 : * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
3702 : *
3703 : * If bLookForNonLinear is set to TRUE, it will be actually looked if
3704 : * the geometry or its subgeometries are or contain a non-linear
3705 : * geometry in them. In which case, if the method returns TRUE, it
3706 : * means that getLinearGeometry() would return an approximate version
3707 : * of the geometry. Otherwise, getLinearGeometry() would do a
3708 : * conversion, but with just converting container type, like
3709 : * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
3710 : * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
3711 : * conversion.
3712 : *
3713 : * This method is the same as the C function OGR_G_HasCurveGeometry().
3714 : *
3715 : * @param bLookForNonLinear set it to TRUE to check if the geometry is
3716 : * or contains a CIRCULARSTRING.
3717 : *
3718 : * @return TRUE if this geometry is or has curve geometry.
3719 : *
3720 : */
3721 :
3722 280751 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3723 : {
3724 280751 : return FALSE;
3725 : }
3726 :
3727 : /************************************************************************/
3728 : /* getLinearGeometry() */
3729 : /************************************************************************/
3730 :
3731 : /**
3732 : * \brief Return, possibly approximate, non-curve version of this geometry.
3733 : *
3734 : * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
3735 : * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
3736 : *
3737 : * The ownership of the returned geometry belongs to the caller.
3738 : *
3739 : * The reverse method is OGRGeometry::getCurveGeometry().
3740 : *
3741 : * This method is the same as the C function OGR_G_GetLinearGeometry().
3742 : *
3743 : * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
3744 : * arc, zero to use the default setting.
3745 : * @param papszOptions options as a null-terminated list of strings.
3746 : * See OGRGeometryFactory::curveToLineString() for
3747 : * valid options.
3748 : *
3749 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3750 : *
3751 : */
3752 :
3753 : OGRGeometry *
3754 88 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3755 : CPL_UNUSED const char *const *papszOptions) const
3756 : {
3757 88 : return clone();
3758 : }
3759 :
3760 : /************************************************************************/
3761 : /* getCurveGeometry() */
3762 : /************************************************************************/
3763 :
3764 : /**
3765 : * \brief Return curve version of this geometry.
3766 : *
3767 : * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
3768 : * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
3769 : * curve geometries.
3770 : *
3771 : * If the geometry has no curve portion, the returned geometry will be a clone
3772 : * of it.
3773 : *
3774 : * The ownership of the returned geometry belongs to the caller.
3775 : *
3776 : * The reverse method is OGRGeometry::getLinearGeometry().
3777 : *
3778 : * This function is the same as C function OGR_G_GetCurveGeometry().
3779 : *
3780 : * @param papszOptions options as a null-terminated list of strings.
3781 : * Unused for now. Must be set to NULL.
3782 : *
3783 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3784 : *
3785 : */
3786 :
3787 : OGRGeometry *
3788 5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
3789 : {
3790 5 : return clone();
3791 : }
3792 :
3793 : /************************************************************************/
3794 : /* Distance() */
3795 : /************************************************************************/
3796 :
3797 : /**
3798 : * \brief Compute distance between two geometries.
3799 : *
3800 : * Returns the shortest distance between the two geometries. The distance is
3801 : * expressed into the same unit as the coordinates of the geometries.
3802 : *
3803 : * This method is the same as the C function OGR_G_Distance().
3804 : *
3805 : * This method is built on the GEOS library, check it for the definition
3806 : * of the geometry operation.
3807 : * If OGR is built without the GEOS library, this method will always fail,
3808 : * issuing a CPLE_NotSupported error.
3809 : *
3810 : * @param poOtherGeom the other geometry to compare against.
3811 : *
3812 : * @return the distance between the geometries or -1 if an error occurs.
3813 : */
3814 :
3815 25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
3816 :
3817 : {
3818 25 : if (nullptr == poOtherGeom)
3819 : {
3820 0 : CPLDebug("OGR",
3821 : "OGRGeometry::Distance called with NULL geometry pointer");
3822 0 : return -1.0;
3823 : }
3824 :
3825 25 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
3826 : {
3827 : #ifndef HAVE_SFCGAL
3828 :
3829 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3830 0 : return -1.0;
3831 :
3832 : #else
3833 :
3834 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3835 : if (poThis == nullptr)
3836 : return -1.0;
3837 :
3838 : sfcgal_geometry_t *poOther =
3839 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3840 : if (poOther == nullptr)
3841 : {
3842 : sfcgal_geometry_delete(poThis);
3843 : return -1.0;
3844 : }
3845 :
3846 : const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
3847 :
3848 : sfcgal_geometry_delete(poThis);
3849 : sfcgal_geometry_delete(poOther);
3850 :
3851 : return dfDistance > 0.0 ? dfDistance : -1.0;
3852 :
3853 : #endif
3854 : }
3855 :
3856 : else
3857 : {
3858 : #ifndef HAVE_GEOS
3859 :
3860 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3861 : return -1.0;
3862 :
3863 : #else
3864 :
3865 25 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3866 : // GEOSGeom is a pointer
3867 25 : GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
3868 25 : GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
3869 :
3870 25 : int bIsErr = 0;
3871 25 : double dfDistance = 0.0;
3872 :
3873 25 : if (hThis != nullptr && hOther != nullptr)
3874 : {
3875 25 : bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
3876 : }
3877 :
3878 25 : GEOSGeom_destroy_r(hGEOSCtxt, hThis);
3879 25 : GEOSGeom_destroy_r(hGEOSCtxt, hOther);
3880 25 : freeGEOSContext(hGEOSCtxt);
3881 :
3882 25 : if (bIsErr > 0)
3883 : {
3884 25 : return dfDistance;
3885 : }
3886 :
3887 : /* Calculations error */
3888 0 : return -1.0;
3889 :
3890 : #endif /* HAVE_GEOS */
3891 : }
3892 : }
3893 :
3894 : /************************************************************************/
3895 : /* OGR_G_Distance() */
3896 : /************************************************************************/
3897 : /**
3898 : * \brief Compute distance between two geometries.
3899 : *
3900 : * Returns the shortest distance between the two geometries. The distance is
3901 : * expressed into the same unit as the coordinates of the geometries.
3902 : *
3903 : * This function is the same as the C++ method OGRGeometry::Distance().
3904 : *
3905 : * This function is built on the GEOS library, check it for the definition
3906 : * of the geometry operation.
3907 : * If OGR is built without the GEOS library, this function will always fail,
3908 : * issuing a CPLE_NotSupported error.
3909 : *
3910 : * @param hFirst the first geometry to compare against.
3911 : * @param hOther the other geometry to compare against.
3912 : *
3913 : * @return the distance between the geometries or -1 if an error occurs.
3914 : */
3915 :
3916 2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
3917 :
3918 : {
3919 2 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
3920 :
3921 4 : return OGRGeometry::FromHandle(hFirst)->Distance(
3922 4 : OGRGeometry::FromHandle(hOther));
3923 : }
3924 :
3925 : /************************************************************************/
3926 : /* Distance3D() */
3927 : /************************************************************************/
3928 :
3929 : /**
3930 : * \brief Returns the 3D distance between two geometries
3931 : *
3932 : * The distance is expressed into the same unit as the coordinates of the
3933 : * geometries.
3934 : *
3935 : * This method is built on the SFCGAL library, check it for the definition
3936 : * of the geometry operation.
3937 : * If OGR is built without the SFCGAL library, this method will always return
3938 : * -1.0
3939 : *
3940 : * This function is the same as the C function OGR_G_Distance3D().
3941 : *
3942 : * @return distance between the two geometries
3943 : */
3944 :
3945 1 : double OGRGeometry::Distance3D(
3946 : UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
3947 : {
3948 1 : if (poOtherGeom == nullptr)
3949 : {
3950 0 : CPLDebug("OGR",
3951 : "OGRTriangle::Distance3D called with NULL geometry pointer");
3952 0 : return -1.0;
3953 : }
3954 :
3955 1 : if (!(poOtherGeom->Is3D() && Is3D()))
3956 : {
3957 0 : CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
3958 : "geometry(geometries)");
3959 0 : return -1.0;
3960 : }
3961 :
3962 : #ifndef HAVE_SFCGAL
3963 :
3964 1 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3965 1 : return -1.0;
3966 :
3967 : #else
3968 :
3969 : sfcgal_init();
3970 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3971 : if (poThis == nullptr)
3972 : return -1.0;
3973 :
3974 : sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3975 : if (poOther == nullptr)
3976 : {
3977 : sfcgal_geometry_delete(poThis);
3978 : return -1.0;
3979 : }
3980 :
3981 : const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
3982 :
3983 : sfcgal_geometry_delete(poThis);
3984 : sfcgal_geometry_delete(poOther);
3985 :
3986 : return dfDistance > 0 ? dfDistance : -1.0;
3987 :
3988 : #endif
3989 : }
3990 :
3991 : /************************************************************************/
3992 : /* OGR_G_Distance3D() */
3993 : /************************************************************************/
3994 : /**
3995 : * \brief Returns the 3D distance between two geometries
3996 : *
3997 : * The distance is expressed into the same unit as the coordinates of the
3998 : * geometries.
3999 : *
4000 : * This method is built on the SFCGAL library, check it for the definition
4001 : * of the geometry operation.
4002 : * If OGR is built without the SFCGAL library, this method will always return
4003 : * -1.0
4004 : *
4005 : * This function is the same as the C++ method OGRGeometry::Distance3D().
4006 : *
4007 : * @param hFirst the first geometry to compare against.
4008 : * @param hOther the other geometry to compare against.
4009 : * @return distance between the two geometries
4010 : *
4011 : * @return the distance between the geometries or -1 if an error occurs.
4012 : */
4013 :
4014 1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
4015 :
4016 : {
4017 1 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
4018 :
4019 2 : return OGRGeometry::FromHandle(hFirst)->Distance3D(
4020 2 : OGRGeometry::FromHandle(hOther));
4021 : }
4022 :
4023 : /************************************************************************/
4024 : /* OGRGeometryRebuildCurves() */
4025 : /************************************************************************/
4026 :
4027 : #ifdef HAVE_GEOS
4028 5087 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
4029 : const OGRGeometry *poOtherGeom,
4030 : OGRGeometry *poOGRProduct)
4031 : {
4032 10174 : if (poOGRProduct != nullptr &&
4033 10091 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
4034 5004 : (poGeom->hasCurveGeometry(true) ||
4035 3849 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
4036 : {
4037 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
4038 8 : delete poOGRProduct;
4039 8 : return poCurveGeom;
4040 : }
4041 5079 : return poOGRProduct;
4042 : }
4043 :
4044 : /************************************************************************/
4045 : /* BuildGeometryFromGEOS() */
4046 : /************************************************************************/
4047 :
4048 4937 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
4049 : GEOSGeom hGeosProduct,
4050 : const OGRGeometry *poSelf,
4051 : const OGRGeometry *poOtherGeom)
4052 : {
4053 4937 : OGRGeometry *poOGRProduct = nullptr;
4054 4937 : if (hGeosProduct != nullptr)
4055 : {
4056 : poOGRProduct =
4057 4935 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
4058 4935 : if (poOGRProduct != nullptr &&
4059 12682 : poSelf->getSpatialReference() != nullptr &&
4060 2812 : (poOtherGeom == nullptr ||
4061 2812 : (poOtherGeom->getSpatialReference() != nullptr &&
4062 2677 : poOtherGeom->getSpatialReference()->IsSame(
4063 : poSelf->getSpatialReference()))))
4064 : {
4065 2733 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
4066 : }
4067 : poOGRProduct =
4068 4935 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
4069 4935 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
4070 : }
4071 4937 : return poOGRProduct;
4072 : }
4073 :
4074 : /************************************************************************/
4075 : /* BuildGeometryFromTwoGeoms() */
4076 : /************************************************************************/
4077 :
4078 3920 : static OGRGeometry *BuildGeometryFromTwoGeoms(
4079 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4080 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
4081 : const GEOSGeometry *,
4082 : const GEOSGeometry *))
4083 : {
4084 3920 : OGRGeometry *poOGRProduct = nullptr;
4085 :
4086 3920 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4087 3920 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4088 3920 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4089 3920 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4090 : {
4091 : GEOSGeom hGeosProduct =
4092 3920 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
4093 :
4094 : poOGRProduct =
4095 3920 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
4096 : }
4097 3920 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4098 3920 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4099 3920 : poSelf->freeGEOSContext(hGEOSCtxt);
4100 :
4101 3920 : return poOGRProduct;
4102 : }
4103 :
4104 : /************************************************************************/
4105 : /* OGRGEOSBooleanPredicate() */
4106 : /************************************************************************/
4107 :
4108 22773 : static bool OGRGEOSBooleanPredicate(
4109 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4110 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
4111 : const GEOSGeometry *))
4112 : {
4113 22773 : bool bResult = false;
4114 :
4115 22773 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4116 22773 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4117 22773 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4118 22773 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4119 : {
4120 22214 : bResult =
4121 22214 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
4122 : }
4123 22773 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4124 22773 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4125 22773 : poSelf->freeGEOSContext(hGEOSCtxt);
4126 :
4127 22773 : return bResult;
4128 : }
4129 :
4130 : #endif // HAVE_GEOS
4131 :
4132 : /************************************************************************/
4133 : /* MakeValid() */
4134 : /************************************************************************/
4135 :
4136 : /**
4137 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4138 : *
4139 : * Already-valid geometries are cloned without further intervention
4140 : * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
4141 : * may be subject to non-significant transformations, such as duplicated point
4142 : * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
4143 : * geometry collections could be returned a single geometry. GDAL 3.10
4144 : * returns the same type of geometry).
4145 : *
4146 : * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
4147 : * post-processing step is often desired.
4148 : *
4149 : * This method is the same as the C function OGR_G_MakeValid().
4150 : *
4151 : * This function is built on the GEOS >= 3.8 library, check it for the
4152 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4153 : * library, this function will return a clone of the input geometry if it is
4154 : * valid, or NULL if it is invalid.
4155 : *
4156 : * Certain geometries cannot be read using GEOS, for example if Polygon rings
4157 : * are not closed or do not contain enough vertices. If a geometry cannot be
4158 : * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
4159 : * attempt to modify these geometries such that they can be read and
4160 : * repaired by GEOS.
4161 : *
4162 : * @param papszOptions NULL terminated list of options, or NULL. The following
4163 : * options are available:
4164 : * <ul>
4165 : * <li>METHOD=LINEWORK/STRUCTURE.
4166 : * LINEWORK is the default method, which combines all rings into a set of
4167 : * noded lines and then extracts valid polygons from that linework.
4168 : * The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
4169 : * all rings valid, then merges shells and
4170 : * subtracts holes from shells to generate valid result. Assumes that
4171 : * holes and shells are correctly categorized.</li>
4172 : * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
4173 : * NO (default): collapses are converted to empty geometries
4174 : * YES: collapses are converted to a valid geometry of lower dimension.</li>
4175 : * </ul>
4176 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4177 : *
4178 : * @since GDAL 3.0
4179 : */
4180 153 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
4181 : {
4182 : (void)papszOptions;
4183 : #ifndef HAVE_GEOS
4184 : if (IsValid())
4185 : return clone();
4186 :
4187 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4188 : return nullptr;
4189 : #else
4190 153 : if (IsSFCGALCompatible())
4191 : {
4192 0 : if (IsValid())
4193 0 : return clone();
4194 : }
4195 153 : else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
4196 : {
4197 3 : GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
4198 3 : bool bIsValid = false;
4199 3 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4200 3 : if (hGeosGeom)
4201 : {
4202 3 : bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
4203 3 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4204 : }
4205 3 : freeGEOSContext(hGEOSCtxt);
4206 3 : if (bIsValid)
4207 1 : return clone();
4208 : }
4209 :
4210 152 : const bool bStructureMethod = EQUAL(
4211 : CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
4212 152 : CPL_IGNORE_RET_VAL(bStructureMethod);
4213 : #if !(GEOS_VERSION_MAJOR > 3 || \
4214 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
4215 : if (bStructureMethod)
4216 : {
4217 : CPLError(CE_Failure, CPLE_NotSupported,
4218 : "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
4219 : return nullptr;
4220 : }
4221 : #endif
4222 :
4223 152 : OGRGeometry *poOGRProduct = nullptr;
4224 :
4225 152 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4226 152 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
4227 152 : if (hGeosGeom != nullptr)
4228 : {
4229 : GEOSGeom hGEOSRet;
4230 : #if GEOS_VERSION_MAJOR > 3 || \
4231 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4232 152 : if (bStructureMethod)
4233 : {
4234 : GEOSMakeValidParams *params =
4235 15 : GEOSMakeValidParams_create_r(hGEOSCtxt);
4236 15 : CPLAssert(params);
4237 15 : GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
4238 : GEOS_MAKE_VALID_STRUCTURE);
4239 15 : GEOSMakeValidParams_setKeepCollapsed_r(
4240 : hGEOSCtxt, params,
4241 15 : CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
4242 15 : hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
4243 15 : GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
4244 : }
4245 : else
4246 : #endif
4247 : {
4248 137 : hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
4249 : }
4250 152 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4251 :
4252 152 : if (hGEOSRet != nullptr)
4253 : {
4254 : poOGRProduct =
4255 152 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
4256 152 : if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
4257 6 : poOGRProduct->assignSpatialReference(getSpatialReference());
4258 : poOGRProduct =
4259 152 : OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
4260 152 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
4261 :
4262 : #if GEOS_VERSION_MAJOR > 3 || \
4263 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4264 : // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
4265 : // if the input is a multiple geometry
4266 152 : if (poOGRProduct && bStructureMethod &&
4267 310 : OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
4268 6 : !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
4269 : wkbGeometryCollection))
4270 : {
4271 6 : poOGRProduct = OGRGeometryFactory::forceTo(
4272 6 : std::unique_ptr<OGRGeometry>(poOGRProduct),
4273 3 : getGeometryType())
4274 3 : .release();
4275 : }
4276 : #endif
4277 : }
4278 : }
4279 152 : freeGEOSContext(hGEOSCtxt);
4280 :
4281 152 : return poOGRProduct;
4282 : #endif
4283 : }
4284 :
4285 : /************************************************************************/
4286 : /* OGR_G_MakeValid() */
4287 : /************************************************************************/
4288 :
4289 : /**
4290 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4291 : *
4292 : * Already-valid geometries are cloned without further intervention.
4293 : *
4294 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4295 : *
4296 : * This function is built on the GEOS >= 3.8 library, check it for the
4297 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4298 : * library, this function will return a clone of the input geometry if it is
4299 : * valid, or NULL if it is invalid
4300 : *
4301 : * @param hGeom The Geometry to make valid.
4302 : *
4303 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4304 : * or NULL if an error occurs.
4305 : *
4306 : * @since GDAL 3.0
4307 : */
4308 :
4309 0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
4310 :
4311 : {
4312 0 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
4313 :
4314 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
4315 : }
4316 :
4317 : /************************************************************************/
4318 : /* OGR_G_MakeValidEx() */
4319 : /************************************************************************/
4320 :
4321 : /**
4322 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4323 : *
4324 : * Already-valid geometries are cloned without further intervention.
4325 : *
4326 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4327 : *
4328 : * See documentation of that method for possible options.
4329 : *
4330 : * @param hGeom The Geometry to make valid.
4331 : * @param papszOptions Options.
4332 : *
4333 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4334 : * or NULL if an error occurs.
4335 : *
4336 : * @since GDAL 3.4
4337 : */
4338 :
4339 25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
4340 :
4341 : {
4342 25 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
4343 :
4344 25 : return OGRGeometry::ToHandle(
4345 50 : OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
4346 : }
4347 :
4348 : /************************************************************************/
4349 : /* Normalize() */
4350 : /************************************************************************/
4351 :
4352 : /**
4353 : * \brief Attempts to bring geometry into normalized/canonical form.
4354 : *
4355 : * This method is the same as the C function OGR_G_Normalize().
4356 : *
4357 : * This function is built on the GEOS library; check it for the definition
4358 : * of the geometry operation.
4359 : * If OGR is built without the GEOS library, this function will always fail,
4360 : * issuing a CPLE_NotSupported error.
4361 : *
4362 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4363 : *
4364 : * @since GDAL 3.3
4365 : */
4366 51 : OGRGeometry *OGRGeometry::Normalize() const
4367 : {
4368 : #ifndef HAVE_GEOS
4369 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4370 : return nullptr;
4371 : #else
4372 51 : OGRGeometry *poOGRProduct = nullptr;
4373 :
4374 51 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4375 51 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4376 51 : if (hGeosGeom != nullptr)
4377 : {
4378 :
4379 51 : int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
4380 :
4381 51 : if (hGEOSRet == 0)
4382 : {
4383 : poOGRProduct =
4384 51 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
4385 : }
4386 : else
4387 : {
4388 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4389 : }
4390 : }
4391 51 : freeGEOSContext(hGEOSCtxt);
4392 :
4393 51 : return poOGRProduct;
4394 : #endif
4395 : }
4396 :
4397 : /************************************************************************/
4398 : /* OGR_G_Normalize() */
4399 : /************************************************************************/
4400 :
4401 : /**
4402 : * \brief Attempts to bring geometry into normalized/canonical form.
4403 : *
4404 : * This function is the same as the C++ method OGRGeometry::Normalize().
4405 : *
4406 : * This function is built on the GEOS library; check it for the definition
4407 : * of the geometry operation.
4408 : * If OGR is built without the GEOS library, this function will always fail,
4409 : * issuing a CPLE_NotSupported error.
4410 : * @param hGeom The Geometry to normalize.
4411 : *
4412 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4413 : * or NULL if an error occurs.
4414 : *
4415 : * @since GDAL 3.3
4416 : */
4417 :
4418 21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
4419 :
4420 : {
4421 21 : VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
4422 :
4423 21 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
4424 : }
4425 :
4426 : /************************************************************************/
4427 : /* ConvexHull() */
4428 : /************************************************************************/
4429 :
4430 : /**
4431 : * \brief Compute convex hull.
4432 : *
4433 : * A new geometry object is created and returned containing the convex
4434 : * hull of the geometry on which the method is invoked.
4435 : *
4436 : * This method is the same as the C function OGR_G_ConvexHull().
4437 : *
4438 : * This method is built on the GEOS library, check it for the definition
4439 : * of the geometry operation.
4440 : * If OGR is built without the GEOS library, this method will always fail,
4441 : * issuing a CPLE_NotSupported error.
4442 : *
4443 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4444 : */
4445 :
4446 6 : OGRGeometry *OGRGeometry::ConvexHull() const
4447 :
4448 : {
4449 6 : if (IsSFCGALCompatible())
4450 : {
4451 : #ifndef HAVE_SFCGAL
4452 :
4453 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4454 0 : return nullptr;
4455 :
4456 : #else
4457 :
4458 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4459 : if (poThis == nullptr)
4460 : return nullptr;
4461 :
4462 : sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
4463 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4464 : if (h_prodGeom)
4465 : h_prodGeom->assignSpatialReference(getSpatialReference());
4466 :
4467 : sfcgal_geometry_delete(poThis);
4468 : sfcgal_geometry_delete(poRes);
4469 :
4470 : return h_prodGeom;
4471 :
4472 : #endif
4473 : }
4474 :
4475 : else
4476 : {
4477 : #ifndef HAVE_GEOS
4478 :
4479 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4480 : return nullptr;
4481 :
4482 : #else
4483 :
4484 6 : OGRGeometry *poOGRProduct = nullptr;
4485 :
4486 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4487 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4488 6 : if (hGeosGeom != nullptr)
4489 : {
4490 6 : GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
4491 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4492 :
4493 : poOGRProduct =
4494 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4495 : }
4496 6 : freeGEOSContext(hGEOSCtxt);
4497 :
4498 6 : return poOGRProduct;
4499 :
4500 : #endif /* HAVE_GEOS */
4501 : }
4502 : }
4503 :
4504 : /************************************************************************/
4505 : /* OGR_G_ConvexHull() */
4506 : /************************************************************************/
4507 : /**
4508 : * \brief Compute convex hull.
4509 : *
4510 : * A new geometry object is created and returned containing the convex
4511 : * hull of the geometry on which the method is invoked.
4512 : *
4513 : * This function is the same as the C++ method OGRGeometry::ConvexHull().
4514 : *
4515 : * This function is built on the GEOS library, check it for the definition
4516 : * of the geometry operation.
4517 : * If OGR is built without the GEOS library, this function will always fail,
4518 : * issuing a CPLE_NotSupported error.
4519 : *
4520 : * @param hTarget The Geometry to calculate the convex hull of.
4521 : *
4522 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4523 : * or NULL if an error occurs.
4524 : */
4525 :
4526 1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
4527 :
4528 : {
4529 1 : VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
4530 :
4531 1 : return OGRGeometry::ToHandle(
4532 1 : OGRGeometry::FromHandle(hTarget)->ConvexHull());
4533 : }
4534 :
4535 : /************************************************************************/
4536 : /* ConcaveHull() */
4537 : /************************************************************************/
4538 :
4539 : /**
4540 : * \brief Compute the concave hull of a geometry.
4541 : *
4542 : * The concave hull is fully contained within the convex hull and also
4543 : * contains all the points of the input, but in a smaller area.
4544 : * The area ratio is the ratio of the area of the convex hull and the concave
4545 : * hull. Frequently used to convert a multi-point into a polygonal area.
4546 : * that contains all the points in the input Geometry.
4547 : *
4548 : * A new geometry object is created and returned containing the concave
4549 : * hull of the geometry on which the method is invoked.
4550 : *
4551 : * This method is the same as the C function OGR_G_ConcaveHull().
4552 : *
4553 : * This method is built on the GEOS >= 3.11 library
4554 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4555 : * fail, issuing a CPLE_NotSupported error.
4556 : *
4557 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4558 : * @param bAllowHoles Whether holes are allowed.
4559 : *
4560 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4561 : *
4562 : * @since GDAL 3.6
4563 : * @see OGRGeometry::ConcaveHullOfPolygons()
4564 : */
4565 :
4566 8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
4567 : {
4568 : #ifndef HAVE_GEOS
4569 : (void)dfRatio;
4570 : (void)bAllowHoles;
4571 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4572 : return nullptr;
4573 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4574 : (void)dfRatio;
4575 : (void)bAllowHoles;
4576 : CPLError(CE_Failure, CPLE_NotSupported,
4577 : "GEOS 3.11 or later needed for ConcaveHull.");
4578 : return nullptr;
4579 : #else
4580 8 : OGRGeometry *poOGRProduct = nullptr;
4581 :
4582 8 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4583 8 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4584 8 : if (hGeosGeom != nullptr)
4585 : {
4586 : GEOSGeom hGeosHull =
4587 8 : GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
4588 8 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4589 :
4590 : poOGRProduct =
4591 8 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4592 : }
4593 8 : freeGEOSContext(hGEOSCtxt);
4594 :
4595 8 : return poOGRProduct;
4596 : #endif /* HAVE_GEOS */
4597 : }
4598 :
4599 : /************************************************************************/
4600 : /* OGR_G_ConcaveHull() */
4601 : /************************************************************************/
4602 : /**
4603 : * \brief Compute the concave hull of a geometry.
4604 : *
4605 : * The concave hull is fully contained within the convex hull and also
4606 : * contains all the points of the input, but in a smaller area.
4607 : * The area ratio is the ratio of the area of the convex hull and the concave
4608 : * hull. Frequently used to convert a multi-point into a polygonal area.
4609 : * that contains all the points in the input Geometry.
4610 : *
4611 : * A new geometry object is created and returned containing the convex
4612 : * hull of the geometry on which the function is invoked.
4613 : *
4614 : * This function is the same as the C++ method OGRGeometry::ConcaveHull().
4615 : *
4616 : * This function is built on the GEOS >= 3.11 library
4617 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4618 : * fail, issuing a CPLE_NotSupported error.
4619 : *
4620 : * @param hTarget The Geometry to calculate the concave hull of.
4621 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4622 : * @param bAllowHoles Whether holes are allowed.
4623 : *
4624 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4625 : * or NULL if an error occurs.
4626 : *
4627 : * @since GDAL 3.6
4628 : * @see OGR_G_ConcaveHullOfPolygons()
4629 : */
4630 :
4631 2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
4632 : bool bAllowHoles)
4633 :
4634 : {
4635 2 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
4636 :
4637 2 : return OGRGeometry::ToHandle(
4638 2 : OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
4639 : }
4640 :
4641 : /************************************************************************/
4642 : /* ConcaveHullOfPolygons() */
4643 : /************************************************************************/
4644 :
4645 : /**
4646 : * \brief Compute the concave hull of a set of polygons, respecting
4647 : * the polygons as constraints.
4648 : *
4649 : * A concave hull is a (possibly) non-convex polygon containing all the input
4650 : * polygons.
4651 : * The computed hull "fills the gap" between the polygons,
4652 : * and does not intersect their interior.
4653 : * A set of polygons has a sequence of hulls of increasing concaveness,
4654 : * determined by a numeric target parameter.
4655 : *
4656 : * The concave hull is constructed by removing the longest outer edges
4657 : * of the Delaunay Triangulation of the space between the polygons,
4658 : * until the target criterion parameter is reached.
4659 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4660 : * between polygons to be no larger than this value.
4661 : * This can be expressed as a ratio between the lengths of the longest and
4662 : * shortest edges.
4663 : *
4664 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4665 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4666 : * for more details.
4667 : *
4668 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4669 : * be non-overlapping).
4670 : *
4671 : * A new geometry object is created and returned containing the concave
4672 : * hull of the geometry on which the method is invoked.
4673 : *
4674 : * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
4675 : *
4676 : * This method is built on the GEOS >= 3.11 library
4677 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4678 : * fail, issuing a CPLE_NotSupported error.
4679 : *
4680 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4681 : * difference between the longest and shortest edge lengths
4682 : * between the polygons.
4683 : * This normalizes the Maximum Edge Length to be scale-free.
4684 : * A value of 1 produces the convex hull; a value of 0 produces
4685 : * the original polygons.
4686 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4687 : * polygons.
4688 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4689 : *
4690 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4691 : *
4692 : * @since GDAL 3.13
4693 : * @see OGRGeometry::ConcaveHull()
4694 : */
4695 :
4696 13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
4697 : bool bIsTight,
4698 : bool bAllowHoles) const
4699 : {
4700 : #ifndef HAVE_GEOS
4701 : (void)dfLengthRatio;
4702 : (void)bIsTight;
4703 : (void)bAllowHoles;
4704 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4705 : return nullptr;
4706 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4707 : (void)dfLengthRatio;
4708 : (void)bIsTight;
4709 : (void)bAllowHoles;
4710 : CPLError(CE_Failure, CPLE_NotSupported,
4711 : "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
4712 : return nullptr;
4713 : #else
4714 13 : OGRGeometry *poOGRProduct = nullptr;
4715 :
4716 13 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4717 13 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4718 13 : if (hGeosGeom != nullptr)
4719 : {
4720 13 : GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
4721 : hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
4722 13 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4723 :
4724 : poOGRProduct =
4725 13 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4726 : }
4727 13 : freeGEOSContext(hGEOSCtxt);
4728 :
4729 13 : return poOGRProduct;
4730 : #endif /* HAVE_GEOS */
4731 : }
4732 :
4733 : /************************************************************************/
4734 : /* OGR_G_ConcaveHullOfPolygons() */
4735 : /************************************************************************/
4736 : /**
4737 : * \brief Compute the concave hull of a set of polygons, respecting
4738 : * the polygons as constraints.
4739 : *
4740 : * A concave hull is a (possibly) non-convex polygon containing all the input
4741 : * polygons.
4742 : * The computed hull "fills the gap" between the polygons,
4743 : * and does not intersect their interior.
4744 : * A set of polygons has a sequence of hulls of increasing concaveness,
4745 : * determined by a numeric target parameter.
4746 : *
4747 : * The concave hull is constructed by removing the longest outer edges
4748 : * of the Delaunay Triangulation of the space between the polygons,
4749 : * until the target criterion parameter is reached.
4750 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4751 : * between polygons to be no larger than this value.
4752 : * This can be expressed as a ratio between the lengths of the longest and
4753 : * shortest edges.
4754 : *
4755 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4756 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4757 : * for more details.
4758 : *
4759 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4760 : * be non-overlapping).
4761 : *
4762 : * A new geometry object is created and returned containing the concave
4763 : * hull of the geometry on which the method is invoked.
4764 : *
4765 : * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
4766 : *
4767 : * This function is built on the GEOS >= 3.11 library
4768 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4769 : * fail, issuing a CPLE_NotSupported error.
4770 : *
4771 : * @param hTarget The Geometry to calculate the concave hull of.
4772 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4773 : * difference between the longest and shortest edge lengths
4774 : * between the polygons.
4775 : * This normalizes the Maximum Edge Length to be scale-free.
4776 : * A value of 1 produces the convex hull; a value of 0 produces
4777 : * the original polygons.
4778 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4779 : * polygons.
4780 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4781 : *
4782 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4783 : * or NULL if an error occurs.
4784 : *
4785 : * @since GDAL 3.13
4786 : * @see OGR_G_ConcaveHull()
4787 : */
4788 :
4789 7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
4790 : double dfLengthRatio, bool bIsTight,
4791 : bool bAllowHoles)
4792 :
4793 : {
4794 7 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
4795 :
4796 7 : return OGRGeometry::ToHandle(
4797 : OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
4798 7 : dfLengthRatio, bIsTight, bAllowHoles));
4799 : }
4800 :
4801 : /************************************************************************/
4802 : /* Boundary() */
4803 : /************************************************************************/
4804 :
4805 : /**
4806 : * \brief Compute boundary.
4807 : *
4808 : * A new geometry object is created and returned containing the boundary
4809 : * of the geometry on which the method is invoked.
4810 : *
4811 : * This method is the same as the C function OGR_G_Boundary().
4812 : *
4813 : * This method is built on the GEOS library, check it for the definition
4814 : * of the geometry operation.
4815 : * If OGR is built without the GEOS library, this method will always fail,
4816 : * issuing a CPLE_NotSupported error.
4817 : *
4818 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4819 : *
4820 : */
4821 :
4822 6 : OGRGeometry *OGRGeometry::Boundary() const
4823 :
4824 : {
4825 : #ifndef HAVE_GEOS
4826 :
4827 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4828 : return nullptr;
4829 :
4830 : #else
4831 :
4832 6 : OGRGeometry *poOGRProduct = nullptr;
4833 :
4834 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4835 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4836 6 : if (hGeosGeom != nullptr)
4837 : {
4838 6 : GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
4839 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4840 :
4841 : poOGRProduct =
4842 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4843 : }
4844 6 : freeGEOSContext(hGEOSCtxt);
4845 :
4846 6 : return poOGRProduct;
4847 :
4848 : #endif // HAVE_GEOS
4849 : }
4850 :
4851 : //! @cond Doxygen_Suppress
4852 : /**
4853 : * \brief Compute boundary (deprecated)
4854 : *
4855 : * @deprecated
4856 : *
4857 : * @see Boundary()
4858 : */
4859 0 : OGRGeometry *OGRGeometry::getBoundary() const
4860 :
4861 : {
4862 0 : return Boundary();
4863 : }
4864 :
4865 : //! @endcond
4866 :
4867 : /************************************************************************/
4868 : /* OGR_G_Boundary() */
4869 : /************************************************************************/
4870 : /**
4871 : * \brief Compute boundary.
4872 : *
4873 : * A new geometry object is created and returned containing the boundary
4874 : * of the geometry on which the method is invoked.
4875 : *
4876 : * This function is the same as the C++ method OGR_G_Boundary().
4877 : *
4878 : * This function is built on the GEOS library, check it for the definition
4879 : * of the geometry operation.
4880 : * If OGR is built without the GEOS library, this function will always fail,
4881 : * issuing a CPLE_NotSupported error.
4882 : *
4883 : * @param hTarget The Geometry to calculate the boundary of.
4884 : *
4885 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4886 : * or NULL if an error occurs.
4887 : *
4888 : */
4889 6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
4890 :
4891 : {
4892 6 : VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
4893 :
4894 6 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4895 : }
4896 :
4897 : /**
4898 : * \brief Compute boundary (deprecated)
4899 : *
4900 : * @deprecated
4901 : *
4902 : * @see OGR_G_Boundary()
4903 : */
4904 0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
4905 :
4906 : {
4907 0 : VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
4908 :
4909 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4910 : }
4911 :
4912 : /************************************************************************/
4913 : /* Buffer() */
4914 : /************************************************************************/
4915 :
4916 : /**
4917 : * \brief Compute buffer of geometry.
4918 : *
4919 : * Builds a new geometry containing the buffer region around the geometry
4920 : * on which it is invoked. The buffer is a polygon containing the region within
4921 : * the buffer distance of the original geometry.
4922 : *
4923 : * Some buffer sections are properly described as curves, but are converted to
4924 : * approximate polygons. The nQuadSegs parameter can be used to control how
4925 : * many segments should be used to define a 90 degree curve - a quadrant of a
4926 : * circle. A value of 30 is a reasonable default. Large values result in
4927 : * large numbers of vertices in the resulting buffer geometry while small
4928 : * numbers reduce the accuracy of the result.
4929 : *
4930 : * This method is the same as the C function OGR_G_Buffer().
4931 : *
4932 : * This method is built on the GEOS library, check it for the definition
4933 : * of the geometry operation.
4934 : * If OGR is built without the GEOS library, this method will always fail,
4935 : * issuing a CPLE_NotSupported error.
4936 : *
4937 : * @param dfDist the buffer distance to be applied. Should be expressed into
4938 : * the same unit as the coordinates of the geometry.
4939 : *
4940 : * @param nQuadSegs the number of segments used to approximate a 90
4941 : * degree (quadrant) of curvature.
4942 : *
4943 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4944 : */
4945 :
4946 42 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
4947 :
4948 : {
4949 : (void)dfDist;
4950 : (void)nQuadSegs;
4951 : #ifndef HAVE_GEOS
4952 :
4953 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4954 : return nullptr;
4955 :
4956 : #else
4957 :
4958 42 : OGRGeometry *poOGRProduct = nullptr;
4959 :
4960 42 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4961 42 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4962 42 : if (hGeosGeom != nullptr)
4963 : {
4964 : GEOSGeom hGeosProduct =
4965 42 : GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
4966 42 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4967 :
4968 : poOGRProduct =
4969 42 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4970 : }
4971 42 : freeGEOSContext(hGEOSCtxt);
4972 :
4973 42 : return poOGRProduct;
4974 :
4975 : #endif // HAVE_GEOS
4976 : }
4977 :
4978 : /************************************************************************/
4979 : /* OGR_G_Buffer() */
4980 : /************************************************************************/
4981 :
4982 : /**
4983 : * \brief Compute buffer of geometry.
4984 : *
4985 : * Builds a new geometry containing the buffer region around the geometry
4986 : * on which it is invoked. The buffer is a polygon containing the region within
4987 : * the buffer distance of the original geometry.
4988 : *
4989 : * Some buffer sections are properly described as curves, but are converted to
4990 : * approximate polygons. The nQuadSegs parameter can be used to control how
4991 : * many segments should be used to define a 90 degree curve - a quadrant of a
4992 : * circle. A value of 30 is a reasonable default. Large values result in
4993 : * large numbers of vertices in the resulting buffer geometry while small
4994 : * numbers reduce the accuracy of the result.
4995 : *
4996 : * This function is the same as the C++ method OGRGeometry::Buffer().
4997 : *
4998 : * This function is built on the GEOS library, check it for the definition
4999 : * of the geometry operation.
5000 : * If OGR is built without the GEOS library, this function will always fail,
5001 : * issuing a CPLE_NotSupported error.
5002 : *
5003 : * @param hTarget the geometry.
5004 : * @param dfDist the buffer distance to be applied. Should be expressed into
5005 : * the same unit as the coordinates of the geometry.
5006 : *
5007 : * @param nQuadSegs the number of segments used to approximate a 90 degree
5008 : * (quadrant) of curvature.
5009 : *
5010 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5011 : * or NULL if an error occurs.
5012 : */
5013 :
5014 42 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
5015 :
5016 : {
5017 42 : VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
5018 :
5019 42 : return OGRGeometry::ToHandle(
5020 42 : OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
5021 : }
5022 :
5023 : /**
5024 : * \brief Compute buffer of geometry.
5025 : *
5026 : * Builds a new geometry containing the buffer region around the geometry
5027 : * on which it is invoked. The buffer is a polygon containing the region within
5028 : * the buffer distance of the original geometry.
5029 : *
5030 : * This function is built on the GEOS library, check it for the definition
5031 : * of the geometry operation.
5032 : * If OGR is built without the GEOS library, this function will always fail,
5033 : * issuing a CPLE_NotSupported error.
5034 : *
5035 : * The following options are supported. See the GEOS library for more detailed
5036 : * descriptions.
5037 : *
5038 : * <ul>
5039 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5040 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5041 : * <li>MITRE_LIMIT=double</li>
5042 : * <li>QUADRANT_SEGMENTS=int</li>
5043 : * <li>SINGLE_SIDED=YES/NO</li>
5044 : * </ul>
5045 : *
5046 : * This function is the same as the C function OGR_G_BufferEx().
5047 : *
5048 : * @param dfDist the buffer distance to be applied. Should be expressed into
5049 : * the same unit as the coordinates of the geometry.
5050 : * @param papszOptions NULL terminated list of options (may be NULL)
5051 : *
5052 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5053 : *
5054 : * @since GDAL 3.10
5055 : */
5056 :
5057 35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
5058 : CSLConstList papszOptions) const
5059 : {
5060 : (void)dfDist;
5061 : (void)papszOptions;
5062 : #ifndef HAVE_GEOS
5063 :
5064 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5065 : return nullptr;
5066 :
5067 : #else
5068 35 : OGRGeometry *poOGRProduct = nullptr;
5069 35 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5070 :
5071 35 : auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
5072 35 : bool bParamsAreValid = true;
5073 :
5074 166 : for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
5075 : {
5076 131 : if (EQUAL(pszParam, "ENDCAP_STYLE"))
5077 : {
5078 : int nStyle;
5079 25 : if (EQUAL(pszValue, "ROUND"))
5080 : {
5081 22 : nStyle = GEOSBUF_CAP_ROUND;
5082 : }
5083 3 : else if (EQUAL(pszValue, "FLAT"))
5084 : {
5085 1 : nStyle = GEOSBUF_CAP_FLAT;
5086 : }
5087 2 : else if (EQUAL(pszValue, "SQUARE"))
5088 : {
5089 1 : nStyle = GEOSBUF_CAP_SQUARE;
5090 : }
5091 : else
5092 : {
5093 1 : bParamsAreValid = false;
5094 1 : CPLError(CE_Failure, CPLE_NotSupported,
5095 : "Invalid value for ENDCAP_STYLE: %s", pszValue);
5096 2 : break;
5097 : }
5098 :
5099 24 : if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
5100 : {
5101 0 : bParamsAreValid = false;
5102 : }
5103 : }
5104 106 : else if (EQUAL(pszParam, "JOIN_STYLE"))
5105 : {
5106 : int nStyle;
5107 25 : if (EQUAL(pszValue, "ROUND"))
5108 : {
5109 21 : nStyle = GEOSBUF_JOIN_ROUND;
5110 : }
5111 4 : else if (EQUAL(pszValue, "MITRE"))
5112 : {
5113 3 : nStyle = GEOSBUF_JOIN_MITRE;
5114 : }
5115 1 : else if (EQUAL(pszValue, "BEVEL"))
5116 : {
5117 0 : nStyle = GEOSBUF_JOIN_BEVEL;
5118 : }
5119 : else
5120 : {
5121 1 : bParamsAreValid = false;
5122 1 : CPLError(CE_Failure, CPLE_NotSupported,
5123 : "Invalid value for JOIN_STYLE: %s", pszValue);
5124 1 : break;
5125 : }
5126 :
5127 24 : if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
5128 : {
5129 0 : bParamsAreValid = false;
5130 0 : break;
5131 : }
5132 : }
5133 81 : else if (EQUAL(pszParam, "MITRE_LIMIT"))
5134 : {
5135 : try
5136 : {
5137 : std::size_t end;
5138 30 : double dfLimit = std::stod(pszValue, &end);
5139 :
5140 24 : if (end != strlen(pszValue))
5141 : {
5142 0 : throw std::invalid_argument("");
5143 : }
5144 :
5145 24 : if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
5146 : dfLimit))
5147 : {
5148 0 : bParamsAreValid = false;
5149 0 : break;
5150 : }
5151 : }
5152 4 : catch (const std::invalid_argument &)
5153 : {
5154 2 : bParamsAreValid = false;
5155 2 : CPLError(CE_Failure, CPLE_IllegalArg,
5156 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5157 : }
5158 0 : catch (const std::out_of_range &)
5159 : {
5160 0 : bParamsAreValid = false;
5161 0 : CPLError(CE_Failure, CPLE_IllegalArg,
5162 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5163 : }
5164 : }
5165 55 : else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
5166 : {
5167 : try
5168 : {
5169 : std::size_t end;
5170 38 : int nQuadSegs = std::stoi(pszValue, &end, 10);
5171 :
5172 26 : if (end != strlen(pszValue))
5173 : {
5174 0 : throw std::invalid_argument("");
5175 : }
5176 :
5177 26 : if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
5178 : nQuadSegs))
5179 : {
5180 0 : bParamsAreValid = false;
5181 0 : break;
5182 : }
5183 : }
5184 6 : catch (const std::invalid_argument &)
5185 : {
5186 3 : bParamsAreValid = false;
5187 3 : CPLError(CE_Failure, CPLE_IllegalArg,
5188 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5189 : }
5190 2 : catch (const std::out_of_range &)
5191 : {
5192 1 : bParamsAreValid = false;
5193 1 : CPLError(CE_Failure, CPLE_IllegalArg,
5194 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5195 : }
5196 : }
5197 25 : else if (EQUAL(pszParam, "SINGLE_SIDED"))
5198 : {
5199 24 : bool bSingleSided = CPLTestBool(pszValue);
5200 :
5201 24 : if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
5202 : bSingleSided))
5203 : {
5204 0 : bParamsAreValid = false;
5205 0 : break;
5206 : }
5207 : }
5208 : else
5209 : {
5210 1 : bParamsAreValid = false;
5211 1 : CPLError(CE_Failure, CPLE_NotSupported,
5212 : "Unsupported buffer option: %s", pszValue);
5213 : }
5214 : }
5215 :
5216 35 : if (bParamsAreValid)
5217 : {
5218 26 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
5219 26 : if (hGeosGeom != nullptr)
5220 : {
5221 : GEOSGeom hGeosProduct =
5222 26 : GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
5223 26 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
5224 :
5225 26 : if (hGeosProduct != nullptr)
5226 : {
5227 26 : poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
5228 : this, nullptr);
5229 : }
5230 : }
5231 : }
5232 :
5233 35 : GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
5234 35 : freeGEOSContext(hGEOSCtxt);
5235 35 : return poOGRProduct;
5236 : #endif
5237 : }
5238 :
5239 : /**
5240 : * \brief Compute buffer of geometry.
5241 : *
5242 : * Builds a new geometry containing the buffer region around the geometry
5243 : * on which it is invoked. The buffer is a polygon containing the region within
5244 : * the buffer distance of the original geometry.
5245 : *
5246 : * This function is built on the GEOS library, check it for the definition
5247 : * of the geometry operation.
5248 : * If OGR is built without the GEOS library, this function will always fail,
5249 : * issuing a CPLE_NotSupported error.
5250 : *
5251 : * The following options are supported. See the GEOS library for more detailed
5252 : * descriptions.
5253 : *
5254 : * <ul>
5255 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5256 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5257 : * <li>MITRE_LIMIT=double</li>
5258 : * <li>QUADRANT_SEGMENTS=int</li>
5259 : * <li>SINGLE_SIDED=YES/NO</li>
5260 : * </ul>
5261 : *
5262 : * This function is the same as the C++ method OGRGeometry::BufferEx().
5263 : *
5264 : * @param hTarget the geometry.
5265 : * @param dfDist the buffer distance to be applied. Should be expressed into
5266 : * the same unit as the coordinates of the geometry.
5267 : * @param papszOptions NULL terminated list of options (may be NULL)
5268 : *
5269 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5270 : * or NULL if an error occurs.
5271 : *
5272 : * @since GDAL 3.10
5273 : */
5274 :
5275 12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
5276 : CSLConstList papszOptions)
5277 :
5278 : {
5279 12 : VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
5280 :
5281 12 : return OGRGeometry::ToHandle(
5282 12 : OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
5283 : }
5284 :
5285 : /************************************************************************/
5286 : /* Intersection() */
5287 : /************************************************************************/
5288 :
5289 : /**
5290 : * \brief Compute intersection.
5291 : *
5292 : * Generates a new geometry which is the region of intersection of the
5293 : * two geometries operated on. The Intersects() method can be used to test if
5294 : * two geometries intersect.
5295 : *
5296 : * Geometry validity is not checked. In case you are unsure of the validity
5297 : * of the input geometries, call IsValid() before, otherwise the result might
5298 : * be wrong.
5299 : *
5300 : * This method is the same as the C function OGR_G_Intersection().
5301 : *
5302 : * This method is built on the GEOS library, check it for the definition
5303 : * of the geometry operation.
5304 : * If OGR is built without the GEOS library, this method will always fail,
5305 : * issuing a CPLE_NotSupported error.
5306 : *
5307 : * @param poOtherGeom the other geometry intersected with "this" geometry.
5308 : *
5309 : * @return a new geometry to be freed by the caller, or NULL if there is no
5310 : * intersection or if an error occurs.
5311 : *
5312 : */
5313 :
5314 : OGRGeometry *
5315 3093 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5316 :
5317 : {
5318 3093 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5319 : {
5320 : #ifndef HAVE_SFCGAL
5321 :
5322 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5323 0 : return nullptr;
5324 :
5325 : #else
5326 :
5327 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5328 : if (poThis == nullptr)
5329 : return nullptr;
5330 :
5331 : sfcgal_geometry_t *poOther =
5332 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5333 : if (poOther == nullptr)
5334 : {
5335 : sfcgal_geometry_delete(poThis);
5336 : return nullptr;
5337 : }
5338 :
5339 : sfcgal_geometry_t *poRes =
5340 : sfcgal_geometry_intersection_3d(poThis, poOther);
5341 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
5342 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5343 : poOtherGeom->getSpatialReference() != nullptr &&
5344 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5345 : h_prodGeom->assignSpatialReference(getSpatialReference());
5346 :
5347 : sfcgal_geometry_delete(poThis);
5348 : sfcgal_geometry_delete(poOther);
5349 : sfcgal_geometry_delete(poRes);
5350 :
5351 : return h_prodGeom;
5352 :
5353 : #endif
5354 : }
5355 :
5356 : else
5357 : {
5358 : #ifndef HAVE_GEOS
5359 :
5360 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5361 : return nullptr;
5362 :
5363 : #else
5364 3093 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
5365 : #endif /* HAVE_GEOS */
5366 : }
5367 : }
5368 :
5369 : /************************************************************************/
5370 : /* OGR_G_Intersection() */
5371 : /************************************************************************/
5372 :
5373 : /**
5374 : * \brief Compute intersection.
5375 : *
5376 : * Generates a new geometry which is the region of intersection of the
5377 : * two geometries operated on. The OGR_G_Intersects() function can be used to
5378 : * test if two geometries intersect.
5379 : *
5380 : * Geometry validity is not checked. In case you are unsure of the validity
5381 : * of the input geometries, call IsValid() before, otherwise the result might
5382 : * be wrong.
5383 : *
5384 : * This function is the same as the C++ method OGRGeometry::Intersection().
5385 : *
5386 : * This function is built on the GEOS library, check it for the definition
5387 : * of the geometry operation.
5388 : * If OGR is built without the GEOS library, this function will always fail,
5389 : * issuing a CPLE_NotSupported error.
5390 : *
5391 : * @param hThis the geometry.
5392 : * @param hOther the other geometry.
5393 : *
5394 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5395 : * or NULL if there is not intersection of if an error occurs.
5396 : */
5397 :
5398 12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
5399 :
5400 : {
5401 12 : VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
5402 :
5403 24 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
5404 24 : OGRGeometry::FromHandle(hOther)));
5405 : }
5406 :
5407 : /************************************************************************/
5408 : /* Union() */
5409 : /************************************************************************/
5410 :
5411 : /**
5412 : * \brief Compute union.
5413 : *
5414 : * Generates a new geometry which is the region of union of the
5415 : * two geometries operated on.
5416 : *
5417 : * Geometry validity is not checked. In case you are unsure of the validity
5418 : * of the input geometries, call IsValid() before, otherwise the result might
5419 : * be wrong.
5420 : *
5421 : * This method is the same as the C function OGR_G_Union().
5422 : *
5423 : * This method is built on the GEOS library, check it for the definition
5424 : * of the geometry operation.
5425 : * If OGR is built without the GEOS library, this method will always fail,
5426 : * issuing a CPLE_NotSupported error.
5427 : *
5428 : * @param poOtherGeom the other geometry unioned with "this" geometry.
5429 : *
5430 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5431 : */
5432 :
5433 : OGRGeometry *
5434 68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5435 :
5436 : {
5437 68 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5438 : {
5439 : #ifndef HAVE_SFCGAL
5440 :
5441 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5442 0 : return nullptr;
5443 :
5444 : #else
5445 :
5446 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5447 : if (poThis == nullptr)
5448 : return nullptr;
5449 :
5450 : sfcgal_geometry_t *poOther =
5451 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5452 : if (poOther == nullptr)
5453 : {
5454 : sfcgal_geometry_delete(poThis);
5455 : return nullptr;
5456 : }
5457 :
5458 : sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
5459 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5460 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5461 : poOtherGeom->getSpatialReference() != nullptr &&
5462 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5463 : h_prodGeom->assignSpatialReference(getSpatialReference());
5464 :
5465 : sfcgal_geometry_delete(poThis);
5466 : sfcgal_geometry_delete(poOther);
5467 : sfcgal_geometry_delete(poRes);
5468 :
5469 : return h_prodGeom;
5470 :
5471 : #endif
5472 : }
5473 :
5474 : else
5475 : {
5476 : #ifndef HAVE_GEOS
5477 :
5478 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5479 : return nullptr;
5480 :
5481 : #else
5482 68 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
5483 : #endif /* HAVE_GEOS */
5484 : }
5485 : }
5486 :
5487 : /************************************************************************/
5488 : /* OGR_G_Union() */
5489 : /************************************************************************/
5490 :
5491 : /**
5492 : * \brief Compute union.
5493 : *
5494 : * Generates a new geometry which is the region of union of the
5495 : * two geometries operated on.
5496 : *
5497 : * Geometry validity is not checked. In case you are unsure of the validity
5498 : * of the input geometries, call IsValid() before, otherwise the result might
5499 : * be wrong.
5500 : *
5501 : * This function is the same as the C++ method OGRGeometry::Union().
5502 : *
5503 : * This function is built on the GEOS library, check it for the definition
5504 : * of the geometry operation.
5505 : * If OGR is built without the GEOS library, this function will always fail,
5506 : * issuing a CPLE_NotSupported error.
5507 : *
5508 : * @param hThis the geometry.
5509 : * @param hOther the other geometry.
5510 : *
5511 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5512 : * or NULL if an error occurs.
5513 : */
5514 :
5515 10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
5516 :
5517 : {
5518 10 : VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
5519 :
5520 20 : return OGRGeometry::ToHandle(
5521 20 : OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
5522 : }
5523 :
5524 : /************************************************************************/
5525 : /* UnionCascaded() */
5526 : /************************************************************************/
5527 :
5528 : /**
5529 : * \brief Compute union using cascading.
5530 : *
5531 : * Geometry validity is not checked. In case you are unsure of the validity
5532 : * of the input geometries, call IsValid() before, otherwise the result might
5533 : * be wrong.
5534 : *
5535 : * The input geometry must be a MultiPolygon.
5536 : *
5537 : * This method is the same as the C function OGR_G_UnionCascaded().
5538 : *
5539 : * This method is built on the GEOS library, check it for the definition
5540 : * of the geometry operation.
5541 : * If OGR is built without the GEOS library, this method will always fail,
5542 : * issuing a CPLE_NotSupported error.
5543 : *
5544 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5545 : *
5546 : *
5547 : * @deprecated Use UnaryUnion() instead
5548 : */
5549 :
5550 2 : OGRGeometry *OGRGeometry::UnionCascaded() const
5551 :
5552 : {
5553 : #ifndef HAVE_GEOS
5554 :
5555 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5556 : return nullptr;
5557 : #else
5558 :
5559 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5560 : if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
5561 : {
5562 : // GEOS < 3.11 crashes on an empty multipolygon input
5563 : auto poRet = new OGRGeometryCollection();
5564 : poRet->assignSpatialReference(getSpatialReference());
5565 : return poRet;
5566 : }
5567 : #endif
5568 2 : OGRGeometry *poOGRProduct = nullptr;
5569 :
5570 2 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5571 2 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5572 2 : if (hThisGeosGeom != nullptr)
5573 : {
5574 2 : GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
5575 2 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5576 :
5577 : poOGRProduct =
5578 2 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5579 : }
5580 2 : freeGEOSContext(hGEOSCtxt);
5581 :
5582 2 : return poOGRProduct;
5583 :
5584 : #endif // HAVE_GEOS
5585 : }
5586 :
5587 : /************************************************************************/
5588 : /* OGR_G_UnionCascaded() */
5589 : /************************************************************************/
5590 :
5591 : /**
5592 : * \brief Compute union using cascading.
5593 : *
5594 : * Geometry validity is not checked. In case you are unsure of the validity
5595 : * of the input geometries, call IsValid() before, otherwise the result might
5596 : * be wrong.
5597 : *
5598 : * The input geometry must be a MultiPolygon.
5599 : *
5600 : * This function is the same as the C++ method OGRGeometry::UnionCascaded().
5601 : *
5602 : * This function is built on the GEOS library, check it for the definition
5603 : * of the geometry operation.
5604 : * If OGR is built without the GEOS library, this function will always fail,
5605 : * issuing a CPLE_NotSupported error.
5606 : *
5607 : * @param hThis the geometry.
5608 : *
5609 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5610 : * or NULL if an error occurs.
5611 : *
5612 : * @deprecated Use OGR_G_UnaryUnion() instead
5613 : */
5614 :
5615 2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
5616 :
5617 : {
5618 2 : VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
5619 :
5620 2 : return OGRGeometry::ToHandle(
5621 2 : OGRGeometry::FromHandle(hThis)->UnionCascaded());
5622 : }
5623 :
5624 : /************************************************************************/
5625 : /* UnaryUnion() */
5626 : /************************************************************************/
5627 :
5628 : /**
5629 : * \brief Returns the union of all components of a single geometry.
5630 : *
5631 : * Usually used to convert a collection into the smallest set of polygons that
5632 : * cover the same area.
5633 : *
5634 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5635 : *
5636 : * This method is the same as the C function OGR_G_UnaryUnion().
5637 : *
5638 : * This method is built on the GEOS library, check it for the definition
5639 : * of the geometry operation.
5640 : * If OGR is built without the GEOS library, this method will always fail,
5641 : * issuing a CPLE_NotSupported error.
5642 : *
5643 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5644 : *
5645 : * @since GDAL 3.7
5646 : */
5647 :
5648 636 : OGRGeometry *OGRGeometry::UnaryUnion() const
5649 :
5650 : {
5651 : #ifndef HAVE_GEOS
5652 :
5653 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5654 : return nullptr;
5655 : #else
5656 :
5657 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5658 : if (IsEmpty())
5659 : {
5660 : // GEOS < 3.11 crashes on an empty geometry
5661 : auto poRet = new OGRGeometryCollection();
5662 : poRet->assignSpatialReference(getSpatialReference());
5663 : return poRet;
5664 : }
5665 : #endif
5666 636 : OGRGeometry *poOGRProduct = nullptr;
5667 :
5668 636 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5669 636 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5670 636 : if (hThisGeosGeom != nullptr)
5671 : {
5672 636 : GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5673 636 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5674 :
5675 : poOGRProduct =
5676 636 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5677 : }
5678 636 : freeGEOSContext(hGEOSCtxt);
5679 :
5680 636 : return poOGRProduct;
5681 :
5682 : #endif // HAVE_GEOS
5683 : }
5684 :
5685 : /************************************************************************/
5686 : /* OGR_G_UnaryUnion() */
5687 : /************************************************************************/
5688 :
5689 : /**
5690 : * \brief Returns the union of all components of a single geometry.
5691 : *
5692 : * Usually used to convert a collection into the smallest set of polygons that
5693 : * cover the same area.
5694 : *
5695 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5696 : *
5697 : * Geometry validity is not checked. In case you are unsure of the validity
5698 : * of the input geometries, call IsValid() before, otherwise the result might
5699 : * be wrong.
5700 : *
5701 : * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5702 : *
5703 : * This function is built on the GEOS library, check it for the definition
5704 : * of the geometry operation.
5705 : * If OGR is built without the GEOS library, this function will always fail,
5706 : * issuing a CPLE_NotSupported error.
5707 : *
5708 : * @param hThis the geometry.
5709 : *
5710 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5711 : * or NULL if an error occurs.
5712 : *
5713 : * @since GDAL 3.7
5714 : */
5715 :
5716 3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5717 :
5718 : {
5719 3 : VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5720 :
5721 3 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5722 : }
5723 :
5724 : /************************************************************************/
5725 : /* Difference() */
5726 : /************************************************************************/
5727 :
5728 : /**
5729 : * \brief Compute difference.
5730 : *
5731 : * Generates a new geometry which is the region of this geometry with the
5732 : * region of the second geometry removed.
5733 : *
5734 : * Geometry validity is not checked. In case you are unsure of the validity
5735 : * of the input geometries, call IsValid() before, otherwise the result might
5736 : * be wrong.
5737 : *
5738 : * This method is the same as the C function OGR_G_Difference().
5739 : *
5740 : * This method is built on the GEOS library, check it for the definition
5741 : * of the geometry operation.
5742 : * If OGR is built without the GEOS library, this method will always fail,
5743 : * issuing a CPLE_NotSupported error.
5744 : *
5745 : * @param poOtherGeom the other geometry removed from "this" geometry.
5746 : *
5747 : * @return a new geometry to be freed by the caller, or NULL if the difference
5748 : * is empty or if an error occurs.
5749 : */
5750 :
5751 : OGRGeometry *
5752 752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5753 :
5754 : {
5755 752 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5756 : {
5757 : #ifndef HAVE_SFCGAL
5758 :
5759 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5760 0 : return nullptr;
5761 :
5762 : #else
5763 :
5764 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5765 : if (poThis == nullptr)
5766 : return nullptr;
5767 :
5768 : sfcgal_geometry_t *poOther =
5769 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5770 : if (poOther == nullptr)
5771 : {
5772 : sfcgal_geometry_delete(poThis);
5773 : return nullptr;
5774 : }
5775 :
5776 : sfcgal_geometry_t *poRes =
5777 : sfcgal_geometry_difference_3d(poThis, poOther);
5778 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5779 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5780 : poOtherGeom->getSpatialReference() != nullptr &&
5781 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5782 : h_prodGeom->assignSpatialReference(getSpatialReference());
5783 :
5784 : sfcgal_geometry_delete(poThis);
5785 : sfcgal_geometry_delete(poOther);
5786 : sfcgal_geometry_delete(poRes);
5787 :
5788 : return h_prodGeom;
5789 :
5790 : #endif
5791 : }
5792 :
5793 : else
5794 : {
5795 : #ifndef HAVE_GEOS
5796 :
5797 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5798 : return nullptr;
5799 :
5800 : #else
5801 752 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5802 : #endif /* HAVE_GEOS */
5803 : }
5804 : }
5805 :
5806 : /************************************************************************/
5807 : /* OGR_G_Difference() */
5808 : /************************************************************************/
5809 :
5810 : /**
5811 : * \brief Compute difference.
5812 : *
5813 : * Generates a new geometry which is the region of this geometry with the
5814 : * region of the other geometry removed.
5815 : *
5816 : * Geometry validity is not checked. In case you are unsure of the validity
5817 : * of the input geometries, call IsValid() before, otherwise the result might
5818 : * be wrong.
5819 : *
5820 : * This function is the same as the C++ method OGRGeometry::Difference().
5821 : *
5822 : * This function is built on the GEOS library, check it for the definition
5823 : * of the geometry operation.
5824 : * If OGR is built without the GEOS library, this function will always fail,
5825 : * issuing a CPLE_NotSupported error.
5826 : *
5827 : * @param hThis the geometry.
5828 : * @param hOther the other geometry.
5829 : *
5830 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5831 : * or NULL if the difference is empty or if an error occurs.
5832 : */
5833 :
5834 6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5835 :
5836 : {
5837 6 : VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5838 :
5839 12 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5840 12 : OGRGeometry::FromHandle(hOther)));
5841 : }
5842 :
5843 : /************************************************************************/
5844 : /* SymDifference() */
5845 : /************************************************************************/
5846 :
5847 : /**
5848 : * \brief Compute symmetric difference.
5849 : *
5850 : * Generates a new geometry which is the symmetric difference of this
5851 : * geometry and the second geometry passed into the method.
5852 : *
5853 : * Geometry validity is not checked. In case you are unsure of the validity
5854 : * of the input geometries, call IsValid() before, otherwise the result might
5855 : * be wrong.
5856 : *
5857 : * This method is the same as the C function OGR_G_SymDifference().
5858 : *
5859 : * This method is built on the GEOS library, check it for the definition
5860 : * of the geometry operation.
5861 : * If OGR is built without the GEOS library, this method will always fail,
5862 : * issuing a CPLE_NotSupported error.
5863 : *
5864 : * @param poOtherGeom the other geometry.
5865 : *
5866 : * @return a new geometry to be freed by the caller, or NULL if the difference
5867 : * is empty or if an error occurs.
5868 : *
5869 : */
5870 :
5871 7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
5872 :
5873 : {
5874 : (void)poOtherGeom;
5875 7 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5876 : {
5877 : #ifndef HAVE_SFCGAL
5878 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5879 0 : return nullptr;
5880 : #else
5881 : OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5882 : if (poFirstDifference == nullptr)
5883 : return nullptr;
5884 :
5885 : OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5886 : if (poOtherDifference == nullptr)
5887 : {
5888 : delete poFirstDifference;
5889 : return nullptr;
5890 : }
5891 :
5892 : OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5893 : delete poFirstDifference;
5894 : delete poOtherDifference;
5895 : return poSymDiff;
5896 : #endif
5897 : }
5898 :
5899 : #ifndef HAVE_GEOS
5900 :
5901 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5902 : return nullptr;
5903 :
5904 : #else
5905 7 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5906 : #endif // HAVE_GEOS
5907 : }
5908 :
5909 : //! @cond Doxygen_Suppress
5910 : /**
5911 : * \brief Compute symmetric difference (deprecated)
5912 : *
5913 : * @deprecated
5914 : *
5915 : * @see OGRGeometry::SymDifference()
5916 : */
5917 : OGRGeometry *
5918 0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5919 :
5920 : {
5921 0 : return SymDifference(poOtherGeom);
5922 : }
5923 :
5924 : //! @endcond
5925 :
5926 : /************************************************************************/
5927 : /* OGR_G_SymDifference() */
5928 : /************************************************************************/
5929 :
5930 : /**
5931 : * \brief Compute symmetric difference.
5932 : *
5933 : * Generates a new geometry which is the symmetric difference of this
5934 : * geometry and the other geometry.
5935 : *
5936 : * Geometry validity is not checked. In case you are unsure of the validity
5937 : * of the input geometries, call IsValid() before, otherwise the result might
5938 : * be wrong.
5939 : *
5940 : * This function is the same as the C++ method
5941 : * OGRGeometry::SymmetricDifference().
5942 : *
5943 : * This function is built on the GEOS library, check it for the definition
5944 : * of the geometry operation.
5945 : * If OGR is built without the GEOS library, this function will always fail,
5946 : * issuing a CPLE_NotSupported error.
5947 : *
5948 : * @param hThis the geometry.
5949 : * @param hOther the other geometry.
5950 : *
5951 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5952 : * or NULL if the difference is empty or if an error occurs.
5953 : *
5954 : */
5955 :
5956 7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5957 :
5958 : {
5959 7 : VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5960 :
5961 14 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5962 14 : OGRGeometry::FromHandle(hOther)));
5963 : }
5964 :
5965 : /**
5966 : * \brief Compute symmetric difference (deprecated)
5967 : *
5968 : * @deprecated
5969 : *
5970 : * @see OGR_G_SymmetricDifference()
5971 : */
5972 0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5973 :
5974 : {
5975 0 : VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5976 :
5977 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5978 0 : OGRGeometry::FromHandle(hOther)));
5979 : }
5980 :
5981 : /************************************************************************/
5982 : /* Disjoint() */
5983 : /************************************************************************/
5984 :
5985 : /**
5986 : * \brief Test for disjointness.
5987 : *
5988 : * Tests if this geometry and the other passed into the method are disjoint.
5989 : *
5990 : * Geometry validity is not checked. In case you are unsure of the validity
5991 : * of the input geometries, call IsValid() before, otherwise the result might
5992 : * be wrong.
5993 : *
5994 : * This method is the same as the C function OGR_G_Disjoint().
5995 : *
5996 : * This method is built on the GEOS library, check it for the definition
5997 : * of the geometry operation.
5998 : * If OGR is built without the GEOS library, this method will always fail,
5999 : * issuing a CPLE_NotSupported error.
6000 : *
6001 : * @param poOtherGeom the geometry to compare to this geometry.
6002 : *
6003 : * @return TRUE if they are disjoint, otherwise FALSE.
6004 : */
6005 :
6006 8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
6007 :
6008 : {
6009 : (void)poOtherGeom;
6010 : #ifndef HAVE_GEOS
6011 :
6012 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6013 : return FALSE;
6014 :
6015 : #else
6016 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
6017 : #endif // HAVE_GEOS
6018 : }
6019 :
6020 : /************************************************************************/
6021 : /* OGR_G_Disjoint() */
6022 : /************************************************************************/
6023 :
6024 : /**
6025 : * \brief Test for disjointness.
6026 : *
6027 : * Tests if this geometry and the other geometry are disjoint.
6028 : *
6029 : * Geometry validity is not checked. In case you are unsure of the validity
6030 : * of the input geometries, call IsValid() before, otherwise the result might
6031 : * be wrong.
6032 : *
6033 : * This function is the same as the C++ method OGRGeometry::Disjoint().
6034 : *
6035 : * This function is built on the GEOS library, check it for the definition
6036 : * of the geometry operation.
6037 : * If OGR is built without the GEOS library, this function will always fail,
6038 : * issuing a CPLE_NotSupported error.
6039 : *
6040 : * @param hThis the geometry to compare.
6041 : * @param hOther the other geometry to compare.
6042 : *
6043 : * @return TRUE if they are disjoint, otherwise FALSE.
6044 : */
6045 8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
6046 :
6047 : {
6048 8 : VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
6049 :
6050 16 : return OGRGeometry::FromHandle(hThis)->Disjoint(
6051 16 : OGRGeometry::FromHandle(hOther));
6052 : }
6053 :
6054 : /************************************************************************/
6055 : /* Touches() */
6056 : /************************************************************************/
6057 :
6058 : /**
6059 : * \brief Test for touching.
6060 : *
6061 : * Tests if this geometry and the other passed into the method are touching.
6062 : *
6063 : * Geometry validity is not checked. In case you are unsure of the validity
6064 : * of the input geometries, call IsValid() before, otherwise the result might
6065 : * be wrong.
6066 : *
6067 : * This method is the same as the C function OGR_G_Touches().
6068 : *
6069 : * This method is built on the GEOS library, check it for the definition
6070 : * of the geometry operation.
6071 : * If OGR is built without the GEOS library, this method will always fail,
6072 : * issuing a CPLE_NotSupported error.
6073 : *
6074 : * @param poOtherGeom the geometry to compare to this geometry.
6075 : *
6076 : * @return TRUE if they are touching, otherwise FALSE.
6077 : */
6078 :
6079 11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
6080 :
6081 : {
6082 : (void)poOtherGeom;
6083 : #ifndef HAVE_GEOS
6084 :
6085 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6086 : return FALSE;
6087 :
6088 : #else
6089 11 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
6090 : #endif // HAVE_GEOS
6091 : }
6092 :
6093 : /************************************************************************/
6094 : /* OGR_G_Touches() */
6095 : /************************************************************************/
6096 : /**
6097 : * \brief Test for touching.
6098 : *
6099 : * Tests if this geometry and the other geometry are touching.
6100 : *
6101 : * Geometry validity is not checked. In case you are unsure of the validity
6102 : * of the input geometries, call IsValid() before, otherwise the result might
6103 : * be wrong.
6104 : *
6105 : * This function is the same as the C++ method OGRGeometry::Touches().
6106 : *
6107 : * This function is built on the GEOS library, check it for the definition
6108 : * of the geometry operation.
6109 : * If OGR is built without the GEOS library, this function will always fail,
6110 : * issuing a CPLE_NotSupported error.
6111 : *
6112 : * @param hThis the geometry to compare.
6113 : * @param hOther the other geometry to compare.
6114 : *
6115 : * @return TRUE if they are touching, otherwise FALSE.
6116 : */
6117 :
6118 8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
6119 :
6120 : {
6121 8 : VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
6122 :
6123 16 : return OGRGeometry::FromHandle(hThis)->Touches(
6124 16 : OGRGeometry::FromHandle(hOther));
6125 : }
6126 :
6127 : /************************************************************************/
6128 : /* Crosses() */
6129 : /************************************************************************/
6130 :
6131 : /**
6132 : * \brief Test for crossing.
6133 : *
6134 : * Tests if this geometry and the other passed into the method are crossing.
6135 : *
6136 : * Geometry validity is not checked. In case you are unsure of the validity
6137 : * of the input geometries, call IsValid() before, otherwise the result might
6138 : * be wrong.
6139 : *
6140 : * This method is the same as the C function OGR_G_Crosses().
6141 : *
6142 : * This method is built on the GEOS library, check it for the definition
6143 : * of the geometry operation.
6144 : * If OGR is built without the GEOS library, this method will always fail,
6145 : * issuing a CPLE_NotSupported error.
6146 : *
6147 : * @param poOtherGeom the geometry to compare to this geometry.
6148 : *
6149 : * @return TRUE if they are crossing, otherwise FALSE.
6150 : */
6151 :
6152 8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
6153 :
6154 : {
6155 8 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
6156 : {
6157 : #ifndef HAVE_SFCGAL
6158 :
6159 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
6160 0 : return FALSE;
6161 :
6162 : #else
6163 :
6164 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
6165 : if (poThis == nullptr)
6166 : return FALSE;
6167 :
6168 : sfcgal_geometry_t *poOther =
6169 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
6170 : if (poOther == nullptr)
6171 : {
6172 : sfcgal_geometry_delete(poThis);
6173 : return FALSE;
6174 : }
6175 :
6176 : int res = sfcgal_geometry_intersects_3d(poThis, poOther);
6177 :
6178 : sfcgal_geometry_delete(poThis);
6179 : sfcgal_geometry_delete(poOther);
6180 :
6181 : return (res == 1) ? TRUE : FALSE;
6182 :
6183 : #endif
6184 : }
6185 :
6186 : else
6187 : {
6188 :
6189 : #ifndef HAVE_GEOS
6190 :
6191 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6192 : return FALSE;
6193 :
6194 : #else
6195 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
6196 : #endif /* HAVE_GEOS */
6197 : }
6198 : }
6199 :
6200 : /************************************************************************/
6201 : /* OGR_G_Crosses() */
6202 : /************************************************************************/
6203 : /**
6204 : * \brief Test for crossing.
6205 : *
6206 : * Tests if this geometry and the other geometry are crossing.
6207 : *
6208 : * Geometry validity is not checked. In case you are unsure of the validity
6209 : * of the input geometries, call IsValid() before, otherwise the result might
6210 : * be wrong.
6211 : *
6212 : * This function is the same as the C++ method OGRGeometry::Crosses().
6213 : *
6214 : * This function is built on the GEOS library, check it for the definition
6215 : * of the geometry operation.
6216 : * If OGR is built without the GEOS library, this function will always fail,
6217 : * issuing a CPLE_NotSupported error.
6218 : *
6219 : * @param hThis the geometry to compare.
6220 : * @param hOther the other geometry to compare.
6221 : *
6222 : * @return TRUE if they are crossing, otherwise FALSE.
6223 : */
6224 :
6225 8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
6226 :
6227 : {
6228 8 : VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
6229 :
6230 16 : return OGRGeometry::FromHandle(hThis)->Crosses(
6231 16 : OGRGeometry::FromHandle(hOther));
6232 : }
6233 :
6234 : /************************************************************************/
6235 : /* Within() */
6236 : /************************************************************************/
6237 :
6238 : /**
6239 : * \brief Test for containment.
6240 : *
6241 : * Tests if actual geometry object is within the passed geometry.
6242 : *
6243 : * Geometry validity is not checked. In case you are unsure of the validity
6244 : * of the input geometries, call IsValid() before, otherwise the result might
6245 : * be wrong.
6246 : *
6247 : * This method is the same as the C function OGR_G_Within().
6248 : *
6249 : * This method is built on the GEOS library, check it for the definition
6250 : * of the geometry operation.
6251 : * If OGR is built without the GEOS library, this method will always fail,
6252 : * issuing a CPLE_NotSupported error.
6253 : *
6254 : * @param poOtherGeom the geometry to compare to this geometry.
6255 : *
6256 : * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
6257 : */
6258 :
6259 22417 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
6260 :
6261 : {
6262 : (void)poOtherGeom;
6263 : #ifndef HAVE_GEOS
6264 :
6265 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6266 : return FALSE;
6267 :
6268 : #else
6269 22417 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
6270 : #endif // HAVE_GEOS
6271 : }
6272 :
6273 : /************************************************************************/
6274 : /* OGR_G_Within() */
6275 : /************************************************************************/
6276 :
6277 : /**
6278 : * \brief Test for containment.
6279 : *
6280 : * Tests if this geometry is within the other geometry.
6281 : *
6282 : * Geometry validity is not checked. In case you are unsure of the validity
6283 : * of the input geometries, call IsValid() before, otherwise the result might
6284 : * be wrong.
6285 : *
6286 : * This function is the same as the C++ method OGRGeometry::Within().
6287 : *
6288 : * This function is built on the GEOS library, check it for the definition
6289 : * of the geometry operation.
6290 : * If OGR is built without the GEOS library, this function will always fail,
6291 : * issuing a CPLE_NotSupported error.
6292 : *
6293 : * @param hThis the geometry to compare.
6294 : * @param hOther the other geometry to compare.
6295 : *
6296 : * @return TRUE if hThis is within hOther, otherwise FALSE.
6297 : */
6298 7374 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
6299 :
6300 : {
6301 7374 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
6302 :
6303 14748 : return OGRGeometry::FromHandle(hThis)->Within(
6304 14748 : OGRGeometry::FromHandle(hOther));
6305 : }
6306 :
6307 : /************************************************************************/
6308 : /* Contains() */
6309 : /************************************************************************/
6310 :
6311 : /**
6312 : * \brief Test for containment.
6313 : *
6314 : * Tests if actual geometry object contains the passed geometry.
6315 : *
6316 : * Geometry validity is not checked. In case you are unsure of the validity
6317 : * of the input geometries, call IsValid() before, otherwise the result might
6318 : * be wrong.
6319 : *
6320 : * This method is the same as the C function OGR_G_Contains().
6321 : *
6322 : * This method is built on the GEOS library, check it for the definition
6323 : * of the geometry operation.
6324 : * If OGR is built without the GEOS library, this method will always fail,
6325 : * issuing a CPLE_NotSupported error.
6326 : *
6327 : * @param poOtherGeom the geometry to compare to this geometry.
6328 : *
6329 : * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
6330 : */
6331 :
6332 322 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
6333 :
6334 : {
6335 : (void)poOtherGeom;
6336 : #ifndef HAVE_GEOS
6337 :
6338 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6339 : return FALSE;
6340 :
6341 : #else
6342 322 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
6343 : #endif // HAVE_GEOS
6344 : }
6345 :
6346 : /************************************************************************/
6347 : /* OGR_G_Contains() */
6348 : /************************************************************************/
6349 :
6350 : /**
6351 : * \brief Test for containment.
6352 : *
6353 : * Tests if this geometry contains the other geometry.
6354 : *
6355 : * Geometry validity is not checked. In case you are unsure of the validity
6356 : * of the input geometries, call IsValid() before, otherwise the result might
6357 : * be wrong.
6358 : *
6359 : * This function is the same as the C++ method OGRGeometry::Contains().
6360 : *
6361 : * This function is built on the GEOS library, check it for the definition
6362 : * of the geometry operation.
6363 : * If OGR is built without the GEOS library, this function will always fail,
6364 : * issuing a CPLE_NotSupported error.
6365 : *
6366 : * @param hThis the geometry to compare.
6367 : * @param hOther the other geometry to compare.
6368 : *
6369 : * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
6370 : */
6371 10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
6372 :
6373 : {
6374 10 : VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
6375 :
6376 20 : return OGRGeometry::FromHandle(hThis)->Contains(
6377 20 : OGRGeometry::FromHandle(hOther));
6378 : }
6379 :
6380 : /************************************************************************/
6381 : /* Overlaps() */
6382 : /************************************************************************/
6383 :
6384 : /**
6385 : * \brief Test for overlap.
6386 : *
6387 : * Tests if this geometry and the other passed into the method overlap, that is
6388 : * their intersection has a non-zero area.
6389 : *
6390 : * Geometry validity is not checked. In case you are unsure of the validity
6391 : * of the input geometries, call IsValid() before, otherwise the result might
6392 : * be wrong.
6393 : *
6394 : * This method is the same as the C function OGR_G_Overlaps().
6395 : *
6396 : * This method is built on the GEOS library, check it for the definition
6397 : * of the geometry operation.
6398 : * If OGR is built without the GEOS library, this method will always fail,
6399 : * issuing a CPLE_NotSupported error.
6400 : *
6401 : * @param poOtherGeom the geometry to compare to this geometry.
6402 : *
6403 : * @return TRUE if they are overlapping, otherwise FALSE.
6404 : */
6405 :
6406 7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
6407 :
6408 : {
6409 : (void)poOtherGeom;
6410 : #ifndef HAVE_GEOS
6411 :
6412 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6413 : return FALSE;
6414 :
6415 : #else
6416 7 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6417 : #endif // HAVE_GEOS
6418 : }
6419 :
6420 : /************************************************************************/
6421 : /* OGR_G_Overlaps() */
6422 : /************************************************************************/
6423 : /**
6424 : * \brief Test for overlap.
6425 : *
6426 : * Tests if this geometry and the other geometry overlap, that is their
6427 : * intersection has a non-zero area.
6428 : *
6429 : * Geometry validity is not checked. In case you are unsure of the validity
6430 : * of the input geometries, call IsValid() before, otherwise the result might
6431 : * be wrong.
6432 : *
6433 : * This function is the same as the C++ method OGRGeometry::Overlaps().
6434 : *
6435 : * This function is built on the GEOS library, check it for the definition
6436 : * of the geometry operation.
6437 : * If OGR is built without the GEOS library, this function will always fail,
6438 : * issuing a CPLE_NotSupported error.
6439 : *
6440 : * @param hThis the geometry to compare.
6441 : * @param hOther the other geometry to compare.
6442 : *
6443 : * @return TRUE if they are overlapping, otherwise FALSE.
6444 : */
6445 :
6446 7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6447 :
6448 : {
6449 7 : VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6450 :
6451 14 : return OGRGeometry::FromHandle(hThis)->Overlaps(
6452 14 : OGRGeometry::FromHandle(hOther));
6453 : }
6454 :
6455 : /************************************************************************/
6456 : /* closeRings() */
6457 : /************************************************************************/
6458 :
6459 : /**
6460 : * \brief Force rings to be closed.
6461 : *
6462 : * If this geometry, or any contained geometries has polygon rings that
6463 : * are not closed, they will be closed by adding the starting point at
6464 : * the end.
6465 : */
6466 :
6467 1264 : void OGRGeometry::closeRings()
6468 : {
6469 1264 : }
6470 :
6471 : /************************************************************************/
6472 : /* OGR_G_CloseRings() */
6473 : /************************************************************************/
6474 :
6475 : /**
6476 : * \brief Force rings to be closed.
6477 : *
6478 : * If this geometry, or any contained geometries has polygon rings that
6479 : * are not closed, they will be closed by adding the starting point at
6480 : * the end.
6481 : *
6482 : * @param hGeom handle to the geometry.
6483 : */
6484 :
6485 6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
6486 :
6487 : {
6488 6 : VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6489 :
6490 6 : OGRGeometry::FromHandle(hGeom)->closeRings();
6491 : }
6492 :
6493 : /************************************************************************/
6494 : /* Centroid() */
6495 : /************************************************************************/
6496 :
6497 : /**
6498 : * \brief Compute the geometry centroid.
6499 : *
6500 : * The centroid location is applied to the passed in OGRPoint object.
6501 : * The centroid is not necessarily within the geometry.
6502 : *
6503 : * This method relates to the SFCOM ISurface::get_Centroid() method
6504 : * however the current implementation based on GEOS can operate on other
6505 : * geometry types such as multipoint, linestring, geometrycollection such as
6506 : * multipolygons.
6507 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6508 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6509 : * (multipolygons).
6510 : *
6511 : * This function is the same as the C function OGR_G_Centroid().
6512 : *
6513 : * This function is built on the GEOS library, check it for the definition
6514 : * of the geometry operation.
6515 : * If OGR is built without the GEOS library, this function will always fail,
6516 : * issuing a CPLE_NotSupported error.
6517 : *
6518 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6519 : *
6520 : * to OGRPolygon)
6521 : */
6522 :
6523 5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6524 :
6525 : {
6526 5 : if (poPoint == nullptr)
6527 0 : return OGRERR_FAILURE;
6528 :
6529 : #ifndef HAVE_GEOS
6530 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6531 : return OGRERR_FAILURE;
6532 :
6533 : #else
6534 :
6535 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6536 : GEOSGeom hThisGeosGeom =
6537 5 : exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6538 :
6539 5 : if (hThisGeosGeom != nullptr)
6540 : {
6541 5 : GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6542 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6543 :
6544 5 : if (hOtherGeosGeom == nullptr)
6545 : {
6546 0 : freeGEOSContext(hGEOSCtxt);
6547 0 : return OGRERR_FAILURE;
6548 : }
6549 :
6550 : OGRGeometry *poCentroidGeom =
6551 5 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6552 :
6553 5 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6554 :
6555 5 : if (poCentroidGeom == nullptr)
6556 : {
6557 0 : freeGEOSContext(hGEOSCtxt);
6558 0 : return OGRERR_FAILURE;
6559 : }
6560 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6561 : {
6562 0 : delete poCentroidGeom;
6563 0 : freeGEOSContext(hGEOSCtxt);
6564 0 : return OGRERR_FAILURE;
6565 : }
6566 :
6567 5 : if (getSpatialReference() != nullptr)
6568 0 : poCentroidGeom->assignSpatialReference(getSpatialReference());
6569 :
6570 5 : OGRPoint *poCentroid = poCentroidGeom->toPoint();
6571 :
6572 5 : if (!poCentroid->IsEmpty())
6573 : {
6574 4 : poPoint->setX(poCentroid->getX());
6575 4 : poPoint->setY(poCentroid->getY());
6576 : }
6577 : else
6578 : {
6579 1 : poPoint->empty();
6580 : }
6581 :
6582 5 : delete poCentroidGeom;
6583 :
6584 5 : freeGEOSContext(hGEOSCtxt);
6585 5 : return OGRERR_NONE;
6586 : }
6587 : else
6588 : {
6589 0 : freeGEOSContext(hGEOSCtxt);
6590 0 : return OGRERR_FAILURE;
6591 : }
6592 :
6593 : #endif // HAVE_GEOS
6594 : }
6595 :
6596 : /************************************************************************/
6597 : /* OGR_G_Centroid() */
6598 : /************************************************************************/
6599 :
6600 : /**
6601 : * \brief Compute the geometry centroid.
6602 : *
6603 : * The centroid location is applied to the passed in OGRPoint object.
6604 : * The centroid is not necessarily within the geometry.
6605 : *
6606 : * This method relates to the SFCOM ISurface::get_Centroid() method
6607 : * however the current implementation based on GEOS can operate on other
6608 : * geometry types such as multipoint, linestring, geometrycollection such as
6609 : * multipolygons.
6610 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6611 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6612 : * (multipolygons).
6613 : *
6614 : * This function is the same as the C++ method OGRGeometry::Centroid().
6615 : *
6616 : * This function is built on the GEOS library, check it for the definition
6617 : * of the geometry operation.
6618 : * If OGR is built without the GEOS library, this function will always fail,
6619 : * issuing a CPLE_NotSupported error.
6620 : *
6621 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6622 : */
6623 :
6624 5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6625 :
6626 : {
6627 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6628 :
6629 5 : OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6630 5 : if (poCentroidGeom == nullptr)
6631 0 : return OGRERR_FAILURE;
6632 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6633 : {
6634 0 : CPLError(CE_Failure, CPLE_AppDefined,
6635 : "Passed wrong geometry type as centroid argument.");
6636 0 : return OGRERR_FAILURE;
6637 : }
6638 :
6639 5 : return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6640 : }
6641 :
6642 : /************************************************************************/
6643 : /* OGR_G_PointOnSurface() */
6644 : /************************************************************************/
6645 :
6646 : /**
6647 : * \brief Returns a point guaranteed to lie on the surface.
6648 : *
6649 : * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6650 : * however the current implementation based on GEOS can operate on other
6651 : * geometry types than the types that are supported by SQL/MM-Part 3 :
6652 : * surfaces (polygons) and multisurfaces (multipolygons).
6653 : *
6654 : * This method is built on the GEOS library, check it for the definition
6655 : * of the geometry operation.
6656 : * If OGR is built without the GEOS library, this method will always fail,
6657 : * issuing a CPLE_NotSupported error.
6658 : *
6659 : * @param hGeom the geometry to operate on.
6660 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6661 : * or NULL if an error occurs.
6662 : *
6663 : */
6664 :
6665 4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6666 :
6667 : {
6668 4 : VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6669 :
6670 : #ifndef HAVE_GEOS
6671 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6672 : return nullptr;
6673 : #else
6674 :
6675 4 : OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6676 :
6677 4 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6678 4 : GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6679 :
6680 4 : if (hThisGeosGeom != nullptr)
6681 : {
6682 : GEOSGeom hOtherGeosGeom =
6683 4 : GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6684 4 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6685 :
6686 4 : if (hOtherGeosGeom == nullptr)
6687 : {
6688 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6689 0 : return nullptr;
6690 : }
6691 :
6692 : OGRGeometry *poInsidePointGeom =
6693 4 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6694 :
6695 4 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6696 :
6697 4 : if (poInsidePointGeom == nullptr)
6698 : {
6699 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6700 0 : return nullptr;
6701 : }
6702 4 : if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6703 : {
6704 0 : delete poInsidePointGeom;
6705 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6706 0 : return nullptr;
6707 : }
6708 :
6709 4 : if (poThis->getSpatialReference() != nullptr)
6710 0 : poInsidePointGeom->assignSpatialReference(
6711 0 : poThis->getSpatialReference());
6712 :
6713 4 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6714 4 : return OGRGeometry::ToHandle(poInsidePointGeom);
6715 : }
6716 :
6717 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6718 0 : return nullptr;
6719 : #endif
6720 : }
6721 :
6722 : /************************************************************************/
6723 : /* PointOnSurfaceInternal() */
6724 : /************************************************************************/
6725 :
6726 : //! @cond Doxygen_Suppress
6727 0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6728 : {
6729 0 : if (poPoint == nullptr || poPoint->IsEmpty())
6730 0 : return OGRERR_FAILURE;
6731 :
6732 0 : OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6733 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6734 0 : if (hInsidePoint == nullptr)
6735 0 : return OGRERR_FAILURE;
6736 :
6737 0 : OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6738 0 : if (poInsidePoint->IsEmpty())
6739 : {
6740 0 : poPoint->empty();
6741 : }
6742 : else
6743 : {
6744 0 : poPoint->setX(poInsidePoint->getX());
6745 0 : poPoint->setY(poInsidePoint->getY());
6746 : }
6747 :
6748 0 : OGR_G_DestroyGeometry(hInsidePoint);
6749 :
6750 0 : return OGRERR_NONE;
6751 : }
6752 :
6753 : //! @endcond
6754 :
6755 : /************************************************************************/
6756 : /* Simplify() */
6757 : /************************************************************************/
6758 :
6759 : /**
6760 : * \brief Simplify the geometry.
6761 : *
6762 : * This function is the same as the C function OGR_G_Simplify().
6763 : *
6764 : * This function is built on the GEOS library, check it for the definition
6765 : * of the geometry operation.
6766 : * If OGR is built without the GEOS library, this function will always fail,
6767 : * issuing a CPLE_NotSupported error.
6768 : *
6769 : * @param dTolerance the distance tolerance for the simplification.
6770 : *
6771 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6772 : *
6773 : */
6774 :
6775 55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
6776 :
6777 : {
6778 : (void)dTolerance;
6779 : #ifndef HAVE_GEOS
6780 :
6781 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6782 : return nullptr;
6783 :
6784 : #else
6785 55 : OGRGeometry *poOGRProduct = nullptr;
6786 :
6787 55 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6788 55 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6789 55 : if (hThisGeosGeom != nullptr)
6790 : {
6791 : GEOSGeom hGeosProduct =
6792 55 : GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6793 55 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6794 : poOGRProduct =
6795 55 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6796 : }
6797 55 : freeGEOSContext(hGEOSCtxt);
6798 55 : return poOGRProduct;
6799 :
6800 : #endif // HAVE_GEOS
6801 : }
6802 :
6803 : /************************************************************************/
6804 : /* OGR_G_Simplify() */
6805 : /************************************************************************/
6806 :
6807 : /**
6808 : * \brief Compute a simplified geometry.
6809 : *
6810 : * This function is the same as the C++ method OGRGeometry::Simplify().
6811 : *
6812 : * This function is built on the GEOS library, check it for the definition
6813 : * of the geometry operation.
6814 : * If OGR is built without the GEOS library, this function will always fail,
6815 : * issuing a CPLE_NotSupported error.
6816 : *
6817 : * @param hThis the geometry.
6818 : * @param dTolerance the distance tolerance for the simplification.
6819 : *
6820 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6821 : * or NULL if an error occurs.
6822 : *
6823 : */
6824 :
6825 1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6826 :
6827 : {
6828 1 : VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6829 1 : return OGRGeometry::ToHandle(
6830 1 : OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6831 : }
6832 :
6833 : /************************************************************************/
6834 : /* SimplifyPreserveTopology() */
6835 : /************************************************************************/
6836 :
6837 : /**
6838 : * \brief Simplify the geometry while preserving topology.
6839 : *
6840 : * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6841 : *
6842 : * This function is built on the GEOS library, check it for the definition
6843 : * of the geometry operation.
6844 : * If OGR is built without the GEOS library, this function will always fail,
6845 : * issuing a CPLE_NotSupported error.
6846 : *
6847 : * @param dTolerance the distance tolerance for the simplification.
6848 : *
6849 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6850 : *
6851 : */
6852 :
6853 17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
6854 :
6855 : {
6856 : (void)dTolerance;
6857 : #ifndef HAVE_GEOS
6858 :
6859 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6860 : return nullptr;
6861 :
6862 : #else
6863 17 : OGRGeometry *poOGRProduct = nullptr;
6864 :
6865 17 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6866 17 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6867 17 : if (hThisGeosGeom != nullptr)
6868 : {
6869 17 : GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6870 : hGEOSCtxt, hThisGeosGeom, dTolerance);
6871 17 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6872 : poOGRProduct =
6873 17 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6874 : }
6875 17 : freeGEOSContext(hGEOSCtxt);
6876 17 : return poOGRProduct;
6877 :
6878 : #endif // HAVE_GEOS
6879 : }
6880 :
6881 : /************************************************************************/
6882 : /* OGR_G_SimplifyPreserveTopology() */
6883 : /************************************************************************/
6884 :
6885 : /**
6886 : * \brief Simplify the geometry while preserving topology.
6887 : *
6888 : * This function is the same as the C++ method
6889 : * OGRGeometry::SimplifyPreserveTopology().
6890 : *
6891 : * This function is built on the GEOS library, check it for the definition
6892 : * of the geometry operation.
6893 : * If OGR is built without the GEOS library, this function will always fail,
6894 : * issuing a CPLE_NotSupported error.
6895 : *
6896 : * @param hThis the geometry.
6897 : * @param dTolerance the distance tolerance for the simplification.
6898 : *
6899 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6900 : * or NULL if an error occurs.
6901 : *
6902 : */
6903 :
6904 1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6905 : double dTolerance)
6906 :
6907 : {
6908 1 : VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6909 1 : return OGRGeometry::ToHandle(
6910 1 : OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6911 : }
6912 :
6913 : /************************************************************************/
6914 : /* roundCoordinates() */
6915 : /************************************************************************/
6916 :
6917 : /** Round coordinates of the geometry to the specified precision.
6918 : *
6919 : * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6920 : * will return valid geometries, whereas roundCoordinates() does not make
6921 : * such guarantee and may return geometries with invalidities, if they are
6922 : * not compatible with the specified precision. roundCoordinates() supports
6923 : * curve geometries, whereas SetPrecision() does not currently.
6924 : *
6925 : * One use case for roundCoordinates() is to undo the effect of
6926 : * quantizeCoordinates().
6927 : *
6928 : * @param sPrecision Contains the precision requirements.
6929 : * @since GDAL 3.9
6930 : */
6931 39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6932 : {
6933 : struct Rounder : public OGRDefaultGeometryVisitor
6934 : {
6935 : const OGRGeomCoordinatePrecision &m_precision;
6936 : const double m_invXYResolution;
6937 : const double m_invZResolution;
6938 : const double m_invMResolution;
6939 :
6940 39 : explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6941 39 : : m_precision(sPrecisionIn),
6942 39 : m_invXYResolution(m_precision.dfXYResolution !=
6943 : OGRGeomCoordinatePrecision::UNKNOWN
6944 39 : ? 1.0 / m_precision.dfXYResolution
6945 : : 0.0),
6946 39 : m_invZResolution(m_precision.dfZResolution !=
6947 : OGRGeomCoordinatePrecision::UNKNOWN
6948 39 : ? 1.0 / m_precision.dfZResolution
6949 : : 0.0),
6950 39 : m_invMResolution(m_precision.dfMResolution !=
6951 : OGRGeomCoordinatePrecision::UNKNOWN
6952 39 : ? 1.0 / m_precision.dfMResolution
6953 117 : : 0.0)
6954 : {
6955 39 : }
6956 :
6957 : using OGRDefaultGeometryVisitor::visit;
6958 :
6959 379 : void visit(OGRPoint *poPoint) override
6960 : {
6961 379 : if (m_precision.dfXYResolution !=
6962 : OGRGeomCoordinatePrecision::UNKNOWN)
6963 : {
6964 379 : poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6965 379 : m_precision.dfXYResolution);
6966 379 : poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6967 379 : m_precision.dfXYResolution);
6968 : }
6969 758 : if (m_precision.dfZResolution !=
6970 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6971 4 : poPoint->Is3D())
6972 : {
6973 4 : poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6974 4 : m_precision.dfZResolution);
6975 : }
6976 758 : if (m_precision.dfMResolution !=
6977 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6978 4 : poPoint->IsMeasured())
6979 : {
6980 4 : poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6981 4 : m_precision.dfMResolution);
6982 : }
6983 379 : }
6984 : };
6985 :
6986 78 : Rounder rounder(sPrecision);
6987 39 : accept(&rounder);
6988 39 : }
6989 :
6990 : /************************************************************************/
6991 : /* SetPrecision() */
6992 : /************************************************************************/
6993 :
6994 : /** Set the geometry's precision, rounding all its coordinates to the precision
6995 : * grid, and making sure the geometry is still valid.
6996 : *
6997 : * This is a stronger version of roundCoordinates().
6998 : *
6999 : * Note that at time of writing GEOS does no supported curve geometries. So
7000 : * currently if this function is called on such a geometry, OGR will first call
7001 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7002 : * that it is unlikely to yield to the expected result.
7003 : *
7004 : * This function is the same as the C function OGR_G_SetPrecision().
7005 : *
7006 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7007 : * GEOS library. Check it for the definition of the geometry operation.
7008 : * If OGR is built without the GEOS library, this function will always fail,
7009 : * issuing a CPLE_NotSupported error.
7010 : *
7011 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7012 : * precision.
7013 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7014 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7015 : *
7016 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7017 : *
7018 : * @since GDAL 3.9
7019 : */
7020 :
7021 6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
7022 : {
7023 : (void)dfGridSize;
7024 : (void)nFlags;
7025 : #ifndef HAVE_GEOS
7026 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7027 : return nullptr;
7028 :
7029 : #else
7030 6 : OGRGeometry *poOGRProduct = nullptr;
7031 :
7032 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7033 6 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7034 6 : if (hThisGeosGeom != nullptr)
7035 : {
7036 6 : GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
7037 : hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
7038 6 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7039 : poOGRProduct =
7040 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7041 : }
7042 6 : freeGEOSContext(hGEOSCtxt);
7043 6 : return poOGRProduct;
7044 :
7045 : #endif // HAVE_GEOS
7046 : }
7047 :
7048 : /************************************************************************/
7049 : /* OGR_G_SetPrecision() */
7050 : /************************************************************************/
7051 :
7052 : /** Set the geometry's precision, rounding all its coordinates to the precision
7053 : * grid, and making sure the geometry is still valid.
7054 : *
7055 : * This is a stronger version of roundCoordinates().
7056 : *
7057 : * Note that at time of writing GEOS does no supported curve geometries. So
7058 : * currently if this function is called on such a geometry, OGR will first call
7059 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7060 : * that it is unlikely to yield to the expected result.
7061 : *
7062 : * This function is the same as the C++ method OGRGeometry::SetPrecision().
7063 : *
7064 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7065 : * GEOS library. Check it for the definition of the geometry operation.
7066 : * If OGR is built without the GEOS library, this function will always fail,
7067 : * issuing a CPLE_NotSupported error.
7068 : *
7069 : * @param hThis the geometry.
7070 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7071 : * precision.
7072 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7073 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7074 : *
7075 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7076 : * or NULL if an error occurs.
7077 : *
7078 : * @since GDAL 3.9
7079 : */
7080 1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
7081 : int nFlags)
7082 : {
7083 1 : VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
7084 1 : return OGRGeometry::ToHandle(
7085 1 : OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
7086 : }
7087 :
7088 : /************************************************************************/
7089 : /* DelaunayTriangulation() */
7090 : /************************************************************************/
7091 :
7092 : /**
7093 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7094 : *
7095 : * This function is the same as the C function OGR_G_DelaunayTriangulation().
7096 : *
7097 : * This function is built on the GEOS library, v3.4 or above.
7098 : * If OGR is built without the GEOS library, this function will always fail,
7099 : * issuing a CPLE_NotSupported error.
7100 : *
7101 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7102 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7103 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7104 : *
7105 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7106 : */
7107 :
7108 : #ifndef HAVE_GEOS
7109 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
7110 : int /*bOnlyEdges*/) const
7111 : {
7112 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7113 : return nullptr;
7114 : }
7115 : #else
7116 1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
7117 : int bOnlyEdges) const
7118 : {
7119 1 : OGRGeometry *poOGRProduct = nullptr;
7120 :
7121 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7122 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7123 1 : if (hThisGeosGeom != nullptr)
7124 : {
7125 1 : GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
7126 : hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
7127 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7128 : poOGRProduct =
7129 1 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7130 : }
7131 1 : freeGEOSContext(hGEOSCtxt);
7132 1 : return poOGRProduct;
7133 : }
7134 : #endif
7135 :
7136 : /************************************************************************/
7137 : /* OGR_G_DelaunayTriangulation() */
7138 : /************************************************************************/
7139 :
7140 : /**
7141 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7142 : *
7143 : * This function is the same as the C++ method
7144 : * OGRGeometry::DelaunayTriangulation().
7145 : *
7146 : * This function is built on the GEOS library, v3.4 or above.
7147 : * If OGR is built without the GEOS library, this function will always fail,
7148 : * issuing a CPLE_NotSupported error.
7149 : *
7150 : * @param hThis the geometry.
7151 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7152 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7153 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7154 : *
7155 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7156 : * or NULL if an error occurs.
7157 : */
7158 :
7159 1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
7160 : int bOnlyEdges)
7161 :
7162 : {
7163 1 : VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
7164 :
7165 1 : return OGRGeometry::ToHandle(
7166 : OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
7167 1 : bOnlyEdges));
7168 : }
7169 :
7170 : /************************************************************************/
7171 : /* ConstrainedDelaunayTriangulation() */
7172 : /************************************************************************/
7173 :
7174 : /**
7175 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7176 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7177 : * geometry collection.
7178 : *
7179 : * This function is the same as the C function
7180 : * OGR_G_ConstrainedDelaunayTriangulation().
7181 : *
7182 : * This function is built on the GEOS library, v3.10 or above.
7183 : * If OGR is built without the GEOS library, this function will always fail,
7184 : * issuing a CPLE_NotSupported error.
7185 : *
7186 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7187 : *
7188 : * @since OGR 3.12
7189 : */
7190 :
7191 3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
7192 : {
7193 : #ifndef HAVE_GEOS
7194 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7195 : return nullptr;
7196 : #elif !(GEOS_VERSION_MAJOR > 3 || \
7197 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
7198 : CPLError(
7199 : CE_Failure, CPLE_NotSupported,
7200 : "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
7201 : return nullptr;
7202 : #else
7203 :
7204 3 : OGRGeometry *poOGRProduct = nullptr;
7205 :
7206 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7207 3 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7208 3 : if (hThisGeosGeom != nullptr)
7209 : {
7210 : GEOSGeom hGeosProduct =
7211 3 : GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
7212 3 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7213 : poOGRProduct =
7214 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7215 : }
7216 3 : freeGEOSContext(hGEOSCtxt);
7217 3 : return poOGRProduct;
7218 : #endif
7219 : }
7220 :
7221 : /************************************************************************/
7222 : /* OGR_G_ConstrainedDelaunayTriangulation() */
7223 : /************************************************************************/
7224 :
7225 : /**
7226 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7227 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7228 : * geometry collection.
7229 : *
7230 : * This function is the same as the C++ method
7231 : * OGRGeometry::ConstrainedDelaunayTriangulation().
7232 : *
7233 : * This function is built on the GEOS library, v3.10 or above.
7234 : * If OGR is built without the GEOS library, this function will always fail,
7235 : * issuing a CPLE_NotSupported error.
7236 : *
7237 : * @param hThis the geometry.
7238 : *
7239 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7240 : * or NULL if an error occurs.
7241 : *
7242 : * @since OGR 3.12
7243 : */
7244 :
7245 3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
7246 : {
7247 3 : VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
7248 :
7249 3 : return OGRGeometry::ToHandle(
7250 3 : OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
7251 : }
7252 :
7253 : /************************************************************************/
7254 : /* Polygonize() */
7255 : /************************************************************************/
7256 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
7257 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
7258 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
7259 : /* AMBIENTALE */
7260 : /************************************************************************/
7261 :
7262 : /**
7263 : * \brief Polygonizes a set of sparse edges.
7264 : *
7265 : * A new geometry object is created and returned containing a collection
7266 : * of reassembled Polygons: NULL will be returned if the input collection
7267 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7268 : * into Polygons is impossible due to topological inconsistencies.
7269 : *
7270 : * This method is the same as the C function OGR_G_Polygonize().
7271 : *
7272 : * This method is built on the GEOS library, check it for the definition
7273 : * of the geometry operation.
7274 : * If OGR is built without the GEOS library, this method will always fail,
7275 : * issuing a CPLE_NotSupported error.
7276 : *
7277 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7278 : *
7279 : */
7280 :
7281 117 : OGRGeometry *OGRGeometry::Polygonize() const
7282 :
7283 : {
7284 : #ifndef HAVE_GEOS
7285 :
7286 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7287 : return nullptr;
7288 :
7289 : #else
7290 :
7291 117 : const OGRGeometryCollection *poColl = nullptr;
7292 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
7293 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
7294 116 : poColl = toGeometryCollection();
7295 : else
7296 1 : return nullptr;
7297 :
7298 116 : const int nCount = poColl->getNumGeometries();
7299 :
7300 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
7301 116 : bool bError = false;
7302 :
7303 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7304 :
7305 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
7306 747 : for (int ig = 0; ig < nCount; ig++)
7307 : {
7308 631 : GEOSGeom hGeosGeom = nullptr;
7309 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
7310 1262 : if (poChild == nullptr ||
7311 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
7312 1 : bError = true;
7313 : else
7314 : {
7315 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
7316 630 : if (hGeosGeom == nullptr)
7317 0 : bError = true;
7318 : }
7319 631 : pahGeosGeomList[ig] = hGeosGeom;
7320 : }
7321 :
7322 116 : if (!bError)
7323 : {
7324 : GEOSGeom hGeosPolygs =
7325 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
7326 :
7327 : poPolygsOGRGeom =
7328 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7329 : }
7330 :
7331 747 : for (int ig = 0; ig < nCount; ig++)
7332 : {
7333 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
7334 631 : if (hGeosGeom != nullptr)
7335 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
7336 : }
7337 116 : delete[] pahGeosGeomList;
7338 116 : freeGEOSContext(hGEOSCtxt);
7339 :
7340 116 : return poPolygsOGRGeom;
7341 :
7342 : #endif // HAVE_GEOS
7343 : }
7344 :
7345 : /************************************************************************/
7346 : /* OGR_G_Polygonize() */
7347 : /************************************************************************/
7348 : /**
7349 : * \brief Polygonizes a set of sparse edges.
7350 : *
7351 : * A new geometry object is created and returned containing a collection
7352 : * of reassembled Polygons: NULL will be returned if the input collection
7353 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7354 : * into Polygons is impossible due to topological inconsistencies.
7355 : *
7356 : * This function is the same as the C++ method OGRGeometry::Polygonize().
7357 : *
7358 : * This function is built on the GEOS library, check it for the definition
7359 : * of the geometry operation.
7360 : * If OGR is built without the GEOS library, this function will always fail,
7361 : * issuing a CPLE_NotSupported error.
7362 : *
7363 : * @param hTarget The Geometry to be polygonized.
7364 : *
7365 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7366 : * or NULL if an error occurs.
7367 : *
7368 : */
7369 :
7370 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
7371 :
7372 : {
7373 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
7374 :
7375 3 : return OGRGeometry::ToHandle(
7376 3 : OGRGeometry::FromHandle(hTarget)->Polygonize());
7377 : }
7378 :
7379 : /************************************************************************/
7380 : /* BuildArea() */
7381 : /************************************************************************/
7382 :
7383 : /**
7384 : * \brief Polygonize a linework assuming inner polygons are holes.
7385 : *
7386 : * This method is the same as the C function OGR_G_BuildArea().
7387 : *
7388 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
7389 : * Additionally, holes are dropped and the result is unified producing
7390 : * a single Polygon or a MultiPolygon.
7391 : *
7392 : * A new geometry object is created and returned: NULL on failure,
7393 : * empty GeometryCollection if the input geometry cannot be polygonized,
7394 : * Polygon or MultiPolygon on success.
7395 : *
7396 : * This method is built on the GEOSBuildArea_r() function of the GEOS
7397 : * library, check it for the definition of the geometry operation.
7398 : * If OGR is built without the GEOS library, this method will always fail,
7399 : * issuing a CPLE_NotSupported error.
7400 : *
7401 : * @return a newly allocated geometry now owned by the caller,
7402 : * or NULL on failure.
7403 : *
7404 : * @since OGR 3.11
7405 : */
7406 :
7407 30 : OGRGeometry *OGRGeometry::BuildArea() const
7408 :
7409 : {
7410 : #ifndef HAVE_GEOS
7411 :
7412 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7413 : return nullptr;
7414 :
7415 : #else
7416 :
7417 30 : OGRGeometry *poPolygsOGRGeom = nullptr;
7418 :
7419 30 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7420 30 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7421 30 : if (hThisGeosGeom != nullptr)
7422 : {
7423 30 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7424 : poPolygsOGRGeom =
7425 30 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7426 30 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7427 : }
7428 30 : freeGEOSContext(hGEOSCtxt);
7429 :
7430 30 : return poPolygsOGRGeom;
7431 :
7432 : #endif // HAVE_GEOS
7433 : }
7434 :
7435 : /************************************************************************/
7436 : /* OGR_G_BuildArea() */
7437 : /************************************************************************/
7438 :
7439 : /**
7440 : * \brief Polygonize a linework assuming inner polygons are holes.
7441 : *
7442 : * This function is the same as the C++ method OGRGeometry::BuildArea().
7443 : *
7444 : * Polygonization is performed similarly to OGR_G_Polygonize().
7445 : * Additionally, holes are dropped and the result is unified producing
7446 : * a single Polygon or a MultiPolygon.
7447 : *
7448 : * A new geometry object is created and returned: NULL on failure,
7449 : * empty GeometryCollection if the input geometry cannot be polygonized,
7450 : * Polygon or MultiPolygon on success.
7451 : *
7452 : * This function is built on the GEOSBuildArea_r() function of the GEOS
7453 : * library, check it for the definition of the geometry operation.
7454 : * If OGR is built without the GEOS library, this function will always fail,
7455 : * issuing a CPLE_NotSupported error.
7456 : *
7457 : * @param hGeom handle on the geometry to polygonize.
7458 : *
7459 : * @return a handle on newly allocated geometry now owned by the caller,
7460 : * or NULL on failure.
7461 : *
7462 : * @since OGR 3.11
7463 : */
7464 :
7465 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7466 :
7467 : {
7468 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7469 :
7470 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7471 : }
7472 :
7473 : /************************************************************************/
7474 : /* swapXY() */
7475 : /************************************************************************/
7476 :
7477 : /**
7478 : * \brief Swap x and y coordinates.
7479 : *
7480 : */
7481 :
7482 0 : void OGRGeometry::swapXY()
7483 :
7484 : {
7485 0 : }
7486 :
7487 : /************************************************************************/
7488 : /* swapXY() */
7489 : /************************************************************************/
7490 :
7491 : /**
7492 : * \brief Swap x and y coordinates.
7493 : *
7494 : * @param hGeom geometry.
7495 : */
7496 :
7497 32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7498 : {
7499 32 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7500 :
7501 32 : OGRGeometry::FromHandle(hGeom)->swapXY();
7502 : }
7503 :
7504 : /************************************************************************/
7505 : /* Prepared geometry API */
7506 : /************************************************************************/
7507 :
7508 : #if defined(HAVE_GEOS)
7509 : struct _OGRPreparedGeometry
7510 : {
7511 : GEOSContextHandle_t hGEOSCtxt;
7512 : GEOSGeom hGEOSGeom;
7513 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7514 : };
7515 : #endif
7516 :
7517 : /************************************************************************/
7518 : /* OGRHasPreparedGeometrySupport() */
7519 : /************************************************************************/
7520 :
7521 : /** Returns if GEOS has prepared geometry support.
7522 : * @return TRUE or FALSE
7523 : */
7524 1 : int OGRHasPreparedGeometrySupport()
7525 : {
7526 : #if defined(HAVE_GEOS)
7527 1 : return TRUE;
7528 : #else
7529 : return FALSE;
7530 : #endif
7531 : }
7532 :
7533 : /************************************************************************/
7534 : /* OGRCreatePreparedGeometry() */
7535 : /************************************************************************/
7536 :
7537 : /** Creates a prepared geometry.
7538 : *
7539 : * To free with OGRDestroyPreparedGeometry()
7540 : *
7541 : * @param hGeom input geometry to prepare.
7542 : * @return handle to a prepared geometry.
7543 : * @since GDAL 3.3
7544 : */
7545 53033 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
7546 : {
7547 : (void)hGeom;
7548 : #if defined(HAVE_GEOS)
7549 53033 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7550 53033 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7551 53033 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7552 53033 : if (hGEOSGeom == nullptr)
7553 : {
7554 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7555 0 : return nullptr;
7556 : }
7557 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7558 53033 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7559 53033 : if (poPreparedGEOSGeom == nullptr)
7560 : {
7561 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7562 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7563 0 : return nullptr;
7564 : }
7565 :
7566 53033 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7567 53033 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7568 53033 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7569 53033 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7570 :
7571 53033 : return poPreparedGeom;
7572 : #else
7573 : return nullptr;
7574 : #endif
7575 : }
7576 :
7577 : /************************************************************************/
7578 : /* OGRDestroyPreparedGeometry() */
7579 : /************************************************************************/
7580 :
7581 : /** Destroys a prepared geometry.
7582 : * @param hPreparedGeom prepared geometry.
7583 : * @since GDAL 3.3
7584 : */
7585 53079 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
7586 : {
7587 : (void)hPreparedGeom;
7588 : #if defined(HAVE_GEOS)
7589 53079 : if (hPreparedGeom != nullptr)
7590 : {
7591 53033 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7592 : hPreparedGeom->poPreparedGEOSGeom);
7593 53033 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7594 53033 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7595 53033 : delete hPreparedGeom;
7596 : }
7597 : #endif
7598 53079 : }
7599 :
7600 : /************************************************************************/
7601 : /* OGRPreparedGeometryIntersects() */
7602 : /************************************************************************/
7603 :
7604 : /** Returns whether a prepared geometry intersects with a geometry.
7605 : * @param hPreparedGeom prepared geometry.
7606 : * @param hOtherGeom other geometry.
7607 : * @return TRUE or FALSE.
7608 : * @since GDAL 3.3
7609 : */
7610 5587 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
7611 : const OGRGeometryH hOtherGeom)
7612 : {
7613 : (void)hPreparedGeom;
7614 : (void)hOtherGeom;
7615 : #if defined(HAVE_GEOS)
7616 5587 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7617 5587 : if (hPreparedGeom == nullptr ||
7618 : poOtherGeom == nullptr
7619 : // The check for IsEmpty() is for buggy GEOS versions.
7620 : // See https://github.com/libgeos/geos/pull/423
7621 11174 : || poOtherGeom->IsEmpty())
7622 : {
7623 1 : return FALSE;
7624 : }
7625 :
7626 : GEOSGeom hGEOSOtherGeom =
7627 5586 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7628 5586 : if (hGEOSOtherGeom == nullptr)
7629 0 : return FALSE;
7630 :
7631 : const bool bRet =
7632 5586 : GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
7633 : hPreparedGeom->poPreparedGEOSGeom,
7634 5586 : hGEOSOtherGeom) == 1;
7635 5586 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7636 :
7637 5586 : return bRet;
7638 : #else
7639 : return FALSE;
7640 : #endif
7641 : }
7642 :
7643 : /** Returns whether a prepared geometry contains a geometry.
7644 : * @param hPreparedGeom prepared geometry.
7645 : * @param hOtherGeom other geometry.
7646 : * @return TRUE or FALSE.
7647 : */
7648 120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
7649 : const OGRGeometryH hOtherGeom)
7650 : {
7651 : (void)hPreparedGeom;
7652 : (void)hOtherGeom;
7653 : #if defined(HAVE_GEOS)
7654 120516 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7655 120516 : if (hPreparedGeom == nullptr ||
7656 : poOtherGeom == nullptr
7657 : // The check for IsEmpty() is for buggy GEOS versions.
7658 : // See https://github.com/libgeos/geos/pull/423
7659 241032 : || poOtherGeom->IsEmpty())
7660 : {
7661 1 : return FALSE;
7662 : }
7663 :
7664 : GEOSGeom hGEOSOtherGeom =
7665 120515 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7666 120515 : if (hGEOSOtherGeom == nullptr)
7667 0 : return FALSE;
7668 :
7669 120515 : const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
7670 : hPreparedGeom->poPreparedGEOSGeom,
7671 120515 : hGEOSOtherGeom) == 1;
7672 120515 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7673 :
7674 120515 : return bRet;
7675 : #else
7676 : return FALSE;
7677 : #endif
7678 : }
7679 :
7680 : /************************************************************************/
7681 : /* OGRGeometryFromEWKB() */
7682 : /************************************************************************/
7683 :
7684 1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7685 : int bIsPostGIS1_EWKB)
7686 :
7687 : {
7688 1445 : OGRGeometry *poGeometry = nullptr;
7689 :
7690 1445 : size_t nWKBSize = 0;
7691 1445 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7692 1445 : if (pabyWKB == nullptr)
7693 0 : return nullptr;
7694 :
7695 : /* -------------------------------------------------------------------- */
7696 : /* Try to ingest the geometry. */
7697 : /* -------------------------------------------------------------------- */
7698 1445 : (void)OGRGeometryFactory::createFromWkb(
7699 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7700 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7701 :
7702 1445 : return poGeometry;
7703 : }
7704 :
7705 : /************************************************************************/
7706 : /* OGRGeometryFromHexEWKB() */
7707 : /************************************************************************/
7708 :
7709 1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7710 : int bIsPostGIS1_EWKB)
7711 :
7712 : {
7713 1443 : if (pszBytea == nullptr)
7714 0 : return nullptr;
7715 :
7716 1443 : int nWKBLength = 0;
7717 1443 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7718 :
7719 : OGRGeometry *poGeometry =
7720 1443 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7721 :
7722 1443 : CPLFree(pabyWKB);
7723 :
7724 1443 : return poGeometry;
7725 : }
7726 :
7727 : /************************************************************************/
7728 : /* OGRGeometryToHexEWKB() */
7729 : /************************************************************************/
7730 :
7731 1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
7732 : int nPostGISMajor, int nPostGISMinor)
7733 : {
7734 1071 : const size_t nWkbSize = poGeometry->WkbSize();
7735 1071 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7736 1071 : if (pabyWKB == nullptr)
7737 0 : return CPLStrdup("");
7738 :
7739 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7740 1815 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7741 626 : poGeometry->IsEmpty())
7742 : {
7743 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7744 : OGRERR_NONE)
7745 : {
7746 0 : CPLFree(pabyWKB);
7747 0 : return CPLStrdup("");
7748 : }
7749 : }
7750 1069 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7751 : (nPostGISMajor < 2)
7752 : ? wkbVariantPostGIS1
7753 1069 : : wkbVariantOldOgc) != OGRERR_NONE)
7754 : {
7755 0 : CPLFree(pabyWKB);
7756 0 : return CPLStrdup("");
7757 : }
7758 :
7759 : // When converting to hex, each byte takes 2 hex characters. In addition
7760 : // we add in 8 characters to represent the SRID integer in hex, and
7761 : // one for a null terminator.
7762 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7763 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7764 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7765 1071 : if (nWkbSize >
7766 1071 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7767 : {
7768 0 : CPLFree(pabyWKB);
7769 0 : return CPLStrdup("");
7770 : }
7771 1071 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7772 1071 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7773 1071 : if (pszTextBuf == nullptr)
7774 : {
7775 0 : CPLFree(pabyWKB);
7776 0 : return CPLStrdup("");
7777 : }
7778 1071 : char *pszTextBufCurrent = pszTextBuf;
7779 :
7780 : // Convert the 1st byte, which is the endianness flag, to hex.
7781 1071 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7782 1071 : strcpy(pszTextBufCurrent, pszHex);
7783 1071 : CPLFree(pszHex);
7784 1071 : pszTextBufCurrent += 2;
7785 :
7786 : // Next, get the geom type which is bytes 2 through 5.
7787 : GUInt32 geomType;
7788 1071 : memcpy(&geomType, pabyWKB + 1, 4);
7789 :
7790 : // Now add the SRID flag if an SRID is provided.
7791 1071 : if (nSRSId > 0)
7792 : {
7793 : // Change the flag to wkbNDR (little) endianness.
7794 541 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7795 541 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7796 : // Apply the flag.
7797 541 : geomType = geomType | nGSrsFlag;
7798 : }
7799 :
7800 : // Now write the geom type which is 4 bytes.
7801 1071 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7802 1071 : strcpy(pszTextBufCurrent, pszHex);
7803 1071 : CPLFree(pszHex);
7804 1071 : pszTextBufCurrent += 8;
7805 :
7806 : // Now include SRID if provided.
7807 1071 : if (nSRSId > 0)
7808 : {
7809 : // Force the srsid to wkbNDR (little) endianness.
7810 541 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7811 541 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7812 : reinterpret_cast<const GByte *>(&nGSRSId));
7813 541 : strcpy(pszTextBufCurrent, pszHex);
7814 541 : CPLFree(pszHex);
7815 541 : pszTextBufCurrent += 8;
7816 : }
7817 :
7818 : // Copy the rest of the data over - subtract
7819 : // 5 since we already copied 5 bytes above.
7820 1071 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7821 1071 : CPLFree(pabyWKB);
7822 1071 : if (!pszHex || pszHex[0] == 0)
7823 : {
7824 0 : CPLFree(pszTextBuf);
7825 0 : return pszHex;
7826 : }
7827 1071 : strcpy(pszTextBufCurrent, pszHex);
7828 1071 : CPLFree(pszHex);
7829 :
7830 1071 : return pszTextBuf;
7831 : }
7832 :
7833 : /************************************************************************/
7834 : /* importPreambleFromWkb() */
7835 : /************************************************************************/
7836 :
7837 : //! @cond Doxygen_Suppress
7838 160381 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7839 : size_t nSize,
7840 : OGRwkbByteOrder &eByteOrder,
7841 : OGRwkbVariant eWkbVariant)
7842 : {
7843 160381 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7844 0 : return OGRERR_NOT_ENOUGH_DATA;
7845 :
7846 : /* -------------------------------------------------------------------- */
7847 : /* Get the byte order byte. */
7848 : /* -------------------------------------------------------------------- */
7849 160381 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7850 160381 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7851 0 : return OGRERR_CORRUPT_DATA;
7852 160381 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7853 :
7854 : /* -------------------------------------------------------------------- */
7855 : /* Get the geometry feature type. */
7856 : /* -------------------------------------------------------------------- */
7857 : OGRwkbGeometryType eGeometryType;
7858 : const OGRErr err =
7859 160381 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7860 160381 : if (wkbHasZ(eGeometryType))
7861 62309 : flags |= OGR_G_3D;
7862 160381 : if (wkbHasM(eGeometryType))
7863 59692 : flags |= OGR_G_MEASURED;
7864 :
7865 160381 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7866 0 : return OGRERR_CORRUPT_DATA;
7867 :
7868 160381 : return OGRERR_NONE;
7869 : }
7870 :
7871 : /************************************************************************/
7872 : /* importPreambleOfCollectionFromWkb() */
7873 : /* */
7874 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7875 : /* OGRCurvePolygon and OGRGeometryCollection. */
7876 : /************************************************************************/
7877 :
7878 77013 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7879 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7880 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7881 : OGRwkbVariant eWkbVariant)
7882 : {
7883 77013 : nGeomCount = 0;
7884 :
7885 : OGRErr eErr =
7886 77013 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7887 77013 : if (eErr != OGRERR_NONE)
7888 0 : return eErr;
7889 :
7890 : /* -------------------------------------------------------------------- */
7891 : /* Clear existing Geoms. */
7892 : /* -------------------------------------------------------------------- */
7893 77013 : int _flags = flags; // flags set in importPreambleFromWkb
7894 77013 : empty(); // may reset flags etc.
7895 :
7896 : // restore
7897 77013 : if (_flags & OGR_G_3D)
7898 59265 : set3D(TRUE);
7899 77013 : if (_flags & OGR_G_MEASURED)
7900 56768 : setMeasured(TRUE);
7901 :
7902 : /* -------------------------------------------------------------------- */
7903 : /* Get the sub-geometry count. */
7904 : /* -------------------------------------------------------------------- */
7905 77013 : memcpy(&nGeomCount, pabyData + 5, 4);
7906 :
7907 77013 : if (OGR_SWAP(eByteOrder))
7908 386 : nGeomCount = CPL_SWAP32(nGeomCount);
7909 :
7910 153892 : if (nGeomCount < 0 ||
7911 76879 : static_cast<size_t>(nGeomCount) >
7912 76879 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7913 : {
7914 134 : nGeomCount = 0;
7915 134 : return OGRERR_CORRUPT_DATA;
7916 : }
7917 76879 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7918 :
7919 : // Each ring has a minimum of nMinSubGeomSize bytes.
7920 76879 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7921 : {
7922 910 : CPLError(CE_Failure, CPLE_AppDefined,
7923 : "Length of input WKB is too small");
7924 910 : nGeomCount = 0;
7925 910 : return OGRERR_NOT_ENOUGH_DATA;
7926 : }
7927 :
7928 75969 : nDataOffset = 9;
7929 75969 : if (nSize != static_cast<size_t>(-1))
7930 : {
7931 75949 : CPLAssert(nSize >= nDataOffset);
7932 75949 : nSize -= nDataOffset;
7933 : }
7934 :
7935 75969 : return OGRERR_NONE;
7936 : }
7937 :
7938 : /************************************************************************/
7939 : /* importCurveCollectionFromWkt() */
7940 : /* */
7941 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7942 : /* OGRMultiCurve. */
7943 : /************************************************************************/
7944 :
7945 1450 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7946 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7947 : int bAllowCurve, int bAllowCompoundCurve,
7948 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7949 :
7950 : {
7951 1450 : int bHasZ = FALSE;
7952 1450 : int bHasM = FALSE;
7953 1450 : bool bIsEmpty = false;
7954 1450 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7955 1450 : flags = 0;
7956 1450 : if (eErr != OGRERR_NONE)
7957 14 : return eErr;
7958 1436 : if (bHasZ)
7959 206 : flags |= OGR_G_3D;
7960 1436 : if (bHasM)
7961 132 : flags |= OGR_G_MEASURED;
7962 1436 : if (bIsEmpty)
7963 111 : return OGRERR_NONE;
7964 :
7965 : char szToken[OGR_WKT_TOKEN_MAX];
7966 1325 : const char *pszInput = *ppszInput;
7967 1325 : eErr = OGRERR_NONE;
7968 :
7969 : // Skip first '('.
7970 1325 : pszInput = OGRWktReadToken(pszInput, szToken);
7971 :
7972 : /* ==================================================================== */
7973 : /* Read each curve in turn. Note that we try to reuse the same */
7974 : /* point list buffer from curve to curve to cut down on */
7975 : /* allocate/deallocate overhead. */
7976 : /* ==================================================================== */
7977 1325 : OGRRawPoint *paoPoints = nullptr;
7978 1325 : int nMaxPoints = 0;
7979 1325 : double *padfZ = nullptr;
7980 :
7981 656 : do
7982 : {
7983 :
7984 : /* --------------------------------------------------------------------
7985 : */
7986 : /* Get the first token, which should be the geometry type. */
7987 : /* --------------------------------------------------------------------
7988 : */
7989 1981 : const char *pszInputBefore = pszInput;
7990 1981 : pszInput = OGRWktReadToken(pszInput, szToken);
7991 :
7992 : /* --------------------------------------------------------------------
7993 : */
7994 : /* Do the import. */
7995 : /* --------------------------------------------------------------------
7996 : */
7997 1981 : OGRCurve *poCurve = nullptr;
7998 1981 : if (EQUAL(szToken, "("))
7999 : {
8000 1427 : OGRLineString *poLine = new OGRLineString();
8001 1427 : poCurve = poLine;
8002 1427 : pszInput = pszInputBefore;
8003 1427 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
8004 : paoPoints, nMaxPoints, padfZ);
8005 : }
8006 554 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
8007 : {
8008 16 : poCurve = new OGRLineString();
8009 : }
8010 : // Accept LINESTRING(), but this is an extension to the BNF, also
8011 : // accepted by PostGIS.
8012 538 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
8013 523 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
8014 523 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
8015 1235 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
8016 159 : (bAllowCompoundCurve &&
8017 159 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
8018 : {
8019 500 : OGRGeometry *poGeom = nullptr;
8020 500 : pszInput = pszInputBefore;
8021 : eErr =
8022 500 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
8023 500 : if (poGeom == nullptr)
8024 : {
8025 1 : eErr = OGRERR_CORRUPT_DATA;
8026 : }
8027 : else
8028 : {
8029 499 : poCurve = poGeom->toCurve();
8030 : }
8031 : }
8032 : else
8033 : {
8034 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
8035 : szToken);
8036 38 : eErr = OGRERR_CORRUPT_DATA;
8037 : }
8038 :
8039 : // If this has M it is an error if poGeom does not have M.
8040 1981 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
8041 0 : eErr = OGRERR_CORRUPT_DATA;
8042 :
8043 1981 : if (eErr == OGRERR_NONE)
8044 1936 : eErr = pfnAddCurveDirectly(this, poCurve);
8045 1981 : if (eErr != OGRERR_NONE)
8046 : {
8047 55 : delete poCurve;
8048 55 : break;
8049 : }
8050 :
8051 : /* --------------------------------------------------------------------
8052 : */
8053 : /* Read the delimiter following the surface. */
8054 : /* --------------------------------------------------------------------
8055 : */
8056 1926 : pszInput = OGRWktReadToken(pszInput, szToken);
8057 1926 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
8058 :
8059 1325 : CPLFree(paoPoints);
8060 1325 : CPLFree(padfZ);
8061 :
8062 : /* -------------------------------------------------------------------- */
8063 : /* freak if we don't get a closing bracket. */
8064 : /* -------------------------------------------------------------------- */
8065 :
8066 1325 : if (eErr != OGRERR_NONE)
8067 55 : return eErr;
8068 :
8069 1270 : if (szToken[0] != ')')
8070 9 : return OGRERR_CORRUPT_DATA;
8071 :
8072 1261 : *ppszInput = pszInput;
8073 1261 : return OGRERR_NONE;
8074 : }
8075 :
8076 : //! @endcond
8077 :
8078 : /************************************************************************/
8079 : /* OGR_GT_Flatten() */
8080 : /************************************************************************/
8081 : /**
8082 : * \brief Returns the 2D geometry type corresponding to the passed geometry
8083 : * type.
8084 : *
8085 : * This function is intended to work with geometry types as old-style 99-402
8086 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
8087 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
8088 : *
8089 : * @param eType Input geometry type
8090 : *
8091 : * @return 2D geometry type corresponding to the passed geometry type.
8092 : *
8093 : */
8094 :
8095 7889970 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
8096 : {
8097 7889970 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
8098 7889970 : if (eType >= 1000 && eType < 2000) // ISO Z.
8099 2776760 : return static_cast<OGRwkbGeometryType>(eType - 1000);
8100 5113210 : if (eType >= 2000 && eType < 3000) // ISO M.
8101 6059 : return static_cast<OGRwkbGeometryType>(eType - 2000);
8102 5107150 : if (eType >= 3000 && eType < 4000) // ISO ZM.
8103 136249 : return static_cast<OGRwkbGeometryType>(eType - 3000);
8104 4970900 : return eType;
8105 : }
8106 :
8107 : /************************************************************************/
8108 : /* OGR_GT_HasZ() */
8109 : /************************************************************************/
8110 : /**
8111 : * \brief Return if the geometry type is a 3D geometry type.
8112 : *
8113 : * @param eType Input geometry type
8114 : *
8115 : * @return TRUE if the geometry type is a 3D geometry type.
8116 : *
8117 : */
8118 :
8119 2083450 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
8120 : {
8121 2083450 : if (eType & wkb25DBitInternalUse)
8122 157130 : return TRUE;
8123 1926320 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
8124 264 : return TRUE;
8125 1926060 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8126 121539 : return TRUE;
8127 1804520 : return FALSE;
8128 : }
8129 :
8130 : /************************************************************************/
8131 : /* OGR_GT_HasM() */
8132 : /************************************************************************/
8133 : /**
8134 : * \brief Return if the geometry type is a measured type.
8135 : *
8136 : * @param eType Input geometry type
8137 : *
8138 : * @return TRUE if the geometry type is a measured type.
8139 : *
8140 : */
8141 :
8142 2144850 : int OGR_GT_HasM(OGRwkbGeometryType eType)
8143 : {
8144 2144850 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
8145 2593 : return TRUE;
8146 2142250 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8147 121195 : return TRUE;
8148 2021060 : return FALSE;
8149 : }
8150 :
8151 : /************************************************************************/
8152 : /* OGR_GT_SetZ() */
8153 : /************************************************************************/
8154 : /**
8155 : * \brief Returns the 3D geometry type corresponding to the passed geometry
8156 : * type.
8157 : *
8158 : * @param eType Input geometry type
8159 : *
8160 : * @return 3D geometry type corresponding to the passed geometry type.
8161 : *
8162 : */
8163 :
8164 5748 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
8165 : {
8166 5748 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
8167 498 : return eType;
8168 5250 : if (eType <= wkbGeometryCollection)
8169 5148 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
8170 : else
8171 102 : return static_cast<OGRwkbGeometryType>(eType + 1000);
8172 : }
8173 :
8174 : /************************************************************************/
8175 : /* OGR_GT_SetM() */
8176 : /************************************************************************/
8177 : /**
8178 : * \brief Returns the measured geometry type corresponding to the passed
8179 : * geometry type.
8180 : *
8181 : * @param eType Input geometry type
8182 : *
8183 : * @return measured geometry type corresponding to the passed geometry type.
8184 : *
8185 : */
8186 :
8187 2013 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
8188 : {
8189 2013 : if (OGR_GT_HasM(eType) || eType == wkbNone)
8190 262 : return eType;
8191 1751 : if (eType & wkb25DBitInternalUse)
8192 : {
8193 717 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
8194 717 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
8195 : }
8196 1751 : return static_cast<OGRwkbGeometryType>(eType + 2000);
8197 : }
8198 :
8199 : /************************************************************************/
8200 : /* OGR_GT_SetModifier() */
8201 : /************************************************************************/
8202 : /**
8203 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
8204 : *
8205 : * @param eType Input geometry type
8206 : * @param bHasZ TRUE if the output geometry type must be 3D.
8207 : * @param bHasM TRUE if the output geometry type must be measured.
8208 : *
8209 : * @return Output geometry type.
8210 : *
8211 : */
8212 :
8213 5578 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
8214 : int bHasM)
8215 : {
8216 5578 : if (bHasZ && bHasM)
8217 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
8218 5236 : else if (bHasM)
8219 333 : return OGR_GT_SetM(wkbFlatten(eType));
8220 4903 : else if (bHasZ)
8221 2110 : return OGR_GT_SetZ(wkbFlatten(eType));
8222 : else
8223 2793 : return wkbFlatten(eType);
8224 : }
8225 :
8226 : /************************************************************************/
8227 : /* OGR_GT_IsSubClassOf) */
8228 : /************************************************************************/
8229 : /**
8230 : * \brief Returns if a type is a subclass of another one
8231 : *
8232 : * @param eType Type.
8233 : * @param eSuperType Super type
8234 : *
8235 : * @return TRUE if eType is a subclass of eSuperType.
8236 : *
8237 : */
8238 :
8239 132549 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
8240 : {
8241 132549 : eSuperType = wkbFlatten(eSuperType);
8242 132549 : eType = wkbFlatten(eType);
8243 :
8244 132549 : if (eSuperType == eType || eSuperType == wkbUnknown)
8245 20031 : return TRUE;
8246 :
8247 112518 : if (eSuperType == wkbGeometryCollection)
8248 31327 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
8249 64654 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
8250 33327 : eType == wkbMultiSurface;
8251 :
8252 79191 : if (eSuperType == wkbCurvePolygon)
8253 21967 : return eType == wkbPolygon || eType == wkbTriangle;
8254 :
8255 57224 : if (eSuperType == wkbMultiCurve)
8256 249 : return eType == wkbMultiLineString;
8257 :
8258 56975 : if (eSuperType == wkbMultiSurface)
8259 288 : return eType == wkbMultiPolygon;
8260 :
8261 56687 : if (eSuperType == wkbCurve)
8262 23399 : return eType == wkbLineString || eType == wkbCircularString ||
8263 23399 : eType == wkbCompoundCurve;
8264 :
8265 33288 : if (eSuperType == wkbSurface)
8266 3521 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
8267 7190 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
8268 3669 : eType == wkbTIN;
8269 :
8270 29619 : if (eSuperType == wkbPolygon)
8271 221 : return eType == wkbTriangle;
8272 :
8273 29398 : if (eSuperType == wkbPolyhedralSurface)
8274 14654 : return eType == wkbTIN;
8275 :
8276 14744 : return FALSE;
8277 : }
8278 :
8279 : /************************************************************************/
8280 : /* OGR_GT_GetCollection() */
8281 : /************************************************************************/
8282 : /**
8283 : * \brief Returns the collection type that can contain the passed geometry type
8284 : *
8285 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
8286 : * wkbLineString->wkbMultiLineString,
8287 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
8288 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
8289 : * wkbCurvePolygon->wkbMultiSurface.
8290 : * In other cases, wkbUnknown is returned
8291 : *
8292 : * Passed Z, M, ZM flag is preserved.
8293 : *
8294 : *
8295 : * @param eType Input geometry type
8296 : *
8297 : * @return the collection type that can contain the passed geometry type or
8298 : * wkbUnknown
8299 : *
8300 : */
8301 :
8302 2709 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
8303 : {
8304 2709 : const bool bHasZ = wkbHasZ(eType);
8305 2709 : const bool bHasM = wkbHasM(eType);
8306 2709 : if (eType == wkbNone)
8307 1 : return wkbNone;
8308 2708 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8309 2708 : if (eFGType == wkbPoint)
8310 67 : eType = wkbMultiPoint;
8311 :
8312 2641 : else if (eFGType == wkbLineString)
8313 187 : eType = wkbMultiLineString;
8314 :
8315 2454 : else if (eFGType == wkbPolygon)
8316 1004 : eType = wkbMultiPolygon;
8317 :
8318 1450 : else if (eFGType == wkbTriangle)
8319 7 : eType = wkbTIN;
8320 :
8321 1443 : else if (OGR_GT_IsCurve(eFGType))
8322 189 : eType = wkbMultiCurve;
8323 :
8324 1254 : else if (OGR_GT_IsSurface(eFGType))
8325 952 : eType = wkbMultiSurface;
8326 :
8327 : else
8328 302 : return wkbUnknown;
8329 :
8330 2406 : if (bHasZ)
8331 3 : eType = wkbSetZ(eType);
8332 2406 : if (bHasM)
8333 3 : eType = wkbSetM(eType);
8334 :
8335 2406 : return eType;
8336 : }
8337 :
8338 : /************************************************************************/
8339 : /* OGR_GT_GetSingle() */
8340 : /************************************************************************/
8341 : /**
8342 : * \brief Returns the non-collection type that be contained in the passed
8343 : * geometry type.
8344 : *
8345 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
8346 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
8347 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
8348 : * wkbGeometryCollection -> wkbUnknown
8349 : * In other cases, the original geometry is returned.
8350 : *
8351 : * Passed Z, M, ZM flag is preserved.
8352 : *
8353 : *
8354 : * @param eType Input geometry type
8355 : *
8356 : * @return the the non-collection type that be contained in the passed geometry
8357 : * type or wkbUnknown
8358 : *
8359 : * @since GDAL 3.11
8360 : */
8361 :
8362 43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
8363 : {
8364 43 : const bool bHasZ = wkbHasZ(eType);
8365 43 : const bool bHasM = wkbHasM(eType);
8366 43 : if (eType == wkbNone)
8367 1 : return wkbNone;
8368 42 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
8369 42 : if (eFGType == wkbMultiPoint)
8370 8 : eType = wkbPoint;
8371 :
8372 34 : else if (eFGType == wkbMultiLineString)
8373 4 : eType = wkbLineString;
8374 :
8375 30 : else if (eFGType == wkbMultiPolygon)
8376 2 : eType = wkbPolygon;
8377 :
8378 28 : else if (eFGType == wkbMultiCurve)
8379 2 : eType = wkbCompoundCurve;
8380 :
8381 26 : else if (eFGType == wkbMultiSurface)
8382 2 : eType = wkbCurvePolygon;
8383 :
8384 24 : else if (eFGType == wkbGeometryCollection)
8385 1 : return wkbUnknown;
8386 :
8387 41 : if (bHasZ)
8388 3 : eType = wkbSetZ(eType);
8389 41 : if (bHasM)
8390 2 : eType = wkbSetM(eType);
8391 :
8392 41 : return eType;
8393 : }
8394 :
8395 : /************************************************************************/
8396 : /* OGR_GT_GetCurve() */
8397 : /************************************************************************/
8398 : /**
8399 : * \brief Returns the curve geometry type that can contain the passed geometry
8400 : * type
8401 : *
8402 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8403 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8404 : * and wkbMultiLineString->wkbMultiCurve.
8405 : * In other cases, the passed geometry is returned.
8406 : *
8407 : * Passed Z, M, ZM flag is preserved.
8408 : *
8409 : * @param eType Input geometry type
8410 : *
8411 : * @return the curve type that can contain the passed geometry type
8412 : *
8413 : */
8414 :
8415 35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8416 : {
8417 35 : const bool bHasZ = wkbHasZ(eType);
8418 35 : const bool bHasM = wkbHasM(eType);
8419 35 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8420 :
8421 35 : if (eFGType == wkbLineString)
8422 3 : eType = wkbCompoundCurve;
8423 :
8424 32 : else if (eFGType == wkbPolygon)
8425 1 : eType = wkbCurvePolygon;
8426 :
8427 31 : else if (eFGType == wkbTriangle)
8428 0 : eType = wkbCurvePolygon;
8429 :
8430 31 : else if (eFGType == wkbMultiLineString)
8431 6 : eType = wkbMultiCurve;
8432 :
8433 25 : else if (eFGType == wkbMultiPolygon)
8434 4 : eType = wkbMultiSurface;
8435 :
8436 35 : if (bHasZ)
8437 4 : eType = wkbSetZ(eType);
8438 35 : if (bHasM)
8439 4 : eType = wkbSetM(eType);
8440 :
8441 35 : return eType;
8442 : }
8443 :
8444 : /************************************************************************/
8445 : /* OGR_GT_GetLinear() */
8446 : /************************************************************************/
8447 : /**
8448 : * \brief Returns the non-curve geometry type that can contain the passed
8449 : * geometry type
8450 : *
8451 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8452 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8453 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8454 : * In other cases, the passed geometry is returned.
8455 : *
8456 : * Passed Z, M, ZM flag is preserved.
8457 : *
8458 : * @param eType Input geometry type
8459 : *
8460 : * @return the non-curve type that can contain the passed geometry type
8461 : *
8462 : */
8463 :
8464 785 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8465 : {
8466 785 : const bool bHasZ = wkbHasZ(eType);
8467 785 : const bool bHasM = wkbHasM(eType);
8468 785 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8469 :
8470 785 : if (OGR_GT_IsCurve(eFGType))
8471 56 : eType = wkbLineString;
8472 :
8473 729 : else if (OGR_GT_IsSurface(eFGType))
8474 43 : eType = wkbPolygon;
8475 :
8476 686 : else if (eFGType == wkbMultiCurve)
8477 186 : eType = wkbMultiLineString;
8478 :
8479 500 : else if (eFGType == wkbMultiSurface)
8480 166 : eType = wkbMultiPolygon;
8481 :
8482 785 : if (bHasZ)
8483 154 : eType = wkbSetZ(eType);
8484 785 : if (bHasM)
8485 101 : eType = wkbSetM(eType);
8486 :
8487 785 : return eType;
8488 : }
8489 :
8490 : /************************************************************************/
8491 : /* OGR_GT_IsCurve() */
8492 : /************************************************************************/
8493 :
8494 : /**
8495 : * \brief Return if a geometry type is an instance of Curve
8496 : *
8497 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8498 : * and their Z/M/ZM variant.
8499 : *
8500 : * @param eGeomType the geometry type
8501 : * @return TRUE if the geometry type is an instance of Curve
8502 : *
8503 : */
8504 :
8505 23390 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8506 : {
8507 23390 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8508 : }
8509 :
8510 : /************************************************************************/
8511 : /* OGR_GT_IsSurface() */
8512 : /************************************************************************/
8513 :
8514 : /**
8515 : * \brief Return if a geometry type is an instance of Surface
8516 : *
8517 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8518 : * and their Z/M/ZM variant.
8519 : *
8520 : * @param eGeomType the geometry type
8521 : * @return TRUE if the geometry type is an instance of Surface
8522 : *
8523 : */
8524 :
8525 3663 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8526 : {
8527 3663 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8528 : }
8529 :
8530 : /************************************************************************/
8531 : /* OGR_GT_IsNonLinear() */
8532 : /************************************************************************/
8533 :
8534 : /**
8535 : * \brief Return if a geometry type is a non-linear geometry type.
8536 : *
8537 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8538 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8539 : * Z/M variants.
8540 : *
8541 : * @param eGeomType the geometry type
8542 : * @return TRUE if the geometry type is a non-linear geometry type.
8543 : *
8544 : */
8545 :
8546 118469 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8547 : {
8548 118469 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8549 118461 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8550 118388 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8551 236930 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8552 118469 : eFGeomType == wkbMultiSurface;
8553 : }
8554 :
8555 : /************************************************************************/
8556 : /* CastToError() */
8557 : /************************************************************************/
8558 :
8559 : //! @cond Doxygen_Suppress
8560 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8561 : {
8562 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8563 0 : poGeom->getGeometryName());
8564 0 : delete poGeom;
8565 0 : return nullptr;
8566 : }
8567 :
8568 : //! @endcond
8569 :
8570 : /************************************************************************/
8571 : /* OGRexportToSFCGAL() */
8572 : /************************************************************************/
8573 :
8574 : //! @cond Doxygen_Suppress
8575 : sfcgal_geometry_t *
8576 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8577 : {
8578 : #ifdef HAVE_SFCGAL
8579 :
8580 : sfcgal_init();
8581 : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8582 :
8583 : const auto exportToSFCGALViaWKB =
8584 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8585 : {
8586 : if (!geom)
8587 : return nullptr;
8588 :
8589 : // Get WKB size and allocate buffer
8590 : size_t nSize = geom->WkbSize();
8591 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8592 :
8593 : // Set export options with NDR byte order
8594 : OGRwkbExportOptions oOptions;
8595 : oOptions.eByteOrder = wkbNDR;
8596 : // and ISO to avoid wkb25DBit for Z geometries
8597 : oOptions.eWkbVariant = wkbVariantIso;
8598 :
8599 : // Export to WKB
8600 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8601 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8602 : {
8603 : sfcgalGeom = sfcgal_io_read_wkb(
8604 : reinterpret_cast<const char *>(pabyWkb), nSize);
8605 : }
8606 :
8607 : CPLFree(pabyWkb);
8608 : return sfcgalGeom;
8609 : };
8610 :
8611 : // Handle special cases
8612 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8613 : {
8614 : std::unique_ptr<OGRLineString> poLS(
8615 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8616 : return exportToSFCGALViaWKB(poLS.get());
8617 : }
8618 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8619 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8620 : {
8621 : std::unique_ptr<OGRLineString> poLS(
8622 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8623 : ->toLineString());
8624 : return exportToSFCGALViaWKB(poLS.get());
8625 : }
8626 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8627 : {
8628 : std::unique_ptr<OGRPolygon> poPolygon(
8629 : OGRGeometryFactory::forceToPolygon(
8630 : poGeom->clone()->toCurvePolygon())
8631 : ->toPolygon());
8632 : return exportToSFCGALViaWKB(poPolygon.get());
8633 : }
8634 : else
8635 : {
8636 : // Default case - direct export
8637 : return exportToSFCGALViaWKB(poGeom);
8638 : }
8639 : #else
8640 : char *buffer = nullptr;
8641 :
8642 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8643 : // Polygon
8644 :
8645 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8646 : {
8647 : // cast it to LineString and get the WKT
8648 : std::unique_ptr<OGRLineString> poLS(
8649 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8650 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8651 : {
8652 : sfcgal_geometry_t *_geometry =
8653 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8654 : CPLFree(buffer);
8655 : return _geometry;
8656 : }
8657 : else
8658 : {
8659 : CPLFree(buffer);
8660 : return nullptr;
8661 : }
8662 : }
8663 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8664 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8665 : {
8666 : // convert it to LineString and get the WKT
8667 : std::unique_ptr<OGRLineString> poLS(
8668 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8669 : ->toLineString());
8670 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8671 : {
8672 : sfcgal_geometry_t *_geometry =
8673 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8674 : CPLFree(buffer);
8675 : return _geometry;
8676 : }
8677 : else
8678 : {
8679 : CPLFree(buffer);
8680 : return nullptr;
8681 : }
8682 : }
8683 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8684 : {
8685 : // convert it to Polygon and get the WKT
8686 : std::unique_ptr<OGRPolygon> poPolygon(
8687 : OGRGeometryFactory::forceToPolygon(
8688 : poGeom->clone()->toCurvePolygon())
8689 : ->toPolygon());
8690 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8691 : {
8692 : sfcgal_geometry_t *_geometry =
8693 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8694 : CPLFree(buffer);
8695 : return _geometry;
8696 : }
8697 : else
8698 : {
8699 : CPLFree(buffer);
8700 : return nullptr;
8701 : }
8702 : }
8703 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8704 : {
8705 : sfcgal_geometry_t *_geometry =
8706 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8707 : CPLFree(buffer);
8708 : return _geometry;
8709 : }
8710 : else
8711 : {
8712 : CPLFree(buffer);
8713 : return nullptr;
8714 : }
8715 : #endif
8716 : #else
8717 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8718 0 : return nullptr;
8719 : #endif
8720 : }
8721 :
8722 : //! @endcond
8723 :
8724 : /************************************************************************/
8725 : /* SFCGALexportToOGR() */
8726 : /************************************************************************/
8727 :
8728 : //! @cond Doxygen_Suppress
8729 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8730 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8731 : {
8732 : #ifdef HAVE_SFCGAL
8733 : if (geometry == nullptr)
8734 : return nullptr;
8735 :
8736 : sfcgal_init();
8737 : char *pabySFCGAL = nullptr;
8738 : size_t nLength = 0;
8739 : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8740 :
8741 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8742 :
8743 : if (pabySFCGAL == nullptr || nLength == 0)
8744 : return nullptr;
8745 :
8746 : OGRGeometry *poGeom = nullptr;
8747 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8748 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8749 : nLength);
8750 :
8751 : free(pabySFCGAL);
8752 :
8753 : if (eErr == OGRERR_NONE)
8754 : {
8755 : return poGeom;
8756 : }
8757 : else
8758 : {
8759 : return nullptr;
8760 : }
8761 : #else
8762 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8763 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8764 : memcpy(pszWKT, pabySFCGAL, nLength);
8765 : pszWKT[nLength] = 0;
8766 : free(pabySFCGAL);
8767 :
8768 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8769 :
8770 : OGRGeometry *poGeom = nullptr;
8771 : if (geom_type == SFCGAL_TYPE_POINT)
8772 : {
8773 : poGeom = new OGRPoint();
8774 : }
8775 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8776 : {
8777 : poGeom = new OGRLineString();
8778 : }
8779 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8780 : {
8781 : poGeom = new OGRPolygon();
8782 : }
8783 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8784 : {
8785 : poGeom = new OGRMultiPoint();
8786 : }
8787 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8788 : {
8789 : poGeom = new OGRMultiLineString();
8790 : }
8791 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8792 : {
8793 : poGeom = new OGRMultiPolygon();
8794 : }
8795 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8796 : {
8797 : poGeom = new OGRGeometryCollection();
8798 : }
8799 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8800 : {
8801 : poGeom = new OGRTriangle();
8802 : }
8803 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8804 : {
8805 : poGeom = new OGRPolyhedralSurface();
8806 : }
8807 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8808 : {
8809 : poGeom = new OGRTriangulatedSurface();
8810 : }
8811 : else
8812 : {
8813 : CPLFree(pszWKT);
8814 : return nullptr;
8815 : }
8816 :
8817 : const char *pszWKTTmp = pszWKT;
8818 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8819 : {
8820 : CPLFree(pszWKT);
8821 : return poGeom;
8822 : }
8823 : else
8824 : {
8825 : delete poGeom;
8826 : CPLFree(pszWKT);
8827 : return nullptr;
8828 : }
8829 : #endif
8830 : #else
8831 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8832 0 : return nullptr;
8833 : #endif
8834 : }
8835 :
8836 : //! @endcond
8837 :
8838 : //! @cond Doxygen_Suppress
8839 11036 : bool OGRGeometry::IsSFCGALCompatible() const
8840 : {
8841 11036 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8842 11036 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8843 : eGType == wkbTIN)
8844 : {
8845 3 : return TRUE;
8846 : }
8847 11033 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8848 : {
8849 13 : const OGRGeometryCollection *poGC = toGeometryCollection();
8850 13 : bool bIsSFCGALCompatible = false;
8851 13 : for (auto &&poSubGeom : *poGC)
8852 : {
8853 : OGRwkbGeometryType eSubGeomType =
8854 13 : wkbFlatten(poSubGeom->getGeometryType());
8855 13 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8856 : {
8857 0 : bIsSFCGALCompatible = true;
8858 : }
8859 13 : else if (eSubGeomType != wkbMultiPolygon)
8860 : {
8861 13 : bIsSFCGALCompatible = false;
8862 13 : break;
8863 : }
8864 : }
8865 13 : return bIsSFCGALCompatible;
8866 : }
8867 11020 : return FALSE;
8868 : }
8869 :
8870 : //! @endcond
8871 :
8872 : /************************************************************************/
8873 : /* roundCoordinatesIEEE754() */
8874 : /************************************************************************/
8875 :
8876 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8877 : * double-precision binary representation.
8878 : *
8879 : * Determines the number of bits (N) required to represent a coordinate value
8880 : * with a specified number of digits after the decimal point, and then sets all
8881 : * but the N most significant bits to zero. The resulting coordinate value will
8882 : * still round to the original value (e.g. after roundCoordinates()), but will
8883 : * have improved compressiblity.
8884 : *
8885 : * @param options Contains the precision requirements.
8886 : * @since GDAL 3.9
8887 : */
8888 1 : void OGRGeometry::roundCoordinatesIEEE754(
8889 : const OGRGeomCoordinateBinaryPrecision &options)
8890 : {
8891 : struct Quantizer : public OGRDefaultGeometryVisitor
8892 : {
8893 : const OGRGeomCoordinateBinaryPrecision &m_options;
8894 :
8895 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8896 1 : : m_options(optionsIn)
8897 : {
8898 1 : }
8899 :
8900 : using OGRDefaultGeometryVisitor::visit;
8901 :
8902 3 : void visit(OGRPoint *poPoint) override
8903 : {
8904 3 : if (m_options.nXYBitPrecision != INT_MIN)
8905 : {
8906 : uint64_t i;
8907 : double d;
8908 3 : d = poPoint->getX();
8909 3 : memcpy(&i, &d, sizeof(i));
8910 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8911 3 : memcpy(&d, &i, sizeof(i));
8912 3 : poPoint->setX(d);
8913 3 : d = poPoint->getY();
8914 3 : memcpy(&i, &d, sizeof(i));
8915 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8916 3 : memcpy(&d, &i, sizeof(i));
8917 3 : poPoint->setY(d);
8918 : }
8919 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8920 : {
8921 : uint64_t i;
8922 : double d;
8923 3 : d = poPoint->getZ();
8924 3 : memcpy(&i, &d, sizeof(i));
8925 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8926 3 : memcpy(&d, &i, sizeof(i));
8927 3 : poPoint->setZ(d);
8928 : }
8929 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8930 : {
8931 : uint64_t i;
8932 : double d;
8933 3 : d = poPoint->getM();
8934 3 : memcpy(&i, &d, sizeof(i));
8935 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8936 3 : memcpy(&d, &i, sizeof(i));
8937 3 : poPoint->setM(d);
8938 : }
8939 3 : }
8940 : };
8941 :
8942 2 : Quantizer quantizer(options);
8943 1 : accept(&quantizer);
8944 1 : }
8945 :
8946 : /************************************************************************/
8947 : /* visit() */
8948 : /************************************************************************/
8949 :
8950 105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8951 : {
8952 1248 : for (auto &&oPoint : *poGeom)
8953 : {
8954 1143 : oPoint.accept(this);
8955 : }
8956 105 : }
8957 :
8958 104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8959 : {
8960 104 : _visit(poGeom);
8961 104 : }
8962 :
8963 80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8964 : {
8965 80 : visit(poGeom->toUpperClass());
8966 80 : }
8967 :
8968 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8969 : {
8970 1 : _visit(poGeom);
8971 1 : }
8972 :
8973 78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8974 : {
8975 159 : for (auto &&poSubGeom : *poGeom)
8976 81 : poSubGeom->accept(this);
8977 78 : }
8978 :
8979 77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8980 : {
8981 77 : visit(poGeom->toUpperClass());
8982 77 : }
8983 :
8984 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8985 : {
8986 1 : visit(poGeom->toUpperClass());
8987 1 : }
8988 :
8989 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8990 : {
8991 8 : visit(poGeom->toUpperClass());
8992 8 : }
8993 :
8994 14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8995 : {
8996 14 : visit(poGeom->toUpperClass());
8997 14 : }
8998 :
8999 26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
9000 : {
9001 75 : for (auto &&poSubGeom : *poGeom)
9002 49 : poSubGeom->accept(this);
9003 26 : }
9004 :
9005 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
9006 : {
9007 2 : for (auto &&poSubGeom : *poGeom)
9008 1 : poSubGeom->accept(this);
9009 1 : }
9010 :
9011 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
9012 : {
9013 1 : visit(poGeom->toUpperClass());
9014 1 : }
9015 :
9016 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
9017 : {
9018 1 : visit(poGeom->toUpperClass());
9019 1 : }
9020 :
9021 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
9022 : {
9023 2 : visit(poGeom->toUpperClass());
9024 2 : }
9025 :
9026 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
9027 : {
9028 4 : for (auto &&poSubGeom : *poGeom)
9029 2 : poSubGeom->accept(this);
9030 2 : }
9031 :
9032 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
9033 : {
9034 1 : visit(poGeom->toUpperClass());
9035 1 : }
9036 :
9037 127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
9038 : {
9039 2988 : for (auto &&oPoint : *poGeom)
9040 : {
9041 2861 : oPoint.accept(this);
9042 : }
9043 127 : }
9044 :
9045 121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
9046 : {
9047 121 : _visit(poGeom);
9048 121 : }
9049 :
9050 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
9051 : {
9052 110 : visit(poGeom->toUpperClass());
9053 110 : }
9054 :
9055 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
9056 : {
9057 6 : _visit(poGeom);
9058 6 : }
9059 :
9060 112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
9061 : {
9062 225 : for (auto &&poSubGeom : *poGeom)
9063 113 : poSubGeom->accept(this);
9064 112 : }
9065 :
9066 109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
9067 : {
9068 109 : visit(poGeom->toUpperClass());
9069 109 : }
9070 :
9071 64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
9072 : {
9073 64 : visit(poGeom->toUpperClass());
9074 64 : }
9075 :
9076 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
9077 : {
9078 1 : visit(poGeom->toUpperClass());
9079 1 : }
9080 :
9081 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
9082 : {
9083 1 : visit(poGeom->toUpperClass());
9084 1 : }
9085 :
9086 69 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
9087 : {
9088 325 : for (auto &&poSubGeom : *poGeom)
9089 256 : poSubGeom->accept(this);
9090 69 : }
9091 :
9092 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
9093 : {
9094 14 : for (auto &&poSubGeom : *poGeom)
9095 11 : poSubGeom->accept(this);
9096 3 : }
9097 :
9098 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
9099 : {
9100 1 : visit(poGeom->toUpperClass());
9101 1 : }
9102 :
9103 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
9104 : {
9105 1 : visit(poGeom->toUpperClass());
9106 1 : }
9107 :
9108 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
9109 : {
9110 2 : visit(poGeom->toUpperClass());
9111 2 : }
9112 :
9113 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
9114 : {
9115 4 : for (auto &&poSubGeom : *poGeom)
9116 2 : poSubGeom->accept(this);
9117 2 : }
9118 :
9119 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
9120 : {
9121 1 : visit(poGeom->toUpperClass());
9122 1 : }
9123 :
9124 : /************************************************************************/
9125 : /* OGRGeometryUniquePtrDeleter */
9126 : /************************************************************************/
9127 :
9128 : //! @cond Doxygen_Suppress
9129 1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
9130 : {
9131 1333 : delete poGeom;
9132 1333 : }
9133 :
9134 : //! @endcond
9135 :
9136 : /************************************************************************/
9137 : /* OGRPreparedGeometryUniquePtrDeleter */
9138 : /************************************************************************/
9139 :
9140 : //! @cond Doxygen_Suppress
9141 144 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
9142 : OGRPreparedGeometry *poPreparedGeom) const
9143 : {
9144 144 : OGRDestroyPreparedGeometry(poPreparedGeom);
9145 144 : }
9146 :
9147 : //! @endcond
9148 :
9149 : /************************************************************************/
9150 : /* HomogenizeDimensionalityWith() */
9151 : /************************************************************************/
9152 :
9153 : //! @cond Doxygen_Suppress
9154 3302940 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
9155 : {
9156 3302940 : if (poOtherGeom->Is3D() && !Is3D())
9157 1328980 : set3D(TRUE);
9158 :
9159 3302940 : if (poOtherGeom->IsMeasured() && !IsMeasured())
9160 851 : setMeasured(TRUE);
9161 :
9162 3302940 : if (!poOtherGeom->Is3D() && Is3D())
9163 298 : poOtherGeom->set3D(TRUE);
9164 :
9165 3302940 : if (!poOtherGeom->IsMeasured() && IsMeasured())
9166 41 : poOtherGeom->setMeasured(TRUE);
9167 3302940 : }
9168 :
9169 : //! @endcond
9170 :
9171 : /************************************************************************/
9172 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
9173 : /************************************************************************/
9174 :
9175 : /** Set binary precision options from resolution.
9176 : *
9177 : * @since GDAL 3.9
9178 : */
9179 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
9180 : const OGRGeomCoordinatePrecision &prec)
9181 : {
9182 16 : if (prec.dfXYResolution != 0)
9183 : {
9184 16 : nXYBitPrecision =
9185 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
9186 : }
9187 16 : if (prec.dfZResolution != 0)
9188 : {
9189 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
9190 : }
9191 16 : if (prec.dfMResolution != 0)
9192 : {
9193 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
9194 : }
9195 16 : }
9196 :
9197 : /************************************************************************/
9198 : /* OGRwkbExportOptionsCreate() */
9199 : /************************************************************************/
9200 :
9201 : /**
9202 : * \brief Create geometry WKB export options.
9203 : *
9204 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
9205 : *
9206 : * @return object to be freed with OGRwkbExportOptionsDestroy().
9207 : * @since GDAL 3.9
9208 : */
9209 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
9210 : {
9211 2 : return new OGRwkbExportOptions;
9212 : }
9213 :
9214 : /************************************************************************/
9215 : /* OGRwkbExportOptionsDestroy() */
9216 : /************************************************************************/
9217 :
9218 : /**
9219 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
9220 : *
9221 : * @param psOptions WKB export options
9222 : * @since GDAL 3.9
9223 : */
9224 :
9225 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
9226 : {
9227 2 : delete psOptions;
9228 2 : }
9229 :
9230 : /************************************************************************/
9231 : /* OGRwkbExportOptionsSetByteOrder() */
9232 : /************************************************************************/
9233 :
9234 : /**
9235 : * \brief Set the WKB byte order.
9236 : *
9237 : * @param psOptions WKB export options
9238 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
9239 : * Intel)
9240 : * @since GDAL 3.9
9241 : */
9242 :
9243 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
9244 : OGRwkbByteOrder eByteOrder)
9245 : {
9246 1 : psOptions->eByteOrder = eByteOrder;
9247 1 : }
9248 :
9249 : /************************************************************************/
9250 : /* OGRwkbExportOptionsSetVariant() */
9251 : /************************************************************************/
9252 :
9253 : /**
9254 : * \brief Set the WKB variant
9255 : *
9256 : * @param psOptions WKB export options
9257 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
9258 : * wkbVariantPostGIS1
9259 : * @since GDAL 3.9
9260 : */
9261 :
9262 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
9263 : OGRwkbVariant eWkbVariant)
9264 : {
9265 1 : psOptions->eWkbVariant = eWkbVariant;
9266 1 : }
9267 :
9268 : /************************************************************************/
9269 : /* OGRwkbExportOptionsSetPrecision() */
9270 : /************************************************************************/
9271 :
9272 : /**
9273 : * \brief Set precision options
9274 : *
9275 : * @param psOptions WKB export options
9276 : * @param hPrecisionOptions Precision options (might be null to reset them)
9277 : * @since GDAL 3.9
9278 : */
9279 :
9280 1 : void OGRwkbExportOptionsSetPrecision(
9281 : OGRwkbExportOptions *psOptions,
9282 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
9283 : {
9284 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
9285 1 : if (hPrecisionOptions)
9286 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
9287 1 : }
9288 :
9289 : /************************************************************************/
9290 : /* IsRectangle() */
9291 : /************************************************************************/
9292 :
9293 : /**
9294 : * \brief Returns whether the geometry is a polygon with 4 corners forming
9295 : * a rectangle.
9296 : *
9297 : * @since GDAL 3.10
9298 : */
9299 53044 : bool OGRGeometry::IsRectangle() const
9300 : {
9301 53044 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
9302 352 : return false;
9303 :
9304 52692 : const OGRPolygon *poPoly = toPolygon();
9305 :
9306 52692 : if (poPoly->getNumInteriorRings() != 0)
9307 27 : return false;
9308 :
9309 52665 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
9310 52665 : if (!poRing)
9311 4 : return false;
9312 :
9313 52661 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
9314 213 : return false;
9315 :
9316 : // If the ring has 5 points, the last should be the first.
9317 104839 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
9318 52391 : poRing->getY(0) != poRing->getY(4)))
9319 1 : return false;
9320 :
9321 : // Polygon with first segment in "y" direction.
9322 104170 : if (poRing->getX(0) == poRing->getX(1) &&
9323 103445 : poRing->getY(1) == poRing->getY(2) &&
9324 155892 : poRing->getX(2) == poRing->getX(3) &&
9325 51722 : poRing->getY(3) == poRing->getY(0))
9326 51722 : return true;
9327 :
9328 : // Polygon with first segment in "x" direction.
9329 1369 : if (poRing->getY(0) == poRing->getY(1) &&
9330 1288 : poRing->getX(1) == poRing->getX(2) &&
9331 2013 : poRing->getY(2) == poRing->getY(3) &&
9332 644 : poRing->getX(3) == poRing->getX(0))
9333 644 : return true;
9334 :
9335 81 : return false;
9336 : }
9337 :
9338 : /************************************************************************/
9339 : /* hasEmptyParts() */
9340 : /************************************************************************/
9341 :
9342 : /**
9343 : * \brief Returns whether a geometry has empty parts/rings.
9344 : *
9345 : * Returns true if removeEmptyParts() will modify the geometry.
9346 : *
9347 : * This is different from IsEmpty().
9348 : *
9349 : * @since GDAL 3.10
9350 : */
9351 103 : bool OGRGeometry::hasEmptyParts() const
9352 : {
9353 103 : return false;
9354 : }
9355 :
9356 : /************************************************************************/
9357 : /* removeEmptyParts() */
9358 : /************************************************************************/
9359 :
9360 : /**
9361 : * \brief Remove empty parts/rings from this geometry.
9362 : *
9363 : * @since GDAL 3.10
9364 : */
9365 17 : void OGRGeometry::removeEmptyParts()
9366 : {
9367 17 : }
9368 :
9369 : /************************************************************************/
9370 : /* ~IOGRGeometryVisitor() */
9371 : /************************************************************************/
9372 :
9373 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
9374 :
9375 : /************************************************************************/
9376 : /* ~IOGRConstGeometryVisitor() */
9377 : /************************************************************************/
9378 :
9379 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
|