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_multiproc.h"
32 : #include "cpl_string.h"
33 : #include "ogr_api.h"
34 : #include "ogr_core.h"
35 : #include "ogr_geos.h"
36 : #include "ogr_sfcgal.h"
37 : #include "ogr_libs.h"
38 : #include "ogr_p.h"
39 : #include "ogr_spatialref.h"
40 : #include "ogr_srs_api.h"
41 : #include "ogr_wkb.h"
42 :
43 : #define SFCGAL_MAKE_VERSION(major, minor, patch) \
44 : ((major) * 10000 + (minor) * 100 + (patch))
45 : #define SFCGAL_VERSION \
46 : SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR, \
47 : SFCGAL_VERSION_PATCH)
48 :
49 : //! @cond Doxygen_Suppress
50 : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
51 : //! @endcond
52 :
53 : #ifdef HAVE_GEOS
54 103 : static void OGRGEOSErrorHandler(const char *fmt, ...)
55 : {
56 : va_list args;
57 :
58 103 : va_start(args, fmt);
59 103 : CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
60 103 : va_end(args);
61 103 : }
62 :
63 5527 : static void OGRGEOSWarningHandler(const char *fmt, ...)
64 : {
65 : va_list args;
66 :
67 5527 : va_start(args, fmt);
68 5527 : CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
69 5527 : va_end(args);
70 5527 : }
71 : #endif
72 :
73 : /************************************************************************/
74 : /* OGRWktOptions() */
75 : /************************************************************************/
76 :
77 11212 : int OGRWktOptions::getDefaultPrecision()
78 : {
79 11212 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
80 : }
81 :
82 11308 : bool OGRWktOptions::getDefaultRound()
83 : {
84 11308 : return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
85 : }
86 :
87 : /************************************************************************/
88 : /* OGRGeometry() */
89 : /************************************************************************/
90 :
91 : OGRGeometry::OGRGeometry() = default;
92 :
93 : /************************************************************************/
94 : /* OGRGeometry( const OGRGeometry& ) */
95 : /************************************************************************/
96 :
97 : /**
98 : * \brief Copy constructor.
99 : */
100 :
101 1825680 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
102 1825680 : : poSRS(other.poSRS), flags(other.flags)
103 : {
104 1825680 : if (poSRS != nullptr)
105 74903 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
106 1825680 : }
107 :
108 : /************************************************************************/
109 : /* OGRGeometry( OGRGeometry&& ) */
110 : /************************************************************************/
111 :
112 : /**
113 : * \brief Move constructor.
114 : *
115 : * @since GDAL 3.11
116 : */
117 :
118 181600 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
119 181600 : : poSRS(other.poSRS), flags(other.flags)
120 : {
121 181600 : other.poSRS = nullptr;
122 181600 : }
123 :
124 : /************************************************************************/
125 : /* ~OGRGeometry() */
126 : /************************************************************************/
127 :
128 26081700 : OGRGeometry::~OGRGeometry()
129 :
130 : {
131 13040900 : if (poSRS != nullptr)
132 3582280 : const_cast<OGRSpatialReference *>(poSRS)->Release();
133 13040900 : }
134 :
135 : /************************************************************************/
136 : /* operator=( const OGRGeometry&) */
137 : /************************************************************************/
138 :
139 : /**
140 : * \brief Assignment operator.
141 : */
142 :
143 1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
144 : {
145 1211 : if (this != &other)
146 : {
147 1211 : empty();
148 1211 : assignSpatialReference(other.getSpatialReference());
149 1211 : flags = other.flags;
150 : }
151 1211 : return *this;
152 : }
153 :
154 : /************************************************************************/
155 : /* operator=( OGRGeometry&&) */
156 : /************************************************************************/
157 :
158 : /**
159 : * \brief Move assignment operator.
160 : *
161 : * @since GDAL 3.11
162 : */
163 :
164 123835 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
165 : {
166 123835 : if (this != &other)
167 : {
168 123835 : poSRS = other.poSRS;
169 123835 : other.poSRS = nullptr;
170 123835 : flags = other.flags;
171 : }
172 123835 : return *this;
173 : }
174 :
175 : /************************************************************************/
176 : /* dumpReadable() */
177 : /************************************************************************/
178 :
179 : /**
180 : * \brief Dump geometry in well known text format to indicated output file.
181 : *
182 : * A few options can be defined to change the default dump :
183 : * <ul>
184 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
185 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
186 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
187 : * </ul>
188 : *
189 : * This method is the same as the C function OGR_G_DumpReadable().
190 : *
191 : * @param fp the text file to write the geometry to.
192 : * @param pszPrefix the prefix to put on each line of output.
193 : * @param papszOptions NULL terminated list of options (may be NULL)
194 : */
195 :
196 0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
197 : CSLConstList papszOptions) const
198 :
199 : {
200 0 : if (fp == nullptr)
201 0 : fp = stdout;
202 :
203 0 : const auto osStr = dumpReadable(pszPrefix, papszOptions);
204 0 : fprintf(fp, "%s", osStr.c_str());
205 0 : }
206 :
207 : /************************************************************************/
208 : /* dumpReadable() */
209 : /************************************************************************/
210 :
211 : /**
212 : * \brief Dump geometry in well known text format to indicated output file.
213 : *
214 : * A few options can be defined to change the default dump :
215 : * <ul>
216 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
217 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
218 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
219 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
220 : * in WKT (added in GDAL 3.9)</li>
221 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
222 : * WKT (added in GDAL 3.9)</li>
223 : * </ul>
224 : *
225 : * @param pszPrefix the prefix to put on each line of output.
226 : * @param papszOptions NULL terminated list of options (may be NULL)
227 : * @return a string with the geometry representation.
228 : * @since GDAL 3.7
229 : */
230 :
231 307 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
232 : CSLConstList papszOptions) const
233 :
234 : {
235 307 : if (pszPrefix == nullptr)
236 306 : pszPrefix = "";
237 :
238 307 : std::string osRet;
239 :
240 : const auto exportToWktWithOpts =
241 2044 : [this, pszPrefix, papszOptions, &osRet](bool bIso)
242 : {
243 292 : OGRErr err(OGRERR_NONE);
244 292 : OGRWktOptions opts;
245 292 : if (const char *pszXYPrecision =
246 292 : CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
247 : {
248 1 : opts.format = OGRWktFormat::F;
249 1 : opts.xyPrecision = atoi(pszXYPrecision);
250 : }
251 292 : if (const char *pszZPrecision =
252 292 : CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
253 : {
254 1 : opts.format = OGRWktFormat::F;
255 1 : opts.zPrecision = atoi(pszZPrecision);
256 : }
257 292 : if (bIso)
258 292 : opts.variant = wkbVariantIso;
259 584 : std::string wkt = exportToWkt(opts, &err);
260 292 : if (err == OGRERR_NONE)
261 : {
262 292 : osRet = pszPrefix;
263 292 : osRet += wkt.data();
264 292 : osRet += '\n';
265 : }
266 292 : };
267 :
268 : const char *pszDisplayGeometry =
269 307 : CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
270 307 : if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
271 : {
272 15 : osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
273 15 : switch (getGeometryType())
274 : {
275 1 : case wkbUnknown:
276 : case wkbNone:
277 : case wkbPoint:
278 : case wkbPoint25D:
279 : case wkbPointM:
280 : case wkbPointZM:
281 1 : break;
282 0 : case wkbPolyhedralSurface:
283 : case wkbTIN:
284 : case wkbPolyhedralSurfaceZ:
285 : case wkbTINZ:
286 : case wkbPolyhedralSurfaceM:
287 : case wkbTINM:
288 : case wkbPolyhedralSurfaceZM:
289 : case wkbTINZM:
290 : {
291 0 : const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
292 : osRet +=
293 0 : CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
294 0 : for (auto &&poSubGeom : *poPS)
295 : {
296 0 : osRet += pszPrefix;
297 0 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
298 : }
299 0 : break;
300 : }
301 0 : case wkbLineString:
302 : case wkbLineString25D:
303 : case wkbLineStringM:
304 : case wkbLineStringZM:
305 : case wkbCircularString:
306 : case wkbCircularStringZ:
307 : case wkbCircularStringM:
308 : case wkbCircularStringZM:
309 : {
310 0 : const OGRSimpleCurve *poSC = toSimpleCurve();
311 0 : osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
312 0 : break;
313 : }
314 11 : case wkbPolygon:
315 : case wkbTriangle:
316 : case wkbTriangleZ:
317 : case wkbTriangleM:
318 : case wkbTriangleZM:
319 : case wkbPolygon25D:
320 : case wkbPolygonM:
321 : case wkbPolygonZM:
322 : case wkbCurvePolygon:
323 : case wkbCurvePolygonZ:
324 : case wkbCurvePolygonM:
325 : case wkbCurvePolygonZM:
326 : {
327 11 : const OGRCurvePolygon *poPoly = toCurvePolygon();
328 11 : const OGRCurve *poRing = poPoly->getExteriorRingCurve();
329 11 : const int nRings = poPoly->getNumInteriorRings();
330 11 : if (poRing == nullptr)
331 : {
332 0 : osRet += "empty";
333 : }
334 : else
335 : {
336 11 : osRet += CPLOPrintf("%d points", poRing->getNumPoints());
337 11 : if (wkbFlatten(poRing->getGeometryType()) ==
338 : wkbCompoundCurve)
339 : {
340 0 : osRet += " (";
341 0 : osRet += poRing->dumpReadable(nullptr, papszOptions);
342 0 : osRet += ")";
343 : }
344 11 : if (nRings)
345 : {
346 1 : osRet += CPLOPrintf(", %d inner rings (", nRings);
347 8 : for (int ir = 0; ir < nRings; ir++)
348 : {
349 7 : poRing = poPoly->getInteriorRingCurve(ir);
350 7 : if (ir)
351 6 : osRet += ", ";
352 : osRet +=
353 7 : CPLOPrintf("%d points", poRing->getNumPoints());
354 7 : if (wkbFlatten(poRing->getGeometryType()) ==
355 : wkbCompoundCurve)
356 : {
357 2 : osRet += " (";
358 : osRet +=
359 2 : poRing->dumpReadable(nullptr, papszOptions);
360 2 : osRet += ")";
361 : }
362 : }
363 1 : osRet += ")";
364 : }
365 : }
366 11 : osRet += "\n";
367 11 : break;
368 : }
369 2 : case wkbCompoundCurve:
370 : case wkbCompoundCurveZ:
371 : case wkbCompoundCurveM:
372 : case wkbCompoundCurveZM:
373 : {
374 2 : const OGRCompoundCurve *poCC = toCompoundCurve();
375 2 : if (poCC->getNumCurves() == 0)
376 : {
377 0 : osRet += "empty";
378 : }
379 : else
380 : {
381 6 : for (int i = 0; i < poCC->getNumCurves(); i++)
382 : {
383 4 : if (i)
384 2 : osRet += ", ";
385 : osRet +=
386 8 : CPLOPrintf("%s (%d points)",
387 4 : poCC->getCurve(i)->getGeometryName(),
388 8 : poCC->getCurve(i)->getNumPoints());
389 : }
390 : }
391 2 : break;
392 : }
393 :
394 1 : case wkbMultiPoint:
395 : case wkbMultiLineString:
396 : case wkbMultiPolygon:
397 : case wkbMultiCurve:
398 : case wkbMultiSurface:
399 : case wkbGeometryCollection:
400 : case wkbMultiPoint25D:
401 : case wkbMultiLineString25D:
402 : case wkbMultiPolygon25D:
403 : case wkbMultiCurveZ:
404 : case wkbMultiSurfaceZ:
405 : case wkbGeometryCollection25D:
406 : case wkbMultiPointM:
407 : case wkbMultiLineStringM:
408 : case wkbMultiPolygonM:
409 : case wkbMultiCurveM:
410 : case wkbMultiSurfaceM:
411 : case wkbGeometryCollectionM:
412 : case wkbMultiPointZM:
413 : case wkbMultiLineStringZM:
414 : case wkbMultiPolygonZM:
415 : case wkbMultiCurveZM:
416 : case wkbMultiSurfaceZM:
417 : case wkbGeometryCollectionZM:
418 : {
419 1 : const OGRGeometryCollection *poColl = toGeometryCollection();
420 : osRet +=
421 1 : CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
422 2 : for (auto &&poSubGeom : *poColl)
423 : {
424 1 : osRet += pszPrefix;
425 1 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
426 : }
427 1 : break;
428 : }
429 0 : case wkbLinearRing:
430 : case wkbCurve:
431 : case wkbSurface:
432 : case wkbCurveZ:
433 : case wkbSurfaceZ:
434 : case wkbCurveM:
435 : case wkbSurfaceM:
436 : case wkbCurveZM:
437 : case wkbSurfaceZM:
438 0 : break;
439 15 : }
440 : }
441 292 : else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
442 : {
443 0 : exportToWktWithOpts(/* bIso=*/false);
444 : }
445 292 : else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
446 0 : EQUAL(pszDisplayGeometry, "ISO_WKT"))
447 : {
448 292 : exportToWktWithOpts(/* bIso=*/true);
449 : }
450 :
451 614 : return osRet;
452 : }
453 :
454 : /************************************************************************/
455 : /* OGR_G_DumpReadable() */
456 : /************************************************************************/
457 : /**
458 : * \brief Dump geometry in well known text format to indicated output file.
459 : *
460 : * This method is the same as the CPP method OGRGeometry::dumpReadable.
461 : *
462 : * @param hGeom handle on the geometry to dump.
463 : * @param fp the text file to write the geometry to.
464 : * @param pszPrefix the prefix to put on each line of output.
465 : */
466 :
467 0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
468 :
469 : {
470 0 : VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
471 :
472 0 : OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
473 : }
474 :
475 : /************************************************************************/
476 : /* assignSpatialReference() */
477 : /************************************************************************/
478 :
479 : /**
480 : * \brief Assign spatial reference to this object.
481 : *
482 : * Any existing spatial reference
483 : * is replaced, but under no circumstances does this result in the object
484 : * being reprojected. It is just changing the interpretation of the existing
485 : * geometry. Note that assigning a spatial reference increments the
486 : * reference count on the OGRSpatialReference, but does not copy it.
487 : *
488 : * This will also assign the spatial reference to
489 : * potential sub-geometries of the geometry (OGRGeometryCollection,
490 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
491 : * derived classes).
492 : *
493 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
494 : *
495 : * This method is the same as the C function OGR_G_AssignSpatialReference().
496 : *
497 : * @param poSR new spatial reference system to apply.
498 : */
499 :
500 5658160 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
501 :
502 : {
503 : // Do in that order to properly handle poSR == poSRS
504 5658160 : if (poSR != nullptr)
505 3543380 : const_cast<OGRSpatialReference *>(poSR)->Reference();
506 5658160 : if (poSRS != nullptr)
507 36007 : const_cast<OGRSpatialReference *>(poSRS)->Release();
508 :
509 5658160 : poSRS = poSR;
510 5658160 : }
511 :
512 : /************************************************************************/
513 : /* OGR_G_AssignSpatialReference() */
514 : /************************************************************************/
515 : /**
516 : * \brief Assign spatial reference to this object.
517 : *
518 : * Any existing spatial reference
519 : * is replaced, but under no circumstances does this result in the object
520 : * being reprojected. It is just changing the interpretation of the existing
521 : * geometry. Note that assigning a spatial reference increments the
522 : * reference count on the OGRSpatialReference, but does not copy it.
523 : *
524 : * This will also assign the spatial reference to
525 : * potential sub-geometries of the geometry (OGRGeometryCollection,
526 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
527 : * derived classes).
528 : *
529 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
530 : *
531 : * This function is the same as the CPP method
532 : * OGRGeometry::assignSpatialReference.
533 : *
534 : * @param hGeom handle on the geometry to apply the new spatial reference
535 : * system.
536 : * @param hSRS handle on the new spatial reference system to apply.
537 : */
538 :
539 80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
540 :
541 : {
542 80 : VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
543 :
544 160 : OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
545 80 : OGRSpatialReference::FromHandle(hSRS));
546 : }
547 :
548 : /************************************************************************/
549 : /* Intersects() */
550 : /************************************************************************/
551 :
552 : /**
553 : * \brief Do these features intersect?
554 : *
555 : * Determines whether two geometries intersect. If GEOS is enabled, then
556 : * this is done in rigorous fashion otherwise TRUE is returned if the
557 : * envelopes (bounding boxes) of the two geometries overlap.
558 : *
559 : * The poOtherGeom argument may be safely NULL, but in this case the method
560 : * will always return TRUE. That is, a NULL geometry is treated as being
561 : * everywhere.
562 : *
563 : * This method is the same as the C function OGR_G_Intersects().
564 : *
565 : * @param poOtherGeom the other geometry to test against.
566 : *
567 : * @return TRUE if the geometries intersect, otherwise FALSE.
568 : */
569 :
570 44 : OGRBoolean OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
571 :
572 : {
573 44 : if (poOtherGeom == nullptr)
574 0 : return TRUE;
575 :
576 44 : OGREnvelope oEnv1;
577 44 : getEnvelope(&oEnv1);
578 :
579 44 : OGREnvelope oEnv2;
580 44 : poOtherGeom->getEnvelope(&oEnv2);
581 :
582 44 : if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
583 26 : oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
584 18 : return FALSE;
585 :
586 : #ifndef HAVE_GEOS
587 : // Without GEOS we assume that envelope overlap is equivalent to
588 : // actual intersection.
589 : return TRUE;
590 : #else
591 :
592 26 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
593 26 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
594 26 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
595 :
596 26 : OGRBoolean bResult = FALSE;
597 26 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
598 : {
599 26 : bResult =
600 26 : GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
601 : }
602 :
603 26 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
604 26 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
605 26 : freeGEOSContext(hGEOSCtxt);
606 :
607 26 : return bResult;
608 : #endif // HAVE_GEOS
609 : }
610 :
611 : // Old API compatibility function.
612 :
613 : //! @cond Doxygen_Suppress
614 0 : OGRBoolean OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
615 :
616 : {
617 0 : return Intersects(poOtherGeom);
618 : }
619 :
620 : //! @endcond
621 :
622 : /************************************************************************/
623 : /* OGR_G_Intersects() */
624 : /************************************************************************/
625 : /**
626 : * \brief Do these features intersect?
627 : *
628 : * Determines whether two geometries intersect. If GEOS is enabled, then
629 : * this is done in rigorous fashion otherwise TRUE is returned if the
630 : * envelopes (bounding boxes) of the two geometries overlap.
631 : *
632 : * This function is the same as the CPP method OGRGeometry::Intersects.
633 : *
634 : * @param hGeom handle on the first geometry.
635 : * @param hOtherGeom handle on the other geometry to test against.
636 : *
637 : * @return TRUE if the geometries intersect, otherwise FALSE.
638 : */
639 :
640 11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
641 :
642 : {
643 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
644 11 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
645 :
646 22 : return OGRGeometry::FromHandle(hGeom)->Intersects(
647 11 : OGRGeometry::FromHandle(hOtherGeom));
648 : }
649 :
650 : //! @cond Doxygen_Suppress
651 0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
652 :
653 : {
654 0 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
655 0 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
656 :
657 0 : return OGRGeometry::FromHandle(hGeom)->Intersects(
658 0 : OGRGeometry::FromHandle(hOtherGeom));
659 : }
660 :
661 : //! @endcond
662 :
663 : /************************************************************************/
664 : /* transformTo() */
665 : /************************************************************************/
666 :
667 : /**
668 : * \brief Transform geometry to new spatial reference system.
669 : *
670 : * This method will transform the coordinates of a geometry from
671 : * their current spatial reference system to a new target spatial
672 : * reference system. Normally this means reprojecting the vectors,
673 : * but it could include datum shifts, and changes of units.
674 : *
675 : * This method will only work if the geometry already has an assigned
676 : * spatial reference system, and if it is transformable to the target
677 : * coordinate system.
678 : *
679 : * Because this method requires internal creation and initialization of an
680 : * OGRCoordinateTransformation object it is significantly more expensive to
681 : * use this method to transform many geometries than it is to create the
682 : * OGRCoordinateTransformation in advance, and call transform() with that
683 : * transformation. This method exists primarily for convenience when only
684 : * transforming a single geometry.
685 : *
686 : * This method is the same as the C function OGR_G_TransformTo().
687 : *
688 : * @param poSR spatial reference system to transform to.
689 : *
690 : * @return OGRERR_NONE on success, or an error code.
691 : */
692 :
693 27 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
694 :
695 : {
696 27 : if (getSpatialReference() == nullptr)
697 : {
698 1 : CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
699 1 : return OGRERR_FAILURE;
700 : }
701 :
702 26 : if (poSR == nullptr)
703 : {
704 0 : CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
705 0 : return OGRERR_FAILURE;
706 : }
707 :
708 : OGRCoordinateTransformation *poCT =
709 26 : OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
710 26 : if (poCT == nullptr)
711 0 : return OGRERR_FAILURE;
712 :
713 26 : const OGRErr eErr = transform(poCT);
714 :
715 26 : delete poCT;
716 :
717 26 : return eErr;
718 : }
719 :
720 : /************************************************************************/
721 : /* OGR_G_TransformTo() */
722 : /************************************************************************/
723 : /**
724 : * \brief Transform geometry to new spatial reference system.
725 : *
726 : * This function will transform the coordinates of a geometry from
727 : * their current spatial reference system to a new target spatial
728 : * reference system. Normally this means reprojecting the vectors,
729 : * but it could include datum shifts, and changes of units.
730 : *
731 : * This function will only work if the geometry already has an assigned
732 : * spatial reference system, and if it is transformable to the target
733 : * coordinate system.
734 : *
735 : * Because this function requires internal creation and initialization of an
736 : * OGRCoordinateTransformation object it is significantly more expensive to
737 : * use this function to transform many geometries than it is to create the
738 : * OGRCoordinateTransformation in advance, and call transform() with that
739 : * transformation. This function exists primarily for convenience when only
740 : * transforming a single geometry.
741 : *
742 : * This function is the same as the CPP method OGRGeometry::transformTo.
743 : *
744 : * @param hGeom handle on the geometry to apply the transform to.
745 : * @param hSRS handle on the spatial reference system to apply.
746 : *
747 : * @return OGRERR_NONE on success, or an error code.
748 : */
749 :
750 9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
751 :
752 : {
753 9 : VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
754 :
755 18 : return OGRGeometry::FromHandle(hGeom)->transformTo(
756 18 : OGRSpatialReference::FromHandle(hSRS));
757 : }
758 :
759 : /**
760 : * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
761 : *
762 : * \brief Apply arbitrary coordinate transformation to geometry.
763 : *
764 : * This method will transform the coordinates of a geometry from
765 : * their current spatial reference system to a new target spatial
766 : * reference system. Normally this means reprojecting the vectors,
767 : * but it could include datum shifts, and changes of units.
768 : *
769 : * Note that this method does not require that the geometry already
770 : * have a spatial reference system. It will be assumed that they can
771 : * be treated as having the source spatial reference system of the
772 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
773 : * will be ignored. On successful completion the output OGRSpatialReference
774 : * of the OGRCoordinateTransformation will be assigned to the geometry.
775 : *
776 : * This method only does reprojection on a point-by-point basis. It does not
777 : * include advanced logic to deal with discontinuities at poles or antimeridian.
778 : * For that, use the OGRGeometryFactory::transformWithOptions() method.
779 : *
780 : * This method is the same as the C function OGR_G_Transform().
781 : *
782 : * @param poCT the transformation to apply.
783 : *
784 : * @return OGRERR_NONE on success or an error code.
785 : */
786 :
787 : /************************************************************************/
788 : /* OGR_G_Transform() */
789 : /************************************************************************/
790 : /**
791 : * \brief Apply arbitrary coordinate transformation to geometry.
792 : *
793 : * This function will transform the coordinates of a geometry from
794 : * their current spatial reference system to a new target spatial
795 : * reference system. Normally this means reprojecting the vectors,
796 : * but it could include datum shifts, and changes of units.
797 : *
798 : * Note that this function does not require that the geometry already
799 : * have a spatial reference system. It will be assumed that they can
800 : * be treated as having the source spatial reference system of the
801 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
802 : * will be ignored. On successful completion the output OGRSpatialReference
803 : * of the OGRCoordinateTransformation will be assigned to the geometry.
804 : *
805 : * This function only does reprojection on a point-by-point basis. It does not
806 : * include advanced logic to deal with discontinuities at poles or antimeridian.
807 : * For that, use the OGR_GeomTransformer_Create() and
808 : * OGR_GeomTransformer_Transform() functions.
809 : *
810 : * This function is the same as the CPP method OGRGeometry::transform.
811 : *
812 : * @param hGeom handle on the geometry to apply the transform to.
813 : * @param hTransform handle on the transformation to apply.
814 : *
815 : * @return OGRERR_NONE on success or an error code.
816 : */
817 :
818 11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
819 : OGRCoordinateTransformationH hTransform)
820 :
821 : {
822 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
823 :
824 22 : return OGRGeometry::FromHandle(hGeom)->transform(
825 11 : OGRCoordinateTransformation::FromHandle(hTransform));
826 : }
827 :
828 : /**
829 : * \fn int OGRGeometry::getDimension() const;
830 : *
831 : * \brief Get the dimension of this object.
832 : *
833 : * This method corresponds to the SFCOM IGeometry::GetDimension() method.
834 : * It indicates the dimension of the object, but does not indicate the
835 : * dimension of the underlying space (as indicated by
836 : * OGRGeometry::getCoordinateDimension()).
837 : *
838 : * This method is the same as the C function OGR_G_GetDimension().
839 : *
840 : * @return 0 for points, 1 for lines and 2 for surfaces.
841 : */
842 :
843 : /**
844 : * \brief Get the geometry type that conforms with ISO SQL/MM Part3
845 : *
846 : * @return the geometry type that conforms with ISO SQL/MM Part3
847 : */
848 720723 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
849 : {
850 720723 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
851 :
852 720723 : if (flags & OGR_G_3D)
853 214215 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
854 720723 : if (flags & OGR_G_MEASURED)
855 26025 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
856 :
857 720723 : return nGType;
858 : }
859 :
860 : /************************************************************************/
861 : /* OGRGeometry::segmentize() */
862 : /************************************************************************/
863 : /**
864 : *
865 : * \brief Modify the geometry such it has no segment longer then the
866 : * given distance.
867 : *
868 : * This method modifies the geometry to add intermediate vertices if necessary
869 : * so that the maximum length between 2 consecutive vertices is lower than
870 : * dfMaxLength.
871 : *
872 : * Interpolated points will have Z and M values (if needed) set to 0.
873 : * Distance computation is performed in 2d only
874 : *
875 : * This function is the same as the C function OGR_G_Segmentize()
876 : *
877 : * @param dfMaxLength the maximum distance between 2 points after segmentization
878 : * @return (since 3.10) true in case of success, false in case of error.
879 : */
880 :
881 0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
882 : {
883 : // Do nothing.
884 0 : return true;
885 : }
886 :
887 : /************************************************************************/
888 : /* OGR_G_Segmentize() */
889 : /************************************************************************/
890 :
891 : /**
892 : *
893 : * \brief Modify the geometry such it has no segment longer then the given
894 : * distance.
895 : *
896 : * Interpolated points will have Z and M values (if needed) set to 0.
897 : * Distance computation is performed in 2d only.
898 : *
899 : * This function is the same as the CPP method OGRGeometry::segmentize().
900 : *
901 : * @param hGeom handle on the geometry to segmentize
902 : * @param dfMaxLength the maximum distance between 2 points after segmentization
903 : */
904 :
905 24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
906 : {
907 24 : VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
908 :
909 24 : if (dfMaxLength <= 0)
910 : {
911 0 : CPLError(CE_Failure, CPLE_AppDefined,
912 : "dfMaxLength must be strictly positive");
913 0 : return;
914 : }
915 24 : OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
916 : }
917 :
918 : /************************************************************************/
919 : /* OGR_G_GetDimension() */
920 : /************************************************************************/
921 : /**
922 : *
923 : * \brief Get the dimension of this geometry.
924 : *
925 : * This function corresponds to the SFCOM IGeometry::GetDimension() method.
926 : * It indicates the dimension of the geometry, but does not indicate the
927 : * dimension of the underlying space (as indicated by
928 : * OGR_G_GetCoordinateDimension() function).
929 : *
930 : * This function is the same as the CPP method OGRGeometry::getDimension().
931 : *
932 : * @param hGeom handle on the geometry to get the dimension from.
933 : * @return 0 for points, 1 for lines and 2 for surfaces.
934 : */
935 :
936 21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
937 :
938 : {
939 21 : VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
940 :
941 21 : return OGRGeometry::FromHandle(hGeom)->getDimension();
942 : }
943 :
944 : /************************************************************************/
945 : /* getCoordinateDimension() */
946 : /************************************************************************/
947 : /**
948 : * \brief Get the dimension of the coordinates in this object.
949 : *
950 : * This method is the same as the C function OGR_G_GetCoordinateDimension().
951 : *
952 : * @deprecated use CoordinateDimension().
953 : *
954 : * @return this will return 2 or 3.
955 : */
956 :
957 607261 : int OGRGeometry::getCoordinateDimension() const
958 :
959 : {
960 607261 : return (flags & OGR_G_3D) ? 3 : 2;
961 : }
962 :
963 : /************************************************************************/
964 : /* CoordinateDimension() */
965 : /************************************************************************/
966 : /**
967 : * \brief Get the dimension of the coordinates in this object.
968 : *
969 : * This method is the same as the C function OGR_G_CoordinateDimension().
970 : *
971 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
972 : *
973 : */
974 :
975 51750 : int OGRGeometry::CoordinateDimension() const
976 :
977 : {
978 51750 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
979 7375 : return 4;
980 44375 : else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
981 6863 : return 3;
982 : else
983 37512 : return 2;
984 : }
985 :
986 : /************************************************************************/
987 : /* OGR_G_GetCoordinateDimension() */
988 : /************************************************************************/
989 : /**
990 : *
991 : * \brief Get the dimension of the coordinates in this geometry.
992 : *
993 : * This function is the same as the CPP method
994 : * OGRGeometry::getCoordinateDimension().
995 : *
996 : * @param hGeom handle on the geometry to get the dimension of the
997 : * coordinates from.
998 : *
999 : * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
1000 : * OGR_G_IsMeasured().
1001 : *
1002 : * @return this will return 2 or 3.
1003 : */
1004 :
1005 710 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
1006 :
1007 : {
1008 710 : VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
1009 :
1010 710 : return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
1011 : }
1012 :
1013 : /************************************************************************/
1014 : /* OGR_G_CoordinateDimension() */
1015 : /************************************************************************/
1016 : /**
1017 : *
1018 : * \brief Get the dimension of the coordinates in this geometry.
1019 : *
1020 : * This function is the same as the CPP method
1021 : * OGRGeometry::CoordinateDimension().
1022 : *
1023 : * @param hGeom handle on the geometry to get the dimension of the
1024 : * coordinates from.
1025 : *
1026 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
1027 : *
1028 : */
1029 :
1030 4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
1031 :
1032 : {
1033 4 : VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
1034 :
1035 4 : return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
1036 : }
1037 :
1038 : /**
1039 : *
1040 : * \brief See whether this geometry has Z coordinates.
1041 : *
1042 : * This function is the same as the CPP method
1043 : * OGRGeometry::Is3D().
1044 : *
1045 : * @param hGeom handle on the geometry to check whether it has Z coordinates.
1046 : *
1047 : * @return TRUE if the geometry has Z coordinates.
1048 : */
1049 :
1050 37714 : int OGR_G_Is3D(OGRGeometryH hGeom)
1051 :
1052 : {
1053 37714 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1054 :
1055 37714 : return OGRGeometry::FromHandle(hGeom)->Is3D();
1056 : }
1057 :
1058 : /**
1059 : *
1060 : * \brief See whether this geometry is measured.
1061 : *
1062 : * This function is the same as the CPP method
1063 : * OGRGeometry::IsMeasured().
1064 : *
1065 : * @param hGeom handle on the geometry to check whether it is measured.
1066 : *
1067 : * @return TRUE if the geometry has M coordinates.
1068 : */
1069 :
1070 40125 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1071 :
1072 : {
1073 40125 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1074 :
1075 40125 : return OGRGeometry::FromHandle(hGeom)->IsMeasured();
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* setCoordinateDimension() */
1080 : /************************************************************************/
1081 :
1082 : /**
1083 : * \brief Set the coordinate dimension.
1084 : *
1085 : * This method sets the explicit coordinate dimension. Setting the coordinate
1086 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1087 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1088 : * will affect the children geometries.
1089 : * This will also remove the M dimension if present before this call.
1090 : *
1091 : * @deprecated use set3D() or setMeasured().
1092 : *
1093 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1094 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1095 : */
1096 :
1097 82718 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1098 :
1099 : {
1100 82718 : if (nNewDimension == 2)
1101 82226 : flags &= ~OGR_G_3D;
1102 : else
1103 492 : flags |= OGR_G_3D;
1104 82718 : return setMeasured(FALSE);
1105 : }
1106 :
1107 : /**
1108 : * \brief Add or remove the Z coordinate dimension.
1109 : *
1110 : * This method adds or removes the explicit Z coordinate dimension.
1111 : * Removing the Z coordinate dimension of a geometry will remove any
1112 : * existing Z values. Adding the Z dimension to a geometry
1113 : * collection, a compound curve, a polygon, etc. will affect the
1114 : * children geometries.
1115 : *
1116 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1117 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1118 : */
1119 :
1120 1617930 : bool OGRGeometry::set3D(OGRBoolean bIs3D)
1121 :
1122 : {
1123 1617930 : if (bIs3D)
1124 1612580 : flags |= OGR_G_3D;
1125 : else
1126 5346 : flags &= ~OGR_G_3D;
1127 1617930 : return true;
1128 : }
1129 :
1130 : /**
1131 : * \brief Add or remove the M coordinate dimension.
1132 : *
1133 : * This method adds or removes the explicit M coordinate dimension.
1134 : * Removing the M coordinate dimension of a geometry will remove any
1135 : * existing M values. Adding the M dimension to a geometry
1136 : * collection, a compound curve, a polygon, etc. will affect the
1137 : * children geometries.
1138 : *
1139 : * @param bIsMeasured Should the geometry have a M dimension, either
1140 : * TRUE or FALSE.
1141 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1142 : */
1143 :
1144 429332 : bool OGRGeometry::setMeasured(OGRBoolean bIsMeasured)
1145 :
1146 : {
1147 429332 : if (bIsMeasured)
1148 137729 : flags |= OGR_G_MEASURED;
1149 : else
1150 291603 : flags &= ~OGR_G_MEASURED;
1151 429332 : return true;
1152 : }
1153 :
1154 : /************************************************************************/
1155 : /* OGR_G_SetCoordinateDimension() */
1156 : /************************************************************************/
1157 :
1158 : /**
1159 : * \brief Set the coordinate dimension.
1160 : *
1161 : * This method sets the explicit coordinate dimension. Setting the coordinate
1162 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1163 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1164 : * will affect the children geometries.
1165 : * This will also remove the M dimension if present before this call.
1166 : *
1167 : * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
1168 : *
1169 : * @param hGeom handle on the geometry to set the dimension of the
1170 : * coordinates.
1171 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1172 : */
1173 :
1174 56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
1175 :
1176 : {
1177 56 : VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
1178 :
1179 56 : OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* OGR_G_Set3D() */
1184 : /************************************************************************/
1185 :
1186 : /**
1187 : * \brief Add or remove the Z coordinate dimension.
1188 : *
1189 : * This method adds or removes the explicit Z coordinate dimension.
1190 : * Removing the Z coordinate dimension of a geometry will remove any
1191 : * existing Z values. Adding the Z dimension to a geometry
1192 : * collection, a compound curve, a polygon, etc. will affect the
1193 : * children geometries.
1194 : *
1195 : * @param hGeom handle on the geometry to set or unset the Z dimension.
1196 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1197 : */
1198 :
1199 154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
1200 :
1201 : {
1202 154 : VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
1203 :
1204 154 : OGRGeometry::FromHandle(hGeom)->set3D(bIs3D);
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* OGR_G_SetMeasured() */
1209 : /************************************************************************/
1210 :
1211 : /**
1212 : * \brief Add or remove the M coordinate dimension.
1213 : *
1214 : * This method adds or removes the explicit M coordinate dimension.
1215 : * Removing the M coordinate dimension of a geometry will remove any
1216 : * existing M values. Adding the M dimension to a geometry
1217 : * collection, a compound curve, a polygon, etc. will affect the
1218 : * children geometries.
1219 : *
1220 : * @param hGeom handle on the geometry to set or unset the M dimension.
1221 : * @param bIsMeasured Should the geometry have a M dimension, either
1222 : * TRUE or FALSE.
1223 : */
1224 :
1225 154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
1226 :
1227 : {
1228 154 : VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
1229 :
1230 154 : OGRGeometry::FromHandle(hGeom)->setMeasured(bIsMeasured);
1231 : }
1232 :
1233 : /**
1234 : * \fn int OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
1235 : *
1236 : * \brief Returns TRUE if two geometries are equivalent.
1237 : *
1238 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1239 : *
1240 : * The comparison is done in a structural way, that is to say that the geometry
1241 : * types must be identical, as well as the number and ordering of sub-geometries
1242 : * and vertices.
1243 : * Or equivalently, two geometries are considered equal by this method if their
1244 : * WKT/WKB representation is equal.
1245 : * Note: this must be distinguished for equality in a spatial way (which is
1246 : * the purpose of the ST_Equals() operation).
1247 : *
1248 : * This method is the same as the C function OGR_G_Equals().
1249 : *
1250 : * @return TRUE if equivalent or FALSE otherwise.
1251 : */
1252 :
1253 : // Backward compatibility method.
1254 :
1255 : //! @cond Doxygen_Suppress
1256 0 : int OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
1257 : {
1258 0 : return Equals(poOtherGeom);
1259 : }
1260 :
1261 : //! @endcond
1262 :
1263 : /************************************************************************/
1264 : /* OGR_G_Equals() */
1265 : /************************************************************************/
1266 :
1267 : /**
1268 : * \brief Returns TRUE if two geometries are equivalent.
1269 : *
1270 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1271 : *
1272 : * The comparison is done in a structural way, that is to say that the geometry
1273 : * types must be identical, as well as the number and ordering of sub-geometries
1274 : * and vertices.
1275 : * Or equivalently, two geometries are considered equal by this method if their
1276 : * WKT/WKB representation is equal.
1277 : * Note: this must be distinguished for equality in a spatial way (which is
1278 : * the purpose of the ST_Equals() operation).
1279 : *
1280 : * This function is the same as the CPP method OGRGeometry::Equals() method.
1281 : *
1282 : * @param hGeom handle on the first geometry.
1283 : * @param hOther handle on the other geometry to test against.
1284 : * @return TRUE if equivalent or FALSE otherwise.
1285 : */
1286 :
1287 28155 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1288 :
1289 : {
1290 28155 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1291 :
1292 28155 : if (hOther == nullptr)
1293 : {
1294 0 : CPLError(CE_Failure, CPLE_ObjectNull,
1295 : "hOther was NULL in OGR_G_Equals");
1296 0 : return 0;
1297 : }
1298 :
1299 56310 : return OGRGeometry::FromHandle(hGeom)->Equals(
1300 28155 : OGRGeometry::FromHandle(hOther));
1301 : }
1302 :
1303 : //! @cond Doxygen_Suppress
1304 0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
1305 :
1306 : {
1307 0 : if (hGeom == nullptr)
1308 : {
1309 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
1310 0 : return 0;
1311 : }
1312 :
1313 0 : if (hOther == nullptr)
1314 : {
1315 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
1316 0 : return 0;
1317 : }
1318 :
1319 0 : return OGRGeometry::FromHandle(hGeom)->Equals(
1320 0 : OGRGeometry::FromHandle(hOther));
1321 : }
1322 :
1323 : //! @endcond
1324 :
1325 : /**
1326 : * \fn int OGRGeometry::WkbSize() const;
1327 : *
1328 : * \brief Returns size of related binary representation.
1329 : *
1330 : * This method returns the exact number of bytes required to hold the
1331 : * well known binary representation of this geometry object. Its computation
1332 : * may be slightly expensive for complex geometries.
1333 : *
1334 : * This method relates to the SFCOM IWks::WkbSize() method.
1335 : *
1336 : * This method is the same as the C function OGR_G_WkbSize().
1337 : *
1338 : * @return size of binary representation in bytes.
1339 : */
1340 :
1341 : /************************************************************************/
1342 : /* OGR_G_WkbSize() */
1343 : /************************************************************************/
1344 : /**
1345 : * \brief Returns size of related binary representation.
1346 : *
1347 : * This function returns the exact number of bytes required to hold the
1348 : * well known binary representation of this geometry object. Its computation
1349 : * may be slightly expensive for complex geometries.
1350 : *
1351 : * This function relates to the SFCOM IWks::WkbSize() method.
1352 : *
1353 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1354 : *
1355 : * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
1356 : *
1357 : * @param hGeom handle on the geometry to get the binary size from.
1358 : * @return size of binary representation in bytes.
1359 : */
1360 :
1361 1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
1362 :
1363 : {
1364 1 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
1365 :
1366 1 : const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
1367 1 : if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
1368 : {
1369 0 : CPLError(CE_Failure, CPLE_AppDefined,
1370 : "OGR_G_WkbSize() would return a value beyond int range. "
1371 : "Use OGR_G_WkbSizeEx() instead");
1372 0 : return 0;
1373 : }
1374 1 : return static_cast<int>(nSize);
1375 : }
1376 :
1377 : /************************************************************************/
1378 : /* OGR_G_WkbSizeEx() */
1379 : /************************************************************************/
1380 : /**
1381 : * \brief Returns size of related binary representation.
1382 : *
1383 : * This function returns the exact number of bytes required to hold the
1384 : * well known binary representation of this geometry object. Its computation
1385 : * may be slightly expensive for complex geometries.
1386 : *
1387 : * This function relates to the SFCOM IWks::WkbSize() method.
1388 : *
1389 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1390 : *
1391 : * @param hGeom handle on the geometry to get the binary size from.
1392 : * @return size of binary representation in bytes.
1393 : * @since GDAL 3.3
1394 : */
1395 :
1396 10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
1397 :
1398 : {
1399 10679 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
1400 :
1401 10679 : return OGRGeometry::FromHandle(hGeom)->WkbSize();
1402 : }
1403 :
1404 : /**
1405 : * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
1406 : *
1407 : * \brief Computes and returns the bounding envelope for this geometry
1408 : * in the passed psEnvelope structure.
1409 : *
1410 : * This method is the same as the C function OGR_G_GetEnvelope().
1411 : *
1412 : * @param psEnvelope the structure in which to place the results.
1413 : */
1414 :
1415 : /************************************************************************/
1416 : /* OGR_G_GetEnvelope() */
1417 : /************************************************************************/
1418 : /**
1419 : * \brief Computes and returns the bounding envelope for this geometry
1420 : * in the passed psEnvelope structure.
1421 : *
1422 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1423 : *
1424 : * @param hGeom handle of the geometry to get envelope from.
1425 : * @param psEnvelope the structure in which to place the results.
1426 : */
1427 :
1428 13361 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
1429 :
1430 : {
1431 13361 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1432 :
1433 13361 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1434 : }
1435 :
1436 : /**
1437 : * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
1438 : *
1439 : * \brief Computes and returns the bounding envelope (3D) for this
1440 : * geometry in the passed psEnvelope structure.
1441 : *
1442 : * This method is the same as the C function OGR_G_GetEnvelope3D().
1443 : *
1444 : * @param psEnvelope the structure in which to place the results.
1445 : *
1446 : */
1447 :
1448 : /************************************************************************/
1449 : /* OGR_G_GetEnvelope3D() */
1450 : /************************************************************************/
1451 : /**
1452 : * \brief Computes and returns the bounding envelope (3D) for this
1453 : * geometry in the passed psEnvelope structure.
1454 : *
1455 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1456 : *
1457 : * @param hGeom handle of the geometry to get envelope from.
1458 : * @param psEnvelope the structure in which to place the results.
1459 : *
1460 : */
1461 :
1462 10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
1463 :
1464 : {
1465 10 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
1466 :
1467 10 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1468 : }
1469 :
1470 : /************************************************************************/
1471 : /* importFromWkb() */
1472 : /************************************************************************/
1473 :
1474 : /**
1475 : * \brief Assign geometry from well known binary data.
1476 : *
1477 : * The object must have already been instantiated as the correct derived
1478 : * type of geometry object to match the binaries type. This method is used
1479 : * by the OGRGeometryFactory class, but not normally called by application
1480 : * code.
1481 : *
1482 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1483 : *
1484 : * This method is the same as the C function OGR_G_ImportFromWkb().
1485 : *
1486 : * @param pabyData the binary input data.
1487 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1488 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1489 : * done for curve geometries code
1490 : *
1491 : * @return OGRERR_NONE if all goes well, otherwise any of
1492 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1493 : * OGRERR_CORRUPT_DATA may be returned.
1494 : */
1495 :
1496 492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
1497 : OGRwkbVariant eWkbVariant)
1498 : {
1499 492 : size_t nBytesConsumedOutIgnored = 0;
1500 492 : return importFromWkb(pabyData, nSize, eWkbVariant,
1501 984 : nBytesConsumedOutIgnored);
1502 : }
1503 :
1504 : /**
1505 : * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
1506 : * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
1507 : *
1508 : * \brief Assign geometry from well known binary data.
1509 : *
1510 : * The object must have already been instantiated as the correct derived
1511 : * type of geometry object to match the binaries type. This method is used
1512 : * by the OGRGeometryFactory class, but not normally called by application
1513 : * code.
1514 : *
1515 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1516 : *
1517 : * This method is the same as the C function OGR_G_ImportFromWkb().
1518 : *
1519 : * @param pabyData the binary input data.
1520 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1521 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1522 : * done for curve geometries code
1523 : * @param nBytesConsumedOut output parameter. Number of bytes consumed.
1524 : *
1525 : * @return OGRERR_NONE if all goes well, otherwise any of
1526 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1527 : * OGRERR_CORRUPT_DATA may be returned.
1528 : *
1529 : */
1530 :
1531 : /************************************************************************/
1532 : /* OGR_G_ImportFromWkb() */
1533 : /************************************************************************/
1534 : /**
1535 : * \brief Assign geometry from well known binary data.
1536 : *
1537 : * The object must have already been instantiated as the correct derived
1538 : * type of geometry object to match the binaries type.
1539 : *
1540 : * This function relates to the SFCOM IWks::ImportFromWKB() method.
1541 : *
1542 : * This function is the same as the CPP method OGRGeometry::importFromWkb().
1543 : *
1544 : * @param hGeom handle on the geometry to assign the well know binary data to.
1545 : * @param pabyData the binary input data.
1546 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1547 : *
1548 : * @return OGRERR_NONE if all goes well, otherwise any of
1549 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1550 : * OGRERR_CORRUPT_DATA may be returned.
1551 : */
1552 :
1553 0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
1554 :
1555 : {
1556 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
1557 :
1558 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkb(
1559 0 : static_cast<const GByte *>(pabyData), nSize);
1560 : }
1561 :
1562 : /************************************************************************/
1563 : /* OGRGeometry::exportToWkb() */
1564 : /************************************************************************/
1565 :
1566 : /* clang-format off */
1567 : /**
1568 : * \brief Convert a geometry into well known binary format.
1569 : *
1570 : * This method relates to the SFCOM IWks::ExportToWKB() method.
1571 : *
1572 : * This method is the same as the C function OGR_G_ExportToWkb() or
1573 : * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
1574 : *
1575 : * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1576 : * respectively.
1577 : * @param pabyData a buffer into which the binary representation is
1578 : * written. This buffer must be at least
1579 : * OGRGeometry::WkbSize() byte in size.
1580 : * @param eWkbVariant What standard to use when exporting geometries
1581 : * with three dimensions (or more). The default
1582 : * wkbVariantOldOgc is the historical OGR
1583 : * variant. wkbVariantIso is the variant defined
1584 : * in ISO SQL/MM and adopted by OGC for SFSQL
1585 : * 1.2.
1586 : *
1587 : * @return Currently OGRERR_NONE is always returned.
1588 : */
1589 : /* clang-format on */
1590 :
1591 279927 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1592 : unsigned char *pabyData,
1593 : OGRwkbVariant eWkbVariant) const
1594 : {
1595 279927 : OGRwkbExportOptions sOptions;
1596 279927 : sOptions.eByteOrder = eByteOrder;
1597 279927 : sOptions.eWkbVariant = eWkbVariant;
1598 559854 : return exportToWkb(pabyData, &sOptions);
1599 : }
1600 :
1601 : /************************************************************************/
1602 : /* OGR_G_ExportToWkb() */
1603 : /************************************************************************/
1604 : /**
1605 : * \brief Convert a geometry well known binary format
1606 : *
1607 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1608 : *
1609 : * For backward compatibility purposes, it exports the Old-style 99-402
1610 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1611 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1612 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
1613 : *
1614 : * This function is the same as the CPP method
1615 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
1616 : * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
1617 : *
1618 : * @param hGeom handle on the geometry to convert to a well know binary
1619 : * data from.
1620 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1621 : * respectively.
1622 : * @param pabyDstBuffer a buffer into which the binary representation is
1623 : * written. This buffer must be at least
1624 : * OGR_G_WkbSize() byte in size.
1625 : *
1626 : * @return Currently OGRERR_NONE is always returned.
1627 : */
1628 :
1629 109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1630 : unsigned char *pabyDstBuffer)
1631 :
1632 : {
1633 109 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
1634 :
1635 109 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
1636 : }
1637 :
1638 : /************************************************************************/
1639 : /* OGR_G_ExportToIsoWkb() */
1640 : /************************************************************************/
1641 : /**
1642 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
1643 : * binary format
1644 : *
1645 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1646 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
1647 : * types.
1648 : *
1649 : * This function is the same as the CPP method
1650 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
1651 : * with eWkbVariant = wkbVariantIso.
1652 : *
1653 : * @param hGeom handle on the geometry to convert to a well know binary
1654 : * data from.
1655 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1656 : * respectively.
1657 : * @param pabyDstBuffer a buffer into which the binary representation is
1658 : * written. This buffer must be at least
1659 : * OGR_G_WkbSize() byte in size.
1660 : *
1661 : * @return Currently OGRERR_NONE is always returned.
1662 : *
1663 : */
1664 :
1665 10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1666 : unsigned char *pabyDstBuffer)
1667 :
1668 : {
1669 10571 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
1670 :
1671 10571 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
1672 10571 : wkbVariantIso);
1673 : }
1674 :
1675 : /************************************************************************/
1676 : /* OGR_G_ExportToWkbEx() */
1677 : /************************************************************************/
1678 :
1679 : /* clang-format off */
1680 : /**
1681 : * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
1682 : *
1683 : * \brief Convert a geometry into well known binary format
1684 : *
1685 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1686 : *
1687 : * This function is the same as the C function OGR_G_ExportToWkbEx().
1688 : *
1689 : * @param pabyDstBuffer a buffer into which the binary representation is
1690 : * written. This buffer must be at least
1691 : * OGR_G_WkbSize() byte in size.
1692 : * @param psOptions WKB export options.
1693 :
1694 : * @return Currently OGRERR_NONE is always returned.
1695 : *
1696 : * @since GDAL 3.9
1697 : */
1698 : /* clang-format on */
1699 :
1700 : /**
1701 : * \brief Convert a geometry into well known binary format
1702 : *
1703 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1704 : *
1705 : * This function is the same as the CPP method
1706 : * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
1707 : *
1708 : * @param hGeom handle on the geometry to convert to a well know binary
1709 : * data from.
1710 : * @param pabyDstBuffer a buffer into which the binary representation is
1711 : * written. This buffer must be at least
1712 : * OGR_G_WkbSize() byte in size.
1713 : * @param psOptions WKB export options.
1714 :
1715 : * @return Currently OGRERR_NONE is always returned.
1716 : *
1717 : * @since GDAL 3.9
1718 : */
1719 :
1720 2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
1721 : const OGRwkbExportOptions *psOptions)
1722 : {
1723 2 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
1724 :
1725 4 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
1726 2 : psOptions);
1727 : }
1728 :
1729 : /**
1730 : * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
1731 : *
1732 : * \brief Assign geometry from well known text data.
1733 : *
1734 : * The object must have already been instantiated as the correct derived
1735 : * type of geometry object to match the text type. This method is used
1736 : * by the OGRGeometryFactory class, but not normally called by application
1737 : * code.
1738 : *
1739 : * This method relates to the SFCOM IWks::ImportFromWKT() method.
1740 : *
1741 : * This method is the same as the C function OGR_G_ImportFromWkt().
1742 : *
1743 : * @param ppszInput pointer to a pointer to the source text. The pointer is
1744 : * updated to pointer after the consumed text.
1745 : *
1746 : * @return OGRERR_NONE if all goes well, otherwise any of
1747 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1748 : * OGRERR_CORRUPT_DATA may be returned.
1749 : */
1750 :
1751 : /************************************************************************/
1752 : /* OGR_G_ImportFromWkt() */
1753 : /************************************************************************/
1754 : /**
1755 : * \brief Assign geometry from well known text data.
1756 : *
1757 : * The object must have already been instantiated as the correct derived
1758 : * type of geometry object to match the text type.
1759 : *
1760 : * This function relates to the SFCOM IWks::ImportFromWKT() method.
1761 : *
1762 : * This function is the same as the CPP method OGRGeometry::importFromWkt().
1763 : *
1764 : * @param hGeom handle on the geometry to assign well know text data to.
1765 : * @param ppszSrcText pointer to a pointer to the source text. The pointer is
1766 : * updated to pointer after the consumed text.
1767 : *
1768 : * @return OGRERR_NONE if all goes well, otherwise any of
1769 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1770 : * OGRERR_CORRUPT_DATA may be returned.
1771 : */
1772 :
1773 0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
1774 :
1775 : {
1776 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
1777 :
1778 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkt(
1779 0 : const_cast<const char **>(ppszSrcText));
1780 : }
1781 :
1782 : /************************************************************************/
1783 : /* importPreambleFromWkt() */
1784 : /************************************************************************/
1785 :
1786 : // Returns -1 if processing must continue.
1787 : //! @cond Doxygen_Suppress
1788 123564 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1789 : int *pbHasM, bool *pbIsEmpty)
1790 : {
1791 123564 : const char *pszInput = *ppszInput;
1792 :
1793 : /* -------------------------------------------------------------------- */
1794 : /* Clear existing Geoms. */
1795 : /* -------------------------------------------------------------------- */
1796 123564 : empty();
1797 123564 : *pbIsEmpty = false;
1798 :
1799 : /* -------------------------------------------------------------------- */
1800 : /* Read and verify the type keyword, and ensure it matches the */
1801 : /* actual type of this container. */
1802 : /* -------------------------------------------------------------------- */
1803 123564 : bool bHasM = false;
1804 123564 : bool bHasZ = false;
1805 123564 : bool bAlreadyGotDimension = false;
1806 :
1807 123564 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1808 123564 : pszInput = OGRWktReadToken(pszInput, szToken);
1809 123564 : if (szToken[0] != '\0')
1810 : {
1811 : // Postgis EWKT: POINTM instead of POINT M.
1812 : // Current QGIS versions (at least <= 3.38) also export POINTZ.
1813 123564 : const size_t nTokenLen = strlen(szToken);
1814 123564 : if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
1815 : {
1816 11 : szToken[nTokenLen - 1] = '\0';
1817 11 : bHasM = true;
1818 11 : bAlreadyGotDimension = true;
1819 :
1820 11 : if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
1821 9 : szToken[nTokenLen - 2] == 'z'))
1822 : {
1823 4 : bHasZ = true;
1824 4 : szToken[nTokenLen - 2] = '\0';
1825 : }
1826 : }
1827 123553 : else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
1828 : {
1829 6 : szToken[nTokenLen - 1] = '\0';
1830 6 : bHasZ = true;
1831 6 : bAlreadyGotDimension = true;
1832 : }
1833 : }
1834 :
1835 123564 : if (!EQUAL(szToken, getGeometryName()))
1836 0 : return OGRERR_CORRUPT_DATA;
1837 :
1838 : /* -------------------------------------------------------------------- */
1839 : /* Check for Z, M or ZM */
1840 : /* -------------------------------------------------------------------- */
1841 123564 : if (!bAlreadyGotDimension)
1842 : {
1843 123547 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1844 123547 : if (EQUAL(szToken, "Z"))
1845 : {
1846 1418 : pszInput = pszNewInput;
1847 1418 : bHasZ = true;
1848 : }
1849 122129 : else if (EQUAL(szToken, "M"))
1850 : {
1851 353 : pszInput = pszNewInput;
1852 353 : bHasM = true;
1853 : }
1854 121776 : else if (EQUAL(szToken, "ZM"))
1855 : {
1856 494 : pszInput = pszNewInput;
1857 494 : bHasZ = true;
1858 494 : bHasM = true;
1859 : }
1860 : }
1861 123564 : *pbHasZ = bHasZ;
1862 123564 : *pbHasM = bHasM;
1863 :
1864 : /* -------------------------------------------------------------------- */
1865 : /* Check for EMPTY ... */
1866 : /* -------------------------------------------------------------------- */
1867 123564 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1868 123564 : if (EQUAL(szToken, "EMPTY"))
1869 : {
1870 1577 : *ppszInput = pszNewInput;
1871 1577 : *pbIsEmpty = true;
1872 1577 : if (bHasZ)
1873 137 : set3D(TRUE);
1874 1577 : if (bHasM)
1875 84 : setMeasured(TRUE);
1876 1577 : return OGRERR_NONE;
1877 : }
1878 :
1879 121987 : if (!EQUAL(szToken, "("))
1880 35 : return OGRERR_CORRUPT_DATA;
1881 :
1882 121952 : if (!bHasZ && !bHasM)
1883 : {
1884 : // Test for old-style XXXXXXXXX(EMPTY).
1885 119850 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1886 119850 : if (EQUAL(szToken, "EMPTY"))
1887 : {
1888 69 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1889 :
1890 69 : if (EQUAL(szToken, ","))
1891 : {
1892 : // This is OK according to SFSQL SPEC.
1893 : }
1894 44 : else if (!EQUAL(szToken, ")"))
1895 : {
1896 9 : return OGRERR_CORRUPT_DATA;
1897 : }
1898 : else
1899 : {
1900 35 : *ppszInput = pszNewInput;
1901 35 : empty();
1902 35 : *pbIsEmpty = true;
1903 35 : return OGRERR_NONE;
1904 : }
1905 : }
1906 : }
1907 :
1908 121908 : *ppszInput = pszInput;
1909 :
1910 121908 : return OGRERR_NONE;
1911 : }
1912 :
1913 : //! @endcond
1914 :
1915 : /************************************************************************/
1916 : /* wktTypeString() */
1917 : /************************************************************************/
1918 :
1919 : //! @cond Doxygen_Suppress
1920 : /** Get a type string for WKT, padded with a space at the end.
1921 : *
1922 : * @param variant OGR type variant
1923 : * @return "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
1924 : */
1925 14230 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1926 : {
1927 14230 : std::string s(" ");
1928 :
1929 14230 : if (variant == wkbVariantIso)
1930 : {
1931 9346 : if (flags & OGR_G_3D)
1932 1957 : s += "Z";
1933 9346 : if (flags & OGR_G_MEASURED)
1934 1200 : s += "M";
1935 : }
1936 14230 : if (s.size() > 1)
1937 2518 : s += " ";
1938 14230 : return s;
1939 : }
1940 :
1941 : //! @endcond
1942 :
1943 : /**
1944 : * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
1945 : * OGRwkbVariant variant = wkbVariantOldOgc ) const;
1946 : *
1947 : * \brief Convert a geometry into well known text format.
1948 : *
1949 : * This method relates to the SFCOM IWks::ExportToWKT() method.
1950 : *
1951 : * This method is the same as the C function OGR_G_ExportToWkt().
1952 : *
1953 : * @param ppszDstText a text buffer is allocated by the program, and assigned
1954 : * to the passed pointer. After use, *ppszDstText should be
1955 : * freed with CPLFree().
1956 : * @param variant the specification that must be conformed too :
1957 : * - wkbVariantOgc for old-style 99-402 extended
1958 : * dimension (Z) WKB types
1959 : * - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
1960 : *
1961 : * @return Currently OGRERR_NONE is always returned.
1962 : */
1963 8879 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1964 : {
1965 8879 : OGRWktOptions opts;
1966 8879 : opts.variant = variant;
1967 8879 : OGRErr err(OGRERR_NONE);
1968 :
1969 8879 : std::string wkt = exportToWkt(opts, &err);
1970 8879 : *ppszDstText = CPLStrdup(wkt.data());
1971 17758 : return err;
1972 : }
1973 :
1974 : /************************************************************************/
1975 : /* OGR_G_ExportToWkt() */
1976 : /************************************************************************/
1977 :
1978 : /**
1979 : * \brief Convert a geometry into well known text format.
1980 : *
1981 : * This function relates to the SFCOM IWks::ExportToWKT() method.
1982 : *
1983 : * For backward compatibility purposes, it exports the Old-style 99-402
1984 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1985 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1986 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
1987 : *
1988 : * This function is the same as the CPP method OGRGeometry::exportToWkt().
1989 : *
1990 : * @param hGeom handle on the geometry to convert to a text format from.
1991 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
1992 : * to the passed pointer. After use, *ppszDstText should be
1993 : * freed with CPLFree().
1994 : *
1995 : * @return Currently OGRERR_NONE is always returned.
1996 : */
1997 :
1998 2551 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
1999 :
2000 : {
2001 2551 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2002 :
2003 2551 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
2004 : }
2005 :
2006 : /************************************************************************/
2007 : /* OGR_G_ExportToIsoWkt() */
2008 : /************************************************************************/
2009 :
2010 : /**
2011 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
2012 : * known text format.
2013 : *
2014 : * This function relates to the SFCOM IWks::ExportToWKT() method.
2015 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
2016 : * (Z&M) WKB types.
2017 : *
2018 : * This function is the same as the CPP method
2019 : * OGRGeometry::exportToWkt(wkbVariantIso).
2020 : *
2021 : * @param hGeom handle on the geometry to convert to a text format from.
2022 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
2023 : * to the passed pointer. After use, *ppszDstText should be
2024 : * freed with CPLFree().
2025 : *
2026 : * @return Currently OGRERR_NONE is always returned.
2027 : *
2028 : */
2029 :
2030 5500 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
2031 :
2032 : {
2033 5500 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2034 :
2035 5500 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2036 5500 : wkbVariantIso);
2037 : }
2038 :
2039 : /**
2040 : * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
2041 : *
2042 : * \brief Fetch geometry type.
2043 : *
2044 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2045 : * flattened version of the geometry type apply the wkbFlatten() macro
2046 : * to the return result.
2047 : *
2048 : * This method is the same as the C function OGR_G_GetGeometryType().
2049 : *
2050 : * @return the geometry type code.
2051 : */
2052 :
2053 : /************************************************************************/
2054 : /* OGR_G_GetGeometryType() */
2055 : /************************************************************************/
2056 : /**
2057 : * \brief Fetch geometry type.
2058 : *
2059 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2060 : * flattened version of the geometry type apply the wkbFlatten() macro
2061 : * to the return result.
2062 : *
2063 : * This function is the same as the CPP method OGRGeometry::getGeometryType().
2064 : *
2065 : * @param hGeom handle on the geometry to get type from.
2066 : * @return the geometry type code.
2067 : */
2068 :
2069 5789 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2070 :
2071 : {
2072 5789 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2073 :
2074 5789 : return OGRGeometry::FromHandle(hGeom)->getGeometryType();
2075 : }
2076 :
2077 : /**
2078 : * \fn const char * OGRGeometry::getGeometryName() const;
2079 : *
2080 : * \brief Fetch WKT name for geometry type.
2081 : *
2082 : * There is no SFCOM analog to this method.
2083 : *
2084 : * This method is the same as the C function OGR_G_GetGeometryName().
2085 : *
2086 : * @return name used for this geometry type in well known text format. The
2087 : * returned pointer is to a static internal string and should not be modified
2088 : * or freed.
2089 : */
2090 :
2091 : /************************************************************************/
2092 : /* OGR_G_GetGeometryName() */
2093 : /************************************************************************/
2094 : /**
2095 : * \brief Fetch WKT name for geometry type.
2096 : *
2097 : * There is no SFCOM analog to this function.
2098 : *
2099 : * This function is the same as the CPP method OGRGeometry::getGeometryName().
2100 : *
2101 : * @param hGeom handle on the geometry to get name from.
2102 : * @return name used for this geometry type in well known text format.
2103 : */
2104 :
2105 18909 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2106 :
2107 : {
2108 18909 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2109 :
2110 18909 : return OGRGeometry::FromHandle(hGeom)->getGeometryName();
2111 : }
2112 :
2113 : /**
2114 : * \fn OGRGeometry *OGRGeometry::clone() const;
2115 : *
2116 : * \brief Make a copy of this object.
2117 : *
2118 : * This method relates to the SFCOM IGeometry::clone() method.
2119 : *
2120 : * This method is the same as the C function OGR_G_Clone().
2121 : *
2122 : * @return a new object instance with the same geometry, and spatial
2123 : * reference system as the original.
2124 : */
2125 :
2126 : /************************************************************************/
2127 : /* OGR_G_Clone() */
2128 : /************************************************************************/
2129 : /**
2130 : * \brief Make a copy of this object.
2131 : *
2132 : * This function relates to the SFCOM IGeometry::clone() method.
2133 : *
2134 : * This function is the same as the CPP method OGRGeometry::clone().
2135 : *
2136 : * @param hGeom handle on the geometry to clone from.
2137 : * @return a handle on the copy of the geometry with the spatial
2138 : * reference system as the original.
2139 : */
2140 :
2141 13564 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
2142 :
2143 : {
2144 13564 : VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
2145 :
2146 13564 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
2147 : }
2148 :
2149 : /**
2150 : * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
2151 : *
2152 : * \brief Returns spatial reference system for object.
2153 : *
2154 : * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
2155 : *
2156 : * This method is the same as the C function OGR_G_GetSpatialReference().
2157 : *
2158 : * @return a reference to the spatial reference object. The object may be
2159 : * shared with many geometry objects, and should not be modified.
2160 : */
2161 :
2162 : /************************************************************************/
2163 : /* OGR_G_GetSpatialReference() */
2164 : /************************************************************************/
2165 : /**
2166 : * \brief Returns spatial reference system for geometry.
2167 : *
2168 : * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
2169 : *
2170 : * This function is the same as the CPP method
2171 : * OGRGeometry::getSpatialReference().
2172 : *
2173 : * @param hGeom handle on the geometry to get spatial reference from.
2174 : * @return a reference to the spatial reference geometry, which should not be
2175 : * modified.
2176 : */
2177 :
2178 84 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
2179 :
2180 : {
2181 84 : VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
2182 :
2183 84 : return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
2184 168 : OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
2185 : }
2186 :
2187 : /**
2188 : * \fn void OGRGeometry::empty();
2189 : *
2190 : * \brief Clear geometry information.
2191 : * This restores the geometry to its initial
2192 : * state after construction, and before assignment of actual geometry.
2193 : *
2194 : * This method relates to the SFCOM IGeometry::Empty() method.
2195 : *
2196 : * This method is the same as the C function OGR_G_Empty().
2197 : */
2198 :
2199 : /************************************************************************/
2200 : /* OGR_G_Empty() */
2201 : /************************************************************************/
2202 : /**
2203 : * \brief Clear geometry information.
2204 : * This restores the geometry to its initial
2205 : * state after construction, and before assignment of actual geometry.
2206 : *
2207 : * This function relates to the SFCOM IGeometry::Empty() method.
2208 : *
2209 : * This function is the same as the CPP method OGRGeometry::empty().
2210 : *
2211 : * @param hGeom handle on the geometry to empty.
2212 : */
2213 :
2214 4 : void OGR_G_Empty(OGRGeometryH hGeom)
2215 :
2216 : {
2217 4 : VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
2218 :
2219 4 : OGRGeometry::FromHandle(hGeom)->empty();
2220 : }
2221 :
2222 : /**
2223 : * \fn OGRBoolean OGRGeometry::IsEmpty() const;
2224 : *
2225 : * \brief Returns TRUE (non-zero) if the object has no points.
2226 : *
2227 : * Normally this
2228 : * returns FALSE except between when an object is instantiated and points
2229 : * have been assigned.
2230 : *
2231 : * This method relates to the SFCOM IGeometry::IsEmpty() method.
2232 : *
2233 : * @return TRUE if object is empty, otherwise FALSE.
2234 : */
2235 :
2236 : /************************************************************************/
2237 : /* OGR_G_IsEmpty() */
2238 : /************************************************************************/
2239 :
2240 : /**
2241 : * \brief Test if the geometry is empty.
2242 : *
2243 : * This method is the same as the CPP method OGRGeometry::IsEmpty().
2244 : *
2245 : * @param hGeom The Geometry to test.
2246 : *
2247 : * @return TRUE if the geometry has no points, otherwise FALSE.
2248 : */
2249 :
2250 2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
2251 :
2252 : {
2253 2391 : VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
2254 :
2255 2391 : return OGRGeometry::FromHandle(hGeom)->IsEmpty();
2256 : }
2257 :
2258 : /************************************************************************/
2259 : /* IsValid() */
2260 : /************************************************************************/
2261 :
2262 : /**
2263 : * \brief Test if the geometry is valid.
2264 : *
2265 : * This method is the same as the C function OGR_G_IsValid().
2266 : *
2267 : * This method is built on the GEOS library, check it for the definition
2268 : * of the geometry operation.
2269 : * If OGR is built without the GEOS library, this method will always return
2270 : * FALSE.
2271 : *
2272 : *
2273 : * @return TRUE if the geometry has no points, otherwise FALSE.
2274 : */
2275 :
2276 7485 : OGRBoolean OGRGeometry::IsValid() const
2277 :
2278 : {
2279 7485 : if (IsSFCGALCompatible())
2280 : {
2281 : #ifndef HAVE_SFCGAL
2282 :
2283 : #ifdef HAVE_GEOS
2284 2 : if (wkbFlatten(getGeometryType()) == wkbTriangle)
2285 : {
2286 : // go on
2287 : }
2288 : else
2289 : #endif
2290 : {
2291 1 : CPLError(CE_Failure, CPLE_NotSupported,
2292 : "SFCGAL support not enabled.");
2293 1 : return FALSE;
2294 : }
2295 : #else
2296 : sfcgal_init();
2297 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
2298 : if (poThis == nullptr)
2299 : {
2300 : CPLError(CE_Failure, CPLE_IllegalArg,
2301 : "SFCGAL geometry returned is NULL");
2302 : return FALSE;
2303 : }
2304 :
2305 : const int res = sfcgal_geometry_is_valid(poThis);
2306 : sfcgal_geometry_delete(poThis);
2307 : return res == 1;
2308 : #endif
2309 : }
2310 :
2311 : {
2312 : #ifndef HAVE_GEOS
2313 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2314 : return FALSE;
2315 :
2316 : #else
2317 7484 : OGRBoolean bResult = FALSE;
2318 :
2319 : // Some invalid geometries, such as lines with one point, or
2320 : // rings that do not close, cannot be converted to GEOS.
2321 : // For validity checking we initialize the GEOS context with
2322 : // the warning handler as the error handler to avoid emitting
2323 : // CE_Failure when a geometry cannot be converted to GEOS.
2324 : GEOSContextHandle_t hGEOSCtxt =
2325 7484 : initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
2326 :
2327 7484 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2328 :
2329 7484 : if (hThisGeosGeom != nullptr)
2330 : {
2331 7483 : bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
2332 : #ifdef DEBUG_VERBOSE
2333 : if (!bResult)
2334 : {
2335 : char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
2336 : CPLDebug("OGR", "%s", pszReason);
2337 : GEOSFree_r(hGEOSCtxt, pszReason);
2338 : }
2339 : #endif
2340 7483 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2341 : }
2342 7484 : freeGEOSContext(hGEOSCtxt);
2343 :
2344 7484 : return bResult;
2345 :
2346 : #endif // HAVE_GEOS
2347 : }
2348 : }
2349 :
2350 : /************************************************************************/
2351 : /* OGR_G_IsValid() */
2352 : /************************************************************************/
2353 :
2354 : /**
2355 : * \brief Test if the geometry is valid.
2356 : *
2357 : * This function is the same as the C++ method OGRGeometry::IsValid().
2358 : *
2359 : * This function is built on the GEOS library, check it for the definition
2360 : * of the geometry operation.
2361 : * If OGR is built without the GEOS library, this function will always return
2362 : * FALSE.
2363 : *
2364 : * @param hGeom The Geometry to test.
2365 : *
2366 : * @return TRUE if the geometry has no points, otherwise FALSE.
2367 : */
2368 :
2369 23 : int OGR_G_IsValid(OGRGeometryH hGeom)
2370 :
2371 : {
2372 23 : VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
2373 :
2374 23 : return OGRGeometry::FromHandle(hGeom)->IsValid();
2375 : }
2376 :
2377 : /************************************************************************/
2378 : /* IsSimple() */
2379 : /************************************************************************/
2380 :
2381 : /**
2382 : * \brief Test if the geometry is simple.
2383 : *
2384 : * This method is the same as the C function OGR_G_IsSimple().
2385 : *
2386 : * This method is built on the GEOS library, check it for the definition
2387 : * of the geometry operation.
2388 : * If OGR is built without the GEOS library, this method will always return
2389 : * FALSE.
2390 : *
2391 : *
2392 : * @return TRUE if the geometry has no points, otherwise FALSE.
2393 : */
2394 :
2395 5 : OGRBoolean OGRGeometry::IsSimple() const
2396 :
2397 : {
2398 : #ifndef HAVE_GEOS
2399 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2400 : return FALSE;
2401 :
2402 : #else
2403 :
2404 5 : OGRBoolean bResult = FALSE;
2405 :
2406 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2407 5 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2408 :
2409 5 : if (hThisGeosGeom != nullptr)
2410 : {
2411 5 : bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
2412 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2413 : }
2414 5 : freeGEOSContext(hGEOSCtxt);
2415 :
2416 5 : return bResult;
2417 :
2418 : #endif // HAVE_GEOS
2419 : }
2420 :
2421 : /**
2422 : * \brief Returns TRUE if the geometry is simple.
2423 : *
2424 : * Returns TRUE if the geometry has no anomalous geometric points, such
2425 : * as self intersection or self tangency. The description of each
2426 : * instantiable geometric class will include the specific conditions that
2427 : * cause an instance of that class to be classified as not simple.
2428 : *
2429 : * This function is the same as the C++ method OGRGeometry::IsSimple() method.
2430 : *
2431 : * If OGR is built without the GEOS library, this function will always return
2432 : * FALSE.
2433 : *
2434 : * @param hGeom The Geometry to test.
2435 : *
2436 : * @return TRUE if object is simple, otherwise FALSE.
2437 : */
2438 :
2439 5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
2440 :
2441 : {
2442 5 : VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
2443 :
2444 5 : return OGRGeometry::FromHandle(hGeom)->IsSimple();
2445 : }
2446 :
2447 : /************************************************************************/
2448 : /* IsRing() */
2449 : /************************************************************************/
2450 :
2451 : /**
2452 : * \brief Test if the geometry is a ring
2453 : *
2454 : * This method is the same as the C function OGR_G_IsRing().
2455 : *
2456 : * This method 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 method will always return
2459 : * FALSE.
2460 : *
2461 : *
2462 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2463 : * length and closure (self-intersection is not checked), otherwise FALSE.
2464 : */
2465 :
2466 1 : OGRBoolean OGRGeometry::IsRing() const
2467 :
2468 : {
2469 : #ifndef HAVE_GEOS
2470 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2471 : return FALSE;
2472 :
2473 : #else
2474 :
2475 1 : OGRBoolean bResult = FALSE;
2476 :
2477 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2478 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2479 :
2480 1 : if (hThisGeosGeom != nullptr)
2481 : {
2482 1 : bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
2483 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2484 : }
2485 1 : freeGEOSContext(hGEOSCtxt);
2486 :
2487 1 : return bResult;
2488 :
2489 : #endif // HAVE_GEOS
2490 : }
2491 :
2492 : /************************************************************************/
2493 : /* OGR_G_IsRing() */
2494 : /************************************************************************/
2495 :
2496 : /**
2497 : * \brief Test if the geometry is a ring
2498 : *
2499 : * This function is the same as the C++ method OGRGeometry::IsRing().
2500 : *
2501 : * This function is built on the GEOS library, check it for the definition
2502 : * of the geometry operation.
2503 : * If OGR is built without the GEOS library, this function will always return
2504 : * FALSE.
2505 : *
2506 : * @param hGeom The Geometry to test.
2507 : *
2508 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2509 : * length and closure (self-intersection is not checked), otherwise FALSE.
2510 : */
2511 :
2512 1 : int OGR_G_IsRing(OGRGeometryH hGeom)
2513 :
2514 : {
2515 1 : VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
2516 :
2517 1 : return OGRGeometry::FromHandle(hGeom)->IsRing();
2518 : }
2519 :
2520 : /************************************************************************/
2521 : /* OGRFromOGCGeomType() */
2522 : /************************************************************************/
2523 :
2524 : /** Map OGC geometry format type to corresponding OGR constants.
2525 : * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
2526 : * @return OGR constant.
2527 : */
2528 3210 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2529 : {
2530 3210 : OGRwkbGeometryType eType = wkbUnknown;
2531 3210 : bool bConvertTo3D = false;
2532 3210 : bool bIsMeasured = false;
2533 3210 : if (*pszGeomType != '\0')
2534 : {
2535 3204 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2536 3204 : if (ch == 'm' || ch == 'M')
2537 : {
2538 2 : bIsMeasured = true;
2539 2 : if (strlen(pszGeomType) > 1)
2540 2 : ch = pszGeomType[strlen(pszGeomType) - 2];
2541 : }
2542 3204 : if (ch == 'z' || ch == 'Z')
2543 : {
2544 34 : bConvertTo3D = true;
2545 : }
2546 : }
2547 :
2548 3210 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2549 971 : eType = wkbPoint;
2550 2239 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2551 184 : eType = wkbLineString;
2552 2055 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2553 891 : eType = wkbPolygon;
2554 1164 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2555 24 : eType = wkbMultiPoint;
2556 1140 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2557 13 : eType = wkbMultiLineString;
2558 1127 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2559 82 : eType = wkbMultiPolygon;
2560 1045 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2561 4 : eType = wkbGeometryCollection;
2562 1041 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2563 351 : eType = wkbCircularString;
2564 690 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2565 0 : eType = wkbCompoundCurve;
2566 690 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2567 16 : eType = wkbCurvePolygon;
2568 674 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2569 2 : eType = wkbMultiCurve;
2570 672 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2571 0 : eType = wkbMultiSurface;
2572 672 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2573 0 : eType = wkbTriangle;
2574 672 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2575 1 : eType = wkbPolyhedralSurface;
2576 671 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2577 5 : eType = wkbTIN;
2578 666 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2579 3 : eType = wkbCurve;
2580 663 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2581 3 : eType = wkbSurface;
2582 : else
2583 660 : eType = wkbUnknown;
2584 :
2585 3210 : if (bConvertTo3D)
2586 34 : eType = wkbSetZ(eType);
2587 3210 : if (bIsMeasured)
2588 2 : eType = wkbSetM(eType);
2589 :
2590 3210 : return eType;
2591 : }
2592 :
2593 : /************************************************************************/
2594 : /* OGRToOGCGeomType() */
2595 : /************************************************************************/
2596 :
2597 : /** Map OGR geometry format constants to corresponding OGC geometry type.
2598 : * @param eGeomType OGR geometry type
2599 : * @param bCamelCase Whether the return should be like "MultiPoint"
2600 : * (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
2601 : * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
2602 : * Default is false.
2603 : * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
2604 : * suffix. Default is false.
2605 : * @return string with OGC geometry type (without dimensionality)
2606 : */
2607 2986 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2608 : bool bAddZM, bool bSpaceBeforeZM)
2609 : {
2610 2986 : const char *pszRet = "";
2611 2986 : switch (wkbFlatten(eGeomType))
2612 : {
2613 1568 : case wkbUnknown:
2614 1568 : pszRet = "Geometry";
2615 1568 : break;
2616 560 : case wkbPoint:
2617 560 : pszRet = "Point";
2618 560 : break;
2619 153 : case wkbLineString:
2620 153 : pszRet = "LineString";
2621 153 : break;
2622 370 : case wkbPolygon:
2623 370 : pszRet = "Polygon";
2624 370 : break;
2625 48 : case wkbMultiPoint:
2626 48 : pszRet = "MultiPoint";
2627 48 : break;
2628 56 : case wkbMultiLineString:
2629 56 : pszRet = "MultiLineString";
2630 56 : break;
2631 74 : case wkbMultiPolygon:
2632 74 : pszRet = "MultiPolygon";
2633 74 : break;
2634 54 : case wkbGeometryCollection:
2635 54 : pszRet = "GeometryCollection";
2636 54 : break;
2637 9 : case wkbCircularString:
2638 9 : pszRet = "CircularString";
2639 9 : break;
2640 3 : case wkbCompoundCurve:
2641 3 : pszRet = "CompoundCurve";
2642 3 : break;
2643 12 : case wkbCurvePolygon:
2644 12 : pszRet = "CurvePolygon";
2645 12 : break;
2646 2 : case wkbMultiCurve:
2647 2 : pszRet = "MultiCurve";
2648 2 : break;
2649 3 : case wkbMultiSurface:
2650 3 : pszRet = "MultiSurface";
2651 3 : break;
2652 3 : case wkbTriangle:
2653 3 : pszRet = "Triangle";
2654 3 : break;
2655 5 : case wkbPolyhedralSurface:
2656 5 : pszRet = "PolyhedralSurface";
2657 5 : break;
2658 1 : case wkbTIN:
2659 1 : pszRet = "Tin";
2660 1 : break;
2661 3 : case wkbCurve:
2662 3 : pszRet = "Curve";
2663 3 : break;
2664 3 : case wkbSurface:
2665 3 : pszRet = "Surface";
2666 3 : break;
2667 59 : default:
2668 59 : break;
2669 : }
2670 2986 : if (bAddZM)
2671 : {
2672 67 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2673 67 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2674 67 : if (bHasZ || bHasM)
2675 : {
2676 10 : if (bSpaceBeforeZM)
2677 1 : pszRet = CPLSPrintf("%s ", pszRet);
2678 10 : if (bHasZ)
2679 9 : pszRet = CPLSPrintf("%sZ", pszRet);
2680 10 : if (bHasM)
2681 5 : pszRet = CPLSPrintf("%sM", pszRet);
2682 : }
2683 : }
2684 2986 : if (!bCamelCase)
2685 2918 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2686 2986 : return pszRet;
2687 : }
2688 :
2689 : /************************************************************************/
2690 : /* OGRGeometryTypeToName() */
2691 : /************************************************************************/
2692 :
2693 : /**
2694 : * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
2695 : * value. The returned value should not be modified, or freed by the
2696 : * application.
2697 : *
2698 : * This function is C callable.
2699 : *
2700 : * @param eType the geometry type.
2701 : *
2702 : * @return internal human readable string, or NULL on failure.
2703 : */
2704 :
2705 393 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2706 :
2707 : {
2708 393 : bool b3D = wkbHasZ(eType);
2709 393 : bool bMeasured = wkbHasM(eType);
2710 :
2711 393 : switch (wkbFlatten(eType))
2712 : {
2713 34 : case wkbUnknown:
2714 34 : if (b3D && bMeasured)
2715 0 : return "3D Measured Unknown (any)";
2716 34 : else if (b3D)
2717 1 : return "3D Unknown (any)";
2718 33 : else if (bMeasured)
2719 0 : return "Measured Unknown (any)";
2720 : else
2721 33 : return "Unknown (any)";
2722 :
2723 59 : case wkbPoint:
2724 59 : if (b3D && bMeasured)
2725 3 : return "3D Measured Point";
2726 56 : else if (b3D)
2727 12 : return "3D Point";
2728 44 : else if (bMeasured)
2729 5 : return "Measured Point";
2730 : else
2731 39 : return "Point";
2732 :
2733 37 : case wkbLineString:
2734 37 : if (b3D && bMeasured)
2735 0 : return "3D Measured Line String";
2736 37 : else if (b3D)
2737 9 : return "3D Line String";
2738 28 : else if (bMeasured)
2739 0 : return "Measured Line String";
2740 : else
2741 28 : return "Line String";
2742 :
2743 62 : case wkbPolygon:
2744 62 : if (b3D && bMeasured)
2745 0 : return "3D Measured Polygon";
2746 62 : else if (b3D)
2747 8 : return "3D Polygon";
2748 54 : else if (bMeasured)
2749 0 : return "Measured Polygon";
2750 : else
2751 54 : return "Polygon";
2752 :
2753 19 : case wkbMultiPoint:
2754 19 : if (b3D && bMeasured)
2755 0 : return "3D Measured Multi Point";
2756 19 : else if (b3D)
2757 9 : return "3D Multi Point";
2758 10 : else if (bMeasured)
2759 0 : return "Measured Multi Point";
2760 : else
2761 10 : return "Multi Point";
2762 :
2763 14 : case wkbMultiLineString:
2764 14 : if (b3D && bMeasured)
2765 0 : return "3D Measured Multi Line String";
2766 14 : else if (b3D)
2767 6 : return "3D Multi Line String";
2768 8 : else if (bMeasured)
2769 0 : return "Measured Multi Line String";
2770 : else
2771 8 : return "Multi Line String";
2772 :
2773 19 : case wkbMultiPolygon:
2774 19 : if (b3D && bMeasured)
2775 0 : return "3D Measured Multi Polygon";
2776 19 : else if (b3D)
2777 8 : return "3D Multi Polygon";
2778 11 : else if (bMeasured)
2779 0 : return "Measured Multi Polygon";
2780 : else
2781 11 : return "Multi Polygon";
2782 :
2783 25 : case wkbGeometryCollection:
2784 25 : if (b3D && bMeasured)
2785 0 : return "3D Measured Geometry Collection";
2786 25 : else if (b3D)
2787 10 : return "3D Geometry Collection";
2788 15 : else if (bMeasured)
2789 0 : return "Measured Geometry Collection";
2790 : else
2791 15 : return "Geometry Collection";
2792 :
2793 0 : case wkbCircularString:
2794 0 : if (b3D && bMeasured)
2795 0 : return "3D Measured Circular String";
2796 0 : else if (b3D)
2797 0 : return "3D Circular String";
2798 0 : else if (bMeasured)
2799 0 : return "Measured Circular String";
2800 : else
2801 0 : return "Circular String";
2802 :
2803 1 : case wkbCompoundCurve:
2804 1 : if (b3D && bMeasured)
2805 0 : return "3D Measured Compound Curve";
2806 1 : else if (b3D)
2807 0 : return "3D Compound Curve";
2808 1 : else if (bMeasured)
2809 0 : return "Measured Compound Curve";
2810 : else
2811 1 : return "Compound Curve";
2812 :
2813 0 : case wkbCurvePolygon:
2814 0 : if (b3D && bMeasured)
2815 0 : return "3D Measured Curve Polygon";
2816 0 : else if (b3D)
2817 0 : return "3D Curve Polygon";
2818 0 : else if (bMeasured)
2819 0 : return "Measured Curve Polygon";
2820 : else
2821 0 : return "Curve Polygon";
2822 :
2823 0 : case wkbMultiCurve:
2824 0 : if (b3D && bMeasured)
2825 0 : return "3D Measured Multi Curve";
2826 0 : else if (b3D)
2827 0 : return "3D Multi Curve";
2828 0 : else if (bMeasured)
2829 0 : return "Measured Multi Curve";
2830 : else
2831 0 : return "Multi Curve";
2832 :
2833 0 : case wkbMultiSurface:
2834 0 : if (b3D && bMeasured)
2835 0 : return "3D Measured Multi Surface";
2836 0 : else if (b3D)
2837 0 : return "3D Multi Surface";
2838 0 : else if (bMeasured)
2839 0 : return "Measured Multi Surface";
2840 : else
2841 0 : return "Multi Surface";
2842 :
2843 4 : case wkbCurve:
2844 4 : if (b3D && bMeasured)
2845 1 : return "3D Measured Curve";
2846 3 : else if (b3D)
2847 1 : return "3D Curve";
2848 2 : else if (bMeasured)
2849 1 : return "Measured Curve";
2850 : else
2851 1 : return "Curve";
2852 :
2853 4 : case wkbSurface:
2854 4 : if (b3D && bMeasured)
2855 1 : return "3D Measured Surface";
2856 3 : else if (b3D)
2857 1 : return "3D Surface";
2858 2 : else if (bMeasured)
2859 1 : return "Measured Surface";
2860 : else
2861 1 : return "Surface";
2862 :
2863 0 : case wkbTriangle:
2864 0 : if (b3D && bMeasured)
2865 0 : return "3D Measured Triangle";
2866 0 : else if (b3D)
2867 0 : return "3D Triangle";
2868 0 : else if (bMeasured)
2869 0 : return "Measured Triangle";
2870 : else
2871 0 : return "Triangle";
2872 :
2873 0 : case wkbPolyhedralSurface:
2874 0 : if (b3D && bMeasured)
2875 0 : return "3D Measured PolyhedralSurface";
2876 0 : else if (b3D)
2877 0 : return "3D PolyhedralSurface";
2878 0 : else if (bMeasured)
2879 0 : return "Measured PolyhedralSurface";
2880 : else
2881 0 : return "PolyhedralSurface";
2882 :
2883 2 : case wkbTIN:
2884 2 : if (b3D && bMeasured)
2885 0 : return "3D Measured TIN";
2886 2 : else if (b3D)
2887 0 : return "3D TIN";
2888 2 : else if (bMeasured)
2889 0 : return "Measured TIN";
2890 : else
2891 2 : return "TIN";
2892 :
2893 112 : case wkbNone:
2894 112 : return "None";
2895 :
2896 1 : default:
2897 : {
2898 1 : return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
2899 : }
2900 : }
2901 : }
2902 :
2903 : /************************************************************************/
2904 : /* OGRMergeGeometryTypes() */
2905 : /************************************************************************/
2906 :
2907 : /**
2908 : * \brief Find common geometry type.
2909 : *
2910 : * Given two geometry types, find the most specific common
2911 : * type. Normally used repeatedly with the geometries in a
2912 : * layer to try and establish the most specific geometry type
2913 : * that can be reported for the layer.
2914 : *
2915 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
2916 : * geometry types with nothing in common but the base geometry
2917 : * type. wkbNone should be used to indicate that no geometries
2918 : * have been encountered yet, and means the first geometry
2919 : * encountered will establish the preliminary type.
2920 : *
2921 : * @param eMain the first input geometry type.
2922 : * @param eExtra the second input geometry type.
2923 : *
2924 : * @return the merged geometry type.
2925 : */
2926 :
2927 0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
2928 : OGRwkbGeometryType eExtra)
2929 :
2930 : {
2931 0 : return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
2932 : }
2933 :
2934 : /**
2935 : * \brief Find common geometry type.
2936 : *
2937 : * Given two geometry types, find the most specific common
2938 : * type. Normally used repeatedly with the geometries in a
2939 : * layer to try and establish the most specific geometry type
2940 : * that can be reported for the layer.
2941 : *
2942 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
2943 : * geometry types with nothing in common but the base geometry
2944 : * type. wkbNone should be used to indicate that no geometries
2945 : * have been encountered yet, and means the first geometry
2946 : * encountered will establish the preliminary type.
2947 : *
2948 : * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
2949 : * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
2950 : * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
2951 : * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
2952 : *
2953 : * @param eMain the first input geometry type.
2954 : * @param eExtra the second input geometry type.
2955 : * @param bAllowPromotingToCurves determine if promotion to curve type
2956 : * must be done.
2957 : *
2958 : * @return the merged geometry type.
2959 : *
2960 : */
2961 :
2962 575 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
2963 : OGRwkbGeometryType eExtra,
2964 : int bAllowPromotingToCurves)
2965 :
2966 : {
2967 575 : OGRwkbGeometryType eFMain = wkbFlatten(eMain);
2968 575 : OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
2969 :
2970 575 : const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
2971 575 : const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
2972 :
2973 575 : if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
2974 17 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
2975 :
2976 558 : if (eFMain == wkbNone)
2977 2 : return eExtra;
2978 :
2979 556 : if (eFExtra == wkbNone)
2980 0 : return eMain;
2981 :
2982 556 : if (eFMain == eFExtra)
2983 : {
2984 539 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
2985 : }
2986 :
2987 17 : if (bAllowPromotingToCurves)
2988 : {
2989 17 : if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
2990 4 : return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
2991 :
2992 13 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
2993 3 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
2994 :
2995 10 : if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
2996 6 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
2997 : }
2998 :
2999 : // One is subclass of the other one
3000 4 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3001 : {
3002 0 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3003 : }
3004 4 : else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3005 : {
3006 0 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3007 : }
3008 :
3009 : // Nothing apparently in common.
3010 4 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3011 : }
3012 :
3013 : /**
3014 : * \fn void OGRGeometry::flattenTo2D();
3015 : *
3016 : * \brief Convert geometry to strictly 2D.
3017 : * In a sense this converts all Z coordinates
3018 : * to 0.0.
3019 : *
3020 : * This method is the same as the C function OGR_G_FlattenTo2D().
3021 : */
3022 :
3023 : /************************************************************************/
3024 : /* OGR_G_FlattenTo2D() */
3025 : /************************************************************************/
3026 : /**
3027 : * \brief Convert geometry to strictly 2D.
3028 : * In a sense this converts all Z coordinates
3029 : * to 0.0.
3030 : *
3031 : * This function is the same as the CPP method OGRGeometry::flattenTo2D().
3032 : *
3033 : * @param hGeom handle on the geometry to convert.
3034 : */
3035 :
3036 31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
3037 :
3038 : {
3039 31 : OGRGeometry::FromHandle(hGeom)->flattenTo2D();
3040 31 : }
3041 :
3042 : /************************************************************************/
3043 : /* exportToGML() */
3044 : /************************************************************************/
3045 :
3046 : /**
3047 : * \fn char *OGRGeometry::exportToGML( const char* const *
3048 : * papszOptions = NULL ) const;
3049 : *
3050 : * \brief Convert a geometry into GML format.
3051 : *
3052 : * The GML geometry is expressed directly in terms of GML basic data
3053 : * types assuming the this is available in the gml namespace. The returned
3054 : * string should be freed with CPLFree() when no longer required.
3055 : *
3056 : * The supported options are :
3057 : * <ul>
3058 : * <li> FORMAT=GML2/GML3/GML32.
3059 : * If not set, it will default to GML 2.1.2 output.
3060 : * </li>
3061 : * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
3062 : * To use gml:Curve element for linestrings.
3063 : * Otherwise gml:LineString will be used .
3064 : * </li>
3065 : * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
3066 : * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES.
3067 : * If YES, SRS with EPSG authority will be written with the
3068 : * "urn:ogc:def:crs:EPSG::" prefix.
3069 : * In the case the SRS should be treated as lat/long or
3070 : * northing/easting, then the function will take care of coordinate order
3071 : * swapping if the data axis to CRS axis mapping indicates it.
3072 : * If set to NO, SRS with EPSG authority will be written with the "EPSG:"
3073 : * prefix, even if they are in lat/long order.
3074 : * </li>
3075 : * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
3076 : * Defaults to OGC_URN. If SHORT, then srsName will be in
3077 : * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
3078 : * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
3079 : * then srsName will be in the form
3080 : * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
3081 : * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
3082 : * or northing/easting, then the function will take care of coordinate
3083 : * order swapping if the data axis to CRS axis mapping indicates it.
3084 : * </li>
3085 : * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
3086 : * top-level geometry element with the provided value.
3087 : * Required for GML 3.2 compatibility.
3088 : * </li>
3089 : * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
3090 : * FORMAT=GML3/GML32) Default to POSLIST.
3091 : * For 2.5D geometries, define the location where to attach the
3092 : * srsDimension attribute.
3093 : * There are diverging implementations. Some put in on the
3094 : * <gml:posList> element, other on the top geometry element.
3095 : * </li>
3096 : * <li> NAMESPACE_DECL=YES/NO. If set to YES,
3097 : * xmlns:gml="http://www.opengis.net/gml" will be added to the root node
3098 : * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
3099 : * </li>
3100 : * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
3101 : * Resolution for the coordinate precision of the X and Y coordinates.
3102 : * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
3103 : * to 5 decimal digits. 0 for the default behavior.
3104 : * </li>
3105 : * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
3106 : * Resolution for the coordinate precision of the Z coordinates.
3107 : * Expressed in the units of the Z axis of the SRS.
3108 : * 0 for the default behavior.
3109 : * </li>
3110 : * </ul>
3111 : *
3112 : * This method is the same as the C function OGR_G_ExportToGMLEx().
3113 : *
3114 : * @param papszOptions NULL-terminated list of options.
3115 : * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
3116 : */
3117 :
3118 251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
3119 : {
3120 251 : return OGR_G_ExportToGMLEx(
3121 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3122 251 : const_cast<char **>(papszOptions));
3123 : }
3124 :
3125 : /************************************************************************/
3126 : /* exportToKML() */
3127 : /************************************************************************/
3128 :
3129 : /**
3130 : * \fn char *OGRGeometry::exportToKML() const;
3131 : *
3132 : * \brief Convert a geometry into KML format.
3133 : *
3134 : * The returned string should be freed with CPLFree() when no longer required.
3135 : *
3136 : * This method is the same as the C function OGR_G_ExportToKML().
3137 : *
3138 : * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
3139 : */
3140 :
3141 0 : char *OGRGeometry::exportToKML() const
3142 : {
3143 0 : return OGR_G_ExportToKML(
3144 0 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
3145 : }
3146 :
3147 : /************************************************************************/
3148 : /* exportToJson() */
3149 : /************************************************************************/
3150 :
3151 : /**
3152 : * \fn char *OGRGeometry::exportToJson() const;
3153 : *
3154 : * \brief Convert a geometry into GeoJSON format.
3155 : *
3156 : * The returned string should be freed with CPLFree() when no longer required.
3157 : *
3158 : * The following options are supported :
3159 : * <ul>
3160 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
3161 : * (added in GDAL 3.9)</li>
3162 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
3163 : * (added in GDAL 3.9)</li>
3164 : * </ul>
3165 : *
3166 : * This method is the same as the C function OGR_G_ExportToJson().
3167 : *
3168 : * @param papszOptions Null terminated list of options, or null (added in 3.9)
3169 : * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
3170 : */
3171 :
3172 43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
3173 : {
3174 43 : OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3175 43 : return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3176 43 : const_cast<char **>(papszOptions));
3177 : }
3178 :
3179 : /************************************************************************/
3180 : /* OGRSetGenerate_DB2_V72_BYTE_ORDER() */
3181 : /************************************************************************/
3182 :
3183 : /**
3184 : * \brief Special entry point to enable the hack for generating DB2 V7.2 style
3185 : * WKB.
3186 : *
3187 : * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
3188 : * order in WKB. This entry point is used to turn on or off the generation of
3189 : * such WKB.
3190 : */
3191 4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
3192 :
3193 : {
3194 : #if defined(HACK_FOR_IBM_DB2_V72)
3195 4 : OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
3196 4 : return OGRERR_NONE;
3197 : #else
3198 : if (bGenerate_DB2_V72_BYTE_ORDER)
3199 : return OGRERR_FAILURE;
3200 : else
3201 : return OGRERR_NONE;
3202 : #endif
3203 : }
3204 :
3205 : /************************************************************************/
3206 : /* OGRGetGenerate_DB2_V72_BYTE_ORDER() */
3207 : /* */
3208 : /* This is a special entry point to get the value of static flag */
3209 : /* OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER. */
3210 : /************************************************************************/
3211 0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
3212 : {
3213 0 : return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
3214 : }
3215 :
3216 : /************************************************************************/
3217 : /* createGEOSContext() */
3218 : /************************************************************************/
3219 :
3220 : /** Create a new GEOS context.
3221 : * @return a new GEOS context (to be freed with freeGEOSContext())
3222 : */
3223 84969 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
3224 : {
3225 : #ifndef HAVE_GEOS
3226 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3227 : return nullptr;
3228 : #else
3229 84969 : return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
3230 : #endif
3231 : }
3232 :
3233 : /************************************************************************/
3234 : /* freeGEOSContext() */
3235 : /************************************************************************/
3236 :
3237 : /** Destroy a GEOS context.
3238 : * @param hGEOSCtxt GEOS context
3239 : */
3240 92242 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
3241 : {
3242 : (void)hGEOSCtxt;
3243 : #ifdef HAVE_GEOS
3244 92242 : if (hGEOSCtxt != nullptr)
3245 : {
3246 92242 : finishGEOS_r(hGEOSCtxt);
3247 : }
3248 : #endif
3249 92242 : }
3250 : #ifdef HAVE_GEOS
3251 :
3252 : /************************************************************************/
3253 : /* canConvertToMultiPolygon() */
3254 : /************************************************************************/
3255 :
3256 148 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
3257 : {
3258 338 : for (const auto *poSubGeom : *poGC)
3259 : {
3260 : const OGRwkbGeometryType eSubGeomType =
3261 272 : wkbFlatten(poSubGeom->getGeometryType());
3262 272 : if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
3263 164 : eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
3264 : {
3265 82 : return false;
3266 : }
3267 : }
3268 :
3269 66 : return true;
3270 : }
3271 :
3272 : /************************************************************************/
3273 : /* GEOSWarningSilencer */
3274 : /************************************************************************/
3275 :
3276 : /** Class that can be used to silence GEOS messages while in-scope. */
3277 : class GEOSWarningSilencer
3278 : {
3279 : public:
3280 5534 : explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
3281 5534 : : m_poContext(poContext)
3282 : {
3283 5534 : GEOSContext_setErrorHandler_r(m_poContext, nullptr);
3284 5534 : GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
3285 5534 : }
3286 :
3287 5534 : ~GEOSWarningSilencer()
3288 5534 : {
3289 5534 : GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
3290 5534 : GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
3291 5534 : }
3292 :
3293 : CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
3294 :
3295 : private:
3296 : GEOSContextHandle_t m_poContext{nullptr};
3297 : };
3298 :
3299 : /************************************************************************/
3300 : /* repairForGEOS() */
3301 : /************************************************************************/
3302 :
3303 : /** Modify an OGRGeometry so that it can be converted into GEOS.
3304 : * Modifications include closing unclosed rings and adding redundant vertices
3305 : * to reach minimum point limits in GEOS.
3306 : *
3307 : * It is assumed that the input is a non-curved type that can be
3308 : * represented in GEOS.
3309 : *
3310 : * @param poGeom the geometry to modify
3311 : * @return an OGRGeometry that can be converted to GEOS using WKB
3312 : */
3313 25 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
3314 : {
3315 : #if GEOS_VERSION_MAJOR >= 3 || \
3316 : (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
3317 : static constexpr int MIN_RING_POINTS = 3;
3318 : #else
3319 : static constexpr int MIN_RING_POINTS = 4;
3320 : #endif
3321 :
3322 25 : const auto eType = wkbFlatten(poGeom->getGeometryType());
3323 :
3324 25 : if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
3325 : {
3326 5 : std::unique_ptr<OGRGeometryCollection> poRet;
3327 5 : if (eType == wkbGeometryCollection)
3328 : {
3329 2 : poRet = std::make_unique<OGRGeometryCollection>();
3330 : }
3331 3 : else if (eType == wkbMultiPolygon)
3332 : {
3333 3 : poRet = std::make_unique<OGRMultiPolygon>();
3334 : }
3335 0 : else if (eType == wkbMultiLineString)
3336 : {
3337 0 : poRet = std::make_unique<OGRMultiLineString>();
3338 : }
3339 0 : else if (eType == wkbMultiPoint)
3340 : {
3341 0 : poRet = std::make_unique<OGRMultiPoint>();
3342 : }
3343 : else
3344 : {
3345 0 : CPLError(CE_Failure, CPLE_AppDefined,
3346 : "Unexpected geometry type: %s",
3347 : OGRGeometryTypeToName(eType));
3348 0 : return nullptr;
3349 : }
3350 :
3351 5 : const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
3352 15 : for (const auto *poSubGeomIn : *poColl)
3353 : {
3354 10 : std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
3355 10 : poRet->addGeometry(std::move(poSubGeom));
3356 : }
3357 :
3358 5 : return poRet;
3359 : }
3360 :
3361 20 : if (eType == wkbPoint)
3362 : {
3363 0 : return std::unique_ptr<OGRGeometry>(poGeom->clone());
3364 : }
3365 20 : if (eType == wkbLineString)
3366 : {
3367 : std::unique_ptr<OGRLineString> poLineString(
3368 4 : poGeom->toLineString()->clone());
3369 2 : if (poLineString->getNumPoints() == 1)
3370 : {
3371 4 : OGRPoint oPoint;
3372 2 : poLineString->getPoint(0, &oPoint);
3373 2 : poLineString->addPoint(&oPoint);
3374 : }
3375 2 : return poLineString;
3376 : }
3377 18 : if (eType == wkbPolygon)
3378 : {
3379 36 : std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
3380 18 : poPolygon->closeRings();
3381 :
3382 : // make sure rings have enough points
3383 38 : for (auto *poRing : *poPolygon)
3384 : {
3385 24 : while (poRing->getNumPoints() < MIN_RING_POINTS)
3386 : {
3387 8 : OGRPoint oPoint;
3388 4 : poRing->getPoint(0, &oPoint);
3389 4 : poRing->addPoint(&oPoint);
3390 : }
3391 : }
3392 :
3393 18 : return poPolygon;
3394 : }
3395 :
3396 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
3397 : OGRGeometryTypeToName(eType));
3398 0 : return nullptr;
3399 : }
3400 :
3401 : /************************************************************************/
3402 : /* convertToGEOSGeom() */
3403 : /************************************************************************/
3404 :
3405 245055 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3406 : const OGRGeometry *poGeom)
3407 : {
3408 245055 : GEOSGeom hGeom = nullptr;
3409 245055 : const size_t nDataSize = poGeom->WkbSize();
3410 : unsigned char *pabyData =
3411 245055 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3412 : #if GEOS_VERSION_MAJOR > 3 || \
3413 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3414 245055 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3415 : #else
3416 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3417 : #endif
3418 245055 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3419 : {
3420 244035 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3421 : }
3422 245055 : CPLFree(pabyData);
3423 :
3424 245055 : return hGeom;
3425 : }
3426 : #endif
3427 :
3428 : /************************************************************************/
3429 : /* exportToGEOS() */
3430 : /************************************************************************/
3431 :
3432 : /** Returns a GEOSGeom object corresponding to the geometry.
3433 : *
3434 : * @param hGEOSCtxt GEOS context
3435 : * @param bRemoveEmptyParts Whether empty parts of the geometry should be
3436 : * removed before exporting to GEOS (GDAL >= 3.10)
3437 : * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
3438 : * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
3439 : * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
3440 : * @return a GEOSGeom object corresponding to the geometry (to be freed with
3441 : * GEOSGeom_destroy_r()), or NULL in case of error
3442 : */
3443 245040 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
3444 : bool bRemoveEmptyParts,
3445 : bool bAddPointsIfNeeded) const
3446 : {
3447 : (void)hGEOSCtxt;
3448 : (void)bRemoveEmptyParts;
3449 : (void)bAddPointsIfNeeded;
3450 :
3451 : #ifndef HAVE_GEOS
3452 :
3453 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3454 : return nullptr;
3455 :
3456 : #else
3457 :
3458 245040 : if (hGEOSCtxt == nullptr)
3459 0 : return nullptr;
3460 :
3461 245040 : const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
3462 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3463 : // POINT EMPTY is exported to WKB as if it were POINT(0 0),
3464 : // so that particular case is necessary.
3465 : if (eType == wkbPoint && IsEmpty())
3466 : {
3467 : return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
3468 : }
3469 : #endif
3470 :
3471 245040 : GEOSGeom hGeom = nullptr;
3472 :
3473 245040 : std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
3474 245040 : const OGRGeometry *poGeosInput = this;
3475 :
3476 245040 : const bool bHasZ = poGeosInput->Is3D();
3477 245040 : bool bHasM = poGeosInput->IsMeasured();
3478 :
3479 245040 : if (poGeosInput->hasCurveGeometry())
3480 : {
3481 865 : poModifiedInput.reset(poGeosInput->getLinearGeometry());
3482 865 : poGeosInput = poModifiedInput.get();
3483 : }
3484 :
3485 245040 : if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
3486 : {
3487 1 : if (!poModifiedInput)
3488 : {
3489 1 : poModifiedInput.reset(poGeosInput->clone());
3490 1 : poGeosInput = poModifiedInput.get();
3491 : }
3492 1 : poModifiedInput->removeEmptyParts();
3493 : }
3494 :
3495 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3496 : // GEOS < 3.12 doesn't support M dimension
3497 : if (bHasM)
3498 : {
3499 : if (!poModifiedInput)
3500 : {
3501 : poModifiedInput.reset(poGeosInput->clone());
3502 : poGeosInput = poModifiedInput.get();
3503 : }
3504 : poModifiedInput->setMeasured(false);
3505 : bHasM = false;
3506 : }
3507 : #endif
3508 :
3509 245040 : if (eType == wkbTriangle)
3510 : {
3511 : poModifiedInput =
3512 97 : std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
3513 97 : poGeosInput = poModifiedInput.get();
3514 : }
3515 244943 : else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3516 : {
3517 844 : if (!poModifiedInput)
3518 : {
3519 844 : poModifiedInput.reset(poGeosInput->clone());
3520 : }
3521 :
3522 2532 : poModifiedInput = OGRGeometryFactory::forceTo(
3523 844 : std::move(poModifiedInput),
3524 844 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
3525 844 : poGeosInput = poModifiedInput.get();
3526 : }
3527 244247 : else if (eType == wkbGeometryCollection &&
3528 148 : CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
3529 : {
3530 66 : if (!poModifiedInput)
3531 : {
3532 64 : poModifiedInput.reset(poGeosInput->clone());
3533 : }
3534 :
3535 : // Force into a MultiPolygon, then back to a GeometryCollection.
3536 : // This gets rid of fancy types like TIN and PolyhedralSurface that
3537 : // GEOS doesn't understand and flattens nested collections.
3538 198 : poModifiedInput = OGRGeometryFactory::forceTo(
3539 66 : std::move(poModifiedInput),
3540 66 : OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
3541 198 : poModifiedInput = OGRGeometryFactory::forceTo(
3542 66 : std::move(poModifiedInput),
3543 66 : OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
3544 :
3545 66 : poGeosInput = poModifiedInput.get();
3546 : }
3547 :
3548 : {
3549 : // Rather than check for conditions that would prevent conversion to
3550 : // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
3551 : // conversion as-is. If the conversion fails, we don't want any
3552 : // warnings emitted; we'll repair the input and try again.
3553 0 : std::optional<GEOSWarningSilencer> oSilencer;
3554 245040 : if (bAddPointsIfNeeded)
3555 : {
3556 5534 : oSilencer.emplace(hGEOSCtxt);
3557 : }
3558 :
3559 245040 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3560 : }
3561 :
3562 245040 : if (hGeom == nullptr && bAddPointsIfNeeded)
3563 : {
3564 15 : poModifiedInput = repairForGEOS(poGeosInput);
3565 15 : poGeosInput = poModifiedInput.get();
3566 :
3567 15 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
3568 : }
3569 :
3570 245040 : return hGeom;
3571 :
3572 : #endif // HAVE_GEOS
3573 : }
3574 :
3575 : /************************************************************************/
3576 : /* hasCurveGeometry() */
3577 : /************************************************************************/
3578 :
3579 : /**
3580 : * \brief Returns if this geometry is or has curve geometry.
3581 : *
3582 : * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
3583 : * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
3584 : *
3585 : * If bLookForNonLinear is set to TRUE, it will be actually looked if
3586 : * the geometry or its subgeometries are or contain a non-linear
3587 : * geometry in them. In which case, if the method returns TRUE, it
3588 : * means that getLinearGeometry() would return an approximate version
3589 : * of the geometry. Otherwise, getLinearGeometry() would do a
3590 : * conversion, but with just converting container type, like
3591 : * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
3592 : * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
3593 : * conversion.
3594 : *
3595 : * This method is the same as the C function OGR_G_HasCurveGeometry().
3596 : *
3597 : * @param bLookForNonLinear set it to TRUE to check if the geometry is
3598 : * or contains a CIRCULARSTRING.
3599 : *
3600 : * @return TRUE if this geometry is or has curve geometry.
3601 : *
3602 : */
3603 :
3604 278952 : OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3605 : {
3606 278952 : return FALSE;
3607 : }
3608 :
3609 : /************************************************************************/
3610 : /* getLinearGeometry() */
3611 : /************************************************************************/
3612 :
3613 : /**
3614 : * \brief Return, possibly approximate, non-curve version of this geometry.
3615 : *
3616 : * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
3617 : * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
3618 : *
3619 : * The ownership of the returned geometry belongs to the caller.
3620 : *
3621 : * The reverse method is OGRGeometry::getCurveGeometry().
3622 : *
3623 : * This method is the same as the C function OGR_G_GetLinearGeometry().
3624 : *
3625 : * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
3626 : * arc, zero to use the default setting.
3627 : * @param papszOptions options as a null-terminated list of strings.
3628 : * See OGRGeometryFactory::curveToLineString() for
3629 : * valid options.
3630 : *
3631 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3632 : *
3633 : */
3634 :
3635 : OGRGeometry *
3636 87 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3637 : CPL_UNUSED const char *const *papszOptions) const
3638 : {
3639 87 : return clone();
3640 : }
3641 :
3642 : /************************************************************************/
3643 : /* getCurveGeometry() */
3644 : /************************************************************************/
3645 :
3646 : /**
3647 : * \brief Return curve version of this geometry.
3648 : *
3649 : * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
3650 : * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
3651 : * curve geometries.
3652 : *
3653 : * If the geometry has no curve portion, the returned geometry will be a clone
3654 : * of it.
3655 : *
3656 : * The ownership of the returned geometry belongs to the caller.
3657 : *
3658 : * The reverse method is OGRGeometry::getLinearGeometry().
3659 : *
3660 : * This function is the same as C function OGR_G_GetCurveGeometry().
3661 : *
3662 : * @param papszOptions options as a null-terminated list of strings.
3663 : * Unused for now. Must be set to NULL.
3664 : *
3665 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3666 : *
3667 : */
3668 :
3669 : OGRGeometry *
3670 5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
3671 : {
3672 5 : return clone();
3673 : }
3674 :
3675 : /************************************************************************/
3676 : /* Distance() */
3677 : /************************************************************************/
3678 :
3679 : /**
3680 : * \brief Compute distance between two geometries.
3681 : *
3682 : * Returns the shortest distance between the two geometries. The distance is
3683 : * expressed into the same unit as the coordinates of the geometries.
3684 : *
3685 : * This method is the same as the C function OGR_G_Distance().
3686 : *
3687 : * This method is built on the GEOS library, check it for the definition
3688 : * of the geometry operation.
3689 : * If OGR is built without the GEOS library, this method will always fail,
3690 : * issuing a CPLE_NotSupported error.
3691 : *
3692 : * @param poOtherGeom the other geometry to compare against.
3693 : *
3694 : * @return the distance between the geometries or -1 if an error occurs.
3695 : */
3696 :
3697 25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
3698 :
3699 : {
3700 25 : if (nullptr == poOtherGeom)
3701 : {
3702 0 : CPLDebug("OGR",
3703 : "OGRGeometry::Distance called with NULL geometry pointer");
3704 0 : return -1.0;
3705 : }
3706 :
3707 25 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
3708 : {
3709 : #ifndef HAVE_SFCGAL
3710 :
3711 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3712 0 : return -1.0;
3713 :
3714 : #else
3715 :
3716 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3717 : if (poThis == nullptr)
3718 : return -1.0;
3719 :
3720 : sfcgal_geometry_t *poOther =
3721 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3722 : if (poOther == nullptr)
3723 : {
3724 : sfcgal_geometry_delete(poThis);
3725 : return -1.0;
3726 : }
3727 :
3728 : const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
3729 :
3730 : sfcgal_geometry_delete(poThis);
3731 : sfcgal_geometry_delete(poOther);
3732 :
3733 : return dfDistance > 0.0 ? dfDistance : -1.0;
3734 :
3735 : #endif
3736 : }
3737 :
3738 : else
3739 : {
3740 : #ifndef HAVE_GEOS
3741 :
3742 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3743 : return -1.0;
3744 :
3745 : #else
3746 :
3747 25 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3748 : // GEOSGeom is a pointer
3749 25 : GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
3750 25 : GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
3751 :
3752 25 : int bIsErr = 0;
3753 25 : double dfDistance = 0.0;
3754 :
3755 25 : if (hThis != nullptr && hOther != nullptr)
3756 : {
3757 25 : bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
3758 : }
3759 :
3760 25 : GEOSGeom_destroy_r(hGEOSCtxt, hThis);
3761 25 : GEOSGeom_destroy_r(hGEOSCtxt, hOther);
3762 25 : freeGEOSContext(hGEOSCtxt);
3763 :
3764 25 : if (bIsErr > 0)
3765 : {
3766 25 : return dfDistance;
3767 : }
3768 :
3769 : /* Calculations error */
3770 0 : return -1.0;
3771 :
3772 : #endif /* HAVE_GEOS */
3773 : }
3774 : }
3775 :
3776 : /************************************************************************/
3777 : /* OGR_G_Distance() */
3778 : /************************************************************************/
3779 : /**
3780 : * \brief Compute distance between two geometries.
3781 : *
3782 : * Returns the shortest distance between the two geometries. The distance is
3783 : * expressed into the same unit as the coordinates of the geometries.
3784 : *
3785 : * This function is the same as the C++ method OGRGeometry::Distance().
3786 : *
3787 : * This function is built on the GEOS library, check it for the definition
3788 : * of the geometry operation.
3789 : * If OGR is built without the GEOS library, this function will always fail,
3790 : * issuing a CPLE_NotSupported error.
3791 : *
3792 : * @param hFirst the first geometry to compare against.
3793 : * @param hOther the other geometry to compare against.
3794 : *
3795 : * @return the distance between the geometries or -1 if an error occurs.
3796 : */
3797 :
3798 2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
3799 :
3800 : {
3801 2 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
3802 :
3803 4 : return OGRGeometry::FromHandle(hFirst)->Distance(
3804 4 : OGRGeometry::FromHandle(hOther));
3805 : }
3806 :
3807 : /************************************************************************/
3808 : /* Distance3D() */
3809 : /************************************************************************/
3810 :
3811 : /**
3812 : * \brief Returns the 3D distance between two geometries
3813 : *
3814 : * The distance is expressed into the same unit as the coordinates of the
3815 : * geometries.
3816 : *
3817 : * This method is built on the SFCGAL library, check it for the definition
3818 : * of the geometry operation.
3819 : * If OGR is built without the SFCGAL library, this method will always return
3820 : * -1.0
3821 : *
3822 : * This function is the same as the C function OGR_G_Distance3D().
3823 : *
3824 : * @return distance between the two geometries
3825 : */
3826 :
3827 1 : double OGRGeometry::Distance3D(
3828 : UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
3829 : {
3830 1 : if (poOtherGeom == nullptr)
3831 : {
3832 0 : CPLDebug("OGR",
3833 : "OGRTriangle::Distance3D called with NULL geometry pointer");
3834 0 : return -1.0;
3835 : }
3836 :
3837 1 : if (!(poOtherGeom->Is3D() && Is3D()))
3838 : {
3839 0 : CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
3840 : "geometry(geometries)");
3841 0 : return -1.0;
3842 : }
3843 :
3844 : #ifndef HAVE_SFCGAL
3845 :
3846 1 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3847 1 : return -1.0;
3848 :
3849 : #else
3850 :
3851 : sfcgal_init();
3852 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3853 : if (poThis == nullptr)
3854 : return -1.0;
3855 :
3856 : sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3857 : if (poOther == nullptr)
3858 : {
3859 : sfcgal_geometry_delete(poThis);
3860 : return -1.0;
3861 : }
3862 :
3863 : const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
3864 :
3865 : sfcgal_geometry_delete(poThis);
3866 : sfcgal_geometry_delete(poOther);
3867 :
3868 : return dfDistance > 0 ? dfDistance : -1.0;
3869 :
3870 : #endif
3871 : }
3872 :
3873 : /************************************************************************/
3874 : /* OGR_G_Distance3D() */
3875 : /************************************************************************/
3876 : /**
3877 : * \brief Returns the 3D distance between two geometries
3878 : *
3879 : * The distance is expressed into the same unit as the coordinates of the
3880 : * geometries.
3881 : *
3882 : * This method is built on the SFCGAL library, check it for the definition
3883 : * of the geometry operation.
3884 : * If OGR is built without the SFCGAL library, this method will always return
3885 : * -1.0
3886 : *
3887 : * This function is the same as the C++ method OGRGeometry::Distance3D().
3888 : *
3889 : * @param hFirst the first geometry to compare against.
3890 : * @param hOther the other geometry to compare against.
3891 : * @return distance between the two geometries
3892 : *
3893 : * @return the distance between the geometries or -1 if an error occurs.
3894 : */
3895 :
3896 1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
3897 :
3898 : {
3899 1 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
3900 :
3901 2 : return OGRGeometry::FromHandle(hFirst)->Distance3D(
3902 2 : OGRGeometry::FromHandle(hOther));
3903 : }
3904 :
3905 : /************************************************************************/
3906 : /* OGRGeometryRebuildCurves() */
3907 : /************************************************************************/
3908 :
3909 : #ifdef HAVE_GEOS
3910 9316 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
3911 : const OGRGeometry *poOtherGeom,
3912 : OGRGeometry *poOGRProduct)
3913 : {
3914 18632 : if (poOGRProduct != nullptr &&
3915 18548 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
3916 9232 : (poGeom->hasCurveGeometry(true) ||
3917 2705 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
3918 : {
3919 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
3920 8 : delete poOGRProduct;
3921 8 : return poCurveGeom;
3922 : }
3923 9308 : return poOGRProduct;
3924 : }
3925 :
3926 : /************************************************************************/
3927 : /* BuildGeometryFromGEOS() */
3928 : /************************************************************************/
3929 :
3930 3784 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
3931 : GEOSGeom hGeosProduct,
3932 : const OGRGeometry *poSelf,
3933 : const OGRGeometry *poOtherGeom)
3934 : {
3935 3784 : OGRGeometry *poOGRProduct = nullptr;
3936 3784 : if (hGeosProduct != nullptr)
3937 : {
3938 : poOGRProduct =
3939 3782 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
3940 3782 : if (poOGRProduct != nullptr &&
3941 9232 : poSelf->getSpatialReference() != nullptr &&
3942 1668 : (poOtherGeom == nullptr ||
3943 1668 : (poOtherGeom->getSpatialReference() != nullptr &&
3944 1533 : poOtherGeom->getSpatialReference()->IsSame(
3945 : poSelf->getSpatialReference()))))
3946 : {
3947 1582 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
3948 : }
3949 : poOGRProduct =
3950 3782 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
3951 3782 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
3952 : }
3953 3784 : return poOGRProduct;
3954 : }
3955 :
3956 : /************************************************************************/
3957 : /* BuildGeometryFromTwoGeoms() */
3958 : /************************************************************************/
3959 :
3960 2777 : static OGRGeometry *BuildGeometryFromTwoGeoms(
3961 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3962 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
3963 : const GEOSGeometry *,
3964 : const GEOSGeometry *))
3965 : {
3966 2777 : OGRGeometry *poOGRProduct = nullptr;
3967 :
3968 2777 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3969 2777 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3970 2777 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3971 2777 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3972 : {
3973 : GEOSGeom hGeosProduct =
3974 2777 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3975 :
3976 : poOGRProduct =
3977 2777 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
3978 : }
3979 2777 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3980 2777 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3981 2777 : poSelf->freeGEOSContext(hGEOSCtxt);
3982 :
3983 2777 : return poOGRProduct;
3984 : }
3985 :
3986 : /************************************************************************/
3987 : /* OGRGEOSBooleanPredicate() */
3988 : /************************************************************************/
3989 :
3990 22722 : static OGRBoolean OGRGEOSBooleanPredicate(
3991 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3992 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
3993 : const GEOSGeometry *))
3994 : {
3995 22722 : OGRBoolean bResult = FALSE;
3996 :
3997 22722 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3998 22722 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3999 22722 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
4000 22722 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
4001 : {
4002 22163 : bResult =
4003 22163 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
4004 : }
4005 22722 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
4006 22722 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
4007 22722 : poSelf->freeGEOSContext(hGEOSCtxt);
4008 :
4009 22722 : return bResult;
4010 : }
4011 :
4012 : #endif // HAVE_GEOS
4013 :
4014 : /************************************************************************/
4015 : /* MakeValid() */
4016 : /************************************************************************/
4017 :
4018 : /**
4019 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4020 : *
4021 : * Already-valid geometries are cloned without further intervention
4022 : * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
4023 : * may be subject to non-significant transformations, such as duplicated point
4024 : * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
4025 : * geometry collections could be returned a single geometry. GDAL 3.10
4026 : * returns the same type of geometry).
4027 : *
4028 : * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
4029 : * post-processing step is often desired.
4030 : *
4031 : * This method is the same as the C function OGR_G_MakeValid().
4032 : *
4033 : * This function is built on the GEOS >= 3.8 library, check it for the
4034 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4035 : * library, this function will return a clone of the input geometry if it is
4036 : * valid, or NULL if it is invalid.
4037 : *
4038 : * Certain geometries cannot be read using GEOS, for example if Polygon rings
4039 : * are not closed or do not contain enough vertices. If a geometry cannot be
4040 : * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
4041 : * attempt to modify these geometries such that they can be read and
4042 : * repaired by GEOS.
4043 : *
4044 : * @param papszOptions NULL terminated list of options, or NULL. The following
4045 : * options are available:
4046 : * <ul>
4047 : * <li>METHOD=LINEWORK/STRUCTURE.
4048 : * LINEWORK is the default method, which combines all rings into a set of
4049 : * noded lines and then extracts valid polygons from that linework.
4050 : * The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
4051 : * all rings valid, then merges shells and
4052 : * subtracts holes from shells to generate valid result. Assumes that
4053 : * holes and shells are correctly categorized.</li>
4054 : * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
4055 : * NO (default): collapses are converted to empty geometries
4056 : * YES: collapses are converted to a valid geometry of lower dimension.</li>
4057 : * </ul>
4058 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4059 : *
4060 : * @since GDAL 3.0
4061 : */
4062 5535 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
4063 : {
4064 : (void)papszOptions;
4065 : #ifndef HAVE_GEOS
4066 : if (IsValid())
4067 : return clone();
4068 :
4069 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4070 : return nullptr;
4071 : #else
4072 5535 : if (IsSFCGALCompatible())
4073 : {
4074 0 : if (IsValid())
4075 0 : return clone();
4076 : }
4077 5535 : else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
4078 : {
4079 3 : GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
4080 3 : OGRBoolean bIsValid = FALSE;
4081 3 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4082 3 : if (hGeosGeom)
4083 : {
4084 3 : bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
4085 3 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4086 : }
4087 3 : freeGEOSContext(hGEOSCtxt);
4088 3 : if (bIsValid)
4089 1 : return clone();
4090 : }
4091 :
4092 5534 : const bool bStructureMethod = EQUAL(
4093 : CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
4094 5534 : CPL_IGNORE_RET_VAL(bStructureMethod);
4095 : #if !(GEOS_VERSION_MAJOR > 3 || \
4096 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
4097 : if (bStructureMethod)
4098 : {
4099 : CPLError(CE_Failure, CPLE_NotSupported,
4100 : "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
4101 : return nullptr;
4102 : }
4103 : #endif
4104 :
4105 5534 : OGRGeometry *poOGRProduct = nullptr;
4106 :
4107 5534 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4108 5534 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
4109 5534 : if (hGeosGeom != nullptr)
4110 : {
4111 : GEOSGeom hGEOSRet;
4112 : #if GEOS_VERSION_MAJOR > 3 || \
4113 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4114 5534 : if (bStructureMethod)
4115 : {
4116 : GEOSMakeValidParams *params =
4117 15 : GEOSMakeValidParams_create_r(hGEOSCtxt);
4118 15 : CPLAssert(params);
4119 15 : GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
4120 : GEOS_MAKE_VALID_STRUCTURE);
4121 15 : GEOSMakeValidParams_setKeepCollapsed_r(
4122 : hGEOSCtxt, params,
4123 15 : CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
4124 15 : hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
4125 15 : GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
4126 : }
4127 : else
4128 : #endif
4129 : {
4130 5519 : hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
4131 : }
4132 5534 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4133 :
4134 5534 : if (hGEOSRet != nullptr)
4135 : {
4136 : poOGRProduct =
4137 5534 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
4138 5534 : if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
4139 6 : poOGRProduct->assignSpatialReference(getSpatialReference());
4140 : poOGRProduct =
4141 5534 : OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
4142 5534 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
4143 :
4144 : #if GEOS_VERSION_MAJOR > 3 || \
4145 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4146 : // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
4147 : // if the input is a multiple geometry
4148 5534 : if (poOGRProduct && bStructureMethod &&
4149 11074 : OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
4150 6 : !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
4151 : wkbGeometryCollection))
4152 : {
4153 6 : poOGRProduct = OGRGeometryFactory::forceTo(
4154 6 : std::unique_ptr<OGRGeometry>(poOGRProduct),
4155 3 : getGeometryType())
4156 3 : .release();
4157 : }
4158 : #endif
4159 : }
4160 : }
4161 5534 : freeGEOSContext(hGEOSCtxt);
4162 :
4163 5534 : return poOGRProduct;
4164 : #endif
4165 : }
4166 :
4167 : /************************************************************************/
4168 : /* OGR_G_MakeValid() */
4169 : /************************************************************************/
4170 :
4171 : /**
4172 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4173 : *
4174 : * Already-valid geometries are cloned without further intervention.
4175 : *
4176 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4177 : *
4178 : * This function is built on the GEOS >= 3.8 library, check it for the
4179 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4180 : * library, this function will return a clone of the input geometry if it is
4181 : * valid, or NULL if it is invalid
4182 : *
4183 : * @param hGeom The Geometry to make valid.
4184 : *
4185 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4186 : * or NULL if an error occurs.
4187 : *
4188 : * @since GDAL 3.0
4189 : */
4190 :
4191 0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
4192 :
4193 : {
4194 0 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
4195 :
4196 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
4197 : }
4198 :
4199 : /************************************************************************/
4200 : /* OGR_G_MakeValidEx() */
4201 : /************************************************************************/
4202 :
4203 : /**
4204 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4205 : *
4206 : * Already-valid geometries are cloned without further intervention.
4207 : *
4208 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4209 : *
4210 : * See documentation of that method for possible options.
4211 : *
4212 : * @param hGeom The Geometry to make valid.
4213 : * @param papszOptions Options.
4214 : *
4215 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4216 : * or NULL if an error occurs.
4217 : *
4218 : * @since GDAL 3.4
4219 : */
4220 :
4221 25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
4222 :
4223 : {
4224 25 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
4225 :
4226 25 : return OGRGeometry::ToHandle(
4227 50 : OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
4228 : }
4229 :
4230 : /************************************************************************/
4231 : /* Normalize() */
4232 : /************************************************************************/
4233 :
4234 : /**
4235 : * \brief Attempts to bring geometry into normalized/canonical form.
4236 : *
4237 : * This method is the same as the C function OGR_G_Normalize().
4238 : *
4239 : * This function is built on the GEOS library; check it for the definition
4240 : * of the geometry operation.
4241 : * If OGR is built without the GEOS library, this function will always fail,
4242 : * issuing a CPLE_NotSupported error.
4243 : *
4244 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4245 : *
4246 : * @since GDAL 3.3
4247 : */
4248 51 : OGRGeometry *OGRGeometry::Normalize() const
4249 : {
4250 : #ifndef HAVE_GEOS
4251 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4252 : return nullptr;
4253 : #else
4254 51 : OGRGeometry *poOGRProduct = nullptr;
4255 :
4256 51 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4257 51 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4258 51 : if (hGeosGeom != nullptr)
4259 : {
4260 :
4261 51 : int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
4262 :
4263 51 : if (hGEOSRet == 0)
4264 : {
4265 : poOGRProduct =
4266 51 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
4267 : }
4268 : else
4269 : {
4270 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4271 : }
4272 : }
4273 51 : freeGEOSContext(hGEOSCtxt);
4274 :
4275 51 : return poOGRProduct;
4276 : #endif
4277 : }
4278 :
4279 : /************************************************************************/
4280 : /* OGR_G_Normalize() */
4281 : /************************************************************************/
4282 :
4283 : /**
4284 : * \brief Attempts to bring geometry into normalized/canonical form.
4285 : *
4286 : * This function is the same as the C++ method OGRGeometry::Normalize().
4287 : *
4288 : * This function is built on the GEOS library; check it for the definition
4289 : * of the geometry operation.
4290 : * If OGR is built without the GEOS library, this function will always fail,
4291 : * issuing a CPLE_NotSupported error.
4292 : * @param hGeom The Geometry to normalize.
4293 : *
4294 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4295 : * or NULL if an error occurs.
4296 : *
4297 : * @since GDAL 3.3
4298 : */
4299 :
4300 21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
4301 :
4302 : {
4303 21 : VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
4304 :
4305 21 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
4306 : }
4307 :
4308 : /************************************************************************/
4309 : /* ConvexHull() */
4310 : /************************************************************************/
4311 :
4312 : /**
4313 : * \brief Compute convex hull.
4314 : *
4315 : * A new geometry object is created and returned containing the convex
4316 : * hull of the geometry on which the method is invoked.
4317 : *
4318 : * This method is the same as the C function OGR_G_ConvexHull().
4319 : *
4320 : * This method is built on the GEOS library, check it for the definition
4321 : * of the geometry operation.
4322 : * If OGR is built without the GEOS library, this method will always fail,
4323 : * issuing a CPLE_NotSupported error.
4324 : *
4325 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4326 : */
4327 :
4328 6 : OGRGeometry *OGRGeometry::ConvexHull() const
4329 :
4330 : {
4331 6 : if (IsSFCGALCompatible())
4332 : {
4333 : #ifndef HAVE_SFCGAL
4334 :
4335 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4336 0 : return nullptr;
4337 :
4338 : #else
4339 :
4340 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4341 : if (poThis == nullptr)
4342 : return nullptr;
4343 :
4344 : sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
4345 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4346 : if (h_prodGeom)
4347 : h_prodGeom->assignSpatialReference(getSpatialReference());
4348 :
4349 : sfcgal_geometry_delete(poThis);
4350 : sfcgal_geometry_delete(poRes);
4351 :
4352 : return h_prodGeom;
4353 :
4354 : #endif
4355 : }
4356 :
4357 : else
4358 : {
4359 : #ifndef HAVE_GEOS
4360 :
4361 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4362 : return nullptr;
4363 :
4364 : #else
4365 :
4366 6 : OGRGeometry *poOGRProduct = nullptr;
4367 :
4368 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4369 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4370 6 : if (hGeosGeom != nullptr)
4371 : {
4372 6 : GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
4373 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4374 :
4375 : poOGRProduct =
4376 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4377 : }
4378 6 : freeGEOSContext(hGEOSCtxt);
4379 :
4380 6 : return poOGRProduct;
4381 :
4382 : #endif /* HAVE_GEOS */
4383 : }
4384 : }
4385 :
4386 : /************************************************************************/
4387 : /* OGR_G_ConvexHull() */
4388 : /************************************************************************/
4389 : /**
4390 : * \brief Compute convex hull.
4391 : *
4392 : * A new geometry object is created and returned containing the convex
4393 : * hull of the geometry on which the method is invoked.
4394 : *
4395 : * This function is the same as the C++ method OGRGeometry::ConvexHull().
4396 : *
4397 : * This function is built on the GEOS library, check it for the definition
4398 : * of the geometry operation.
4399 : * If OGR is built without the GEOS library, this function will always fail,
4400 : * issuing a CPLE_NotSupported error.
4401 : *
4402 : * @param hTarget The Geometry to calculate the convex hull of.
4403 : *
4404 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4405 : * or NULL if an error occurs.
4406 : */
4407 :
4408 1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
4409 :
4410 : {
4411 1 : VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
4412 :
4413 1 : return OGRGeometry::ToHandle(
4414 1 : OGRGeometry::FromHandle(hTarget)->ConvexHull());
4415 : }
4416 :
4417 : /************************************************************************/
4418 : /* ConcaveHull() */
4419 : /************************************************************************/
4420 :
4421 : /**
4422 : * \brief Compute the concave hull of a geometry.
4423 : *
4424 : * The concave hull is fully contained within the convex hull and also
4425 : * contains all the points of the input, but in a smaller area.
4426 : * The area ratio is the ratio of the area of the convex hull and the concave
4427 : * hull. Frequently used to convert a multi-point into a polygonal area.
4428 : * that contains all the points in the input Geometry.
4429 : *
4430 : * A new geometry object is created and returned containing the concave
4431 : * hull of the geometry on which the method is invoked.
4432 : *
4433 : * This method is the same as the C function OGR_G_ConcaveHull().
4434 : *
4435 : * This method is built on the GEOS >= 3.11 library
4436 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4437 : * fail, issuing a CPLE_NotSupported error.
4438 : *
4439 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4440 : * @param bAllowHoles Whether holes are allowed.
4441 : *
4442 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4443 : *
4444 : * @since GDAL 3.6
4445 : * @see OGRGeometry::ConcaveHullOfPolygons()
4446 : */
4447 :
4448 8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
4449 : {
4450 : #ifndef HAVE_GEOS
4451 : (void)dfRatio;
4452 : (void)bAllowHoles;
4453 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4454 : return nullptr;
4455 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4456 : (void)dfRatio;
4457 : (void)bAllowHoles;
4458 : CPLError(CE_Failure, CPLE_NotSupported,
4459 : "GEOS 3.11 or later needed for ConcaveHull.");
4460 : return nullptr;
4461 : #else
4462 8 : OGRGeometry *poOGRProduct = nullptr;
4463 :
4464 8 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4465 8 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4466 8 : if (hGeosGeom != nullptr)
4467 : {
4468 : GEOSGeom hGeosHull =
4469 8 : GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
4470 8 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4471 :
4472 : poOGRProduct =
4473 8 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4474 : }
4475 8 : freeGEOSContext(hGEOSCtxt);
4476 :
4477 8 : return poOGRProduct;
4478 : #endif /* HAVE_GEOS */
4479 : }
4480 :
4481 : /************************************************************************/
4482 : /* OGR_G_ConcaveHull() */
4483 : /************************************************************************/
4484 : /**
4485 : * \brief Compute the concave hull of a geometry.
4486 : *
4487 : * The concave hull is fully contained within the convex hull and also
4488 : * contains all the points of the input, but in a smaller area.
4489 : * The area ratio is the ratio of the area of the convex hull and the concave
4490 : * hull. Frequently used to convert a multi-point into a polygonal area.
4491 : * that contains all the points in the input Geometry.
4492 : *
4493 : * A new geometry object is created and returned containing the convex
4494 : * hull of the geometry on which the function is invoked.
4495 : *
4496 : * This function is the same as the C++ method OGRGeometry::ConcaveHull().
4497 : *
4498 : * This function is built on the GEOS >= 3.11 library
4499 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4500 : * fail, issuing a CPLE_NotSupported error.
4501 : *
4502 : * @param hTarget The Geometry to calculate the concave hull of.
4503 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4504 : * @param bAllowHoles Whether holes are allowed.
4505 : *
4506 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4507 : * or NULL if an error occurs.
4508 : *
4509 : * @since GDAL 3.6
4510 : * @see OGR_G_ConcaveHullOfPolygons()
4511 : */
4512 :
4513 2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
4514 : bool bAllowHoles)
4515 :
4516 : {
4517 2 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
4518 :
4519 2 : return OGRGeometry::ToHandle(
4520 2 : OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
4521 : }
4522 :
4523 : /************************************************************************/
4524 : /* ConcaveHullOfPolygons() */
4525 : /************************************************************************/
4526 :
4527 : /**
4528 : * \brief Compute the concave hull of a set of polygons, respecting
4529 : * the polygons as constraints.
4530 : *
4531 : * A concave hull is a (possibly) non-convex polygon containing all the input
4532 : * polygons.
4533 : * The computed hull "fills the gap" between the polygons,
4534 : * and does not intersect their interior.
4535 : * A set of polygons has a sequence of hulls of increasing concaveness,
4536 : * determined by a numeric target parameter.
4537 : *
4538 : * The concave hull is constructed by removing the longest outer edges
4539 : * of the Delaunay Triangulation of the space between the polygons,
4540 : * until the target criterion parameter is reached.
4541 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4542 : * between polygons to be no larger than this value.
4543 : * This can be expressed as a ratio between the lengths of the longest and
4544 : * shortest edges.
4545 : *
4546 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4547 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4548 : * for more details.
4549 : *
4550 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4551 : * be non-overlapping).
4552 : *
4553 : * A new geometry object is created and returned containing the concave
4554 : * hull of the geometry on which the method is invoked.
4555 : *
4556 : * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
4557 : *
4558 : * This method is built on the GEOS >= 3.11 library
4559 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4560 : * fail, issuing a CPLE_NotSupported error.
4561 : *
4562 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4563 : * difference between the longest and shortest edge lengths
4564 : * between the polygons.
4565 : * This normalizes the Maximum Edge Length to be scale-free.
4566 : * A value of 1 produces the convex hull; a value of 0 produces
4567 : * the original polygons.
4568 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4569 : * polygons.
4570 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4571 : *
4572 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4573 : *
4574 : * @since GDAL 3.13
4575 : * @see OGRGeometry::ConcaveHull()
4576 : */
4577 :
4578 13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
4579 : bool bIsTight,
4580 : bool bAllowHoles) const
4581 : {
4582 : #ifndef HAVE_GEOS
4583 : (void)dfLengthRatio;
4584 : (void)bIsTight;
4585 : (void)bAllowHoles;
4586 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4587 : return nullptr;
4588 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4589 : (void)dfLengthRatio;
4590 : (void)bIsTight;
4591 : (void)bAllowHoles;
4592 : CPLError(CE_Failure, CPLE_NotSupported,
4593 : "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
4594 : return nullptr;
4595 : #else
4596 13 : OGRGeometry *poOGRProduct = nullptr;
4597 :
4598 13 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4599 13 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4600 13 : if (hGeosGeom != nullptr)
4601 : {
4602 13 : GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
4603 : hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
4604 13 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4605 :
4606 : poOGRProduct =
4607 13 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4608 : }
4609 13 : freeGEOSContext(hGEOSCtxt);
4610 :
4611 13 : return poOGRProduct;
4612 : #endif /* HAVE_GEOS */
4613 : }
4614 :
4615 : /************************************************************************/
4616 : /* OGR_G_ConcaveHullOfPolygons() */
4617 : /************************************************************************/
4618 : /**
4619 : * \brief Compute the concave hull of a set of polygons, respecting
4620 : * the polygons as constraints.
4621 : *
4622 : * A concave hull is a (possibly) non-convex polygon containing all the input
4623 : * polygons.
4624 : * The computed hull "fills the gap" between the polygons,
4625 : * and does not intersect their interior.
4626 : * A set of polygons has a sequence of hulls of increasing concaveness,
4627 : * determined by a numeric target parameter.
4628 : *
4629 : * The concave hull is constructed by removing the longest outer edges
4630 : * of the Delaunay Triangulation of the space between the polygons,
4631 : * until the target criterion parameter is reached.
4632 : * The "Maximum Edge Length" parameter limits the length of the longest edge
4633 : * between polygons to be no larger than this value.
4634 : * This can be expressed as a ratio between the lengths of the longest and
4635 : * shortest edges.
4636 : *
4637 : * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
4638 : * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
4639 : * for more details.
4640 : *
4641 : * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
4642 : * be non-overlapping).
4643 : *
4644 : * A new geometry object is created and returned containing the concave
4645 : * hull of the geometry on which the method is invoked.
4646 : *
4647 : * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
4648 : *
4649 : * This function is built on the GEOS >= 3.11 library
4650 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4651 : * fail, issuing a CPLE_NotSupported error.
4652 : *
4653 : * @param hTarget The Geometry to calculate the concave hull of.
4654 : * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
4655 : * difference between the longest and shortest edge lengths
4656 : * between the polygons.
4657 : * This normalizes the Maximum Edge Length to be scale-free.
4658 : * A value of 1 produces the convex hull; a value of 0 produces
4659 : * the original polygons.
4660 : * @param bIsTight Whether the hull must follow the outer boundaries of the input
4661 : * polygons.
4662 : * @param bAllowHoles Whether the concave hull is allowed to contain holes
4663 : *
4664 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4665 : * or NULL if an error occurs.
4666 : *
4667 : * @since GDAL 3.13
4668 : * @see OGR_G_ConcaveHull()
4669 : */
4670 :
4671 7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
4672 : double dfLengthRatio, bool bIsTight,
4673 : bool bAllowHoles)
4674 :
4675 : {
4676 7 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
4677 :
4678 7 : return OGRGeometry::ToHandle(
4679 : OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
4680 7 : dfLengthRatio, bIsTight, bAllowHoles));
4681 : }
4682 :
4683 : /************************************************************************/
4684 : /* Boundary() */
4685 : /************************************************************************/
4686 :
4687 : /**
4688 : * \brief Compute boundary.
4689 : *
4690 : * A new geometry object is created and returned containing the boundary
4691 : * of the geometry on which the method is invoked.
4692 : *
4693 : * This method is the same as the C function OGR_G_Boundary().
4694 : *
4695 : * This method is built on the GEOS library, check it for the definition
4696 : * of the geometry operation.
4697 : * If OGR is built without the GEOS library, this method will always fail,
4698 : * issuing a CPLE_NotSupported error.
4699 : *
4700 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4701 : *
4702 : */
4703 :
4704 6 : OGRGeometry *OGRGeometry::Boundary() const
4705 :
4706 : {
4707 : #ifndef HAVE_GEOS
4708 :
4709 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4710 : return nullptr;
4711 :
4712 : #else
4713 :
4714 6 : OGRGeometry *poOGRProduct = nullptr;
4715 :
4716 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4717 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4718 6 : if (hGeosGeom != nullptr)
4719 : {
4720 6 : GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
4721 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4722 :
4723 : poOGRProduct =
4724 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4725 : }
4726 6 : freeGEOSContext(hGEOSCtxt);
4727 :
4728 6 : return poOGRProduct;
4729 :
4730 : #endif // HAVE_GEOS
4731 : }
4732 :
4733 : //! @cond Doxygen_Suppress
4734 : /**
4735 : * \brief Compute boundary (deprecated)
4736 : *
4737 : * @deprecated
4738 : *
4739 : * @see Boundary()
4740 : */
4741 0 : OGRGeometry *OGRGeometry::getBoundary() const
4742 :
4743 : {
4744 0 : return Boundary();
4745 : }
4746 :
4747 : //! @endcond
4748 :
4749 : /************************************************************************/
4750 : /* OGR_G_Boundary() */
4751 : /************************************************************************/
4752 : /**
4753 : * \brief Compute boundary.
4754 : *
4755 : * A new geometry object is created and returned containing the boundary
4756 : * of the geometry on which the method is invoked.
4757 : *
4758 : * This function is the same as the C++ method OGR_G_Boundary().
4759 : *
4760 : * This function is built on the GEOS library, check it for the definition
4761 : * of the geometry operation.
4762 : * If OGR is built without the GEOS library, this function will always fail,
4763 : * issuing a CPLE_NotSupported error.
4764 : *
4765 : * @param hTarget The Geometry to calculate the boundary of.
4766 : *
4767 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4768 : * or NULL if an error occurs.
4769 : *
4770 : */
4771 6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
4772 :
4773 : {
4774 6 : VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
4775 :
4776 6 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4777 : }
4778 :
4779 : /**
4780 : * \brief Compute boundary (deprecated)
4781 : *
4782 : * @deprecated
4783 : *
4784 : * @see OGR_G_Boundary()
4785 : */
4786 0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
4787 :
4788 : {
4789 0 : VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
4790 :
4791 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4792 : }
4793 :
4794 : /************************************************************************/
4795 : /* Buffer() */
4796 : /************************************************************************/
4797 :
4798 : /**
4799 : * \brief Compute buffer of geometry.
4800 : *
4801 : * Builds a new geometry containing the buffer region around the geometry
4802 : * on which it is invoked. The buffer is a polygon containing the region within
4803 : * the buffer distance of the original geometry.
4804 : *
4805 : * Some buffer sections are properly described as curves, but are converted to
4806 : * approximate polygons. The nQuadSegs parameter can be used to control how
4807 : * many segments should be used to define a 90 degree curve - a quadrant of a
4808 : * circle. A value of 30 is a reasonable default. Large values result in
4809 : * large numbers of vertices in the resulting buffer geometry while small
4810 : * numbers reduce the accuracy of the result.
4811 : *
4812 : * This method is the same as the C function OGR_G_Buffer().
4813 : *
4814 : * This method is built on the GEOS library, check it for the definition
4815 : * of the geometry operation.
4816 : * If OGR is built without the GEOS library, this method will always fail,
4817 : * issuing a CPLE_NotSupported error.
4818 : *
4819 : * @param dfDist the buffer distance to be applied. Should be expressed into
4820 : * the same unit as the coordinates of the geometry.
4821 : *
4822 : * @param nQuadSegs the number of segments used to approximate a 90
4823 : * degree (quadrant) of curvature.
4824 : *
4825 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4826 : */
4827 :
4828 39 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
4829 :
4830 : {
4831 : (void)dfDist;
4832 : (void)nQuadSegs;
4833 : #ifndef HAVE_GEOS
4834 :
4835 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4836 : return nullptr;
4837 :
4838 : #else
4839 :
4840 39 : OGRGeometry *poOGRProduct = nullptr;
4841 :
4842 39 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4843 39 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4844 39 : if (hGeosGeom != nullptr)
4845 : {
4846 : GEOSGeom hGeosProduct =
4847 39 : GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
4848 39 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4849 :
4850 : poOGRProduct =
4851 39 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4852 : }
4853 39 : freeGEOSContext(hGEOSCtxt);
4854 :
4855 39 : return poOGRProduct;
4856 :
4857 : #endif // HAVE_GEOS
4858 : }
4859 :
4860 : /************************************************************************/
4861 : /* OGR_G_Buffer() */
4862 : /************************************************************************/
4863 :
4864 : /**
4865 : * \brief Compute buffer of geometry.
4866 : *
4867 : * Builds a new geometry containing the buffer region around the geometry
4868 : * on which it is invoked. The buffer is a polygon containing the region within
4869 : * the buffer distance of the original geometry.
4870 : *
4871 : * Some buffer sections are properly described as curves, but are converted to
4872 : * approximate polygons. The nQuadSegs parameter can be used to control how
4873 : * many segments should be used to define a 90 degree curve - a quadrant of a
4874 : * circle. A value of 30 is a reasonable default. Large values result in
4875 : * large numbers of vertices in the resulting buffer geometry while small
4876 : * numbers reduce the accuracy of the result.
4877 : *
4878 : * This function is the same as the C++ method OGRGeometry::Buffer().
4879 : *
4880 : * This function is built on the GEOS library, check it for the definition
4881 : * of the geometry operation.
4882 : * If OGR is built without the GEOS library, this function will always fail,
4883 : * issuing a CPLE_NotSupported error.
4884 : *
4885 : * @param hTarget the geometry.
4886 : * @param dfDist the buffer distance to be applied. Should be expressed into
4887 : * the same unit as the coordinates of the geometry.
4888 : *
4889 : * @param nQuadSegs the number of segments used to approximate a 90 degree
4890 : * (quadrant) of curvature.
4891 : *
4892 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4893 : * or NULL if an error occurs.
4894 : */
4895 :
4896 39 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
4897 :
4898 : {
4899 39 : VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
4900 :
4901 39 : return OGRGeometry::ToHandle(
4902 39 : OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
4903 : }
4904 :
4905 : /**
4906 : * \brief Compute buffer of geometry.
4907 : *
4908 : * Builds a new geometry containing the buffer region around the geometry
4909 : * on which it is invoked. The buffer is a polygon containing the region within
4910 : * the buffer distance of the original geometry.
4911 : *
4912 : * This function is built on the GEOS library, check it for the definition
4913 : * of the geometry operation.
4914 : * If OGR is built without the GEOS library, this function will always fail,
4915 : * issuing a CPLE_NotSupported error.
4916 : *
4917 : * The following options are supported. See the GEOS library for more detailed
4918 : * descriptions.
4919 : *
4920 : * <ul>
4921 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
4922 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
4923 : * <li>MITRE_LIMIT=double</li>
4924 : * <li>QUADRANT_SEGMENTS=int</li>
4925 : * <li>SINGLE_SIDED=YES/NO</li>
4926 : * </ul>
4927 : *
4928 : * This function is the same as the C function OGR_G_BufferEx().
4929 : *
4930 : * @param dfDist the buffer distance to be applied. Should be expressed into
4931 : * the same unit as the coordinates of the geometry.
4932 : * @param papszOptions NULL terminated list of options (may be NULL)
4933 : *
4934 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4935 : *
4936 : * @since GDAL 3.10
4937 : */
4938 :
4939 35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
4940 : CSLConstList papszOptions) const
4941 : {
4942 : (void)dfDist;
4943 : (void)papszOptions;
4944 : #ifndef HAVE_GEOS
4945 :
4946 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4947 : return nullptr;
4948 :
4949 : #else
4950 35 : OGRGeometry *poOGRProduct = nullptr;
4951 35 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4952 :
4953 35 : auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
4954 35 : bool bParamsAreValid = true;
4955 :
4956 166 : for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
4957 : {
4958 131 : if (EQUAL(pszParam, "ENDCAP_STYLE"))
4959 : {
4960 : int nStyle;
4961 25 : if (EQUAL(pszValue, "ROUND"))
4962 : {
4963 22 : nStyle = GEOSBUF_CAP_ROUND;
4964 : }
4965 3 : else if (EQUAL(pszValue, "FLAT"))
4966 : {
4967 1 : nStyle = GEOSBUF_CAP_FLAT;
4968 : }
4969 2 : else if (EQUAL(pszValue, "SQUARE"))
4970 : {
4971 1 : nStyle = GEOSBUF_CAP_SQUARE;
4972 : }
4973 : else
4974 : {
4975 1 : bParamsAreValid = false;
4976 1 : CPLError(CE_Failure, CPLE_NotSupported,
4977 : "Invalid value for ENDCAP_STYLE: %s", pszValue);
4978 2 : break;
4979 : }
4980 :
4981 24 : if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
4982 : {
4983 0 : bParamsAreValid = false;
4984 : }
4985 : }
4986 106 : else if (EQUAL(pszParam, "JOIN_STYLE"))
4987 : {
4988 : int nStyle;
4989 25 : if (EQUAL(pszValue, "ROUND"))
4990 : {
4991 21 : nStyle = GEOSBUF_JOIN_ROUND;
4992 : }
4993 4 : else if (EQUAL(pszValue, "MITRE"))
4994 : {
4995 3 : nStyle = GEOSBUF_JOIN_MITRE;
4996 : }
4997 1 : else if (EQUAL(pszValue, "BEVEL"))
4998 : {
4999 0 : nStyle = GEOSBUF_JOIN_BEVEL;
5000 : }
5001 : else
5002 : {
5003 1 : bParamsAreValid = false;
5004 1 : CPLError(CE_Failure, CPLE_NotSupported,
5005 : "Invalid value for JOIN_STYLE: %s", pszValue);
5006 1 : break;
5007 : }
5008 :
5009 24 : if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
5010 : {
5011 0 : bParamsAreValid = false;
5012 0 : break;
5013 : }
5014 : }
5015 81 : else if (EQUAL(pszParam, "MITRE_LIMIT"))
5016 : {
5017 : try
5018 : {
5019 : std::size_t end;
5020 30 : double dfLimit = std::stod(pszValue, &end);
5021 :
5022 24 : if (end != strlen(pszValue))
5023 : {
5024 0 : throw std::invalid_argument("");
5025 : }
5026 :
5027 24 : if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
5028 : dfLimit))
5029 : {
5030 0 : bParamsAreValid = false;
5031 0 : break;
5032 : }
5033 : }
5034 4 : catch (const std::invalid_argument &)
5035 : {
5036 2 : bParamsAreValid = false;
5037 2 : CPLError(CE_Failure, CPLE_IllegalArg,
5038 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5039 : }
5040 0 : catch (const std::out_of_range &)
5041 : {
5042 0 : bParamsAreValid = false;
5043 0 : CPLError(CE_Failure, CPLE_IllegalArg,
5044 : "Invalid value for MITRE_LIMIT: %s", pszValue);
5045 : }
5046 : }
5047 55 : else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
5048 : {
5049 : try
5050 : {
5051 : std::size_t end;
5052 38 : int nQuadSegs = std::stoi(pszValue, &end, 10);
5053 :
5054 26 : if (end != strlen(pszValue))
5055 : {
5056 0 : throw std::invalid_argument("");
5057 : }
5058 :
5059 26 : if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
5060 : nQuadSegs))
5061 : {
5062 0 : bParamsAreValid = false;
5063 0 : break;
5064 : }
5065 : }
5066 6 : catch (const std::invalid_argument &)
5067 : {
5068 3 : bParamsAreValid = false;
5069 3 : CPLError(CE_Failure, CPLE_IllegalArg,
5070 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5071 : }
5072 2 : catch (const std::out_of_range &)
5073 : {
5074 1 : bParamsAreValid = false;
5075 1 : CPLError(CE_Failure, CPLE_IllegalArg,
5076 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
5077 : }
5078 : }
5079 25 : else if (EQUAL(pszParam, "SINGLE_SIDED"))
5080 : {
5081 24 : bool bSingleSided = CPLTestBool(pszValue);
5082 :
5083 24 : if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
5084 : bSingleSided))
5085 : {
5086 0 : bParamsAreValid = false;
5087 0 : break;
5088 : }
5089 : }
5090 : else
5091 : {
5092 1 : bParamsAreValid = false;
5093 1 : CPLError(CE_Failure, CPLE_NotSupported,
5094 : "Unsupported buffer option: %s", pszValue);
5095 : }
5096 : }
5097 :
5098 35 : if (bParamsAreValid)
5099 : {
5100 26 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
5101 26 : if (hGeosGeom != nullptr)
5102 : {
5103 : GEOSGeom hGeosProduct =
5104 26 : GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
5105 26 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
5106 :
5107 26 : if (hGeosProduct != nullptr)
5108 : {
5109 26 : poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
5110 : this, nullptr);
5111 : }
5112 : }
5113 : }
5114 :
5115 35 : GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
5116 35 : freeGEOSContext(hGEOSCtxt);
5117 35 : return poOGRProduct;
5118 : #endif
5119 : }
5120 :
5121 : /**
5122 : * \brief Compute buffer of geometry.
5123 : *
5124 : * Builds a new geometry containing the buffer region around the geometry
5125 : * on which it is invoked. The buffer is a polygon containing the region within
5126 : * the buffer distance of the original geometry.
5127 : *
5128 : * This function is built on the GEOS library, check it for the definition
5129 : * of the geometry operation.
5130 : * If OGR is built without the GEOS library, this function will always fail,
5131 : * issuing a CPLE_NotSupported error.
5132 : *
5133 : * The following options are supported. See the GEOS library for more detailed
5134 : * descriptions.
5135 : *
5136 : * <ul>
5137 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
5138 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
5139 : * <li>MITRE_LIMIT=double</li>
5140 : * <li>QUADRANT_SEGMENTS=int</li>
5141 : * <li>SINGLE_SIDED=YES/NO</li>
5142 : * </ul>
5143 : *
5144 : * This function is the same as the C++ method OGRGeometry::BufferEx().
5145 : *
5146 : * @param hTarget the geometry.
5147 : * @param dfDist the buffer distance to be applied. Should be expressed into
5148 : * the same unit as the coordinates of the geometry.
5149 : * @param papszOptions NULL terminated list of options (may be NULL)
5150 : *
5151 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5152 : * or NULL if an error occurs.
5153 : *
5154 : * @since GDAL 3.10
5155 : */
5156 :
5157 12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
5158 : CSLConstList papszOptions)
5159 :
5160 : {
5161 12 : VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
5162 :
5163 12 : return OGRGeometry::ToHandle(
5164 12 : OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
5165 : }
5166 :
5167 : /************************************************************************/
5168 : /* Intersection() */
5169 : /************************************************************************/
5170 :
5171 : /**
5172 : * \brief Compute intersection.
5173 : *
5174 : * Generates a new geometry which is the region of intersection of the
5175 : * two geometries operated on. The Intersects() method can be used to test if
5176 : * two geometries intersect.
5177 : *
5178 : * Geometry validity is not checked. In case you are unsure of the validity
5179 : * of the input geometries, call IsValid() before, otherwise the result might
5180 : * be wrong.
5181 : *
5182 : * This method is the same as the C function OGR_G_Intersection().
5183 : *
5184 : * This method is built on the GEOS library, check it for the definition
5185 : * of the geometry operation.
5186 : * If OGR is built without the GEOS library, this method will always fail,
5187 : * issuing a CPLE_NotSupported error.
5188 : *
5189 : * @param poOtherGeom the other geometry intersected with "this" geometry.
5190 : *
5191 : * @return a new geometry to be freed by the caller, or NULL if there is no
5192 : * intersection or if an error occurs.
5193 : *
5194 : */
5195 :
5196 : OGRGeometry *
5197 1950 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5198 :
5199 : {
5200 1950 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5201 : {
5202 : #ifndef HAVE_SFCGAL
5203 :
5204 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5205 0 : return nullptr;
5206 :
5207 : #else
5208 :
5209 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5210 : if (poThis == nullptr)
5211 : return nullptr;
5212 :
5213 : sfcgal_geometry_t *poOther =
5214 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5215 : if (poOther == nullptr)
5216 : {
5217 : sfcgal_geometry_delete(poThis);
5218 : return nullptr;
5219 : }
5220 :
5221 : sfcgal_geometry_t *poRes =
5222 : sfcgal_geometry_intersection_3d(poThis, poOther);
5223 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
5224 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5225 : poOtherGeom->getSpatialReference() != nullptr &&
5226 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5227 : h_prodGeom->assignSpatialReference(getSpatialReference());
5228 :
5229 : sfcgal_geometry_delete(poThis);
5230 : sfcgal_geometry_delete(poOther);
5231 : sfcgal_geometry_delete(poRes);
5232 :
5233 : return h_prodGeom;
5234 :
5235 : #endif
5236 : }
5237 :
5238 : else
5239 : {
5240 : #ifndef HAVE_GEOS
5241 :
5242 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5243 : return nullptr;
5244 :
5245 : #else
5246 1950 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
5247 : #endif /* HAVE_GEOS */
5248 : }
5249 : }
5250 :
5251 : /************************************************************************/
5252 : /* OGR_G_Intersection() */
5253 : /************************************************************************/
5254 :
5255 : /**
5256 : * \brief Compute intersection.
5257 : *
5258 : * Generates a new geometry which is the region of intersection of the
5259 : * two geometries operated on. The OGR_G_Intersects() function can be used to
5260 : * test if two geometries intersect.
5261 : *
5262 : * Geometry validity is not checked. In case you are unsure of the validity
5263 : * of the input geometries, call IsValid() before, otherwise the result might
5264 : * be wrong.
5265 : *
5266 : * This function is the same as the C++ method OGRGeometry::Intersection().
5267 : *
5268 : * This function is built on the GEOS library, check it for the definition
5269 : * of the geometry operation.
5270 : * If OGR is built without the GEOS library, this function will always fail,
5271 : * issuing a CPLE_NotSupported error.
5272 : *
5273 : * @param hThis the geometry.
5274 : * @param hOther the other geometry.
5275 : *
5276 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5277 : * or NULL if there is not intersection of if an error occurs.
5278 : */
5279 :
5280 12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
5281 :
5282 : {
5283 12 : VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
5284 :
5285 24 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
5286 24 : OGRGeometry::FromHandle(hOther)));
5287 : }
5288 :
5289 : /************************************************************************/
5290 : /* Union() */
5291 : /************************************************************************/
5292 :
5293 : /**
5294 : * \brief Compute union.
5295 : *
5296 : * Generates a new geometry which is the region of union of the
5297 : * two geometries operated on.
5298 : *
5299 : * Geometry validity is not checked. In case you are unsure of the validity
5300 : * of the input geometries, call IsValid() before, otherwise the result might
5301 : * be wrong.
5302 : *
5303 : * This method is the same as the C function OGR_G_Union().
5304 : *
5305 : * This method is built on the GEOS library, check it for the definition
5306 : * of the geometry operation.
5307 : * If OGR is built without the GEOS library, this method will always fail,
5308 : * issuing a CPLE_NotSupported error.
5309 : *
5310 : * @param poOtherGeom the other geometry unioned with "this" geometry.
5311 : *
5312 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5313 : */
5314 :
5315 : OGRGeometry *
5316 68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5317 :
5318 : {
5319 68 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5320 : {
5321 : #ifndef HAVE_SFCGAL
5322 :
5323 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5324 0 : return nullptr;
5325 :
5326 : #else
5327 :
5328 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5329 : if (poThis == nullptr)
5330 : return nullptr;
5331 :
5332 : sfcgal_geometry_t *poOther =
5333 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5334 : if (poOther == nullptr)
5335 : {
5336 : sfcgal_geometry_delete(poThis);
5337 : return nullptr;
5338 : }
5339 :
5340 : sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
5341 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5342 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5343 : poOtherGeom->getSpatialReference() != nullptr &&
5344 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5345 : h_prodGeom->assignSpatialReference(getSpatialReference());
5346 :
5347 : sfcgal_geometry_delete(poThis);
5348 : sfcgal_geometry_delete(poOther);
5349 : sfcgal_geometry_delete(poRes);
5350 :
5351 : return h_prodGeom;
5352 :
5353 : #endif
5354 : }
5355 :
5356 : else
5357 : {
5358 : #ifndef HAVE_GEOS
5359 :
5360 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5361 : return nullptr;
5362 :
5363 : #else
5364 68 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
5365 : #endif /* HAVE_GEOS */
5366 : }
5367 : }
5368 :
5369 : /************************************************************************/
5370 : /* OGR_G_Union() */
5371 : /************************************************************************/
5372 :
5373 : /**
5374 : * \brief Compute union.
5375 : *
5376 : * Generates a new geometry which is the region of union of the
5377 : * two geometries operated on.
5378 : *
5379 : * Geometry validity is not checked. In case you are unsure of the validity
5380 : * of the input geometries, call IsValid() before, otherwise the result might
5381 : * be wrong.
5382 : *
5383 : * This function is the same as the C++ method OGRGeometry::Union().
5384 : *
5385 : * This function is built on the GEOS library, check it for the definition
5386 : * of the geometry operation.
5387 : * If OGR is built without the GEOS library, this function will always fail,
5388 : * issuing a CPLE_NotSupported error.
5389 : *
5390 : * @param hThis the geometry.
5391 : * @param hOther the other geometry.
5392 : *
5393 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5394 : * or NULL if an error occurs.
5395 : */
5396 :
5397 10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
5398 :
5399 : {
5400 10 : VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
5401 :
5402 20 : return OGRGeometry::ToHandle(
5403 20 : OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
5404 : }
5405 :
5406 : /************************************************************************/
5407 : /* UnionCascaded() */
5408 : /************************************************************************/
5409 :
5410 : /**
5411 : * \brief Compute union using cascading.
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 : * The input geometry must be a MultiPolygon.
5418 : *
5419 : * This method is the same as the C function OGR_G_UnionCascaded().
5420 : *
5421 : * This method is built on the GEOS library, check it for the definition
5422 : * of the geometry operation.
5423 : * If OGR is built without the GEOS library, this method will always fail,
5424 : * issuing a CPLE_NotSupported error.
5425 : *
5426 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5427 : *
5428 : *
5429 : * @deprecated Use UnaryUnion() instead
5430 : */
5431 :
5432 2 : OGRGeometry *OGRGeometry::UnionCascaded() const
5433 :
5434 : {
5435 : #ifndef HAVE_GEOS
5436 :
5437 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5438 : return nullptr;
5439 : #else
5440 :
5441 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5442 : if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
5443 : {
5444 : // GEOS < 3.11 crashes on an empty multipolygon input
5445 : auto poRet = new OGRGeometryCollection();
5446 : poRet->assignSpatialReference(getSpatialReference());
5447 : return poRet;
5448 : }
5449 : #endif
5450 2 : OGRGeometry *poOGRProduct = nullptr;
5451 :
5452 2 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5453 2 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5454 2 : if (hThisGeosGeom != nullptr)
5455 : {
5456 2 : GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
5457 2 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5458 :
5459 : poOGRProduct =
5460 2 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5461 : }
5462 2 : freeGEOSContext(hGEOSCtxt);
5463 :
5464 2 : return poOGRProduct;
5465 :
5466 : #endif // HAVE_GEOS
5467 : }
5468 :
5469 : /************************************************************************/
5470 : /* OGR_G_UnionCascaded() */
5471 : /************************************************************************/
5472 :
5473 : /**
5474 : * \brief Compute union using cascading.
5475 : *
5476 : * Geometry validity is not checked. In case you are unsure of the validity
5477 : * of the input geometries, call IsValid() before, otherwise the result might
5478 : * be wrong.
5479 : *
5480 : * The input geometry must be a MultiPolygon.
5481 : *
5482 : * This function is the same as the C++ method OGRGeometry::UnionCascaded().
5483 : *
5484 : * This function is built on the GEOS library, check it for the definition
5485 : * of the geometry operation.
5486 : * If OGR is built without the GEOS library, this function will always fail,
5487 : * issuing a CPLE_NotSupported error.
5488 : *
5489 : * @param hThis the geometry.
5490 : *
5491 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5492 : * or NULL if an error occurs.
5493 : *
5494 : * @deprecated Use OGR_G_UnaryUnion() instead
5495 : */
5496 :
5497 2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
5498 :
5499 : {
5500 2 : VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
5501 :
5502 2 : return OGRGeometry::ToHandle(
5503 2 : OGRGeometry::FromHandle(hThis)->UnionCascaded());
5504 : }
5505 :
5506 : /************************************************************************/
5507 : /* UnaryUnion() */
5508 : /************************************************************************/
5509 :
5510 : /**
5511 : * \brief Returns the union of all components of a single geometry.
5512 : *
5513 : * Usually used to convert a collection into the smallest set of polygons that
5514 : * cover the same area.
5515 : *
5516 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5517 : *
5518 : * This method is the same as the C function OGR_G_UnaryUnion().
5519 : *
5520 : * This method is built on the GEOS library, check it for the definition
5521 : * of the geometry operation.
5522 : * If OGR is built without the GEOS library, this method will always fail,
5523 : * issuing a CPLE_NotSupported error.
5524 : *
5525 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5526 : *
5527 : * @since GDAL 3.7
5528 : */
5529 :
5530 629 : OGRGeometry *OGRGeometry::UnaryUnion() const
5531 :
5532 : {
5533 : #ifndef HAVE_GEOS
5534 :
5535 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5536 : return nullptr;
5537 : #else
5538 :
5539 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5540 : if (IsEmpty())
5541 : {
5542 : // GEOS < 3.11 crashes on an empty geometry
5543 : auto poRet = new OGRGeometryCollection();
5544 : poRet->assignSpatialReference(getSpatialReference());
5545 : return poRet;
5546 : }
5547 : #endif
5548 629 : OGRGeometry *poOGRProduct = nullptr;
5549 :
5550 629 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5551 629 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5552 629 : if (hThisGeosGeom != nullptr)
5553 : {
5554 629 : GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5555 629 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5556 :
5557 : poOGRProduct =
5558 629 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5559 : }
5560 629 : freeGEOSContext(hGEOSCtxt);
5561 :
5562 629 : return poOGRProduct;
5563 :
5564 : #endif // HAVE_GEOS
5565 : }
5566 :
5567 : /************************************************************************/
5568 : /* OGR_G_UnaryUnion() */
5569 : /************************************************************************/
5570 :
5571 : /**
5572 : * \brief Returns the union of all components of a single geometry.
5573 : *
5574 : * Usually used to convert a collection into the smallest set of polygons that
5575 : * cover the same area.
5576 : *
5577 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5578 : *
5579 : * Geometry validity is not checked. In case you are unsure of the validity
5580 : * of the input geometries, call IsValid() before, otherwise the result might
5581 : * be wrong.
5582 : *
5583 : * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5584 : *
5585 : * This function is built on the GEOS library, check it for the definition
5586 : * of the geometry operation.
5587 : * If OGR is built without the GEOS library, this function will always fail,
5588 : * issuing a CPLE_NotSupported error.
5589 : *
5590 : * @param hThis the geometry.
5591 : *
5592 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5593 : * or NULL if an error occurs.
5594 : *
5595 : * @since GDAL 3.7
5596 : */
5597 :
5598 3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5599 :
5600 : {
5601 3 : VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5602 :
5603 3 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5604 : }
5605 :
5606 : /************************************************************************/
5607 : /* Difference() */
5608 : /************************************************************************/
5609 :
5610 : /**
5611 : * \brief Compute difference.
5612 : *
5613 : * Generates a new geometry which is the region of this geometry with the
5614 : * region of the second geometry removed.
5615 : *
5616 : * Geometry validity is not checked. In case you are unsure of the validity
5617 : * of the input geometries, call IsValid() before, otherwise the result might
5618 : * be wrong.
5619 : *
5620 : * This method is the same as the C function OGR_G_Difference().
5621 : *
5622 : * This method is built on the GEOS library, check it for the definition
5623 : * of the geometry operation.
5624 : * If OGR is built without the GEOS library, this method will always fail,
5625 : * issuing a CPLE_NotSupported error.
5626 : *
5627 : * @param poOtherGeom the other geometry removed from "this" geometry.
5628 : *
5629 : * @return a new geometry to be freed by the caller, or NULL if the difference
5630 : * is empty or if an error occurs.
5631 : */
5632 :
5633 : OGRGeometry *
5634 752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5635 :
5636 : {
5637 752 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5638 : {
5639 : #ifndef HAVE_SFCGAL
5640 :
5641 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5642 0 : return nullptr;
5643 :
5644 : #else
5645 :
5646 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5647 : if (poThis == nullptr)
5648 : return nullptr;
5649 :
5650 : sfcgal_geometry_t *poOther =
5651 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5652 : if (poOther == nullptr)
5653 : {
5654 : sfcgal_geometry_delete(poThis);
5655 : return nullptr;
5656 : }
5657 :
5658 : sfcgal_geometry_t *poRes =
5659 : sfcgal_geometry_difference_3d(poThis, poOther);
5660 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5661 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5662 : poOtherGeom->getSpatialReference() != nullptr &&
5663 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5664 : h_prodGeom->assignSpatialReference(getSpatialReference());
5665 :
5666 : sfcgal_geometry_delete(poThis);
5667 : sfcgal_geometry_delete(poOther);
5668 : sfcgal_geometry_delete(poRes);
5669 :
5670 : return h_prodGeom;
5671 :
5672 : #endif
5673 : }
5674 :
5675 : else
5676 : {
5677 : #ifndef HAVE_GEOS
5678 :
5679 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5680 : return nullptr;
5681 :
5682 : #else
5683 752 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5684 : #endif /* HAVE_GEOS */
5685 : }
5686 : }
5687 :
5688 : /************************************************************************/
5689 : /* OGR_G_Difference() */
5690 : /************************************************************************/
5691 :
5692 : /**
5693 : * \brief Compute difference.
5694 : *
5695 : * Generates a new geometry which is the region of this geometry with the
5696 : * region of the other geometry removed.
5697 : *
5698 : * Geometry validity is not checked. In case you are unsure of the validity
5699 : * of the input geometries, call IsValid() before, otherwise the result might
5700 : * be wrong.
5701 : *
5702 : * This function is the same as the C++ method OGRGeometry::Difference().
5703 : *
5704 : * This function is built on the GEOS library, check it for the definition
5705 : * of the geometry operation.
5706 : * If OGR is built without the GEOS library, this function will always fail,
5707 : * issuing a CPLE_NotSupported error.
5708 : *
5709 : * @param hThis the geometry.
5710 : * @param hOther the other geometry.
5711 : *
5712 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5713 : * or NULL if the difference is empty or if an error occurs.
5714 : */
5715 :
5716 6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5717 :
5718 : {
5719 6 : VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5720 :
5721 12 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5722 12 : OGRGeometry::FromHandle(hOther)));
5723 : }
5724 :
5725 : /************************************************************************/
5726 : /* SymDifference() */
5727 : /************************************************************************/
5728 :
5729 : /**
5730 : * \brief Compute symmetric difference.
5731 : *
5732 : * Generates a new geometry which is the symmetric difference of this
5733 : * geometry and the second geometry passed into the method.
5734 : *
5735 : * Geometry validity is not checked. In case you are unsure of the validity
5736 : * of the input geometries, call IsValid() before, otherwise the result might
5737 : * be wrong.
5738 : *
5739 : * This method is the same as the C function OGR_G_SymDifference().
5740 : *
5741 : * This method is built on the GEOS library, check it for the definition
5742 : * of the geometry operation.
5743 : * If OGR is built without the GEOS library, this method will always fail,
5744 : * issuing a CPLE_NotSupported error.
5745 : *
5746 : * @param poOtherGeom the other geometry.
5747 : *
5748 : * @return a new geometry to be freed by the caller, or NULL if the difference
5749 : * is empty or if an error occurs.
5750 : *
5751 : */
5752 :
5753 7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
5754 :
5755 : {
5756 : (void)poOtherGeom;
5757 7 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5758 : {
5759 : #ifndef HAVE_SFCGAL
5760 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5761 0 : return nullptr;
5762 : #else
5763 : OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5764 : if (poFirstDifference == nullptr)
5765 : return nullptr;
5766 :
5767 : OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5768 : if (poOtherDifference == nullptr)
5769 : {
5770 : delete poFirstDifference;
5771 : return nullptr;
5772 : }
5773 :
5774 : OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5775 : delete poFirstDifference;
5776 : delete poOtherDifference;
5777 : return poSymDiff;
5778 : #endif
5779 : }
5780 :
5781 : #ifndef HAVE_GEOS
5782 :
5783 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5784 : return nullptr;
5785 :
5786 : #else
5787 7 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5788 : #endif // HAVE_GEOS
5789 : }
5790 :
5791 : //! @cond Doxygen_Suppress
5792 : /**
5793 : * \brief Compute symmetric difference (deprecated)
5794 : *
5795 : * @deprecated
5796 : *
5797 : * @see OGRGeometry::SymDifference()
5798 : */
5799 : OGRGeometry *
5800 0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5801 :
5802 : {
5803 0 : return SymDifference(poOtherGeom);
5804 : }
5805 :
5806 : //! @endcond
5807 :
5808 : /************************************************************************/
5809 : /* OGR_G_SymDifference() */
5810 : /************************************************************************/
5811 :
5812 : /**
5813 : * \brief Compute symmetric difference.
5814 : *
5815 : * Generates a new geometry which is the symmetric difference of this
5816 : * geometry and the other geometry.
5817 : *
5818 : * Geometry validity is not checked. In case you are unsure of the validity
5819 : * of the input geometries, call IsValid() before, otherwise the result might
5820 : * be wrong.
5821 : *
5822 : * This function is the same as the C++ method
5823 : * OGRGeometry::SymmetricDifference().
5824 : *
5825 : * This function is built on the GEOS library, check it for the definition
5826 : * of the geometry operation.
5827 : * If OGR is built without the GEOS library, this function will always fail,
5828 : * issuing a CPLE_NotSupported error.
5829 : *
5830 : * @param hThis the geometry.
5831 : * @param hOther the other geometry.
5832 : *
5833 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5834 : * or NULL if the difference is empty or if an error occurs.
5835 : *
5836 : */
5837 :
5838 7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5839 :
5840 : {
5841 7 : VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5842 :
5843 14 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5844 14 : OGRGeometry::FromHandle(hOther)));
5845 : }
5846 :
5847 : /**
5848 : * \brief Compute symmetric difference (deprecated)
5849 : *
5850 : * @deprecated
5851 : *
5852 : * @see OGR_G_SymmetricDifference()
5853 : */
5854 0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5855 :
5856 : {
5857 0 : VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5858 :
5859 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5860 0 : OGRGeometry::FromHandle(hOther)));
5861 : }
5862 :
5863 : /************************************************************************/
5864 : /* Disjoint() */
5865 : /************************************************************************/
5866 :
5867 : /**
5868 : * \brief Test for disjointness.
5869 : *
5870 : * Tests if this geometry and the other passed into the method are disjoint.
5871 : *
5872 : * Geometry validity is not checked. In case you are unsure of the validity
5873 : * of the input geometries, call IsValid() before, otherwise the result might
5874 : * be wrong.
5875 : *
5876 : * This method is the same as the C function OGR_G_Disjoint().
5877 : *
5878 : * This method is built on the GEOS library, check it for the definition
5879 : * of the geometry operation.
5880 : * If OGR is built without the GEOS library, this method will always fail,
5881 : * issuing a CPLE_NotSupported error.
5882 : *
5883 : * @param poOtherGeom the geometry to compare to this geometry.
5884 : *
5885 : * @return TRUE if they are disjoint, otherwise FALSE.
5886 : */
5887 :
5888 8 : OGRBoolean OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
5889 :
5890 : {
5891 : (void)poOtherGeom;
5892 : #ifndef HAVE_GEOS
5893 :
5894 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5895 : return FALSE;
5896 :
5897 : #else
5898 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
5899 : #endif // HAVE_GEOS
5900 : }
5901 :
5902 : /************************************************************************/
5903 : /* OGR_G_Disjoint() */
5904 : /************************************************************************/
5905 :
5906 : /**
5907 : * \brief Test for disjointness.
5908 : *
5909 : * Tests if this geometry and the other geometry are disjoint.
5910 : *
5911 : * Geometry validity is not checked. In case you are unsure of the validity
5912 : * of the input geometries, call IsValid() before, otherwise the result might
5913 : * be wrong.
5914 : *
5915 : * This function is the same as the C++ method OGRGeometry::Disjoint().
5916 : *
5917 : * This function is built on the GEOS library, check it for the definition
5918 : * of the geometry operation.
5919 : * If OGR is built without the GEOS library, this function will always fail,
5920 : * issuing a CPLE_NotSupported error.
5921 : *
5922 : * @param hThis the geometry to compare.
5923 : * @param hOther the other geometry to compare.
5924 : *
5925 : * @return TRUE if they are disjoint, otherwise FALSE.
5926 : */
5927 8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
5928 :
5929 : {
5930 8 : VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
5931 :
5932 16 : return OGRGeometry::FromHandle(hThis)->Disjoint(
5933 16 : OGRGeometry::FromHandle(hOther));
5934 : }
5935 :
5936 : /************************************************************************/
5937 : /* Touches() */
5938 : /************************************************************************/
5939 :
5940 : /**
5941 : * \brief Test for touching.
5942 : *
5943 : * Tests if this geometry and the other passed into the method are touching.
5944 : *
5945 : * Geometry validity is not checked. In case you are unsure of the validity
5946 : * of the input geometries, call IsValid() before, otherwise the result might
5947 : * be wrong.
5948 : *
5949 : * This method is the same as the C function OGR_G_Touches().
5950 : *
5951 : * This method is built on the GEOS library, check it for the definition
5952 : * of the geometry operation.
5953 : * If OGR is built without the GEOS library, this method will always fail,
5954 : * issuing a CPLE_NotSupported error.
5955 : *
5956 : * @param poOtherGeom the geometry to compare to this geometry.
5957 : *
5958 : * @return TRUE if they are touching, otherwise FALSE.
5959 : */
5960 :
5961 11 : OGRBoolean OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
5962 :
5963 : {
5964 : (void)poOtherGeom;
5965 : #ifndef HAVE_GEOS
5966 :
5967 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5968 : return FALSE;
5969 :
5970 : #else
5971 11 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
5972 : #endif // HAVE_GEOS
5973 : }
5974 :
5975 : /************************************************************************/
5976 : /* OGR_G_Touches() */
5977 : /************************************************************************/
5978 : /**
5979 : * \brief Test for touching.
5980 : *
5981 : * Tests if this geometry and the other geometry are touching.
5982 : *
5983 : * Geometry validity is not checked. In case you are unsure of the validity
5984 : * of the input geometries, call IsValid() before, otherwise the result might
5985 : * be wrong.
5986 : *
5987 : * This function is the same as the C++ method OGRGeometry::Touches().
5988 : *
5989 : * This function is built on the GEOS library, check it for the definition
5990 : * of the geometry operation.
5991 : * If OGR is built without the GEOS library, this function will always fail,
5992 : * issuing a CPLE_NotSupported error.
5993 : *
5994 : * @param hThis the geometry to compare.
5995 : * @param hOther the other geometry to compare.
5996 : *
5997 : * @return TRUE if they are touching, otherwise FALSE.
5998 : */
5999 :
6000 8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
6001 :
6002 : {
6003 8 : VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
6004 :
6005 16 : return OGRGeometry::FromHandle(hThis)->Touches(
6006 16 : OGRGeometry::FromHandle(hOther));
6007 : }
6008 :
6009 : /************************************************************************/
6010 : /* Crosses() */
6011 : /************************************************************************/
6012 :
6013 : /**
6014 : * \brief Test for crossing.
6015 : *
6016 : * Tests if this geometry and the other passed into the method are crossing.
6017 : *
6018 : * Geometry validity is not checked. In case you are unsure of the validity
6019 : * of the input geometries, call IsValid() before, otherwise the result might
6020 : * be wrong.
6021 : *
6022 : * This method is the same as the C function OGR_G_Crosses().
6023 : *
6024 : * This method is built on the GEOS library, check it for the definition
6025 : * of the geometry operation.
6026 : * If OGR is built without the GEOS library, this method will always fail,
6027 : * issuing a CPLE_NotSupported error.
6028 : *
6029 : * @param poOtherGeom the geometry to compare to this geometry.
6030 : *
6031 : * @return TRUE if they are crossing, otherwise FALSE.
6032 : */
6033 :
6034 : OGRBoolean
6035 8 : OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
6036 :
6037 : {
6038 8 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
6039 : {
6040 : #ifndef HAVE_SFCGAL
6041 :
6042 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
6043 0 : return FALSE;
6044 :
6045 : #else
6046 :
6047 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
6048 : if (poThis == nullptr)
6049 : return FALSE;
6050 :
6051 : sfcgal_geometry_t *poOther =
6052 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
6053 : if (poOther == nullptr)
6054 : {
6055 : sfcgal_geometry_delete(poThis);
6056 : return FALSE;
6057 : }
6058 :
6059 : int res = sfcgal_geometry_intersects_3d(poThis, poOther);
6060 :
6061 : sfcgal_geometry_delete(poThis);
6062 : sfcgal_geometry_delete(poOther);
6063 :
6064 : return (res == 1) ? TRUE : FALSE;
6065 :
6066 : #endif
6067 : }
6068 :
6069 : else
6070 : {
6071 :
6072 : #ifndef HAVE_GEOS
6073 :
6074 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6075 : return FALSE;
6076 :
6077 : #else
6078 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
6079 : #endif /* HAVE_GEOS */
6080 : }
6081 : }
6082 :
6083 : /************************************************************************/
6084 : /* OGR_G_Crosses() */
6085 : /************************************************************************/
6086 : /**
6087 : * \brief Test for crossing.
6088 : *
6089 : * Tests if this geometry and the other geometry are crossing.
6090 : *
6091 : * Geometry validity is not checked. In case you are unsure of the validity
6092 : * of the input geometries, call IsValid() before, otherwise the result might
6093 : * be wrong.
6094 : *
6095 : * This function is the same as the C++ method OGRGeometry::Crosses().
6096 : *
6097 : * This function is built on the GEOS library, check it for the definition
6098 : * of the geometry operation.
6099 : * If OGR is built without the GEOS library, this function will always fail,
6100 : * issuing a CPLE_NotSupported error.
6101 : *
6102 : * @param hThis the geometry to compare.
6103 : * @param hOther the other geometry to compare.
6104 : *
6105 : * @return TRUE if they are crossing, otherwise FALSE.
6106 : */
6107 :
6108 8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
6109 :
6110 : {
6111 8 : VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
6112 :
6113 16 : return OGRGeometry::FromHandle(hThis)->Crosses(
6114 16 : OGRGeometry::FromHandle(hOther));
6115 : }
6116 :
6117 : /************************************************************************/
6118 : /* Within() */
6119 : /************************************************************************/
6120 :
6121 : /**
6122 : * \brief Test for containment.
6123 : *
6124 : * Tests if actual geometry object is within the passed geometry.
6125 : *
6126 : * Geometry validity is not checked. In case you are unsure of the validity
6127 : * of the input geometries, call IsValid() before, otherwise the result might
6128 : * be wrong.
6129 : *
6130 : * This method is the same as the C function OGR_G_Within().
6131 : *
6132 : * This method is built on the GEOS library, check it for the definition
6133 : * of the geometry operation.
6134 : * If OGR is built without the GEOS library, this method will always fail,
6135 : * issuing a CPLE_NotSupported error.
6136 : *
6137 : * @param poOtherGeom the geometry to compare to this geometry.
6138 : *
6139 : * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
6140 : */
6141 :
6142 22377 : OGRBoolean OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
6143 :
6144 : {
6145 : (void)poOtherGeom;
6146 : #ifndef HAVE_GEOS
6147 :
6148 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6149 : return FALSE;
6150 :
6151 : #else
6152 22377 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
6153 : #endif // HAVE_GEOS
6154 : }
6155 :
6156 : /************************************************************************/
6157 : /* OGR_G_Within() */
6158 : /************************************************************************/
6159 :
6160 : /**
6161 : * \brief Test for containment.
6162 : *
6163 : * Tests if this geometry is within the other geometry.
6164 : *
6165 : * Geometry validity is not checked. In case you are unsure of the validity
6166 : * of the input geometries, call IsValid() before, otherwise the result might
6167 : * be wrong.
6168 : *
6169 : * This function is the same as the C++ method OGRGeometry::Within().
6170 : *
6171 : * This function is built on the GEOS library, check it for the definition
6172 : * of the geometry operation.
6173 : * If OGR is built without the GEOS library, this function will always fail,
6174 : * issuing a CPLE_NotSupported error.
6175 : *
6176 : * @param hThis the geometry to compare.
6177 : * @param hOther the other geometry to compare.
6178 : *
6179 : * @return TRUE if hThis is within hOther, otherwise FALSE.
6180 : */
6181 7334 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
6182 :
6183 : {
6184 7334 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
6185 :
6186 14668 : return OGRGeometry::FromHandle(hThis)->Within(
6187 7334 : OGRGeometry::FromHandle(hOther));
6188 : }
6189 :
6190 : /************************************************************************/
6191 : /* Contains() */
6192 : /************************************************************************/
6193 :
6194 : /**
6195 : * \brief Test for containment.
6196 : *
6197 : * Tests if actual geometry object contains the passed geometry.
6198 : *
6199 : * Geometry validity is not checked. In case you are unsure of the validity
6200 : * of the input geometries, call IsValid() before, otherwise the result might
6201 : * be wrong.
6202 : *
6203 : * This method is the same as the C function OGR_G_Contains().
6204 : *
6205 : * This method is built on the GEOS library, check it for the definition
6206 : * of the geometry operation.
6207 : * If OGR is built without the GEOS library, this method will always fail,
6208 : * issuing a CPLE_NotSupported error.
6209 : *
6210 : * @param poOtherGeom the geometry to compare to this geometry.
6211 : *
6212 : * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
6213 : */
6214 :
6215 311 : OGRBoolean OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
6216 :
6217 : {
6218 : (void)poOtherGeom;
6219 : #ifndef HAVE_GEOS
6220 :
6221 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6222 : return FALSE;
6223 :
6224 : #else
6225 311 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
6226 : #endif // HAVE_GEOS
6227 : }
6228 :
6229 : /************************************************************************/
6230 : /* OGR_G_Contains() */
6231 : /************************************************************************/
6232 :
6233 : /**
6234 : * \brief Test for containment.
6235 : *
6236 : * Tests if this geometry contains the other geometry.
6237 : *
6238 : * Geometry validity is not checked. In case you are unsure of the validity
6239 : * of the input geometries, call IsValid() before, otherwise the result might
6240 : * be wrong.
6241 : *
6242 : * This function is the same as the C++ method OGRGeometry::Contains().
6243 : *
6244 : * This function is built on the GEOS library, check it for the definition
6245 : * of the geometry operation.
6246 : * If OGR is built without the GEOS library, this function will always fail,
6247 : * issuing a CPLE_NotSupported error.
6248 : *
6249 : * @param hThis the geometry to compare.
6250 : * @param hOther the other geometry to compare.
6251 : *
6252 : * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
6253 : */
6254 10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
6255 :
6256 : {
6257 10 : VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
6258 :
6259 20 : return OGRGeometry::FromHandle(hThis)->Contains(
6260 10 : OGRGeometry::FromHandle(hOther));
6261 : }
6262 :
6263 : /************************************************************************/
6264 : /* Overlaps() */
6265 : /************************************************************************/
6266 :
6267 : /**
6268 : * \brief Test for overlap.
6269 : *
6270 : * Tests if this geometry and the other passed into the method overlap, that is
6271 : * their intersection has a non-zero area.
6272 : *
6273 : * Geometry validity is not checked. In case you are unsure of the validity
6274 : * of the input geometries, call IsValid() before, otherwise the result might
6275 : * be wrong.
6276 : *
6277 : * This method is the same as the C function OGR_G_Overlaps().
6278 : *
6279 : * This method is built on the GEOS library, check it for the definition
6280 : * of the geometry operation.
6281 : * If OGR is built without the GEOS library, this method will always fail,
6282 : * issuing a CPLE_NotSupported error.
6283 : *
6284 : * @param poOtherGeom the geometry to compare to this geometry.
6285 : *
6286 : * @return TRUE if they are overlapping, otherwise FALSE.
6287 : */
6288 :
6289 7 : OGRBoolean OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
6290 :
6291 : {
6292 : (void)poOtherGeom;
6293 : #ifndef HAVE_GEOS
6294 :
6295 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6296 : return FALSE;
6297 :
6298 : #else
6299 7 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6300 : #endif // HAVE_GEOS
6301 : }
6302 :
6303 : /************************************************************************/
6304 : /* OGR_G_Overlaps() */
6305 : /************************************************************************/
6306 : /**
6307 : * \brief Test for overlap.
6308 : *
6309 : * Tests if this geometry and the other geometry overlap, that is their
6310 : * intersection has a non-zero area.
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 function is the same as the C++ method OGRGeometry::Overlaps().
6317 : *
6318 : * This function 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 function will always fail,
6321 : * issuing a CPLE_NotSupported error.
6322 : *
6323 : * @param hThis the geometry to compare.
6324 : * @param hOther the other geometry to compare.
6325 : *
6326 : * @return TRUE if they are overlapping, otherwise FALSE.
6327 : */
6328 :
6329 7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6330 :
6331 : {
6332 7 : VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6333 :
6334 14 : return OGRGeometry::FromHandle(hThis)->Overlaps(
6335 14 : OGRGeometry::FromHandle(hOther));
6336 : }
6337 :
6338 : /************************************************************************/
6339 : /* closeRings() */
6340 : /************************************************************************/
6341 :
6342 : /**
6343 : * \brief Force rings to be closed.
6344 : *
6345 : * If this geometry, or any contained geometries has polygon rings that
6346 : * are not closed, they will be closed by adding the starting point at
6347 : * the end.
6348 : */
6349 :
6350 1264 : void OGRGeometry::closeRings()
6351 : {
6352 1264 : }
6353 :
6354 : /************************************************************************/
6355 : /* OGR_G_CloseRings() */
6356 : /************************************************************************/
6357 :
6358 : /**
6359 : * \brief Force rings to be closed.
6360 : *
6361 : * If this geometry, or any contained geometries has polygon rings that
6362 : * are not closed, they will be closed by adding the starting point at
6363 : * the end.
6364 : *
6365 : * @param hGeom handle to the geometry.
6366 : */
6367 :
6368 6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
6369 :
6370 : {
6371 6 : VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6372 :
6373 6 : OGRGeometry::FromHandle(hGeom)->closeRings();
6374 : }
6375 :
6376 : /************************************************************************/
6377 : /* Centroid() */
6378 : /************************************************************************/
6379 :
6380 : /**
6381 : * \brief Compute the geometry centroid.
6382 : *
6383 : * The centroid location is applied to the passed in OGRPoint object.
6384 : * The centroid is not necessarily within the geometry.
6385 : *
6386 : * This method relates to the SFCOM ISurface::get_Centroid() method
6387 : * however the current implementation based on GEOS can operate on other
6388 : * geometry types such as multipoint, linestring, geometrycollection such as
6389 : * multipolygons.
6390 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6391 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6392 : * (multipolygons).
6393 : *
6394 : * This function is the same as the C function OGR_G_Centroid().
6395 : *
6396 : * This function is built on the GEOS library, check it for the definition
6397 : * of the geometry operation.
6398 : * If OGR is built without the GEOS library, this function will always fail,
6399 : * issuing a CPLE_NotSupported error.
6400 : *
6401 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6402 : *
6403 : * to OGRPolygon)
6404 : */
6405 :
6406 5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6407 :
6408 : {
6409 5 : if (poPoint == nullptr)
6410 0 : return OGRERR_FAILURE;
6411 :
6412 : #ifndef HAVE_GEOS
6413 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6414 : return OGRERR_FAILURE;
6415 :
6416 : #else
6417 :
6418 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6419 : GEOSGeom hThisGeosGeom =
6420 5 : exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6421 :
6422 5 : if (hThisGeosGeom != nullptr)
6423 : {
6424 5 : GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6425 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6426 :
6427 5 : if (hOtherGeosGeom == nullptr)
6428 : {
6429 0 : freeGEOSContext(hGEOSCtxt);
6430 0 : return OGRERR_FAILURE;
6431 : }
6432 :
6433 : OGRGeometry *poCentroidGeom =
6434 5 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6435 :
6436 5 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6437 :
6438 5 : if (poCentroidGeom == nullptr)
6439 : {
6440 0 : freeGEOSContext(hGEOSCtxt);
6441 0 : return OGRERR_FAILURE;
6442 : }
6443 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6444 : {
6445 0 : delete poCentroidGeom;
6446 0 : freeGEOSContext(hGEOSCtxt);
6447 0 : return OGRERR_FAILURE;
6448 : }
6449 :
6450 5 : if (getSpatialReference() != nullptr)
6451 0 : poCentroidGeom->assignSpatialReference(getSpatialReference());
6452 :
6453 5 : OGRPoint *poCentroid = poCentroidGeom->toPoint();
6454 :
6455 5 : if (!poCentroid->IsEmpty())
6456 : {
6457 4 : poPoint->setX(poCentroid->getX());
6458 4 : poPoint->setY(poCentroid->getY());
6459 : }
6460 : else
6461 : {
6462 1 : poPoint->empty();
6463 : }
6464 :
6465 5 : delete poCentroidGeom;
6466 :
6467 5 : freeGEOSContext(hGEOSCtxt);
6468 5 : return OGRERR_NONE;
6469 : }
6470 : else
6471 : {
6472 0 : freeGEOSContext(hGEOSCtxt);
6473 0 : return OGRERR_FAILURE;
6474 : }
6475 :
6476 : #endif // HAVE_GEOS
6477 : }
6478 :
6479 : /************************************************************************/
6480 : /* OGR_G_Centroid() */
6481 : /************************************************************************/
6482 :
6483 : /**
6484 : * \brief Compute the geometry centroid.
6485 : *
6486 : * The centroid location is applied to the passed in OGRPoint object.
6487 : * The centroid is not necessarily within the geometry.
6488 : *
6489 : * This method relates to the SFCOM ISurface::get_Centroid() method
6490 : * however the current implementation based on GEOS can operate on other
6491 : * geometry types such as multipoint, linestring, geometrycollection such as
6492 : * multipolygons.
6493 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6494 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6495 : * (multipolygons).
6496 : *
6497 : * This function is the same as the C++ method OGRGeometry::Centroid().
6498 : *
6499 : * This function is built on the GEOS library, check it for the definition
6500 : * of the geometry operation.
6501 : * If OGR is built without the GEOS library, this function will always fail,
6502 : * issuing a CPLE_NotSupported error.
6503 : *
6504 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6505 : */
6506 :
6507 5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6508 :
6509 : {
6510 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6511 :
6512 5 : OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6513 5 : if (poCentroidGeom == nullptr)
6514 0 : return OGRERR_FAILURE;
6515 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6516 : {
6517 0 : CPLError(CE_Failure, CPLE_AppDefined,
6518 : "Passed wrong geometry type as centroid argument.");
6519 0 : return OGRERR_FAILURE;
6520 : }
6521 :
6522 5 : return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6523 : }
6524 :
6525 : /************************************************************************/
6526 : /* OGR_G_PointOnSurface() */
6527 : /************************************************************************/
6528 :
6529 : /**
6530 : * \brief Returns a point guaranteed to lie on the surface.
6531 : *
6532 : * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6533 : * however the current implementation based on GEOS can operate on other
6534 : * geometry types than the types that are supported by SQL/MM-Part 3 :
6535 : * surfaces (polygons) and multisurfaces (multipolygons).
6536 : *
6537 : * This method is built on the GEOS library, check it for the definition
6538 : * of the geometry operation.
6539 : * If OGR is built without the GEOS library, this method will always fail,
6540 : * issuing a CPLE_NotSupported error.
6541 : *
6542 : * @param hGeom the geometry to operate on.
6543 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6544 : * or NULL if an error occurs.
6545 : *
6546 : */
6547 :
6548 4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6549 :
6550 : {
6551 4 : VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6552 :
6553 : #ifndef HAVE_GEOS
6554 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6555 : return nullptr;
6556 : #else
6557 :
6558 4 : OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6559 :
6560 4 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6561 4 : GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6562 :
6563 4 : if (hThisGeosGeom != nullptr)
6564 : {
6565 : GEOSGeom hOtherGeosGeom =
6566 4 : GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6567 4 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6568 :
6569 4 : if (hOtherGeosGeom == nullptr)
6570 : {
6571 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6572 0 : return nullptr;
6573 : }
6574 :
6575 : OGRGeometry *poInsidePointGeom =
6576 4 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6577 :
6578 4 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6579 :
6580 4 : if (poInsidePointGeom == nullptr)
6581 : {
6582 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6583 0 : return nullptr;
6584 : }
6585 4 : if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6586 : {
6587 0 : delete poInsidePointGeom;
6588 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6589 0 : return nullptr;
6590 : }
6591 :
6592 4 : if (poThis->getSpatialReference() != nullptr)
6593 0 : poInsidePointGeom->assignSpatialReference(
6594 0 : poThis->getSpatialReference());
6595 :
6596 4 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6597 4 : return OGRGeometry::ToHandle(poInsidePointGeom);
6598 : }
6599 :
6600 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6601 0 : return nullptr;
6602 : #endif
6603 : }
6604 :
6605 : /************************************************************************/
6606 : /* PointOnSurfaceInternal() */
6607 : /************************************************************************/
6608 :
6609 : //! @cond Doxygen_Suppress
6610 0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6611 : {
6612 0 : if (poPoint == nullptr || poPoint->IsEmpty())
6613 0 : return OGRERR_FAILURE;
6614 :
6615 0 : OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6616 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6617 0 : if (hInsidePoint == nullptr)
6618 0 : return OGRERR_FAILURE;
6619 :
6620 0 : OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6621 0 : if (poInsidePoint->IsEmpty())
6622 : {
6623 0 : poPoint->empty();
6624 : }
6625 : else
6626 : {
6627 0 : poPoint->setX(poInsidePoint->getX());
6628 0 : poPoint->setY(poInsidePoint->getY());
6629 : }
6630 :
6631 0 : OGR_G_DestroyGeometry(hInsidePoint);
6632 :
6633 0 : return OGRERR_NONE;
6634 : }
6635 :
6636 : //! @endcond
6637 :
6638 : /************************************************************************/
6639 : /* Simplify() */
6640 : /************************************************************************/
6641 :
6642 : /**
6643 : * \brief Simplify the geometry.
6644 : *
6645 : * This function is the same as the C function OGR_G_Simplify().
6646 : *
6647 : * This function is built on the GEOS library, check it for the definition
6648 : * of the geometry operation.
6649 : * If OGR is built without the GEOS library, this function will always fail,
6650 : * issuing a CPLE_NotSupported error.
6651 : *
6652 : * @param dTolerance the distance tolerance for the simplification.
6653 : *
6654 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6655 : *
6656 : */
6657 :
6658 55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
6659 :
6660 : {
6661 : (void)dTolerance;
6662 : #ifndef HAVE_GEOS
6663 :
6664 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6665 : return nullptr;
6666 :
6667 : #else
6668 55 : OGRGeometry *poOGRProduct = nullptr;
6669 :
6670 55 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6671 55 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6672 55 : if (hThisGeosGeom != nullptr)
6673 : {
6674 : GEOSGeom hGeosProduct =
6675 55 : GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6676 55 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6677 : poOGRProduct =
6678 55 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6679 : }
6680 55 : freeGEOSContext(hGEOSCtxt);
6681 55 : return poOGRProduct;
6682 :
6683 : #endif // HAVE_GEOS
6684 : }
6685 :
6686 : /************************************************************************/
6687 : /* OGR_G_Simplify() */
6688 : /************************************************************************/
6689 :
6690 : /**
6691 : * \brief Compute a simplified geometry.
6692 : *
6693 : * This function is the same as the C++ method OGRGeometry::Simplify().
6694 : *
6695 : * This function is built on the GEOS library, check it for the definition
6696 : * of the geometry operation.
6697 : * If OGR is built without the GEOS library, this function will always fail,
6698 : * issuing a CPLE_NotSupported error.
6699 : *
6700 : * @param hThis the geometry.
6701 : * @param dTolerance the distance tolerance for the simplification.
6702 : *
6703 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6704 : * or NULL if an error occurs.
6705 : *
6706 : */
6707 :
6708 1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6709 :
6710 : {
6711 1 : VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6712 1 : return OGRGeometry::ToHandle(
6713 1 : OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6714 : }
6715 :
6716 : /************************************************************************/
6717 : /* SimplifyPreserveTopology() */
6718 : /************************************************************************/
6719 :
6720 : /**
6721 : * \brief Simplify the geometry while preserving topology.
6722 : *
6723 : * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6724 : *
6725 : * This function is built on the GEOS library, check it for the definition
6726 : * of the geometry operation.
6727 : * If OGR is built without the GEOS library, this function will always fail,
6728 : * issuing a CPLE_NotSupported error.
6729 : *
6730 : * @param dTolerance the distance tolerance for the simplification.
6731 : *
6732 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6733 : *
6734 : */
6735 :
6736 17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
6737 :
6738 : {
6739 : (void)dTolerance;
6740 : #ifndef HAVE_GEOS
6741 :
6742 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6743 : return nullptr;
6744 :
6745 : #else
6746 17 : OGRGeometry *poOGRProduct = nullptr;
6747 :
6748 17 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6749 17 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6750 17 : if (hThisGeosGeom != nullptr)
6751 : {
6752 17 : GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6753 : hGEOSCtxt, hThisGeosGeom, dTolerance);
6754 17 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6755 : poOGRProduct =
6756 17 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6757 : }
6758 17 : freeGEOSContext(hGEOSCtxt);
6759 17 : return poOGRProduct;
6760 :
6761 : #endif // HAVE_GEOS
6762 : }
6763 :
6764 : /************************************************************************/
6765 : /* OGR_G_SimplifyPreserveTopology() */
6766 : /************************************************************************/
6767 :
6768 : /**
6769 : * \brief Simplify the geometry while preserving topology.
6770 : *
6771 : * This function is the same as the C++ method
6772 : * OGRGeometry::SimplifyPreserveTopology().
6773 : *
6774 : * This function is built on the GEOS library, check it for the definition
6775 : * of the geometry operation.
6776 : * If OGR is built without the GEOS library, this function will always fail,
6777 : * issuing a CPLE_NotSupported error.
6778 : *
6779 : * @param hThis the geometry.
6780 : * @param dTolerance the distance tolerance for the simplification.
6781 : *
6782 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6783 : * or NULL if an error occurs.
6784 : *
6785 : */
6786 :
6787 1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6788 : double dTolerance)
6789 :
6790 : {
6791 1 : VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6792 1 : return OGRGeometry::ToHandle(
6793 1 : OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6794 : }
6795 :
6796 : /************************************************************************/
6797 : /* roundCoordinates() */
6798 : /************************************************************************/
6799 :
6800 : /** Round coordinates of the geometry to the specified precision.
6801 : *
6802 : * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6803 : * will return valid geometries, whereas roundCoordinates() does not make
6804 : * such guarantee and may return geometries with invalidities, if they are
6805 : * not compatible of the specified precision. roundCoordinates() supports
6806 : * curve geometries, whereas SetPrecision() does not currently.
6807 : *
6808 : * One use case for roundCoordinates() is to undo the effect of
6809 : * quantizeCoordinates().
6810 : *
6811 : * @param sPrecision Contains the precision requirements.
6812 : * @since GDAL 3.9
6813 : */
6814 40 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6815 : {
6816 : struct Rounder : public OGRDefaultGeometryVisitor
6817 : {
6818 : const OGRGeomCoordinatePrecision &m_precision;
6819 : const double m_invXYResolution;
6820 : const double m_invZResolution;
6821 : const double m_invMResolution;
6822 :
6823 40 : explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6824 40 : : m_precision(sPrecisionIn),
6825 40 : m_invXYResolution(m_precision.dfXYResolution !=
6826 : OGRGeomCoordinatePrecision::UNKNOWN
6827 40 : ? 1.0 / m_precision.dfXYResolution
6828 : : 0.0),
6829 40 : m_invZResolution(m_precision.dfZResolution !=
6830 : OGRGeomCoordinatePrecision::UNKNOWN
6831 40 : ? 1.0 / m_precision.dfZResolution
6832 : : 0.0),
6833 40 : m_invMResolution(m_precision.dfMResolution !=
6834 : OGRGeomCoordinatePrecision::UNKNOWN
6835 40 : ? 1.0 / m_precision.dfMResolution
6836 120 : : 0.0)
6837 : {
6838 40 : }
6839 :
6840 : using OGRDefaultGeometryVisitor::visit;
6841 :
6842 386 : void visit(OGRPoint *poPoint) override
6843 : {
6844 386 : if (m_precision.dfXYResolution !=
6845 : OGRGeomCoordinatePrecision::UNKNOWN)
6846 : {
6847 386 : poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6848 386 : m_precision.dfXYResolution);
6849 386 : poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6850 386 : m_precision.dfXYResolution);
6851 : }
6852 772 : if (m_precision.dfZResolution !=
6853 390 : OGRGeomCoordinatePrecision::UNKNOWN &&
6854 4 : poPoint->Is3D())
6855 : {
6856 4 : poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6857 4 : m_precision.dfZResolution);
6858 : }
6859 772 : if (m_precision.dfMResolution !=
6860 390 : OGRGeomCoordinatePrecision::UNKNOWN &&
6861 4 : poPoint->IsMeasured())
6862 : {
6863 4 : poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6864 4 : m_precision.dfMResolution);
6865 : }
6866 386 : }
6867 : };
6868 :
6869 80 : Rounder rounder(sPrecision);
6870 40 : accept(&rounder);
6871 40 : }
6872 :
6873 : /************************************************************************/
6874 : /* SetPrecision() */
6875 : /************************************************************************/
6876 :
6877 : /** Set the geometry's precision, rounding all its coordinates to the precision
6878 : * grid, and making sure the geometry is still valid.
6879 : *
6880 : * This is a stronger version of roundCoordinates().
6881 : *
6882 : * Note that at time of writing GEOS does no supported curve geometries. So
6883 : * currently if this function is called on such a geometry, OGR will first call
6884 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6885 : * that it is unlikely to yield to the expected result.
6886 : *
6887 : * This function is the same as the C function OGR_G_SetPrecision().
6888 : *
6889 : * This function is built on the GEOSGeom_setPrecision_r() function of the
6890 : * GEOS library. Check it for the definition of the geometry operation.
6891 : * If OGR is built without the GEOS library, this function will always fail,
6892 : * issuing a CPLE_NotSupported error.
6893 : *
6894 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
6895 : * precision.
6896 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
6897 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
6898 : *
6899 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6900 : *
6901 : * @since GDAL 3.9
6902 : */
6903 :
6904 6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
6905 : {
6906 : (void)dfGridSize;
6907 : (void)nFlags;
6908 : #ifndef HAVE_GEOS
6909 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6910 : return nullptr;
6911 :
6912 : #else
6913 6 : OGRGeometry *poOGRProduct = nullptr;
6914 :
6915 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6916 6 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6917 6 : if (hThisGeosGeom != nullptr)
6918 : {
6919 6 : GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
6920 : hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
6921 6 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6922 : poOGRProduct =
6923 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6924 : }
6925 6 : freeGEOSContext(hGEOSCtxt);
6926 6 : return poOGRProduct;
6927 :
6928 : #endif // HAVE_GEOS
6929 : }
6930 :
6931 : /************************************************************************/
6932 : /* OGR_G_SetPrecision() */
6933 : /************************************************************************/
6934 :
6935 : /** Set the geometry's precision, rounding all its coordinates to the precision
6936 : * grid, and making sure the geometry is still valid.
6937 : *
6938 : * This is a stronger version of roundCoordinates().
6939 : *
6940 : * Note that at time of writing GEOS does no supported curve geometries. So
6941 : * currently if this function is called on such a geometry, OGR will first call
6942 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6943 : * that it is unlikely to yield to the expected result.
6944 : *
6945 : * This function is the same as the C++ method OGRGeometry::SetPrecision().
6946 : *
6947 : * This function is built on the GEOSGeom_setPrecision_r() function of the
6948 : * GEOS library. Check it for the definition of the geometry operation.
6949 : * If OGR is built without the GEOS library, this function will always fail,
6950 : * issuing a CPLE_NotSupported error.
6951 : *
6952 : * @param hThis the geometry.
6953 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
6954 : * precision.
6955 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
6956 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
6957 : *
6958 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6959 : * or NULL if an error occurs.
6960 : *
6961 : * @since GDAL 3.9
6962 : */
6963 1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
6964 : int nFlags)
6965 : {
6966 1 : VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
6967 1 : return OGRGeometry::ToHandle(
6968 1 : OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
6969 : }
6970 :
6971 : /************************************************************************/
6972 : /* DelaunayTriangulation() */
6973 : /************************************************************************/
6974 :
6975 : /**
6976 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
6977 : *
6978 : * This function is the same as the C function OGR_G_DelaunayTriangulation().
6979 : *
6980 : * This function is built on the GEOS library, v3.4 or above.
6981 : * If OGR is built without the GEOS library, this function will always fail,
6982 : * issuing a CPLE_NotSupported error.
6983 : *
6984 : * @param dfTolerance optional snapping tolerance to use for improved robustness
6985 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
6986 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
6987 : *
6988 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6989 : */
6990 :
6991 : #ifndef HAVE_GEOS
6992 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
6993 : int /*bOnlyEdges*/) const
6994 : {
6995 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6996 : return nullptr;
6997 : }
6998 : #else
6999 1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
7000 : int bOnlyEdges) const
7001 : {
7002 1 : OGRGeometry *poOGRProduct = nullptr;
7003 :
7004 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7005 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7006 1 : if (hThisGeosGeom != nullptr)
7007 : {
7008 1 : GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
7009 : hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
7010 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7011 : poOGRProduct =
7012 1 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7013 : }
7014 1 : freeGEOSContext(hGEOSCtxt);
7015 1 : return poOGRProduct;
7016 : }
7017 : #endif
7018 :
7019 : /************************************************************************/
7020 : /* OGR_G_DelaunayTriangulation() */
7021 : /************************************************************************/
7022 :
7023 : /**
7024 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
7025 : *
7026 : * This function is the same as the C++ method
7027 : * OGRGeometry::DelaunayTriangulation().
7028 : *
7029 : * This function is built on the GEOS library, v3.4 or above.
7030 : * If OGR is built without the GEOS library, this function will always fail,
7031 : * issuing a CPLE_NotSupported error.
7032 : *
7033 : * @param hThis the geometry.
7034 : * @param dfTolerance optional snapping tolerance to use for improved robustness
7035 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7036 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7037 : *
7038 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7039 : * or NULL if an error occurs.
7040 : */
7041 :
7042 1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
7043 : int bOnlyEdges)
7044 :
7045 : {
7046 1 : VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
7047 :
7048 1 : return OGRGeometry::ToHandle(
7049 : OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
7050 1 : bOnlyEdges));
7051 : }
7052 :
7053 : /************************************************************************/
7054 : /* ConstrainedDelaunayTriangulation() */
7055 : /************************************************************************/
7056 :
7057 : /**
7058 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7059 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7060 : * geometry collection.
7061 : *
7062 : * This function is the same as the C function
7063 : * OGR_G_ConstrainedDelaunayTriangulation().
7064 : *
7065 : * This function is built on the GEOS library, v3.10 or above.
7066 : * If OGR is built without the GEOS library, this function will always fail,
7067 : * issuing a CPLE_NotSupported error.
7068 : *
7069 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7070 : *
7071 : * @since OGR 3.12
7072 : */
7073 :
7074 3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
7075 : {
7076 : #ifndef HAVE_GEOS
7077 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7078 : return nullptr;
7079 : #elif !(GEOS_VERSION_MAJOR > 3 || \
7080 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
7081 : CPLError(
7082 : CE_Failure, CPLE_NotSupported,
7083 : "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
7084 : return nullptr;
7085 : #else
7086 :
7087 3 : OGRGeometry *poOGRProduct = nullptr;
7088 :
7089 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7090 3 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7091 3 : if (hThisGeosGeom != nullptr)
7092 : {
7093 : GEOSGeom hGeosProduct =
7094 3 : GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
7095 3 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7096 : poOGRProduct =
7097 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7098 : }
7099 3 : freeGEOSContext(hGEOSCtxt);
7100 3 : return poOGRProduct;
7101 : #endif
7102 : }
7103 :
7104 : /************************************************************************/
7105 : /* OGR_G_ConstrainedDelaunayTriangulation() */
7106 : /************************************************************************/
7107 :
7108 : /**
7109 : * \brief Return a constrained Delaunay triangulation of the vertices of the
7110 : * given polygon(s). For non-polygonal inputs, silently returns an empty
7111 : * geometry collection.
7112 : *
7113 : * This function is the same as the C++ method
7114 : * OGRGeometry::ConstrainedDelaunayTriangulation().
7115 : *
7116 : * This function is built on the GEOS library, v3.10 or above.
7117 : * If OGR is built without the GEOS library, this function will always fail,
7118 : * issuing a CPLE_NotSupported error.
7119 : *
7120 : * @param hThis the geometry.
7121 : *
7122 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7123 : * or NULL if an error occurs.
7124 : *
7125 : * @since OGR 3.12
7126 : */
7127 :
7128 3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
7129 : {
7130 3 : VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
7131 :
7132 3 : return OGRGeometry::ToHandle(
7133 3 : OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
7134 : }
7135 :
7136 : /************************************************************************/
7137 : /* Polygonize() */
7138 : /************************************************************************/
7139 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
7140 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
7141 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
7142 : /* AMBIENTALE */
7143 : /************************************************************************/
7144 :
7145 : /**
7146 : * \brief Polygonizes a set of sparse edges.
7147 : *
7148 : * A new geometry object is created and returned containing a collection
7149 : * of reassembled Polygons: NULL will be returned if the input collection
7150 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7151 : * into Polygons is impossible due to topological inconsistencies.
7152 : *
7153 : * This method is the same as the C function OGR_G_Polygonize().
7154 : *
7155 : * This method is built on the GEOS library, check it for the definition
7156 : * of the geometry operation.
7157 : * If OGR is built without the GEOS library, this method will always fail,
7158 : * issuing a CPLE_NotSupported error.
7159 : *
7160 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7161 : *
7162 : */
7163 :
7164 117 : OGRGeometry *OGRGeometry::Polygonize() const
7165 :
7166 : {
7167 : #ifndef HAVE_GEOS
7168 :
7169 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7170 : return nullptr;
7171 :
7172 : #else
7173 :
7174 117 : const OGRGeometryCollection *poColl = nullptr;
7175 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
7176 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
7177 116 : poColl = toGeometryCollection();
7178 : else
7179 1 : return nullptr;
7180 :
7181 116 : const int nCount = poColl->getNumGeometries();
7182 :
7183 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
7184 116 : bool bError = false;
7185 :
7186 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7187 :
7188 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
7189 747 : for (int ig = 0; ig < nCount; ig++)
7190 : {
7191 631 : GEOSGeom hGeosGeom = nullptr;
7192 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
7193 1262 : if (poChild == nullptr ||
7194 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
7195 1 : bError = true;
7196 : else
7197 : {
7198 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
7199 630 : if (hGeosGeom == nullptr)
7200 0 : bError = true;
7201 : }
7202 631 : pahGeosGeomList[ig] = hGeosGeom;
7203 : }
7204 :
7205 116 : if (!bError)
7206 : {
7207 : GEOSGeom hGeosPolygs =
7208 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
7209 :
7210 : poPolygsOGRGeom =
7211 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7212 : }
7213 :
7214 747 : for (int ig = 0; ig < nCount; ig++)
7215 : {
7216 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
7217 631 : if (hGeosGeom != nullptr)
7218 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
7219 : }
7220 116 : delete[] pahGeosGeomList;
7221 116 : freeGEOSContext(hGEOSCtxt);
7222 :
7223 116 : return poPolygsOGRGeom;
7224 :
7225 : #endif // HAVE_GEOS
7226 : }
7227 :
7228 : /************************************************************************/
7229 : /* OGR_G_Polygonize() */
7230 : /************************************************************************/
7231 : /**
7232 : * \brief Polygonizes a set of sparse edges.
7233 : *
7234 : * A new geometry object is created and returned containing a collection
7235 : * of reassembled Polygons: NULL will be returned if the input collection
7236 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7237 : * into Polygons is impossible due to topological inconsistencies.
7238 : *
7239 : * This function is the same as the C++ method OGRGeometry::Polygonize().
7240 : *
7241 : * This function is built on the GEOS library, check it for the definition
7242 : * of the geometry operation.
7243 : * If OGR is built without the GEOS library, this function will always fail,
7244 : * issuing a CPLE_NotSupported error.
7245 : *
7246 : * @param hTarget The Geometry to be polygonized.
7247 : *
7248 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7249 : * or NULL if an error occurs.
7250 : *
7251 : */
7252 :
7253 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
7254 :
7255 : {
7256 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
7257 :
7258 3 : return OGRGeometry::ToHandle(
7259 3 : OGRGeometry::FromHandle(hTarget)->Polygonize());
7260 : }
7261 :
7262 : /************************************************************************/
7263 : /* BuildArea() */
7264 : /************************************************************************/
7265 :
7266 : /**
7267 : * \brief Polygonize a linework assuming inner polygons are holes.
7268 : *
7269 : * This method is the same as the C function OGR_G_BuildArea().
7270 : *
7271 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
7272 : * Additionally, holes are dropped and the result is unified producing
7273 : * a single Polygon or a MultiPolygon.
7274 : *
7275 : * A new geometry object is created and returned: NULL on failure,
7276 : * empty GeometryCollection if the input geometry cannot be polygonized,
7277 : * Polygon or MultiPolygon on success.
7278 : *
7279 : * This method is built on the GEOSBuildArea_r() function of the GEOS
7280 : * library, check it for the definition of the geometry operation.
7281 : * If OGR is built without the GEOS library, this method will always fail,
7282 : * issuing a CPLE_NotSupported error.
7283 : *
7284 : * @return a newly allocated geometry now owned by the caller,
7285 : * or NULL on failure.
7286 : *
7287 : * @since OGR 3.11
7288 : */
7289 :
7290 30 : OGRGeometry *OGRGeometry::BuildArea() const
7291 :
7292 : {
7293 : #ifndef HAVE_GEOS
7294 :
7295 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7296 : return nullptr;
7297 :
7298 : #else
7299 :
7300 30 : OGRGeometry *poPolygsOGRGeom = nullptr;
7301 :
7302 30 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7303 30 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7304 30 : if (hThisGeosGeom != nullptr)
7305 : {
7306 30 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7307 : poPolygsOGRGeom =
7308 30 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7309 30 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7310 : }
7311 30 : freeGEOSContext(hGEOSCtxt);
7312 :
7313 30 : return poPolygsOGRGeom;
7314 :
7315 : #endif // HAVE_GEOS
7316 : }
7317 :
7318 : /************************************************************************/
7319 : /* OGR_G_BuildArea() */
7320 : /************************************************************************/
7321 :
7322 : /**
7323 : * \brief Polygonize a linework assuming inner polygons are holes.
7324 : *
7325 : * This function is the same as the C++ method OGRGeometry::BuildArea().
7326 : *
7327 : * Polygonization is performed similarly to OGR_G_Polygonize().
7328 : * Additionally, holes are dropped and the result is unified producing
7329 : * a single Polygon or a MultiPolygon.
7330 : *
7331 : * A new geometry object is created and returned: NULL on failure,
7332 : * empty GeometryCollection if the input geometry cannot be polygonized,
7333 : * Polygon or MultiPolygon on success.
7334 : *
7335 : * This function is built on the GEOSBuildArea_r() function of the GEOS
7336 : * library, check it for the definition of the geometry operation.
7337 : * If OGR is built without the GEOS library, this function will always fail,
7338 : * issuing a CPLE_NotSupported error.
7339 : *
7340 : * @param hGeom handle on the geometry to polygonize.
7341 : *
7342 : * @return a handle on newly allocated geometry now owned by the caller,
7343 : * or NULL on failure.
7344 : *
7345 : * @since OGR 3.11
7346 : */
7347 :
7348 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7349 :
7350 : {
7351 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7352 :
7353 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7354 : }
7355 :
7356 : /************************************************************************/
7357 : /* swapXY() */
7358 : /************************************************************************/
7359 :
7360 : /**
7361 : * \brief Swap x and y coordinates.
7362 : *
7363 : */
7364 :
7365 0 : void OGRGeometry::swapXY()
7366 :
7367 : {
7368 0 : }
7369 :
7370 : /************************************************************************/
7371 : /* swapXY() */
7372 : /************************************************************************/
7373 :
7374 : /**
7375 : * \brief Swap x and y coordinates.
7376 : *
7377 : * @param hGeom geometry.
7378 : */
7379 :
7380 22 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7381 : {
7382 22 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7383 :
7384 22 : OGRGeometry::FromHandle(hGeom)->swapXY();
7385 : }
7386 :
7387 : /************************************************************************/
7388 : /* Prepared geometry API */
7389 : /************************************************************************/
7390 :
7391 : #if defined(HAVE_GEOS)
7392 : struct _OGRPreparedGeometry
7393 : {
7394 : GEOSContextHandle_t hGEOSCtxt;
7395 : GEOSGeom hGEOSGeom;
7396 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7397 : };
7398 : #endif
7399 :
7400 : /************************************************************************/
7401 : /* OGRHasPreparedGeometrySupport() */
7402 : /************************************************************************/
7403 :
7404 : /** Returns if GEOS has prepared geometry support.
7405 : * @return TRUE or FALSE
7406 : */
7407 1 : int OGRHasPreparedGeometrySupport()
7408 : {
7409 : #if defined(HAVE_GEOS)
7410 1 : return TRUE;
7411 : #else
7412 : return FALSE;
7413 : #endif
7414 : }
7415 :
7416 : /************************************************************************/
7417 : /* OGRCreatePreparedGeometry() */
7418 : /************************************************************************/
7419 :
7420 : /** Creates a prepared geometry.
7421 : *
7422 : * To free with OGRDestroyPreparedGeometry()
7423 : *
7424 : * @param hGeom input geometry to prepare.
7425 : * @return handle to a prepared geometry.
7426 : * @since GDAL 3.3
7427 : */
7428 52569 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
7429 : {
7430 : (void)hGeom;
7431 : #if defined(HAVE_GEOS)
7432 52569 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7433 52569 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7434 52569 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7435 52569 : if (hGEOSGeom == nullptr)
7436 : {
7437 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7438 0 : return nullptr;
7439 : }
7440 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7441 52569 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7442 52569 : if (poPreparedGEOSGeom == nullptr)
7443 : {
7444 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7445 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7446 0 : return nullptr;
7447 : }
7448 :
7449 52569 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7450 52569 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7451 52569 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7452 52569 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7453 :
7454 52569 : return poPreparedGeom;
7455 : #else
7456 : return nullptr;
7457 : #endif
7458 : }
7459 :
7460 : /************************************************************************/
7461 : /* OGRDestroyPreparedGeometry() */
7462 : /************************************************************************/
7463 :
7464 : /** Destroys a prepared geometry.
7465 : * @param hPreparedGeom prepared geometry.
7466 : * @since GDAL 3.3
7467 : */
7468 52615 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
7469 : {
7470 : (void)hPreparedGeom;
7471 : #if defined(HAVE_GEOS)
7472 52615 : if (hPreparedGeom != nullptr)
7473 : {
7474 52569 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7475 : hPreparedGeom->poPreparedGEOSGeom);
7476 52569 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7477 52569 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7478 52569 : delete hPreparedGeom;
7479 : }
7480 : #endif
7481 52615 : }
7482 :
7483 : /************************************************************************/
7484 : /* OGRPreparedGeometryIntersects() */
7485 : /************************************************************************/
7486 :
7487 : /** Returns whether a prepared geometry intersects with a geometry.
7488 : * @param hPreparedGeom prepared geometry.
7489 : * @param hOtherGeom other geometry.
7490 : * @return TRUE or FALSE.
7491 : * @since GDAL 3.3
7492 : */
7493 5563 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
7494 : const OGRGeometryH hOtherGeom)
7495 : {
7496 : (void)hPreparedGeom;
7497 : (void)hOtherGeom;
7498 : #if defined(HAVE_GEOS)
7499 5563 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7500 5563 : if (hPreparedGeom == nullptr ||
7501 : poOtherGeom == nullptr
7502 : // The check for IsEmpty() is for buggy GEOS versions.
7503 : // See https://github.com/libgeos/geos/pull/423
7504 11126 : || poOtherGeom->IsEmpty())
7505 : {
7506 1 : return FALSE;
7507 : }
7508 :
7509 : GEOSGeom hGEOSOtherGeom =
7510 5562 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7511 5562 : if (hGEOSOtherGeom == nullptr)
7512 0 : return FALSE;
7513 :
7514 : const bool bRet =
7515 5562 : GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
7516 : hPreparedGeom->poPreparedGEOSGeom,
7517 5562 : hGEOSOtherGeom) == 1;
7518 5562 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7519 :
7520 5562 : return bRet;
7521 : #else
7522 : return FALSE;
7523 : #endif
7524 : }
7525 :
7526 : /** Returns whether a prepared geometry contains a geometry.
7527 : * @param hPreparedGeom prepared geometry.
7528 : * @param hOtherGeom other geometry.
7529 : * @return TRUE or FALSE.
7530 : */
7531 120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
7532 : const OGRGeometryH hOtherGeom)
7533 : {
7534 : (void)hPreparedGeom;
7535 : (void)hOtherGeom;
7536 : #if defined(HAVE_GEOS)
7537 120516 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7538 120516 : if (hPreparedGeom == nullptr ||
7539 : poOtherGeom == nullptr
7540 : // The check for IsEmpty() is for buggy GEOS versions.
7541 : // See https://github.com/libgeos/geos/pull/423
7542 241032 : || poOtherGeom->IsEmpty())
7543 : {
7544 1 : return FALSE;
7545 : }
7546 :
7547 : GEOSGeom hGEOSOtherGeom =
7548 120515 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7549 120515 : if (hGEOSOtherGeom == nullptr)
7550 0 : return FALSE;
7551 :
7552 120515 : const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
7553 : hPreparedGeom->poPreparedGEOSGeom,
7554 120515 : hGEOSOtherGeom) == 1;
7555 120515 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7556 :
7557 120515 : return bRet;
7558 : #else
7559 : return FALSE;
7560 : #endif
7561 : }
7562 :
7563 : /************************************************************************/
7564 : /* OGRGeometryFromEWKB() */
7565 : /************************************************************************/
7566 :
7567 1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7568 : int bIsPostGIS1_EWKB)
7569 :
7570 : {
7571 1445 : OGRGeometry *poGeometry = nullptr;
7572 :
7573 1445 : size_t nWKBSize = 0;
7574 1445 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7575 1445 : if (pabyWKB == nullptr)
7576 0 : return nullptr;
7577 :
7578 : /* -------------------------------------------------------------------- */
7579 : /* Try to ingest the geometry. */
7580 : /* -------------------------------------------------------------------- */
7581 1445 : (void)OGRGeometryFactory::createFromWkb(
7582 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7583 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7584 :
7585 1445 : return poGeometry;
7586 : }
7587 :
7588 : /************************************************************************/
7589 : /* OGRGeometryFromHexEWKB() */
7590 : /************************************************************************/
7591 :
7592 1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7593 : int bIsPostGIS1_EWKB)
7594 :
7595 : {
7596 1443 : if (pszBytea == nullptr)
7597 0 : return nullptr;
7598 :
7599 1443 : int nWKBLength = 0;
7600 1443 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7601 :
7602 : OGRGeometry *poGeometry =
7603 1443 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7604 :
7605 1443 : CPLFree(pabyWKB);
7606 :
7607 1443 : return poGeometry;
7608 : }
7609 :
7610 : /************************************************************************/
7611 : /* OGRGeometryToHexEWKB() */
7612 : /************************************************************************/
7613 :
7614 1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
7615 : int nPostGISMajor, int nPostGISMinor)
7616 : {
7617 1071 : const size_t nWkbSize = poGeometry->WkbSize();
7618 1071 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7619 1071 : if (pabyWKB == nullptr)
7620 0 : return CPLStrdup("");
7621 :
7622 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7623 1815 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7624 626 : poGeometry->IsEmpty())
7625 : {
7626 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7627 : OGRERR_NONE)
7628 : {
7629 0 : CPLFree(pabyWKB);
7630 0 : return CPLStrdup("");
7631 : }
7632 : }
7633 1069 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7634 : (nPostGISMajor < 2)
7635 : ? wkbVariantPostGIS1
7636 1069 : : wkbVariantOldOgc) != OGRERR_NONE)
7637 : {
7638 0 : CPLFree(pabyWKB);
7639 0 : return CPLStrdup("");
7640 : }
7641 :
7642 : // When converting to hex, each byte takes 2 hex characters. In addition
7643 : // we add in 8 characters to represent the SRID integer in hex, and
7644 : // one for a null terminator.
7645 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7646 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7647 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7648 1071 : if (nWkbSize >
7649 1071 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7650 : {
7651 0 : CPLFree(pabyWKB);
7652 0 : return CPLStrdup("");
7653 : }
7654 1071 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7655 1071 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7656 1071 : if (pszTextBuf == nullptr)
7657 : {
7658 0 : CPLFree(pabyWKB);
7659 0 : return CPLStrdup("");
7660 : }
7661 1071 : char *pszTextBufCurrent = pszTextBuf;
7662 :
7663 : // Convert the 1st byte, which is the endianness flag, to hex.
7664 1071 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7665 1071 : strcpy(pszTextBufCurrent, pszHex);
7666 1071 : CPLFree(pszHex);
7667 1071 : pszTextBufCurrent += 2;
7668 :
7669 : // Next, get the geom type which is bytes 2 through 5.
7670 : GUInt32 geomType;
7671 1071 : memcpy(&geomType, pabyWKB + 1, 4);
7672 :
7673 : // Now add the SRID flag if an SRID is provided.
7674 1071 : if (nSRSId > 0)
7675 : {
7676 : // Change the flag to wkbNDR (little) endianness.
7677 541 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7678 541 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7679 : // Apply the flag.
7680 541 : geomType = geomType | nGSrsFlag;
7681 : }
7682 :
7683 : // Now write the geom type which is 4 bytes.
7684 1071 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7685 1071 : strcpy(pszTextBufCurrent, pszHex);
7686 1071 : CPLFree(pszHex);
7687 1071 : pszTextBufCurrent += 8;
7688 :
7689 : // Now include SRID if provided.
7690 1071 : if (nSRSId > 0)
7691 : {
7692 : // Force the srsid to wkbNDR (little) endianness.
7693 541 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7694 541 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7695 : reinterpret_cast<const GByte *>(&nGSRSId));
7696 541 : strcpy(pszTextBufCurrent, pszHex);
7697 541 : CPLFree(pszHex);
7698 541 : pszTextBufCurrent += 8;
7699 : }
7700 :
7701 : // Copy the rest of the data over - subtract
7702 : // 5 since we already copied 5 bytes above.
7703 1071 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7704 1071 : CPLFree(pabyWKB);
7705 1071 : if (!pszHex || pszHex[0] == 0)
7706 : {
7707 0 : CPLFree(pszTextBuf);
7708 0 : return pszHex;
7709 : }
7710 1071 : strcpy(pszTextBufCurrent, pszHex);
7711 1071 : CPLFree(pszHex);
7712 :
7713 1071 : return pszTextBuf;
7714 : }
7715 :
7716 : /************************************************************************/
7717 : /* importPreambleFromWkb() */
7718 : /************************************************************************/
7719 :
7720 : //! @cond Doxygen_Suppress
7721 180798 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7722 : size_t nSize,
7723 : OGRwkbByteOrder &eByteOrder,
7724 : OGRwkbVariant eWkbVariant)
7725 : {
7726 180798 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7727 0 : return OGRERR_NOT_ENOUGH_DATA;
7728 :
7729 : /* -------------------------------------------------------------------- */
7730 : /* Get the byte order byte. */
7731 : /* -------------------------------------------------------------------- */
7732 180798 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7733 180798 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7734 0 : return OGRERR_CORRUPT_DATA;
7735 180798 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7736 :
7737 : /* -------------------------------------------------------------------- */
7738 : /* Get the geometry feature type. */
7739 : /* -------------------------------------------------------------------- */
7740 : OGRwkbGeometryType eGeometryType;
7741 : const OGRErr err =
7742 180798 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7743 180798 : if (wkbHasZ(eGeometryType))
7744 62309 : flags |= OGR_G_3D;
7745 180798 : if (wkbHasM(eGeometryType))
7746 59692 : flags |= OGR_G_MEASURED;
7747 :
7748 180798 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7749 0 : return OGRERR_CORRUPT_DATA;
7750 :
7751 180798 : return OGRERR_NONE;
7752 : }
7753 :
7754 : /************************************************************************/
7755 : /* importPreambleOfCollectionFromWkb() */
7756 : /* */
7757 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7758 : /* OGRCurvePolygon and OGRGeometryCollection. */
7759 : /************************************************************************/
7760 :
7761 97429 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7762 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7763 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7764 : OGRwkbVariant eWkbVariant)
7765 : {
7766 97429 : nGeomCount = 0;
7767 :
7768 : OGRErr eErr =
7769 97429 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7770 97429 : if (eErr != OGRERR_NONE)
7771 0 : return eErr;
7772 :
7773 : /* -------------------------------------------------------------------- */
7774 : /* Clear existing Geoms. */
7775 : /* -------------------------------------------------------------------- */
7776 97429 : int _flags = flags; // flags set in importPreambleFromWkb
7777 97429 : empty(); // may reset flags etc.
7778 :
7779 : // restore
7780 97429 : if (_flags & OGR_G_3D)
7781 59265 : set3D(TRUE);
7782 97429 : if (_flags & OGR_G_MEASURED)
7783 56768 : setMeasured(TRUE);
7784 :
7785 : /* -------------------------------------------------------------------- */
7786 : /* Get the sub-geometry count. */
7787 : /* -------------------------------------------------------------------- */
7788 97429 : memcpy(&nGeomCount, pabyData + 5, 4);
7789 :
7790 97429 : if (OGR_SWAP(eByteOrder))
7791 386 : nGeomCount = CPL_SWAP32(nGeomCount);
7792 :
7793 194724 : if (nGeomCount < 0 ||
7794 97295 : static_cast<size_t>(nGeomCount) >
7795 97295 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7796 : {
7797 134 : nGeomCount = 0;
7798 134 : return OGRERR_CORRUPT_DATA;
7799 : }
7800 97295 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7801 :
7802 : // Each ring has a minimum of nMinSubGeomSize bytes.
7803 97295 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7804 : {
7805 910 : CPLError(CE_Failure, CPLE_AppDefined,
7806 : "Length of input WKB is too small");
7807 910 : nGeomCount = 0;
7808 910 : return OGRERR_NOT_ENOUGH_DATA;
7809 : }
7810 :
7811 96385 : nDataOffset = 9;
7812 96385 : if (nSize != static_cast<size_t>(-1))
7813 : {
7814 96365 : CPLAssert(nSize >= nDataOffset);
7815 96365 : nSize -= nDataOffset;
7816 : }
7817 :
7818 96385 : return OGRERR_NONE;
7819 : }
7820 :
7821 : /************************************************************************/
7822 : /* importCurveCollectionFromWkt() */
7823 : /* */
7824 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7825 : /* OGRMultiCurve. */
7826 : /************************************************************************/
7827 :
7828 1445 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7829 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7830 : int bAllowCurve, int bAllowCompoundCurve,
7831 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7832 :
7833 : {
7834 1445 : int bHasZ = FALSE;
7835 1445 : int bHasM = FALSE;
7836 1445 : bool bIsEmpty = false;
7837 1445 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7838 1445 : flags = 0;
7839 1445 : if (eErr != OGRERR_NONE)
7840 14 : return eErr;
7841 1431 : if (bHasZ)
7842 206 : flags |= OGR_G_3D;
7843 1431 : if (bHasM)
7844 132 : flags |= OGR_G_MEASURED;
7845 1431 : if (bIsEmpty)
7846 111 : return OGRERR_NONE;
7847 :
7848 : char szToken[OGR_WKT_TOKEN_MAX];
7849 1320 : const char *pszInput = *ppszInput;
7850 1320 : eErr = OGRERR_NONE;
7851 :
7852 : // Skip first '('.
7853 1320 : pszInput = OGRWktReadToken(pszInput, szToken);
7854 :
7855 : /* ==================================================================== */
7856 : /* Read each curve in turn. Note that we try to reuse the same */
7857 : /* point list buffer from curve to curve to cut down on */
7858 : /* allocate/deallocate overhead. */
7859 : /* ==================================================================== */
7860 1320 : OGRRawPoint *paoPoints = nullptr;
7861 1320 : int nMaxPoints = 0;
7862 1320 : double *padfZ = nullptr;
7863 :
7864 653 : do
7865 : {
7866 :
7867 : /* --------------------------------------------------------------------
7868 : */
7869 : /* Get the first token, which should be the geometry type. */
7870 : /* --------------------------------------------------------------------
7871 : */
7872 1973 : const char *pszInputBefore = pszInput;
7873 1973 : pszInput = OGRWktReadToken(pszInput, szToken);
7874 :
7875 : /* --------------------------------------------------------------------
7876 : */
7877 : /* Do the import. */
7878 : /* --------------------------------------------------------------------
7879 : */
7880 1973 : OGRCurve *poCurve = nullptr;
7881 1973 : if (EQUAL(szToken, "("))
7882 : {
7883 1425 : OGRLineString *poLine = new OGRLineString();
7884 1425 : poCurve = poLine;
7885 1425 : pszInput = pszInputBefore;
7886 1425 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
7887 : paoPoints, nMaxPoints, padfZ);
7888 : }
7889 548 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
7890 : {
7891 16 : poCurve = new OGRLineString();
7892 : }
7893 : // Accept LINESTRING(), but this is an extension to the BNF, also
7894 : // accepted by PostGIS.
7895 532 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
7896 517 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
7897 517 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
7898 1222 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
7899 158 : (bAllowCompoundCurve &&
7900 158 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
7901 : {
7902 494 : OGRGeometry *poGeom = nullptr;
7903 494 : pszInput = pszInputBefore;
7904 : eErr =
7905 494 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
7906 494 : if (poGeom == nullptr)
7907 : {
7908 1 : eErr = OGRERR_CORRUPT_DATA;
7909 : }
7910 : else
7911 : {
7912 493 : poCurve = poGeom->toCurve();
7913 : }
7914 : }
7915 : else
7916 : {
7917 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
7918 : szToken);
7919 38 : eErr = OGRERR_CORRUPT_DATA;
7920 : }
7921 :
7922 : // If this has M it is an error if poGeom does not have M.
7923 1973 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
7924 0 : eErr = OGRERR_CORRUPT_DATA;
7925 :
7926 1973 : if (eErr == OGRERR_NONE)
7927 1928 : eErr = pfnAddCurveDirectly(this, poCurve);
7928 1973 : if (eErr != OGRERR_NONE)
7929 : {
7930 55 : delete poCurve;
7931 55 : break;
7932 : }
7933 :
7934 : /* --------------------------------------------------------------------
7935 : */
7936 : /* Read the delimiter following the surface. */
7937 : /* --------------------------------------------------------------------
7938 : */
7939 1918 : pszInput = OGRWktReadToken(pszInput, szToken);
7940 1918 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
7941 :
7942 1320 : CPLFree(paoPoints);
7943 1320 : CPLFree(padfZ);
7944 :
7945 : /* -------------------------------------------------------------------- */
7946 : /* freak if we don't get a closing bracket. */
7947 : /* -------------------------------------------------------------------- */
7948 :
7949 1320 : if (eErr != OGRERR_NONE)
7950 55 : return eErr;
7951 :
7952 1265 : if (szToken[0] != ')')
7953 9 : return OGRERR_CORRUPT_DATA;
7954 :
7955 1256 : *ppszInput = pszInput;
7956 1256 : return OGRERR_NONE;
7957 : }
7958 :
7959 : //! @endcond
7960 :
7961 : /************************************************************************/
7962 : /* OGR_GT_Flatten() */
7963 : /************************************************************************/
7964 : /**
7965 : * \brief Returns the 2D geometry type corresponding to the passed geometry
7966 : * type.
7967 : *
7968 : * This function is intended to work with geometry types as old-style 99-402
7969 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
7970 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
7971 : *
7972 : * @param eType Input geometry type
7973 : *
7974 : * @return 2D geometry type corresponding to the passed geometry type.
7975 : *
7976 : */
7977 :
7978 8053280 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
7979 : {
7980 8053280 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
7981 8053280 : if (eType >= 1000 && eType < 2000) // ISO Z.
7982 2776760 : return static_cast<OGRwkbGeometryType>(eType - 1000);
7983 5276510 : if (eType >= 2000 && eType < 3000) // ISO M.
7984 6059 : return static_cast<OGRwkbGeometryType>(eType - 2000);
7985 5270450 : if (eType >= 3000 && eType < 4000) // ISO ZM.
7986 136249 : return static_cast<OGRwkbGeometryType>(eType - 3000);
7987 5134200 : return eType;
7988 : }
7989 :
7990 : /************************************************************************/
7991 : /* OGR_GT_HasZ() */
7992 : /************************************************************************/
7993 : /**
7994 : * \brief Return if the geometry type is a 3D geometry type.
7995 : *
7996 : * @param eType Input geometry type
7997 : *
7998 : * @return TRUE if the geometry type is a 3D geometry type.
7999 : *
8000 : */
8001 :
8002 2118020 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
8003 : {
8004 2118020 : if (eType & wkb25DBitInternalUse)
8005 157100 : return TRUE;
8006 1960920 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
8007 264 : return TRUE;
8008 1960660 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8009 121539 : return TRUE;
8010 1839120 : return FALSE;
8011 : }
8012 :
8013 : /************************************************************************/
8014 : /* OGR_GT_HasM() */
8015 : /************************************************************************/
8016 : /**
8017 : * \brief Return if the geometry type is a measured type.
8018 : *
8019 : * @param eType Input geometry type
8020 : *
8021 : * @return TRUE if the geometry type is a measured type.
8022 : *
8023 : */
8024 :
8025 2177030 : int OGR_GT_HasM(OGRwkbGeometryType eType)
8026 : {
8027 2177030 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
8028 2593 : return TRUE;
8029 2174440 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
8030 121195 : return TRUE;
8031 2053240 : return FALSE;
8032 : }
8033 :
8034 : /************************************************************************/
8035 : /* OGR_GT_SetZ() */
8036 : /************************************************************************/
8037 : /**
8038 : * \brief Returns the 3D geometry type corresponding to the passed geometry
8039 : * type.
8040 : *
8041 : * @param eType Input geometry type
8042 : *
8043 : * @return 3D geometry type corresponding to the passed geometry type.
8044 : *
8045 : */
8046 :
8047 5748 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
8048 : {
8049 5748 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
8050 498 : return eType;
8051 5250 : if (eType <= wkbGeometryCollection)
8052 5148 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
8053 : else
8054 102 : return static_cast<OGRwkbGeometryType>(eType + 1000);
8055 : }
8056 :
8057 : /************************************************************************/
8058 : /* OGR_GT_SetM() */
8059 : /************************************************************************/
8060 : /**
8061 : * \brief Returns the measured geometry type corresponding to the passed
8062 : * geometry type.
8063 : *
8064 : * @param eType Input geometry type
8065 : *
8066 : * @return measured geometry type corresponding to the passed geometry type.
8067 : *
8068 : */
8069 :
8070 2013 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
8071 : {
8072 2013 : if (OGR_GT_HasM(eType) || eType == wkbNone)
8073 262 : return eType;
8074 1751 : if (eType & wkb25DBitInternalUse)
8075 : {
8076 717 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
8077 717 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
8078 : }
8079 1751 : return static_cast<OGRwkbGeometryType>(eType + 2000);
8080 : }
8081 :
8082 : /************************************************************************/
8083 : /* OGR_GT_SetModifier() */
8084 : /************************************************************************/
8085 : /**
8086 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
8087 : *
8088 : * @param eType Input geometry type
8089 : * @param bHasZ TRUE if the output geometry type must be 3D.
8090 : * @param bHasM TRUE if the output geometry type must be measured.
8091 : *
8092 : * @return Output geometry type.
8093 : *
8094 : */
8095 :
8096 5561 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
8097 : int bHasM)
8098 : {
8099 5561 : if (bHasZ && bHasM)
8100 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
8101 5219 : else if (bHasM)
8102 333 : return OGR_GT_SetM(wkbFlatten(eType));
8103 4886 : else if (bHasZ)
8104 2110 : return OGR_GT_SetZ(wkbFlatten(eType));
8105 : else
8106 2776 : return wkbFlatten(eType);
8107 : }
8108 :
8109 : /************************************************************************/
8110 : /* OGR_GT_IsSubClassOf) */
8111 : /************************************************************************/
8112 : /**
8113 : * \brief Returns if a type is a subclass of another one
8114 : *
8115 : * @param eType Type.
8116 : * @param eSuperType Super type
8117 : *
8118 : * @return TRUE if eType is a subclass of eSuperType.
8119 : *
8120 : */
8121 :
8122 152600 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
8123 : {
8124 152600 : eSuperType = wkbFlatten(eSuperType);
8125 152600 : eType = wkbFlatten(eType);
8126 :
8127 152600 : if (eSuperType == eType || eSuperType == wkbUnknown)
8128 19437 : return TRUE;
8129 :
8130 133163 : if (eSuperType == wkbGeometryCollection)
8131 52340 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
8132 106442 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
8133 54102 : eType == wkbMultiSurface;
8134 :
8135 79061 : if (eSuperType == wkbCurvePolygon)
8136 21915 : return eType == wkbPolygon || eType == wkbTriangle;
8137 :
8138 57146 : if (eSuperType == wkbMultiCurve)
8139 249 : return eType == wkbMultiLineString;
8140 :
8141 56897 : if (eSuperType == wkbMultiSurface)
8142 288 : return eType == wkbMultiPolygon;
8143 :
8144 56609 : if (eSuperType == wkbCurve)
8145 23335 : return eType == wkbLineString || eType == wkbCircularString ||
8146 23335 : eType == wkbCompoundCurve;
8147 :
8148 33274 : if (eSuperType == wkbSurface)
8149 3519 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
8150 7186 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
8151 3667 : eType == wkbTIN;
8152 :
8153 29607 : if (eSuperType == wkbPolygon)
8154 221 : return eType == wkbTriangle;
8155 :
8156 29386 : if (eSuperType == wkbPolyhedralSurface)
8157 14640 : return eType == wkbTIN;
8158 :
8159 14746 : return FALSE;
8160 : }
8161 :
8162 : /************************************************************************/
8163 : /* OGR_GT_GetCollection() */
8164 : /************************************************************************/
8165 : /**
8166 : * \brief Returns the collection type that can contain the passed geometry type
8167 : *
8168 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
8169 : * wkbLineString->wkbMultiLineString,
8170 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
8171 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
8172 : * wkbCurvePolygon->wkbMultiSurface.
8173 : * In other cases, wkbUnknown is returned
8174 : *
8175 : * Passed Z, M, ZM flag is preserved.
8176 : *
8177 : *
8178 : * @param eType Input geometry type
8179 : *
8180 : * @return the collection type that can contain the passed geometry type or
8181 : * wkbUnknown
8182 : *
8183 : */
8184 :
8185 2707 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
8186 : {
8187 2707 : const bool bHasZ = wkbHasZ(eType);
8188 2707 : const bool bHasM = wkbHasM(eType);
8189 2707 : if (eType == wkbNone)
8190 1 : return wkbNone;
8191 2706 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8192 2706 : if (eFGType == wkbPoint)
8193 67 : eType = wkbMultiPoint;
8194 :
8195 2639 : else if (eFGType == wkbLineString)
8196 187 : eType = wkbMultiLineString;
8197 :
8198 2452 : else if (eFGType == wkbPolygon)
8199 1002 : eType = wkbMultiPolygon;
8200 :
8201 1450 : else if (eFGType == wkbTriangle)
8202 7 : eType = wkbTIN;
8203 :
8204 1443 : else if (OGR_GT_IsCurve(eFGType))
8205 189 : eType = wkbMultiCurve;
8206 :
8207 1254 : else if (OGR_GT_IsSurface(eFGType))
8208 952 : eType = wkbMultiSurface;
8209 :
8210 : else
8211 302 : return wkbUnknown;
8212 :
8213 2404 : if (bHasZ)
8214 3 : eType = wkbSetZ(eType);
8215 2404 : if (bHasM)
8216 3 : eType = wkbSetM(eType);
8217 :
8218 2404 : return eType;
8219 : }
8220 :
8221 : /************************************************************************/
8222 : /* OGR_GT_GetSingle() */
8223 : /************************************************************************/
8224 : /**
8225 : * \brief Returns the non-collection type that be contained in the passed
8226 : * geometry type.
8227 : *
8228 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
8229 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
8230 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
8231 : * wkbGeometryCollection -> wkbUnknown
8232 : * In other cases, the original geometry is returned.
8233 : *
8234 : * Passed Z, M, ZM flag is preserved.
8235 : *
8236 : *
8237 : * @param eType Input geometry type
8238 : *
8239 : * @return the the non-collection type that be contained in the passed geometry
8240 : * type or wkbUnknown
8241 : *
8242 : * @since GDAL 3.11
8243 : */
8244 :
8245 43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
8246 : {
8247 43 : const bool bHasZ = wkbHasZ(eType);
8248 43 : const bool bHasM = wkbHasM(eType);
8249 43 : if (eType == wkbNone)
8250 1 : return wkbNone;
8251 42 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
8252 42 : if (eFGType == wkbMultiPoint)
8253 8 : eType = wkbPoint;
8254 :
8255 34 : else if (eFGType == wkbMultiLineString)
8256 4 : eType = wkbLineString;
8257 :
8258 30 : else if (eFGType == wkbMultiPolygon)
8259 2 : eType = wkbPolygon;
8260 :
8261 28 : else if (eFGType == wkbMultiCurve)
8262 2 : eType = wkbCompoundCurve;
8263 :
8264 26 : else if (eFGType == wkbMultiSurface)
8265 2 : eType = wkbCurvePolygon;
8266 :
8267 24 : else if (eFGType == wkbGeometryCollection)
8268 1 : return wkbUnknown;
8269 :
8270 41 : if (bHasZ)
8271 3 : eType = wkbSetZ(eType);
8272 41 : if (bHasM)
8273 2 : eType = wkbSetM(eType);
8274 :
8275 41 : return eType;
8276 : }
8277 :
8278 : /************************************************************************/
8279 : /* OGR_GT_GetCurve() */
8280 : /************************************************************************/
8281 : /**
8282 : * \brief Returns the curve geometry type that can contain the passed geometry
8283 : * type
8284 : *
8285 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8286 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8287 : * and wkbMultiLineString->wkbMultiCurve.
8288 : * In other cases, the passed geometry is returned.
8289 : *
8290 : * Passed Z, M, ZM flag is preserved.
8291 : *
8292 : * @param eType Input geometry type
8293 : *
8294 : * @return the curve type that can contain the passed geometry type
8295 : *
8296 : */
8297 :
8298 35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8299 : {
8300 35 : const bool bHasZ = wkbHasZ(eType);
8301 35 : const bool bHasM = wkbHasM(eType);
8302 35 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8303 :
8304 35 : if (eFGType == wkbLineString)
8305 3 : eType = wkbCompoundCurve;
8306 :
8307 32 : else if (eFGType == wkbPolygon)
8308 1 : eType = wkbCurvePolygon;
8309 :
8310 31 : else if (eFGType == wkbTriangle)
8311 0 : eType = wkbCurvePolygon;
8312 :
8313 31 : else if (eFGType == wkbMultiLineString)
8314 6 : eType = wkbMultiCurve;
8315 :
8316 25 : else if (eFGType == wkbMultiPolygon)
8317 4 : eType = wkbMultiSurface;
8318 :
8319 35 : if (bHasZ)
8320 4 : eType = wkbSetZ(eType);
8321 35 : if (bHasM)
8322 4 : eType = wkbSetM(eType);
8323 :
8324 35 : return eType;
8325 : }
8326 :
8327 : /************************************************************************/
8328 : /* OGR_GT_GetLinear() */
8329 : /************************************************************************/
8330 : /**
8331 : * \brief Returns the non-curve geometry type that can contain the passed
8332 : * geometry type
8333 : *
8334 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8335 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8336 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8337 : * In other cases, the passed geometry is returned.
8338 : *
8339 : * Passed Z, M, ZM flag is preserved.
8340 : *
8341 : * @param eType Input geometry type
8342 : *
8343 : * @return the non-curve type that can contain the passed geometry type
8344 : *
8345 : */
8346 :
8347 783 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8348 : {
8349 783 : const bool bHasZ = wkbHasZ(eType);
8350 783 : const bool bHasM = wkbHasM(eType);
8351 783 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8352 :
8353 783 : if (OGR_GT_IsCurve(eFGType))
8354 56 : eType = wkbLineString;
8355 :
8356 727 : else if (OGR_GT_IsSurface(eFGType))
8357 43 : eType = wkbPolygon;
8358 :
8359 684 : else if (eFGType == wkbMultiCurve)
8360 185 : eType = wkbMultiLineString;
8361 :
8362 499 : else if (eFGType == wkbMultiSurface)
8363 165 : eType = wkbMultiPolygon;
8364 :
8365 783 : if (bHasZ)
8366 154 : eType = wkbSetZ(eType);
8367 783 : if (bHasM)
8368 101 : eType = wkbSetM(eType);
8369 :
8370 783 : return eType;
8371 : }
8372 :
8373 : /************************************************************************/
8374 : /* OGR_GT_IsCurve() */
8375 : /************************************************************************/
8376 :
8377 : /**
8378 : * \brief Return if a geometry type is an instance of Curve
8379 : *
8380 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8381 : * and their Z/M/ZM variant.
8382 : *
8383 : * @param eGeomType the geometry type
8384 : * @return TRUE if the geometry type is an instance of Curve
8385 : *
8386 : */
8387 :
8388 23326 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8389 : {
8390 23326 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8391 : }
8392 :
8393 : /************************************************************************/
8394 : /* OGR_GT_IsSurface() */
8395 : /************************************************************************/
8396 :
8397 : /**
8398 : * \brief Return if a geometry type is an instance of Surface
8399 : *
8400 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8401 : * and their Z/M/ZM variant.
8402 : *
8403 : * @param eGeomType the geometry type
8404 : * @return TRUE if the geometry type is an instance of Surface
8405 : *
8406 : */
8407 :
8408 3661 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8409 : {
8410 3661 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8411 : }
8412 :
8413 : /************************************************************************/
8414 : /* OGR_GT_IsNonLinear() */
8415 : /************************************************************************/
8416 :
8417 : /**
8418 : * \brief Return if a geometry type is a non-linear geometry type.
8419 : *
8420 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8421 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8422 : * Z/M variants.
8423 : *
8424 : * @param eGeomType the geometry type
8425 : * @return TRUE if the geometry type is a non-linear geometry type.
8426 : *
8427 : */
8428 :
8429 114674 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8430 : {
8431 114674 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8432 114666 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8433 114593 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8434 229340 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8435 114674 : eFGeomType == wkbMultiSurface;
8436 : }
8437 :
8438 : /************************************************************************/
8439 : /* CastToError() */
8440 : /************************************************************************/
8441 :
8442 : //! @cond Doxygen_Suppress
8443 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8444 : {
8445 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8446 0 : poGeom->getGeometryName());
8447 0 : delete poGeom;
8448 0 : return nullptr;
8449 : }
8450 :
8451 : //! @endcond
8452 :
8453 : /************************************************************************/
8454 : /* OGRexportToSFCGAL() */
8455 : /************************************************************************/
8456 :
8457 : //! @cond Doxygen_Suppress
8458 : sfcgal_geometry_t *
8459 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8460 : {
8461 : #ifdef HAVE_SFCGAL
8462 :
8463 : sfcgal_init();
8464 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8465 :
8466 : const auto exportToSFCGALViaWKB =
8467 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8468 : {
8469 : if (!geom)
8470 : return nullptr;
8471 :
8472 : // Get WKB size and allocate buffer
8473 : size_t nSize = geom->WkbSize();
8474 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8475 :
8476 : // Set export options with NDR byte order
8477 : OGRwkbExportOptions oOptions;
8478 : oOptions.eByteOrder = wkbNDR;
8479 : // and ISO to avoid wkb25DBit for Z geometries
8480 : oOptions.eWkbVariant = wkbVariantIso;
8481 :
8482 : // Export to WKB
8483 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8484 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8485 : {
8486 : sfcgalGeom = sfcgal_io_read_wkb(
8487 : reinterpret_cast<const char *>(pabyWkb), nSize);
8488 : }
8489 :
8490 : CPLFree(pabyWkb);
8491 : return sfcgalGeom;
8492 : };
8493 :
8494 : // Handle special cases
8495 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8496 : {
8497 : std::unique_ptr<OGRLineString> poLS(
8498 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8499 : return exportToSFCGALViaWKB(poLS.get());
8500 : }
8501 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8502 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8503 : {
8504 : std::unique_ptr<OGRLineString> poLS(
8505 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8506 : ->toLineString());
8507 : return exportToSFCGALViaWKB(poLS.get());
8508 : }
8509 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8510 : {
8511 : std::unique_ptr<OGRPolygon> poPolygon(
8512 : OGRGeometryFactory::forceToPolygon(
8513 : poGeom->clone()->toCurvePolygon())
8514 : ->toPolygon());
8515 : return exportToSFCGALViaWKB(poPolygon.get());
8516 : }
8517 : else
8518 : {
8519 : // Default case - direct export
8520 : return exportToSFCGALViaWKB(poGeom);
8521 : }
8522 : #else
8523 : char *buffer = nullptr;
8524 :
8525 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8526 : // Polygon
8527 :
8528 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8529 : {
8530 : // cast it to LineString and get the WKT
8531 : std::unique_ptr<OGRLineString> poLS(
8532 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8533 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8534 : {
8535 : sfcgal_geometry_t *_geometry =
8536 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8537 : CPLFree(buffer);
8538 : return _geometry;
8539 : }
8540 : else
8541 : {
8542 : CPLFree(buffer);
8543 : return nullptr;
8544 : }
8545 : }
8546 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8547 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8548 : {
8549 : // convert it to LineString and get the WKT
8550 : std::unique_ptr<OGRLineString> poLS(
8551 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8552 : ->toLineString());
8553 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8554 : {
8555 : sfcgal_geometry_t *_geometry =
8556 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8557 : CPLFree(buffer);
8558 : return _geometry;
8559 : }
8560 : else
8561 : {
8562 : CPLFree(buffer);
8563 : return nullptr;
8564 : }
8565 : }
8566 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8567 : {
8568 : // convert it to Polygon and get the WKT
8569 : std::unique_ptr<OGRPolygon> poPolygon(
8570 : OGRGeometryFactory::forceToPolygon(
8571 : poGeom->clone()->toCurvePolygon())
8572 : ->toPolygon());
8573 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8574 : {
8575 : sfcgal_geometry_t *_geometry =
8576 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8577 : CPLFree(buffer);
8578 : return _geometry;
8579 : }
8580 : else
8581 : {
8582 : CPLFree(buffer);
8583 : return nullptr;
8584 : }
8585 : }
8586 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8587 : {
8588 : sfcgal_geometry_t *_geometry =
8589 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8590 : CPLFree(buffer);
8591 : return _geometry;
8592 : }
8593 : else
8594 : {
8595 : CPLFree(buffer);
8596 : return nullptr;
8597 : }
8598 : #endif
8599 : #else
8600 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8601 0 : return nullptr;
8602 : #endif
8603 : }
8604 :
8605 : //! @endcond
8606 :
8607 : /************************************************************************/
8608 : /* SFCGALexportToOGR() */
8609 : /************************************************************************/
8610 :
8611 : //! @cond Doxygen_Suppress
8612 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8613 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8614 : {
8615 : #ifdef HAVE_SFCGAL
8616 : if (geometry == nullptr)
8617 : return nullptr;
8618 :
8619 : sfcgal_init();
8620 : char *pabySFCGAL = nullptr;
8621 : size_t nLength = 0;
8622 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8623 :
8624 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8625 :
8626 : if (pabySFCGAL == nullptr || nLength == 0)
8627 : return nullptr;
8628 :
8629 : OGRGeometry *poGeom = nullptr;
8630 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8631 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8632 : nLength);
8633 :
8634 : free(pabySFCGAL);
8635 :
8636 : if (eErr == OGRERR_NONE)
8637 : {
8638 : return poGeom;
8639 : }
8640 : else
8641 : {
8642 : return nullptr;
8643 : }
8644 : #else
8645 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8646 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8647 : memcpy(pszWKT, pabySFCGAL, nLength);
8648 : pszWKT[nLength] = 0;
8649 : free(pabySFCGAL);
8650 :
8651 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8652 :
8653 : OGRGeometry *poGeom = nullptr;
8654 : if (geom_type == SFCGAL_TYPE_POINT)
8655 : {
8656 : poGeom = new OGRPoint();
8657 : }
8658 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8659 : {
8660 : poGeom = new OGRLineString();
8661 : }
8662 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8663 : {
8664 : poGeom = new OGRPolygon();
8665 : }
8666 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8667 : {
8668 : poGeom = new OGRMultiPoint();
8669 : }
8670 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8671 : {
8672 : poGeom = new OGRMultiLineString();
8673 : }
8674 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8675 : {
8676 : poGeom = new OGRMultiPolygon();
8677 : }
8678 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8679 : {
8680 : poGeom = new OGRGeometryCollection();
8681 : }
8682 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8683 : {
8684 : poGeom = new OGRTriangle();
8685 : }
8686 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8687 : {
8688 : poGeom = new OGRPolyhedralSurface();
8689 : }
8690 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8691 : {
8692 : poGeom = new OGRTriangulatedSurface();
8693 : }
8694 : else
8695 : {
8696 : CPLFree(pszWKT);
8697 : return nullptr;
8698 : }
8699 :
8700 : const char *pszWKTTmp = pszWKT;
8701 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8702 : {
8703 : CPLFree(pszWKT);
8704 : return poGeom;
8705 : }
8706 : else
8707 : {
8708 : delete poGeom;
8709 : CPLFree(pszWKT);
8710 : return nullptr;
8711 : }
8712 : #endif
8713 : #else
8714 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8715 0 : return nullptr;
8716 : #endif
8717 : }
8718 :
8719 : //! @endcond
8720 :
8721 : //! @cond Doxygen_Suppress
8722 18646 : OGRBoolean OGRGeometry::IsSFCGALCompatible() const
8723 : {
8724 18646 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8725 18646 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8726 : eGType == wkbTIN)
8727 : {
8728 2 : return TRUE;
8729 : }
8730 18644 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8731 : {
8732 13 : const OGRGeometryCollection *poGC = toGeometryCollection();
8733 13 : bool bIsSFCGALCompatible = false;
8734 13 : for (auto &&poSubGeom : *poGC)
8735 : {
8736 : OGRwkbGeometryType eSubGeomType =
8737 13 : wkbFlatten(poSubGeom->getGeometryType());
8738 13 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8739 : {
8740 0 : bIsSFCGALCompatible = true;
8741 : }
8742 13 : else if (eSubGeomType != wkbMultiPolygon)
8743 : {
8744 13 : bIsSFCGALCompatible = false;
8745 13 : break;
8746 : }
8747 : }
8748 13 : return bIsSFCGALCompatible;
8749 : }
8750 18631 : return FALSE;
8751 : }
8752 :
8753 : //! @endcond
8754 :
8755 : /************************************************************************/
8756 : /* roundCoordinatesIEEE754() */
8757 : /************************************************************************/
8758 :
8759 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8760 : * double-precision binary representation.
8761 : *
8762 : * Determines the number of bits (N) required to represent a coordinate value
8763 : * with a specified number of digits after the decimal point, and then sets all
8764 : * but the N most significant bits to zero. The resulting coordinate value will
8765 : * still round to the original value (e.g. after roundCoordinates()), but will
8766 : * have improved compressiblity.
8767 : *
8768 : * @param options Contains the precision requirements.
8769 : * @since GDAL 3.9
8770 : */
8771 1 : void OGRGeometry::roundCoordinatesIEEE754(
8772 : const OGRGeomCoordinateBinaryPrecision &options)
8773 : {
8774 : struct Quantizer : public OGRDefaultGeometryVisitor
8775 : {
8776 : const OGRGeomCoordinateBinaryPrecision &m_options;
8777 :
8778 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8779 1 : : m_options(optionsIn)
8780 : {
8781 1 : }
8782 :
8783 : using OGRDefaultGeometryVisitor::visit;
8784 :
8785 3 : void visit(OGRPoint *poPoint) override
8786 : {
8787 3 : if (m_options.nXYBitPrecision != INT_MIN)
8788 : {
8789 : uint64_t i;
8790 : double d;
8791 3 : d = poPoint->getX();
8792 3 : memcpy(&i, &d, sizeof(i));
8793 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8794 3 : memcpy(&d, &i, sizeof(i));
8795 3 : poPoint->setX(d);
8796 3 : d = poPoint->getY();
8797 3 : memcpy(&i, &d, sizeof(i));
8798 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8799 3 : memcpy(&d, &i, sizeof(i));
8800 3 : poPoint->setY(d);
8801 : }
8802 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8803 : {
8804 : uint64_t i;
8805 : double d;
8806 3 : d = poPoint->getZ();
8807 3 : memcpy(&i, &d, sizeof(i));
8808 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8809 3 : memcpy(&d, &i, sizeof(i));
8810 3 : poPoint->setZ(d);
8811 : }
8812 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8813 : {
8814 : uint64_t i;
8815 : double d;
8816 3 : d = poPoint->getM();
8817 3 : memcpy(&i, &d, sizeof(i));
8818 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8819 3 : memcpy(&d, &i, sizeof(i));
8820 3 : poPoint->setM(d);
8821 : }
8822 3 : }
8823 : };
8824 :
8825 2 : Quantizer quantizer(options);
8826 1 : accept(&quantizer);
8827 1 : }
8828 :
8829 : /************************************************************************/
8830 : /* visit() */
8831 : /************************************************************************/
8832 :
8833 107 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8834 : {
8835 1257 : for (auto &&oPoint : *poGeom)
8836 : {
8837 1150 : oPoint.accept(this);
8838 : }
8839 107 : }
8840 :
8841 106 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8842 : {
8843 106 : _visit(poGeom);
8844 106 : }
8845 :
8846 82 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8847 : {
8848 82 : visit(poGeom->toUpperClass());
8849 82 : }
8850 :
8851 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8852 : {
8853 1 : _visit(poGeom);
8854 1 : }
8855 :
8856 80 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8857 : {
8858 163 : for (auto &&poSubGeom : *poGeom)
8859 83 : poSubGeom->accept(this);
8860 80 : }
8861 :
8862 79 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8863 : {
8864 79 : visit(poGeom->toUpperClass());
8865 79 : }
8866 :
8867 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8868 : {
8869 1 : visit(poGeom->toUpperClass());
8870 1 : }
8871 :
8872 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8873 : {
8874 8 : visit(poGeom->toUpperClass());
8875 8 : }
8876 :
8877 15 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8878 : {
8879 15 : visit(poGeom->toUpperClass());
8880 15 : }
8881 :
8882 27 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
8883 : {
8884 78 : for (auto &&poSubGeom : *poGeom)
8885 51 : poSubGeom->accept(this);
8886 27 : }
8887 :
8888 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
8889 : {
8890 2 : for (auto &&poSubGeom : *poGeom)
8891 1 : poSubGeom->accept(this);
8892 1 : }
8893 :
8894 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
8895 : {
8896 1 : visit(poGeom->toUpperClass());
8897 1 : }
8898 :
8899 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
8900 : {
8901 1 : visit(poGeom->toUpperClass());
8902 1 : }
8903 :
8904 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
8905 : {
8906 2 : visit(poGeom->toUpperClass());
8907 2 : }
8908 :
8909 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
8910 : {
8911 4 : for (auto &&poSubGeom : *poGeom)
8912 2 : poSubGeom->accept(this);
8913 2 : }
8914 :
8915 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
8916 : {
8917 1 : visit(poGeom->toUpperClass());
8918 1 : }
8919 :
8920 127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
8921 : {
8922 2988 : for (auto &&oPoint : *poGeom)
8923 : {
8924 2861 : oPoint.accept(this);
8925 : }
8926 127 : }
8927 :
8928 121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
8929 : {
8930 121 : _visit(poGeom);
8931 121 : }
8932 :
8933 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
8934 : {
8935 110 : visit(poGeom->toUpperClass());
8936 110 : }
8937 :
8938 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
8939 : {
8940 6 : _visit(poGeom);
8941 6 : }
8942 :
8943 112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
8944 : {
8945 225 : for (auto &&poSubGeom : *poGeom)
8946 113 : poSubGeom->accept(this);
8947 112 : }
8948 :
8949 109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
8950 : {
8951 109 : visit(poGeom->toUpperClass());
8952 109 : }
8953 :
8954 64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
8955 : {
8956 64 : visit(poGeom->toUpperClass());
8957 64 : }
8958 :
8959 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
8960 : {
8961 1 : visit(poGeom->toUpperClass());
8962 1 : }
8963 :
8964 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
8965 : {
8966 1 : visit(poGeom->toUpperClass());
8967 1 : }
8968 :
8969 69 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
8970 : {
8971 325 : for (auto &&poSubGeom : *poGeom)
8972 256 : poSubGeom->accept(this);
8973 69 : }
8974 :
8975 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
8976 : {
8977 14 : for (auto &&poSubGeom : *poGeom)
8978 11 : poSubGeom->accept(this);
8979 3 : }
8980 :
8981 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
8982 : {
8983 1 : visit(poGeom->toUpperClass());
8984 1 : }
8985 :
8986 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
8987 : {
8988 1 : visit(poGeom->toUpperClass());
8989 1 : }
8990 :
8991 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
8992 : {
8993 2 : visit(poGeom->toUpperClass());
8994 2 : }
8995 :
8996 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
8997 : {
8998 4 : for (auto &&poSubGeom : *poGeom)
8999 2 : poSubGeom->accept(this);
9000 2 : }
9001 :
9002 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
9003 : {
9004 1 : visit(poGeom->toUpperClass());
9005 1 : }
9006 :
9007 : /************************************************************************/
9008 : /* OGRGeometryUniquePtrDeleter */
9009 : /************************************************************************/
9010 :
9011 : //! @cond Doxygen_Suppress
9012 1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
9013 : {
9014 1333 : delete poGeom;
9015 1333 : }
9016 :
9017 : //! @endcond
9018 :
9019 : /************************************************************************/
9020 : /* OGRPreparedGeometryUniquePtrDeleter */
9021 : /************************************************************************/
9022 :
9023 : //! @cond Doxygen_Suppress
9024 145 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
9025 : OGRPreparedGeometry *poPreparedGeom) const
9026 : {
9027 145 : OGRDestroyPreparedGeometry(poPreparedGeom);
9028 145 : }
9029 :
9030 : //! @endcond
9031 :
9032 : /************************************************************************/
9033 : /* HomogenizeDimensionalityWith() */
9034 : /************************************************************************/
9035 :
9036 : //! @cond Doxygen_Suppress
9037 3358650 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
9038 : {
9039 3358650 : if (poOtherGeom->Is3D() && !Is3D())
9040 1328850 : set3D(TRUE);
9041 :
9042 3358650 : if (poOtherGeom->IsMeasured() && !IsMeasured())
9043 851 : setMeasured(TRUE);
9044 :
9045 3358650 : if (!poOtherGeom->Is3D() && Is3D())
9046 298 : poOtherGeom->set3D(TRUE);
9047 :
9048 3358650 : if (!poOtherGeom->IsMeasured() && IsMeasured())
9049 41 : poOtherGeom->setMeasured(TRUE);
9050 3358650 : }
9051 :
9052 : //! @endcond
9053 :
9054 : /************************************************************************/
9055 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
9056 : /************************************************************************/
9057 :
9058 : /** Set binary precision options from resolution.
9059 : *
9060 : * @since GDAL 3.9
9061 : */
9062 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
9063 : const OGRGeomCoordinatePrecision &prec)
9064 : {
9065 16 : if (prec.dfXYResolution != 0)
9066 : {
9067 16 : nXYBitPrecision =
9068 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
9069 : }
9070 16 : if (prec.dfZResolution != 0)
9071 : {
9072 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
9073 : }
9074 16 : if (prec.dfMResolution != 0)
9075 : {
9076 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
9077 : }
9078 16 : }
9079 :
9080 : /************************************************************************/
9081 : /* OGRwkbExportOptionsCreate() */
9082 : /************************************************************************/
9083 :
9084 : /**
9085 : * \brief Create geometry WKB export options.
9086 : *
9087 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
9088 : *
9089 : * @return object to be freed with OGRwkbExportOptionsDestroy().
9090 : * @since GDAL 3.9
9091 : */
9092 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
9093 : {
9094 2 : return new OGRwkbExportOptions;
9095 : }
9096 :
9097 : /************************************************************************/
9098 : /* OGRwkbExportOptionsDestroy() */
9099 : /************************************************************************/
9100 :
9101 : /**
9102 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
9103 : *
9104 : * @param psOptions WKB export options
9105 : * @since GDAL 3.9
9106 : */
9107 :
9108 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
9109 : {
9110 2 : delete psOptions;
9111 2 : }
9112 :
9113 : /************************************************************************/
9114 : /* OGRwkbExportOptionsSetByteOrder() */
9115 : /************************************************************************/
9116 :
9117 : /**
9118 : * \brief Set the WKB byte order.
9119 : *
9120 : * @param psOptions WKB export options
9121 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
9122 : * Intel)
9123 : * @since GDAL 3.9
9124 : */
9125 :
9126 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
9127 : OGRwkbByteOrder eByteOrder)
9128 : {
9129 1 : psOptions->eByteOrder = eByteOrder;
9130 1 : }
9131 :
9132 : /************************************************************************/
9133 : /* OGRwkbExportOptionsSetVariant() */
9134 : /************************************************************************/
9135 :
9136 : /**
9137 : * \brief Set the WKB variant
9138 : *
9139 : * @param psOptions WKB export options
9140 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
9141 : * wkbVariantPostGIS1
9142 : * @since GDAL 3.9
9143 : */
9144 :
9145 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
9146 : OGRwkbVariant eWkbVariant)
9147 : {
9148 1 : psOptions->eWkbVariant = eWkbVariant;
9149 1 : }
9150 :
9151 : /************************************************************************/
9152 : /* OGRwkbExportOptionsSetPrecision() */
9153 : /************************************************************************/
9154 :
9155 : /**
9156 : * \brief Set precision options
9157 : *
9158 : * @param psOptions WKB export options
9159 : * @param hPrecisionOptions Precision options (might be null to reset them)
9160 : * @since GDAL 3.9
9161 : */
9162 :
9163 1 : void OGRwkbExportOptionsSetPrecision(
9164 : OGRwkbExportOptions *psOptions,
9165 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
9166 : {
9167 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
9168 1 : if (hPrecisionOptions)
9169 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
9170 1 : }
9171 :
9172 : /************************************************************************/
9173 : /* IsRectangle() */
9174 : /************************************************************************/
9175 :
9176 : /**
9177 : * \brief Returns whether the geometry is a polygon with 4 corners forming
9178 : * a rectangle.
9179 : *
9180 : * @since GDAL 3.10
9181 : */
9182 52571 : bool OGRGeometry::IsRectangle() const
9183 : {
9184 52571 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
9185 353 : return false;
9186 :
9187 52218 : const OGRPolygon *poPoly = toPolygon();
9188 :
9189 52218 : if (poPoly->getNumInteriorRings() != 0)
9190 27 : return false;
9191 :
9192 52191 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
9193 52191 : if (!poRing)
9194 4 : return false;
9195 :
9196 52187 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
9197 206 : return false;
9198 :
9199 : // If the ring has 5 points, the last should be the first.
9200 103905 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
9201 51924 : poRing->getY(0) != poRing->getY(4)))
9202 1 : return false;
9203 :
9204 : // Polygon with first segment in "y" direction.
9205 103294 : if (poRing->getX(0) == poRing->getX(1) &&
9206 102627 : poRing->getY(1) == poRing->getY(2) &&
9207 154607 : poRing->getX(2) == poRing->getX(3) &&
9208 51313 : poRing->getY(3) == poRing->getY(0))
9209 51313 : return true;
9210 :
9211 : // Polygon with first segment in "x" direction.
9212 1253 : if (poRing->getY(0) == poRing->getY(1) &&
9213 1172 : poRing->getX(1) == poRing->getX(2) &&
9214 1839 : poRing->getY(2) == poRing->getY(3) &&
9215 586 : poRing->getX(3) == poRing->getX(0))
9216 586 : return true;
9217 :
9218 81 : return false;
9219 : }
9220 :
9221 : /************************************************************************/
9222 : /* hasEmptyParts() */
9223 : /************************************************************************/
9224 :
9225 : /**
9226 : * \brief Returns whether a geometry has empty parts/rings.
9227 : *
9228 : * Returns true if removeEmptyParts() will modify the geometry.
9229 : *
9230 : * This is different from IsEmpty().
9231 : *
9232 : * @since GDAL 3.10
9233 : */
9234 103 : bool OGRGeometry::hasEmptyParts() const
9235 : {
9236 103 : return false;
9237 : }
9238 :
9239 : /************************************************************************/
9240 : /* removeEmptyParts() */
9241 : /************************************************************************/
9242 :
9243 : /**
9244 : * \brief Remove empty parts/rings from this geometry.
9245 : *
9246 : * @since GDAL 3.10
9247 : */
9248 17 : void OGRGeometry::removeEmptyParts()
9249 : {
9250 17 : }
9251 :
9252 : /************************************************************************/
9253 : /* ~IOGRGeometryVisitor() */
9254 : /************************************************************************/
9255 :
9256 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
9257 :
9258 : /************************************************************************/
9259 : /* ~IOGRConstGeometryVisitor() */
9260 : /************************************************************************/
9261 :
9262 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
|