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 : #define SFCGAL_MAKE_VERSION(major, minor, patch) \
45 : ((major) * 10000 + (minor) * 100 + (patch))
46 : #define SFCGAL_VERSION \
47 : SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR, \
48 : SFCGAL_VERSION_PATCH)
49 :
50 : //! @cond Doxygen_Suppress
51 : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
52 : //! @endcond
53 :
54 : #ifdef HAVE_GEOS
55 103 : static void OGRGEOSErrorHandler(const char *fmt, ...)
56 : {
57 : va_list args;
58 :
59 103 : va_start(args, fmt);
60 103 : CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
61 103 : va_end(args);
62 103 : }
63 :
64 111 : static void OGRGEOSWarningHandler(const char *fmt, ...)
65 : {
66 : va_list args;
67 :
68 111 : va_start(args, fmt);
69 111 : CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
70 111 : va_end(args);
71 111 : }
72 : #endif
73 :
74 : /************************************************************************/
75 : /* OGRWktOptions() */
76 : /************************************************************************/
77 :
78 11220 : int OGRWktOptions::getDefaultPrecision()
79 : {
80 11220 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
81 : }
82 :
83 11316 : bool OGRWktOptions::getDefaultRound()
84 : {
85 11316 : return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
86 : }
87 :
88 : /************************************************************************/
89 : /* OGRGeometry() */
90 : /************************************************************************/
91 :
92 : OGRGeometry::OGRGeometry() = default;
93 :
94 : /************************************************************************/
95 : /* OGRGeometry( const OGRGeometry& ) */
96 : /************************************************************************/
97 :
98 : /**
99 : * \brief Copy constructor.
100 : */
101 :
102 1784260 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
103 1784260 : : poSRS(other.poSRS), flags(other.flags)
104 : {
105 1784260 : if (poSRS != nullptr)
106 75344 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
107 1784260 : }
108 :
109 : /************************************************************************/
110 : /* OGRGeometry( OGRGeometry&& ) */
111 : /************************************************************************/
112 :
113 : /**
114 : * \brief Move constructor.
115 : *
116 : * @since GDAL 3.11
117 : */
118 :
119 156278 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
120 156278 : : poSRS(other.poSRS), flags(other.flags)
121 : {
122 156278 : other.poSRS = nullptr;
123 156278 : }
124 :
125 : /************************************************************************/
126 : /* ~OGRGeometry() */
127 : /************************************************************************/
128 :
129 25577100 : OGRGeometry::~OGRGeometry()
130 :
131 : {
132 12788500 : if (poSRS != nullptr)
133 3625420 : const_cast<OGRSpatialReference *>(poSRS)->Release();
134 12788500 : }
135 :
136 : /************************************************************************/
137 : /* operator=( const OGRGeometry&) */
138 : /************************************************************************/
139 :
140 : /**
141 : * \brief Assignment operator.
142 : */
143 :
144 1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
145 : {
146 1211 : if (this != &other)
147 : {
148 1211 : empty();
149 1211 : assignSpatialReference(other.getSpatialReference());
150 1211 : flags = other.flags;
151 : }
152 1211 : return *this;
153 : }
154 :
155 : /************************************************************************/
156 : /* operator=( OGRGeometry&&) */
157 : /************************************************************************/
158 :
159 : /**
160 : * \brief Move assignment operator.
161 : *
162 : * @since GDAL 3.11
163 : */
164 :
165 103910 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
166 : {
167 103910 : if (this != &other)
168 : {
169 103910 : poSRS = other.poSRS;
170 103910 : other.poSRS = nullptr;
171 103910 : flags = other.flags;
172 : }
173 103910 : return *this;
174 : }
175 :
176 : /************************************************************************/
177 : /* dumpReadable() */
178 : /************************************************************************/
179 :
180 : /**
181 : * \brief Dump geometry in well known text format to indicated output file.
182 : *
183 : * A few options can be defined to change the default dump :
184 : * <ul>
185 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
186 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
187 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
188 : * </ul>
189 : *
190 : * This method is the same as the C function OGR_G_DumpReadable().
191 : *
192 : * @param fp the text file to write the geometry to.
193 : * @param pszPrefix the prefix to put on each line of output.
194 : * @param papszOptions NULL terminated list of options (may be NULL)
195 : */
196 :
197 0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
198 : CSLConstList papszOptions) const
199 :
200 : {
201 0 : if (fp == nullptr)
202 0 : fp = stdout;
203 :
204 0 : const auto osStr = dumpReadable(pszPrefix, papszOptions);
205 0 : fprintf(fp, "%s", osStr.c_str());
206 0 : }
207 :
208 : /************************************************************************/
209 : /* dumpReadable() */
210 : /************************************************************************/
211 :
212 : /**
213 : * \brief Dump geometry in well known text format to indicated output file.
214 : *
215 : * A few options can be defined to change the default dump :
216 : * <ul>
217 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
218 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
219 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
220 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
221 : * in WKT (added in GDAL 3.9)</li>
222 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
223 : * WKT (added in GDAL 3.9)</li>
224 : * </ul>
225 : *
226 : * @param pszPrefix the prefix to put on each line of output.
227 : * @param papszOptions NULL terminated list of options (may be NULL)
228 : * @return a string with the geometry representation.
229 : * @since GDAL 3.7
230 : */
231 :
232 307 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
233 : CSLConstList papszOptions) const
234 :
235 : {
236 307 : if (pszPrefix == nullptr)
237 306 : pszPrefix = "";
238 :
239 307 : std::string osRet;
240 :
241 : const auto exportToWktWithOpts =
242 2044 : [this, pszPrefix, papszOptions, &osRet](bool bIso)
243 : {
244 292 : OGRErr err(OGRERR_NONE);
245 292 : OGRWktOptions opts;
246 292 : if (const char *pszXYPrecision =
247 292 : CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
248 : {
249 1 : opts.format = OGRWktFormat::F;
250 1 : opts.xyPrecision = atoi(pszXYPrecision);
251 : }
252 292 : if (const char *pszZPrecision =
253 292 : CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
254 : {
255 1 : opts.format = OGRWktFormat::F;
256 1 : opts.zPrecision = atoi(pszZPrecision);
257 : }
258 292 : if (bIso)
259 292 : opts.variant = wkbVariantIso;
260 584 : std::string wkt = exportToWkt(opts, &err);
261 292 : if (err == OGRERR_NONE)
262 : {
263 292 : osRet = pszPrefix;
264 292 : osRet += wkt.data();
265 292 : osRet += '\n';
266 : }
267 292 : };
268 :
269 : const char *pszDisplayGeometry =
270 307 : CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
271 307 : if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
272 : {
273 15 : osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
274 15 : switch (getGeometryType())
275 : {
276 1 : case wkbUnknown:
277 : case wkbNone:
278 : case wkbPoint:
279 : case wkbPoint25D:
280 : case wkbPointM:
281 : case wkbPointZM:
282 1 : break;
283 0 : case wkbPolyhedralSurface:
284 : case wkbTIN:
285 : case wkbPolyhedralSurfaceZ:
286 : case wkbTINZ:
287 : case wkbPolyhedralSurfaceM:
288 : case wkbTINM:
289 : case wkbPolyhedralSurfaceZM:
290 : case wkbTINZM:
291 : {
292 0 : const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
293 : osRet +=
294 0 : CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
295 0 : for (auto &&poSubGeom : *poPS)
296 : {
297 0 : osRet += pszPrefix;
298 0 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
299 : }
300 0 : break;
301 : }
302 0 : case wkbLineString:
303 : case wkbLineString25D:
304 : case wkbLineStringM:
305 : case wkbLineStringZM:
306 : case wkbCircularString:
307 : case wkbCircularStringZ:
308 : case wkbCircularStringM:
309 : case wkbCircularStringZM:
310 : {
311 0 : const OGRSimpleCurve *poSC = toSimpleCurve();
312 0 : osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
313 0 : break;
314 : }
315 11 : case wkbPolygon:
316 : case wkbTriangle:
317 : case wkbTriangleZ:
318 : case wkbTriangleM:
319 : case wkbTriangleZM:
320 : case wkbPolygon25D:
321 : case wkbPolygonM:
322 : case wkbPolygonZM:
323 : case wkbCurvePolygon:
324 : case wkbCurvePolygonZ:
325 : case wkbCurvePolygonM:
326 : case wkbCurvePolygonZM:
327 : {
328 11 : const OGRCurvePolygon *poPoly = toCurvePolygon();
329 11 : const OGRCurve *poRing = poPoly->getExteriorRingCurve();
330 11 : const int nRings = poPoly->getNumInteriorRings();
331 11 : if (poRing == nullptr)
332 : {
333 0 : osRet += "empty";
334 : }
335 : else
336 : {
337 11 : osRet += CPLOPrintf("%d points", poRing->getNumPoints());
338 11 : if (wkbFlatten(poRing->getGeometryType()) ==
339 : wkbCompoundCurve)
340 : {
341 0 : osRet += " (";
342 0 : osRet += poRing->dumpReadable(nullptr, papszOptions);
343 0 : osRet += ")";
344 : }
345 11 : if (nRings)
346 : {
347 1 : osRet += CPLOPrintf(", %d inner rings (", nRings);
348 8 : for (int ir = 0; ir < nRings; ir++)
349 : {
350 7 : poRing = poPoly->getInteriorRingCurve(ir);
351 7 : if (ir)
352 6 : osRet += ", ";
353 : osRet +=
354 7 : CPLOPrintf("%d points", poRing->getNumPoints());
355 7 : if (wkbFlatten(poRing->getGeometryType()) ==
356 : wkbCompoundCurve)
357 : {
358 2 : osRet += " (";
359 : osRet +=
360 2 : poRing->dumpReadable(nullptr, papszOptions);
361 2 : osRet += ")";
362 : }
363 : }
364 1 : osRet += ")";
365 : }
366 : }
367 11 : osRet += "\n";
368 11 : break;
369 : }
370 2 : case wkbCompoundCurve:
371 : case wkbCompoundCurveZ:
372 : case wkbCompoundCurveM:
373 : case wkbCompoundCurveZM:
374 : {
375 2 : const OGRCompoundCurve *poCC = toCompoundCurve();
376 2 : if (poCC->getNumCurves() == 0)
377 : {
378 0 : osRet += "empty";
379 : }
380 : else
381 : {
382 6 : for (int i = 0; i < poCC->getNumCurves(); i++)
383 : {
384 4 : if (i)
385 2 : osRet += ", ";
386 : osRet +=
387 8 : CPLOPrintf("%s (%d points)",
388 4 : poCC->getCurve(i)->getGeometryName(),
389 8 : poCC->getCurve(i)->getNumPoints());
390 : }
391 : }
392 2 : break;
393 : }
394 :
395 1 : case wkbMultiPoint:
396 : case wkbMultiLineString:
397 : case wkbMultiPolygon:
398 : case wkbMultiCurve:
399 : case wkbMultiSurface:
400 : case wkbGeometryCollection:
401 : case wkbMultiPoint25D:
402 : case wkbMultiLineString25D:
403 : case wkbMultiPolygon25D:
404 : case wkbMultiCurveZ:
405 : case wkbMultiSurfaceZ:
406 : case wkbGeometryCollection25D:
407 : case wkbMultiPointM:
408 : case wkbMultiLineStringM:
409 : case wkbMultiPolygonM:
410 : case wkbMultiCurveM:
411 : case wkbMultiSurfaceM:
412 : case wkbGeometryCollectionM:
413 : case wkbMultiPointZM:
414 : case wkbMultiLineStringZM:
415 : case wkbMultiPolygonZM:
416 : case wkbMultiCurveZM:
417 : case wkbMultiSurfaceZM:
418 : case wkbGeometryCollectionZM:
419 : {
420 1 : const OGRGeometryCollection *poColl = toGeometryCollection();
421 : osRet +=
422 1 : CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
423 2 : for (auto &&poSubGeom : *poColl)
424 : {
425 1 : osRet += pszPrefix;
426 1 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
427 : }
428 1 : break;
429 : }
430 0 : case wkbLinearRing:
431 : case wkbCurve:
432 : case wkbSurface:
433 : case wkbCurveZ:
434 : case wkbSurfaceZ:
435 : case wkbCurveM:
436 : case wkbSurfaceM:
437 : case wkbCurveZM:
438 : case wkbSurfaceZM:
439 0 : break;
440 15 : }
441 : }
442 292 : else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
443 : {
444 0 : exportToWktWithOpts(/* bIso=*/false);
445 : }
446 292 : else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
447 0 : EQUAL(pszDisplayGeometry, "ISO_WKT"))
448 : {
449 292 : exportToWktWithOpts(/* bIso=*/true);
450 : }
451 :
452 614 : return osRet;
453 : }
454 :
455 : /************************************************************************/
456 : /* OGR_G_DumpReadable() */
457 : /************************************************************************/
458 : /**
459 : * \brief Dump geometry in well known text format to indicated output file.
460 : *
461 : * This method is the same as the CPP method OGRGeometry::dumpReadable.
462 : *
463 : * @param hGeom handle on the geometry to dump.
464 : * @param fp the text file to write the geometry to.
465 : * @param pszPrefix the prefix to put on each line of output.
466 : */
467 :
468 0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
469 :
470 : {
471 0 : VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
472 :
473 0 : OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
474 : }
475 :
476 : /************************************************************************/
477 : /* assignSpatialReference() */
478 : /************************************************************************/
479 :
480 : /**
481 : * \brief Assign spatial reference to this object.
482 : *
483 : * Any existing spatial reference
484 : * is replaced, but under no circumstances does this result in the object
485 : * being reprojected. It is just changing the interpretation of the existing
486 : * geometry. Note that assigning a spatial reference increments the
487 : * reference count on the OGRSpatialReference, but does not copy it.
488 : *
489 : * This will also assign the spatial reference to
490 : * potential sub-geometries of the geometry (OGRGeometryCollection,
491 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
492 : * derived classes).
493 : *
494 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
495 : *
496 : * This method is the same as the C function OGR_G_AssignSpatialReference().
497 : *
498 : * @param poSR new spatial reference system to apply.
499 : */
500 :
501 5593890 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
502 :
503 : {
504 : // Do in that order to properly handle poSR == poSRS
505 5593890 : if (poSR != nullptr)
506 3586140 : const_cast<OGRSpatialReference *>(poSR)->Reference();
507 5593890 : if (poSRS != nullptr)
508 36059 : const_cast<OGRSpatialReference *>(poSRS)->Release();
509 :
510 5593890 : poSRS = poSR;
511 5593890 : }
512 :
513 : /************************************************************************/
514 : /* OGR_G_AssignSpatialReference() */
515 : /************************************************************************/
516 : /**
517 : * \brief Assign spatial reference to this object.
518 : *
519 : * Any existing spatial reference
520 : * is replaced, but under no circumstances does this result in the object
521 : * being reprojected. It is just changing the interpretation of the existing
522 : * geometry. Note that assigning a spatial reference increments the
523 : * reference count on the OGRSpatialReference, but does not copy it.
524 : *
525 : * This will also assign the spatial reference to
526 : * potential sub-geometries of the geometry (OGRGeometryCollection,
527 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
528 : * derived classes).
529 : *
530 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
531 : *
532 : * This function is the same as the CPP method
533 : * OGRGeometry::assignSpatialReference.
534 : *
535 : * @param hGeom handle on the geometry to apply the new spatial reference
536 : * system.
537 : * @param hSRS handle on the new spatial reference system to apply.
538 : */
539 :
540 80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
541 :
542 : {
543 80 : VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
544 :
545 160 : OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
546 80 : OGRSpatialReference::FromHandle(hSRS));
547 : }
548 :
549 : /************************************************************************/
550 : /* Intersects() */
551 : /************************************************************************/
552 :
553 : /**
554 : * \brief Do these features intersect?
555 : *
556 : * Determines whether two geometries intersect. If GEOS is enabled, then
557 : * this is done in rigorous fashion otherwise TRUE is returned if the
558 : * envelopes (bounding boxes) of the two geometries overlap.
559 : *
560 : * The poOtherGeom argument may be safely NULL, but in this case the method
561 : * will always return TRUE. That is, a NULL geometry is treated as being
562 : * everywhere.
563 : *
564 : * This method is the same as the C function OGR_G_Intersects().
565 : *
566 : * @param poOtherGeom the other geometry to test against.
567 : *
568 : * @return TRUE if the geometries intersect, otherwise FALSE.
569 : */
570 :
571 44 : bool OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
572 :
573 : {
574 44 : if (poOtherGeom == nullptr)
575 0 : return TRUE;
576 :
577 44 : OGREnvelope oEnv1;
578 44 : getEnvelope(&oEnv1);
579 :
580 44 : OGREnvelope oEnv2;
581 44 : poOtherGeom->getEnvelope(&oEnv2);
582 :
583 44 : if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
584 26 : oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
585 18 : return FALSE;
586 :
587 : #ifndef HAVE_GEOS
588 : // Without GEOS we assume that envelope overlap is equivalent to
589 : // actual intersection.
590 : return TRUE;
591 : #else
592 :
593 26 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
594 26 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
595 26 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
596 :
597 26 : bool bResult = false;
598 26 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
599 : {
600 26 : bResult =
601 26 : GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
602 : }
603 :
604 26 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
605 26 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
606 26 : freeGEOSContext(hGEOSCtxt);
607 :
608 26 : return bResult;
609 : #endif // HAVE_GEOS
610 : }
611 :
612 : // Old API compatibility function.
613 :
614 : //! @cond Doxygen_Suppress
615 0 : bool OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
616 :
617 : {
618 0 : return Intersects(poOtherGeom);
619 : }
620 :
621 : //! @endcond
622 :
623 : /************************************************************************/
624 : /* OGR_G_Intersects() */
625 : /************************************************************************/
626 : /**
627 : * \brief Do these features intersect?
628 : *
629 : * Determines whether two geometries intersect. If GEOS is enabled, then
630 : * this is done in rigorous fashion otherwise TRUE is returned if the
631 : * envelopes (bounding boxes) of the two geometries overlap.
632 : *
633 : * This function is the same as the CPP method OGRGeometry::Intersects.
634 : *
635 : * @param hGeom handle on the first geometry.
636 : * @param hOtherGeom handle on the other geometry to test against.
637 : *
638 : * @return TRUE if the geometries intersect, otherwise FALSE.
639 : */
640 :
641 11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
642 :
643 : {
644 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
645 11 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
646 :
647 22 : return OGRGeometry::FromHandle(hGeom)->Intersects(
648 22 : OGRGeometry::FromHandle(hOtherGeom));
649 : }
650 :
651 : //! @cond Doxygen_Suppress
652 0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
653 :
654 : {
655 0 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
656 0 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
657 :
658 0 : return OGRGeometry::FromHandle(hGeom)->Intersects(
659 0 : OGRGeometry::FromHandle(hOtherGeom));
660 : }
661 :
662 : //! @endcond
663 :
664 : /************************************************************************/
665 : /* transformTo() */
666 : /************************************************************************/
667 :
668 : /**
669 : * \brief Transform geometry to new spatial reference system.
670 : *
671 : * This method will transform the coordinates of a geometry from
672 : * their current spatial reference system to a new target spatial
673 : * reference system. Normally this means reprojecting the vectors,
674 : * but it could include datum shifts, and changes of units.
675 : *
676 : * This method will only work if the geometry already has an assigned
677 : * spatial reference system, and if it is transformable to the target
678 : * coordinate system.
679 : *
680 : * Because this method requires internal creation and initialization of an
681 : * OGRCoordinateTransformation object it is significantly more expensive to
682 : * use this method to transform many geometries than it is to create the
683 : * OGRCoordinateTransformation in advance, and call transform() with that
684 : * transformation. This method exists primarily for convenience when only
685 : * transforming a single geometry.
686 : *
687 : * This method is the same as the C function OGR_G_TransformTo().
688 : *
689 : * @param poSR spatial reference system to transform to.
690 : *
691 : * @return OGRERR_NONE on success, or an error code.
692 : */
693 :
694 27 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
695 :
696 : {
697 27 : if (getSpatialReference() == nullptr)
698 : {
699 1 : CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
700 1 : return OGRERR_FAILURE;
701 : }
702 :
703 26 : if (poSR == nullptr)
704 : {
705 0 : CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
706 0 : return OGRERR_FAILURE;
707 : }
708 :
709 : OGRCoordinateTransformation *poCT =
710 26 : OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
711 26 : if (poCT == nullptr)
712 0 : return OGRERR_FAILURE;
713 :
714 26 : const OGRErr eErr = transform(poCT);
715 :
716 26 : delete poCT;
717 :
718 26 : return eErr;
719 : }
720 :
721 : /************************************************************************/
722 : /* OGR_G_TransformTo() */
723 : /************************************************************************/
724 : /**
725 : * \brief Transform geometry to new spatial reference system.
726 : *
727 : * This function will transform the coordinates of a geometry from
728 : * their current spatial reference system to a new target spatial
729 : * reference system. Normally this means reprojecting the vectors,
730 : * but it could include datum shifts, and changes of units.
731 : *
732 : * This function will only work if the geometry already has an assigned
733 : * spatial reference system, and if it is transformable to the target
734 : * coordinate system.
735 : *
736 : * Because this function requires internal creation and initialization of an
737 : * OGRCoordinateTransformation object it is significantly more expensive to
738 : * use this function to transform many geometries than it is to create the
739 : * OGRCoordinateTransformation in advance, and call transform() with that
740 : * transformation. This function exists primarily for convenience when only
741 : * transforming a single geometry.
742 : *
743 : * This function is the same as the CPP method OGRGeometry::transformTo.
744 : *
745 : * @param hGeom handle on the geometry to apply the transform to.
746 : * @param hSRS handle on the spatial reference system to apply.
747 : *
748 : * @return OGRERR_NONE on success, or an error code.
749 : */
750 :
751 9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
752 :
753 : {
754 9 : VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
755 :
756 18 : return OGRGeometry::FromHandle(hGeom)->transformTo(
757 18 : OGRSpatialReference::FromHandle(hSRS));
758 : }
759 :
760 : /**
761 : * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
762 : *
763 : * \brief Apply arbitrary coordinate transformation to geometry.
764 : *
765 : * This method will transform the coordinates of a geometry from
766 : * their current spatial reference system to a new target spatial
767 : * reference system. Normally this means reprojecting the vectors,
768 : * but it could include datum shifts, and changes of units.
769 : *
770 : * Note that this method does not require that the geometry already
771 : * have a spatial reference system. It will be assumed that they can
772 : * be treated as having the source spatial reference system of the
773 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
774 : * will be ignored. On successful completion the output OGRSpatialReference
775 : * of the OGRCoordinateTransformation will be assigned to the geometry.
776 : *
777 : * This method only does reprojection on a point-by-point basis. It does not
778 : * include advanced logic to deal with discontinuities at poles or antimeridian.
779 : * For that, use the OGRGeometryFactory::transformWithOptions() method.
780 : *
781 : * This method is the same as the C function OGR_G_Transform().
782 : *
783 : * @param poCT the transformation to apply.
784 : *
785 : * @return OGRERR_NONE on success or an error code.
786 : */
787 :
788 : /************************************************************************/
789 : /* OGR_G_Transform() */
790 : /************************************************************************/
791 : /**
792 : * \brief Apply arbitrary coordinate transformation to geometry.
793 : *
794 : * This function will transform the coordinates of a geometry from
795 : * their current spatial reference system to a new target spatial
796 : * reference system. Normally this means reprojecting the vectors,
797 : * but it could include datum shifts, and changes of units.
798 : *
799 : * Note that this function does not require that the geometry already
800 : * have a spatial reference system. It will be assumed that they can
801 : * be treated as having the source spatial reference system of the
802 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
803 : * will be ignored. On successful completion the output OGRSpatialReference
804 : * of the OGRCoordinateTransformation will be assigned to the geometry.
805 : *
806 : * This function only does reprojection on a point-by-point basis. It does not
807 : * include advanced logic to deal with discontinuities at poles or antimeridian.
808 : * For that, use the OGR_GeomTransformer_Create() and
809 : * OGR_GeomTransformer_Transform() functions.
810 : *
811 : * This function is the same as the CPP method OGRGeometry::transform.
812 : *
813 : * @param hGeom handle on the geometry to apply the transform to.
814 : * @param hTransform handle on the transformation to apply.
815 : *
816 : * @return OGRERR_NONE on success or an error code.
817 : */
818 :
819 11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
820 : OGRCoordinateTransformationH hTransform)
821 :
822 : {
823 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
824 :
825 22 : return OGRGeometry::FromHandle(hGeom)->transform(
826 11 : OGRCoordinateTransformation::FromHandle(hTransform));
827 : }
828 :
829 : /**
830 : * \fn int OGRGeometry::getDimension() const;
831 : *
832 : * \brief Get the dimension of this object.
833 : *
834 : * This method corresponds to the SFCOM IGeometry::GetDimension() method.
835 : * It indicates the dimension of the object, but does not indicate the
836 : * dimension of the underlying space (as indicated by
837 : * OGRGeometry::getCoordinateDimension()).
838 : *
839 : * This method is the same as the C function OGR_G_GetDimension().
840 : *
841 : * @return 0 for points, 1 for lines and 2 for surfaces.
842 : */
843 :
844 : /**
845 : * \brief Get the geometry type that conforms with ISO SQL/MM Part3
846 : *
847 : * @return the geometry type that conforms with ISO SQL/MM Part3
848 : */
849 716953 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
850 : {
851 716953 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
852 :
853 716953 : if (flags & OGR_G_3D)
854 214269 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
855 716953 : if (flags & OGR_G_MEASURED)
856 26025 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
857 :
858 716953 : return nGType;
859 : }
860 :
861 : /************************************************************************/
862 : /* OGRGeometry::segmentize() */
863 : /************************************************************************/
864 : /**
865 : *
866 : * \brief Modify the geometry such it has no segment longer then the
867 : * given distance.
868 : *
869 : * This method modifies the geometry to add intermediate vertices if necessary
870 : * so that the maximum length between 2 consecutive vertices is lower than
871 : * dfMaxLength.
872 : *
873 : * Interpolated points will have Z and M values (if needed) set to 0.
874 : * Distance computation is performed in 2d only
875 : *
876 : * This function is the same as the C function OGR_G_Segmentize()
877 : *
878 : * @param dfMaxLength the maximum distance between 2 points after segmentization
879 : * @return (since 3.10) true in case of success, false in case of error.
880 : */
881 :
882 0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
883 : {
884 : // Do nothing.
885 0 : return true;
886 : }
887 :
888 : /************************************************************************/
889 : /* OGR_G_Segmentize() */
890 : /************************************************************************/
891 :
892 : /**
893 : *
894 : * \brief Modify the geometry such it has no segment longer then the given
895 : * distance.
896 : *
897 : * Interpolated points will have Z and M values (if needed) set to 0.
898 : * Distance computation is performed in 2d only.
899 : *
900 : * This function is the same as the CPP method OGRGeometry::segmentize().
901 : *
902 : * @param hGeom handle on the geometry to segmentize
903 : * @param dfMaxLength the maximum distance between 2 points after segmentization
904 : */
905 :
906 24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
907 : {
908 24 : VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
909 :
910 24 : if (dfMaxLength <= 0)
911 : {
912 0 : CPLError(CE_Failure, CPLE_AppDefined,
913 : "dfMaxLength must be strictly positive");
914 0 : return;
915 : }
916 24 : OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
917 : }
918 :
919 : /************************************************************************/
920 : /* OGR_G_GetDimension() */
921 : /************************************************************************/
922 : /**
923 : *
924 : * \brief Get the dimension of this geometry.
925 : *
926 : * This function corresponds to the SFCOM IGeometry::GetDimension() method.
927 : * It indicates the dimension of the geometry, but does not indicate the
928 : * dimension of the underlying space (as indicated by
929 : * OGR_G_GetCoordinateDimension() function).
930 : *
931 : * This function is the same as the CPP method OGRGeometry::getDimension().
932 : *
933 : * @param hGeom handle on the geometry to get the dimension from.
934 : * @return 0 for points, 1 for lines and 2 for surfaces.
935 : */
936 :
937 21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
938 :
939 : {
940 21 : VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
941 :
942 21 : return OGRGeometry::FromHandle(hGeom)->getDimension();
943 : }
944 :
945 : /************************************************************************/
946 : /* getCoordinateDimension() */
947 : /************************************************************************/
948 : /**
949 : * \brief Get the dimension of the coordinates in this object.
950 : *
951 : * This method is the same as the C function OGR_G_GetCoordinateDimension().
952 : *
953 : * @deprecated use CoordinateDimension().
954 : *
955 : * @return this will return 2 or 3.
956 : */
957 :
958 571478 : int OGRGeometry::getCoordinateDimension() const
959 :
960 : {
961 571478 : return (flags & OGR_G_3D) ? 3 : 2;
962 : }
963 :
964 : /************************************************************************/
965 : /* CoordinateDimension() */
966 : /************************************************************************/
967 : /**
968 : * \brief Get the dimension of the coordinates in this object.
969 : *
970 : * This method is the same as the C function OGR_G_CoordinateDimension().
971 : *
972 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
973 : *
974 : */
975 :
976 30428 : int OGRGeometry::CoordinateDimension() const
977 :
978 : {
979 30428 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
980 7375 : return 4;
981 23053 : else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
982 6863 : return 3;
983 : else
984 16190 : return 2;
985 : }
986 :
987 : /************************************************************************/
988 : /* OGR_G_GetCoordinateDimension() */
989 : /************************************************************************/
990 : /**
991 : *
992 : * \brief Get the dimension of the coordinates in this geometry.
993 : *
994 : * This function is the same as the CPP method
995 : * OGRGeometry::getCoordinateDimension().
996 : *
997 : * @param hGeom handle on the geometry to get the dimension of the
998 : * coordinates from.
999 : *
1000 : * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
1001 : * OGR_G_IsMeasured().
1002 : *
1003 : * @return this will return 2 or 3.
1004 : */
1005 :
1006 724 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
1007 :
1008 : {
1009 724 : VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
1010 :
1011 724 : return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
1012 : }
1013 :
1014 : /************************************************************************/
1015 : /* OGR_G_CoordinateDimension() */
1016 : /************************************************************************/
1017 : /**
1018 : *
1019 : * \brief Get the dimension of the coordinates in this geometry.
1020 : *
1021 : * This function is the same as the CPP method
1022 : * OGRGeometry::CoordinateDimension().
1023 : *
1024 : * @param hGeom handle on the geometry to get the dimension of the
1025 : * coordinates from.
1026 : *
1027 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
1028 : *
1029 : */
1030 :
1031 4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
1032 :
1033 : {
1034 4 : VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
1035 :
1036 4 : return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
1037 : }
1038 :
1039 : /**
1040 : *
1041 : * \brief See whether this geometry has Z coordinates.
1042 : *
1043 : * This function is the same as the CPP method
1044 : * OGRGeometry::Is3D().
1045 : *
1046 : * @param hGeom handle on the geometry to check whether it has Z coordinates.
1047 : *
1048 : * @return TRUE if the geometry has Z coordinates.
1049 : */
1050 :
1051 37776 : int OGR_G_Is3D(OGRGeometryH hGeom)
1052 :
1053 : {
1054 37776 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1055 :
1056 37776 : return OGRGeometry::FromHandle(hGeom)->Is3D();
1057 : }
1058 :
1059 : /**
1060 : *
1061 : * \brief See whether this geometry is measured.
1062 : *
1063 : * This function is the same as the CPP method
1064 : * OGRGeometry::IsMeasured().
1065 : *
1066 : * @param hGeom handle on the geometry to check whether it is measured.
1067 : *
1068 : * @return TRUE if the geometry has M coordinates.
1069 : */
1070 :
1071 40185 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1072 :
1073 : {
1074 40185 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1075 :
1076 40185 : return OGRGeometry::FromHandle(hGeom)->IsMeasured();
1077 : }
1078 :
1079 : /************************************************************************/
1080 : /* setCoordinateDimension() */
1081 : /************************************************************************/
1082 :
1083 : /**
1084 : * \brief Set the coordinate dimension.
1085 : *
1086 : * This method sets the explicit coordinate dimension. Setting the coordinate
1087 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1088 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1089 : * will affect the children geometries.
1090 : * This will also remove the M dimension if present before this call.
1091 : *
1092 : * @deprecated use set3D() or setMeasured().
1093 : *
1094 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1095 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1096 : */
1097 :
1098 66665 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1099 :
1100 : {
1101 66665 : if (nNewDimension == 2)
1102 66173 : flags &= ~OGR_G_3D;
1103 : else
1104 492 : flags |= OGR_G_3D;
1105 66665 : return setMeasured(FALSE);
1106 : }
1107 :
1108 : /**
1109 : * \brief Add or remove the Z coordinate dimension.
1110 : *
1111 : * This method adds or removes the explicit Z coordinate dimension.
1112 : * Removing the Z coordinate dimension of a geometry will remove any
1113 : * existing Z values. Adding the Z dimension to a geometry
1114 : * collection, a compound curve, a polygon, etc. will affect the
1115 : * children geometries.
1116 : *
1117 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1118 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1119 : */
1120 :
1121 1618090 : bool OGRGeometry::set3D(bool bIs3D)
1122 :
1123 : {
1124 1618090 : if (bIs3D)
1125 1612740 : flags |= OGR_G_3D;
1126 : else
1127 5350 : flags &= ~OGR_G_3D;
1128 1618090 : return true;
1129 : }
1130 :
1131 : /**
1132 : * \brief Add or remove the M coordinate dimension.
1133 : *
1134 : * This method adds or removes the explicit M coordinate dimension.
1135 : * Removing the M coordinate dimension of a geometry will remove any
1136 : * existing M values. Adding the M dimension to a geometry
1137 : * collection, a compound curve, a polygon, etc. will affect the
1138 : * children geometries.
1139 : *
1140 : * @param bIsMeasured Should the geometry have a M dimension, either
1141 : * TRUE or FALSE.
1142 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1143 : */
1144 :
1145 413283 : bool OGRGeometry::setMeasured(bool bIsMeasured)
1146 :
1147 : {
1148 413283 : if (bIsMeasured)
1149 137729 : flags |= OGR_G_MEASURED;
1150 : else
1151 275554 : flags &= ~OGR_G_MEASURED;
1152 413283 : return true;
1153 : }
1154 :
1155 : /************************************************************************/
1156 : /* OGR_G_SetCoordinateDimension() */
1157 : /************************************************************************/
1158 :
1159 : /**
1160 : * \brief Set the coordinate dimension.
1161 : *
1162 : * This method sets the explicit coordinate dimension. Setting the coordinate
1163 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1164 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1165 : * will affect the children geometries.
1166 : * This will also remove the M dimension if present before this call.
1167 : *
1168 : * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
1169 : *
1170 : * @param hGeom handle on the geometry to set the dimension of the
1171 : * coordinates.
1172 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1173 : */
1174 :
1175 56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
1176 :
1177 : {
1178 56 : VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
1179 :
1180 56 : OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
1181 : }
1182 :
1183 : /************************************************************************/
1184 : /* OGR_G_Set3D() */
1185 : /************************************************************************/
1186 :
1187 : /**
1188 : * \brief Add or remove the Z coordinate dimension.
1189 : *
1190 : * This method adds or removes the explicit Z coordinate dimension.
1191 : * Removing the Z coordinate dimension of a geometry will remove any
1192 : * existing Z values. Adding the Z dimension to a geometry
1193 : * collection, a compound curve, a polygon, etc. will affect the
1194 : * children geometries.
1195 : *
1196 : * @param hGeom handle on the geometry to set or unset the Z dimension.
1197 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1198 : */
1199 :
1200 154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
1201 :
1202 : {
1203 154 : VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
1204 :
1205 154 : OGRGeometry::FromHandle(hGeom)->set3D(CPL_TO_BOOL(bIs3D));
1206 : }
1207 :
1208 : /************************************************************************/
1209 : /* OGR_G_SetMeasured() */
1210 : /************************************************************************/
1211 :
1212 : /**
1213 : * \brief Add or remove the M coordinate dimension.
1214 : *
1215 : * This method adds or removes the explicit M coordinate dimension.
1216 : * Removing the M coordinate dimension of a geometry will remove any
1217 : * existing M values. Adding the M dimension to a geometry
1218 : * collection, a compound curve, a polygon, etc. will affect the
1219 : * children geometries.
1220 : *
1221 : * @param hGeom handle on the geometry to set or unset the M dimension.
1222 : * @param bIsMeasured Should the geometry have a M dimension, either
1223 : * TRUE or FALSE.
1224 : */
1225 :
1226 154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
1227 :
1228 : {
1229 154 : VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
1230 :
1231 154 : OGRGeometry::FromHandle(hGeom)->setMeasured(CPL_TO_BOOL(bIsMeasured));
1232 : }
1233 :
1234 : /**
1235 : * \fn bool OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
1236 : *
1237 : * \brief Returns TRUE if two geometries are equivalent.
1238 : *
1239 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1240 : *
1241 : * The comparison is done in a structural way, that is to say that the geometry
1242 : * types must be identical, as well as the number and ordering of sub-geometries
1243 : * and vertices.
1244 : * Or equivalently, two geometries are considered equal by this method if their
1245 : * WKT/WKB representation is equal.
1246 : * Note: this must be distinguished for equality in a spatial way (which is
1247 : * the purpose of the ST_Equals() operation).
1248 : *
1249 : * This method is the same as the C function OGR_G_Equals().
1250 : *
1251 : * @return TRUE if equivalent or FALSE otherwise.
1252 : */
1253 :
1254 : // Backward compatibility method.
1255 :
1256 : //! @cond Doxygen_Suppress
1257 0 : bool OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
1258 : {
1259 0 : return Equals(poOtherGeom);
1260 : }
1261 :
1262 : //! @endcond
1263 :
1264 : /************************************************************************/
1265 : /* OGR_G_Equals() */
1266 : /************************************************************************/
1267 :
1268 : /**
1269 : * \brief Returns TRUE if two geometries are equivalent.
1270 : *
1271 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1272 : *
1273 : * The comparison is done in a structural way, that is to say that the geometry
1274 : * types must be identical, as well as the number and ordering of sub-geometries
1275 : * and vertices.
1276 : * Or equivalently, two geometries are considered equal by this method if their
1277 : * WKT/WKB representation is equal.
1278 : * Note: this must be distinguished for equality in a spatial way (which is
1279 : * the purpose of the ST_Equals() operation).
1280 : *
1281 : * This function is the same as the CPP method OGRGeometry::Equals() method.
1282 : *
1283 : * @param hGeom handle on the first geometry.
1284 : * @param hOther handle on the other geometry to test against.
1285 : * @return TRUE if equivalent or FALSE otherwise.
1286 : */
1287 :
1288 28137 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1289 :
1290 : {
1291 28137 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1292 :
1293 28137 : if (hOther == nullptr)
1294 : {
1295 0 : CPLError(CE_Failure, CPLE_ObjectNull,
1296 : "hOther was NULL in OGR_G_Equals");
1297 0 : return 0;
1298 : }
1299 :
1300 56274 : return OGRGeometry::FromHandle(hGeom)->Equals(
1301 56274 : OGRGeometry::FromHandle(hOther));
1302 : }
1303 :
1304 : //! @cond Doxygen_Suppress
1305 0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
1306 :
1307 : {
1308 0 : if (hGeom == nullptr)
1309 : {
1310 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
1311 0 : return 0;
1312 : }
1313 :
1314 0 : if (hOther == nullptr)
1315 : {
1316 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
1317 0 : return 0;
1318 : }
1319 :
1320 0 : return OGRGeometry::FromHandle(hGeom)->Equals(
1321 0 : OGRGeometry::FromHandle(hOther));
1322 : }
1323 :
1324 : //! @endcond
1325 :
1326 : /**
1327 : * \fn int OGRGeometry::WkbSize() const;
1328 : *
1329 : * \brief Returns size of related binary representation.
1330 : *
1331 : * This method returns the exact number of bytes required to hold the
1332 : * well known binary representation of this geometry object. Its computation
1333 : * may be slightly expensive for complex geometries.
1334 : *
1335 : * This method relates to the SFCOM IWks::WkbSize() method.
1336 : *
1337 : * This method is the same as the C function OGR_G_WkbSize().
1338 : *
1339 : * @return size of binary representation in bytes.
1340 : */
1341 :
1342 : /************************************************************************/
1343 : /* OGR_G_WkbSize() */
1344 : /************************************************************************/
1345 : /**
1346 : * \brief Returns size of related binary representation.
1347 : *
1348 : * This function returns the exact number of bytes required to hold the
1349 : * well known binary representation of this geometry object. Its computation
1350 : * may be slightly expensive for complex geometries.
1351 : *
1352 : * This function relates to the SFCOM IWks::WkbSize() method.
1353 : *
1354 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1355 : *
1356 : * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
1357 : *
1358 : * @param hGeom handle on the geometry to get the binary size from.
1359 : * @return size of binary representation in bytes.
1360 : */
1361 :
1362 1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
1363 :
1364 : {
1365 1 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
1366 :
1367 1 : const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
1368 1 : if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
1369 : {
1370 0 : CPLError(CE_Failure, CPLE_AppDefined,
1371 : "OGR_G_WkbSize() would return a value beyond int range. "
1372 : "Use OGR_G_WkbSizeEx() instead");
1373 0 : return 0;
1374 : }
1375 1 : return static_cast<int>(nSize);
1376 : }
1377 :
1378 : /************************************************************************/
1379 : /* OGR_G_WkbSizeEx() */
1380 : /************************************************************************/
1381 : /**
1382 : * \brief Returns size of related binary representation.
1383 : *
1384 : * This function returns the exact number of bytes required to hold the
1385 : * well known binary representation of this geometry object. Its computation
1386 : * may be slightly expensive for complex geometries.
1387 : *
1388 : * This function relates to the SFCOM IWks::WkbSize() method.
1389 : *
1390 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1391 : *
1392 : * @param hGeom handle on the geometry to get the binary size from.
1393 : * @return size of binary representation in bytes.
1394 : * @since GDAL 3.3
1395 : */
1396 :
1397 10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
1398 :
1399 : {
1400 10679 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
1401 :
1402 10679 : return OGRGeometry::FromHandle(hGeom)->WkbSize();
1403 : }
1404 :
1405 : /**
1406 : * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
1407 : *
1408 : * \brief Computes and returns the bounding envelope for this geometry
1409 : * in the passed psEnvelope structure.
1410 : *
1411 : * This method is the same as the C function OGR_G_GetEnvelope().
1412 : *
1413 : * @param psEnvelope the structure in which to place the results.
1414 : */
1415 :
1416 : /************************************************************************/
1417 : /* OGR_G_GetEnvelope() */
1418 : /************************************************************************/
1419 : /**
1420 : * \brief Computes and returns the bounding envelope for this geometry
1421 : * in the passed psEnvelope structure.
1422 : *
1423 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1424 : *
1425 : * @param hGeom handle of the geometry to get envelope from.
1426 : * @param psEnvelope the structure in which to place the results.
1427 : */
1428 :
1429 13358 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
1430 :
1431 : {
1432 13358 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1433 :
1434 13358 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1435 : }
1436 :
1437 : /**
1438 : * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
1439 : *
1440 : * \brief Computes and returns the bounding envelope (3D) for this
1441 : * geometry in the passed psEnvelope structure.
1442 : *
1443 : * This method is the same as the C function OGR_G_GetEnvelope3D().
1444 : *
1445 : * @param psEnvelope the structure in which to place the results.
1446 : *
1447 : */
1448 :
1449 : /************************************************************************/
1450 : /* OGR_G_GetEnvelope3D() */
1451 : /************************************************************************/
1452 : /**
1453 : * \brief Computes and returns the bounding envelope (3D) for this
1454 : * geometry in the passed psEnvelope structure.
1455 : *
1456 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1457 : *
1458 : * @param hGeom handle of the geometry to get envelope from.
1459 : * @param psEnvelope the structure in which to place the results.
1460 : *
1461 : */
1462 :
1463 10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
1464 :
1465 : {
1466 10 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
1467 :
1468 10 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1469 : }
1470 :
1471 : /************************************************************************/
1472 : /* importFromWkb() */
1473 : /************************************************************************/
1474 :
1475 : /**
1476 : * \brief Assign geometry from well known binary data.
1477 : *
1478 : * The object must have already been instantiated as the correct derived
1479 : * type of geometry object to match the binaries type. This method is used
1480 : * by the OGRGeometryFactory class, but not normally called by application
1481 : * code.
1482 : *
1483 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1484 : *
1485 : * This method is the same as the C function OGR_G_ImportFromWkb().
1486 : *
1487 : * @param pabyData the binary input data.
1488 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1489 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1490 : * done for curve geometries code
1491 : *
1492 : * @return OGRERR_NONE if all goes well, otherwise any of
1493 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1494 : * OGRERR_CORRUPT_DATA may be returned.
1495 : */
1496 :
1497 492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
1498 : OGRwkbVariant eWkbVariant)
1499 : {
1500 492 : size_t nBytesConsumedOutIgnored = 0;
1501 492 : return importFromWkb(pabyData, nSize, eWkbVariant,
1502 984 : nBytesConsumedOutIgnored);
1503 : }
1504 :
1505 : /**
1506 : * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
1507 : * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
1508 : *
1509 : * \brief Assign geometry from well known binary data.
1510 : *
1511 : * The object must have already been instantiated as the correct derived
1512 : * type of geometry object to match the binaries type. This method is used
1513 : * by the OGRGeometryFactory class, but not normally called by application
1514 : * code.
1515 : *
1516 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1517 : *
1518 : * This method is the same as the C function OGR_G_ImportFromWkb().
1519 : *
1520 : * @param pabyData the binary input data.
1521 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1522 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1523 : * done for curve geometries code
1524 : * @param nBytesConsumedOut output parameter. Number of bytes consumed.
1525 : *
1526 : * @return OGRERR_NONE if all goes well, otherwise any of
1527 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1528 : * OGRERR_CORRUPT_DATA may be returned.
1529 : *
1530 : */
1531 :
1532 : /************************************************************************/
1533 : /* OGR_G_ImportFromWkb() */
1534 : /************************************************************************/
1535 : /**
1536 : * \brief Assign geometry from well known binary data.
1537 : *
1538 : * The object must have already been instantiated as the correct derived
1539 : * type of geometry object to match the binaries type.
1540 : *
1541 : * This function relates to the SFCOM IWks::ImportFromWKB() method.
1542 : *
1543 : * This function is the same as the CPP method OGRGeometry::importFromWkb().
1544 : *
1545 : * @param hGeom handle on the geometry to assign the well know binary data to.
1546 : * @param pabyData the binary input data.
1547 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1548 : *
1549 : * @return OGRERR_NONE if all goes well, otherwise any of
1550 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1551 : * OGRERR_CORRUPT_DATA may be returned.
1552 : */
1553 :
1554 0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
1555 :
1556 : {
1557 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
1558 :
1559 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkb(
1560 0 : static_cast<const GByte *>(pabyData), nSize);
1561 : }
1562 :
1563 : /************************************************************************/
1564 : /* OGRGeometry::exportToWkb() */
1565 : /************************************************************************/
1566 :
1567 : /* clang-format off */
1568 : /**
1569 : * \brief Convert a geometry into well known binary format.
1570 : *
1571 : * This method relates to the SFCOM IWks::ExportToWKB() method.
1572 : *
1573 : * This method is the same as the C function OGR_G_ExportToWkb() or
1574 : * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
1575 : *
1576 : * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1577 : * respectively.
1578 : * @param pabyData a buffer into which the binary representation is
1579 : * written. This buffer must be at least
1580 : * OGRGeometry::WkbSize() byte in size.
1581 : * @param eWkbVariant What standard to use when exporting geometries
1582 : * with three dimensions (or more). The default
1583 : * wkbVariantOldOgc is the historical OGR
1584 : * variant. wkbVariantIso is the variant defined
1585 : * in ISO SQL/MM and adopted by OGC for SFSQL
1586 : * 1.2.
1587 : *
1588 : * @return Currently OGRERR_NONE is always returned.
1589 : */
1590 : /* clang-format on */
1591 :
1592 275936 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1593 : unsigned char *pabyData,
1594 : OGRwkbVariant eWkbVariant) const
1595 : {
1596 275936 : OGRwkbExportOptions sOptions;
1597 275936 : sOptions.eByteOrder = eByteOrder;
1598 275936 : sOptions.eWkbVariant = eWkbVariant;
1599 551872 : return exportToWkb(pabyData, &sOptions);
1600 : }
1601 :
1602 : /************************************************************************/
1603 : /* OGR_G_ExportToWkb() */
1604 : /************************************************************************/
1605 : /**
1606 : * \brief Convert a geometry well known binary format
1607 : *
1608 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1609 : *
1610 : * For backward compatibility purposes, it exports the Old-style 99-402
1611 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1612 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1613 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
1614 : *
1615 : * This function is the same as the CPP method
1616 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
1617 : * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
1618 : *
1619 : * @param hGeom handle on the geometry to convert to a well know binary
1620 : * data from.
1621 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1622 : * respectively.
1623 : * @param pabyDstBuffer a buffer into which the binary representation is
1624 : * written. This buffer must be at least
1625 : * OGR_G_WkbSize() byte in size.
1626 : *
1627 : * @return Currently OGRERR_NONE is always returned.
1628 : */
1629 :
1630 109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1631 : unsigned char *pabyDstBuffer)
1632 :
1633 : {
1634 109 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
1635 :
1636 109 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
1637 : }
1638 :
1639 : /************************************************************************/
1640 : /* OGR_G_ExportToIsoWkb() */
1641 : /************************************************************************/
1642 : /**
1643 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
1644 : * binary format
1645 : *
1646 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1647 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
1648 : * types.
1649 : *
1650 : * This function is the same as the CPP method
1651 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
1652 : * with eWkbVariant = wkbVariantIso.
1653 : *
1654 : * @param hGeom handle on the geometry to convert to a well know binary
1655 : * data from.
1656 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1657 : * respectively.
1658 : * @param pabyDstBuffer a buffer into which the binary representation is
1659 : * written. This buffer must be at least
1660 : * OGR_G_WkbSize() byte in size.
1661 : *
1662 : * @return Currently OGRERR_NONE is always returned.
1663 : *
1664 : */
1665 :
1666 10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1667 : unsigned char *pabyDstBuffer)
1668 :
1669 : {
1670 10571 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
1671 :
1672 10571 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
1673 10571 : wkbVariantIso);
1674 : }
1675 :
1676 : /************************************************************************/
1677 : /* OGR_G_ExportToWkbEx() */
1678 : /************************************************************************/
1679 :
1680 : /* clang-format off */
1681 : /**
1682 : * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
1683 : *
1684 : * \brief Convert a geometry into well known binary format
1685 : *
1686 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1687 : *
1688 : * This function is the same as the C function OGR_G_ExportToWkbEx().
1689 : *
1690 : * @param pabyDstBuffer a buffer into which the binary representation is
1691 : * written. This buffer must be at least
1692 : * OGR_G_WkbSize() byte in size.
1693 : * @param psOptions WKB export options.
1694 :
1695 : * @return Currently OGRERR_NONE is always returned.
1696 : *
1697 : * @since GDAL 3.9
1698 : */
1699 : /* clang-format on */
1700 :
1701 : /**
1702 : * \brief Convert a geometry into well known binary format
1703 : *
1704 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1705 : *
1706 : * This function is the same as the CPP method
1707 : * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
1708 : *
1709 : * @param hGeom handle on the geometry to convert to a well know binary
1710 : * data from.
1711 : * @param pabyDstBuffer a buffer into which the binary representation is
1712 : * written. This buffer must be at least
1713 : * OGR_G_WkbSize() byte in size.
1714 : * @param psOptions WKB export options.
1715 :
1716 : * @return Currently OGRERR_NONE is always returned.
1717 : *
1718 : * @since GDAL 3.9
1719 : */
1720 :
1721 2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
1722 : const OGRwkbExportOptions *psOptions)
1723 : {
1724 2 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
1725 :
1726 4 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
1727 2 : psOptions);
1728 : }
1729 :
1730 : /**
1731 : * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
1732 : *
1733 : * \brief Assign geometry from well known text data.
1734 : *
1735 : * The object must have already been instantiated as the correct derived
1736 : * type of geometry object to match the text type. This method is used
1737 : * by the OGRGeometryFactory class, but not normally called by application
1738 : * code.
1739 : *
1740 : * This method relates to the SFCOM IWks::ImportFromWKT() method.
1741 : *
1742 : * This method is the same as the C function OGR_G_ImportFromWkt().
1743 : *
1744 : * @param ppszInput pointer to a pointer to the source text. The pointer is
1745 : * updated to pointer after the consumed text.
1746 : *
1747 : * @return OGRERR_NONE if all goes well, otherwise any of
1748 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1749 : * OGRERR_CORRUPT_DATA may be returned.
1750 : */
1751 :
1752 : /************************************************************************/
1753 : /* OGR_G_ImportFromWkt() */
1754 : /************************************************************************/
1755 : /**
1756 : * \brief Assign geometry from well known text data.
1757 : *
1758 : * The object must have already been instantiated as the correct derived
1759 : * type of geometry object to match the text type.
1760 : *
1761 : * This function relates to the SFCOM IWks::ImportFromWKT() method.
1762 : *
1763 : * This function is the same as the CPP method OGRGeometry::importFromWkt().
1764 : *
1765 : * @param hGeom handle on the geometry to assign well know text data to.
1766 : * @param ppszSrcText pointer to a pointer to the source text. The pointer is
1767 : * updated to pointer after the consumed text.
1768 : *
1769 : * @return OGRERR_NONE if all goes well, otherwise any of
1770 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1771 : * OGRERR_CORRUPT_DATA may be returned.
1772 : */
1773 :
1774 0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
1775 :
1776 : {
1777 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
1778 :
1779 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkt(
1780 0 : const_cast<const char **>(ppszSrcText));
1781 : }
1782 :
1783 : /************************************************************************/
1784 : /* importPreambleFromWkt() */
1785 : /************************************************************************/
1786 :
1787 : // Returns -1 if processing must continue.
1788 : //! @cond Doxygen_Suppress
1789 123642 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1790 : int *pbHasM, bool *pbIsEmpty)
1791 : {
1792 123642 : const char *pszInput = *ppszInput;
1793 :
1794 : /* -------------------------------------------------------------------- */
1795 : /* Clear existing Geoms. */
1796 : /* -------------------------------------------------------------------- */
1797 123642 : empty();
1798 123642 : *pbIsEmpty = false;
1799 :
1800 : /* -------------------------------------------------------------------- */
1801 : /* Read and verify the type keyword, and ensure it matches the */
1802 : /* actual type of this container. */
1803 : /* -------------------------------------------------------------------- */
1804 123642 : bool bHasM = false;
1805 123642 : bool bHasZ = false;
1806 123642 : bool bAlreadyGotDimension = false;
1807 :
1808 123642 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1809 123642 : pszInput = OGRWktReadToken(pszInput, szToken);
1810 123642 : if (szToken[0] != '\0')
1811 : {
1812 : // Postgis EWKT: POINTM instead of POINT M.
1813 : // Current QGIS versions (at least <= 3.38) also export POINTZ.
1814 123642 : const size_t nTokenLen = strlen(szToken);
1815 123642 : if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
1816 : {
1817 11 : szToken[nTokenLen - 1] = '\0';
1818 11 : bHasM = true;
1819 11 : bAlreadyGotDimension = true;
1820 :
1821 11 : if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
1822 9 : szToken[nTokenLen - 2] == 'z'))
1823 : {
1824 4 : bHasZ = true;
1825 4 : szToken[nTokenLen - 2] = '\0';
1826 : }
1827 : }
1828 123631 : else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
1829 : {
1830 6 : szToken[nTokenLen - 1] = '\0';
1831 6 : bHasZ = true;
1832 6 : bAlreadyGotDimension = true;
1833 : }
1834 : }
1835 :
1836 123642 : if (!EQUAL(szToken, getGeometryName()))
1837 0 : return OGRERR_CORRUPT_DATA;
1838 :
1839 : /* -------------------------------------------------------------------- */
1840 : /* Check for Z, M or ZM */
1841 : /* -------------------------------------------------------------------- */
1842 123642 : if (!bAlreadyGotDimension)
1843 : {
1844 123625 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1845 123625 : if (EQUAL(szToken, "Z"))
1846 : {
1847 1418 : pszInput = pszNewInput;
1848 1418 : bHasZ = true;
1849 : }
1850 122207 : else if (EQUAL(szToken, "M"))
1851 : {
1852 353 : pszInput = pszNewInput;
1853 353 : bHasM = true;
1854 : }
1855 121854 : else if (EQUAL(szToken, "ZM"))
1856 : {
1857 494 : pszInput = pszNewInput;
1858 494 : bHasZ = true;
1859 494 : bHasM = true;
1860 : }
1861 : }
1862 123642 : *pbHasZ = bHasZ;
1863 123642 : *pbHasM = bHasM;
1864 :
1865 : /* -------------------------------------------------------------------- */
1866 : /* Check for EMPTY ... */
1867 : /* -------------------------------------------------------------------- */
1868 123642 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1869 123642 : if (EQUAL(szToken, "EMPTY"))
1870 : {
1871 1578 : *ppszInput = pszNewInput;
1872 1578 : *pbIsEmpty = true;
1873 1578 : if (bHasZ)
1874 137 : set3D(TRUE);
1875 1578 : if (bHasM)
1876 84 : setMeasured(TRUE);
1877 1578 : return OGRERR_NONE;
1878 : }
1879 :
1880 122064 : if (!EQUAL(szToken, "("))
1881 35 : return OGRERR_CORRUPT_DATA;
1882 :
1883 122029 : if (!bHasZ && !bHasM)
1884 : {
1885 : // Test for old-style XXXXXXXXX(EMPTY).
1886 119927 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1887 119927 : if (EQUAL(szToken, "EMPTY"))
1888 : {
1889 69 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1890 :
1891 69 : if (EQUAL(szToken, ","))
1892 : {
1893 : // This is OK according to SFSQL SPEC.
1894 : }
1895 44 : else if (!EQUAL(szToken, ")"))
1896 : {
1897 9 : return OGRERR_CORRUPT_DATA;
1898 : }
1899 : else
1900 : {
1901 35 : *ppszInput = pszNewInput;
1902 35 : empty();
1903 35 : *pbIsEmpty = true;
1904 35 : return OGRERR_NONE;
1905 : }
1906 : }
1907 : }
1908 :
1909 121985 : *ppszInput = pszInput;
1910 :
1911 121985 : return OGRERR_NONE;
1912 : }
1913 :
1914 : //! @endcond
1915 :
1916 : /************************************************************************/
1917 : /* wktTypeString() */
1918 : /************************************************************************/
1919 :
1920 : //! @cond Doxygen_Suppress
1921 : /** Get a type string for WKT, padded with a space at the end.
1922 : *
1923 : * @param variant OGR type variant
1924 : * @return "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
1925 : */
1926 14243 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1927 : {
1928 14243 : std::string s(" ");
1929 :
1930 14243 : if (variant == wkbVariantIso)
1931 : {
1932 9352 : if (flags & OGR_G_3D)
1933 1957 : s += "Z";
1934 9352 : if (flags & OGR_G_MEASURED)
1935 1200 : s += "M";
1936 : }
1937 14243 : if (s.size() > 1)
1938 2518 : s += " ";
1939 14243 : return s;
1940 : }
1941 :
1942 : //! @endcond
1943 :
1944 : /**
1945 : * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
1946 : * OGRwkbVariant variant = wkbVariantOldOgc ) const;
1947 : *
1948 : * \brief Convert a geometry into well known text format.
1949 : *
1950 : * This method relates to the SFCOM IWks::ExportToWKT() method.
1951 : *
1952 : * This method is the same as the C function OGR_G_ExportToWkt().
1953 : *
1954 : * @param ppszDstText a text buffer is allocated by the program, and assigned
1955 : * to the passed pointer. After use, *ppszDstText should be
1956 : * freed with CPLFree().
1957 : * @param variant the specification that must be conformed too :
1958 : * - wkbVariantOgc for old-style 99-402 extended
1959 : * dimension (Z) WKB types
1960 : * - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
1961 : *
1962 : * @return Currently OGRERR_NONE is always returned.
1963 : */
1964 8879 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1965 : {
1966 8879 : OGRWktOptions opts;
1967 8879 : opts.variant = variant;
1968 8879 : OGRErr err(OGRERR_NONE);
1969 :
1970 8879 : std::string wkt = exportToWkt(opts, &err);
1971 8879 : *ppszDstText = CPLStrdup(wkt.data());
1972 17758 : return err;
1973 : }
1974 :
1975 : /************************************************************************/
1976 : /* OGR_G_ExportToWkt() */
1977 : /************************************************************************/
1978 :
1979 : /**
1980 : * \brief Convert a geometry into well known text format.
1981 : *
1982 : * This function relates to the SFCOM IWks::ExportToWKT() method.
1983 : *
1984 : * For backward compatibility purposes, it exports the Old-style 99-402
1985 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1986 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1987 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
1988 : *
1989 : * This function is the same as the CPP method OGRGeometry::exportToWkt().
1990 : *
1991 : * @param hGeom handle on the geometry to convert to a text format from.
1992 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
1993 : * to the passed pointer. After use, *ppszDstText should be
1994 : * freed with CPLFree().
1995 : *
1996 : * @return Currently OGRERR_NONE is always returned.
1997 : */
1998 :
1999 2551 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
2000 :
2001 : {
2002 2551 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2003 :
2004 2551 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
2005 : }
2006 :
2007 : /************************************************************************/
2008 : /* OGR_G_ExportToIsoWkt() */
2009 : /************************************************************************/
2010 :
2011 : /**
2012 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
2013 : * known text format.
2014 : *
2015 : * This function relates to the SFCOM IWks::ExportToWKT() method.
2016 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
2017 : * (Z&M) WKB types.
2018 : *
2019 : * This function is the same as the CPP method
2020 : * OGRGeometry::exportToWkt(wkbVariantIso).
2021 : *
2022 : * @param hGeom handle on the geometry to convert to a text format from.
2023 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
2024 : * to the passed pointer. After use, *ppszDstText should be
2025 : * freed with CPLFree().
2026 : *
2027 : * @return Currently OGRERR_NONE is always returned.
2028 : *
2029 : */
2030 :
2031 5500 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
2032 :
2033 : {
2034 5500 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2035 :
2036 5500 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2037 5500 : wkbVariantIso);
2038 : }
2039 :
2040 : /**
2041 : * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
2042 : *
2043 : * \brief Fetch geometry type.
2044 : *
2045 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2046 : * flattened version of the geometry type apply the wkbFlatten() macro
2047 : * to the return result.
2048 : *
2049 : * This method is the same as the C function OGR_G_GetGeometryType().
2050 : *
2051 : * @return the geometry type code.
2052 : */
2053 :
2054 : /************************************************************************/
2055 : /* OGR_G_GetGeometryType() */
2056 : /************************************************************************/
2057 : /**
2058 : * \brief Fetch geometry type.
2059 : *
2060 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2061 : * flattened version of the geometry type apply the wkbFlatten() macro
2062 : * to the return result.
2063 : *
2064 : * This function is the same as the CPP method OGRGeometry::getGeometryType().
2065 : *
2066 : * @param hGeom handle on the geometry to get type from.
2067 : * @return the geometry type code.
2068 : */
2069 :
2070 5789 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2071 :
2072 : {
2073 5789 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2074 :
2075 5789 : return OGRGeometry::FromHandle(hGeom)->getGeometryType();
2076 : }
2077 :
2078 : /**
2079 : * \fn const char * OGRGeometry::getGeometryName() const;
2080 : *
2081 : * \brief Fetch WKT name for geometry type.
2082 : *
2083 : * There is no SFCOM analog to this method.
2084 : *
2085 : * This method is the same as the C function OGR_G_GetGeometryName().
2086 : *
2087 : * @return name used for this geometry type in well known text format. The
2088 : * returned pointer is to a static internal string and should not be modified
2089 : * or freed.
2090 : */
2091 :
2092 : /************************************************************************/
2093 : /* OGR_G_GetGeometryName() */
2094 : /************************************************************************/
2095 : /**
2096 : * \brief Fetch WKT name for geometry type.
2097 : *
2098 : * There is no SFCOM analog to this function.
2099 : *
2100 : * This function is the same as the CPP method OGRGeometry::getGeometryName().
2101 : *
2102 : * @param hGeom handle on the geometry to get name from.
2103 : * @return name used for this geometry type in well known text format.
2104 : */
2105 :
2106 18986 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2107 :
2108 : {
2109 18986 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2110 :
2111 18986 : return OGRGeometry::FromHandle(hGeom)->getGeometryName();
2112 : }
2113 :
2114 : /**
2115 : * \fn OGRGeometry *OGRGeometry::clone() const;
2116 : *
2117 : * \brief Make a copy of this object.
2118 : *
2119 : * This method relates to the SFCOM IGeometry::clone() method.
2120 : *
2121 : * This method is the same as the C function OGR_G_Clone().
2122 : *
2123 : * @return a new object instance with the same geometry, and spatial
2124 : * reference system as the original.
2125 : */
2126 :
2127 : /************************************************************************/
2128 : /* OGR_G_Clone() */
2129 : /************************************************************************/
2130 : /**
2131 : * \brief Make a copy of this object.
2132 : *
2133 : * This function relates to the SFCOM IGeometry::clone() method.
2134 : *
2135 : * This function is the same as the CPP method OGRGeometry::clone().
2136 : *
2137 : * @param hGeom handle on the geometry to clone from.
2138 : * @return a handle on the copy of the geometry with the spatial
2139 : * reference system as the original.
2140 : */
2141 :
2142 13574 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
2143 :
2144 : {
2145 13574 : VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
2146 :
2147 13574 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
2148 : }
2149 :
2150 : /**
2151 : * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
2152 : *
2153 : * \brief Returns spatial reference system for object.
2154 : *
2155 : * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
2156 : *
2157 : * This method is the same as the C function OGR_G_GetSpatialReference().
2158 : *
2159 : * @return a reference to the spatial reference object. The object may be
2160 : * shared with many geometry objects, and should not be modified.
2161 : */
2162 :
2163 : /************************************************************************/
2164 : /* OGR_G_GetSpatialReference() */
2165 : /************************************************************************/
2166 : /**
2167 : * \brief Returns spatial reference system for geometry.
2168 : *
2169 : * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
2170 : *
2171 : * This function is the same as the CPP method
2172 : * OGRGeometry::getSpatialReference().
2173 : *
2174 : * @param hGeom handle on the geometry to get spatial reference from.
2175 : * @return a reference to the spatial reference geometry, which should not be
2176 : * modified.
2177 : */
2178 :
2179 126 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
2180 :
2181 : {
2182 126 : VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
2183 :
2184 126 : return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
2185 252 : OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
2186 : }
2187 :
2188 : /**
2189 : * \fn void OGRGeometry::empty();
2190 : *
2191 : * \brief Clear geometry information.
2192 : * This restores the geometry to its initial
2193 : * state after construction, and before assignment of actual geometry.
2194 : *
2195 : * This method relates to the SFCOM IGeometry::Empty() method.
2196 : *
2197 : * This method is the same as the C function OGR_G_Empty().
2198 : */
2199 :
2200 : /************************************************************************/
2201 : /* OGR_G_Empty() */
2202 : /************************************************************************/
2203 : /**
2204 : * \brief Clear geometry information.
2205 : * This restores the geometry to its initial
2206 : * state after construction, and before assignment of actual geometry.
2207 : *
2208 : * This function relates to the SFCOM IGeometry::Empty() method.
2209 : *
2210 : * This function is the same as the CPP method OGRGeometry::empty().
2211 : *
2212 : * @param hGeom handle on the geometry to empty.
2213 : */
2214 :
2215 4 : void OGR_G_Empty(OGRGeometryH hGeom)
2216 :
2217 : {
2218 4 : VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
2219 :
2220 4 : OGRGeometry::FromHandle(hGeom)->empty();
2221 : }
2222 :
2223 : /**
2224 : * \fn bool OGRGeometry::IsEmpty() const;
2225 : *
2226 : * \brief Returns TRUE (non-zero) if the object has no points.
2227 : *
2228 : * Normally this
2229 : * returns FALSE except between when an object is instantiated and points
2230 : * have been assigned.
2231 : *
2232 : * This method relates to the SFCOM IGeometry::IsEmpty() method.
2233 : *
2234 : * @return TRUE if object is empty, otherwise FALSE.
2235 : */
2236 :
2237 : /************************************************************************/
2238 : /* OGR_G_IsEmpty() */
2239 : /************************************************************************/
2240 :
2241 : /**
2242 : * \brief Test if the geometry is empty.
2243 : *
2244 : * This method is the same as the CPP method OGRGeometry::IsEmpty().
2245 : *
2246 : * @param hGeom The Geometry to test.
2247 : *
2248 : * @return TRUE if the geometry has no points, otherwise FALSE.
2249 : */
2250 :
2251 2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
2252 :
2253 : {
2254 2391 : VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
2255 :
2256 2391 : return OGRGeometry::FromHandle(hGeom)->IsEmpty();
2257 : }
2258 :
2259 : /************************************************************************/
2260 : /* IsValid() */
2261 : /************************************************************************/
2262 :
2263 : /**
2264 : * \brief Test if the geometry is valid.
2265 : *
2266 : * This method is the same as the C functions OGR_G_IsValid() and
2267 : * OGR_G_GetInvalidityReason().
2268 : *
2269 : * This method is built on the GEOS library, check it for the definition
2270 : * of the geometry operation.
2271 : * If OGR is built without the GEOS library, this method will always return
2272 : * FALSE.
2273 : *
2274 : * @param[out] posReason (since 3.13) Pointer to a string to receive the reason
2275 : * for invalidity, or nullptr. When nullptr, invalidity
2276 : * reasons are emitted as CPL warnings.
2277 : * @return TRUE if the geometry has no points, otherwise FALSE.
2278 : */
2279 :
2280 2606 : bool OGRGeometry::IsValid(std::string *posReason) const
2281 :
2282 : {
2283 2606 : if (posReason)
2284 833 : posReason->clear();
2285 :
2286 2606 : if (IsSFCGALCompatible())
2287 : {
2288 : #ifndef HAVE_SFCGAL
2289 :
2290 : #ifdef HAVE_GEOS
2291 3 : if (wkbFlatten(getGeometryType()) == wkbTriangle)
2292 : {
2293 : // go on
2294 : }
2295 : else
2296 : #endif
2297 : {
2298 1 : CPLError(CE_Failure, CPLE_NotSupported,
2299 : "SFCGAL support not enabled.");
2300 1 : return FALSE;
2301 : }
2302 : #else
2303 : sfcgal_init();
2304 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
2305 : if (poThis == nullptr)
2306 : {
2307 : CPLError(CE_Failure, CPLE_IllegalArg,
2308 : "SFCGAL geometry returned is NULL");
2309 : return FALSE;
2310 : }
2311 :
2312 : const int res = sfcgal_geometry_is_valid(poThis);
2313 : if (res != 1 && posReason)
2314 : {
2315 : char *pszReason = nullptr;
2316 : sfcgal_geometry_is_valid_detail(poThis, &pszReason, nullptr);
2317 : if (pszReason)
2318 : {
2319 : *posReason = pszReason;
2320 : free(pszReason);
2321 : }
2322 : else
2323 : *posReason = "unknown reason";
2324 : }
2325 : sfcgal_geometry_delete(poThis);
2326 : return res == 1;
2327 : #endif
2328 : }
2329 :
2330 : {
2331 : #ifndef HAVE_GEOS
2332 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2333 : return FALSE;
2334 :
2335 : #else
2336 2605 : bool bResult = false;
2337 :
2338 : // Some invalid geometries, such as lines with one point, or
2339 : // rings that do not close, cannot be converted to GEOS.
2340 : // For validity checking we initialize the GEOS context with
2341 : // the warning handler as the error handler to avoid emitting
2342 : // CE_Failure when a geometry cannot be converted to GEOS.
2343 : GEOSContextHandle_t hGEOSCtxt =
2344 2605 : initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
2345 :
2346 : GEOSGeom hThisGeosGeom;
2347 2605 : if (posReason)
2348 : {
2349 1666 : CPLErrorAccumulator oAccumulator;
2350 : {
2351 833 : auto oContext = oAccumulator.InstallForCurrentScope();
2352 833 : CPL_IGNORE_RET_VAL(oContext);
2353 833 : hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2354 : }
2355 833 : if (!hThisGeosGeom && oAccumulator.GetErrors().size() == 1)
2356 : {
2357 2 : std::string msg = oAccumulator.GetErrors()[0].msg;
2358 :
2359 : // Trim GEOS exception name
2360 1 : const auto subMsgPos = msg.find(": ");
2361 1 : if (subMsgPos != std::string::npos)
2362 : {
2363 1 : msg = msg.substr(subMsgPos + strlen(": "));
2364 : }
2365 :
2366 : // Trim newline from end of GEOS exception message
2367 1 : if (!msg.empty() && msg.back() == '\n')
2368 : {
2369 1 : msg.pop_back();
2370 : }
2371 :
2372 1 : *posReason = std::move(msg);
2373 : }
2374 : }
2375 : else
2376 : {
2377 1772 : hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2378 : }
2379 :
2380 2605 : if (hThisGeosGeom != nullptr)
2381 : {
2382 2604 : if (posReason)
2383 : {
2384 1664 : CPLErrorAccumulator oAccumulator;
2385 : {
2386 832 : auto oContext = oAccumulator.InstallForCurrentScope();
2387 832 : CPL_IGNORE_RET_VAL(oContext);
2388 832 : bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
2389 : }
2390 832 : if (!bResult && oAccumulator.GetErrors().size() == 1)
2391 : {
2392 27 : *posReason = oAccumulator.GetErrors()[0].msg;
2393 : }
2394 : }
2395 : else
2396 : {
2397 1772 : bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
2398 : }
2399 : #ifdef DEBUG_VERBOSE
2400 : if (!bResult && !posReason)
2401 : {
2402 : char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
2403 : CPLDebug("OGR", "%s", pszReason);
2404 : GEOSFree_r(hGEOSCtxt, pszReason);
2405 : }
2406 : #endif
2407 2604 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2408 : }
2409 2605 : freeGEOSContext(hGEOSCtxt);
2410 :
2411 2605 : return bResult;
2412 :
2413 : #endif // HAVE_GEOS
2414 : }
2415 : }
2416 :
2417 : /************************************************************************/
2418 : /* OGR_G_IsValid() */
2419 : /************************************************************************/
2420 :
2421 : /**
2422 : * \brief Test if the geometry is valid.
2423 : *
2424 : * This function is the same as the C++ method OGRGeometry::IsValid().
2425 : *
2426 : * This function is built on the GEOS library, check it for the definition
2427 : * of the geometry operation.
2428 : * If OGR is built without the GEOS library, this function will always return
2429 : * FALSE.
2430 : *
2431 : * If the geometry is invalid, the reason for its invalidity is emitted as a
2432 : * CPL warning. To get it in a string instead, use OGR_G_GetInvalidityReason()
2433 : *
2434 : * @param hGeom The Geometry to test.
2435 : *
2436 : * @return TRUE if the geometry is valid, otherwise FALSE.
2437 : */
2438 :
2439 22 : int OGR_G_IsValid(OGRGeometryH hGeom)
2440 :
2441 : {
2442 22 : VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
2443 :
2444 22 : return OGRGeometry::FromHandle(hGeom)->IsValid();
2445 : }
2446 :
2447 : /************************************************************************/
2448 : /* OGR_G_GetInvalidityReason() */
2449 : /************************************************************************/
2450 :
2451 : /**
2452 : * \brief Test if the geometry is valid and, if not, return the invalidity reason.
2453 : *
2454 : * This function is the same as the C++ method OGRGeometry::IsValid().
2455 : *
2456 : * This function is built on the GEOS library, check it for the definition
2457 : * of the geometry operation.
2458 : * If OGR is built without the GEOS library, this function will always return
2459 : * FALSE.
2460 : *
2461 : * @param hGeom The Geometry to test.
2462 : * @return a string with the invalidity reason, to free with CPLFree(),
2463 : * if the geometry is invalid, or nullptr if the geometry is valid.
2464 : *
2465 : * @since 3.13
2466 : */
2467 :
2468 3 : char *OGR_G_GetInvalidityReason(OGRGeometryH hGeom)
2469 :
2470 : {
2471 3 : VALIDATE_POINTER1(hGeom, "OGR_G_GetInvalidityReason", nullptr);
2472 :
2473 6 : std::string osReason;
2474 3 : const int nRet = OGRGeometry::FromHandle(hGeom)->IsValid(&osReason);
2475 3 : if (osReason.empty())
2476 : {
2477 1 : if (!nRet)
2478 : {
2479 : // not sure if that can happen
2480 0 : return CPLStrdup("unknown reason");
2481 : }
2482 : else
2483 1 : return nullptr;
2484 : }
2485 : else
2486 : {
2487 2 : return CPLStrdup(osReason.c_str());
2488 : }
2489 : }
2490 :
2491 : /************************************************************************/
2492 : /* IsSimple() */
2493 : /************************************************************************/
2494 :
2495 : /**
2496 : * \brief Test if the geometry is simple.
2497 : *
2498 : * This method is the same as the C function OGR_G_IsSimple().
2499 : *
2500 : * This method is built on the GEOS library, check it for the definition
2501 : * of the geometry operation.
2502 : * If OGR is built without the GEOS library, this method will always return
2503 : * FALSE.
2504 : *
2505 : *
2506 : * @return TRUE if the geometry has no points, otherwise FALSE.
2507 : */
2508 :
2509 5 : bool OGRGeometry::IsSimple() const
2510 :
2511 : {
2512 : #ifndef HAVE_GEOS
2513 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2514 : return FALSE;
2515 :
2516 : #else
2517 :
2518 5 : bool bResult = false;
2519 :
2520 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2521 5 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2522 :
2523 5 : if (hThisGeosGeom != nullptr)
2524 : {
2525 5 : bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
2526 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2527 : }
2528 5 : freeGEOSContext(hGEOSCtxt);
2529 :
2530 5 : return bResult;
2531 :
2532 : #endif // HAVE_GEOS
2533 : }
2534 :
2535 : /**
2536 : * \brief Returns TRUE if the geometry is simple.
2537 : *
2538 : * Returns TRUE if the geometry has no anomalous geometric points, such
2539 : * as self intersection or self tangency. The description of each
2540 : * instantiable geometric class will include the specific conditions that
2541 : * cause an instance of that class to be classified as not simple.
2542 : *
2543 : * This function is the same as the C++ method OGRGeometry::IsSimple() method.
2544 : *
2545 : * If OGR is built without the GEOS library, this function will always return
2546 : * FALSE.
2547 : *
2548 : * @param hGeom The Geometry to test.
2549 : *
2550 : * @return TRUE if object is simple, otherwise FALSE.
2551 : */
2552 :
2553 5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
2554 :
2555 : {
2556 5 : VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
2557 :
2558 5 : return OGRGeometry::FromHandle(hGeom)->IsSimple();
2559 : }
2560 :
2561 : /************************************************************************/
2562 : /* IsRing() */
2563 : /************************************************************************/
2564 :
2565 : /**
2566 : * \brief Test if the geometry is a ring
2567 : *
2568 : * This method is the same as the C function OGR_G_IsRing().
2569 : *
2570 : * This method is built on the GEOS library, check it for the definition
2571 : * of the geometry operation.
2572 : * If OGR is built without the GEOS library, this method will always return
2573 : * FALSE.
2574 : *
2575 : *
2576 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2577 : * length and closure (self-intersection is not checked), otherwise FALSE.
2578 : */
2579 :
2580 1 : bool OGRGeometry::IsRing() const
2581 :
2582 : {
2583 : #ifndef HAVE_GEOS
2584 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2585 : return FALSE;
2586 :
2587 : #else
2588 :
2589 1 : bool bResult = false;
2590 :
2591 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2592 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2593 :
2594 1 : if (hThisGeosGeom != nullptr)
2595 : {
2596 1 : bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
2597 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2598 : }
2599 1 : freeGEOSContext(hGEOSCtxt);
2600 :
2601 1 : return bResult;
2602 :
2603 : #endif // HAVE_GEOS
2604 : }
2605 :
2606 : /************************************************************************/
2607 : /* OGR_G_IsRing() */
2608 : /************************************************************************/
2609 :
2610 : /**
2611 : * \brief Test if the geometry is a ring
2612 : *
2613 : * This function is the same as the C++ method OGRGeometry::IsRing().
2614 : *
2615 : * This function is built on the GEOS library, check it for the definition
2616 : * of the geometry operation.
2617 : * If OGR is built without the GEOS library, this function will always return
2618 : * FALSE.
2619 : *
2620 : * @param hGeom The Geometry to test.
2621 : *
2622 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2623 : * length and closure (self-intersection is not checked), otherwise FALSE.
2624 : */
2625 :
2626 1 : int OGR_G_IsRing(OGRGeometryH hGeom)
2627 :
2628 : {
2629 1 : VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
2630 :
2631 1 : return OGRGeometry::FromHandle(hGeom)->IsRing();
2632 : }
2633 :
2634 : /************************************************************************/
2635 : /* OGRFromOGCGeomType() */
2636 : /************************************************************************/
2637 :
2638 : /** Map OGC geometry format type to corresponding OGR constants.
2639 : * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
2640 : * @return OGR constant.
2641 : */
2642 3219 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2643 : {
2644 3219 : OGRwkbGeometryType eType = wkbUnknown;
2645 3219 : bool bConvertTo3D = false;
2646 3219 : bool bIsMeasured = false;
2647 3219 : if (*pszGeomType != '\0')
2648 : {
2649 3213 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2650 3213 : if (ch == 'm' || ch == 'M')
2651 : {
2652 2 : bIsMeasured = true;
2653 2 : if (strlen(pszGeomType) > 1)
2654 2 : ch = pszGeomType[strlen(pszGeomType) - 2];
2655 : }
2656 3213 : if (ch == 'z' || ch == 'Z')
2657 : {
2658 34 : bConvertTo3D = true;
2659 : }
2660 : }
2661 :
2662 3219 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2663 971 : eType = wkbPoint;
2664 2248 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2665 184 : eType = wkbLineString;
2666 2064 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2667 895 : eType = wkbPolygon;
2668 1169 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2669 24 : eType = wkbMultiPoint;
2670 1145 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2671 13 : eType = wkbMultiLineString;
2672 1132 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2673 82 : eType = wkbMultiPolygon;
2674 1050 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2675 4 : eType = wkbGeometryCollection;
2676 1046 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2677 356 : eType = wkbCircularString;
2678 690 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2679 0 : eType = wkbCompoundCurve;
2680 690 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2681 16 : eType = wkbCurvePolygon;
2682 674 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2683 2 : eType = wkbMultiCurve;
2684 672 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2685 0 : eType = wkbMultiSurface;
2686 672 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2687 0 : eType = wkbTriangle;
2688 672 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2689 1 : eType = wkbPolyhedralSurface;
2690 671 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2691 5 : eType = wkbTIN;
2692 666 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2693 3 : eType = wkbCurve;
2694 663 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2695 3 : eType = wkbSurface;
2696 : else
2697 660 : eType = wkbUnknown;
2698 :
2699 3219 : if (bConvertTo3D)
2700 34 : eType = wkbSetZ(eType);
2701 3219 : if (bIsMeasured)
2702 2 : eType = wkbSetM(eType);
2703 :
2704 3219 : return eType;
2705 : }
2706 :
2707 : /************************************************************************/
2708 : /* OGRToOGCGeomType() */
2709 : /************************************************************************/
2710 :
2711 : /** Map OGR geometry format constants to corresponding OGC geometry type.
2712 : * @param eGeomType OGR geometry type
2713 : * @param bCamelCase Whether the return should be like "MultiPoint"
2714 : * (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
2715 : * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
2716 : * Default is false.
2717 : * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
2718 : * suffix. Default is false.
2719 : * @return string with OGC geometry type (without dimensionality)
2720 : */
2721 3119 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2722 : bool bAddZM, bool bSpaceBeforeZM)
2723 : {
2724 3119 : const char *pszRet = "";
2725 3119 : switch (wkbFlatten(eGeomType))
2726 : {
2727 1699 : case wkbUnknown:
2728 1699 : pszRet = "Geometry";
2729 1699 : break;
2730 560 : case wkbPoint:
2731 560 : pszRet = "Point";
2732 560 : break;
2733 153 : case wkbLineString:
2734 153 : pszRet = "LineString";
2735 153 : break;
2736 372 : case wkbPolygon:
2737 372 : pszRet = "Polygon";
2738 372 : break;
2739 48 : case wkbMultiPoint:
2740 48 : pszRet = "MultiPoint";
2741 48 : break;
2742 56 : case wkbMultiLineString:
2743 56 : pszRet = "MultiLineString";
2744 56 : break;
2745 74 : case wkbMultiPolygon:
2746 74 : pszRet = "MultiPolygon";
2747 74 : break;
2748 54 : case wkbGeometryCollection:
2749 54 : pszRet = "GeometryCollection";
2750 54 : break;
2751 9 : case wkbCircularString:
2752 9 : pszRet = "CircularString";
2753 9 : break;
2754 3 : case wkbCompoundCurve:
2755 3 : pszRet = "CompoundCurve";
2756 3 : break;
2757 12 : case wkbCurvePolygon:
2758 12 : pszRet = "CurvePolygon";
2759 12 : break;
2760 2 : case wkbMultiCurve:
2761 2 : pszRet = "MultiCurve";
2762 2 : break;
2763 3 : case wkbMultiSurface:
2764 3 : pszRet = "MultiSurface";
2765 3 : break;
2766 3 : case wkbTriangle:
2767 3 : pszRet = "Triangle";
2768 3 : break;
2769 5 : case wkbPolyhedralSurface:
2770 5 : pszRet = "PolyhedralSurface";
2771 5 : break;
2772 1 : case wkbTIN:
2773 1 : pszRet = "Tin";
2774 1 : break;
2775 3 : case wkbCurve:
2776 3 : pszRet = "Curve";
2777 3 : break;
2778 3 : case wkbSurface:
2779 3 : pszRet = "Surface";
2780 3 : break;
2781 59 : default:
2782 59 : break;
2783 : }
2784 3119 : if (bAddZM)
2785 : {
2786 67 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2787 67 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2788 67 : if (bHasZ || bHasM)
2789 : {
2790 10 : if (bSpaceBeforeZM)
2791 1 : pszRet = CPLSPrintf("%s ", pszRet);
2792 10 : if (bHasZ)
2793 9 : pszRet = CPLSPrintf("%sZ", pszRet);
2794 10 : if (bHasM)
2795 5 : pszRet = CPLSPrintf("%sM", pszRet);
2796 : }
2797 : }
2798 3119 : if (!bCamelCase)
2799 3051 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2800 3119 : return pszRet;
2801 : }
2802 :
2803 : /************************************************************************/
2804 : /* OGRGeometryTypeToName() */
2805 : /************************************************************************/
2806 :
2807 : /**
2808 : * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
2809 : * value. The returned value should not be modified, or freed by the
2810 : * application.
2811 : *
2812 : * This function is C callable.
2813 : *
2814 : * @param eType the geometry type.
2815 : *
2816 : * @return internal human readable string, or NULL on failure.
2817 : */
2818 :
2819 393 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2820 :
2821 : {
2822 393 : bool b3D = wkbHasZ(eType);
2823 393 : bool bMeasured = wkbHasM(eType);
2824 :
2825 393 : switch (wkbFlatten(eType))
2826 : {
2827 34 : case wkbUnknown:
2828 34 : if (b3D && bMeasured)
2829 0 : return "3D Measured Unknown (any)";
2830 34 : else if (b3D)
2831 1 : return "3D Unknown (any)";
2832 33 : else if (bMeasured)
2833 0 : return "Measured Unknown (any)";
2834 : else
2835 33 : return "Unknown (any)";
2836 :
2837 59 : case wkbPoint:
2838 59 : if (b3D && bMeasured)
2839 3 : return "3D Measured Point";
2840 56 : else if (b3D)
2841 12 : return "3D Point";
2842 44 : else if (bMeasured)
2843 5 : return "Measured Point";
2844 : else
2845 39 : return "Point";
2846 :
2847 37 : case wkbLineString:
2848 37 : if (b3D && bMeasured)
2849 0 : return "3D Measured Line String";
2850 37 : else if (b3D)
2851 9 : return "3D Line String";
2852 28 : else if (bMeasured)
2853 0 : return "Measured Line String";
2854 : else
2855 28 : return "Line String";
2856 :
2857 62 : case wkbPolygon:
2858 62 : if (b3D && bMeasured)
2859 0 : return "3D Measured Polygon";
2860 62 : else if (b3D)
2861 8 : return "3D Polygon";
2862 54 : else if (bMeasured)
2863 0 : return "Measured Polygon";
2864 : else
2865 54 : return "Polygon";
2866 :
2867 19 : case wkbMultiPoint:
2868 19 : if (b3D && bMeasured)
2869 0 : return "3D Measured Multi Point";
2870 19 : else if (b3D)
2871 9 : return "3D Multi Point";
2872 10 : else if (bMeasured)
2873 0 : return "Measured Multi Point";
2874 : else
2875 10 : return "Multi Point";
2876 :
2877 14 : case wkbMultiLineString:
2878 14 : if (b3D && bMeasured)
2879 0 : return "3D Measured Multi Line String";
2880 14 : else if (b3D)
2881 6 : return "3D Multi Line String";
2882 8 : else if (bMeasured)
2883 0 : return "Measured Multi Line String";
2884 : else
2885 8 : return "Multi Line String";
2886 :
2887 19 : case wkbMultiPolygon:
2888 19 : if (b3D && bMeasured)
2889 0 : return "3D Measured Multi Polygon";
2890 19 : else if (b3D)
2891 8 : return "3D Multi Polygon";
2892 11 : else if (bMeasured)
2893 0 : return "Measured Multi Polygon";
2894 : else
2895 11 : return "Multi Polygon";
2896 :
2897 25 : case wkbGeometryCollection:
2898 25 : if (b3D && bMeasured)
2899 0 : return "3D Measured Geometry Collection";
2900 25 : else if (b3D)
2901 10 : return "3D Geometry Collection";
2902 15 : else if (bMeasured)
2903 0 : return "Measured Geometry Collection";
2904 : else
2905 15 : return "Geometry Collection";
2906 :
2907 0 : case wkbCircularString:
2908 0 : if (b3D && bMeasured)
2909 0 : return "3D Measured Circular String";
2910 0 : else if (b3D)
2911 0 : return "3D Circular String";
2912 0 : else if (bMeasured)
2913 0 : return "Measured Circular String";
2914 : else
2915 0 : return "Circular String";
2916 :
2917 1 : case wkbCompoundCurve:
2918 1 : if (b3D && bMeasured)
2919 0 : return "3D Measured Compound Curve";
2920 1 : else if (b3D)
2921 0 : return "3D Compound Curve";
2922 1 : else if (bMeasured)
2923 0 : return "Measured Compound Curve";
2924 : else
2925 1 : return "Compound Curve";
2926 :
2927 0 : case wkbCurvePolygon:
2928 0 : if (b3D && bMeasured)
2929 0 : return "3D Measured Curve Polygon";
2930 0 : else if (b3D)
2931 0 : return "3D Curve Polygon";
2932 0 : else if (bMeasured)
2933 0 : return "Measured Curve Polygon";
2934 : else
2935 0 : return "Curve Polygon";
2936 :
2937 0 : case wkbMultiCurve:
2938 0 : if (b3D && bMeasured)
2939 0 : return "3D Measured Multi Curve";
2940 0 : else if (b3D)
2941 0 : return "3D Multi Curve";
2942 0 : else if (bMeasured)
2943 0 : return "Measured Multi Curve";
2944 : else
2945 0 : return "Multi Curve";
2946 :
2947 0 : case wkbMultiSurface:
2948 0 : if (b3D && bMeasured)
2949 0 : return "3D Measured Multi Surface";
2950 0 : else if (b3D)
2951 0 : return "3D Multi Surface";
2952 0 : else if (bMeasured)
2953 0 : return "Measured Multi Surface";
2954 : else
2955 0 : return "Multi Surface";
2956 :
2957 4 : case wkbCurve:
2958 4 : if (b3D && bMeasured)
2959 1 : return "3D Measured Curve";
2960 3 : else if (b3D)
2961 1 : return "3D Curve";
2962 2 : else if (bMeasured)
2963 1 : return "Measured Curve";
2964 : else
2965 1 : return "Curve";
2966 :
2967 4 : case wkbSurface:
2968 4 : if (b3D && bMeasured)
2969 1 : return "3D Measured Surface";
2970 3 : else if (b3D)
2971 1 : return "3D Surface";
2972 2 : else if (bMeasured)
2973 1 : return "Measured Surface";
2974 : else
2975 1 : return "Surface";
2976 :
2977 0 : case wkbTriangle:
2978 0 : if (b3D && bMeasured)
2979 0 : return "3D Measured Triangle";
2980 0 : else if (b3D)
2981 0 : return "3D Triangle";
2982 0 : else if (bMeasured)
2983 0 : return "Measured Triangle";
2984 : else
2985 0 : return "Triangle";
2986 :
2987 0 : case wkbPolyhedralSurface:
2988 0 : if (b3D && bMeasured)
2989 0 : return "3D Measured PolyhedralSurface";
2990 0 : else if (b3D)
2991 0 : return "3D PolyhedralSurface";
2992 0 : else if (bMeasured)
2993 0 : return "Measured PolyhedralSurface";
2994 : else
2995 0 : return "PolyhedralSurface";
2996 :
2997 2 : case wkbTIN:
2998 2 : if (b3D && bMeasured)
2999 0 : return "3D Measured TIN";
3000 2 : else if (b3D)
3001 0 : return "3D TIN";
3002 2 : else if (bMeasured)
3003 0 : return "Measured TIN";
3004 : else
3005 2 : return "TIN";
3006 :
3007 112 : case wkbNone:
3008 112 : return "None";
3009 :
3010 1 : default:
3011 : {
3012 1 : return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
3013 : }
3014 : }
3015 : }
3016 :
3017 : /************************************************************************/
3018 : /* OGRMergeGeometryTypes() */
3019 : /************************************************************************/
3020 :
3021 : /**
3022 : * \brief Find common geometry type.
3023 : *
3024 : * Given two geometry types, find the most specific common
3025 : * type. Normally used repeatedly with the geometries in a
3026 : * layer to try and establish the most specific geometry type
3027 : * that can be reported for the layer.
3028 : *
3029 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
3030 : * geometry types with nothing in common but the base geometry
3031 : * type. wkbNone should be used to indicate that no geometries
3032 : * have been encountered yet, and means the first geometry
3033 : * encountered will establish the preliminary type.
3034 : *
3035 : * @param eMain the first input geometry type.
3036 : * @param eExtra the second input geometry type.
3037 : *
3038 : * @return the merged geometry type.
3039 : */
3040 :
3041 0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
3042 : OGRwkbGeometryType eExtra)
3043 :
3044 : {
3045 0 : return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
3046 : }
3047 :
3048 : /**
3049 : * \brief Find common geometry type.
3050 : *
3051 : * Given two geometry types, find the most specific common
3052 : * type. Normally used repeatedly with the geometries in a
3053 : * layer to try and establish the most specific geometry type
3054 : * that can be reported for the layer.
3055 : *
3056 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
3057 : * geometry types with nothing in common but the base geometry
3058 : * type. wkbNone should be used to indicate that no geometries
3059 : * have been encountered yet, and means the first geometry
3060 : * encountered will establish the preliminary type.
3061 : *
3062 : * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
3063 : * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
3064 : * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
3065 : * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
3066 : *
3067 : * @param eMain the first input geometry type.
3068 : * @param eExtra the second input geometry type.
3069 : * @param bAllowPromotingToCurves determine if promotion to curve type
3070 : * must be done.
3071 : *
3072 : * @return the merged geometry type.
3073 : *
3074 : */
3075 :
3076 575 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
3077 : OGRwkbGeometryType eExtra,
3078 : int bAllowPromotingToCurves)
3079 :
3080 : {
3081 575 : OGRwkbGeometryType eFMain = wkbFlatten(eMain);
3082 575 : OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
3083 :
3084 575 : const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
3085 575 : const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
3086 :
3087 575 : if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
3088 17 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3089 :
3090 558 : if (eFMain == wkbNone)
3091 2 : return eExtra;
3092 :
3093 556 : if (eFExtra == wkbNone)
3094 0 : return eMain;
3095 :
3096 556 : if (eFMain == eFExtra)
3097 : {
3098 539 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3099 : }
3100 :
3101 17 : if (bAllowPromotingToCurves)
3102 : {
3103 17 : if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
3104 4 : return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
3105 :
3106 13 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3107 3 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3108 :
3109 10 : if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3110 6 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3111 : }
3112 :
3113 : // One is subclass of the other one
3114 4 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3115 : {
3116 0 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3117 : }
3118 4 : else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3119 : {
3120 0 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3121 : }
3122 :
3123 : // Nothing apparently in common.
3124 4 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3125 : }
3126 :
3127 : /**
3128 : * \fn void OGRGeometry::flattenTo2D();
3129 : *
3130 : * \brief Convert geometry to strictly 2D.
3131 : * In a sense this converts all Z coordinates
3132 : * to 0.0.
3133 : *
3134 : * This method is the same as the C function OGR_G_FlattenTo2D().
3135 : */
3136 :
3137 : /************************************************************************/
3138 : /* OGR_G_FlattenTo2D() */
3139 : /************************************************************************/
3140 : /**
3141 : * \brief Convert geometry to strictly 2D.
3142 : * In a sense this converts all Z coordinates
3143 : * to 0.0.
3144 : *
3145 : * This function is the same as the CPP method OGRGeometry::flattenTo2D().
3146 : *
3147 : * @param hGeom handle on the geometry to convert.
3148 : */
3149 :
3150 31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
3151 :
3152 : {
3153 31 : OGRGeometry::FromHandle(hGeom)->flattenTo2D();
3154 31 : }
3155 :
3156 : /************************************************************************/
3157 : /* exportToGML() */
3158 : /************************************************************************/
3159 :
3160 : /**
3161 : * \fn char *OGRGeometry::exportToGML( const char* const *
3162 : * papszOptions = NULL ) const;
3163 : *
3164 : * \brief Convert a geometry into GML format.
3165 : *
3166 : * The GML geometry is expressed directly in terms of GML basic data
3167 : * types assuming the this is available in the gml namespace. The returned
3168 : * string should be freed with CPLFree() when no longer required.
3169 : *
3170 : * The supported options are :
3171 : * <ul>
3172 : * <li> FORMAT=GML2/GML3/GML32.
3173 : * If not set, it will default to GML 2.1.2 output.
3174 : * </li>
3175 : * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
3176 : * To use gml:Curve element for linestrings.
3177 : * Otherwise gml:LineString will be used .
3178 : * </li>
3179 : * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
3180 : * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES.
3181 : * If YES, SRS with EPSG authority will be written with the
3182 : * "urn:ogc:def:crs:EPSG::" prefix.
3183 : * In the case the SRS should be treated as lat/long or
3184 : * northing/easting, then the function will take care of coordinate order
3185 : * swapping if the data axis to CRS axis mapping indicates it.
3186 : * If set to NO, SRS with EPSG authority will be written with the "EPSG:"
3187 : * prefix, even if they are in lat/long order.
3188 : * </li>
3189 : * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
3190 : * Defaults to OGC_URN. If SHORT, then srsName will be in
3191 : * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
3192 : * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
3193 : * then srsName will be in the form
3194 : * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
3195 : * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
3196 : * or northing/easting, then the function will take care of coordinate
3197 : * order swapping if the data axis to CRS axis mapping indicates it.
3198 : * </li>
3199 : * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
3200 : * top-level geometry element with the provided value.
3201 : * Required for GML 3.2 compatibility.
3202 : * </li>
3203 : * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
3204 : * FORMAT=GML3/GML32) Default to POSLIST.
3205 : * For 2.5D geometries, define the location where to attach the
3206 : * srsDimension attribute.
3207 : * There are diverging implementations. Some put in on the
3208 : * <gml:posList> element, other on the top geometry element.
3209 : * </li>
3210 : * <li> NAMESPACE_DECL=YES/NO. If set to YES,
3211 : * xmlns:gml="http://www.opengis.net/gml" will be added to the root node
3212 : * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
3213 : * </li>
3214 : * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
3215 : * Resolution for the coordinate precision of the X and Y coordinates.
3216 : * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
3217 : * to 5 decimal digits. 0 for the default behavior.
3218 : * </li>
3219 : * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
3220 : * Resolution for the coordinate precision of the Z coordinates.
3221 : * Expressed in the units of the Z axis of the SRS.
3222 : * 0 for the default behavior.
3223 : * </li>
3224 : * </ul>
3225 : *
3226 : * This method is the same as the C function OGR_G_ExportToGMLEx().
3227 : *
3228 : * @param papszOptions NULL-terminated list of options.
3229 : * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
3230 : */
3231 :
3232 251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
3233 : {
3234 251 : return OGR_G_ExportToGMLEx(
3235 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3236 251 : const_cast<char **>(papszOptions));
3237 : }
3238 :
3239 : /************************************************************************/
3240 : /* exportToKML() */
3241 : /************************************************************************/
3242 :
3243 : /**
3244 : * \fn char *OGRGeometry::exportToKML() const;
3245 : *
3246 : * \brief Convert a geometry into KML format.
3247 : *
3248 : * The returned string should be freed with CPLFree() when no longer required.
3249 : *
3250 : * This method is the same as the C function OGR_G_ExportToKML().
3251 : *
3252 : * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
3253 : */
3254 :
3255 0 : char *OGRGeometry::exportToKML() const
3256 : {
3257 0 : return OGR_G_ExportToKML(
3258 0 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
3259 : }
3260 :
3261 : /************************************************************************/
3262 : /* exportToJson() */
3263 : /************************************************************************/
3264 :
3265 : /**
3266 : * \fn char *OGRGeometry::exportToJson() const;
3267 : *
3268 : * \brief Convert a geometry into GeoJSON format.
3269 : *
3270 : * The returned string should be freed with CPLFree() when no longer required.
3271 : *
3272 : * The following options are supported :
3273 : * <ul>
3274 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
3275 : * (added in GDAL 3.9)</li>
3276 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
3277 : * (added in GDAL 3.9)</li>
3278 : * </ul>
3279 : *
3280 : * This method is the same as the C function OGR_G_ExportToJson().
3281 : *
3282 : * @param papszOptions Null terminated list of options, or null (added in 3.9)
3283 : * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
3284 : */
3285 :
3286 43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
3287 : {
3288 43 : OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3289 43 : return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3290 43 : const_cast<char **>(papszOptions));
3291 : }
3292 :
3293 : /************************************************************************/
3294 : /* OGRSetGenerate_DB2_V72_BYTE_ORDER() */
3295 : /************************************************************************/
3296 :
3297 : /**
3298 : * \brief Special entry point to enable the hack for generating DB2 V7.2 style
3299 : * WKB.
3300 : *
3301 : * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
3302 : * order in WKB. This entry point is used to turn on or off the generation of
3303 : * such WKB.
3304 : */
3305 4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
3306 :
3307 : {
3308 : #if defined(HACK_FOR_IBM_DB2_V72)
3309 4 : OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
3310 4 : return OGRERR_NONE;
3311 : #else
3312 : if (bGenerate_DB2_V72_BYTE_ORDER)
3313 : return OGRERR_FAILURE;
3314 : else
3315 : return OGRERR_NONE;
3316 : #endif
3317 : }
3318 :
3319 : /************************************************************************/
3320 : /* OGRGetGenerate_DB2_V72_BYTE_ORDER() */
3321 : /* */
3322 : /* This is a special entry point to get the value of static flag */
3323 : /* OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER. */
3324 : /************************************************************************/
3325 0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
3326 : {
3327 0 : return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
3328 : }
3329 :
3330 : /************************************************************************/
3331 : /* createGEOSContext() */
3332 : /************************************************************************/
3333 :
3334 : /** Create a new GEOS context.
3335 : * @return a new GEOS context (to be freed with freeGEOSContext())
3336 : */
3337 85525 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
3338 : {
3339 : #ifndef HAVE_GEOS
3340 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3341 : return nullptr;
3342 : #else
3343 85525 : return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
3344 : #endif
3345 : }
3346 :
3347 : /************************************************************************/
3348 : /* freeGEOSContext() */
3349 : /************************************************************************/
3350 :
3351 : /** Destroy a GEOS context.
3352 : * @param hGEOSCtxt GEOS context
3353 : */
3354 82481 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
3355 : {
3356 : (void)hGEOSCtxt;
3357 : #ifdef HAVE_GEOS
3358 82481 : if (hGEOSCtxt != nullptr)
3359 : {
3360 82481 : finishGEOS_r(hGEOSCtxt);
3361 : }
3362 : #endif
3363 82481 : }
3364 : #ifdef HAVE_GEOS
3365 :
3366 : /************************************************************************/
3367 : /* canConvertToMultiPolygon() */
3368 : /************************************************************************/
3369 :
3370 148 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
3371 : {
3372 338 : for (const auto *poSubGeom : *poGC)
3373 : {
3374 : const OGRwkbGeometryType eSubGeomType =
3375 272 : wkbFlatten(poSubGeom->getGeometryType());
3376 272 : if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
3377 164 : eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
3378 : {
3379 82 : return false;
3380 : }
3381 : }
3382 :
3383 66 : return true;
3384 : }
3385 :
3386 : /************************************************************************/
3387 : /* GEOSWarningSilencer */
3388 : /************************************************************************/
3389 :
3390 : /** Class that can be used to silence GEOS messages while in-scope. */
3391 : class GEOSWarningSilencer
3392 : {
3393 : public:
3394 152 : explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
3395 152 : : m_poContext(poContext)
3396 : {
3397 152 : GEOSContext_setErrorHandler_r(m_poContext, nullptr);
3398 152 : GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
3399 152 : }
3400 :
3401 152 : ~GEOSWarningSilencer()
3402 152 : {
3403 152 : GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
3404 152 : GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
3405 152 : }
3406 :
3407 : CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
3408 :
3409 : private:
3410 : GEOSContextHandle_t m_poContext{nullptr};
3411 : };
3412 :
3413 : /************************************************************************/
3414 : /* repairForGEOS() */
3415 : /************************************************************************/
3416 :
3417 : /** Modify an OGRGeometry so that it can be converted into GEOS.
3418 : * Modifications include closing unclosed rings and adding redundant vertices
3419 : * to reach minimum point limits in GEOS.
3420 : *
3421 : * It is assumed that the input is a non-curved type that can be
3422 : * represented in GEOS.
3423 : *
3424 : * @param poGeom the geometry to modify
3425 : * @return an OGRGeometry that can be converted to GEOS using WKB
3426 : */
3427 22 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
3428 : {
3429 : #if GEOS_VERSION_MAJOR >= 3 || \
3430 : (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
3431 : static constexpr int MIN_RING_POINTS = 3;
3432 : #else
3433 : static constexpr int MIN_RING_POINTS = 4;
3434 : #endif
3435 :
3436 22 : const auto eType = wkbFlatten(poGeom->getGeometryType());
3437 :
3438 22 : if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
3439 : {
3440 4 : std::unique_ptr<OGRGeometryCollection> poRet;
3441 4 : if (eType == wkbGeometryCollection)
3442 : {
3443 2 : poRet = std::make_unique<OGRGeometryCollection>();
3444 : }
3445 2 : else if (eType == wkbMultiPolygon)
3446 : {
3447 2 : poRet = std::make_unique<OGRMultiPolygon>();
3448 : }
3449 0 : else if (eType == wkbMultiLineString)
3450 : {
3451 0 : poRet = std::make_unique<OGRMultiLineString>();
3452 : }
3453 0 : else if (eType == wkbMultiPoint)
3454 : {
3455 0 : poRet = std::make_unique<OGRMultiPoint>();
3456 : }
3457 : else
3458 : {
3459 0 : CPLError(CE_Failure, CPLE_AppDefined,
3460 : "Unexpected geometry type: %s",
3461 : OGRGeometryTypeToName(eType));
3462 0 : return nullptr;
3463 : }
3464 :
3465 4 : const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
3466 12 : for (const auto *poSubGeomIn : *poColl)
3467 : {
3468 8 : std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
3469 8 : poRet->addGeometry(std::move(poSubGeom));
3470 : }
3471 :
3472 4 : return poRet;
3473 : }
3474 :
3475 18 : if (eType == wkbPoint)
3476 : {
3477 0 : return std::unique_ptr<OGRGeometry>(poGeom->clone());
3478 : }
3479 18 : if (eType == wkbLineString)
3480 : {
3481 : std::unique_ptr<OGRLineString> poLineString(
3482 4 : poGeom->toLineString()->clone());
3483 2 : if (poLineString->getNumPoints() == 1)
3484 : {
3485 4 : OGRPoint oPoint;
3486 2 : poLineString->getPoint(0, &oPoint);
3487 2 : poLineString->addPoint(&oPoint);
3488 : }
3489 2 : return poLineString;
3490 : }
3491 16 : if (eType == wkbPolygon)
3492 : {
3493 32 : std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
3494 16 : poPolygon->closeRings();
3495 :
3496 : // make sure rings have enough points
3497 34 : for (auto *poRing : *poPolygon)
3498 : {
3499 22 : while (poRing->getNumPoints() < MIN_RING_POINTS)
3500 : {
3501 8 : OGRPoint oPoint;
3502 4 : poRing->getPoint(0, &oPoint);
3503 4 : poRing->addPoint(&oPoint);
3504 : }
3505 : }
3506 :
3507 16 : return poPolygon;
3508 : }
3509 :
3510 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
3511 : OGRGeometryTypeToName(eType));
3512 0 : return nullptr;
3513 : }
3514 :
3515 : /************************************************************************/
3516 : /* convertToGEOSGeom() */
3517 : /************************************************************************/
3518 :
3519 240733 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3520 : const OGRGeometry *poGeom)
3521 : {
3522 240733 : GEOSGeom hGeom = nullptr;
3523 240733 : const size_t nDataSize = poGeom->WkbSize();
3524 : unsigned char *pabyData =
3525 240733 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3526 : #if GEOS_VERSION_MAJOR > 3 || \
3527 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3528 240733 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3529 : #else
3530 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3531 : #endif
3532 240733 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3533 : {
3534 239713 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3535 : }
3536 240733 : CPLFree(pabyData);
3537 :
3538 240733 : return hGeom;
3539 : }
3540 : #endif
3541 :
3542 : /************************************************************************/
3543 : /* exportToGEOS() */
3544 : /************************************************************************/
3545 :
3546 : /** Returns a GEOSGeom object corresponding to the geometry.
3547 : *
3548 : * @param hGEOSCtxt GEOS context
3549 : * @param bRemoveEmptyParts Whether empty parts of the geometry should be
3550 : * removed before exporting to GEOS (GDAL >= 3.10)
3551 : * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
3552 : * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
3553 : * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
3554 : * @return a GEOSGeom object corresponding to the geometry (to be freed with
3555 : * GEOSGeom_destroy_r()), or NULL in case of error
3556 : */
3557 240719 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
3558 : bool bRemoveEmptyParts,
3559 : bool bAddPointsIfNeeded) const
3560 : {
3561 : (void)hGEOSCtxt;
3562 : (void)bRemoveEmptyParts;
3563 : (void)bAddPointsIfNeeded;
3564 :
3565 : #ifndef HAVE_GEOS
3566 :
3567 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3568 : return nullptr;
3569 :
3570 : #else
3571 :
3572 240719 : if (hGEOSCtxt == nullptr)
3573 0 : return nullptr;
3574 :
3575 240719 : const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
3576 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3577 : // POINT EMPTY is exported to WKB as if it were POINT(0 0),
3578 : // so that particular case is necessary.
3579 : if (eType == wkbPoint && IsEmpty())
3580 : {
3581 : return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
3582 : }
3583 : #endif
3584 :
3585 240719 : GEOSGeom hGeom = nullptr;
3586 :
3587 240719 : std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
3588 240719 : const OGRGeometry *poGeosInput = this;
3589 :
3590 240719 : const bool bHasZ = poGeosInput->Is3D();
3591 240719 : bool bHasM = poGeosInput->IsMeasured();
3592 :
3593 240719 : if (poGeosInput->hasCurveGeometry())
3594 : {
3595 864 : poModifiedInput.reset(poGeosInput->getLinearGeometry());
3596 864 : poGeosInput = poModifiedInput.get();
3597 : }
3598 :
3599 240719 : if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
3600 : {
3601 1 : if (!poModifiedInput)
3602 : {
3603 1 : poModifiedInput.reset(poGeosInput->clone());
3604 1 : poGeosInput = poModifiedInput.get();
3605 : }
3606 1 : poModifiedInput->removeEmptyParts();
3607 : }
3608 :
3609 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3610 : // GEOS < 3.12 doesn't support M dimension
3611 : if (bHasM)
3612 : {
3613 : if (!poModifiedInput)
3614 : {
3615 : poModifiedInput.reset(poGeosInput->clone());
3616 : poGeosInput = poModifiedInput.get();
3617 : }
3618 : poModifiedInput->setMeasured(false);
3619 : bHasM = false;
3620 : }
3621 : #endif
3622 :
3623 240719 : if (eType == wkbTriangle)
3624 : {
3625 : poModifiedInput =
3626 98 : std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
3627 98 : poGeosInput = poModifiedInput.get();
3628 : }
3629 240621 : else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3630 : {
3631 844 : if (!poModifiedInput)
3632 : {
3633 844 : poModifiedInput.reset(poGeosInput->clone());
3634 : }
3635 :
3636 2532 : poModifiedInput = OGRGeometryFactory::forceTo(
3637 844 : std::move(poModifiedInput),
3638 844 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
3639 844 : poGeosInput = poModifiedInput.get();
3640 : }
3641 239925 : else if (eType == wkbGeometryCollection &&
3642 148 : CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
3643 : {
3644 66 : if (!poModifiedInput)
3645 : {
3646 64 : poModifiedInput.reset(poGeosInput->clone());
3647 : }
3648 :
3649 : // Force into a MultiPolygon, then back to a GeometryCollection.
3650 : // This gets rid of fancy types like TIN and PolyhedralSurface that
3651 : // GEOS doesn't understand and flattens nested collections.
3652 198 : poModifiedInput = OGRGeometryFactory::forceTo(
3653 66 : std::move(poModifiedInput),
3654 66 : OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
3655 198 : poModifiedInput = OGRGeometryFactory::forceTo(
3656 66 : std::move(poModifiedInput),
3657 66 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
3658 :
3659 66 : poGeosInput = poModifiedInput.get();
3660 : }
3661 :
3662 : {
3663 : // Rather than check for conditions that would prevent conversion to
3664 : // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
3665 : // conversion as-is. If the conversion fails, we don't want any
3666 : // warnings emitted; we'll repair the input and try again.
3667 0 : std::optional<GEOSWarningSilencer> oSilencer;
3668 240719 : if (bAddPointsIfNeeded)
3669 : {
3670 152 : oSilencer.emplace(hGEOSCtxt);
3671 : }
3672 :
3673 240719 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3674 : }
3675 :
3676 240719 : if (hGeom == nullptr && bAddPointsIfNeeded)
3677 : {
3678 14 : poModifiedInput = repairForGEOS(poGeosInput);
3679 14 : poGeosInput = poModifiedInput.get();
3680 :
3681 14 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3682 : }
3683 :
3684 240719 : return hGeom;
3685 :
3686 : #endif // HAVE_GEOS
3687 : }
3688 :
3689 : /************************************************************************/
3690 : /* hasCurveGeometry() */
3691 : /************************************************************************/
3692 :
3693 : /**
3694 : * \brief Returns if this geometry is or has curve geometry.
3695 : *
3696 : * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
3697 : * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
3698 : *
3699 : * If bLookForNonLinear is set to TRUE, it will be actually looked if
3700 : * the geometry or its subgeometries are or contain a non-linear
3701 : * geometry in them. In which case, if the method returns TRUE, it
3702 : * means that getLinearGeometry() would return an approximate version
3703 : * of the geometry. Otherwise, getLinearGeometry() would do a
3704 : * conversion, but with just converting container type, like
3705 : * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
3706 : * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
3707 : * conversion.
3708 : *
3709 : * This method is the same as the C function OGR_G_HasCurveGeometry().
3710 : *
3711 : * @param bLookForNonLinear set it to TRUE to check if the geometry is
3712 : * or contains a CIRCULARSTRING.
3713 : *
3714 : * @return TRUE if this geometry is or has curve geometry.
3715 : *
3716 : */
3717 :
3718 280546 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3719 : {
3720 280546 : return FALSE;
3721 : }
3722 :
3723 : /************************************************************************/
3724 : /* getLinearGeometry() */
3725 : /************************************************************************/
3726 :
3727 : /**
3728 : * \brief Return, possibly approximate, non-curve version of this geometry.
3729 : *
3730 : * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
3731 : * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
3732 : *
3733 : * The ownership of the returned geometry belongs to the caller.
3734 : *
3735 : * The reverse method is OGRGeometry::getCurveGeometry().
3736 : *
3737 : * This method is the same as the C function OGR_G_GetLinearGeometry().
3738 : *
3739 : * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
3740 : * arc, zero to use the default setting.
3741 : * @param papszOptions options as a null-terminated list of strings.
3742 : * See OGRGeometryFactory::curveToLineString() for
3743 : * valid options.
3744 : *
3745 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3746 : *
3747 : */
3748 :
3749 : OGRGeometry *
3750 88 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3751 : CPL_UNUSED const char *const *papszOptions) const
3752 : {
3753 88 : return clone();
3754 : }
3755 :
3756 : /************************************************************************/
3757 : /* getCurveGeometry() */
3758 : /************************************************************************/
3759 :
3760 : /**
3761 : * \brief Return curve version of this geometry.
3762 : *
3763 : * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
3764 : * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
3765 : * curve geometries.
3766 : *
3767 : * If the geometry has no curve portion, the returned geometry will be a clone
3768 : * of it.
3769 : *
3770 : * The ownership of the returned geometry belongs to the caller.
3771 : *
3772 : * The reverse method is OGRGeometry::getLinearGeometry().
3773 : *
3774 : * This function is the same as C function OGR_G_GetCurveGeometry().
3775 : *
3776 : * @param papszOptions options as a null-terminated list of strings.
3777 : * Unused for now. Must be set to NULL.
3778 : *
3779 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3780 : *
3781 : */
3782 :
3783 : OGRGeometry *
3784 5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
3785 : {
3786 5 : return clone();
3787 : }
3788 :
3789 : /************************************************************************/
3790 : /* Distance() */
3791 : /************************************************************************/
3792 :
3793 : /**
3794 : * \brief Compute distance between two geometries.
3795 : *
3796 : * Returns the shortest distance between the two geometries. The distance is
3797 : * expressed into the same unit as the coordinates of the geometries.
3798 : *
3799 : * This method is the same as the C function OGR_G_Distance().
3800 : *
3801 : * This method is built on the GEOS library, check it for the definition
3802 : * of the geometry operation.
3803 : * If OGR is built without the GEOS library, this method will always fail,
3804 : * issuing a CPLE_NotSupported error.
3805 : *
3806 : * @param poOtherGeom the other geometry to compare against.
3807 : *
3808 : * @return the distance between the geometries or -1 if an error occurs.
3809 : */
3810 :
3811 25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
3812 :
3813 : {
3814 25 : if (nullptr == poOtherGeom)
3815 : {
3816 0 : CPLDebug("OGR",
3817 : "OGRGeometry::Distance called with NULL geometry pointer");
3818 0 : return -1.0;
3819 : }
3820 :
3821 25 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
3822 : {
3823 : #ifndef HAVE_SFCGAL
3824 :
3825 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3826 0 : return -1.0;
3827 :
3828 : #else
3829 :
3830 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3831 : if (poThis == nullptr)
3832 : return -1.0;
3833 :
3834 : sfcgal_geometry_t *poOther =
3835 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3836 : if (poOther == nullptr)
3837 : {
3838 : sfcgal_geometry_delete(poThis);
3839 : return -1.0;
3840 : }
3841 :
3842 : const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
3843 :
3844 : sfcgal_geometry_delete(poThis);
3845 : sfcgal_geometry_delete(poOther);
3846 :
3847 : return dfDistance > 0.0 ? dfDistance : -1.0;
3848 :
3849 : #endif
3850 : }
3851 :
3852 : else
3853 : {
3854 : #ifndef HAVE_GEOS
3855 :
3856 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3857 : return -1.0;
3858 :
3859 : #else
3860 :
3861 25 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3862 : // GEOSGeom is a pointer
3863 25 : GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
3864 25 : GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
3865 :
3866 25 : int bIsErr = 0;
3867 25 : double dfDistance = 0.0;
3868 :
3869 25 : if (hThis != nullptr && hOther != nullptr)
3870 : {
3871 25 : bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
3872 : }
3873 :
3874 25 : GEOSGeom_destroy_r(hGEOSCtxt, hThis);
3875 25 : GEOSGeom_destroy_r(hGEOSCtxt, hOther);
3876 25 : freeGEOSContext(hGEOSCtxt);
3877 :
3878 25 : if (bIsErr > 0)
3879 : {
3880 25 : return dfDistance;
3881 : }
3882 :
3883 : /* Calculations error */
3884 0 : return -1.0;
3885 :
3886 : #endif /* HAVE_GEOS */
3887 : }
3888 : }
3889 :
3890 : /************************************************************************/
3891 : /* OGR_G_Distance() */
3892 : /************************************************************************/
3893 : /**
3894 : * \brief Compute distance between two geometries.
3895 : *
3896 : * Returns the shortest distance between the two geometries. The distance is
3897 : * expressed into the same unit as the coordinates of the geometries.
3898 : *
3899 : * This function is the same as the C++ method OGRGeometry::Distance().
3900 : *
3901 : * This function is built on the GEOS library, check it for the definition
3902 : * of the geometry operation.
3903 : * If OGR is built without the GEOS library, this function will always fail,
3904 : * issuing a CPLE_NotSupported error.
3905 : *
3906 : * @param hFirst the first geometry to compare against.
3907 : * @param hOther the other geometry to compare against.
3908 : *
3909 : * @return the distance between the geometries or -1 if an error occurs.
3910 : */
3911 :
3912 2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
3913 :
3914 : {
3915 2 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
3916 :
3917 4 : return OGRGeometry::FromHandle(hFirst)->Distance(
3918 4 : OGRGeometry::FromHandle(hOther));
3919 : }
3920 :
3921 : /************************************************************************/
3922 : /* Distance3D() */
3923 : /************************************************************************/
3924 :
3925 : /**
3926 : * \brief Returns the 3D distance between two geometries
3927 : *
3928 : * The distance is expressed into the same unit as the coordinates of the
3929 : * geometries.
3930 : *
3931 : * This method is built on the SFCGAL library, check it for the definition
3932 : * of the geometry operation.
3933 : * If OGR is built without the SFCGAL library, this method will always return
3934 : * -1.0
3935 : *
3936 : * This function is the same as the C function OGR_G_Distance3D().
3937 : *
3938 : * @return distance between the two geometries
3939 : */
3940 :
3941 1 : double OGRGeometry::Distance3D(
3942 : UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
3943 : {
3944 1 : if (poOtherGeom == nullptr)
3945 : {
3946 0 : CPLDebug("OGR",
3947 : "OGRTriangle::Distance3D called with NULL geometry pointer");
3948 0 : return -1.0;
3949 : }
3950 :
3951 1 : if (!(poOtherGeom->Is3D() && Is3D()))
3952 : {
3953 0 : CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
3954 : "geometry(geometries)");
3955 0 : return -1.0;
3956 : }
3957 :
3958 : #ifndef HAVE_SFCGAL
3959 :
3960 1 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3961 1 : return -1.0;
3962 :
3963 : #else
3964 :
3965 : sfcgal_init();
3966 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3967 : if (poThis == nullptr)
3968 : return -1.0;
3969 :
3970 : sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3971 : if (poOther == nullptr)
3972 : {
3973 : sfcgal_geometry_delete(poThis);
3974 : return -1.0;
3975 : }
3976 :
3977 : const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
3978 :
3979 : sfcgal_geometry_delete(poThis);
3980 : sfcgal_geometry_delete(poOther);
3981 :
3982 : return dfDistance > 0 ? dfDistance : -1.0;
3983 :
3984 : #endif
3985 : }
3986 :
3987 : /************************************************************************/
3988 : /* OGR_G_Distance3D() */
3989 : /************************************************************************/
3990 : /**
3991 : * \brief Returns the 3D distance between two geometries
3992 : *
3993 : * The distance is expressed into the same unit as the coordinates of the
3994 : * geometries.
3995 : *
3996 : * This method is built on the SFCGAL library, check it for the definition
3997 : * of the geometry operation.
3998 : * If OGR is built without the SFCGAL library, this method will always return
3999 : * -1.0
4000 : *
4001 : * This function is the same as the C++ method OGRGeometry::Distance3D().
4002 : *
4003 : * @param hFirst the first geometry to compare against.
4004 : * @param hOther the other geometry to compare against.
4005 : * @return distance between the two geometries
4006 : *
4007 : * @return the distance between the geometries or -1 if an error occurs.
4008 : */
4009 :
4010 1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
4011 :
4012 : {
4013 1 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
4014 :
4015 2 : return OGRGeometry::FromHandle(hFirst)->Distance3D(
4016 2 : OGRGeometry::FromHandle(hOther));
4017 : }
4018 :
4019 : /************************************************************************/
4020 : /* OGRGeometryRebuildCurves() */
4021 : /************************************************************************/
4022 :
4023 : #ifdef HAVE_GEOS
4024 3936 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
4025 : const OGRGeometry *poOtherGeom,
4026 : OGRGeometry *poOGRProduct)
4027 : {
4028 7872 : if (poOGRProduct != nullptr &&
4029 7789 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
4030 3853 : (poGeom->hasCurveGeometry(true) ||
4031 2705 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
4032 : {
4033 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
4034 8 : delete poOGRProduct;
4035 8 : return poCurveGeom;
4036 : }
4037 3928 : return poOGRProduct;
4038 : }
4039 :
4040 : /************************************************************************/
4041 : /* BuildGeometryFromGEOS() */
4042 : /************************************************************************/
4043 :
4044 3786 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
4045 : GEOSGeom hGeosProduct,
4046 : const OGRGeometry *poSelf,
4047 : const OGRGeometry *poOtherGeom)
4048 : {
4049 3786 : OGRGeometry *poOGRProduct = nullptr;
4050 3786 : if (hGeosProduct != nullptr)
4051 : {
4052 : poOGRProduct =
4053 3784 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
4054 3784 : if (poOGRProduct != nullptr &&
4055 9236 : poSelf->getSpatialReference() != nullptr &&
4056 1668 : (poOtherGeom == nullptr ||
4057 1668 : (poOtherGeom->getSpatialReference() != nullptr &&
4058 1533 : poOtherGeom->getSpatialReference()->IsSame(
4059 : poSelf->getSpatialReference()))))
4060 : {
4061 1582 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
4062 : }
4063 : poOGRProduct =
4064 3784 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
4065 3784 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
4066 : }
4067 3786 : return poOGRProduct;
4068 : }
4069 :
4070 : /************************************************************************/
4071 : /* BuildGeometryFromTwoGeoms() */
4072 : /************************************************************************/
4073 :
4074 2776 : static OGRGeometry *BuildGeometryFromTwoGeoms(
4075 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4076 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
4077 : const GEOSGeometry *,
4078 : const GEOSGeometry *))
4079 : {
4080 2776 : OGRGeometry *poOGRProduct = nullptr;
4081 :
4082 2776 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4083 2776 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4084 2776 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4085 2776 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4086 : {
4087 : GEOSGeom hGeosProduct =
4088 2776 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
4089 :
4090 : poOGRProduct =
4091 2776 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
4092 : }
4093 2776 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4094 2776 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4095 2776 : poSelf->freeGEOSContext(hGEOSCtxt);
4096 :
4097 2776 : return poOGRProduct;
4098 : }
4099 :
4100 : /************************************************************************/
4101 : /* OGRGEOSBooleanPredicate() */
4102 : /************************************************************************/
4103 :
4104 22762 : static bool OGRGEOSBooleanPredicate(
4105 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
4106 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
4107 : const GEOSGeometry *))
4108 : {
4109 22762 : bool bResult = false;
4110 :
4111 22762 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
4112 22762 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
4113 22762 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4114 22762 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4115 : {
4116 22203 : bResult =
4117 22203 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
4118 : }
4119 22762 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4120 22762 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4121 22762 : poSelf->freeGEOSContext(hGEOSCtxt);
4122 :
4123 22762 : return bResult;
4124 : }
4125 :
4126 : #endif // HAVE_GEOS
4127 :
4128 : /************************************************************************/
4129 : /* MakeValid() */
4130 : /************************************************************************/
4131 :
4132 : /**
4133 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4134 : *
4135 : * Already-valid geometries are cloned without further intervention
4136 : * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
4137 : * may be subject to non-significant transformations, such as duplicated point
4138 : * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
4139 : * geometry collections could be returned a single geometry. GDAL 3.10
4140 : * returns the same type of geometry).
4141 : *
4142 : * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
4143 : * post-processing step is often desired.
4144 : *
4145 : * This method is the same as the C function OGR_G_MakeValid().
4146 : *
4147 : * This function is built on the GEOS >= 3.8 library, check it for the
4148 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4149 : * library, this function will return a clone of the input geometry if it is
4150 : * valid, or NULL if it is invalid.
4151 : *
4152 : * Certain geometries cannot be read using GEOS, for example if Polygon rings
4153 : * are not closed or do not contain enough vertices. If a geometry cannot be
4154 : * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
4155 : * attempt to modify these geometries such that they can be read and
4156 : * repaired by GEOS.
4157 : *
4158 : * @param papszOptions NULL terminated list of options, or NULL. The following
4159 : * options are available:
4160 : * <ul>
4161 : * <li>METHOD=LINEWORK/STRUCTURE.
4162 : * LINEWORK is the default method, which combines all rings into a set of
4163 : * noded lines and then extracts valid polygons from that linework.
4164 : * The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
4165 : * all rings valid, then merges shells and
4166 : * subtracts holes from shells to generate valid result. Assumes that
4167 : * holes and shells are correctly categorized.</li>
4168 : * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
4169 : * NO (default): collapses are converted to empty geometries
4170 : * YES: collapses are converted to a valid geometry of lower dimension.</li>
4171 : * </ul>
4172 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4173 : *
4174 : * @since GDAL 3.0
4175 : */
4176 153 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
4177 : {
4178 : (void)papszOptions;
4179 : #ifndef HAVE_GEOS
4180 : if (IsValid())
4181 : return clone();
4182 :
4183 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4184 : return nullptr;
4185 : #else
4186 153 : if (IsSFCGALCompatible())
4187 : {
4188 0 : if (IsValid())
4189 0 : return clone();
4190 : }
4191 153 : else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
4192 : {
4193 3 : GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
4194 3 : bool bIsValid = false;
4195 3 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4196 3 : if (hGeosGeom)
4197 : {
4198 3 : bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
4199 3 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4200 : }
4201 3 : freeGEOSContext(hGEOSCtxt);
4202 3 : if (bIsValid)
4203 1 : return clone();
4204 : }
4205 :
4206 152 : const bool bStructureMethod = EQUAL(
4207 : CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
4208 152 : CPL_IGNORE_RET_VAL(bStructureMethod);
4209 : #if !(GEOS_VERSION_MAJOR > 3 || \
4210 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
4211 : if (bStructureMethod)
4212 : {
4213 : CPLError(CE_Failure, CPLE_NotSupported,
4214 : "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
4215 : return nullptr;
4216 : }
4217 : #endif
4218 :
4219 152 : OGRGeometry *poOGRProduct = nullptr;
4220 :
4221 152 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4222 152 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
4223 152 : if (hGeosGeom != nullptr)
4224 : {
4225 : GEOSGeom hGEOSRet;
4226 : #if GEOS_VERSION_MAJOR > 3 || \
4227 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4228 152 : if (bStructureMethod)
4229 : {
4230 : GEOSMakeValidParams *params =
4231 15 : GEOSMakeValidParams_create_r(hGEOSCtxt);
4232 15 : CPLAssert(params);
4233 15 : GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
4234 : GEOS_MAKE_VALID_STRUCTURE);
4235 15 : GEOSMakeValidParams_setKeepCollapsed_r(
4236 : hGEOSCtxt, params,
4237 15 : CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
4238 15 : hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
4239 15 : GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
4240 : }
4241 : else
4242 : #endif
4243 : {
4244 137 : hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
4245 : }
4246 152 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4247 :
4248 152 : if (hGEOSRet != nullptr)
4249 : {
4250 : poOGRProduct =
4251 152 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
4252 152 : if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
4253 6 : poOGRProduct->assignSpatialReference(getSpatialReference());
4254 : poOGRProduct =
4255 152 : OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
4256 152 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
4257 :
4258 : #if GEOS_VERSION_MAJOR > 3 || \
4259 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4260 : // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
4261 : // if the input is a multiple geometry
4262 152 : if (poOGRProduct && bStructureMethod &&
4263 310 : OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
4264 6 : !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
4265 : wkbGeometryCollection))
4266 : {
4267 6 : poOGRProduct = OGRGeometryFactory::forceTo(
4268 6 : std::unique_ptr<OGRGeometry>(poOGRProduct),
4269 3 : getGeometryType())
4270 3 : .release();
4271 : }
4272 : #endif
4273 : }
4274 : }
4275 152 : freeGEOSContext(hGEOSCtxt);
4276 :
4277 152 : return poOGRProduct;
4278 : #endif
4279 : }
4280 :
4281 : /************************************************************************/
4282 : /* OGR_G_MakeValid() */
4283 : /************************************************************************/
4284 :
4285 : /**
4286 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4287 : *
4288 : * Already-valid geometries are cloned without further intervention.
4289 : *
4290 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4291 : *
4292 : * This function is built on the GEOS >= 3.8 library, check it for the
4293 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4294 : * library, this function will return a clone of the input geometry if it is
4295 : * valid, or NULL if it is invalid
4296 : *
4297 : * @param hGeom The Geometry to make valid.
4298 : *
4299 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4300 : * or NULL if an error occurs.
4301 : *
4302 : * @since GDAL 3.0
4303 : */
4304 :
4305 0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
4306 :
4307 : {
4308 0 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
4309 :
4310 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
4311 : }
4312 :
4313 : /************************************************************************/
4314 : /* OGR_G_MakeValidEx() */
4315 : /************************************************************************/
4316 :
4317 : /**
4318 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4319 : *
4320 : * Already-valid geometries are cloned without further intervention.
4321 : *
4322 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4323 : *
4324 : * See documentation of that method for possible options.
4325 : *
4326 : * @param hGeom The Geometry to make valid.
4327 : * @param papszOptions Options.
4328 : *
4329 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4330 : * or NULL if an error occurs.
4331 : *
4332 : * @since GDAL 3.4
4333 : */
4334 :
4335 25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
4336 :
4337 : {
4338 25 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
4339 :
4340 25 : return OGRGeometry::ToHandle(
4341 50 : OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
4342 : }
4343 :
4344 : /************************************************************************/
4345 : /* Normalize() */
4346 : /************************************************************************/
4347 :
4348 : /**
4349 : * \brief Attempts to bring geometry into normalized/canonical form.
4350 : *
4351 : * This method is the same as the C function OGR_G_Normalize().
4352 : *
4353 : * This function is built on the GEOS library; check it for the definition
4354 : * of the geometry operation.
4355 : * If OGR is built without the GEOS library, this function will always fail,
4356 : * issuing a CPLE_NotSupported error.
4357 : *
4358 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4359 : *
4360 : * @since GDAL 3.3
4361 : */
4362 51 : OGRGeometry *OGRGeometry::Normalize() const
4363 : {
4364 : #ifndef HAVE_GEOS
4365 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4366 : return nullptr;
4367 : #else
4368 51 : OGRGeometry *poOGRProduct = nullptr;
4369 :
4370 51 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4371 51 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4372 51 : if (hGeosGeom != nullptr)
4373 : {
4374 :
4375 51 : int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
4376 :
4377 51 : if (hGEOSRet == 0)
4378 : {
4379 : poOGRProduct =
4380 51 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
4381 : }
4382 : else
4383 : {
4384 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4385 : }
4386 : }
4387 51 : freeGEOSContext(hGEOSCtxt);
4388 :
4389 51 : return poOGRProduct;
4390 : #endif
4391 : }
4392 :
4393 : /************************************************************************/
4394 : /* OGR_G_Normalize() */
4395 : /************************************************************************/
4396 :
4397 : /**
4398 : * \brief Attempts to bring geometry into normalized/canonical form.
4399 : *
4400 : * This function is the same as the C++ method OGRGeometry::Normalize().
4401 : *
4402 : * This function is built on the GEOS library; check it for the definition
4403 : * of the geometry operation.
4404 : * If OGR is built without the GEOS library, this function will always fail,
4405 : * issuing a CPLE_NotSupported error.
4406 : * @param hGeom The Geometry to normalize.
4407 : *
4408 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4409 : * or NULL if an error occurs.
4410 : *
4411 : * @since GDAL 3.3
4412 : */
4413 :
4414 21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
4415 :
4416 : {
4417 21 : VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
4418 :
4419 21 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
4420 : }
4421 :
4422 : /************************************************************************/
4423 : /* ConvexHull() */
4424 : /************************************************************************/
4425 :
4426 : /**
4427 : * \brief Compute convex hull.
4428 : *
4429 : * A new geometry object is created and returned containing the convex
4430 : * hull of the geometry on which the method is invoked.
4431 : *
4432 : * This method is the same as the C function OGR_G_ConvexHull().
4433 : *
4434 : * This method is built on the GEOS library, check it for the definition
4435 : * of the geometry operation.
4436 : * If OGR is built without the GEOS library, this method will always fail,
4437 : * issuing a CPLE_NotSupported error.
4438 : *
4439 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4440 : */
4441 :
4442 6 : OGRGeometry *OGRGeometry::ConvexHull() const
4443 :
4444 : {
4445 6 : if (IsSFCGALCompatible())
4446 : {
4447 : #ifndef HAVE_SFCGAL
4448 :
4449 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4450 0 : return nullptr;
4451 :
4452 : #else
4453 :
4454 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4455 : if (poThis == nullptr)
4456 : return nullptr;
4457 :
4458 : sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
4459 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4460 : if (h_prodGeom)
4461 : h_prodGeom->assignSpatialReference(getSpatialReference());
4462 :
4463 : sfcgal_geometry_delete(poThis);
4464 : sfcgal_geometry_delete(poRes);
4465 :
4466 : return h_prodGeom;
4467 :
4468 : #endif
4469 : }
4470 :
4471 : else
4472 : {
4473 : #ifndef HAVE_GEOS
4474 :
4475 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4476 : return nullptr;
4477 :
4478 : #else
4479 :
4480 6 : OGRGeometry *poOGRProduct = nullptr;
4481 :
4482 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4483 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4484 6 : if (hGeosGeom != nullptr)
4485 : {
4486 6 : GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
4487 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4488 :
4489 : poOGRProduct =
4490 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4491 : }
4492 6 : freeGEOSContext(hGEOSCtxt);
4493 :
4494 6 : return poOGRProduct;
4495 :
4496 : #endif /* HAVE_GEOS */
4497 : }
4498 : }
4499 :
4500 : /************************************************************************/
4501 : /* OGR_G_ConvexHull() */
4502 : /************************************************************************/
4503 : /**
4504 : * \brief Compute convex hull.
4505 : *
4506 : * A new geometry object is created and returned containing the convex
4507 : * hull of the geometry on which the method is invoked.
4508 : *
4509 : * This function is the same as the C++ method OGRGeometry::ConvexHull().
4510 : *
4511 : * This function is built on the GEOS library, check it for the definition
4512 : * of the geometry operation.
4513 : * If OGR is built without the GEOS library, this function will always fail,
4514 : * issuing a CPLE_NotSupported error.
4515 : *
4516 : * @param hTarget The Geometry to calculate the convex hull of.
4517 : *
4518 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4519 : * or NULL if an error occurs.
4520 : */
4521 :
4522 1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
4523 :
4524 : {
4525 1 : VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
4526 :
4527 1 : return OGRGeometry::ToHandle(
4528 1 : OGRGeometry::FromHandle(hTarget)->ConvexHull());
4529 : }
4530 :
4531 : /************************************************************************/
4532 : /* ConcaveHull() */
4533 : /************************************************************************/
4534 :
4535 : /**
4536 : * \brief Compute the concave hull of a geometry.
4537 : *
4538 : * The concave hull is fully contained within the convex hull and also
4539 : * contains all the points of the input, but in a smaller area.
4540 : * The area ratio is the ratio of the area of the convex hull and the concave
4541 : * hull. Frequently used to convert a multi-point into a polygonal area.
4542 : * that contains all the points in the input Geometry.
4543 : *
4544 : * A new geometry object is created and returned containing the concave
4545 : * hull of the geometry on which the method is invoked.
4546 : *
4547 : * This method is the same as the C function OGR_G_ConcaveHull().
4548 : *
4549 : * This method is built on the GEOS >= 3.11 library
4550 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4551 : * fail, issuing a CPLE_NotSupported error.
4552 : *
4553 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4554 : * @param bAllowHoles Whether holes are allowed.
4555 : *
4556 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4557 : *
4558 : * @since GDAL 3.6
4559 : * @see OGRGeometry::ConcaveHullOfPolygons()
4560 : */
4561 :
4562 8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
4563 : {
4564 : #ifndef HAVE_GEOS
4565 : (void)dfRatio;
4566 : (void)bAllowHoles;
4567 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4568 : return nullptr;
4569 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4570 : (void)dfRatio;
4571 : (void)bAllowHoles;
4572 : CPLError(CE_Failure, CPLE_NotSupported,
4573 : "GEOS 3.11 or later needed for ConcaveHull.");
4574 : return nullptr;
4575 : #else
4576 8 : OGRGeometry *poOGRProduct = nullptr;
4577 :
4578 8 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4579 8 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4580 8 : if (hGeosGeom != nullptr)
4581 : {
4582 : GEOSGeom hGeosHull =
4583 8 : GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
4584 8 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4585 :
4586 : poOGRProduct =
4587 8 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4588 : }
4589 8 : freeGEOSContext(hGEOSCtxt);
4590 :
4591 8 : return poOGRProduct;
4592 : #endif /* HAVE_GEOS */
4593 : }
4594 :
4595 : /************************************************************************/
4596 : /* OGR_G_ConcaveHull() */
4597 : /************************************************************************/
4598 : /**
4599 : * \brief Compute the concave hull of a geometry.
4600 : *
4601 : * The concave hull is fully contained within the convex hull and also
4602 : * contains all the points of the input, but in a smaller area.
4603 : * The area ratio is the ratio of the area of the convex hull and the concave
4604 : * hull. Frequently used to convert a multi-point into a polygonal area.
4605 : * that contains all the points in the input Geometry.
4606 : *
4607 : * A new geometry object is created and returned containing the convex
4608 : * hull of the geometry on which the function is invoked.
4609 : *
4610 : * This function is the same as the C++ method OGRGeometry::ConcaveHull().
4611 : *
4612 : * This function is built on the GEOS >= 3.11 library
4613 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4614 : * fail, issuing a CPLE_NotSupported error.
4615 : *
4616 : * @param hTarget The Geometry to calculate the concave hull of.
4617 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4618 : * @param bAllowHoles Whether holes are allowed.
4619 : *
4620 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4621 : * or NULL if an error occurs.
4622 : *
4623 : * @since GDAL 3.6
4624 : * @see OGR_G_ConcaveHullOfPolygons()
4625 : */
4626 :
4627 2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
4628 : bool bAllowHoles)
4629 :
4630 : {
4631 2 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
4632 :
4633 2 : return OGRGeometry::ToHandle(
4634 2 : OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
4635 : }
4636 :
4637 : /************************************************************************/
4638 : /* ConcaveHullOfPolygons() */
4639 : /************************************************************************/
4640 :
4641 : /**
4642 : * \brief Compute the concave hull of a set of polygons, respecting
4643 : * the polygons as constraints.
4644 : *
4645 : * A concave hull is a (possibly) non-convex polygon containing all the input
4646 : * polygons.
4647 : * The computed hull "fills the gap" between the polygons,
4648 : * and does not intersect their interior.
4649 : * A set of polygons has a sequence of hulls of increasing concaveness,
4650 : * determined by a numeric target parameter.
4651 : *
4652 : * The concave hull is constructed by removing the longest outer edges
4653 : * of the Delaunay Triangulation of the space between the polygons,
4654 : * until the target criterion parameter is reached.
4655 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4656 : * between polygons to be no larger than this value.
4657 : * This can be expressed as a ratio between the lengths of the longest and
4658 : * shortest edges.
4659 : *
4660 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4661 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4662 : * for more details.
4663 : *
4664 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4665 : * be non-overlapping).
4666 : *
4667 : * A new geometry object is created and returned containing the concave
4668 : * hull of the geometry on which the method is invoked.
4669 : *
4670 : * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
4671 : *
4672 : * This method is built on the GEOS >= 3.11 library
4673 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4674 : * fail, issuing a CPLE_NotSupported error.
4675 : *
4676 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4677 : * difference between the longest and shortest edge lengths
4678 : * between the polygons.
4679 : * This normalizes the Maximum Edge Length to be scale-free.
4680 : * A value of 1 produces the convex hull; a value of 0 produces
4681 : * the original polygons.
4682 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4683 : * polygons.
4684 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4685 : *
4686 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4687 : *
4688 : * @since GDAL 3.13
4689 : * @see OGRGeometry::ConcaveHull()
4690 : */
4691 :
4692 13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
4693 : bool bIsTight,
4694 : bool bAllowHoles) const
4695 : {
4696 : #ifndef HAVE_GEOS
4697 : (void)dfLengthRatio;
4698 : (void)bIsTight;
4699 : (void)bAllowHoles;
4700 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4701 : return nullptr;
4702 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4703 : (void)dfLengthRatio;
4704 : (void)bIsTight;
4705 : (void)bAllowHoles;
4706 : CPLError(CE_Failure, CPLE_NotSupported,
4707 : "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
4708 : return nullptr;
4709 : #else
4710 13 : OGRGeometry *poOGRProduct = nullptr;
4711 :
4712 13 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4713 13 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4714 13 : if (hGeosGeom != nullptr)
4715 : {
4716 13 : GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
4717 : hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
4718 13 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4719 :
4720 : poOGRProduct =
4721 13 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4722 : }
4723 13 : freeGEOSContext(hGEOSCtxt);
4724 :
4725 13 : return poOGRProduct;
4726 : #endif /* HAVE_GEOS */
4727 : }
4728 :
4729 : /************************************************************************/
4730 : /* OGR_G_ConcaveHullOfPolygons() */
4731 : /************************************************************************/
4732 : /**
4733 : * \brief Compute the concave hull of a set of polygons, respecting
4734 : * the polygons as constraints.
4735 : *
4736 : * A concave hull is a (possibly) non-convex polygon containing all the input
4737 : * polygons.
4738 : * The computed hull "fills the gap" between the polygons,
4739 : * and does not intersect their interior.
4740 : * A set of polygons has a sequence of hulls of increasing concaveness,
4741 : * determined by a numeric target parameter.
4742 : *
4743 : * The concave hull is constructed by removing the longest outer edges
4744 : * of the Delaunay Triangulation of the space between the polygons,
4745 : * until the target criterion parameter is reached.
4746 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4747 : * between polygons to be no larger than this value.
4748 : * This can be expressed as a ratio between the lengths of the longest and
4749 : * shortest edges.
4750 : *
4751 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4752 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4753 : * for more details.
4754 : *
4755 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4756 : * be non-overlapping).
4757 : *
4758 : * A new geometry object is created and returned containing the concave
4759 : * hull of the geometry on which the method is invoked.
4760 : *
4761 : * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
4762 : *
4763 : * This function is built on the GEOS >= 3.11 library
4764 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4765 : * fail, issuing a CPLE_NotSupported error.
4766 : *
4767 : * @param hTarget The Geometry to calculate the concave hull of.
4768 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4769 : * difference between the longest and shortest edge lengths
4770 : * between the polygons.
4771 : * This normalizes the Maximum Edge Length to be scale-free.
4772 : * A value of 1 produces the convex hull; a value of 0 produces
4773 : * the original polygons.
4774 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4775 : * polygons.
4776 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4777 : *
4778 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4779 : * or NULL if an error occurs.
4780 : *
4781 : * @since GDAL 3.13
4782 : * @see OGR_G_ConcaveHull()
4783 : */
4784 :
4785 7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
4786 : double dfLengthRatio, bool bIsTight,
4787 : bool bAllowHoles)
4788 :
4789 : {
4790 7 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
4791 :
4792 7 : return OGRGeometry::ToHandle(
4793 : OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
4794 7 : dfLengthRatio, bIsTight, bAllowHoles));
4795 : }
4796 :
4797 : /************************************************************************/
4798 : /* Boundary() */
4799 : /************************************************************************/
4800 :
4801 : /**
4802 : * \brief Compute boundary.
4803 : *
4804 : * A new geometry object is created and returned containing the boundary
4805 : * of the geometry on which the method is invoked.
4806 : *
4807 : * This method is the same as the C function OGR_G_Boundary().
4808 : *
4809 : * This method is built on the GEOS library, check it for the definition
4810 : * of the geometry operation.
4811 : * If OGR is built without the GEOS library, this method will always fail,
4812 : * issuing a CPLE_NotSupported error.
4813 : *
4814 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4815 : *
4816 : */
4817 :
4818 6 : OGRGeometry *OGRGeometry::Boundary() const
4819 :
4820 : {
4821 : #ifndef HAVE_GEOS
4822 :
4823 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4824 : return nullptr;
4825 :
4826 : #else
4827 :
4828 6 : OGRGeometry *poOGRProduct = nullptr;
4829 :
4830 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4831 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4832 6 : if (hGeosGeom != nullptr)
4833 : {
4834 6 : GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
4835 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4836 :
4837 : poOGRProduct =
4838 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4839 : }
4840 6 : freeGEOSContext(hGEOSCtxt);
4841 :
4842 6 : return poOGRProduct;
4843 :
4844 : #endif // HAVE_GEOS
4845 : }
4846 :
4847 : //! @cond Doxygen_Suppress
4848 : /**
4849 : * \brief Compute boundary (deprecated)
4850 : *
4851 : * @deprecated
4852 : *
4853 : * @see Boundary()
4854 : */
4855 0 : OGRGeometry *OGRGeometry::getBoundary() const
4856 :
4857 : {
4858 0 : return Boundary();
4859 : }
4860 :
4861 : //! @endcond
4862 :
4863 : /************************************************************************/
4864 : /* OGR_G_Boundary() */
4865 : /************************************************************************/
4866 : /**
4867 : * \brief Compute boundary.
4868 : *
4869 : * A new geometry object is created and returned containing the boundary
4870 : * of the geometry on which the method is invoked.
4871 : *
4872 : * This function is the same as the C++ method OGR_G_Boundary().
4873 : *
4874 : * This function is built on the GEOS library, check it for the definition
4875 : * of the geometry operation.
4876 : * If OGR is built without the GEOS library, this function will always fail,
4877 : * issuing a CPLE_NotSupported error.
4878 : *
4879 : * @param hTarget The Geometry to calculate the boundary of.
4880 : *
4881 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4882 : * or NULL if an error occurs.
4883 : *
4884 : */
4885 6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
4886 :
4887 : {
4888 6 : VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
4889 :
4890 6 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4891 : }
4892 :
4893 : /**
4894 : * \brief Compute boundary (deprecated)
4895 : *
4896 : * @deprecated
4897 : *
4898 : * @see OGR_G_Boundary()
4899 : */
4900 0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
4901 :
4902 : {
4903 0 : VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
4904 :
4905 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4906 : }
4907 :
4908 : /************************************************************************/
4909 : /* Buffer() */
4910 : /************************************************************************/
4911 :
4912 : /**
4913 : * \brief Compute buffer of geometry.
4914 : *
4915 : * Builds a new geometry containing the buffer region around the geometry
4916 : * on which it is invoked. The buffer is a polygon containing the region within
4917 : * the buffer distance of the original geometry.
4918 : *
4919 : * Some buffer sections are properly described as curves, but are converted to
4920 : * approximate polygons. The nQuadSegs parameter can be used to control how
4921 : * many segments should be used to define a 90 degree curve - a quadrant of a
4922 : * circle. A value of 30 is a reasonable default. Large values result in
4923 : * large numbers of vertices in the resulting buffer geometry while small
4924 : * numbers reduce the accuracy of the result.
4925 : *
4926 : * This method is the same as the C function OGR_G_Buffer().
4927 : *
4928 : * This method is built on the GEOS library, check it for the definition
4929 : * of the geometry operation.
4930 : * If OGR is built without the GEOS library, this method will always fail,
4931 : * issuing a CPLE_NotSupported error.
4932 : *
4933 : * @param dfDist the buffer distance to be applied. Should be expressed into
4934 : * the same unit as the coordinates of the geometry.
4935 : *
4936 : * @param nQuadSegs the number of segments used to approximate a 90
4937 : * degree (quadrant) of curvature.
4938 : *
4939 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4940 : */
4941 :
4942 42 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
4943 :
4944 : {
4945 : (void)dfDist;
4946 : (void)nQuadSegs;
4947 : #ifndef HAVE_GEOS
4948 :
4949 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4950 : return nullptr;
4951 :
4952 : #else
4953 :
4954 42 : OGRGeometry *poOGRProduct = nullptr;
4955 :
4956 42 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4957 42 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4958 42 : if (hGeosGeom != nullptr)
4959 : {
4960 : GEOSGeom hGeosProduct =
4961 42 : GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
4962 42 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4963 :
4964 : poOGRProduct =
4965 42 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4966 : }
4967 42 : freeGEOSContext(hGEOSCtxt);
4968 :
4969 42 : return poOGRProduct;
4970 :
4971 : #endif // HAVE_GEOS
4972 : }
4973 :
4974 : /************************************************************************/
4975 : /* OGR_G_Buffer() */
4976 : /************************************************************************/
4977 :
4978 : /**
4979 : * \brief Compute buffer of geometry.
4980 : *
4981 : * Builds a new geometry containing the buffer region around the geometry
4982 : * on which it is invoked. The buffer is a polygon containing the region within
4983 : * the buffer distance of the original geometry.
4984 : *
4985 : * Some buffer sections are properly described as curves, but are converted to
4986 : * approximate polygons. The nQuadSegs parameter can be used to control how
4987 : * many segments should be used to define a 90 degree curve - a quadrant of a
4988 : * circle. A value of 30 is a reasonable default. Large values result in
4989 : * large numbers of vertices in the resulting buffer geometry while small
4990 : * numbers reduce the accuracy of the result.
4991 : *
4992 : * This function is the same as the C++ method OGRGeometry::Buffer().
4993 : *
4994 : * This function is built on the GEOS library, check it for the definition
4995 : * of the geometry operation.
4996 : * If OGR is built without the GEOS library, this function will always fail,
4997 : * issuing a CPLE_NotSupported error.
4998 : *
4999 : * @param hTarget the geometry.
5000 : * @param dfDist the buffer distance to be applied. Should be expressed into
5001 : * the same unit as the coordinates of the geometry.
5002 : *
5003 : * @param nQuadSegs the number of segments used to approximate a 90 degree
5004 : * (quadrant) of curvature.
5005 : *
5006 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5007 : * or NULL if an error occurs.
5008 : */
5009 :
5010 42 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
5011 :
5012 : {
5013 42 : VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
5014 :
5015 42 : return OGRGeometry::ToHandle(
5016 42 : OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
5017 : }
5018 :
5019 : /**
5020 : * \brief Compute buffer of geometry.
5021 : *
5022 : * Builds a new geometry containing the buffer region around the geometry
5023 : * on which it is invoked. The buffer is a polygon containing the region within
5024 : * the buffer distance of the original geometry.
5025 : *
5026 : * This function is built on the GEOS library, check it for the definition
5027 : * of the geometry operation.
5028 : * If OGR is built without the GEOS library, this function will always fail,
5029 : * issuing a CPLE_NotSupported error.
5030 : *
5031 : * The following options are supported. See the GEOS library for more detailed
5032 : * descriptions.
5033 : *
5034 : * <ul>
5035 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5036 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5037 : * <li>MITRE_LIMIT=double</li>
5038 : * <li>QUADRANT_SEGMENTS=int</li>
5039 : * <li>SINGLE_SIDED=YES/NO</li>
5040 : * </ul>
5041 : *
5042 : * This function is the same as the C function OGR_G_BufferEx().
5043 : *
5044 : * @param dfDist the buffer distance to be applied. Should be expressed into
5045 : * the same unit as the coordinates of the geometry.
5046 : * @param papszOptions NULL terminated list of options (may be NULL)
5047 : *
5048 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5049 : *
5050 : * @since GDAL 3.10
5051 : */
5052 :
5053 35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
5054 : CSLConstList papszOptions) const
5055 : {
5056 : (void)dfDist;
5057 : (void)papszOptions;
5058 : #ifndef HAVE_GEOS
5059 :
5060 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5061 : return nullptr;
5062 :
5063 : #else
5064 35 : OGRGeometry *poOGRProduct = nullptr;
5065 35 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5066 :
5067 35 : auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
5068 35 : bool bParamsAreValid = true;
5069 :
5070 166 : for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
5071 : {
5072 131 : if (EQUAL(pszParam, "ENDCAP_STYLE"))
5073 : {
5074 : int nStyle;
5075 25 : if (EQUAL(pszValue, "ROUND"))
5076 : {
5077 22 : nStyle = GEOSBUF_CAP_ROUND;
5078 : }
5079 3 : else if (EQUAL(pszValue, "FLAT"))
5080 : {
5081 1 : nStyle = GEOSBUF_CAP_FLAT;
5082 : }
5083 2 : else if (EQUAL(pszValue, "SQUARE"))
5084 : {
5085 1 : nStyle = GEOSBUF_CAP_SQUARE;
5086 : }
5087 : else
5088 : {
5089 1 : bParamsAreValid = false;
5090 1 : CPLError(CE_Failure, CPLE_NotSupported,
5091 : "Invalid value for ENDCAP_STYLE: %s", pszValue);
5092 2 : break;
5093 : }
5094 :
5095 24 : if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
5096 : {
5097 0 : bParamsAreValid = false;
5098 : }
5099 : }
5100 106 : else if (EQUAL(pszParam, "JOIN_STYLE"))
5101 : {
5102 : int nStyle;
5103 25 : if (EQUAL(pszValue, "ROUND"))
5104 : {
5105 21 : nStyle = GEOSBUF_JOIN_ROUND;
5106 : }
5107 4 : else if (EQUAL(pszValue, "MITRE"))
5108 : {
5109 3 : nStyle = GEOSBUF_JOIN_MITRE;
5110 : }
5111 1 : else if (EQUAL(pszValue, "BEVEL"))
5112 : {
5113 0 : nStyle = GEOSBUF_JOIN_BEVEL;
5114 : }
5115 : else
5116 : {
5117 1 : bParamsAreValid = false;
5118 1 : CPLError(CE_Failure, CPLE_NotSupported,
5119 : "Invalid value for JOIN_STYLE: %s", pszValue);
5120 1 : break;
5121 : }
5122 :
5123 24 : if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
5124 : {
5125 0 : bParamsAreValid = false;
5126 0 : break;
5127 : }
5128 : }
5129 81 : else if (EQUAL(pszParam, "MITRE_LIMIT"))
5130 : {
5131 : try
5132 : {
5133 : std::size_t end;
5134 30 : double dfLimit = std::stod(pszValue, &end);
5135 :
5136 24 : if (end != strlen(pszValue))
5137 : {
5138 0 : throw std::invalid_argument("");
5139 : }
5140 :
5141 24 : if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
5142 : dfLimit))
5143 : {
5144 0 : bParamsAreValid = false;
5145 0 : break;
5146 : }
5147 : }
5148 4 : catch (const std::invalid_argument &)
5149 : {
5150 2 : bParamsAreValid = false;
5151 2 : CPLError(CE_Failure, CPLE_IllegalArg,
5152 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5153 : }
5154 0 : catch (const std::out_of_range &)
5155 : {
5156 0 : bParamsAreValid = false;
5157 0 : CPLError(CE_Failure, CPLE_IllegalArg,
5158 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5159 : }
5160 : }
5161 55 : else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
5162 : {
5163 : try
5164 : {
5165 : std::size_t end;
5166 38 : int nQuadSegs = std::stoi(pszValue, &end, 10);
5167 :
5168 26 : if (end != strlen(pszValue))
5169 : {
5170 0 : throw std::invalid_argument("");
5171 : }
5172 :
5173 26 : if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
5174 : nQuadSegs))
5175 : {
5176 0 : bParamsAreValid = false;
5177 0 : break;
5178 : }
5179 : }
5180 6 : catch (const std::invalid_argument &)
5181 : {
5182 3 : bParamsAreValid = false;
5183 3 : CPLError(CE_Failure, CPLE_IllegalArg,
5184 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5185 : }
5186 2 : catch (const std::out_of_range &)
5187 : {
5188 1 : bParamsAreValid = false;
5189 1 : CPLError(CE_Failure, CPLE_IllegalArg,
5190 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5191 : }
5192 : }
5193 25 : else if (EQUAL(pszParam, "SINGLE_SIDED"))
5194 : {
5195 24 : bool bSingleSided = CPLTestBool(pszValue);
5196 :
5197 24 : if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
5198 : bSingleSided))
5199 : {
5200 0 : bParamsAreValid = false;
5201 0 : break;
5202 : }
5203 : }
5204 : else
5205 : {
5206 1 : bParamsAreValid = false;
5207 1 : CPLError(CE_Failure, CPLE_NotSupported,
5208 : "Unsupported buffer option: %s", pszValue);
5209 : }
5210 : }
5211 :
5212 35 : if (bParamsAreValid)
5213 : {
5214 26 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
5215 26 : if (hGeosGeom != nullptr)
5216 : {
5217 : GEOSGeom hGeosProduct =
5218 26 : GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
5219 26 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
5220 :
5221 26 : if (hGeosProduct != nullptr)
5222 : {
5223 26 : poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
5224 : this, nullptr);
5225 : }
5226 : }
5227 : }
5228 :
5229 35 : GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
5230 35 : freeGEOSContext(hGEOSCtxt);
5231 35 : return poOGRProduct;
5232 : #endif
5233 : }
5234 :
5235 : /**
5236 : * \brief Compute buffer of geometry.
5237 : *
5238 : * Builds a new geometry containing the buffer region around the geometry
5239 : * on which it is invoked. The buffer is a polygon containing the region within
5240 : * the buffer distance of the original geometry.
5241 : *
5242 : * This function is built on the GEOS library, check it for the definition
5243 : * of the geometry operation.
5244 : * If OGR is built without the GEOS library, this function will always fail,
5245 : * issuing a CPLE_NotSupported error.
5246 : *
5247 : * The following options are supported. See the GEOS library for more detailed
5248 : * descriptions.
5249 : *
5250 : * <ul>
5251 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5252 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5253 : * <li>MITRE_LIMIT=double</li>
5254 : * <li>QUADRANT_SEGMENTS=int</li>
5255 : * <li>SINGLE_SIDED=YES/NO</li>
5256 : * </ul>
5257 : *
5258 : * This function is the same as the C++ method OGRGeometry::BufferEx().
5259 : *
5260 : * @param hTarget the geometry.
5261 : * @param dfDist the buffer distance to be applied. Should be expressed into
5262 : * the same unit as the coordinates of the geometry.
5263 : * @param papszOptions NULL terminated list of options (may be NULL)
5264 : *
5265 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5266 : * or NULL if an error occurs.
5267 : *
5268 : * @since GDAL 3.10
5269 : */
5270 :
5271 12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
5272 : CSLConstList papszOptions)
5273 :
5274 : {
5275 12 : VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
5276 :
5277 12 : return OGRGeometry::ToHandle(
5278 12 : OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
5279 : }
5280 :
5281 : /************************************************************************/
5282 : /* Intersection() */
5283 : /************************************************************************/
5284 :
5285 : /**
5286 : * \brief Compute intersection.
5287 : *
5288 : * Generates a new geometry which is the region of intersection of the
5289 : * two geometries operated on. The Intersects() method can be used to test if
5290 : * two geometries intersect.
5291 : *
5292 : * Geometry validity is not checked. In case you are unsure of the validity
5293 : * of the input geometries, call IsValid() before, otherwise the result might
5294 : * be wrong.
5295 : *
5296 : * This method is the same as the C function OGR_G_Intersection().
5297 : *
5298 : * This method is built on the GEOS library, check it for the definition
5299 : * of the geometry operation.
5300 : * If OGR is built without the GEOS library, this method will always fail,
5301 : * issuing a CPLE_NotSupported error.
5302 : *
5303 : * @param poOtherGeom the other geometry intersected with "this" geometry.
5304 : *
5305 : * @return a new geometry to be freed by the caller, or NULL if there is no
5306 : * intersection or if an error occurs.
5307 : *
5308 : */
5309 :
5310 : OGRGeometry *
5311 1949 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5312 :
5313 : {
5314 1949 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5315 : {
5316 : #ifndef HAVE_SFCGAL
5317 :
5318 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5319 0 : return nullptr;
5320 :
5321 : #else
5322 :
5323 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5324 : if (poThis == nullptr)
5325 : return nullptr;
5326 :
5327 : sfcgal_geometry_t *poOther =
5328 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5329 : if (poOther == nullptr)
5330 : {
5331 : sfcgal_geometry_delete(poThis);
5332 : return nullptr;
5333 : }
5334 :
5335 : sfcgal_geometry_t *poRes =
5336 : sfcgal_geometry_intersection_3d(poThis, poOther);
5337 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
5338 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5339 : poOtherGeom->getSpatialReference() != nullptr &&
5340 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5341 : h_prodGeom->assignSpatialReference(getSpatialReference());
5342 :
5343 : sfcgal_geometry_delete(poThis);
5344 : sfcgal_geometry_delete(poOther);
5345 : sfcgal_geometry_delete(poRes);
5346 :
5347 : return h_prodGeom;
5348 :
5349 : #endif
5350 : }
5351 :
5352 : else
5353 : {
5354 : #ifndef HAVE_GEOS
5355 :
5356 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5357 : return nullptr;
5358 :
5359 : #else
5360 1949 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
5361 : #endif /* HAVE_GEOS */
5362 : }
5363 : }
5364 :
5365 : /************************************************************************/
5366 : /* OGR_G_Intersection() */
5367 : /************************************************************************/
5368 :
5369 : /**
5370 : * \brief Compute intersection.
5371 : *
5372 : * Generates a new geometry which is the region of intersection of the
5373 : * two geometries operated on. The OGR_G_Intersects() function can be used to
5374 : * test if two geometries intersect.
5375 : *
5376 : * Geometry validity is not checked. In case you are unsure of the validity
5377 : * of the input geometries, call IsValid() before, otherwise the result might
5378 : * be wrong.
5379 : *
5380 : * This function is the same as the C++ method OGRGeometry::Intersection().
5381 : *
5382 : * This function is built on the GEOS library, check it for the definition
5383 : * of the geometry operation.
5384 : * If OGR is built without the GEOS library, this function will always fail,
5385 : * issuing a CPLE_NotSupported error.
5386 : *
5387 : * @param hThis the geometry.
5388 : * @param hOther the other geometry.
5389 : *
5390 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5391 : * or NULL if there is not intersection of if an error occurs.
5392 : */
5393 :
5394 12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
5395 :
5396 : {
5397 12 : VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
5398 :
5399 24 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
5400 24 : OGRGeometry::FromHandle(hOther)));
5401 : }
5402 :
5403 : /************************************************************************/
5404 : /* Union() */
5405 : /************************************************************************/
5406 :
5407 : /**
5408 : * \brief Compute union.
5409 : *
5410 : * Generates a new geometry which is the region of union of the
5411 : * two geometries operated on.
5412 : *
5413 : * Geometry validity is not checked. In case you are unsure of the validity
5414 : * of the input geometries, call IsValid() before, otherwise the result might
5415 : * be wrong.
5416 : *
5417 : * This method is the same as the C function OGR_G_Union().
5418 : *
5419 : * This method is built on the GEOS library, check it for the definition
5420 : * of the geometry operation.
5421 : * If OGR is built without the GEOS library, this method will always fail,
5422 : * issuing a CPLE_NotSupported error.
5423 : *
5424 : * @param poOtherGeom the other geometry unioned with "this" geometry.
5425 : *
5426 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5427 : */
5428 :
5429 : OGRGeometry *
5430 68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5431 :
5432 : {
5433 68 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5434 : {
5435 : #ifndef HAVE_SFCGAL
5436 :
5437 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5438 0 : return nullptr;
5439 :
5440 : #else
5441 :
5442 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5443 : if (poThis == nullptr)
5444 : return nullptr;
5445 :
5446 : sfcgal_geometry_t *poOther =
5447 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5448 : if (poOther == nullptr)
5449 : {
5450 : sfcgal_geometry_delete(poThis);
5451 : return nullptr;
5452 : }
5453 :
5454 : sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
5455 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5456 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5457 : poOtherGeom->getSpatialReference() != nullptr &&
5458 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5459 : h_prodGeom->assignSpatialReference(getSpatialReference());
5460 :
5461 : sfcgal_geometry_delete(poThis);
5462 : sfcgal_geometry_delete(poOther);
5463 : sfcgal_geometry_delete(poRes);
5464 :
5465 : return h_prodGeom;
5466 :
5467 : #endif
5468 : }
5469 :
5470 : else
5471 : {
5472 : #ifndef HAVE_GEOS
5473 :
5474 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5475 : return nullptr;
5476 :
5477 : #else
5478 68 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
5479 : #endif /* HAVE_GEOS */
5480 : }
5481 : }
5482 :
5483 : /************************************************************************/
5484 : /* OGR_G_Union() */
5485 : /************************************************************************/
5486 :
5487 : /**
5488 : * \brief Compute union.
5489 : *
5490 : * Generates a new geometry which is the region of union of the
5491 : * two geometries operated on.
5492 : *
5493 : * Geometry validity is not checked. In case you are unsure of the validity
5494 : * of the input geometries, call IsValid() before, otherwise the result might
5495 : * be wrong.
5496 : *
5497 : * This function is the same as the C++ method OGRGeometry::Union().
5498 : *
5499 : * This function is built on the GEOS library, check it for the definition
5500 : * of the geometry operation.
5501 : * If OGR is built without the GEOS library, this function will always fail,
5502 : * issuing a CPLE_NotSupported error.
5503 : *
5504 : * @param hThis the geometry.
5505 : * @param hOther the other geometry.
5506 : *
5507 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5508 : * or NULL if an error occurs.
5509 : */
5510 :
5511 10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
5512 :
5513 : {
5514 10 : VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
5515 :
5516 20 : return OGRGeometry::ToHandle(
5517 20 : OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
5518 : }
5519 :
5520 : /************************************************************************/
5521 : /* UnionCascaded() */
5522 : /************************************************************************/
5523 :
5524 : /**
5525 : * \brief Compute union using cascading.
5526 : *
5527 : * Geometry validity is not checked. In case you are unsure of the validity
5528 : * of the input geometries, call IsValid() before, otherwise the result might
5529 : * be wrong.
5530 : *
5531 : * The input geometry must be a MultiPolygon.
5532 : *
5533 : * This method is the same as the C function OGR_G_UnionCascaded().
5534 : *
5535 : * This method is built on the GEOS library, check it for the definition
5536 : * of the geometry operation.
5537 : * If OGR is built without the GEOS library, this method will always fail,
5538 : * issuing a CPLE_NotSupported error.
5539 : *
5540 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5541 : *
5542 : *
5543 : * @deprecated Use UnaryUnion() instead
5544 : */
5545 :
5546 2 : OGRGeometry *OGRGeometry::UnionCascaded() const
5547 :
5548 : {
5549 : #ifndef HAVE_GEOS
5550 :
5551 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5552 : return nullptr;
5553 : #else
5554 :
5555 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5556 : if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
5557 : {
5558 : // GEOS < 3.11 crashes on an empty multipolygon input
5559 : auto poRet = new OGRGeometryCollection();
5560 : poRet->assignSpatialReference(getSpatialReference());
5561 : return poRet;
5562 : }
5563 : #endif
5564 2 : OGRGeometry *poOGRProduct = nullptr;
5565 :
5566 2 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5567 2 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5568 2 : if (hThisGeosGeom != nullptr)
5569 : {
5570 2 : GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
5571 2 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5572 :
5573 : poOGRProduct =
5574 2 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5575 : }
5576 2 : freeGEOSContext(hGEOSCtxt);
5577 :
5578 2 : return poOGRProduct;
5579 :
5580 : #endif // HAVE_GEOS
5581 : }
5582 :
5583 : /************************************************************************/
5584 : /* OGR_G_UnionCascaded() */
5585 : /************************************************************************/
5586 :
5587 : /**
5588 : * \brief Compute union using cascading.
5589 : *
5590 : * Geometry validity is not checked. In case you are unsure of the validity
5591 : * of the input geometries, call IsValid() before, otherwise the result might
5592 : * be wrong.
5593 : *
5594 : * The input geometry must be a MultiPolygon.
5595 : *
5596 : * This function is the same as the C++ method OGRGeometry::UnionCascaded().
5597 : *
5598 : * This function is built on the GEOS library, check it for the definition
5599 : * of the geometry operation.
5600 : * If OGR is built without the GEOS library, this function will always fail,
5601 : * issuing a CPLE_NotSupported error.
5602 : *
5603 : * @param hThis the geometry.
5604 : *
5605 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5606 : * or NULL if an error occurs.
5607 : *
5608 : * @deprecated Use OGR_G_UnaryUnion() instead
5609 : */
5610 :
5611 2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
5612 :
5613 : {
5614 2 : VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
5615 :
5616 2 : return OGRGeometry::ToHandle(
5617 2 : OGRGeometry::FromHandle(hThis)->UnionCascaded());
5618 : }
5619 :
5620 : /************************************************************************/
5621 : /* UnaryUnion() */
5622 : /************************************************************************/
5623 :
5624 : /**
5625 : * \brief Returns the union of all components of a single geometry.
5626 : *
5627 : * Usually used to convert a collection into the smallest set of polygons that
5628 : * cover the same area.
5629 : *
5630 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5631 : *
5632 : * This method is the same as the C function OGR_G_UnaryUnion().
5633 : *
5634 : * This method is built on the GEOS library, check it for the definition
5635 : * of the geometry operation.
5636 : * If OGR is built without the GEOS library, this method will always fail,
5637 : * issuing a CPLE_NotSupported error.
5638 : *
5639 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5640 : *
5641 : * @since GDAL 3.7
5642 : */
5643 :
5644 629 : OGRGeometry *OGRGeometry::UnaryUnion() const
5645 :
5646 : {
5647 : #ifndef HAVE_GEOS
5648 :
5649 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5650 : return nullptr;
5651 : #else
5652 :
5653 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5654 : if (IsEmpty())
5655 : {
5656 : // GEOS < 3.11 crashes on an empty geometry
5657 : auto poRet = new OGRGeometryCollection();
5658 : poRet->assignSpatialReference(getSpatialReference());
5659 : return poRet;
5660 : }
5661 : #endif
5662 629 : OGRGeometry *poOGRProduct = nullptr;
5663 :
5664 629 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5665 629 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5666 629 : if (hThisGeosGeom != nullptr)
5667 : {
5668 629 : GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5669 629 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5670 :
5671 : poOGRProduct =
5672 629 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5673 : }
5674 629 : freeGEOSContext(hGEOSCtxt);
5675 :
5676 629 : return poOGRProduct;
5677 :
5678 : #endif // HAVE_GEOS
5679 : }
5680 :
5681 : /************************************************************************/
5682 : /* OGR_G_UnaryUnion() */
5683 : /************************************************************************/
5684 :
5685 : /**
5686 : * \brief Returns the union of all components of a single geometry.
5687 : *
5688 : * Usually used to convert a collection into the smallest set of polygons that
5689 : * cover the same area.
5690 : *
5691 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5692 : *
5693 : * Geometry validity is not checked. In case you are unsure of the validity
5694 : * of the input geometries, call IsValid() before, otherwise the result might
5695 : * be wrong.
5696 : *
5697 : * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5698 : *
5699 : * This function is built on the GEOS library, check it for the definition
5700 : * of the geometry operation.
5701 : * If OGR is built without the GEOS library, this function will always fail,
5702 : * issuing a CPLE_NotSupported error.
5703 : *
5704 : * @param hThis the geometry.
5705 : *
5706 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5707 : * or NULL if an error occurs.
5708 : *
5709 : * @since GDAL 3.7
5710 : */
5711 :
5712 3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5713 :
5714 : {
5715 3 : VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5716 :
5717 3 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5718 : }
5719 :
5720 : /************************************************************************/
5721 : /* Difference() */
5722 : /************************************************************************/
5723 :
5724 : /**
5725 : * \brief Compute difference.
5726 : *
5727 : * Generates a new geometry which is the region of this geometry with the
5728 : * region of the second geometry removed.
5729 : *
5730 : * Geometry validity is not checked. In case you are unsure of the validity
5731 : * of the input geometries, call IsValid() before, otherwise the result might
5732 : * be wrong.
5733 : *
5734 : * This method is the same as the C function OGR_G_Difference().
5735 : *
5736 : * This method is built on the GEOS library, check it for the definition
5737 : * of the geometry operation.
5738 : * If OGR is built without the GEOS library, this method will always fail,
5739 : * issuing a CPLE_NotSupported error.
5740 : *
5741 : * @param poOtherGeom the other geometry removed from "this" geometry.
5742 : *
5743 : * @return a new geometry to be freed by the caller, or NULL if the difference
5744 : * is empty or if an error occurs.
5745 : */
5746 :
5747 : OGRGeometry *
5748 752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5749 :
5750 : {
5751 752 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5752 : {
5753 : #ifndef HAVE_SFCGAL
5754 :
5755 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5756 0 : return nullptr;
5757 :
5758 : #else
5759 :
5760 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5761 : if (poThis == nullptr)
5762 : return nullptr;
5763 :
5764 : sfcgal_geometry_t *poOther =
5765 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5766 : if (poOther == nullptr)
5767 : {
5768 : sfcgal_geometry_delete(poThis);
5769 : return nullptr;
5770 : }
5771 :
5772 : sfcgal_geometry_t *poRes =
5773 : sfcgal_geometry_difference_3d(poThis, poOther);
5774 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5775 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5776 : poOtherGeom->getSpatialReference() != nullptr &&
5777 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5778 : h_prodGeom->assignSpatialReference(getSpatialReference());
5779 :
5780 : sfcgal_geometry_delete(poThis);
5781 : sfcgal_geometry_delete(poOther);
5782 : sfcgal_geometry_delete(poRes);
5783 :
5784 : return h_prodGeom;
5785 :
5786 : #endif
5787 : }
5788 :
5789 : else
5790 : {
5791 : #ifndef HAVE_GEOS
5792 :
5793 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5794 : return nullptr;
5795 :
5796 : #else
5797 752 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5798 : #endif /* HAVE_GEOS */
5799 : }
5800 : }
5801 :
5802 : /************************************************************************/
5803 : /* OGR_G_Difference() */
5804 : /************************************************************************/
5805 :
5806 : /**
5807 : * \brief Compute difference.
5808 : *
5809 : * Generates a new geometry which is the region of this geometry with the
5810 : * region of the other geometry removed.
5811 : *
5812 : * Geometry validity is not checked. In case you are unsure of the validity
5813 : * of the input geometries, call IsValid() before, otherwise the result might
5814 : * be wrong.
5815 : *
5816 : * This function is the same as the C++ method OGRGeometry::Difference().
5817 : *
5818 : * This function is built on the GEOS library, check it for the definition
5819 : * of the geometry operation.
5820 : * If OGR is built without the GEOS library, this function will always fail,
5821 : * issuing a CPLE_NotSupported error.
5822 : *
5823 : * @param hThis the geometry.
5824 : * @param hOther the other geometry.
5825 : *
5826 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5827 : * or NULL if the difference is empty or if an error occurs.
5828 : */
5829 :
5830 6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5831 :
5832 : {
5833 6 : VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5834 :
5835 12 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5836 12 : OGRGeometry::FromHandle(hOther)));
5837 : }
5838 :
5839 : /************************************************************************/
5840 : /* SymDifference() */
5841 : /************************************************************************/
5842 :
5843 : /**
5844 : * \brief Compute symmetric difference.
5845 : *
5846 : * Generates a new geometry which is the symmetric difference of this
5847 : * geometry and the second geometry passed into the method.
5848 : *
5849 : * Geometry validity is not checked. In case you are unsure of the validity
5850 : * of the input geometries, call IsValid() before, otherwise the result might
5851 : * be wrong.
5852 : *
5853 : * This method is the same as the C function OGR_G_SymDifference().
5854 : *
5855 : * This method is built on the GEOS library, check it for the definition
5856 : * of the geometry operation.
5857 : * If OGR is built without the GEOS library, this method will always fail,
5858 : * issuing a CPLE_NotSupported error.
5859 : *
5860 : * @param poOtherGeom the other geometry.
5861 : *
5862 : * @return a new geometry to be freed by the caller, or NULL if the difference
5863 : * is empty or if an error occurs.
5864 : *
5865 : */
5866 :
5867 7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
5868 :
5869 : {
5870 : (void)poOtherGeom;
5871 7 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5872 : {
5873 : #ifndef HAVE_SFCGAL
5874 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5875 0 : return nullptr;
5876 : #else
5877 : OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5878 : if (poFirstDifference == nullptr)
5879 : return nullptr;
5880 :
5881 : OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5882 : if (poOtherDifference == nullptr)
5883 : {
5884 : delete poFirstDifference;
5885 : return nullptr;
5886 : }
5887 :
5888 : OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5889 : delete poFirstDifference;
5890 : delete poOtherDifference;
5891 : return poSymDiff;
5892 : #endif
5893 : }
5894 :
5895 : #ifndef HAVE_GEOS
5896 :
5897 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5898 : return nullptr;
5899 :
5900 : #else
5901 7 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5902 : #endif // HAVE_GEOS
5903 : }
5904 :
5905 : //! @cond Doxygen_Suppress
5906 : /**
5907 : * \brief Compute symmetric difference (deprecated)
5908 : *
5909 : * @deprecated
5910 : *
5911 : * @see OGRGeometry::SymDifference()
5912 : */
5913 : OGRGeometry *
5914 0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5915 :
5916 : {
5917 0 : return SymDifference(poOtherGeom);
5918 : }
5919 :
5920 : //! @endcond
5921 :
5922 : /************************************************************************/
5923 : /* OGR_G_SymDifference() */
5924 : /************************************************************************/
5925 :
5926 : /**
5927 : * \brief Compute symmetric difference.
5928 : *
5929 : * Generates a new geometry which is the symmetric difference of this
5930 : * geometry and the other geometry.
5931 : *
5932 : * Geometry validity is not checked. In case you are unsure of the validity
5933 : * of the input geometries, call IsValid() before, otherwise the result might
5934 : * be wrong.
5935 : *
5936 : * This function is the same as the C++ method
5937 : * OGRGeometry::SymmetricDifference().
5938 : *
5939 : * This function is built on the GEOS library, check it for the definition
5940 : * of the geometry operation.
5941 : * If OGR is built without the GEOS library, this function will always fail,
5942 : * issuing a CPLE_NotSupported error.
5943 : *
5944 : * @param hThis the geometry.
5945 : * @param hOther the other geometry.
5946 : *
5947 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5948 : * or NULL if the difference is empty or if an error occurs.
5949 : *
5950 : */
5951 :
5952 7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5953 :
5954 : {
5955 7 : VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5956 :
5957 14 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5958 14 : OGRGeometry::FromHandle(hOther)));
5959 : }
5960 :
5961 : /**
5962 : * \brief Compute symmetric difference (deprecated)
5963 : *
5964 : * @deprecated
5965 : *
5966 : * @see OGR_G_SymmetricDifference()
5967 : */
5968 0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5969 :
5970 : {
5971 0 : VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5972 :
5973 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5974 0 : OGRGeometry::FromHandle(hOther)));
5975 : }
5976 :
5977 : /************************************************************************/
5978 : /* Disjoint() */
5979 : /************************************************************************/
5980 :
5981 : /**
5982 : * \brief Test for disjointness.
5983 : *
5984 : * Tests if this geometry and the other passed into the method are disjoint.
5985 : *
5986 : * Geometry validity is not checked. In case you are unsure of the validity
5987 : * of the input geometries, call IsValid() before, otherwise the result might
5988 : * be wrong.
5989 : *
5990 : * This method is the same as the C function OGR_G_Disjoint().
5991 : *
5992 : * This method is built on the GEOS library, check it for the definition
5993 : * of the geometry operation.
5994 : * If OGR is built without the GEOS library, this method will always fail,
5995 : * issuing a CPLE_NotSupported error.
5996 : *
5997 : * @param poOtherGeom the geometry to compare to this geometry.
5998 : *
5999 : * @return TRUE if they are disjoint, otherwise FALSE.
6000 : */
6001 :
6002 8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
6003 :
6004 : {
6005 : (void)poOtherGeom;
6006 : #ifndef HAVE_GEOS
6007 :
6008 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6009 : return FALSE;
6010 :
6011 : #else
6012 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
6013 : #endif // HAVE_GEOS
6014 : }
6015 :
6016 : /************************************************************************/
6017 : /* OGR_G_Disjoint() */
6018 : /************************************************************************/
6019 :
6020 : /**
6021 : * \brief Test for disjointness.
6022 : *
6023 : * Tests if this geometry and the other geometry are disjoint.
6024 : *
6025 : * Geometry validity is not checked. In case you are unsure of the validity
6026 : * of the input geometries, call IsValid() before, otherwise the result might
6027 : * be wrong.
6028 : *
6029 : * This function is the same as the C++ method OGRGeometry::Disjoint().
6030 : *
6031 : * This function is built on the GEOS library, check it for the definition
6032 : * of the geometry operation.
6033 : * If OGR is built without the GEOS library, this function will always fail,
6034 : * issuing a CPLE_NotSupported error.
6035 : *
6036 : * @param hThis the geometry to compare.
6037 : * @param hOther the other geometry to compare.
6038 : *
6039 : * @return TRUE if they are disjoint, otherwise FALSE.
6040 : */
6041 8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
6042 :
6043 : {
6044 8 : VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
6045 :
6046 16 : return OGRGeometry::FromHandle(hThis)->Disjoint(
6047 16 : OGRGeometry::FromHandle(hOther));
6048 : }
6049 :
6050 : /************************************************************************/
6051 : /* Touches() */
6052 : /************************************************************************/
6053 :
6054 : /**
6055 : * \brief Test for touching.
6056 : *
6057 : * Tests if this geometry and the other passed into the method are touching.
6058 : *
6059 : * Geometry validity is not checked. In case you are unsure of the validity
6060 : * of the input geometries, call IsValid() before, otherwise the result might
6061 : * be wrong.
6062 : *
6063 : * This method is the same as the C function OGR_G_Touches().
6064 : *
6065 : * This method is built on the GEOS library, check it for the definition
6066 : * of the geometry operation.
6067 : * If OGR is built without the GEOS library, this method will always fail,
6068 : * issuing a CPLE_NotSupported error.
6069 : *
6070 : * @param poOtherGeom the geometry to compare to this geometry.
6071 : *
6072 : * @return TRUE if they are touching, otherwise FALSE.
6073 : */
6074 :
6075 11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
6076 :
6077 : {
6078 : (void)poOtherGeom;
6079 : #ifndef HAVE_GEOS
6080 :
6081 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6082 : return FALSE;
6083 :
6084 : #else
6085 11 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
6086 : #endif // HAVE_GEOS
6087 : }
6088 :
6089 : /************************************************************************/
6090 : /* OGR_G_Touches() */
6091 : /************************************************************************/
6092 : /**
6093 : * \brief Test for touching.
6094 : *
6095 : * Tests if this geometry and the other geometry are touching.
6096 : *
6097 : * Geometry validity is not checked. In case you are unsure of the validity
6098 : * of the input geometries, call IsValid() before, otherwise the result might
6099 : * be wrong.
6100 : *
6101 : * This function is the same as the C++ method OGRGeometry::Touches().
6102 : *
6103 : * This function is built on the GEOS library, check it for the definition
6104 : * of the geometry operation.
6105 : * If OGR is built without the GEOS library, this function will always fail,
6106 : * issuing a CPLE_NotSupported error.
6107 : *
6108 : * @param hThis the geometry to compare.
6109 : * @param hOther the other geometry to compare.
6110 : *
6111 : * @return TRUE if they are touching, otherwise FALSE.
6112 : */
6113 :
6114 8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
6115 :
6116 : {
6117 8 : VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
6118 :
6119 16 : return OGRGeometry::FromHandle(hThis)->Touches(
6120 16 : OGRGeometry::FromHandle(hOther));
6121 : }
6122 :
6123 : /************************************************************************/
6124 : /* Crosses() */
6125 : /************************************************************************/
6126 :
6127 : /**
6128 : * \brief Test for crossing.
6129 : *
6130 : * Tests if this geometry and the other passed into the method are crossing.
6131 : *
6132 : * Geometry validity is not checked. In case you are unsure of the validity
6133 : * of the input geometries, call IsValid() before, otherwise the result might
6134 : * be wrong.
6135 : *
6136 : * This method is the same as the C function OGR_G_Crosses().
6137 : *
6138 : * This method is built on the GEOS library, check it for the definition
6139 : * of the geometry operation.
6140 : * If OGR is built without the GEOS library, this method will always fail,
6141 : * issuing a CPLE_NotSupported error.
6142 : *
6143 : * @param poOtherGeom the geometry to compare to this geometry.
6144 : *
6145 : * @return TRUE if they are crossing, otherwise FALSE.
6146 : */
6147 :
6148 8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
6149 :
6150 : {
6151 8 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
6152 : {
6153 : #ifndef HAVE_SFCGAL
6154 :
6155 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
6156 0 : return FALSE;
6157 :
6158 : #else
6159 :
6160 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
6161 : if (poThis == nullptr)
6162 : return FALSE;
6163 :
6164 : sfcgal_geometry_t *poOther =
6165 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
6166 : if (poOther == nullptr)
6167 : {
6168 : sfcgal_geometry_delete(poThis);
6169 : return FALSE;
6170 : }
6171 :
6172 : int res = sfcgal_geometry_intersects_3d(poThis, poOther);
6173 :
6174 : sfcgal_geometry_delete(poThis);
6175 : sfcgal_geometry_delete(poOther);
6176 :
6177 : return (res == 1) ? TRUE : FALSE;
6178 :
6179 : #endif
6180 : }
6181 :
6182 : else
6183 : {
6184 :
6185 : #ifndef HAVE_GEOS
6186 :
6187 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6188 : return FALSE;
6189 :
6190 : #else
6191 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
6192 : #endif /* HAVE_GEOS */
6193 : }
6194 : }
6195 :
6196 : /************************************************************************/
6197 : /* OGR_G_Crosses() */
6198 : /************************************************************************/
6199 : /**
6200 : * \brief Test for crossing.
6201 : *
6202 : * Tests if this geometry and the other geometry are crossing.
6203 : *
6204 : * Geometry validity is not checked. In case you are unsure of the validity
6205 : * of the input geometries, call IsValid() before, otherwise the result might
6206 : * be wrong.
6207 : *
6208 : * This function is the same as the C++ method OGRGeometry::Crosses().
6209 : *
6210 : * This function is built on the GEOS library, check it for the definition
6211 : * of the geometry operation.
6212 : * If OGR is built without the GEOS library, this function will always fail,
6213 : * issuing a CPLE_NotSupported error.
6214 : *
6215 : * @param hThis the geometry to compare.
6216 : * @param hOther the other geometry to compare.
6217 : *
6218 : * @return TRUE if they are crossing, otherwise FALSE.
6219 : */
6220 :
6221 8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
6222 :
6223 : {
6224 8 : VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
6225 :
6226 16 : return OGRGeometry::FromHandle(hThis)->Crosses(
6227 16 : OGRGeometry::FromHandle(hOther));
6228 : }
6229 :
6230 : /************************************************************************/
6231 : /* Within() */
6232 : /************************************************************************/
6233 :
6234 : /**
6235 : * \brief Test for containment.
6236 : *
6237 : * Tests if actual geometry object is within the passed geometry.
6238 : *
6239 : * Geometry validity is not checked. In case you are unsure of the validity
6240 : * of the input geometries, call IsValid() before, otherwise the result might
6241 : * be wrong.
6242 : *
6243 : * This method is the same as the C function OGR_G_Within().
6244 : *
6245 : * This method is built on the GEOS library, check it for the definition
6246 : * of the geometry operation.
6247 : * If OGR is built without the GEOS library, this method will always fail,
6248 : * issuing a CPLE_NotSupported error.
6249 : *
6250 : * @param poOtherGeom the geometry to compare to this geometry.
6251 : *
6252 : * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
6253 : */
6254 :
6255 22417 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
6256 :
6257 : {
6258 : (void)poOtherGeom;
6259 : #ifndef HAVE_GEOS
6260 :
6261 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6262 : return FALSE;
6263 :
6264 : #else
6265 22417 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
6266 : #endif // HAVE_GEOS
6267 : }
6268 :
6269 : /************************************************************************/
6270 : /* OGR_G_Within() */
6271 : /************************************************************************/
6272 :
6273 : /**
6274 : * \brief Test for containment.
6275 : *
6276 : * Tests if this geometry is within the other geometry.
6277 : *
6278 : * Geometry validity is not checked. In case you are unsure of the validity
6279 : * of the input geometries, call IsValid() before, otherwise the result might
6280 : * be wrong.
6281 : *
6282 : * This function is the same as the C++ method OGRGeometry::Within().
6283 : *
6284 : * This function is built on the GEOS library, check it for the definition
6285 : * of the geometry operation.
6286 : * If OGR is built without the GEOS library, this function will always fail,
6287 : * issuing a CPLE_NotSupported error.
6288 : *
6289 : * @param hThis the geometry to compare.
6290 : * @param hOther the other geometry to compare.
6291 : *
6292 : * @return TRUE if hThis is within hOther, otherwise FALSE.
6293 : */
6294 7374 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
6295 :
6296 : {
6297 7374 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
6298 :
6299 14748 : return OGRGeometry::FromHandle(hThis)->Within(
6300 14748 : OGRGeometry::FromHandle(hOther));
6301 : }
6302 :
6303 : /************************************************************************/
6304 : /* Contains() */
6305 : /************************************************************************/
6306 :
6307 : /**
6308 : * \brief Test for containment.
6309 : *
6310 : * Tests if actual geometry object contains the passed geometry.
6311 : *
6312 : * Geometry validity is not checked. In case you are unsure of the validity
6313 : * of the input geometries, call IsValid() before, otherwise the result might
6314 : * be wrong.
6315 : *
6316 : * This method is the same as the C function OGR_G_Contains().
6317 : *
6318 : * This method is built on the GEOS library, check it for the definition
6319 : * of the geometry operation.
6320 : * If OGR is built without the GEOS library, this method will always fail,
6321 : * issuing a CPLE_NotSupported error.
6322 : *
6323 : * @param poOtherGeom the geometry to compare to this geometry.
6324 : *
6325 : * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
6326 : */
6327 :
6328 311 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
6329 :
6330 : {
6331 : (void)poOtherGeom;
6332 : #ifndef HAVE_GEOS
6333 :
6334 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6335 : return FALSE;
6336 :
6337 : #else
6338 311 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
6339 : #endif // HAVE_GEOS
6340 : }
6341 :
6342 : /************************************************************************/
6343 : /* OGR_G_Contains() */
6344 : /************************************************************************/
6345 :
6346 : /**
6347 : * \brief Test for containment.
6348 : *
6349 : * Tests if this geometry contains the other geometry.
6350 : *
6351 : * Geometry validity is not checked. In case you are unsure of the validity
6352 : * of the input geometries, call IsValid() before, otherwise the result might
6353 : * be wrong.
6354 : *
6355 : * This function is the same as the C++ method OGRGeometry::Contains().
6356 : *
6357 : * This function is built on the GEOS library, check it for the definition
6358 : * of the geometry operation.
6359 : * If OGR is built without the GEOS library, this function will always fail,
6360 : * issuing a CPLE_NotSupported error.
6361 : *
6362 : * @param hThis the geometry to compare.
6363 : * @param hOther the other geometry to compare.
6364 : *
6365 : * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
6366 : */
6367 10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
6368 :
6369 : {
6370 10 : VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
6371 :
6372 20 : return OGRGeometry::FromHandle(hThis)->Contains(
6373 20 : OGRGeometry::FromHandle(hOther));
6374 : }
6375 :
6376 : /************************************************************************/
6377 : /* Overlaps() */
6378 : /************************************************************************/
6379 :
6380 : /**
6381 : * \brief Test for overlap.
6382 : *
6383 : * Tests if this geometry and the other passed into the method overlap, that is
6384 : * their intersection has a non-zero area.
6385 : *
6386 : * Geometry validity is not checked. In case you are unsure of the validity
6387 : * of the input geometries, call IsValid() before, otherwise the result might
6388 : * be wrong.
6389 : *
6390 : * This method is the same as the C function OGR_G_Overlaps().
6391 : *
6392 : * This method is built on the GEOS library, check it for the definition
6393 : * of the geometry operation.
6394 : * If OGR is built without the GEOS library, this method will always fail,
6395 : * issuing a CPLE_NotSupported error.
6396 : *
6397 : * @param poOtherGeom the geometry to compare to this geometry.
6398 : *
6399 : * @return TRUE if they are overlapping, otherwise FALSE.
6400 : */
6401 :
6402 7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
6403 :
6404 : {
6405 : (void)poOtherGeom;
6406 : #ifndef HAVE_GEOS
6407 :
6408 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6409 : return FALSE;
6410 :
6411 : #else
6412 7 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6413 : #endif // HAVE_GEOS
6414 : }
6415 :
6416 : /************************************************************************/
6417 : /* OGR_G_Overlaps() */
6418 : /************************************************************************/
6419 : /**
6420 : * \brief Test for overlap.
6421 : *
6422 : * Tests if this geometry and the other geometry overlap, that is their
6423 : * intersection has a non-zero area.
6424 : *
6425 : * Geometry validity is not checked. In case you are unsure of the validity
6426 : * of the input geometries, call IsValid() before, otherwise the result might
6427 : * be wrong.
6428 : *
6429 : * This function is the same as the C++ method OGRGeometry::Overlaps().
6430 : *
6431 : * This function is built on the GEOS library, check it for the definition
6432 : * of the geometry operation.
6433 : * If OGR is built without the GEOS library, this function will always fail,
6434 : * issuing a CPLE_NotSupported error.
6435 : *
6436 : * @param hThis the geometry to compare.
6437 : * @param hOther the other geometry to compare.
6438 : *
6439 : * @return TRUE if they are overlapping, otherwise FALSE.
6440 : */
6441 :
6442 7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6443 :
6444 : {
6445 7 : VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6446 :
6447 14 : return OGRGeometry::FromHandle(hThis)->Overlaps(
6448 14 : OGRGeometry::FromHandle(hOther));
6449 : }
6450 :
6451 : /************************************************************************/
6452 : /* closeRings() */
6453 : /************************************************************************/
6454 :
6455 : /**
6456 : * \brief Force rings to be closed.
6457 : *
6458 : * If this geometry, or any contained geometries has polygon rings that
6459 : * are not closed, they will be closed by adding the starting point at
6460 : * the end.
6461 : */
6462 :
6463 1264 : void OGRGeometry::closeRings()
6464 : {
6465 1264 : }
6466 :
6467 : /************************************************************************/
6468 : /* OGR_G_CloseRings() */
6469 : /************************************************************************/
6470 :
6471 : /**
6472 : * \brief Force rings to be closed.
6473 : *
6474 : * If this geometry, or any contained geometries has polygon rings that
6475 : * are not closed, they will be closed by adding the starting point at
6476 : * the end.
6477 : *
6478 : * @param hGeom handle to the geometry.
6479 : */
6480 :
6481 6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
6482 :
6483 : {
6484 6 : VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6485 :
6486 6 : OGRGeometry::FromHandle(hGeom)->closeRings();
6487 : }
6488 :
6489 : /************************************************************************/
6490 : /* Centroid() */
6491 : /************************************************************************/
6492 :
6493 : /**
6494 : * \brief Compute the geometry centroid.
6495 : *
6496 : * The centroid location is applied to the passed in OGRPoint object.
6497 : * The centroid is not necessarily within the geometry.
6498 : *
6499 : * This method relates to the SFCOM ISurface::get_Centroid() method
6500 : * however the current implementation based on GEOS can operate on other
6501 : * geometry types such as multipoint, linestring, geometrycollection such as
6502 : * multipolygons.
6503 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6504 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6505 : * (multipolygons).
6506 : *
6507 : * This function is the same as the C function OGR_G_Centroid().
6508 : *
6509 : * This function is built on the GEOS library, check it for the definition
6510 : * of the geometry operation.
6511 : * If OGR is built without the GEOS library, this function will always fail,
6512 : * issuing a CPLE_NotSupported error.
6513 : *
6514 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6515 : *
6516 : * to OGRPolygon)
6517 : */
6518 :
6519 5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6520 :
6521 : {
6522 5 : if (poPoint == nullptr)
6523 0 : return OGRERR_FAILURE;
6524 :
6525 : #ifndef HAVE_GEOS
6526 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6527 : return OGRERR_FAILURE;
6528 :
6529 : #else
6530 :
6531 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6532 : GEOSGeom hThisGeosGeom =
6533 5 : exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6534 :
6535 5 : if (hThisGeosGeom != nullptr)
6536 : {
6537 5 : GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6538 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6539 :
6540 5 : if (hOtherGeosGeom == nullptr)
6541 : {
6542 0 : freeGEOSContext(hGEOSCtxt);
6543 0 : return OGRERR_FAILURE;
6544 : }
6545 :
6546 : OGRGeometry *poCentroidGeom =
6547 5 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6548 :
6549 5 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6550 :
6551 5 : if (poCentroidGeom == nullptr)
6552 : {
6553 0 : freeGEOSContext(hGEOSCtxt);
6554 0 : return OGRERR_FAILURE;
6555 : }
6556 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6557 : {
6558 0 : delete poCentroidGeom;
6559 0 : freeGEOSContext(hGEOSCtxt);
6560 0 : return OGRERR_FAILURE;
6561 : }
6562 :
6563 5 : if (getSpatialReference() != nullptr)
6564 0 : poCentroidGeom->assignSpatialReference(getSpatialReference());
6565 :
6566 5 : OGRPoint *poCentroid = poCentroidGeom->toPoint();
6567 :
6568 5 : if (!poCentroid->IsEmpty())
6569 : {
6570 4 : poPoint->setX(poCentroid->getX());
6571 4 : poPoint->setY(poCentroid->getY());
6572 : }
6573 : else
6574 : {
6575 1 : poPoint->empty();
6576 : }
6577 :
6578 5 : delete poCentroidGeom;
6579 :
6580 5 : freeGEOSContext(hGEOSCtxt);
6581 5 : return OGRERR_NONE;
6582 : }
6583 : else
6584 : {
6585 0 : freeGEOSContext(hGEOSCtxt);
6586 0 : return OGRERR_FAILURE;
6587 : }
6588 :
6589 : #endif // HAVE_GEOS
6590 : }
6591 :
6592 : /************************************************************************/
6593 : /* OGR_G_Centroid() */
6594 : /************************************************************************/
6595 :
6596 : /**
6597 : * \brief Compute the geometry centroid.
6598 : *
6599 : * The centroid location is applied to the passed in OGRPoint object.
6600 : * The centroid is not necessarily within the geometry.
6601 : *
6602 : * This method relates to the SFCOM ISurface::get_Centroid() method
6603 : * however the current implementation based on GEOS can operate on other
6604 : * geometry types such as multipoint, linestring, geometrycollection such as
6605 : * multipolygons.
6606 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6607 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6608 : * (multipolygons).
6609 : *
6610 : * This function is the same as the C++ method OGRGeometry::Centroid().
6611 : *
6612 : * This function is built on the GEOS library, check it for the definition
6613 : * of the geometry operation.
6614 : * If OGR is built without the GEOS library, this function will always fail,
6615 : * issuing a CPLE_NotSupported error.
6616 : *
6617 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6618 : */
6619 :
6620 5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6621 :
6622 : {
6623 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6624 :
6625 5 : OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6626 5 : if (poCentroidGeom == nullptr)
6627 0 : return OGRERR_FAILURE;
6628 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6629 : {
6630 0 : CPLError(CE_Failure, CPLE_AppDefined,
6631 : "Passed wrong geometry type as centroid argument.");
6632 0 : return OGRERR_FAILURE;
6633 : }
6634 :
6635 5 : return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6636 : }
6637 :
6638 : /************************************************************************/
6639 : /* OGR_G_PointOnSurface() */
6640 : /************************************************************************/
6641 :
6642 : /**
6643 : * \brief Returns a point guaranteed to lie on the surface.
6644 : *
6645 : * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6646 : * however the current implementation based on GEOS can operate on other
6647 : * geometry types than the types that are supported by SQL/MM-Part 3 :
6648 : * surfaces (polygons) and multisurfaces (multipolygons).
6649 : *
6650 : * This method is built on the GEOS library, check it for the definition
6651 : * of the geometry operation.
6652 : * If OGR is built without the GEOS library, this method will always fail,
6653 : * issuing a CPLE_NotSupported error.
6654 : *
6655 : * @param hGeom the geometry to operate on.
6656 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6657 : * or NULL if an error occurs.
6658 : *
6659 : */
6660 :
6661 4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6662 :
6663 : {
6664 4 : VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6665 :
6666 : #ifndef HAVE_GEOS
6667 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6668 : return nullptr;
6669 : #else
6670 :
6671 4 : OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6672 :
6673 4 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6674 4 : GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6675 :
6676 4 : if (hThisGeosGeom != nullptr)
6677 : {
6678 : GEOSGeom hOtherGeosGeom =
6679 4 : GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6680 4 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6681 :
6682 4 : if (hOtherGeosGeom == nullptr)
6683 : {
6684 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6685 0 : return nullptr;
6686 : }
6687 :
6688 : OGRGeometry *poInsidePointGeom =
6689 4 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6690 :
6691 4 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6692 :
6693 4 : if (poInsidePointGeom == nullptr)
6694 : {
6695 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6696 0 : return nullptr;
6697 : }
6698 4 : if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6699 : {
6700 0 : delete poInsidePointGeom;
6701 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6702 0 : return nullptr;
6703 : }
6704 :
6705 4 : if (poThis->getSpatialReference() != nullptr)
6706 0 : poInsidePointGeom->assignSpatialReference(
6707 0 : poThis->getSpatialReference());
6708 :
6709 4 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6710 4 : return OGRGeometry::ToHandle(poInsidePointGeom);
6711 : }
6712 :
6713 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6714 0 : return nullptr;
6715 : #endif
6716 : }
6717 :
6718 : /************************************************************************/
6719 : /* PointOnSurfaceInternal() */
6720 : /************************************************************************/
6721 :
6722 : //! @cond Doxygen_Suppress
6723 0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6724 : {
6725 0 : if (poPoint == nullptr || poPoint->IsEmpty())
6726 0 : return OGRERR_FAILURE;
6727 :
6728 0 : OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6729 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6730 0 : if (hInsidePoint == nullptr)
6731 0 : return OGRERR_FAILURE;
6732 :
6733 0 : OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6734 0 : if (poInsidePoint->IsEmpty())
6735 : {
6736 0 : poPoint->empty();
6737 : }
6738 : else
6739 : {
6740 0 : poPoint->setX(poInsidePoint->getX());
6741 0 : poPoint->setY(poInsidePoint->getY());
6742 : }
6743 :
6744 0 : OGR_G_DestroyGeometry(hInsidePoint);
6745 :
6746 0 : return OGRERR_NONE;
6747 : }
6748 :
6749 : //! @endcond
6750 :
6751 : /************************************************************************/
6752 : /* Simplify() */
6753 : /************************************************************************/
6754 :
6755 : /**
6756 : * \brief Simplify the geometry.
6757 : *
6758 : * This function is the same as the C function OGR_G_Simplify().
6759 : *
6760 : * This function is built on the GEOS library, check it for the definition
6761 : * of the geometry operation.
6762 : * If OGR is built without the GEOS library, this function will always fail,
6763 : * issuing a CPLE_NotSupported error.
6764 : *
6765 : * @param dTolerance the distance tolerance for the simplification.
6766 : *
6767 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6768 : *
6769 : */
6770 :
6771 55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
6772 :
6773 : {
6774 : (void)dTolerance;
6775 : #ifndef HAVE_GEOS
6776 :
6777 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6778 : return nullptr;
6779 :
6780 : #else
6781 55 : OGRGeometry *poOGRProduct = nullptr;
6782 :
6783 55 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6784 55 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6785 55 : if (hThisGeosGeom != nullptr)
6786 : {
6787 : GEOSGeom hGeosProduct =
6788 55 : GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6789 55 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6790 : poOGRProduct =
6791 55 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6792 : }
6793 55 : freeGEOSContext(hGEOSCtxt);
6794 55 : return poOGRProduct;
6795 :
6796 : #endif // HAVE_GEOS
6797 : }
6798 :
6799 : /************************************************************************/
6800 : /* OGR_G_Simplify() */
6801 : /************************************************************************/
6802 :
6803 : /**
6804 : * \brief Compute a simplified geometry.
6805 : *
6806 : * This function is the same as the C++ method OGRGeometry::Simplify().
6807 : *
6808 : * This function is built on the GEOS library, check it for the definition
6809 : * of the geometry operation.
6810 : * If OGR is built without the GEOS library, this function will always fail,
6811 : * issuing a CPLE_NotSupported error.
6812 : *
6813 : * @param hThis the geometry.
6814 : * @param dTolerance the distance tolerance for the simplification.
6815 : *
6816 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6817 : * or NULL if an error occurs.
6818 : *
6819 : */
6820 :
6821 1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6822 :
6823 : {
6824 1 : VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6825 1 : return OGRGeometry::ToHandle(
6826 1 : OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6827 : }
6828 :
6829 : /************************************************************************/
6830 : /* SimplifyPreserveTopology() */
6831 : /************************************************************************/
6832 :
6833 : /**
6834 : * \brief Simplify the geometry while preserving topology.
6835 : *
6836 : * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6837 : *
6838 : * This function is built on the GEOS library, check it for the definition
6839 : * of the geometry operation.
6840 : * If OGR is built without the GEOS library, this function will always fail,
6841 : * issuing a CPLE_NotSupported error.
6842 : *
6843 : * @param dTolerance the distance tolerance for the simplification.
6844 : *
6845 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6846 : *
6847 : */
6848 :
6849 17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
6850 :
6851 : {
6852 : (void)dTolerance;
6853 : #ifndef HAVE_GEOS
6854 :
6855 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6856 : return nullptr;
6857 :
6858 : #else
6859 17 : OGRGeometry *poOGRProduct = nullptr;
6860 :
6861 17 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6862 17 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6863 17 : if (hThisGeosGeom != nullptr)
6864 : {
6865 17 : GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6866 : hGEOSCtxt, hThisGeosGeom, dTolerance);
6867 17 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6868 : poOGRProduct =
6869 17 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6870 : }
6871 17 : freeGEOSContext(hGEOSCtxt);
6872 17 : return poOGRProduct;
6873 :
6874 : #endif // HAVE_GEOS
6875 : }
6876 :
6877 : /************************************************************************/
6878 : /* OGR_G_SimplifyPreserveTopology() */
6879 : /************************************************************************/
6880 :
6881 : /**
6882 : * \brief Simplify the geometry while preserving topology.
6883 : *
6884 : * This function is the same as the C++ method
6885 : * OGRGeometry::SimplifyPreserveTopology().
6886 : *
6887 : * This function is built on the GEOS library, check it for the definition
6888 : * of the geometry operation.
6889 : * If OGR is built without the GEOS library, this function will always fail,
6890 : * issuing a CPLE_NotSupported error.
6891 : *
6892 : * @param hThis the geometry.
6893 : * @param dTolerance the distance tolerance for the simplification.
6894 : *
6895 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6896 : * or NULL if an error occurs.
6897 : *
6898 : */
6899 :
6900 1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6901 : double dTolerance)
6902 :
6903 : {
6904 1 : VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6905 1 : return OGRGeometry::ToHandle(
6906 1 : OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6907 : }
6908 :
6909 : /************************************************************************/
6910 : /* roundCoordinates() */
6911 : /************************************************************************/
6912 :
6913 : /** Round coordinates of the geometry to the specified precision.
6914 : *
6915 : * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6916 : * will return valid geometries, whereas roundCoordinates() does not make
6917 : * such guarantee and may return geometries with invalidities, if they are
6918 : * not compatible of the specified precision. roundCoordinates() supports
6919 : * curve geometries, whereas SetPrecision() does not currently.
6920 : *
6921 : * One use case for roundCoordinates() is to undo the effect of
6922 : * quantizeCoordinates().
6923 : *
6924 : * @param sPrecision Contains the precision requirements.
6925 : * @since GDAL 3.9
6926 : */
6927 39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6928 : {
6929 : struct Rounder : public OGRDefaultGeometryVisitor
6930 : {
6931 : const OGRGeomCoordinatePrecision &m_precision;
6932 : const double m_invXYResolution;
6933 : const double m_invZResolution;
6934 : const double m_invMResolution;
6935 :
6936 39 : explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6937 39 : : m_precision(sPrecisionIn),
6938 39 : m_invXYResolution(m_precision.dfXYResolution !=
6939 : OGRGeomCoordinatePrecision::UNKNOWN
6940 39 : ? 1.0 / m_precision.dfXYResolution
6941 : : 0.0),
6942 39 : m_invZResolution(m_precision.dfZResolution !=
6943 : OGRGeomCoordinatePrecision::UNKNOWN
6944 39 : ? 1.0 / m_precision.dfZResolution
6945 : : 0.0),
6946 39 : m_invMResolution(m_precision.dfMResolution !=
6947 : OGRGeomCoordinatePrecision::UNKNOWN
6948 39 : ? 1.0 / m_precision.dfMResolution
6949 117 : : 0.0)
6950 : {
6951 39 : }
6952 :
6953 : using OGRDefaultGeometryVisitor::visit;
6954 :
6955 379 : void visit(OGRPoint *poPoint) override
6956 : {
6957 379 : if (m_precision.dfXYResolution !=
6958 : OGRGeomCoordinatePrecision::UNKNOWN)
6959 : {
6960 379 : poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6961 379 : m_precision.dfXYResolution);
6962 379 : poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6963 379 : m_precision.dfXYResolution);
6964 : }
6965 758 : if (m_precision.dfZResolution !=
6966 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6967 4 : poPoint->Is3D())
6968 : {
6969 4 : poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6970 4 : m_precision.dfZResolution);
6971 : }
6972 758 : if (m_precision.dfMResolution !=
6973 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6974 4 : poPoint->IsMeasured())
6975 : {
6976 4 : poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6977 4 : m_precision.dfMResolution);
6978 : }
6979 379 : }
6980 : };
6981 :
6982 78 : Rounder rounder(sPrecision);
6983 39 : accept(&rounder);
6984 39 : }
6985 :
6986 : /************************************************************************/
6987 : /* SetPrecision() */
6988 : /************************************************************************/
6989 :
6990 : /** Set the geometry's precision, rounding all its coordinates to the precision
6991 : * grid, and making sure the geometry is still valid.
6992 : *
6993 : * This is a stronger version of roundCoordinates().
6994 : *
6995 : * Note that at time of writing GEOS does no supported curve geometries. So
6996 : * currently if this function is called on such a geometry, OGR will first call
6997 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6998 : * that it is unlikely to yield to the expected result.
6999 : *
7000 : * This function is the same as the C function OGR_G_SetPrecision().
7001 : *
7002 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7003 : * GEOS library. Check it for the definition of the geometry operation.
7004 : * If OGR is built without the GEOS library, this function will always fail,
7005 : * issuing a CPLE_NotSupported error.
7006 : *
7007 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7008 : * precision.
7009 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7010 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7011 : *
7012 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7013 : *
7014 : * @since GDAL 3.9
7015 : */
7016 :
7017 6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
7018 : {
7019 : (void)dfGridSize;
7020 : (void)nFlags;
7021 : #ifndef HAVE_GEOS
7022 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7023 : return nullptr;
7024 :
7025 : #else
7026 6 : OGRGeometry *poOGRProduct = nullptr;
7027 :
7028 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7029 6 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7030 6 : if (hThisGeosGeom != nullptr)
7031 : {
7032 6 : GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
7033 : hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
7034 6 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7035 : poOGRProduct =
7036 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7037 : }
7038 6 : freeGEOSContext(hGEOSCtxt);
7039 6 : return poOGRProduct;
7040 :
7041 : #endif // HAVE_GEOS
7042 : }
7043 :
7044 : /************************************************************************/
7045 : /* OGR_G_SetPrecision() */
7046 : /************************************************************************/
7047 :
7048 : /** Set the geometry's precision, rounding all its coordinates to the precision
7049 : * grid, and making sure the geometry is still valid.
7050 : *
7051 : * This is a stronger version of roundCoordinates().
7052 : *
7053 : * Note that at time of writing GEOS does no supported curve geometries. So
7054 : * currently if this function is called on such a geometry, OGR will first call
7055 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7056 : * that it is unlikely to yield to the expected result.
7057 : *
7058 : * This function is the same as the C++ method OGRGeometry::SetPrecision().
7059 : *
7060 : * This function is built on the GEOSGeom_setPrecision_r() function of the
7061 : * GEOS library. Check it for the definition of the geometry operation.
7062 : * If OGR is built without the GEOS library, this function will always fail,
7063 : * issuing a CPLE_NotSupported error.
7064 : *
7065 : * @param hThis the geometry.
7066 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
7067 : * precision.
7068 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7069 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
7070 : *
7071 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7072 : * or NULL if an error occurs.
7073 : *
7074 : * @since GDAL 3.9
7075 : */
7076 1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
7077 : int nFlags)
7078 : {
7079 1 : VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
7080 1 : return OGRGeometry::ToHandle(
7081 1 : OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
7082 : }
7083 :
7084 : /************************************************************************/
7085 : /* DelaunayTriangulation() */
7086 : /************************************************************************/
7087 :
7088 : /**
7089 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7090 : *
7091 : * This function is the same as the C function OGR_G_DelaunayTriangulation().
7092 : *
7093 : * This function is built on the GEOS library, v3.4 or above.
7094 : * If OGR is built without the GEOS library, this function will always fail,
7095 : * issuing a CPLE_NotSupported error.
7096 : *
7097 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7098 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7099 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7100 : *
7101 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7102 : */
7103 :
7104 : #ifndef HAVE_GEOS
7105 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
7106 : int /*bOnlyEdges*/) const
7107 : {
7108 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7109 : return nullptr;
7110 : }
7111 : #else
7112 1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
7113 : int bOnlyEdges) const
7114 : {
7115 1 : OGRGeometry *poOGRProduct = nullptr;
7116 :
7117 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7118 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7119 1 : if (hThisGeosGeom != nullptr)
7120 : {
7121 1 : GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
7122 : hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
7123 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7124 : poOGRProduct =
7125 1 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7126 : }
7127 1 : freeGEOSContext(hGEOSCtxt);
7128 1 : return poOGRProduct;
7129 : }
7130 : #endif
7131 :
7132 : /************************************************************************/
7133 : /* OGR_G_DelaunayTriangulation() */
7134 : /************************************************************************/
7135 :
7136 : /**
7137 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7138 : *
7139 : * This function is the same as the C++ method
7140 : * OGRGeometry::DelaunayTriangulation().
7141 : *
7142 : * This function is built on the GEOS library, v3.4 or above.
7143 : * If OGR is built without the GEOS library, this function will always fail,
7144 : * issuing a CPLE_NotSupported error.
7145 : *
7146 : * @param hThis the geometry.
7147 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7148 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7149 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7150 : *
7151 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7152 : * or NULL if an error occurs.
7153 : */
7154 :
7155 1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
7156 : int bOnlyEdges)
7157 :
7158 : {
7159 1 : VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
7160 :
7161 1 : return OGRGeometry::ToHandle(
7162 : OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
7163 1 : bOnlyEdges));
7164 : }
7165 :
7166 : /************************************************************************/
7167 : /* ConstrainedDelaunayTriangulation() */
7168 : /************************************************************************/
7169 :
7170 : /**
7171 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7172 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7173 : * geometry collection.
7174 : *
7175 : * This function is the same as the C function
7176 : * OGR_G_ConstrainedDelaunayTriangulation().
7177 : *
7178 : * This function is built on the GEOS library, v3.10 or above.
7179 : * If OGR is built without the GEOS library, this function will always fail,
7180 : * issuing a CPLE_NotSupported error.
7181 : *
7182 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7183 : *
7184 : * @since OGR 3.12
7185 : */
7186 :
7187 3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
7188 : {
7189 : #ifndef HAVE_GEOS
7190 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7191 : return nullptr;
7192 : #elif !(GEOS_VERSION_MAJOR > 3 || \
7193 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
7194 : CPLError(
7195 : CE_Failure, CPLE_NotSupported,
7196 : "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
7197 : return nullptr;
7198 : #else
7199 :
7200 3 : OGRGeometry *poOGRProduct = nullptr;
7201 :
7202 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7203 3 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7204 3 : if (hThisGeosGeom != nullptr)
7205 : {
7206 : GEOSGeom hGeosProduct =
7207 3 : GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
7208 3 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7209 : poOGRProduct =
7210 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7211 : }
7212 3 : freeGEOSContext(hGEOSCtxt);
7213 3 : return poOGRProduct;
7214 : #endif
7215 : }
7216 :
7217 : /************************************************************************/
7218 : /* OGR_G_ConstrainedDelaunayTriangulation() */
7219 : /************************************************************************/
7220 :
7221 : /**
7222 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7223 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7224 : * geometry collection.
7225 : *
7226 : * This function is the same as the C++ method
7227 : * OGRGeometry::ConstrainedDelaunayTriangulation().
7228 : *
7229 : * This function is built on the GEOS library, v3.10 or above.
7230 : * If OGR is built without the GEOS library, this function will always fail,
7231 : * issuing a CPLE_NotSupported error.
7232 : *
7233 : * @param hThis the geometry.
7234 : *
7235 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7236 : * or NULL if an error occurs.
7237 : *
7238 : * @since OGR 3.12
7239 : */
7240 :
7241 3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
7242 : {
7243 3 : VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
7244 :
7245 3 : return OGRGeometry::ToHandle(
7246 3 : OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
7247 : }
7248 :
7249 : /************************************************************************/
7250 : /* Polygonize() */
7251 : /************************************************************************/
7252 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
7253 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
7254 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
7255 : /* AMBIENTALE */
7256 : /************************************************************************/
7257 :
7258 : /**
7259 : * \brief Polygonizes a set of sparse edges.
7260 : *
7261 : * A new geometry object is created and returned containing a collection
7262 : * of reassembled Polygons: NULL will be returned if the input collection
7263 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7264 : * into Polygons is impossible due to topological inconsistencies.
7265 : *
7266 : * This method is the same as the C function OGR_G_Polygonize().
7267 : *
7268 : * This method is built on the GEOS library, check it for the definition
7269 : * of the geometry operation.
7270 : * If OGR is built without the GEOS library, this method will always fail,
7271 : * issuing a CPLE_NotSupported error.
7272 : *
7273 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7274 : *
7275 : */
7276 :
7277 117 : OGRGeometry *OGRGeometry::Polygonize() const
7278 :
7279 : {
7280 : #ifndef HAVE_GEOS
7281 :
7282 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7283 : return nullptr;
7284 :
7285 : #else
7286 :
7287 117 : const OGRGeometryCollection *poColl = nullptr;
7288 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
7289 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
7290 116 : poColl = toGeometryCollection();
7291 : else
7292 1 : return nullptr;
7293 :
7294 116 : const int nCount = poColl->getNumGeometries();
7295 :
7296 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
7297 116 : bool bError = false;
7298 :
7299 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7300 :
7301 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
7302 747 : for (int ig = 0; ig < nCount; ig++)
7303 : {
7304 631 : GEOSGeom hGeosGeom = nullptr;
7305 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
7306 1262 : if (poChild == nullptr ||
7307 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
7308 1 : bError = true;
7309 : else
7310 : {
7311 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
7312 630 : if (hGeosGeom == nullptr)
7313 0 : bError = true;
7314 : }
7315 631 : pahGeosGeomList[ig] = hGeosGeom;
7316 : }
7317 :
7318 116 : if (!bError)
7319 : {
7320 : GEOSGeom hGeosPolygs =
7321 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
7322 :
7323 : poPolygsOGRGeom =
7324 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7325 : }
7326 :
7327 747 : for (int ig = 0; ig < nCount; ig++)
7328 : {
7329 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
7330 631 : if (hGeosGeom != nullptr)
7331 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
7332 : }
7333 116 : delete[] pahGeosGeomList;
7334 116 : freeGEOSContext(hGEOSCtxt);
7335 :
7336 116 : return poPolygsOGRGeom;
7337 :
7338 : #endif // HAVE_GEOS
7339 : }
7340 :
7341 : /************************************************************************/
7342 : /* OGR_G_Polygonize() */
7343 : /************************************************************************/
7344 : /**
7345 : * \brief Polygonizes a set of sparse edges.
7346 : *
7347 : * A new geometry object is created and returned containing a collection
7348 : * of reassembled Polygons: NULL will be returned if the input collection
7349 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7350 : * into Polygons is impossible due to topological inconsistencies.
7351 : *
7352 : * This function is the same as the C++ method OGRGeometry::Polygonize().
7353 : *
7354 : * This function is built on the GEOS library, check it for the definition
7355 : * of the geometry operation.
7356 : * If OGR is built without the GEOS library, this function will always fail,
7357 : * issuing a CPLE_NotSupported error.
7358 : *
7359 : * @param hTarget The Geometry to be polygonized.
7360 : *
7361 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7362 : * or NULL if an error occurs.
7363 : *
7364 : */
7365 :
7366 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
7367 :
7368 : {
7369 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
7370 :
7371 3 : return OGRGeometry::ToHandle(
7372 3 : OGRGeometry::FromHandle(hTarget)->Polygonize());
7373 : }
7374 :
7375 : /************************************************************************/
7376 : /* BuildArea() */
7377 : /************************************************************************/
7378 :
7379 : /**
7380 : * \brief Polygonize a linework assuming inner polygons are holes.
7381 : *
7382 : * This method is the same as the C function OGR_G_BuildArea().
7383 : *
7384 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
7385 : * Additionally, holes are dropped and the result is unified producing
7386 : * a single Polygon or a MultiPolygon.
7387 : *
7388 : * A new geometry object is created and returned: NULL on failure,
7389 : * empty GeometryCollection if the input geometry cannot be polygonized,
7390 : * Polygon or MultiPolygon on success.
7391 : *
7392 : * This method is built on the GEOSBuildArea_r() function of the GEOS
7393 : * library, check it for the definition of the geometry operation.
7394 : * If OGR is built without the GEOS library, this method will always fail,
7395 : * issuing a CPLE_NotSupported error.
7396 : *
7397 : * @return a newly allocated geometry now owned by the caller,
7398 : * or NULL on failure.
7399 : *
7400 : * @since OGR 3.11
7401 : */
7402 :
7403 30 : OGRGeometry *OGRGeometry::BuildArea() const
7404 :
7405 : {
7406 : #ifndef HAVE_GEOS
7407 :
7408 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7409 : return nullptr;
7410 :
7411 : #else
7412 :
7413 30 : OGRGeometry *poPolygsOGRGeom = nullptr;
7414 :
7415 30 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7416 30 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7417 30 : if (hThisGeosGeom != nullptr)
7418 : {
7419 30 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7420 : poPolygsOGRGeom =
7421 30 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7422 30 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7423 : }
7424 30 : freeGEOSContext(hGEOSCtxt);
7425 :
7426 30 : return poPolygsOGRGeom;
7427 :
7428 : #endif // HAVE_GEOS
7429 : }
7430 :
7431 : /************************************************************************/
7432 : /* OGR_G_BuildArea() */
7433 : /************************************************************************/
7434 :
7435 : /**
7436 : * \brief Polygonize a linework assuming inner polygons are holes.
7437 : *
7438 : * This function is the same as the C++ method OGRGeometry::BuildArea().
7439 : *
7440 : * Polygonization is performed similarly to OGR_G_Polygonize().
7441 : * Additionally, holes are dropped and the result is unified producing
7442 : * a single Polygon or a MultiPolygon.
7443 : *
7444 : * A new geometry object is created and returned: NULL on failure,
7445 : * empty GeometryCollection if the input geometry cannot be polygonized,
7446 : * Polygon or MultiPolygon on success.
7447 : *
7448 : * This function is built on the GEOSBuildArea_r() function of the GEOS
7449 : * library, check it for the definition of the geometry operation.
7450 : * If OGR is built without the GEOS library, this function will always fail,
7451 : * issuing a CPLE_NotSupported error.
7452 : *
7453 : * @param hGeom handle on the geometry to polygonize.
7454 : *
7455 : * @return a handle on newly allocated geometry now owned by the caller,
7456 : * or NULL on failure.
7457 : *
7458 : * @since OGR 3.11
7459 : */
7460 :
7461 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7462 :
7463 : {
7464 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7465 :
7466 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7467 : }
7468 :
7469 : /************************************************************************/
7470 : /* swapXY() */
7471 : /************************************************************************/
7472 :
7473 : /**
7474 : * \brief Swap x and y coordinates.
7475 : *
7476 : */
7477 :
7478 0 : void OGRGeometry::swapXY()
7479 :
7480 : {
7481 0 : }
7482 :
7483 : /************************************************************************/
7484 : /* swapXY() */
7485 : /************************************************************************/
7486 :
7487 : /**
7488 : * \brief Swap x and y coordinates.
7489 : *
7490 : * @param hGeom geometry.
7491 : */
7492 :
7493 32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7494 : {
7495 32 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7496 :
7497 32 : OGRGeometry::FromHandle(hGeom)->swapXY();
7498 : }
7499 :
7500 : /************************************************************************/
7501 : /* Prepared geometry API */
7502 : /************************************************************************/
7503 :
7504 : #if defined(HAVE_GEOS)
7505 : struct _OGRPreparedGeometry
7506 : {
7507 : GEOSContextHandle_t hGEOSCtxt;
7508 : GEOSGeom hGEOSGeom;
7509 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7510 : };
7511 : #endif
7512 :
7513 : /************************************************************************/
7514 : /* OGRHasPreparedGeometrySupport() */
7515 : /************************************************************************/
7516 :
7517 : /** Returns if GEOS has prepared geometry support.
7518 : * @return TRUE or FALSE
7519 : */
7520 1 : int OGRHasPreparedGeometrySupport()
7521 : {
7522 : #if defined(HAVE_GEOS)
7523 1 : return TRUE;
7524 : #else
7525 : return FALSE;
7526 : #endif
7527 : }
7528 :
7529 : /************************************************************************/
7530 : /* OGRCreatePreparedGeometry() */
7531 : /************************************************************************/
7532 :
7533 : /** Creates a prepared geometry.
7534 : *
7535 : * To free with OGRDestroyPreparedGeometry()
7536 : *
7537 : * @param hGeom input geometry to prepare.
7538 : * @return handle to a prepared geometry.
7539 : * @since GDAL 3.3
7540 : */
7541 53027 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
7542 : {
7543 : (void)hGeom;
7544 : #if defined(HAVE_GEOS)
7545 53027 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7546 53027 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7547 53027 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7548 53027 : if (hGEOSGeom == nullptr)
7549 : {
7550 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7551 0 : return nullptr;
7552 : }
7553 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7554 53027 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7555 53027 : if (poPreparedGEOSGeom == nullptr)
7556 : {
7557 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7558 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7559 0 : return nullptr;
7560 : }
7561 :
7562 53027 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7563 53027 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7564 53027 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7565 53027 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7566 :
7567 53027 : return poPreparedGeom;
7568 : #else
7569 : return nullptr;
7570 : #endif
7571 : }
7572 :
7573 : /************************************************************************/
7574 : /* OGRDestroyPreparedGeometry() */
7575 : /************************************************************************/
7576 :
7577 : /** Destroys a prepared geometry.
7578 : * @param hPreparedGeom prepared geometry.
7579 : * @since GDAL 3.3
7580 : */
7581 53073 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
7582 : {
7583 : (void)hPreparedGeom;
7584 : #if defined(HAVE_GEOS)
7585 53073 : if (hPreparedGeom != nullptr)
7586 : {
7587 53027 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7588 : hPreparedGeom->poPreparedGEOSGeom);
7589 53027 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7590 53027 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7591 53027 : delete hPreparedGeom;
7592 : }
7593 : #endif
7594 53073 : }
7595 :
7596 : /************************************************************************/
7597 : /* OGRPreparedGeometryIntersects() */
7598 : /************************************************************************/
7599 :
7600 : /** Returns whether a prepared geometry intersects with a geometry.
7601 : * @param hPreparedGeom prepared geometry.
7602 : * @param hOtherGeom other geometry.
7603 : * @return TRUE or FALSE.
7604 : * @since GDAL 3.3
7605 : */
7606 5538 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
7607 : const OGRGeometryH hOtherGeom)
7608 : {
7609 : (void)hPreparedGeom;
7610 : (void)hOtherGeom;
7611 : #if defined(HAVE_GEOS)
7612 5538 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7613 5538 : if (hPreparedGeom == nullptr ||
7614 : poOtherGeom == nullptr
7615 : // The check for IsEmpty() is for buggy GEOS versions.
7616 : // See https://github.com/libgeos/geos/pull/423
7617 11076 : || poOtherGeom->IsEmpty())
7618 : {
7619 1 : return FALSE;
7620 : }
7621 :
7622 : GEOSGeom hGEOSOtherGeom =
7623 5537 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7624 5537 : if (hGEOSOtherGeom == nullptr)
7625 0 : return FALSE;
7626 :
7627 : const bool bRet =
7628 5537 : GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
7629 : hPreparedGeom->poPreparedGEOSGeom,
7630 5537 : hGEOSOtherGeom) == 1;
7631 5537 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7632 :
7633 5537 : return bRet;
7634 : #else
7635 : return FALSE;
7636 : #endif
7637 : }
7638 :
7639 : /** Returns whether a prepared geometry contains a geometry.
7640 : * @param hPreparedGeom prepared geometry.
7641 : * @param hOtherGeom other geometry.
7642 : * @return TRUE or FALSE.
7643 : */
7644 120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
7645 : const OGRGeometryH hOtherGeom)
7646 : {
7647 : (void)hPreparedGeom;
7648 : (void)hOtherGeom;
7649 : #if defined(HAVE_GEOS)
7650 120516 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7651 120516 : if (hPreparedGeom == nullptr ||
7652 : poOtherGeom == nullptr
7653 : // The check for IsEmpty() is for buggy GEOS versions.
7654 : // See https://github.com/libgeos/geos/pull/423
7655 241032 : || poOtherGeom->IsEmpty())
7656 : {
7657 1 : return FALSE;
7658 : }
7659 :
7660 : GEOSGeom hGEOSOtherGeom =
7661 120515 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7662 120515 : if (hGEOSOtherGeom == nullptr)
7663 0 : return FALSE;
7664 :
7665 120515 : const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
7666 : hPreparedGeom->poPreparedGEOSGeom,
7667 120515 : hGEOSOtherGeom) == 1;
7668 120515 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7669 :
7670 120515 : return bRet;
7671 : #else
7672 : return FALSE;
7673 : #endif
7674 : }
7675 :
7676 : /************************************************************************/
7677 : /* OGRGeometryFromEWKB() */
7678 : /************************************************************************/
7679 :
7680 1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7681 : int bIsPostGIS1_EWKB)
7682 :
7683 : {
7684 1445 : OGRGeometry *poGeometry = nullptr;
7685 :
7686 1445 : size_t nWKBSize = 0;
7687 1445 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7688 1445 : if (pabyWKB == nullptr)
7689 0 : return nullptr;
7690 :
7691 : /* -------------------------------------------------------------------- */
7692 : /* Try to ingest the geometry. */
7693 : /* -------------------------------------------------------------------- */
7694 1445 : (void)OGRGeometryFactory::createFromWkb(
7695 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7696 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7697 :
7698 1445 : return poGeometry;
7699 : }
7700 :
7701 : /************************************************************************/
7702 : /* OGRGeometryFromHexEWKB() */
7703 : /************************************************************************/
7704 :
7705 1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7706 : int bIsPostGIS1_EWKB)
7707 :
7708 : {
7709 1443 : if (pszBytea == nullptr)
7710 0 : return nullptr;
7711 :
7712 1443 : int nWKBLength = 0;
7713 1443 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7714 :
7715 : OGRGeometry *poGeometry =
7716 1443 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7717 :
7718 1443 : CPLFree(pabyWKB);
7719 :
7720 1443 : return poGeometry;
7721 : }
7722 :
7723 : /************************************************************************/
7724 : /* OGRGeometryToHexEWKB() */
7725 : /************************************************************************/
7726 :
7727 1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
7728 : int nPostGISMajor, int nPostGISMinor)
7729 : {
7730 1071 : const size_t nWkbSize = poGeometry->WkbSize();
7731 1071 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7732 1071 : if (pabyWKB == nullptr)
7733 0 : return CPLStrdup("");
7734 :
7735 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7736 1815 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7737 626 : poGeometry->IsEmpty())
7738 : {
7739 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7740 : OGRERR_NONE)
7741 : {
7742 0 : CPLFree(pabyWKB);
7743 0 : return CPLStrdup("");
7744 : }
7745 : }
7746 1069 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7747 : (nPostGISMajor < 2)
7748 : ? wkbVariantPostGIS1
7749 1069 : : wkbVariantOldOgc) != OGRERR_NONE)
7750 : {
7751 0 : CPLFree(pabyWKB);
7752 0 : return CPLStrdup("");
7753 : }
7754 :
7755 : // When converting to hex, each byte takes 2 hex characters. In addition
7756 : // we add in 8 characters to represent the SRID integer in hex, and
7757 : // one for a null terminator.
7758 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7759 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7760 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7761 1071 : if (nWkbSize >
7762 1071 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7763 : {
7764 0 : CPLFree(pabyWKB);
7765 0 : return CPLStrdup("");
7766 : }
7767 1071 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7768 1071 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7769 1071 : if (pszTextBuf == nullptr)
7770 : {
7771 0 : CPLFree(pabyWKB);
7772 0 : return CPLStrdup("");
7773 : }
7774 1071 : char *pszTextBufCurrent = pszTextBuf;
7775 :
7776 : // Convert the 1st byte, which is the endianness flag, to hex.
7777 1071 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7778 1071 : strcpy(pszTextBufCurrent, pszHex);
7779 1071 : CPLFree(pszHex);
7780 1071 : pszTextBufCurrent += 2;
7781 :
7782 : // Next, get the geom type which is bytes 2 through 5.
7783 : GUInt32 geomType;
7784 1071 : memcpy(&geomType, pabyWKB + 1, 4);
7785 :
7786 : // Now add the SRID flag if an SRID is provided.
7787 1071 : if (nSRSId > 0)
7788 : {
7789 : // Change the flag to wkbNDR (little) endianness.
7790 541 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7791 541 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7792 : // Apply the flag.
7793 541 : geomType = geomType | nGSrsFlag;
7794 : }
7795 :
7796 : // Now write the geom type which is 4 bytes.
7797 1071 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7798 1071 : strcpy(pszTextBufCurrent, pszHex);
7799 1071 : CPLFree(pszHex);
7800 1071 : pszTextBufCurrent += 8;
7801 :
7802 : // Now include SRID if provided.
7803 1071 : if (nSRSId > 0)
7804 : {
7805 : // Force the srsid to wkbNDR (little) endianness.
7806 541 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7807 541 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7808 : reinterpret_cast<const GByte *>(&nGSRSId));
7809 541 : strcpy(pszTextBufCurrent, pszHex);
7810 541 : CPLFree(pszHex);
7811 541 : pszTextBufCurrent += 8;
7812 : }
7813 :
7814 : // Copy the rest of the data over - subtract
7815 : // 5 since we already copied 5 bytes above.
7816 1071 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7817 1071 : CPLFree(pabyWKB);
7818 1071 : if (!pszHex || pszHex[0] == 0)
7819 : {
7820 0 : CPLFree(pszTextBuf);
7821 0 : return pszHex;
7822 : }
7823 1071 : strcpy(pszTextBufCurrent, pszHex);
7824 1071 : CPLFree(pszHex);
7825 :
7826 1071 : return pszTextBuf;
7827 : }
7828 :
7829 : /************************************************************************/
7830 : /* importPreambleFromWkb() */
7831 : /************************************************************************/
7832 :
7833 : //! @cond Doxygen_Suppress
7834 158893 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7835 : size_t nSize,
7836 : OGRwkbByteOrder &eByteOrder,
7837 : OGRwkbVariant eWkbVariant)
7838 : {
7839 158893 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7840 0 : return OGRERR_NOT_ENOUGH_DATA;
7841 :
7842 : /* -------------------------------------------------------------------- */
7843 : /* Get the byte order byte. */
7844 : /* -------------------------------------------------------------------- */
7845 158893 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7846 158893 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7847 0 : return OGRERR_CORRUPT_DATA;
7848 158893 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7849 :
7850 : /* -------------------------------------------------------------------- */
7851 : /* Get the geometry feature type. */
7852 : /* -------------------------------------------------------------------- */
7853 : OGRwkbGeometryType eGeometryType;
7854 : const OGRErr err =
7855 158893 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7856 158893 : if (wkbHasZ(eGeometryType))
7857 62309 : flags |= OGR_G_3D;
7858 158893 : if (wkbHasM(eGeometryType))
7859 59692 : flags |= OGR_G_MEASURED;
7860 :
7861 158893 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7862 0 : return OGRERR_CORRUPT_DATA;
7863 :
7864 158893 : return OGRERR_NONE;
7865 : }
7866 :
7867 : /************************************************************************/
7868 : /* importPreambleOfCollectionFromWkb() */
7869 : /* */
7870 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7871 : /* OGRCurvePolygon and OGRGeometryCollection. */
7872 : /************************************************************************/
7873 :
7874 75525 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7875 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7876 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7877 : OGRwkbVariant eWkbVariant)
7878 : {
7879 75525 : nGeomCount = 0;
7880 :
7881 : OGRErr eErr =
7882 75525 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7883 75525 : if (eErr != OGRERR_NONE)
7884 0 : return eErr;
7885 :
7886 : /* -------------------------------------------------------------------- */
7887 : /* Clear existing Geoms. */
7888 : /* -------------------------------------------------------------------- */
7889 75525 : int _flags = flags; // flags set in importPreambleFromWkb
7890 75525 : empty(); // may reset flags etc.
7891 :
7892 : // restore
7893 75525 : if (_flags & OGR_G_3D)
7894 59265 : set3D(TRUE);
7895 75525 : if (_flags & OGR_G_MEASURED)
7896 56768 : setMeasured(TRUE);
7897 :
7898 : /* -------------------------------------------------------------------- */
7899 : /* Get the sub-geometry count. */
7900 : /* -------------------------------------------------------------------- */
7901 75525 : memcpy(&nGeomCount, pabyData + 5, 4);
7902 :
7903 75525 : if (OGR_SWAP(eByteOrder))
7904 386 : nGeomCount = CPL_SWAP32(nGeomCount);
7905 :
7906 150916 : if (nGeomCount < 0 ||
7907 75391 : static_cast<size_t>(nGeomCount) >
7908 75391 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7909 : {
7910 134 : nGeomCount = 0;
7911 134 : return OGRERR_CORRUPT_DATA;
7912 : }
7913 75391 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7914 :
7915 : // Each ring has a minimum of nMinSubGeomSize bytes.
7916 75391 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7917 : {
7918 910 : CPLError(CE_Failure, CPLE_AppDefined,
7919 : "Length of input WKB is too small");
7920 910 : nGeomCount = 0;
7921 910 : return OGRERR_NOT_ENOUGH_DATA;
7922 : }
7923 :
7924 74481 : nDataOffset = 9;
7925 74481 : if (nSize != static_cast<size_t>(-1))
7926 : {
7927 74461 : CPLAssert(nSize >= nDataOffset);
7928 74461 : nSize -= nDataOffset;
7929 : }
7930 :
7931 74481 : return OGRERR_NONE;
7932 : }
7933 :
7934 : /************************************************************************/
7935 : /* importCurveCollectionFromWkt() */
7936 : /* */
7937 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7938 : /* OGRMultiCurve. */
7939 : /************************************************************************/
7940 :
7941 1450 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7942 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7943 : int bAllowCurve, int bAllowCompoundCurve,
7944 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7945 :
7946 : {
7947 1450 : int bHasZ = FALSE;
7948 1450 : int bHasM = FALSE;
7949 1450 : bool bIsEmpty = false;
7950 1450 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7951 1450 : flags = 0;
7952 1450 : if (eErr != OGRERR_NONE)
7953 14 : return eErr;
7954 1436 : if (bHasZ)
7955 206 : flags |= OGR_G_3D;
7956 1436 : if (bHasM)
7957 132 : flags |= OGR_G_MEASURED;
7958 1436 : if (bIsEmpty)
7959 111 : return OGRERR_NONE;
7960 :
7961 : char szToken[OGR_WKT_TOKEN_MAX];
7962 1325 : const char *pszInput = *ppszInput;
7963 1325 : eErr = OGRERR_NONE;
7964 :
7965 : // Skip first '('.
7966 1325 : pszInput = OGRWktReadToken(pszInput, szToken);
7967 :
7968 : /* ==================================================================== */
7969 : /* Read each curve in turn. Note that we try to reuse the same */
7970 : /* point list buffer from curve to curve to cut down on */
7971 : /* allocate/deallocate overhead. */
7972 : /* ==================================================================== */
7973 1325 : OGRRawPoint *paoPoints = nullptr;
7974 1325 : int nMaxPoints = 0;
7975 1325 : double *padfZ = nullptr;
7976 :
7977 656 : do
7978 : {
7979 :
7980 : /* --------------------------------------------------------------------
7981 : */
7982 : /* Get the first token, which should be the geometry type. */
7983 : /* --------------------------------------------------------------------
7984 : */
7985 1981 : const char *pszInputBefore = pszInput;
7986 1981 : pszInput = OGRWktReadToken(pszInput, szToken);
7987 :
7988 : /* --------------------------------------------------------------------
7989 : */
7990 : /* Do the import. */
7991 : /* --------------------------------------------------------------------
7992 : */
7993 1981 : OGRCurve *poCurve = nullptr;
7994 1981 : if (EQUAL(szToken, "("))
7995 : {
7996 1427 : OGRLineString *poLine = new OGRLineString();
7997 1427 : poCurve = poLine;
7998 1427 : pszInput = pszInputBefore;
7999 1427 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
8000 : paoPoints, nMaxPoints, padfZ);
8001 : }
8002 554 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
8003 : {
8004 16 : poCurve = new OGRLineString();
8005 : }
8006 : // Accept LINESTRING(), but this is an extension to the BNF, also
8007 : // accepted by PostGIS.
8008 538 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
8009 523 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
8010 523 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
8011 1235 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
8012 159 : (bAllowCompoundCurve &&
8013 159 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
8014 : {
8015 500 : OGRGeometry *poGeom = nullptr;
8016 500 : pszInput = pszInputBefore;
8017 : eErr =
8018 500 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
8019 500 : if (poGeom == nullptr)
8020 : {
8021 1 : eErr = OGRERR_CORRUPT_DATA;
8022 : }
8023 : else
8024 : {
8025 499 : poCurve = poGeom->toCurve();
8026 : }
8027 : }
8028 : else
8029 : {
8030 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
8031 : szToken);
8032 38 : eErr = OGRERR_CORRUPT_DATA;
8033 : }
8034 :
8035 : // If this has M it is an error if poGeom does not have M.
8036 1981 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
8037 0 : eErr = OGRERR_CORRUPT_DATA;
8038 :
8039 1981 : if (eErr == OGRERR_NONE)
8040 1936 : eErr = pfnAddCurveDirectly(this, poCurve);
8041 1981 : if (eErr != OGRERR_NONE)
8042 : {
8043 55 : delete poCurve;
8044 55 : break;
8045 : }
8046 :
8047 : /* --------------------------------------------------------------------
8048 : */
8049 : /* Read the delimiter following the surface. */
8050 : /* --------------------------------------------------------------------
8051 : */
8052 1926 : pszInput = OGRWktReadToken(pszInput, szToken);
8053 1926 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
8054 :
8055 1325 : CPLFree(paoPoints);
8056 1325 : CPLFree(padfZ);
8057 :
8058 : /* -------------------------------------------------------------------- */
8059 : /* freak if we don't get a closing bracket. */
8060 : /* -------------------------------------------------------------------- */
8061 :
8062 1325 : if (eErr != OGRERR_NONE)
8063 55 : return eErr;
8064 :
8065 1270 : if (szToken[0] != ')')
8066 9 : return OGRERR_CORRUPT_DATA;
8067 :
8068 1261 : *ppszInput = pszInput;
8069 1261 : return OGRERR_NONE;
8070 : }
8071 :
8072 : //! @endcond
8073 :
8074 : /************************************************************************/
8075 : /* OGR_GT_Flatten() */
8076 : /************************************************************************/
8077 : /**
8078 : * \brief Returns the 2D geometry type corresponding to the passed geometry
8079 : * type.
8080 : *
8081 : * This function is intended to work with geometry types as old-style 99-402
8082 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
8083 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
8084 : *
8085 : * @param eType Input geometry type
8086 : *
8087 : * @return 2D geometry type corresponding to the passed geometry type.
8088 : *
8089 : */
8090 :
8091 7864010 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
8092 : {
8093 7864010 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
8094 7864010 : if (eType >= 1000 && eType < 2000) // ISO Z.
8095 2776760 : return static_cast<OGRwkbGeometryType>(eType - 1000);
8096 5087240 : if (eType >= 2000 && eType < 3000) // ISO M.
8097 6059 : return static_cast<OGRwkbGeometryType>(eType - 2000);
8098 5081180 : if (eType >= 3000 && eType < 4000) // ISO ZM.
8099 136249 : return static_cast<OGRwkbGeometryType>(eType - 3000);
8100 4944940 : return eType;
8101 : }
8102 :
8103 : /************************************************************************/
8104 : /* OGR_GT_HasZ() */
8105 : /************************************************************************/
8106 : /**
8107 : * \brief Return if the geometry type is a 3D geometry type.
8108 : *
8109 : * @param eType Input geometry type
8110 : *
8111 : * @return TRUE if the geometry type is a 3D geometry type.
8112 : *
8113 : */
8114 :
8115 2074980 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
8116 : {
8117 2074980 : if (eType & wkb25DBitInternalUse)
8118 157128 : return TRUE;
8119 1917860 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
8120 264 : return TRUE;
8121 1917590 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8122 121539 : return TRUE;
8123 1796050 : return FALSE;
8124 : }
8125 :
8126 : /************************************************************************/
8127 : /* OGR_GT_HasM() */
8128 : /************************************************************************/
8129 : /**
8130 : * \brief Return if the geometry type is a measured type.
8131 : *
8132 : * @param eType Input geometry type
8133 : *
8134 : * @return TRUE if the geometry type is a measured type.
8135 : *
8136 : */
8137 :
8138 2134080 : int OGR_GT_HasM(OGRwkbGeometryType eType)
8139 : {
8140 2134080 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
8141 2593 : return TRUE;
8142 2131480 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8143 121195 : return TRUE;
8144 2010290 : return FALSE;
8145 : }
8146 :
8147 : /************************************************************************/
8148 : /* OGR_GT_SetZ() */
8149 : /************************************************************************/
8150 : /**
8151 : * \brief Returns the 3D geometry type corresponding to the passed geometry
8152 : * type.
8153 : *
8154 : * @param eType Input geometry type
8155 : *
8156 : * @return 3D geometry type corresponding to the passed geometry type.
8157 : *
8158 : */
8159 :
8160 5748 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
8161 : {
8162 5748 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
8163 498 : return eType;
8164 5250 : if (eType <= wkbGeometryCollection)
8165 5148 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
8166 : else
8167 102 : return static_cast<OGRwkbGeometryType>(eType + 1000);
8168 : }
8169 :
8170 : /************************************************************************/
8171 : /* OGR_GT_SetM() */
8172 : /************************************************************************/
8173 : /**
8174 : * \brief Returns the measured geometry type corresponding to the passed
8175 : * geometry type.
8176 : *
8177 : * @param eType Input geometry type
8178 : *
8179 : * @return measured geometry type corresponding to the passed geometry type.
8180 : *
8181 : */
8182 :
8183 2013 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
8184 : {
8185 2013 : if (OGR_GT_HasM(eType) || eType == wkbNone)
8186 262 : return eType;
8187 1751 : if (eType & wkb25DBitInternalUse)
8188 : {
8189 717 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
8190 717 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
8191 : }
8192 1751 : return static_cast<OGRwkbGeometryType>(eType + 2000);
8193 : }
8194 :
8195 : /************************************************************************/
8196 : /* OGR_GT_SetModifier() */
8197 : /************************************************************************/
8198 : /**
8199 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
8200 : *
8201 : * @param eType Input geometry type
8202 : * @param bHasZ TRUE if the output geometry type must be 3D.
8203 : * @param bHasM TRUE if the output geometry type must be measured.
8204 : *
8205 : * @return Output geometry type.
8206 : *
8207 : */
8208 :
8209 5564 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
8210 : int bHasM)
8211 : {
8212 5564 : if (bHasZ && bHasM)
8213 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
8214 5222 : else if (bHasM)
8215 333 : return OGR_GT_SetM(wkbFlatten(eType));
8216 4889 : else if (bHasZ)
8217 2110 : return OGR_GT_SetZ(wkbFlatten(eType));
8218 : else
8219 2779 : return wkbFlatten(eType);
8220 : }
8221 :
8222 : /************************************************************************/
8223 : /* OGR_GT_IsSubClassOf) */
8224 : /************************************************************************/
8225 : /**
8226 : * \brief Returns if a type is a subclass of another one
8227 : *
8228 : * @param eType Type.
8229 : * @param eSuperType Super type
8230 : *
8231 : * @return TRUE if eType is a subclass of eSuperType.
8232 : *
8233 : */
8234 :
8235 131862 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
8236 : {
8237 131862 : eSuperType = wkbFlatten(eSuperType);
8238 131862 : eType = wkbFlatten(eType);
8239 :
8240 131862 : if (eSuperType == eType || eSuperType == wkbUnknown)
8241 19389 : return TRUE;
8242 :
8243 112473 : if (eSuperType == wkbGeometryCollection)
8244 31314 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
8245 64628 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
8246 33314 : eType == wkbMultiSurface;
8247 :
8248 79159 : if (eSuperType == wkbCurvePolygon)
8249 21960 : return eType == wkbPolygon || eType == wkbTriangle;
8250 :
8251 57199 : if (eSuperType == wkbMultiCurve)
8252 249 : return eType == wkbMultiLineString;
8253 :
8254 56950 : if (eSuperType == wkbMultiSurface)
8255 288 : return eType == wkbMultiPolygon;
8256 :
8257 56662 : if (eSuperType == wkbCurve)
8258 23388 : return eType == wkbLineString || eType == wkbCircularString ||
8259 23388 : eType == wkbCompoundCurve;
8260 :
8261 33274 : if (eSuperType == wkbSurface)
8262 3521 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
8263 7190 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
8264 3669 : eType == wkbTIN;
8265 :
8266 29605 : if (eSuperType == wkbPolygon)
8267 221 : return eType == wkbTriangle;
8268 :
8269 29384 : if (eSuperType == wkbPolyhedralSurface)
8270 14640 : return eType == wkbTIN;
8271 :
8272 14744 : return FALSE;
8273 : }
8274 :
8275 : /************************************************************************/
8276 : /* OGR_GT_GetCollection() */
8277 : /************************************************************************/
8278 : /**
8279 : * \brief Returns the collection type that can contain the passed geometry type
8280 : *
8281 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
8282 : * wkbLineString->wkbMultiLineString,
8283 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
8284 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
8285 : * wkbCurvePolygon->wkbMultiSurface.
8286 : * In other cases, wkbUnknown is returned
8287 : *
8288 : * Passed Z, M, ZM flag is preserved.
8289 : *
8290 : *
8291 : * @param eType Input geometry type
8292 : *
8293 : * @return the collection type that can contain the passed geometry type or
8294 : * wkbUnknown
8295 : *
8296 : */
8297 :
8298 2709 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
8299 : {
8300 2709 : const bool bHasZ = wkbHasZ(eType);
8301 2709 : const bool bHasM = wkbHasM(eType);
8302 2709 : if (eType == wkbNone)
8303 1 : return wkbNone;
8304 2708 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8305 2708 : if (eFGType == wkbPoint)
8306 67 : eType = wkbMultiPoint;
8307 :
8308 2641 : else if (eFGType == wkbLineString)
8309 187 : eType = wkbMultiLineString;
8310 :
8311 2454 : else if (eFGType == wkbPolygon)
8312 1004 : eType = wkbMultiPolygon;
8313 :
8314 1450 : else if (eFGType == wkbTriangle)
8315 7 : eType = wkbTIN;
8316 :
8317 1443 : else if (OGR_GT_IsCurve(eFGType))
8318 189 : eType = wkbMultiCurve;
8319 :
8320 1254 : else if (OGR_GT_IsSurface(eFGType))
8321 952 : eType = wkbMultiSurface;
8322 :
8323 : else
8324 302 : return wkbUnknown;
8325 :
8326 2406 : if (bHasZ)
8327 3 : eType = wkbSetZ(eType);
8328 2406 : if (bHasM)
8329 3 : eType = wkbSetM(eType);
8330 :
8331 2406 : return eType;
8332 : }
8333 :
8334 : /************************************************************************/
8335 : /* OGR_GT_GetSingle() */
8336 : /************************************************************************/
8337 : /**
8338 : * \brief Returns the non-collection type that be contained in the passed
8339 : * geometry type.
8340 : *
8341 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
8342 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
8343 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
8344 : * wkbGeometryCollection -> wkbUnknown
8345 : * In other cases, the original geometry is returned.
8346 : *
8347 : * Passed Z, M, ZM flag is preserved.
8348 : *
8349 : *
8350 : * @param eType Input geometry type
8351 : *
8352 : * @return the the non-collection type that be contained in the passed geometry
8353 : * type or wkbUnknown
8354 : *
8355 : * @since GDAL 3.11
8356 : */
8357 :
8358 43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
8359 : {
8360 43 : const bool bHasZ = wkbHasZ(eType);
8361 43 : const bool bHasM = wkbHasM(eType);
8362 43 : if (eType == wkbNone)
8363 1 : return wkbNone;
8364 42 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
8365 42 : if (eFGType == wkbMultiPoint)
8366 8 : eType = wkbPoint;
8367 :
8368 34 : else if (eFGType == wkbMultiLineString)
8369 4 : eType = wkbLineString;
8370 :
8371 30 : else if (eFGType == wkbMultiPolygon)
8372 2 : eType = wkbPolygon;
8373 :
8374 28 : else if (eFGType == wkbMultiCurve)
8375 2 : eType = wkbCompoundCurve;
8376 :
8377 26 : else if (eFGType == wkbMultiSurface)
8378 2 : eType = wkbCurvePolygon;
8379 :
8380 24 : else if (eFGType == wkbGeometryCollection)
8381 1 : return wkbUnknown;
8382 :
8383 41 : if (bHasZ)
8384 3 : eType = wkbSetZ(eType);
8385 41 : if (bHasM)
8386 2 : eType = wkbSetM(eType);
8387 :
8388 41 : return eType;
8389 : }
8390 :
8391 : /************************************************************************/
8392 : /* OGR_GT_GetCurve() */
8393 : /************************************************************************/
8394 : /**
8395 : * \brief Returns the curve geometry type that can contain the passed geometry
8396 : * type
8397 : *
8398 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8399 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8400 : * and wkbMultiLineString->wkbMultiCurve.
8401 : * In other cases, the passed geometry is returned.
8402 : *
8403 : * Passed Z, M, ZM flag is preserved.
8404 : *
8405 : * @param eType Input geometry type
8406 : *
8407 : * @return the curve type that can contain the passed geometry type
8408 : *
8409 : */
8410 :
8411 35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8412 : {
8413 35 : const bool bHasZ = wkbHasZ(eType);
8414 35 : const bool bHasM = wkbHasM(eType);
8415 35 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8416 :
8417 35 : if (eFGType == wkbLineString)
8418 3 : eType = wkbCompoundCurve;
8419 :
8420 32 : else if (eFGType == wkbPolygon)
8421 1 : eType = wkbCurvePolygon;
8422 :
8423 31 : else if (eFGType == wkbTriangle)
8424 0 : eType = wkbCurvePolygon;
8425 :
8426 31 : else if (eFGType == wkbMultiLineString)
8427 6 : eType = wkbMultiCurve;
8428 :
8429 25 : else if (eFGType == wkbMultiPolygon)
8430 4 : eType = wkbMultiSurface;
8431 :
8432 35 : if (bHasZ)
8433 4 : eType = wkbSetZ(eType);
8434 35 : if (bHasM)
8435 4 : eType = wkbSetM(eType);
8436 :
8437 35 : return eType;
8438 : }
8439 :
8440 : /************************************************************************/
8441 : /* OGR_GT_GetLinear() */
8442 : /************************************************************************/
8443 : /**
8444 : * \brief Returns the non-curve geometry type that can contain the passed
8445 : * geometry type
8446 : *
8447 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8448 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8449 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8450 : * In other cases, the passed geometry is returned.
8451 : *
8452 : * Passed Z, M, ZM flag is preserved.
8453 : *
8454 : * @param eType Input geometry type
8455 : *
8456 : * @return the non-curve type that can contain the passed geometry type
8457 : *
8458 : */
8459 :
8460 785 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8461 : {
8462 785 : const bool bHasZ = wkbHasZ(eType);
8463 785 : const bool bHasM = wkbHasM(eType);
8464 785 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8465 :
8466 785 : if (OGR_GT_IsCurve(eFGType))
8467 56 : eType = wkbLineString;
8468 :
8469 729 : else if (OGR_GT_IsSurface(eFGType))
8470 43 : eType = wkbPolygon;
8471 :
8472 686 : else if (eFGType == wkbMultiCurve)
8473 186 : eType = wkbMultiLineString;
8474 :
8475 500 : else if (eFGType == wkbMultiSurface)
8476 166 : eType = wkbMultiPolygon;
8477 :
8478 785 : if (bHasZ)
8479 154 : eType = wkbSetZ(eType);
8480 785 : if (bHasM)
8481 101 : eType = wkbSetM(eType);
8482 :
8483 785 : return eType;
8484 : }
8485 :
8486 : /************************************************************************/
8487 : /* OGR_GT_IsCurve() */
8488 : /************************************************************************/
8489 :
8490 : /**
8491 : * \brief Return if a geometry type is an instance of Curve
8492 : *
8493 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8494 : * and their Z/M/ZM variant.
8495 : *
8496 : * @param eGeomType the geometry type
8497 : * @return TRUE if the geometry type is an instance of Curve
8498 : *
8499 : */
8500 :
8501 23379 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8502 : {
8503 23379 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8504 : }
8505 :
8506 : /************************************************************************/
8507 : /* OGR_GT_IsSurface() */
8508 : /************************************************************************/
8509 :
8510 : /**
8511 : * \brief Return if a geometry type is an instance of Surface
8512 : *
8513 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8514 : * and their Z/M/ZM variant.
8515 : *
8516 : * @param eGeomType the geometry type
8517 : * @return TRUE if the geometry type is an instance of Surface
8518 : *
8519 : */
8520 :
8521 3663 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8522 : {
8523 3663 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8524 : }
8525 :
8526 : /************************************************************************/
8527 : /* OGR_GT_IsNonLinear() */
8528 : /************************************************************************/
8529 :
8530 : /**
8531 : * \brief Return if a geometry type is a non-linear geometry type.
8532 : *
8533 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8534 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8535 : * Z/M variants.
8536 : *
8537 : * @param eGeomType the geometry type
8538 : * @return TRUE if the geometry type is a non-linear geometry type.
8539 : *
8540 : */
8541 :
8542 114999 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8543 : {
8544 114999 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8545 114991 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8546 114918 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8547 229990 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8548 114999 : eFGeomType == wkbMultiSurface;
8549 : }
8550 :
8551 : /************************************************************************/
8552 : /* CastToError() */
8553 : /************************************************************************/
8554 :
8555 : //! @cond Doxygen_Suppress
8556 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8557 : {
8558 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8559 0 : poGeom->getGeometryName());
8560 0 : delete poGeom;
8561 0 : return nullptr;
8562 : }
8563 :
8564 : //! @endcond
8565 :
8566 : /************************************************************************/
8567 : /* OGRexportToSFCGAL() */
8568 : /************************************************************************/
8569 :
8570 : //! @cond Doxygen_Suppress
8571 : sfcgal_geometry_t *
8572 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8573 : {
8574 : #ifdef HAVE_SFCGAL
8575 :
8576 : sfcgal_init();
8577 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8578 :
8579 : const auto exportToSFCGALViaWKB =
8580 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8581 : {
8582 : if (!geom)
8583 : return nullptr;
8584 :
8585 : // Get WKB size and allocate buffer
8586 : size_t nSize = geom->WkbSize();
8587 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8588 :
8589 : // Set export options with NDR byte order
8590 : OGRwkbExportOptions oOptions;
8591 : oOptions.eByteOrder = wkbNDR;
8592 : // and ISO to avoid wkb25DBit for Z geometries
8593 : oOptions.eWkbVariant = wkbVariantIso;
8594 :
8595 : // Export to WKB
8596 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8597 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8598 : {
8599 : sfcgalGeom = sfcgal_io_read_wkb(
8600 : reinterpret_cast<const char *>(pabyWkb), nSize);
8601 : }
8602 :
8603 : CPLFree(pabyWkb);
8604 : return sfcgalGeom;
8605 : };
8606 :
8607 : // Handle special cases
8608 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8609 : {
8610 : std::unique_ptr<OGRLineString> poLS(
8611 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8612 : return exportToSFCGALViaWKB(poLS.get());
8613 : }
8614 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8615 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8616 : {
8617 : std::unique_ptr<OGRLineString> poLS(
8618 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8619 : ->toLineString());
8620 : return exportToSFCGALViaWKB(poLS.get());
8621 : }
8622 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8623 : {
8624 : std::unique_ptr<OGRPolygon> poPolygon(
8625 : OGRGeometryFactory::forceToPolygon(
8626 : poGeom->clone()->toCurvePolygon())
8627 : ->toPolygon());
8628 : return exportToSFCGALViaWKB(poPolygon.get());
8629 : }
8630 : else
8631 : {
8632 : // Default case - direct export
8633 : return exportToSFCGALViaWKB(poGeom);
8634 : }
8635 : #else
8636 : char *buffer = nullptr;
8637 :
8638 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8639 : // Polygon
8640 :
8641 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8642 : {
8643 : // cast it to LineString and get the WKT
8644 : std::unique_ptr<OGRLineString> poLS(
8645 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8646 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8647 : {
8648 : sfcgal_geometry_t *_geometry =
8649 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8650 : CPLFree(buffer);
8651 : return _geometry;
8652 : }
8653 : else
8654 : {
8655 : CPLFree(buffer);
8656 : return nullptr;
8657 : }
8658 : }
8659 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8660 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8661 : {
8662 : // convert it to LineString and get the WKT
8663 : std::unique_ptr<OGRLineString> poLS(
8664 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8665 : ->toLineString());
8666 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8667 : {
8668 : sfcgal_geometry_t *_geometry =
8669 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8670 : CPLFree(buffer);
8671 : return _geometry;
8672 : }
8673 : else
8674 : {
8675 : CPLFree(buffer);
8676 : return nullptr;
8677 : }
8678 : }
8679 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8680 : {
8681 : // convert it to Polygon and get the WKT
8682 : std::unique_ptr<OGRPolygon> poPolygon(
8683 : OGRGeometryFactory::forceToPolygon(
8684 : poGeom->clone()->toCurvePolygon())
8685 : ->toPolygon());
8686 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8687 : {
8688 : sfcgal_geometry_t *_geometry =
8689 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8690 : CPLFree(buffer);
8691 : return _geometry;
8692 : }
8693 : else
8694 : {
8695 : CPLFree(buffer);
8696 : return nullptr;
8697 : }
8698 : }
8699 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8700 : {
8701 : sfcgal_geometry_t *_geometry =
8702 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8703 : CPLFree(buffer);
8704 : return _geometry;
8705 : }
8706 : else
8707 : {
8708 : CPLFree(buffer);
8709 : return nullptr;
8710 : }
8711 : #endif
8712 : #else
8713 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8714 0 : return nullptr;
8715 : #endif
8716 : }
8717 :
8718 : //! @endcond
8719 :
8720 : /************************************************************************/
8721 : /* SFCGALexportToOGR() */
8722 : /************************************************************************/
8723 :
8724 : //! @cond Doxygen_Suppress
8725 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8726 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8727 : {
8728 : #ifdef HAVE_SFCGAL
8729 : if (geometry == nullptr)
8730 : return nullptr;
8731 :
8732 : sfcgal_init();
8733 : char *pabySFCGAL = nullptr;
8734 : size_t nLength = 0;
8735 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8736 :
8737 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8738 :
8739 : if (pabySFCGAL == nullptr || nLength == 0)
8740 : return nullptr;
8741 :
8742 : OGRGeometry *poGeom = nullptr;
8743 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8744 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8745 : nLength);
8746 :
8747 : free(pabySFCGAL);
8748 :
8749 : if (eErr == OGRERR_NONE)
8750 : {
8751 : return poGeom;
8752 : }
8753 : else
8754 : {
8755 : return nullptr;
8756 : }
8757 : #else
8758 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8759 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8760 : memcpy(pszWKT, pabySFCGAL, nLength);
8761 : pszWKT[nLength] = 0;
8762 : free(pabySFCGAL);
8763 :
8764 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8765 :
8766 : OGRGeometry *poGeom = nullptr;
8767 : if (geom_type == SFCGAL_TYPE_POINT)
8768 : {
8769 : poGeom = new OGRPoint();
8770 : }
8771 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8772 : {
8773 : poGeom = new OGRLineString();
8774 : }
8775 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8776 : {
8777 : poGeom = new OGRPolygon();
8778 : }
8779 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8780 : {
8781 : poGeom = new OGRMultiPoint();
8782 : }
8783 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8784 : {
8785 : poGeom = new OGRMultiLineString();
8786 : }
8787 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8788 : {
8789 : poGeom = new OGRMultiPolygon();
8790 : }
8791 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8792 : {
8793 : poGeom = new OGRGeometryCollection();
8794 : }
8795 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8796 : {
8797 : poGeom = new OGRTriangle();
8798 : }
8799 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8800 : {
8801 : poGeom = new OGRPolyhedralSurface();
8802 : }
8803 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8804 : {
8805 : poGeom = new OGRTriangulatedSurface();
8806 : }
8807 : else
8808 : {
8809 : CPLFree(pszWKT);
8810 : return nullptr;
8811 : }
8812 :
8813 : const char *pszWKTTmp = pszWKT;
8814 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8815 : {
8816 : CPLFree(pszWKT);
8817 : return poGeom;
8818 : }
8819 : else
8820 : {
8821 : delete poGeom;
8822 : CPLFree(pszWKT);
8823 : return nullptr;
8824 : }
8825 : #endif
8826 : #else
8827 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8828 0 : return nullptr;
8829 : #endif
8830 : }
8831 :
8832 : //! @endcond
8833 :
8834 : //! @cond Doxygen_Suppress
8835 8383 : bool OGRGeometry::IsSFCGALCompatible() const
8836 : {
8837 8383 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8838 8383 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8839 : eGType == wkbTIN)
8840 : {
8841 3 : return TRUE;
8842 : }
8843 8380 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8844 : {
8845 13 : const OGRGeometryCollection *poGC = toGeometryCollection();
8846 13 : bool bIsSFCGALCompatible = false;
8847 13 : for (auto &&poSubGeom : *poGC)
8848 : {
8849 : OGRwkbGeometryType eSubGeomType =
8850 13 : wkbFlatten(poSubGeom->getGeometryType());
8851 13 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8852 : {
8853 0 : bIsSFCGALCompatible = true;
8854 : }
8855 13 : else if (eSubGeomType != wkbMultiPolygon)
8856 : {
8857 13 : bIsSFCGALCompatible = false;
8858 13 : break;
8859 : }
8860 : }
8861 13 : return bIsSFCGALCompatible;
8862 : }
8863 8367 : return FALSE;
8864 : }
8865 :
8866 : //! @endcond
8867 :
8868 : /************************************************************************/
8869 : /* roundCoordinatesIEEE754() */
8870 : /************************************************************************/
8871 :
8872 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8873 : * double-precision binary representation.
8874 : *
8875 : * Determines the number of bits (N) required to represent a coordinate value
8876 : * with a specified number of digits after the decimal point, and then sets all
8877 : * but the N most significant bits to zero. The resulting coordinate value will
8878 : * still round to the original value (e.g. after roundCoordinates()), but will
8879 : * have improved compressiblity.
8880 : *
8881 : * @param options Contains the precision requirements.
8882 : * @since GDAL 3.9
8883 : */
8884 1 : void OGRGeometry::roundCoordinatesIEEE754(
8885 : const OGRGeomCoordinateBinaryPrecision &options)
8886 : {
8887 : struct Quantizer : public OGRDefaultGeometryVisitor
8888 : {
8889 : const OGRGeomCoordinateBinaryPrecision &m_options;
8890 :
8891 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8892 1 : : m_options(optionsIn)
8893 : {
8894 1 : }
8895 :
8896 : using OGRDefaultGeometryVisitor::visit;
8897 :
8898 3 : void visit(OGRPoint *poPoint) override
8899 : {
8900 3 : if (m_options.nXYBitPrecision != INT_MIN)
8901 : {
8902 : uint64_t i;
8903 : double d;
8904 3 : d = poPoint->getX();
8905 3 : memcpy(&i, &d, sizeof(i));
8906 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8907 3 : memcpy(&d, &i, sizeof(i));
8908 3 : poPoint->setX(d);
8909 3 : d = poPoint->getY();
8910 3 : memcpy(&i, &d, sizeof(i));
8911 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8912 3 : memcpy(&d, &i, sizeof(i));
8913 3 : poPoint->setY(d);
8914 : }
8915 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8916 : {
8917 : uint64_t i;
8918 : double d;
8919 3 : d = poPoint->getZ();
8920 3 : memcpy(&i, &d, sizeof(i));
8921 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8922 3 : memcpy(&d, &i, sizeof(i));
8923 3 : poPoint->setZ(d);
8924 : }
8925 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8926 : {
8927 : uint64_t i;
8928 : double d;
8929 3 : d = poPoint->getM();
8930 3 : memcpy(&i, &d, sizeof(i));
8931 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8932 3 : memcpy(&d, &i, sizeof(i));
8933 3 : poPoint->setM(d);
8934 : }
8935 3 : }
8936 : };
8937 :
8938 2 : Quantizer quantizer(options);
8939 1 : accept(&quantizer);
8940 1 : }
8941 :
8942 : /************************************************************************/
8943 : /* visit() */
8944 : /************************************************************************/
8945 :
8946 105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8947 : {
8948 1248 : for (auto &&oPoint : *poGeom)
8949 : {
8950 1143 : oPoint.accept(this);
8951 : }
8952 105 : }
8953 :
8954 104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8955 : {
8956 104 : _visit(poGeom);
8957 104 : }
8958 :
8959 80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8960 : {
8961 80 : visit(poGeom->toUpperClass());
8962 80 : }
8963 :
8964 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8965 : {
8966 1 : _visit(poGeom);
8967 1 : }
8968 :
8969 78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8970 : {
8971 159 : for (auto &&poSubGeom : *poGeom)
8972 81 : poSubGeom->accept(this);
8973 78 : }
8974 :
8975 77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8976 : {
8977 77 : visit(poGeom->toUpperClass());
8978 77 : }
8979 :
8980 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8981 : {
8982 1 : visit(poGeom->toUpperClass());
8983 1 : }
8984 :
8985 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8986 : {
8987 8 : visit(poGeom->toUpperClass());
8988 8 : }
8989 :
8990 14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8991 : {
8992 14 : visit(poGeom->toUpperClass());
8993 14 : }
8994 :
8995 26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
8996 : {
8997 75 : for (auto &&poSubGeom : *poGeom)
8998 49 : poSubGeom->accept(this);
8999 26 : }
9000 :
9001 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
9002 : {
9003 2 : for (auto &&poSubGeom : *poGeom)
9004 1 : poSubGeom->accept(this);
9005 1 : }
9006 :
9007 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
9008 : {
9009 1 : visit(poGeom->toUpperClass());
9010 1 : }
9011 :
9012 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
9013 : {
9014 1 : visit(poGeom->toUpperClass());
9015 1 : }
9016 :
9017 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
9018 : {
9019 2 : visit(poGeom->toUpperClass());
9020 2 : }
9021 :
9022 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
9023 : {
9024 4 : for (auto &&poSubGeom : *poGeom)
9025 2 : poSubGeom->accept(this);
9026 2 : }
9027 :
9028 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
9029 : {
9030 1 : visit(poGeom->toUpperClass());
9031 1 : }
9032 :
9033 127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
9034 : {
9035 2988 : for (auto &&oPoint : *poGeom)
9036 : {
9037 2861 : oPoint.accept(this);
9038 : }
9039 127 : }
9040 :
9041 121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
9042 : {
9043 121 : _visit(poGeom);
9044 121 : }
9045 :
9046 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
9047 : {
9048 110 : visit(poGeom->toUpperClass());
9049 110 : }
9050 :
9051 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
9052 : {
9053 6 : _visit(poGeom);
9054 6 : }
9055 :
9056 112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
9057 : {
9058 225 : for (auto &&poSubGeom : *poGeom)
9059 113 : poSubGeom->accept(this);
9060 112 : }
9061 :
9062 109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
9063 : {
9064 109 : visit(poGeom->toUpperClass());
9065 109 : }
9066 :
9067 64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
9068 : {
9069 64 : visit(poGeom->toUpperClass());
9070 64 : }
9071 :
9072 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
9073 : {
9074 1 : visit(poGeom->toUpperClass());
9075 1 : }
9076 :
9077 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
9078 : {
9079 1 : visit(poGeom->toUpperClass());
9080 1 : }
9081 :
9082 69 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
9083 : {
9084 325 : for (auto &&poSubGeom : *poGeom)
9085 256 : poSubGeom->accept(this);
9086 69 : }
9087 :
9088 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
9089 : {
9090 14 : for (auto &&poSubGeom : *poGeom)
9091 11 : poSubGeom->accept(this);
9092 3 : }
9093 :
9094 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
9095 : {
9096 1 : visit(poGeom->toUpperClass());
9097 1 : }
9098 :
9099 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
9100 : {
9101 1 : visit(poGeom->toUpperClass());
9102 1 : }
9103 :
9104 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
9105 : {
9106 2 : visit(poGeom->toUpperClass());
9107 2 : }
9108 :
9109 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
9110 : {
9111 4 : for (auto &&poSubGeom : *poGeom)
9112 2 : poSubGeom->accept(this);
9113 2 : }
9114 :
9115 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
9116 : {
9117 1 : visit(poGeom->toUpperClass());
9118 1 : }
9119 :
9120 : /************************************************************************/
9121 : /* OGRGeometryUniquePtrDeleter */
9122 : /************************************************************************/
9123 :
9124 : //! @cond Doxygen_Suppress
9125 1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
9126 : {
9127 1333 : delete poGeom;
9128 1333 : }
9129 :
9130 : //! @endcond
9131 :
9132 : /************************************************************************/
9133 : /* OGRPreparedGeometryUniquePtrDeleter */
9134 : /************************************************************************/
9135 :
9136 : //! @cond Doxygen_Suppress
9137 144 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
9138 : OGRPreparedGeometry *poPreparedGeom) const
9139 : {
9140 144 : OGRDestroyPreparedGeometry(poPreparedGeom);
9141 144 : }
9142 :
9143 : //! @endcond
9144 :
9145 : /************************************************************************/
9146 : /* HomogenizeDimensionalityWith() */
9147 : /************************************************************************/
9148 :
9149 : //! @cond Doxygen_Suppress
9150 3299500 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
9151 : {
9152 3299500 : if (poOtherGeom->Is3D() && !Is3D())
9153 1328980 : set3D(TRUE);
9154 :
9155 3299500 : if (poOtherGeom->IsMeasured() && !IsMeasured())
9156 851 : setMeasured(TRUE);
9157 :
9158 3299500 : if (!poOtherGeom->Is3D() && Is3D())
9159 298 : poOtherGeom->set3D(TRUE);
9160 :
9161 3299500 : if (!poOtherGeom->IsMeasured() && IsMeasured())
9162 41 : poOtherGeom->setMeasured(TRUE);
9163 3299500 : }
9164 :
9165 : //! @endcond
9166 :
9167 : /************************************************************************/
9168 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
9169 : /************************************************************************/
9170 :
9171 : /** Set binary precision options from resolution.
9172 : *
9173 : * @since GDAL 3.9
9174 : */
9175 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
9176 : const OGRGeomCoordinatePrecision &prec)
9177 : {
9178 16 : if (prec.dfXYResolution != 0)
9179 : {
9180 16 : nXYBitPrecision =
9181 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
9182 : }
9183 16 : if (prec.dfZResolution != 0)
9184 : {
9185 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
9186 : }
9187 16 : if (prec.dfMResolution != 0)
9188 : {
9189 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
9190 : }
9191 16 : }
9192 :
9193 : /************************************************************************/
9194 : /* OGRwkbExportOptionsCreate() */
9195 : /************************************************************************/
9196 :
9197 : /**
9198 : * \brief Create geometry WKB export options.
9199 : *
9200 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
9201 : *
9202 : * @return object to be freed with OGRwkbExportOptionsDestroy().
9203 : * @since GDAL 3.9
9204 : */
9205 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
9206 : {
9207 2 : return new OGRwkbExportOptions;
9208 : }
9209 :
9210 : /************************************************************************/
9211 : /* OGRwkbExportOptionsDestroy() */
9212 : /************************************************************************/
9213 :
9214 : /**
9215 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
9216 : *
9217 : * @param psOptions WKB export options
9218 : * @since GDAL 3.9
9219 : */
9220 :
9221 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
9222 : {
9223 2 : delete psOptions;
9224 2 : }
9225 :
9226 : /************************************************************************/
9227 : /* OGRwkbExportOptionsSetByteOrder() */
9228 : /************************************************************************/
9229 :
9230 : /**
9231 : * \brief Set the WKB byte order.
9232 : *
9233 : * @param psOptions WKB export options
9234 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
9235 : * Intel)
9236 : * @since GDAL 3.9
9237 : */
9238 :
9239 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
9240 : OGRwkbByteOrder eByteOrder)
9241 : {
9242 1 : psOptions->eByteOrder = eByteOrder;
9243 1 : }
9244 :
9245 : /************************************************************************/
9246 : /* OGRwkbExportOptionsSetVariant() */
9247 : /************************************************************************/
9248 :
9249 : /**
9250 : * \brief Set the WKB variant
9251 : *
9252 : * @param psOptions WKB export options
9253 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
9254 : * wkbVariantPostGIS1
9255 : * @since GDAL 3.9
9256 : */
9257 :
9258 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
9259 : OGRwkbVariant eWkbVariant)
9260 : {
9261 1 : psOptions->eWkbVariant = eWkbVariant;
9262 1 : }
9263 :
9264 : /************************************************************************/
9265 : /* OGRwkbExportOptionsSetPrecision() */
9266 : /************************************************************************/
9267 :
9268 : /**
9269 : * \brief Set precision options
9270 : *
9271 : * @param psOptions WKB export options
9272 : * @param hPrecisionOptions Precision options (might be null to reset them)
9273 : * @since GDAL 3.9
9274 : */
9275 :
9276 1 : void OGRwkbExportOptionsSetPrecision(
9277 : OGRwkbExportOptions *psOptions,
9278 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
9279 : {
9280 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
9281 1 : if (hPrecisionOptions)
9282 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
9283 1 : }
9284 :
9285 : /************************************************************************/
9286 : /* IsRectangle() */
9287 : /************************************************************************/
9288 :
9289 : /**
9290 : * \brief Returns whether the geometry is a polygon with 4 corners forming
9291 : * a rectangle.
9292 : *
9293 : * @since GDAL 3.10
9294 : */
9295 53030 : bool OGRGeometry::IsRectangle() const
9296 : {
9297 53030 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
9298 352 : return false;
9299 :
9300 52678 : const OGRPolygon *poPoly = toPolygon();
9301 :
9302 52678 : if (poPoly->getNumInteriorRings() != 0)
9303 27 : return false;
9304 :
9305 52651 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
9306 52651 : if (!poRing)
9307 4 : return false;
9308 :
9309 52647 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
9310 206 : return false;
9311 :
9312 : // If the ring has 5 points, the last should be the first.
9313 104825 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
9314 52384 : poRing->getY(0) != poRing->getY(4)))
9315 1 : return false;
9316 :
9317 : // Polygon with first segment in "y" direction.
9318 104163 : if (poRing->getX(0) == poRing->getX(1) &&
9319 103445 : poRing->getY(1) == poRing->getY(2) &&
9320 155885 : poRing->getX(2) == poRing->getX(3) &&
9321 51722 : poRing->getY(3) == poRing->getY(0))
9322 51722 : return true;
9323 :
9324 : // Polygon with first segment in "x" direction.
9325 1355 : if (poRing->getY(0) == poRing->getY(1) &&
9326 1274 : poRing->getX(1) == poRing->getX(2) &&
9327 1992 : poRing->getY(2) == poRing->getY(3) &&
9328 637 : poRing->getX(3) == poRing->getX(0))
9329 637 : return true;
9330 :
9331 81 : return false;
9332 : }
9333 :
9334 : /************************************************************************/
9335 : /* hasEmptyParts() */
9336 : /************************************************************************/
9337 :
9338 : /**
9339 : * \brief Returns whether a geometry has empty parts/rings.
9340 : *
9341 : * Returns true if removeEmptyParts() will modify the geometry.
9342 : *
9343 : * This is different from IsEmpty().
9344 : *
9345 : * @since GDAL 3.10
9346 : */
9347 103 : bool OGRGeometry::hasEmptyParts() const
9348 : {
9349 103 : return false;
9350 : }
9351 :
9352 : /************************************************************************/
9353 : /* removeEmptyParts() */
9354 : /************************************************************************/
9355 :
9356 : /**
9357 : * \brief Remove empty parts/rings from this geometry.
9358 : *
9359 : * @since GDAL 3.10
9360 : */
9361 17 : void OGRGeometry::removeEmptyParts()
9362 : {
9363 17 : }
9364 :
9365 : /************************************************************************/
9366 : /* ~IOGRGeometryVisitor() */
9367 : /************************************************************************/
9368 :
9369 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
9370 :
9371 : /************************************************************************/
9372 : /* ~IOGRConstGeometryVisitor() */
9373 : /************************************************************************/
9374 :
9375 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
|