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 <stdexcept>
26 : #include <string>
27 :
28 : #include "cpl_conv.h"
29 : #include "cpl_error.h"
30 : #include "cpl_multiproc.h"
31 : #include "cpl_string.h"
32 : #include "ogr_api.h"
33 : #include "ogr_core.h"
34 : #include "ogr_geos.h"
35 : #include "ogr_sfcgal.h"
36 : #include "ogr_libs.h"
37 : #include "ogr_p.h"
38 : #include "ogr_spatialref.h"
39 : #include "ogr_srs_api.h"
40 : #include "ogr_wkb.h"
41 :
42 : #define SFCGAL_MAKE_VERSION(major, minor, patch) \
43 : ((major)*10000 + (minor)*100 + (patch))
44 : #define SFCGAL_VERSION \
45 : SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR, \
46 : SFCGAL_VERSION_PATCH)
47 :
48 : #ifndef HAVE_GEOS
49 : #define UNUSED_IF_NO_GEOS CPL_UNUSED
50 : #else
51 : #define UNUSED_IF_NO_GEOS
52 : #endif
53 :
54 : //! @cond Doxygen_Suppress
55 : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
56 : //! @endcond
57 :
58 : #ifdef HAVE_GEOS
59 107 : static void OGRGEOSErrorHandler(const char *fmt, ...)
60 : {
61 : va_list args;
62 :
63 107 : va_start(args, fmt);
64 107 : CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
65 107 : va_end(args);
66 107 : }
67 :
68 102 : static void OGRGEOSWarningHandler(const char *fmt, ...)
69 : {
70 : va_list args;
71 :
72 102 : va_start(args, fmt);
73 102 : CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
74 102 : va_end(args);
75 102 : }
76 : #endif
77 :
78 : /************************************************************************/
79 : /* OGRWktOptions() */
80 : /************************************************************************/
81 :
82 11109 : int OGRWktOptions::getDefaultPrecision()
83 : {
84 11109 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
85 : }
86 :
87 11205 : bool OGRWktOptions::getDefaultRound()
88 : {
89 11205 : return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
90 : }
91 :
92 : /************************************************************************/
93 : /* OGRGeometry() */
94 : /************************************************************************/
95 :
96 : OGRGeometry::OGRGeometry() = default;
97 :
98 : /************************************************************************/
99 : /* OGRGeometry( const OGRGeometry& ) */
100 : /************************************************************************/
101 :
102 : /**
103 : * \brief Copy constructor.
104 : *
105 : * Note: before GDAL 2.1, only the default implementation of the constructor
106 : * existed, which could be unsafe to use.
107 : *
108 : * @since GDAL 2.1
109 : */
110 :
111 2193850 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
112 2193850 : : poSRS(other.poSRS), flags(other.flags)
113 : {
114 2193850 : if (poSRS != nullptr)
115 487283 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
116 2193850 : }
117 :
118 : /************************************************************************/
119 : /* OGRGeometry( OGRGeometry&& ) */
120 : /************************************************************************/
121 :
122 : /**
123 : * \brief Move constructor.
124 : *
125 : * @since GDAL 3.11
126 : */
127 :
128 112193 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
129 112193 : : poSRS(other.poSRS), flags(other.flags)
130 : {
131 112193 : other.poSRS = nullptr;
132 112193 : }
133 :
134 : /************************************************************************/
135 : /* ~OGRGeometry() */
136 : /************************************************************************/
137 :
138 26153000 : OGRGeometry::~OGRGeometry()
139 :
140 : {
141 13076500 : if (poSRS != nullptr)
142 4063200 : const_cast<OGRSpatialReference *>(poSRS)->Release();
143 13076500 : }
144 :
145 : /************************************************************************/
146 : /* operator=( const OGRGeometry&) */
147 : /************************************************************************/
148 :
149 : /**
150 : * \brief Assignment operator.
151 : *
152 : * Note: before GDAL 2.1, only the default implementation of the operator
153 : * existed, which could be unsafe to use.
154 : *
155 : * @since GDAL 2.1
156 : */
157 :
158 1217 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
159 : {
160 1217 : if (this != &other)
161 : {
162 1217 : empty();
163 1217 : assignSpatialReference(other.getSpatialReference());
164 1217 : flags = other.flags;
165 : }
166 1217 : return *this;
167 : }
168 :
169 : /************************************************************************/
170 : /* operator=( OGRGeometry&&) */
171 : /************************************************************************/
172 :
173 : /**
174 : * \brief Move assignment operator.
175 : *
176 : * @since GDAL 3.11
177 : */
178 :
179 85599 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
180 : {
181 85599 : if (this != &other)
182 : {
183 85599 : poSRS = other.poSRS;
184 85599 : other.poSRS = nullptr;
185 85599 : flags = other.flags;
186 : }
187 85599 : return *this;
188 : }
189 :
190 : /************************************************************************/
191 : /* dumpReadable() */
192 : /************************************************************************/
193 :
194 : /**
195 : * \brief Dump geometry in well known text format to indicated output file.
196 : *
197 : * A few options can be defined to change the default dump :
198 : * <ul>
199 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
200 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
201 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
202 : * </ul>
203 : *
204 : * This method is the same as the C function OGR_G_DumpReadable().
205 : *
206 : * @param fp the text file to write the geometry to.
207 : * @param pszPrefix the prefix to put on each line of output.
208 : * @param papszOptions NULL terminated list of options (may be NULL)
209 : */
210 :
211 0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
212 : CSLConstList papszOptions) const
213 :
214 : {
215 0 : if (fp == nullptr)
216 0 : fp = stdout;
217 :
218 0 : const auto osStr = dumpReadable(pszPrefix, papszOptions);
219 0 : fprintf(fp, "%s", osStr.c_str());
220 0 : }
221 :
222 : /************************************************************************/
223 : /* dumpReadable() */
224 : /************************************************************************/
225 :
226 : /**
227 : * \brief Dump geometry in well known text format to indicated output file.
228 : *
229 : * A few options can be defined to change the default dump :
230 : * <ul>
231 : * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
232 : * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
233 : * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
234 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
235 : * in WKT (added in GDAL 3.9)</li>
236 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
237 : * WKT (added in GDAL 3.9)</li>
238 : * </ul>
239 : *
240 : * @param pszPrefix the prefix to put on each line of output.
241 : * @param papszOptions NULL terminated list of options (may be NULL)
242 : * @return a string with the geometry representation.
243 : * @since GDAL 3.7
244 : */
245 :
246 303 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
247 : CSLConstList papszOptions) const
248 :
249 : {
250 303 : if (pszPrefix == nullptr)
251 303 : pszPrefix = "";
252 :
253 303 : std::string osRet;
254 :
255 : const auto exportToWktWithOpts =
256 2044 : [this, pszPrefix, papszOptions, &osRet](bool bIso)
257 : {
258 292 : OGRErr err(OGRERR_NONE);
259 292 : OGRWktOptions opts;
260 292 : if (const char *pszXYPrecision =
261 292 : CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
262 : {
263 1 : opts.format = OGRWktFormat::F;
264 1 : opts.xyPrecision = atoi(pszXYPrecision);
265 : }
266 292 : if (const char *pszZPrecision =
267 292 : CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
268 : {
269 1 : opts.format = OGRWktFormat::F;
270 1 : opts.zPrecision = atoi(pszZPrecision);
271 : }
272 292 : if (bIso)
273 292 : opts.variant = wkbVariantIso;
274 584 : std::string wkt = exportToWkt(opts, &err);
275 292 : if (err == OGRERR_NONE)
276 : {
277 292 : osRet = pszPrefix;
278 292 : osRet += wkt.data();
279 292 : osRet += '\n';
280 : }
281 292 : };
282 :
283 : const char *pszDisplayGeometry =
284 303 : CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
285 303 : if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
286 : {
287 11 : osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
288 11 : switch (getGeometryType())
289 : {
290 1 : case wkbUnknown:
291 : case wkbNone:
292 : case wkbPoint:
293 : case wkbPoint25D:
294 : case wkbPointM:
295 : case wkbPointZM:
296 1 : break;
297 0 : case wkbPolyhedralSurface:
298 : case wkbTIN:
299 : case wkbPolyhedralSurfaceZ:
300 : case wkbTINZ:
301 : case wkbPolyhedralSurfaceM:
302 : case wkbTINM:
303 : case wkbPolyhedralSurfaceZM:
304 : case wkbTINZM:
305 : {
306 0 : const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
307 : osRet +=
308 0 : CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
309 0 : for (auto &&poSubGeom : *poPS)
310 : {
311 0 : osRet += pszPrefix;
312 0 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
313 : }
314 0 : break;
315 : }
316 0 : case wkbLineString:
317 : case wkbLineString25D:
318 : case wkbLineStringM:
319 : case wkbLineStringZM:
320 : case wkbCircularString:
321 : case wkbCircularStringZ:
322 : case wkbCircularStringM:
323 : case wkbCircularStringZM:
324 : {
325 0 : const OGRSimpleCurve *poSC = toSimpleCurve();
326 0 : osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
327 0 : break;
328 : }
329 10 : case wkbPolygon:
330 : case wkbTriangle:
331 : case wkbTriangleZ:
332 : case wkbTriangleM:
333 : case wkbTriangleZM:
334 : case wkbPolygon25D:
335 : case wkbPolygonM:
336 : case wkbPolygonZM:
337 : case wkbCurvePolygon:
338 : case wkbCurvePolygonZ:
339 : case wkbCurvePolygonM:
340 : case wkbCurvePolygonZM:
341 : {
342 10 : const OGRCurvePolygon *poPoly = toCurvePolygon();
343 10 : const OGRCurve *poRing = poPoly->getExteriorRingCurve();
344 10 : const int nRings = poPoly->getNumInteriorRings();
345 10 : if (poRing == nullptr)
346 : {
347 0 : osRet += "empty";
348 : }
349 : else
350 : {
351 10 : osRet += CPLOPrintf("%d points", poRing->getNumPoints());
352 10 : if (wkbFlatten(poRing->getGeometryType()) ==
353 : wkbCompoundCurve)
354 : {
355 0 : osRet += " (";
356 0 : osRet += poRing->dumpReadable(nullptr, papszOptions);
357 0 : osRet += ")";
358 : }
359 10 : if (nRings)
360 : {
361 0 : osRet += CPLOPrintf(", %d inner rings (", nRings);
362 0 : for (int ir = 0; ir < nRings; ir++)
363 : {
364 0 : poRing = poPoly->getInteriorRingCurve(ir);
365 0 : if (ir)
366 0 : osRet += ", ";
367 : osRet +=
368 0 : CPLOPrintf("%d points", poRing->getNumPoints());
369 0 : if (wkbFlatten(poRing->getGeometryType()) ==
370 : wkbCompoundCurve)
371 : {
372 0 : osRet += " (";
373 : osRet +=
374 0 : poRing->dumpReadable(nullptr, papszOptions);
375 0 : osRet += ")";
376 : }
377 : }
378 0 : osRet += ")";
379 : }
380 : }
381 10 : osRet += "\n";
382 10 : break;
383 : }
384 0 : case wkbCompoundCurve:
385 : case wkbCompoundCurveZ:
386 : case wkbCompoundCurveM:
387 : case wkbCompoundCurveZM:
388 : {
389 0 : const OGRCompoundCurve *poCC = toCompoundCurve();
390 0 : if (poCC->getNumCurves() == 0)
391 : {
392 0 : osRet += "empty";
393 : }
394 : else
395 : {
396 0 : for (int i = 0; i < poCC->getNumCurves(); i++)
397 : {
398 0 : if (i)
399 0 : osRet += ", ";
400 : osRet +=
401 0 : CPLOPrintf("%s (%d points)",
402 0 : poCC->getCurve(i)->getGeometryName(),
403 0 : poCC->getCurve(i)->getNumPoints());
404 : }
405 : }
406 0 : break;
407 : }
408 :
409 0 : case wkbMultiPoint:
410 : case wkbMultiLineString:
411 : case wkbMultiPolygon:
412 : case wkbMultiCurve:
413 : case wkbMultiSurface:
414 : case wkbGeometryCollection:
415 : case wkbMultiPoint25D:
416 : case wkbMultiLineString25D:
417 : case wkbMultiPolygon25D:
418 : case wkbMultiCurveZ:
419 : case wkbMultiSurfaceZ:
420 : case wkbGeometryCollection25D:
421 : case wkbMultiPointM:
422 : case wkbMultiLineStringM:
423 : case wkbMultiPolygonM:
424 : case wkbMultiCurveM:
425 : case wkbMultiSurfaceM:
426 : case wkbGeometryCollectionM:
427 : case wkbMultiPointZM:
428 : case wkbMultiLineStringZM:
429 : case wkbMultiPolygonZM:
430 : case wkbMultiCurveZM:
431 : case wkbMultiSurfaceZM:
432 : case wkbGeometryCollectionZM:
433 : {
434 0 : const OGRGeometryCollection *poColl = toGeometryCollection();
435 : osRet +=
436 0 : CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
437 0 : for (auto &&poSubGeom : *poColl)
438 : {
439 0 : osRet += pszPrefix;
440 0 : osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
441 : }
442 0 : break;
443 : }
444 0 : case wkbLinearRing:
445 : case wkbCurve:
446 : case wkbSurface:
447 : case wkbCurveZ:
448 : case wkbSurfaceZ:
449 : case wkbCurveM:
450 : case wkbSurfaceM:
451 : case wkbCurveZM:
452 : case wkbSurfaceZM:
453 0 : break;
454 11 : }
455 : }
456 292 : else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
457 : {
458 0 : exportToWktWithOpts(/* bIso=*/false);
459 : }
460 292 : else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
461 0 : EQUAL(pszDisplayGeometry, "ISO_WKT"))
462 : {
463 292 : exportToWktWithOpts(/* bIso=*/true);
464 : }
465 :
466 606 : return osRet;
467 : }
468 :
469 : /************************************************************************/
470 : /* OGR_G_DumpReadable() */
471 : /************************************************************************/
472 : /**
473 : * \brief Dump geometry in well known text format to indicated output file.
474 : *
475 : * This method is the same as the CPP method OGRGeometry::dumpReadable.
476 : *
477 : * @param hGeom handle on the geometry to dump.
478 : * @param fp the text file to write the geometry to.
479 : * @param pszPrefix the prefix to put on each line of output.
480 : */
481 :
482 0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
483 :
484 : {
485 0 : VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
486 :
487 0 : OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
488 : }
489 :
490 : /************************************************************************/
491 : /* assignSpatialReference() */
492 : /************************************************************************/
493 :
494 : /**
495 : * \brief Assign spatial reference to this object.
496 : *
497 : * Any existing spatial reference
498 : * is replaced, but under no circumstances does this result in the object
499 : * being reprojected. It is just changing the interpretation of the existing
500 : * geometry. Note that assigning a spatial reference increments the
501 : * reference count on the OGRSpatialReference, but does not copy it.
502 : *
503 : * Starting with GDAL 2.3, this will also assign the spatial reference to
504 : * potential sub-geometries of the geometry (OGRGeometryCollection,
505 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
506 : * derived classes).
507 : *
508 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
509 : *
510 : * This method is the same as the C function OGR_G_AssignSpatialReference().
511 : *
512 : * @param poSR new spatial reference system to apply.
513 : */
514 :
515 5594850 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
516 :
517 : {
518 : // Do in that order to properly handle poSR == poSRS
519 5594850 : if (poSR != nullptr)
520 3606100 : const_cast<OGRSpatialReference *>(poSR)->Reference();
521 5594850 : if (poSRS != nullptr)
522 30184 : const_cast<OGRSpatialReference *>(poSRS)->Release();
523 :
524 5594850 : poSRS = poSR;
525 5594850 : }
526 :
527 : /************************************************************************/
528 : /* OGR_G_AssignSpatialReference() */
529 : /************************************************************************/
530 : /**
531 : * \brief Assign spatial reference to this object.
532 : *
533 : * Any existing spatial reference
534 : * is replaced, but under no circumstances does this result in the object
535 : * being reprojected. It is just changing the interpretation of the existing
536 : * geometry. Note that assigning a spatial reference increments the
537 : * reference count on the OGRSpatialReference, but does not copy it.
538 : *
539 : * Starting with GDAL 2.3, this will also assign the spatial reference to
540 : * potential sub-geometries of the geometry (OGRGeometryCollection,
541 : * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
542 : * derived classes).
543 : *
544 : * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
545 : *
546 : * This function is the same as the CPP method
547 : * OGRGeometry::assignSpatialReference.
548 : *
549 : * @param hGeom handle on the geometry to apply the new spatial reference
550 : * system.
551 : * @param hSRS handle on the new spatial reference system to apply.
552 : */
553 :
554 74 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
555 :
556 : {
557 74 : VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
558 :
559 148 : OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
560 74 : OGRSpatialReference::FromHandle(hSRS));
561 : }
562 :
563 : /************************************************************************/
564 : /* Intersects() */
565 : /************************************************************************/
566 :
567 : /**
568 : * \brief Do these features intersect?
569 : *
570 : * Determines whether two geometries intersect. If GEOS is enabled, then
571 : * this is done in rigorous fashion otherwise TRUE is returned if the
572 : * envelopes (bounding boxes) of the two geometries overlap.
573 : *
574 : * The poOtherGeom argument may be safely NULL, but in this case the method
575 : * will always return TRUE. That is, a NULL geometry is treated as being
576 : * everywhere.
577 : *
578 : * This method is the same as the C function OGR_G_Intersects().
579 : *
580 : * @param poOtherGeom the other geometry to test against.
581 : *
582 : * @return TRUE if the geometries intersect, otherwise FALSE.
583 : */
584 :
585 35 : OGRBoolean OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
586 :
587 : {
588 35 : if (poOtherGeom == nullptr)
589 0 : return TRUE;
590 :
591 35 : OGREnvelope oEnv1;
592 35 : getEnvelope(&oEnv1);
593 :
594 35 : OGREnvelope oEnv2;
595 35 : poOtherGeom->getEnvelope(&oEnv2);
596 :
597 35 : if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
598 19 : oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
599 16 : return FALSE;
600 :
601 : #ifndef HAVE_GEOS
602 : // Without GEOS we assume that envelope overlap is equivalent to
603 : // actual intersection.
604 : return TRUE;
605 : #else
606 :
607 19 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
608 19 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
609 19 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
610 :
611 19 : OGRBoolean bResult = FALSE;
612 19 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
613 : {
614 19 : bResult =
615 19 : GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) != 0;
616 : }
617 :
618 19 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
619 19 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
620 19 : freeGEOSContext(hGEOSCtxt);
621 :
622 19 : return bResult;
623 : #endif // HAVE_GEOS
624 : }
625 :
626 : // Old API compatibility function.
627 :
628 : //! @cond Doxygen_Suppress
629 0 : OGRBoolean OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
630 :
631 : {
632 0 : return Intersects(poOtherGeom);
633 : }
634 :
635 : //! @endcond
636 :
637 : /************************************************************************/
638 : /* OGR_G_Intersects() */
639 : /************************************************************************/
640 : /**
641 : * \brief Do these features intersect?
642 : *
643 : * Determines whether two geometries intersect. If GEOS is enabled, then
644 : * this is done in rigorous fashion otherwise TRUE is returned if the
645 : * envelopes (bounding boxes) of the two geometries overlap.
646 : *
647 : * This function is the same as the CPP method OGRGeometry::Intersects.
648 : *
649 : * @param hGeom handle on the first geometry.
650 : * @param hOtherGeom handle on the other geometry to test against.
651 : *
652 : * @return TRUE if the geometries intersect, otherwise FALSE.
653 : */
654 :
655 11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
656 :
657 : {
658 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
659 11 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
660 :
661 22 : return OGRGeometry::FromHandle(hGeom)->Intersects(
662 11 : OGRGeometry::FromHandle(hOtherGeom));
663 : }
664 :
665 : //! @cond Doxygen_Suppress
666 0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
667 :
668 : {
669 0 : VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
670 0 : VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
671 :
672 0 : return OGRGeometry::FromHandle(hGeom)->Intersects(
673 0 : OGRGeometry::FromHandle(hOtherGeom));
674 : }
675 :
676 : //! @endcond
677 :
678 : /************************************************************************/
679 : /* transformTo() */
680 : /************************************************************************/
681 :
682 : /**
683 : * \brief Transform geometry to new spatial reference system.
684 : *
685 : * This method will transform the coordinates of a geometry from
686 : * their current spatial reference system to a new target spatial
687 : * reference system. Normally this means reprojecting the vectors,
688 : * but it could include datum shifts, and changes of units.
689 : *
690 : * This method will only work if the geometry already has an assigned
691 : * spatial reference system, and if it is transformable to the target
692 : * coordinate system.
693 : *
694 : * Because this method requires internal creation and initialization of an
695 : * OGRCoordinateTransformation object it is significantly more expensive to
696 : * use this method to transform many geometries than it is to create the
697 : * OGRCoordinateTransformation in advance, and call transform() with that
698 : * transformation. This method exists primarily for convenience when only
699 : * transforming a single geometry.
700 : *
701 : * This method is the same as the C function OGR_G_TransformTo().
702 : *
703 : * @param poSR spatial reference system to transform to.
704 : *
705 : * @return OGRERR_NONE on success, or an error code.
706 : */
707 :
708 27 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
709 :
710 : {
711 27 : if (getSpatialReference() == nullptr)
712 : {
713 1 : CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
714 1 : return OGRERR_FAILURE;
715 : }
716 :
717 26 : if (poSR == nullptr)
718 : {
719 0 : CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
720 0 : return OGRERR_FAILURE;
721 : }
722 :
723 : OGRCoordinateTransformation *poCT =
724 26 : OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
725 26 : if (poCT == nullptr)
726 0 : return OGRERR_FAILURE;
727 :
728 26 : const OGRErr eErr = transform(poCT);
729 :
730 26 : delete poCT;
731 :
732 26 : return eErr;
733 : }
734 :
735 : /************************************************************************/
736 : /* OGR_G_TransformTo() */
737 : /************************************************************************/
738 : /**
739 : * \brief Transform geometry to new spatial reference system.
740 : *
741 : * This function will transform the coordinates of a geometry from
742 : * their current spatial reference system to a new target spatial
743 : * reference system. Normally this means reprojecting the vectors,
744 : * but it could include datum shifts, and changes of units.
745 : *
746 : * This function will only work if the geometry already has an assigned
747 : * spatial reference system, and if it is transformable to the target
748 : * coordinate system.
749 : *
750 : * Because this function requires internal creation and initialization of an
751 : * OGRCoordinateTransformation object it is significantly more expensive to
752 : * use this function to transform many geometries than it is to create the
753 : * OGRCoordinateTransformation in advance, and call transform() with that
754 : * transformation. This function exists primarily for convenience when only
755 : * transforming a single geometry.
756 : *
757 : * This function is the same as the CPP method OGRGeometry::transformTo.
758 : *
759 : * @param hGeom handle on the geometry to apply the transform to.
760 : * @param hSRS handle on the spatial reference system to apply.
761 : *
762 : * @return OGRERR_NONE on success, or an error code.
763 : */
764 :
765 9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
766 :
767 : {
768 9 : VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
769 :
770 18 : return OGRGeometry::FromHandle(hGeom)->transformTo(
771 18 : OGRSpatialReference::FromHandle(hSRS));
772 : }
773 :
774 : /**
775 : * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
776 : *
777 : * \brief Apply arbitrary coordinate transformation to geometry.
778 : *
779 : * This method will transform the coordinates of a geometry from
780 : * their current spatial reference system to a new target spatial
781 : * reference system. Normally this means reprojecting the vectors,
782 : * but it could include datum shifts, and changes of units.
783 : *
784 : * Note that this method does not require that the geometry already
785 : * have a spatial reference system. It will be assumed that they can
786 : * be treated as having the source spatial reference system of the
787 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
788 : * will be ignored. On successful completion the output OGRSpatialReference
789 : * of the OGRCoordinateTransformation will be assigned to the geometry.
790 : *
791 : * This method only does reprojection on a point-by-point basis. It does not
792 : * include advanced logic to deal with discontinuities at poles or antimeridian.
793 : * For that, use the OGRGeometryFactory::transformWithOptions() method.
794 : *
795 : * This method is the same as the C function OGR_G_Transform().
796 : *
797 : * @param poCT the transformation to apply.
798 : *
799 : * @return OGRERR_NONE on success or an error code.
800 : */
801 :
802 : /************************************************************************/
803 : /* OGR_G_Transform() */
804 : /************************************************************************/
805 : /**
806 : * \brief Apply arbitrary coordinate transformation to geometry.
807 : *
808 : * This function will transform the coordinates of a geometry from
809 : * their current spatial reference system to a new target spatial
810 : * reference system. Normally this means reprojecting the vectors,
811 : * but it could include datum shifts, and changes of units.
812 : *
813 : * Note that this function does not require that the geometry already
814 : * have a spatial reference system. It will be assumed that they can
815 : * be treated as having the source spatial reference system of the
816 : * OGRCoordinateTransformation object, and the actual SRS of the geometry
817 : * will be ignored. On successful completion the output OGRSpatialReference
818 : * of the OGRCoordinateTransformation will be assigned to the geometry.
819 : *
820 : * This function only does reprojection on a point-by-point basis. It does not
821 : * include advanced logic to deal with discontinuities at poles or antimeridian.
822 : * For that, use the OGR_GeomTransformer_Create() and
823 : * OGR_GeomTransformer_Transform() functions.
824 : *
825 : * This function is the same as the CPP method OGRGeometry::transform.
826 : *
827 : * @param hGeom handle on the geometry to apply the transform to.
828 : * @param hTransform handle on the transformation to apply.
829 : *
830 : * @return OGRERR_NONE on success or an error code.
831 : */
832 :
833 11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
834 : OGRCoordinateTransformationH hTransform)
835 :
836 : {
837 11 : VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
838 :
839 22 : return OGRGeometry::FromHandle(hGeom)->transform(
840 11 : OGRCoordinateTransformation::FromHandle(hTransform));
841 : }
842 :
843 : /**
844 : * \fn int OGRGeometry::getDimension() const;
845 : *
846 : * \brief Get the dimension of this object.
847 : *
848 : * This method corresponds to the SFCOM IGeometry::GetDimension() method.
849 : * It indicates the dimension of the object, but does not indicate the
850 : * dimension of the underlying space (as indicated by
851 : * OGRGeometry::getCoordinateDimension()).
852 : *
853 : * This method is the same as the C function OGR_G_GetDimension().
854 : *
855 : * @return 0 for points, 1 for lines and 2 for surfaces.
856 : */
857 :
858 : /**
859 : * \brief Get the geometry type that conforms with ISO SQL/MM Part3
860 : *
861 : * @return the geometry type that conforms with ISO SQL/MM Part3
862 : */
863 701425 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
864 : {
865 701425 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
866 :
867 701438 : if (flags & OGR_G_3D)
868 212663 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
869 701438 : if (flags & OGR_G_MEASURED)
870 26023 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
871 :
872 701438 : return nGType;
873 : }
874 :
875 : /************************************************************************/
876 : /* OGRGeometry::segmentize() */
877 : /************************************************************************/
878 : /**
879 : *
880 : * \brief Modify the geometry such it has no segment longer then the
881 : * given distance.
882 : *
883 : * This method modifies the geometry to add intermediate vertices if necessary
884 : * so that the maximum length between 2 consecutive vertices is lower than
885 : * dfMaxLength.
886 : *
887 : * Interpolated points will have Z and M values (if needed) set to 0.
888 : * Distance computation is performed in 2d only
889 : *
890 : * This function is the same as the C function OGR_G_Segmentize()
891 : *
892 : * @param dfMaxLength the maximum distance between 2 points after segmentization
893 : * @return (since 3.10) true in case of success, false in case of error.
894 : */
895 :
896 0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
897 : {
898 : // Do nothing.
899 0 : return true;
900 : }
901 :
902 : /************************************************************************/
903 : /* OGR_G_Segmentize() */
904 : /************************************************************************/
905 :
906 : /**
907 : *
908 : * \brief Modify the geometry such it has no segment longer then the given
909 : * distance.
910 : *
911 : * Interpolated points will have Z and M values (if needed) set to 0.
912 : * Distance computation is performed in 2d only.
913 : *
914 : * This function is the same as the CPP method OGRGeometry::segmentize().
915 : *
916 : * @param hGeom handle on the geometry to segmentize
917 : * @param dfMaxLength the maximum distance between 2 points after segmentization
918 : */
919 :
920 21 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
921 : {
922 21 : VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
923 :
924 21 : if (dfMaxLength <= 0)
925 : {
926 0 : CPLError(CE_Failure, CPLE_AppDefined,
927 : "dfMaxLength must be strictly positive");
928 0 : return;
929 : }
930 21 : OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
931 : }
932 :
933 : /************************************************************************/
934 : /* OGR_G_GetDimension() */
935 : /************************************************************************/
936 : /**
937 : *
938 : * \brief Get the dimension of this geometry.
939 : *
940 : * This function corresponds to the SFCOM IGeometry::GetDimension() method.
941 : * It indicates the dimension of the geometry, but does not indicate the
942 : * dimension of the underlying space (as indicated by
943 : * OGR_G_GetCoordinateDimension() function).
944 : *
945 : * This function is the same as the CPP method OGRGeometry::getDimension().
946 : *
947 : * @param hGeom handle on the geometry to get the dimension from.
948 : * @return 0 for points, 1 for lines and 2 for surfaces.
949 : */
950 :
951 21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
952 :
953 : {
954 21 : VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
955 :
956 21 : return OGRGeometry::FromHandle(hGeom)->getDimension();
957 : }
958 :
959 : /************************************************************************/
960 : /* getCoordinateDimension() */
961 : /************************************************************************/
962 : /**
963 : * \brief Get the dimension of the coordinates in this object.
964 : *
965 : * This method is the same as the C function OGR_G_GetCoordinateDimension().
966 : *
967 : * @deprecated use CoordinateDimension().
968 : *
969 : * @return this will return 2 or 3.
970 : */
971 :
972 940344 : int OGRGeometry::getCoordinateDimension() const
973 :
974 : {
975 940344 : return (flags & OGR_G_3D) ? 3 : 2;
976 : }
977 :
978 : /************************************************************************/
979 : /* CoordinateDimension() */
980 : /************************************************************************/
981 : /**
982 : * \brief Get the dimension of the coordinates in this object.
983 : *
984 : * This method is the same as the C function OGR_G_CoordinateDimension().
985 : *
986 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
987 : *
988 : * @since GDAL 2.1
989 : */
990 :
991 100964 : int OGRGeometry::CoordinateDimension() const
992 :
993 : {
994 100964 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
995 7405 : return 4;
996 93559 : else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
997 6108 : return 3;
998 : else
999 87451 : return 2;
1000 : }
1001 :
1002 : /************************************************************************/
1003 : /* OGR_G_GetCoordinateDimension() */
1004 : /************************************************************************/
1005 : /**
1006 : *
1007 : * \brief Get the dimension of the coordinates in this geometry.
1008 : *
1009 : * This function is the same as the CPP method
1010 : * OGRGeometry::getCoordinateDimension().
1011 : *
1012 : * @param hGeom handle on the geometry to get the dimension of the
1013 : * coordinates from.
1014 : *
1015 : * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
1016 : * OGR_G_IsMeasured().
1017 : *
1018 : * @return this will return 2 or 3.
1019 : */
1020 :
1021 708 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
1022 :
1023 : {
1024 708 : VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
1025 :
1026 708 : return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
1027 : }
1028 :
1029 : /************************************************************************/
1030 : /* OGR_G_CoordinateDimension() */
1031 : /************************************************************************/
1032 : /**
1033 : *
1034 : * \brief Get the dimension of the coordinates in this geometry.
1035 : *
1036 : * This function is the same as the CPP method
1037 : * OGRGeometry::CoordinateDimension().
1038 : *
1039 : * @param hGeom handle on the geometry to get the dimension of the
1040 : * coordinates from.
1041 : *
1042 : * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
1043 : *
1044 : * @since GDAL 2.1
1045 : */
1046 :
1047 4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
1048 :
1049 : {
1050 4 : VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
1051 :
1052 4 : return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
1053 : }
1054 :
1055 : /**
1056 : *
1057 : * \brief See whether this geometry has Z coordinates.
1058 : *
1059 : * This function is the same as the CPP method
1060 : * OGRGeometry::Is3D().
1061 : *
1062 : * @param hGeom handle on the geometry to check whether it has Z coordinates.
1063 : *
1064 : * @return TRUE if the geometry has Z coordinates.
1065 : * @since GDAL 2.1
1066 : */
1067 :
1068 35028 : int OGR_G_Is3D(OGRGeometryH hGeom)
1069 :
1070 : {
1071 35028 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1072 :
1073 35028 : return OGRGeometry::FromHandle(hGeom)->Is3D();
1074 : }
1075 :
1076 : /**
1077 : *
1078 : * \brief See whether this geometry is measured.
1079 : *
1080 : * This function is the same as the CPP method
1081 : * OGRGeometry::IsMeasured().
1082 : *
1083 : * @param hGeom handle on the geometry to check whether it is measured.
1084 : *
1085 : * @return TRUE if the geometry has M coordinates.
1086 : * @since GDAL 2.1
1087 : */
1088 :
1089 37908 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1090 :
1091 : {
1092 37908 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1093 :
1094 37908 : return OGRGeometry::FromHandle(hGeom)->IsMeasured();
1095 : }
1096 :
1097 : /************************************************************************/
1098 : /* setCoordinateDimension() */
1099 : /************************************************************************/
1100 :
1101 : /**
1102 : * \brief Set the coordinate dimension.
1103 : *
1104 : * This method sets the explicit coordinate dimension. Setting the coordinate
1105 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1106 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1107 : * will affect the children geometries.
1108 : * This will also remove the M dimension if present before this call.
1109 : *
1110 : * @deprecated use set3D() or setMeasured().
1111 : *
1112 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1113 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1114 : */
1115 :
1116 65363 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1117 :
1118 : {
1119 65363 : if (nNewDimension == 2)
1120 64871 : flags &= ~OGR_G_3D;
1121 : else
1122 492 : flags |= OGR_G_3D;
1123 65363 : return setMeasured(FALSE);
1124 : }
1125 :
1126 : /**
1127 : * \brief Add or remove the Z coordinate dimension.
1128 : *
1129 : * This method adds or removes the explicit Z coordinate dimension.
1130 : * Removing the Z coordinate dimension of a geometry will remove any
1131 : * existing Z values. Adding the Z dimension to a geometry
1132 : * collection, a compound curve, a polygon, etc. will affect the
1133 : * children geometries.
1134 : *
1135 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1136 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1137 : * @since GDAL 2.1
1138 : */
1139 :
1140 1617550 : bool OGRGeometry::set3D(OGRBoolean bIs3D)
1141 :
1142 : {
1143 1617550 : if (bIs3D)
1144 1612380 : flags |= OGR_G_3D;
1145 : else
1146 5175 : flags &= ~OGR_G_3D;
1147 1617550 : return true;
1148 : }
1149 :
1150 : /**
1151 : * \brief Add or remove the M coordinate dimension.
1152 : *
1153 : * This method adds or removes the explicit M coordinate dimension.
1154 : * Removing the M coordinate dimension of a geometry will remove any
1155 : * existing M values. Adding the M dimension to a geometry
1156 : * collection, a compound curve, a polygon, etc. will affect the
1157 : * children geometries.
1158 : *
1159 : * @param bIsMeasured Should the geometry have a M dimension, either
1160 : * TRUE or FALSE.
1161 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1162 : * @since GDAL 2.1
1163 : */
1164 :
1165 410545 : bool OGRGeometry::setMeasured(OGRBoolean bIsMeasured)
1166 :
1167 : {
1168 410545 : if (bIsMeasured)
1169 137686 : flags |= OGR_G_MEASURED;
1170 : else
1171 272859 : flags &= ~OGR_G_MEASURED;
1172 410545 : return true;
1173 : }
1174 :
1175 : /************************************************************************/
1176 : /* OGR_G_SetCoordinateDimension() */
1177 : /************************************************************************/
1178 :
1179 : /**
1180 : * \brief Set the coordinate dimension.
1181 : *
1182 : * This method sets the explicit coordinate dimension. Setting the coordinate
1183 : * dimension of a geometry to 2 should zero out any existing Z values. Setting
1184 : * the dimension of a geometry collection, a compound curve, a polygon, etc.
1185 : * will affect the children geometries.
1186 : * This will also remove the M dimension if present before this call.
1187 : *
1188 : * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
1189 : *
1190 : * @param hGeom handle on the geometry to set the dimension of the
1191 : * coordinates.
1192 : * @param nNewDimension New coordinate dimension value, either 2 or 3.
1193 : */
1194 :
1195 56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
1196 :
1197 : {
1198 56 : VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
1199 :
1200 56 : OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
1201 : }
1202 :
1203 : /************************************************************************/
1204 : /* OGR_G_Set3D() */
1205 : /************************************************************************/
1206 :
1207 : /**
1208 : * \brief Add or remove the Z coordinate dimension.
1209 : *
1210 : * This method adds or removes the explicit Z coordinate dimension.
1211 : * Removing the Z coordinate dimension of a geometry will remove any
1212 : * existing Z values. Adding the Z dimension to a geometry
1213 : * collection, a compound curve, a polygon, etc. will affect the
1214 : * children geometries.
1215 : *
1216 : * @param hGeom handle on the geometry to set or unset the Z dimension.
1217 : * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
1218 : * @since GDAL 2.1
1219 : */
1220 :
1221 154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
1222 :
1223 : {
1224 154 : VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
1225 :
1226 154 : OGRGeometry::FromHandle(hGeom)->set3D(bIs3D);
1227 : }
1228 :
1229 : /************************************************************************/
1230 : /* OGR_G_SetMeasured() */
1231 : /************************************************************************/
1232 :
1233 : /**
1234 : * \brief Add or remove the M coordinate dimension.
1235 : *
1236 : * This method adds or removes the explicit M coordinate dimension.
1237 : * Removing the M coordinate dimension of a geometry will remove any
1238 : * existing M values. Adding the M dimension to a geometry
1239 : * collection, a compound curve, a polygon, etc. will affect the
1240 : * children geometries.
1241 : *
1242 : * @param hGeom handle on the geometry to set or unset the M dimension.
1243 : * @param bIsMeasured Should the geometry have a M dimension, either
1244 : * TRUE or FALSE.
1245 : * @since GDAL 2.1
1246 : */
1247 :
1248 154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
1249 :
1250 : {
1251 154 : VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
1252 :
1253 154 : OGRGeometry::FromHandle(hGeom)->setMeasured(bIsMeasured);
1254 : }
1255 :
1256 : /**
1257 : * \fn int OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
1258 : *
1259 : * \brief Returns TRUE if two geometries are equivalent.
1260 : *
1261 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1262 : *
1263 : * The comparison is done in a structural way, that is to say that the geometry
1264 : * types must be identical, as well as the number and ordering of sub-geometries
1265 : * and vertices.
1266 : * Or equivalently, two geometries are considered equal by this method if their
1267 : * WKT/WKB representation is equal.
1268 : * Note: this must be distinguished for equality in a spatial way (which is
1269 : * the purpose of the ST_Equals() operation).
1270 : *
1271 : * This method is the same as the C function OGR_G_Equals().
1272 : *
1273 : * @return TRUE if equivalent or FALSE otherwise.
1274 : */
1275 :
1276 : // Backward compatibility method.
1277 :
1278 : //! @cond Doxygen_Suppress
1279 0 : int OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
1280 : {
1281 0 : return Equals(poOtherGeom);
1282 : }
1283 :
1284 : //! @endcond
1285 :
1286 : /************************************************************************/
1287 : /* OGR_G_Equals() */
1288 : /************************************************************************/
1289 :
1290 : /**
1291 : * \brief Returns TRUE if two geometries are equivalent.
1292 : *
1293 : * This operation implements the SQL/MM ST_OrderingEquals() operation.
1294 : *
1295 : * The comparison is done in a structural way, that is to say that the geometry
1296 : * types must be identical, as well as the number and ordering of sub-geometries
1297 : * and vertices.
1298 : * Or equivalently, two geometries are considered equal by this method if their
1299 : * WKT/WKB representation is equal.
1300 : * Note: this must be distinguished for equality in a spatial way (which is
1301 : * the purpose of the ST_Equals() operation).
1302 : *
1303 : * This function is the same as the CPP method OGRGeometry::Equals() method.
1304 : *
1305 : * @param hGeom handle on the first geometry.
1306 : * @param hOther handle on the other geometry to test against.
1307 : * @return TRUE if equivalent or FALSE otherwise.
1308 : */
1309 :
1310 28211 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1311 :
1312 : {
1313 28211 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1314 :
1315 28211 : if (hOther == nullptr)
1316 : {
1317 0 : CPLError(CE_Failure, CPLE_ObjectNull,
1318 : "hOther was NULL in OGR_G_Equals");
1319 0 : return 0;
1320 : }
1321 :
1322 56422 : return OGRGeometry::FromHandle(hGeom)->Equals(
1323 28211 : OGRGeometry::FromHandle(hOther));
1324 : }
1325 :
1326 : //! @cond Doxygen_Suppress
1327 0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
1328 :
1329 : {
1330 0 : if (hGeom == nullptr)
1331 : {
1332 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
1333 0 : return 0;
1334 : }
1335 :
1336 0 : if (hOther == nullptr)
1337 : {
1338 0 : CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
1339 0 : return 0;
1340 : }
1341 :
1342 0 : return OGRGeometry::FromHandle(hGeom)->Equals(
1343 0 : OGRGeometry::FromHandle(hOther));
1344 : }
1345 :
1346 : //! @endcond
1347 :
1348 : /**
1349 : * \fn int OGRGeometry::WkbSize() const;
1350 : *
1351 : * \brief Returns size of related binary representation.
1352 : *
1353 : * This method returns the exact number of bytes required to hold the
1354 : * well known binary representation of this geometry object. Its computation
1355 : * may be slightly expensive for complex geometries.
1356 : *
1357 : * This method relates to the SFCOM IWks::WkbSize() method.
1358 : *
1359 : * This method is the same as the C function OGR_G_WkbSize().
1360 : *
1361 : * @return size of binary representation in bytes.
1362 : */
1363 :
1364 : /************************************************************************/
1365 : /* OGR_G_WkbSize() */
1366 : /************************************************************************/
1367 : /**
1368 : * \brief Returns size of related binary representation.
1369 : *
1370 : * This function returns the exact number of bytes required to hold the
1371 : * well known binary representation of this geometry object. Its computation
1372 : * may be slightly expensive for complex geometries.
1373 : *
1374 : * This function relates to the SFCOM IWks::WkbSize() method.
1375 : *
1376 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1377 : *
1378 : * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
1379 : *
1380 : * @param hGeom handle on the geometry to get the binary size from.
1381 : * @return size of binary representation in bytes.
1382 : */
1383 :
1384 1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
1385 :
1386 : {
1387 1 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
1388 :
1389 1 : const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
1390 1 : if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
1391 : {
1392 0 : CPLError(CE_Failure, CPLE_AppDefined,
1393 : "OGR_G_WkbSize() would return a value beyond int range. "
1394 : "Use OGR_G_WkbSizeEx() instead");
1395 0 : return 0;
1396 : }
1397 1 : return static_cast<int>(nSize);
1398 : }
1399 :
1400 : /************************************************************************/
1401 : /* OGR_G_WkbSizeEx() */
1402 : /************************************************************************/
1403 : /**
1404 : * \brief Returns size of related binary representation.
1405 : *
1406 : * This function returns the exact number of bytes required to hold the
1407 : * well known binary representation of this geometry object. Its computation
1408 : * may be slightly expensive for complex geometries.
1409 : *
1410 : * This function relates to the SFCOM IWks::WkbSize() method.
1411 : *
1412 : * This function is the same as the CPP method OGRGeometry::WkbSize().
1413 : *
1414 : * @param hGeom handle on the geometry to get the binary size from.
1415 : * @return size of binary representation in bytes.
1416 : * @since GDAL 3.3
1417 : */
1418 :
1419 10675 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
1420 :
1421 : {
1422 10675 : VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
1423 :
1424 10675 : return OGRGeometry::FromHandle(hGeom)->WkbSize();
1425 : }
1426 :
1427 : /**
1428 : * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
1429 : *
1430 : * \brief Computes and returns the bounding envelope for this geometry
1431 : * in the passed psEnvelope structure.
1432 : *
1433 : * This method is the same as the C function OGR_G_GetEnvelope().
1434 : *
1435 : * @param psEnvelope the structure in which to place the results.
1436 : */
1437 :
1438 : /************************************************************************/
1439 : /* OGR_G_GetEnvelope() */
1440 : /************************************************************************/
1441 : /**
1442 : * \brief Computes and returns the bounding envelope for this geometry
1443 : * in the passed psEnvelope structure.
1444 : *
1445 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1446 : *
1447 : * @param hGeom handle of the geometry to get envelope from.
1448 : * @param psEnvelope the structure in which to place the results.
1449 : */
1450 :
1451 13305 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
1452 :
1453 : {
1454 13305 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1455 :
1456 13305 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1457 : }
1458 :
1459 : /**
1460 : * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
1461 : *
1462 : * \brief Computes and returns the bounding envelope (3D) for this
1463 : * geometry in the passed psEnvelope structure.
1464 : *
1465 : * This method is the same as the C function OGR_G_GetEnvelope3D().
1466 : *
1467 : * @param psEnvelope the structure in which to place the results.
1468 : *
1469 : * @since OGR 1.9.0
1470 : */
1471 :
1472 : /************************************************************************/
1473 : /* OGR_G_GetEnvelope3D() */
1474 : /************************************************************************/
1475 : /**
1476 : * \brief Computes and returns the bounding envelope (3D) for this
1477 : * geometry in the passed psEnvelope structure.
1478 : *
1479 : * This function is the same as the CPP method OGRGeometry::getEnvelope().
1480 : *
1481 : * @param hGeom handle of the geometry to get envelope from.
1482 : * @param psEnvelope the structure in which to place the results.
1483 : *
1484 : * @since OGR 1.9.0
1485 : */
1486 :
1487 10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
1488 :
1489 : {
1490 10 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
1491 :
1492 10 : OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1493 : }
1494 :
1495 : /************************************************************************/
1496 : /* importFromWkb() */
1497 : /************************************************************************/
1498 :
1499 : /**
1500 : * \brief Assign geometry from well known binary data.
1501 : *
1502 : * The object must have already been instantiated as the correct derived
1503 : * type of geometry object to match the binaries type. This method is used
1504 : * by the OGRGeometryFactory class, but not normally called by application
1505 : * code.
1506 : *
1507 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1508 : *
1509 : * This method is the same as the C function OGR_G_ImportFromWkb().
1510 : *
1511 : * @param pabyData the binary input data.
1512 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1513 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1514 : * done for curve geometries code
1515 : *
1516 : * @return OGRERR_NONE if all goes well, otherwise any of
1517 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1518 : * OGRERR_CORRUPT_DATA may be returned.
1519 : */
1520 :
1521 492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
1522 : OGRwkbVariant eWkbVariant)
1523 : {
1524 492 : size_t nBytesConsumedOutIgnored = 0;
1525 492 : return importFromWkb(pabyData, nSize, eWkbVariant,
1526 984 : nBytesConsumedOutIgnored);
1527 : }
1528 :
1529 : /**
1530 : * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
1531 : * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
1532 : *
1533 : * \brief Assign geometry from well known binary data.
1534 : *
1535 : * The object must have already been instantiated as the correct derived
1536 : * type of geometry object to match the binaries type. This method is used
1537 : * by the OGRGeometryFactory class, but not normally called by application
1538 : * code.
1539 : *
1540 : * This method relates to the SFCOM IWks::ImportFromWKB() method.
1541 : *
1542 : * This method is the same as the C function OGR_G_ImportFromWkb().
1543 : *
1544 : * @param pabyData the binary input data.
1545 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1546 : * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
1547 : * done for curve geometries code
1548 : * @param nBytesConsumedOut output parameter. Number of bytes consumed.
1549 : *
1550 : * @return OGRERR_NONE if all goes well, otherwise any of
1551 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1552 : * OGRERR_CORRUPT_DATA may be returned.
1553 : *
1554 : * @since GDAL 2.3
1555 : */
1556 :
1557 : /************************************************************************/
1558 : /* OGR_G_ImportFromWkb() */
1559 : /************************************************************************/
1560 : /**
1561 : * \brief Assign geometry from well known binary data.
1562 : *
1563 : * The object must have already been instantiated as the correct derived
1564 : * type of geometry object to match the binaries type.
1565 : *
1566 : * This function relates to the SFCOM IWks::ImportFromWKB() method.
1567 : *
1568 : * This function is the same as the CPP method OGRGeometry::importFromWkb().
1569 : *
1570 : * @param hGeom handle on the geometry to assign the well know binary data to.
1571 : * @param pabyData the binary input data.
1572 : * @param nSize the size of pabyData in bytes, or -1 if not known.
1573 : *
1574 : * @return OGRERR_NONE if all goes well, otherwise any of
1575 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1576 : * OGRERR_CORRUPT_DATA may be returned.
1577 : */
1578 :
1579 0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
1580 :
1581 : {
1582 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
1583 :
1584 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkb(
1585 0 : static_cast<const GByte *>(pabyData), nSize);
1586 : }
1587 :
1588 : /************************************************************************/
1589 : /* OGRGeometry::exportToWkb() */
1590 : /************************************************************************/
1591 :
1592 : /* clang-format off */
1593 : /**
1594 : * \brief Convert a geometry into well known binary format.
1595 : *
1596 : * This method relates to the SFCOM IWks::ExportToWKB() method.
1597 : *
1598 : * This method is the same as the C function OGR_G_ExportToWkb() or
1599 : * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
1600 : *
1601 : * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1602 : * respectively.
1603 : * @param pabyData a buffer into which the binary representation is
1604 : * written. This buffer must be at least
1605 : * OGRGeometry::WkbSize() byte in size.
1606 : * @param eWkbVariant What standard to use when exporting geometries
1607 : * with three dimensions (or more). The default
1608 : * wkbVariantOldOgc is the historical OGR
1609 : * variant. wkbVariantIso is the variant defined
1610 : * in ISO SQL/MM and adopted by OGC for SFSQL
1611 : * 1.2.
1612 : *
1613 : * @return Currently OGRERR_NONE is always returned.
1614 : */
1615 : /* clang-format on */
1616 264233 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1617 : unsigned char *pabyData,
1618 : OGRwkbVariant eWkbVariant) const
1619 : {
1620 264233 : OGRwkbExportOptions sOptions;
1621 264233 : sOptions.eByteOrder = eByteOrder;
1622 264233 : sOptions.eWkbVariant = eWkbVariant;
1623 528475 : return exportToWkb(pabyData, &sOptions);
1624 : }
1625 :
1626 : /************************************************************************/
1627 : /* OGR_G_ExportToWkb() */
1628 : /************************************************************************/
1629 : /**
1630 : * \brief Convert a geometry well known binary format
1631 : *
1632 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1633 : *
1634 : * For backward compatibility purposes, it exports the Old-style 99-402
1635 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
1636 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
1637 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
1638 : *
1639 : * This function is the same as the CPP method
1640 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
1641 : * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
1642 : *
1643 : * @param hGeom handle on the geometry to convert to a well know binary
1644 : * data from.
1645 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1646 : * respectively.
1647 : * @param pabyDstBuffer a buffer into which the binary representation is
1648 : * written. This buffer must be at least
1649 : * OGR_G_WkbSize() byte in size.
1650 : *
1651 : * @return Currently OGRERR_NONE is always returned.
1652 : */
1653 :
1654 105 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1655 : unsigned char *pabyDstBuffer)
1656 :
1657 : {
1658 105 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
1659 :
1660 105 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
1661 : }
1662 :
1663 : /************************************************************************/
1664 : /* OGR_G_ExportToIsoWkb() */
1665 : /************************************************************************/
1666 : /**
1667 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
1668 : * binary format
1669 : *
1670 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1671 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
1672 : * types.
1673 : *
1674 : * This function is the same as the CPP method
1675 : * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
1676 : * with eWkbVariant = wkbVariantIso.
1677 : *
1678 : * @param hGeom handle on the geometry to convert to a well know binary
1679 : * data from.
1680 : * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
1681 : * respectively.
1682 : * @param pabyDstBuffer a buffer into which the binary representation is
1683 : * written. This buffer must be at least
1684 : * OGR_G_WkbSize() byte in size.
1685 : *
1686 : * @return Currently OGRERR_NONE is always returned.
1687 : *
1688 : * @since GDAL 2.0
1689 : */
1690 :
1691 10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
1692 : unsigned char *pabyDstBuffer)
1693 :
1694 : {
1695 10571 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
1696 :
1697 10571 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
1698 10571 : wkbVariantIso);
1699 : }
1700 :
1701 : /************************************************************************/
1702 : /* OGR_G_ExportToWkbEx() */
1703 : /************************************************************************/
1704 :
1705 : /* clang-format off */
1706 : /**
1707 : * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
1708 : *
1709 : * \brief Convert a geometry into well known binary format
1710 : *
1711 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1712 : *
1713 : * This function is the same as the C function OGR_G_ExportToWkbEx().
1714 : *
1715 : * @param pabyDstBuffer a buffer into which the binary representation is
1716 : * written. This buffer must be at least
1717 : * OGR_G_WkbSize() byte in size.
1718 : * @param psOptions WKB export options.
1719 :
1720 : * @return Currently OGRERR_NONE is always returned.
1721 : *
1722 : * @since GDAL 3.9
1723 : */
1724 : /* clang-format on */
1725 :
1726 : /**
1727 : * \brief Convert a geometry into well known binary format
1728 : *
1729 : * This function relates to the SFCOM IWks::ExportToWKB() method.
1730 : *
1731 : * This function is the same as the CPP method
1732 : * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
1733 : *
1734 : * @param hGeom handle on the geometry to convert to a well know binary
1735 : * data from.
1736 : * @param pabyDstBuffer a buffer into which the binary representation is
1737 : * written. This buffer must be at least
1738 : * OGR_G_WkbSize() byte in size.
1739 : * @param psOptions WKB export options.
1740 :
1741 : * @return Currently OGRERR_NONE is always returned.
1742 : *
1743 : * @since GDAL 3.9
1744 : */
1745 :
1746 2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
1747 : const OGRwkbExportOptions *psOptions)
1748 : {
1749 2 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
1750 :
1751 4 : return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
1752 2 : psOptions);
1753 : }
1754 :
1755 : /**
1756 : * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
1757 : *
1758 : * \brief Assign geometry from well known text data.
1759 : *
1760 : * The object must have already been instantiated as the correct derived
1761 : * type of geometry object to match the text type. This method is used
1762 : * by the OGRGeometryFactory class, but not normally called by application
1763 : * code.
1764 : *
1765 : * This method relates to the SFCOM IWks::ImportFromWKT() method.
1766 : *
1767 : * This method is the same as the C function OGR_G_ImportFromWkt().
1768 : *
1769 : * @param ppszInput pointer to a pointer to the source text. The pointer is
1770 : * updated to pointer after the consumed text.
1771 : *
1772 : * @return OGRERR_NONE if all goes well, otherwise any of
1773 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1774 : * OGRERR_CORRUPT_DATA may be returned.
1775 : */
1776 :
1777 : /************************************************************************/
1778 : /* OGR_G_ImportFromWkt() */
1779 : /************************************************************************/
1780 : /**
1781 : * \brief Assign geometry from well known text data.
1782 : *
1783 : * The object must have already been instantiated as the correct derived
1784 : * type of geometry object to match the text type.
1785 : *
1786 : * This function relates to the SFCOM IWks::ImportFromWKT() method.
1787 : *
1788 : * This function is the same as the CPP method OGRGeometry::importFromWkt().
1789 : *
1790 : * @param hGeom handle on the geometry to assign well know text data to.
1791 : * @param ppszSrcText pointer to a pointer to the source text. The pointer is
1792 : * updated to pointer after the consumed text.
1793 : *
1794 : * @return OGRERR_NONE if all goes well, otherwise any of
1795 : * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
1796 : * OGRERR_CORRUPT_DATA may be returned.
1797 : */
1798 :
1799 0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
1800 :
1801 : {
1802 0 : VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
1803 :
1804 0 : return OGRGeometry::FromHandle(hGeom)->importFromWkt(
1805 0 : const_cast<const char **>(ppszSrcText));
1806 : }
1807 :
1808 : /************************************************************************/
1809 : /* importPreambleFromWkt() */
1810 : /************************************************************************/
1811 :
1812 : // Returns -1 if processing must continue.
1813 : //! @cond Doxygen_Suppress
1814 122814 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1815 : int *pbHasM, bool *pbIsEmpty)
1816 : {
1817 122814 : const char *pszInput = *ppszInput;
1818 :
1819 : /* -------------------------------------------------------------------- */
1820 : /* Clear existing Geoms. */
1821 : /* -------------------------------------------------------------------- */
1822 122814 : empty();
1823 122814 : *pbIsEmpty = false;
1824 :
1825 : /* -------------------------------------------------------------------- */
1826 : /* Read and verify the type keyword, and ensure it matches the */
1827 : /* actual type of this container. */
1828 : /* -------------------------------------------------------------------- */
1829 122814 : bool bHasM = false;
1830 122814 : bool bHasZ = false;
1831 122814 : bool bAlreadyGotDimension = false;
1832 :
1833 122814 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1834 122814 : pszInput = OGRWktReadToken(pszInput, szToken);
1835 122814 : if (szToken[0] != '\0')
1836 : {
1837 : // Postgis EWKT: POINTM instead of POINT M.
1838 : // Current QGIS versions (at least <= 3.38) also export POINTZ.
1839 122814 : const size_t nTokenLen = strlen(szToken);
1840 122814 : if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
1841 : {
1842 11 : szToken[nTokenLen - 1] = '\0';
1843 11 : bHasM = true;
1844 11 : bAlreadyGotDimension = true;
1845 :
1846 11 : if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
1847 9 : szToken[nTokenLen - 2] == 'z'))
1848 : {
1849 4 : bHasZ = true;
1850 4 : szToken[nTokenLen - 2] = '\0';
1851 : }
1852 : }
1853 122803 : else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
1854 : {
1855 5 : szToken[nTokenLen - 1] = '\0';
1856 5 : bHasZ = true;
1857 5 : bAlreadyGotDimension = true;
1858 : }
1859 : }
1860 :
1861 122814 : if (!EQUAL(szToken, getGeometryName()))
1862 0 : return OGRERR_CORRUPT_DATA;
1863 :
1864 : /* -------------------------------------------------------------------- */
1865 : /* Check for Z, M or ZM */
1866 : /* -------------------------------------------------------------------- */
1867 122814 : if (!bAlreadyGotDimension)
1868 : {
1869 122798 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1870 122798 : if (EQUAL(szToken, "Z"))
1871 : {
1872 1373 : pszInput = pszNewInput;
1873 1373 : bHasZ = true;
1874 : }
1875 121425 : else if (EQUAL(szToken, "M"))
1876 : {
1877 336 : pszInput = pszNewInput;
1878 336 : bHasM = true;
1879 : }
1880 121089 : else if (EQUAL(szToken, "ZM"))
1881 : {
1882 491 : pszInput = pszNewInput;
1883 491 : bHasZ = true;
1884 491 : bHasM = true;
1885 : }
1886 : }
1887 122814 : *pbHasZ = bHasZ;
1888 122814 : *pbHasM = bHasM;
1889 :
1890 : /* -------------------------------------------------------------------- */
1891 : /* Check for EMPTY ... */
1892 : /* -------------------------------------------------------------------- */
1893 122814 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1894 122814 : if (EQUAL(szToken, "EMPTY"))
1895 : {
1896 1542 : *ppszInput = pszNewInput;
1897 1542 : *pbIsEmpty = true;
1898 1542 : if (bHasZ)
1899 130 : set3D(TRUE);
1900 1542 : if (bHasM)
1901 84 : setMeasured(TRUE);
1902 1542 : return OGRERR_NONE;
1903 : }
1904 :
1905 121272 : if (!EQUAL(szToken, "("))
1906 35 : return OGRERR_CORRUPT_DATA;
1907 :
1908 121237 : if (!bHasZ && !bHasM)
1909 : {
1910 : // Test for old-style XXXXXXXXX(EMPTY).
1911 119194 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1912 119194 : if (EQUAL(szToken, "EMPTY"))
1913 : {
1914 69 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1915 :
1916 69 : if (EQUAL(szToken, ","))
1917 : {
1918 : // This is OK according to SFSQL SPEC.
1919 : }
1920 44 : else if (!EQUAL(szToken, ")"))
1921 : {
1922 9 : return OGRERR_CORRUPT_DATA;
1923 : }
1924 : else
1925 : {
1926 35 : *ppszInput = pszNewInput;
1927 35 : empty();
1928 35 : *pbIsEmpty = true;
1929 35 : return OGRERR_NONE;
1930 : }
1931 : }
1932 : }
1933 :
1934 121193 : *ppszInput = pszInput;
1935 :
1936 121193 : return OGRERR_NONE;
1937 : }
1938 :
1939 : //! @endcond
1940 :
1941 : /************************************************************************/
1942 : /* wktTypeString() */
1943 : /************************************************************************/
1944 :
1945 : //! @cond Doxygen_Suppress
1946 : /** Get a type string for WKT, padded with a space at the end.
1947 : *
1948 : * @param variant OGR type variant
1949 : * @return "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
1950 : */
1951 14118 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1952 : {
1953 14118 : std::string s(" ");
1954 :
1955 14118 : if (variant == wkbVariantIso)
1956 : {
1957 9296 : if (flags & OGR_G_3D)
1958 1947 : s += "Z";
1959 9296 : if (flags & OGR_G_MEASURED)
1960 1184 : s += "M";
1961 : }
1962 14118 : if (s.size() > 1)
1963 2501 : s += " ";
1964 14118 : return s;
1965 : }
1966 :
1967 : //! @endcond
1968 :
1969 : /**
1970 : * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
1971 : * OGRwkbVariant variant = wkbVariantOldOgc ) const;
1972 : *
1973 : * \brief Convert a geometry into well known text format.
1974 : *
1975 : * This method relates to the SFCOM IWks::ExportToWKT() method.
1976 : *
1977 : * This method is the same as the C function OGR_G_ExportToWkt().
1978 : *
1979 : * @param ppszDstText a text buffer is allocated by the program, and assigned
1980 : * to the passed pointer. After use, *ppszDstText should be
1981 : * freed with CPLFree().
1982 : * @param variant the specification that must be conformed too :
1983 : * - wkbVariantOgc for old-style 99-402 extended
1984 : * dimension (Z) WKB types
1985 : * - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
1986 : *
1987 : * @return Currently OGRERR_NONE is always returned.
1988 : */
1989 8810 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1990 : {
1991 8810 : OGRWktOptions opts;
1992 8810 : opts.variant = variant;
1993 8810 : OGRErr err(OGRERR_NONE);
1994 :
1995 8810 : std::string wkt = exportToWkt(opts, &err);
1996 8810 : *ppszDstText = CPLStrdup(wkt.data());
1997 17620 : return err;
1998 : }
1999 :
2000 : /************************************************************************/
2001 : /* OGR_G_ExportToWkt() */
2002 : /************************************************************************/
2003 :
2004 : /**
2005 : * \brief Convert a geometry into well known text format.
2006 : *
2007 : * This function relates to the SFCOM IWks::ExportToWKT() method.
2008 : *
2009 : * For backward compatibility purposes, it exports the Old-style 99-402
2010 : * extended dimension (Z) WKB types for types Point, LineString, Polygon,
2011 : * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
2012 : * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
2013 : *
2014 : * This function is the same as the CPP method OGRGeometry::exportToWkt().
2015 : *
2016 : * @param hGeom handle on the geometry to convert to a text format from.
2017 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
2018 : * to the passed pointer. After use, *ppszDstText should be
2019 : * freed with CPLFree().
2020 : *
2021 : * @return Currently OGRERR_NONE is always returned.
2022 : */
2023 :
2024 2521 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
2025 :
2026 : {
2027 2521 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2028 :
2029 2521 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
2030 : }
2031 :
2032 : /************************************************************************/
2033 : /* OGR_G_ExportToIsoWkt() */
2034 : /************************************************************************/
2035 :
2036 : /**
2037 : * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
2038 : * known text format.
2039 : *
2040 : * This function relates to the SFCOM IWks::ExportToWKT() method.
2041 : * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
2042 : * (Z&M) WKB types.
2043 : *
2044 : * This function is the same as the CPP method
2045 : * OGRGeometry::exportToWkt(wkbVariantIso).
2046 : *
2047 : * @param hGeom handle on the geometry to convert to a text format from.
2048 : * @param ppszSrcText a text buffer is allocated by the program, and assigned
2049 : * to the passed pointer. After use, *ppszDstText should be
2050 : * freed with CPLFree().
2051 : *
2052 : * @return Currently OGRERR_NONE is always returned.
2053 : *
2054 : * @since GDAL 2.0
2055 : */
2056 :
2057 5466 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
2058 :
2059 : {
2060 5466 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2061 :
2062 5466 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2063 5466 : wkbVariantIso);
2064 : }
2065 :
2066 : /**
2067 : * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
2068 : *
2069 : * \brief Fetch geometry type.
2070 : *
2071 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2072 : * flattened version of the geometry type apply the wkbFlatten() macro
2073 : * to the return result.
2074 : *
2075 : * This method is the same as the C function OGR_G_GetGeometryType().
2076 : *
2077 : * @return the geometry type code.
2078 : */
2079 :
2080 : /************************************************************************/
2081 : /* OGR_G_GetGeometryType() */
2082 : /************************************************************************/
2083 : /**
2084 : * \brief Fetch geometry type.
2085 : *
2086 : * Note that the geometry type may include the 2.5D flag. To get a 2D
2087 : * flattened version of the geometry type apply the wkbFlatten() macro
2088 : * to the return result.
2089 : *
2090 : * This function is the same as the CPP method OGRGeometry::getGeometryType().
2091 : *
2092 : * @param hGeom handle on the geometry to get type from.
2093 : * @return the geometry type code.
2094 : */
2095 :
2096 5063 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2097 :
2098 : {
2099 5063 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2100 :
2101 5063 : return OGRGeometry::FromHandle(hGeom)->getGeometryType();
2102 : }
2103 :
2104 : /**
2105 : * \fn const char * OGRGeometry::getGeometryName() const;
2106 : *
2107 : * \brief Fetch WKT name for geometry type.
2108 : *
2109 : * There is no SFCOM analog to this method.
2110 : *
2111 : * This method is the same as the C function OGR_G_GetGeometryName().
2112 : *
2113 : * @return name used for this geometry type in well known text format. The
2114 : * returned pointer is to a static internal string and should not be modified
2115 : * or freed.
2116 : */
2117 :
2118 : /************************************************************************/
2119 : /* OGR_G_GetGeometryName() */
2120 : /************************************************************************/
2121 : /**
2122 : * \brief Fetch WKT name for geometry type.
2123 : *
2124 : * There is no SFCOM analog to this function.
2125 : *
2126 : * This function is the same as the CPP method OGRGeometry::getGeometryName().
2127 : *
2128 : * @param hGeom handle on the geometry to get name from.
2129 : * @return name used for this geometry type in well known text format.
2130 : */
2131 :
2132 16549 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2133 :
2134 : {
2135 16549 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2136 :
2137 16549 : return OGRGeometry::FromHandle(hGeom)->getGeometryName();
2138 : }
2139 :
2140 : /**
2141 : * \fn OGRGeometry *OGRGeometry::clone() const;
2142 : *
2143 : * \brief Make a copy of this object.
2144 : *
2145 : * This method relates to the SFCOM IGeometry::clone() method.
2146 : *
2147 : * This method is the same as the C function OGR_G_Clone().
2148 : *
2149 : * @return a new object instance with the same geometry, and spatial
2150 : * reference system as the original.
2151 : */
2152 :
2153 : /************************************************************************/
2154 : /* OGR_G_Clone() */
2155 : /************************************************************************/
2156 : /**
2157 : * \brief Make a copy of this object.
2158 : *
2159 : * This function relates to the SFCOM IGeometry::clone() method.
2160 : *
2161 : * This function is the same as the CPP method OGRGeometry::clone().
2162 : *
2163 : * @param hGeom handle on the geometry to clone from.
2164 : * @return a handle on the copy of the geometry with the spatial
2165 : * reference system as the original.
2166 : */
2167 :
2168 13516 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
2169 :
2170 : {
2171 13516 : VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
2172 :
2173 13516 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
2174 : }
2175 :
2176 : /**
2177 : * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
2178 : *
2179 : * \brief Returns spatial reference system for object.
2180 : *
2181 : * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
2182 : *
2183 : * This method is the same as the C function OGR_G_GetSpatialReference().
2184 : *
2185 : * @return a reference to the spatial reference object. The object may be
2186 : * shared with many geometry objects, and should not be modified.
2187 : */
2188 :
2189 : /************************************************************************/
2190 : /* OGR_G_GetSpatialReference() */
2191 : /************************************************************************/
2192 : /**
2193 : * \brief Returns spatial reference system for geometry.
2194 : *
2195 : * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
2196 : *
2197 : * This function is the same as the CPP method
2198 : * OGRGeometry::getSpatialReference().
2199 : *
2200 : * @param hGeom handle on the geometry to get spatial reference from.
2201 : * @return a reference to the spatial reference geometry, which should not be
2202 : * modified.
2203 : */
2204 :
2205 63 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
2206 :
2207 : {
2208 63 : VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
2209 :
2210 63 : return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
2211 126 : OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
2212 : }
2213 :
2214 : /**
2215 : * \fn void OGRGeometry::empty();
2216 : *
2217 : * \brief Clear geometry information.
2218 : * This restores the geometry to its initial
2219 : * state after construction, and before assignment of actual geometry.
2220 : *
2221 : * This method relates to the SFCOM IGeometry::Empty() method.
2222 : *
2223 : * This method is the same as the C function OGR_G_Empty().
2224 : */
2225 :
2226 : /************************************************************************/
2227 : /* OGR_G_Empty() */
2228 : /************************************************************************/
2229 : /**
2230 : * \brief Clear geometry information.
2231 : * This restores the geometry to its initial
2232 : * state after construction, and before assignment of actual geometry.
2233 : *
2234 : * This function relates to the SFCOM IGeometry::Empty() method.
2235 : *
2236 : * This function is the same as the CPP method OGRGeometry::empty().
2237 : *
2238 : * @param hGeom handle on the geometry to empty.
2239 : */
2240 :
2241 4 : void OGR_G_Empty(OGRGeometryH hGeom)
2242 :
2243 : {
2244 4 : VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
2245 :
2246 4 : OGRGeometry::FromHandle(hGeom)->empty();
2247 : }
2248 :
2249 : /**
2250 : * \fn OGRBoolean OGRGeometry::IsEmpty() const;
2251 : *
2252 : * \brief Returns TRUE (non-zero) if the object has no points.
2253 : *
2254 : * Normally this
2255 : * returns FALSE except between when an object is instantiated and points
2256 : * have been assigned.
2257 : *
2258 : * This method relates to the SFCOM IGeometry::IsEmpty() method.
2259 : *
2260 : * @return TRUE if object is empty, otherwise FALSE.
2261 : */
2262 :
2263 : /************************************************************************/
2264 : /* OGR_G_IsEmpty() */
2265 : /************************************************************************/
2266 :
2267 : /**
2268 : * \brief Test if the geometry is empty.
2269 : *
2270 : * This method is the same as the CPP method OGRGeometry::IsEmpty().
2271 : *
2272 : * @param hGeom The Geometry to test.
2273 : *
2274 : * @return TRUE if the geometry has no points, otherwise FALSE.
2275 : */
2276 :
2277 2169 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
2278 :
2279 : {
2280 2169 : VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
2281 :
2282 2169 : return OGRGeometry::FromHandle(hGeom)->IsEmpty();
2283 : }
2284 :
2285 : /************************************************************************/
2286 : /* IsValid() */
2287 : /************************************************************************/
2288 :
2289 : /**
2290 : * \brief Test if the geometry is valid.
2291 : *
2292 : * This method is the same as the C function OGR_G_IsValid().
2293 : *
2294 : * This method is built on the GEOS library, check it for the definition
2295 : * of the geometry operation.
2296 : * If OGR is built without the GEOS library, this method will always return
2297 : * FALSE.
2298 : *
2299 : *
2300 : * @return TRUE if the geometry has no points, otherwise FALSE.
2301 : */
2302 :
2303 2016 : OGRBoolean OGRGeometry::IsValid() const
2304 :
2305 : {
2306 2016 : if (IsSFCGALCompatible())
2307 : {
2308 : #ifndef HAVE_SFCGAL
2309 :
2310 : #ifdef HAVE_GEOS
2311 2 : if (wkbFlatten(getGeometryType()) == wkbTriangle)
2312 : {
2313 : // go on
2314 : }
2315 : else
2316 : #endif
2317 : {
2318 1 : CPLError(CE_Failure, CPLE_NotSupported,
2319 : "SFCGAL support not enabled.");
2320 1 : return FALSE;
2321 : }
2322 : #else
2323 : sfcgal_init();
2324 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
2325 : if (poThis == nullptr)
2326 : {
2327 : CPLError(CE_Failure, CPLE_IllegalArg,
2328 : "SFCGAL geometry returned is NULL");
2329 : return FALSE;
2330 : }
2331 :
2332 : const int res = sfcgal_geometry_is_valid(poThis);
2333 : sfcgal_geometry_delete(poThis);
2334 : return res == 1;
2335 : #endif
2336 : }
2337 :
2338 : {
2339 : #ifndef HAVE_GEOS
2340 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2341 : return FALSE;
2342 :
2343 : #else
2344 2023 : OGRBoolean bResult = FALSE;
2345 :
2346 : // Some invalid geometries, such as lines with one point, or
2347 : // rings that do not close, cannot be converted to GEOS.
2348 : // For validity checking we initialize the GEOS context with
2349 : // the warning handler as the error handler to avoid emitting
2350 : // CE_Failure when a geometry cannot be converted to GEOS.
2351 : GEOSContextHandle_t hGEOSCtxt =
2352 2023 : initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
2353 :
2354 2010 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2355 :
2356 2009 : if (hThisGeosGeom != nullptr)
2357 : {
2358 2017 : bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom);
2359 : #ifdef DEBUG_VERBOSE
2360 : if (!bResult)
2361 : {
2362 : char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
2363 : CPLDebug("OGR", "%s", pszReason);
2364 : GEOSFree_r(hGEOSCtxt, pszReason);
2365 : }
2366 : #endif
2367 2004 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2368 : }
2369 2003 : freeGEOSContext(hGEOSCtxt);
2370 :
2371 2041 : return bResult;
2372 :
2373 : #endif // HAVE_GEOS
2374 : }
2375 : }
2376 :
2377 : /************************************************************************/
2378 : /* OGR_G_IsValid() */
2379 : /************************************************************************/
2380 :
2381 : /**
2382 : * \brief Test if the geometry is valid.
2383 : *
2384 : * This function is the same as the C++ method OGRGeometry::IsValid().
2385 : *
2386 : * This function 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 function will always return
2389 : * FALSE.
2390 : *
2391 : * @param hGeom The Geometry to test.
2392 : *
2393 : * @return TRUE if the geometry has no points, otherwise FALSE.
2394 : */
2395 :
2396 19 : int OGR_G_IsValid(OGRGeometryH hGeom)
2397 :
2398 : {
2399 19 : VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
2400 :
2401 19 : return OGRGeometry::FromHandle(hGeom)->IsValid();
2402 : }
2403 :
2404 : /************************************************************************/
2405 : /* IsSimple() */
2406 : /************************************************************************/
2407 :
2408 : /**
2409 : * \brief Test if the geometry is simple.
2410 : *
2411 : * This method is the same as the C function OGR_G_IsSimple().
2412 : *
2413 : * This method is built on the GEOS library, check it for the definition
2414 : * of the geometry operation.
2415 : * If OGR is built without the GEOS library, this method will always return
2416 : * FALSE.
2417 : *
2418 : *
2419 : * @return TRUE if the geometry has no points, otherwise FALSE.
2420 : */
2421 :
2422 5 : OGRBoolean OGRGeometry::IsSimple() const
2423 :
2424 : {
2425 : #ifndef HAVE_GEOS
2426 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2427 : return FALSE;
2428 :
2429 : #else
2430 :
2431 5 : OGRBoolean bResult = FALSE;
2432 :
2433 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2434 5 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2435 :
2436 5 : if (hThisGeosGeom != nullptr)
2437 : {
2438 5 : bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom);
2439 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2440 : }
2441 5 : freeGEOSContext(hGEOSCtxt);
2442 :
2443 5 : return bResult;
2444 :
2445 : #endif // HAVE_GEOS
2446 : }
2447 :
2448 : /**
2449 : * \brief Returns TRUE if the geometry is simple.
2450 : *
2451 : * Returns TRUE if the geometry has no anomalous geometric points, such
2452 : * as self intersection or self tangency. The description of each
2453 : * instantiable geometric class will include the specific conditions that
2454 : * cause an instance of that class to be classified as not simple.
2455 : *
2456 : * This function is the same as the C++ method OGRGeometry::IsSimple() method.
2457 : *
2458 : * If OGR is built without the GEOS library, this function will always return
2459 : * FALSE.
2460 : *
2461 : * @param hGeom The Geometry to test.
2462 : *
2463 : * @return TRUE if object is simple, otherwise FALSE.
2464 : */
2465 :
2466 5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
2467 :
2468 : {
2469 5 : VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
2470 :
2471 5 : return OGRGeometry::FromHandle(hGeom)->IsSimple();
2472 : }
2473 :
2474 : /************************************************************************/
2475 : /* IsRing() */
2476 : /************************************************************************/
2477 :
2478 : /**
2479 : * \brief Test if the geometry is a ring
2480 : *
2481 : * This method is the same as the C function OGR_G_IsRing().
2482 : *
2483 : * This method is built on the GEOS library, check it for the definition
2484 : * of the geometry operation.
2485 : * If OGR is built without the GEOS library, this method will always return
2486 : * FALSE.
2487 : *
2488 : *
2489 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2490 : * length and closure (self-intersection is not checked), otherwise FALSE.
2491 : */
2492 :
2493 1 : OGRBoolean OGRGeometry::IsRing() const
2494 :
2495 : {
2496 : #ifndef HAVE_GEOS
2497 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2498 : return FALSE;
2499 :
2500 : #else
2501 :
2502 1 : OGRBoolean bResult = FALSE;
2503 :
2504 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2505 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2506 :
2507 1 : if (hThisGeosGeom != nullptr)
2508 : {
2509 1 : bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom);
2510 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2511 : }
2512 1 : freeGEOSContext(hGEOSCtxt);
2513 :
2514 1 : return bResult;
2515 :
2516 : #endif // HAVE_GEOS
2517 : }
2518 :
2519 : /************************************************************************/
2520 : /* OGR_G_IsRing() */
2521 : /************************************************************************/
2522 :
2523 : /**
2524 : * \brief Test if the geometry is a ring
2525 : *
2526 : * This function is the same as the C++ method OGRGeometry::IsRing().
2527 : *
2528 : * This function is built on the GEOS library, check it for the definition
2529 : * of the geometry operation.
2530 : * If OGR is built without the GEOS library, this function will always return
2531 : * FALSE.
2532 : *
2533 : * @param hGeom The Geometry to test.
2534 : *
2535 : * @return TRUE if the coordinates of the geometry form a ring, by checking
2536 : * length and closure (self-intersection is not checked), otherwise FALSE.
2537 : */
2538 :
2539 1 : int OGR_G_IsRing(OGRGeometryH hGeom)
2540 :
2541 : {
2542 1 : VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
2543 :
2544 1 : return OGRGeometry::FromHandle(hGeom)->IsRing();
2545 : }
2546 :
2547 : /************************************************************************/
2548 : /* OGRFromOGCGeomType() */
2549 : /************************************************************************/
2550 :
2551 : /** Map OGC geometry format type to corresponding OGR constants.
2552 : * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
2553 : * @return OGR constant.
2554 : */
2555 2812 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2556 : {
2557 2812 : OGRwkbGeometryType eType = wkbUnknown;
2558 2812 : bool bConvertTo3D = false;
2559 2812 : bool bIsMeasured = false;
2560 2812 : if (*pszGeomType != '\0')
2561 : {
2562 2806 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2563 2806 : if (ch == 'm' || ch == 'M')
2564 : {
2565 2 : bIsMeasured = true;
2566 2 : if (strlen(pszGeomType) > 1)
2567 2 : ch = pszGeomType[strlen(pszGeomType) - 2];
2568 : }
2569 2806 : if (ch == 'z' || ch == 'Z')
2570 : {
2571 33 : bConvertTo3D = true;
2572 : }
2573 : }
2574 :
2575 2812 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2576 735 : eType = wkbPoint;
2577 2077 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2578 184 : eType = wkbLineString;
2579 1893 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2580 736 : eType = wkbPolygon;
2581 1157 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2582 24 : eType = wkbMultiPoint;
2583 1133 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2584 13 : eType = wkbMultiLineString;
2585 1120 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2586 82 : eType = wkbMultiPolygon;
2587 1038 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2588 4 : eType = wkbGeometryCollection;
2589 1034 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2590 349 : eType = wkbCircularString;
2591 685 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2592 0 : eType = wkbCompoundCurve;
2593 685 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2594 16 : eType = wkbCurvePolygon;
2595 669 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2596 2 : eType = wkbMultiCurve;
2597 667 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2598 0 : eType = wkbMultiSurface;
2599 667 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2600 0 : eType = wkbTriangle;
2601 667 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2602 1 : eType = wkbPolyhedralSurface;
2603 666 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2604 5 : eType = wkbTIN;
2605 661 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2606 3 : eType = wkbCurve;
2607 658 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2608 3 : eType = wkbSurface;
2609 : else
2610 655 : eType = wkbUnknown;
2611 :
2612 2812 : if (bConvertTo3D)
2613 33 : eType = wkbSetZ(eType);
2614 2812 : if (bIsMeasured)
2615 2 : eType = wkbSetM(eType);
2616 :
2617 2812 : return eType;
2618 : }
2619 :
2620 : /************************************************************************/
2621 : /* OGRToOGCGeomType() */
2622 : /************************************************************************/
2623 :
2624 : /** Map OGR geometry format constants to corresponding OGC geometry type.
2625 : * @param eGeomType OGR geometry type
2626 : * @param bCamelCase Whether the return should be like "MultiPoint"
2627 : * (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
2628 : * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
2629 : * Default is false.
2630 : * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
2631 : * suffix. Default is false.
2632 : * @return string with OGC geometry type (without dimensionality)
2633 : */
2634 2734 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2635 : bool bAddZM, bool bSpaceBeforeZM)
2636 : {
2637 2734 : const char *pszRet = "";
2638 2734 : switch (wkbFlatten(eGeomType))
2639 : {
2640 1487 : case wkbUnknown:
2641 1487 : pszRet = "Geometry";
2642 1487 : break;
2643 449 : case wkbPoint:
2644 449 : pszRet = "Point";
2645 449 : break;
2646 146 : case wkbLineString:
2647 146 : pszRet = "LineString";
2648 146 : break;
2649 317 : case wkbPolygon:
2650 317 : pszRet = "Polygon";
2651 317 : break;
2652 48 : case wkbMultiPoint:
2653 48 : pszRet = "MultiPoint";
2654 48 : break;
2655 56 : case wkbMultiLineString:
2656 56 : pszRet = "MultiLineString";
2657 56 : break;
2658 74 : case wkbMultiPolygon:
2659 74 : pszRet = "MultiPolygon";
2660 74 : break;
2661 54 : case wkbGeometryCollection:
2662 54 : pszRet = "GeometryCollection";
2663 54 : break;
2664 9 : case wkbCircularString:
2665 9 : pszRet = "CircularString";
2666 9 : break;
2667 3 : case wkbCompoundCurve:
2668 3 : pszRet = "CompoundCurve";
2669 3 : break;
2670 12 : case wkbCurvePolygon:
2671 12 : pszRet = "CurvePolygon";
2672 12 : break;
2673 2 : case wkbMultiCurve:
2674 2 : pszRet = "MultiCurve";
2675 2 : break;
2676 3 : case wkbMultiSurface:
2677 3 : pszRet = "MultiSurface";
2678 3 : break;
2679 3 : case wkbTriangle:
2680 3 : pszRet = "Triangle";
2681 3 : break;
2682 5 : case wkbPolyhedralSurface:
2683 5 : pszRet = "PolyhedralSurface";
2684 5 : break;
2685 1 : case wkbTIN:
2686 1 : pszRet = "Tin";
2687 1 : break;
2688 3 : case wkbCurve:
2689 3 : pszRet = "Curve";
2690 3 : break;
2691 3 : case wkbSurface:
2692 3 : pszRet = "Surface";
2693 3 : break;
2694 59 : default:
2695 59 : break;
2696 : }
2697 2734 : if (bAddZM)
2698 : {
2699 45 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2700 45 : const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2701 45 : if (bHasZ || bHasM)
2702 : {
2703 10 : if (bSpaceBeforeZM)
2704 1 : pszRet = CPLSPrintf("%s ", pszRet);
2705 10 : if (bHasZ)
2706 9 : pszRet = CPLSPrintf("%sZ", pszRet);
2707 10 : if (bHasM)
2708 5 : pszRet = CPLSPrintf("%sM", pszRet);
2709 : }
2710 : }
2711 2734 : if (!bCamelCase)
2712 2688 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2713 2734 : return pszRet;
2714 : }
2715 :
2716 : /************************************************************************/
2717 : /* OGRGeometryTypeToName() */
2718 : /************************************************************************/
2719 :
2720 : /**
2721 : * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
2722 : * value. The returned value should not be modified, or freed by the
2723 : * application.
2724 : *
2725 : * This function is C callable.
2726 : *
2727 : * @param eType the geometry type.
2728 : *
2729 : * @return internal human readable string, or NULL on failure.
2730 : */
2731 :
2732 386 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2733 :
2734 : {
2735 386 : bool b3D = wkbHasZ(eType);
2736 386 : bool bMeasured = wkbHasM(eType);
2737 :
2738 386 : switch (wkbFlatten(eType))
2739 : {
2740 34 : case wkbUnknown:
2741 34 : if (b3D && bMeasured)
2742 0 : return "3D Measured Unknown (any)";
2743 34 : else if (b3D)
2744 1 : return "3D Unknown (any)";
2745 33 : else if (bMeasured)
2746 0 : return "Measured Unknown (any)";
2747 : else
2748 33 : return "Unknown (any)";
2749 :
2750 53 : case wkbPoint:
2751 53 : if (b3D && bMeasured)
2752 2 : return "3D Measured Point";
2753 51 : else if (b3D)
2754 11 : return "3D Point";
2755 40 : else if (bMeasured)
2756 2 : return "Measured Point";
2757 : else
2758 38 : return "Point";
2759 :
2760 37 : case wkbLineString:
2761 37 : if (b3D && bMeasured)
2762 0 : return "3D Measured Line String";
2763 37 : else if (b3D)
2764 9 : return "3D Line String";
2765 28 : else if (bMeasured)
2766 0 : return "Measured Line String";
2767 : else
2768 28 : return "Line String";
2769 :
2770 61 : case wkbPolygon:
2771 61 : if (b3D && bMeasured)
2772 0 : return "3D Measured Polygon";
2773 61 : else if (b3D)
2774 8 : return "3D Polygon";
2775 53 : else if (bMeasured)
2776 0 : return "Measured Polygon";
2777 : else
2778 53 : return "Polygon";
2779 :
2780 19 : case wkbMultiPoint:
2781 19 : if (b3D && bMeasured)
2782 0 : return "3D Measured Multi Point";
2783 19 : else if (b3D)
2784 9 : return "3D Multi Point";
2785 10 : else if (bMeasured)
2786 0 : return "Measured Multi Point";
2787 : else
2788 10 : return "Multi Point";
2789 :
2790 14 : case wkbMultiLineString:
2791 14 : if (b3D && bMeasured)
2792 0 : return "3D Measured Multi Line String";
2793 14 : else if (b3D)
2794 6 : return "3D Multi Line String";
2795 8 : else if (bMeasured)
2796 0 : return "Measured Multi Line String";
2797 : else
2798 8 : return "Multi Line String";
2799 :
2800 19 : case wkbMultiPolygon:
2801 19 : if (b3D && bMeasured)
2802 0 : return "3D Measured Multi Polygon";
2803 19 : else if (b3D)
2804 8 : return "3D Multi Polygon";
2805 11 : else if (bMeasured)
2806 0 : return "Measured Multi Polygon";
2807 : else
2808 11 : return "Multi Polygon";
2809 :
2810 25 : case wkbGeometryCollection:
2811 25 : if (b3D && bMeasured)
2812 0 : return "3D Measured Geometry Collection";
2813 25 : else if (b3D)
2814 10 : return "3D Geometry Collection";
2815 15 : else if (bMeasured)
2816 0 : return "Measured Geometry Collection";
2817 : else
2818 15 : return "Geometry Collection";
2819 :
2820 0 : case wkbCircularString:
2821 0 : if (b3D && bMeasured)
2822 0 : return "3D Measured Circular String";
2823 0 : else if (b3D)
2824 0 : return "3D Circular String";
2825 0 : else if (bMeasured)
2826 0 : return "Measured Circular String";
2827 : else
2828 0 : return "Circular String";
2829 :
2830 1 : case wkbCompoundCurve:
2831 1 : if (b3D && bMeasured)
2832 0 : return "3D Measured Compound Curve";
2833 1 : else if (b3D)
2834 0 : return "3D Compound Curve";
2835 1 : else if (bMeasured)
2836 0 : return "Measured Compound Curve";
2837 : else
2838 1 : return "Compound Curve";
2839 :
2840 0 : case wkbCurvePolygon:
2841 0 : if (b3D && bMeasured)
2842 0 : return "3D Measured Curve Polygon";
2843 0 : else if (b3D)
2844 0 : return "3D Curve Polygon";
2845 0 : else if (bMeasured)
2846 0 : return "Measured Curve Polygon";
2847 : else
2848 0 : return "Curve Polygon";
2849 :
2850 0 : case wkbMultiCurve:
2851 0 : if (b3D && bMeasured)
2852 0 : return "3D Measured Multi Curve";
2853 0 : else if (b3D)
2854 0 : return "3D Multi Curve";
2855 0 : else if (bMeasured)
2856 0 : return "Measured Multi Curve";
2857 : else
2858 0 : return "Multi Curve";
2859 :
2860 0 : case wkbMultiSurface:
2861 0 : if (b3D && bMeasured)
2862 0 : return "3D Measured Multi Surface";
2863 0 : else if (b3D)
2864 0 : return "3D Multi Surface";
2865 0 : else if (bMeasured)
2866 0 : return "Measured Multi Surface";
2867 : else
2868 0 : return "Multi Surface";
2869 :
2870 4 : case wkbCurve:
2871 4 : if (b3D && bMeasured)
2872 1 : return "3D Measured Curve";
2873 3 : else if (b3D)
2874 1 : return "3D Curve";
2875 2 : else if (bMeasured)
2876 1 : return "Measured Curve";
2877 : else
2878 1 : return "Curve";
2879 :
2880 4 : case wkbSurface:
2881 4 : if (b3D && bMeasured)
2882 1 : return "3D Measured Surface";
2883 3 : else if (b3D)
2884 1 : return "3D Surface";
2885 2 : else if (bMeasured)
2886 1 : return "Measured Surface";
2887 : else
2888 1 : return "Surface";
2889 :
2890 0 : case wkbTriangle:
2891 0 : if (b3D && bMeasured)
2892 0 : return "3D Measured Triangle";
2893 0 : else if (b3D)
2894 0 : return "3D Triangle";
2895 0 : else if (bMeasured)
2896 0 : return "Measured Triangle";
2897 : else
2898 0 : return "Triangle";
2899 :
2900 0 : case wkbPolyhedralSurface:
2901 0 : if (b3D && bMeasured)
2902 0 : return "3D Measured PolyhedralSurface";
2903 0 : else if (b3D)
2904 0 : return "3D PolyhedralSurface";
2905 0 : else if (bMeasured)
2906 0 : return "Measured PolyhedralSurface";
2907 : else
2908 0 : return "PolyhedralSurface";
2909 :
2910 2 : case wkbTIN:
2911 2 : if (b3D && bMeasured)
2912 0 : return "3D Measured TIN";
2913 2 : else if (b3D)
2914 0 : return "3D TIN";
2915 2 : else if (bMeasured)
2916 0 : return "Measured TIN";
2917 : else
2918 2 : return "TIN";
2919 :
2920 112 : case wkbNone:
2921 112 : return "None";
2922 :
2923 1 : default:
2924 : {
2925 1 : return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
2926 : }
2927 : }
2928 : }
2929 :
2930 : /************************************************************************/
2931 : /* OGRMergeGeometryTypes() */
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 : * @param eMain the first input geometry type.
2949 : * @param eExtra the second input geometry type.
2950 : *
2951 : * @return the merged geometry type.
2952 : */
2953 :
2954 0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
2955 : OGRwkbGeometryType eExtra)
2956 :
2957 : {
2958 0 : return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
2959 : }
2960 :
2961 : /**
2962 : * \brief Find common geometry type.
2963 : *
2964 : * Given two geometry types, find the most specific common
2965 : * type. Normally used repeatedly with the geometries in a
2966 : * layer to try and establish the most specific geometry type
2967 : * that can be reported for the layer.
2968 : *
2969 : * NOTE: wkbUnknown is the "worst case" indicating a mixture of
2970 : * geometry types with nothing in common but the base geometry
2971 : * type. wkbNone should be used to indicate that no geometries
2972 : * have been encountered yet, and means the first geometry
2973 : * encountered will establish the preliminary type.
2974 : *
2975 : * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
2976 : * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
2977 : * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
2978 : * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
2979 : *
2980 : * @param eMain the first input geometry type.
2981 : * @param eExtra the second input geometry type.
2982 : * @param bAllowPromotingToCurves determine if promotion to curve type
2983 : * must be done.
2984 : *
2985 : * @return the merged geometry type.
2986 : *
2987 : * @since GDAL 2.0
2988 : */
2989 :
2990 573 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
2991 : OGRwkbGeometryType eExtra,
2992 : int bAllowPromotingToCurves)
2993 :
2994 : {
2995 573 : OGRwkbGeometryType eFMain = wkbFlatten(eMain);
2996 573 : OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
2997 :
2998 573 : const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
2999 573 : const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
3000 :
3001 573 : if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
3002 17 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3003 :
3004 556 : if (eFMain == wkbNone)
3005 2 : return eExtra;
3006 :
3007 554 : if (eFExtra == wkbNone)
3008 0 : return eMain;
3009 :
3010 554 : if (eFMain == eFExtra)
3011 : {
3012 537 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3013 : }
3014 :
3015 17 : if (bAllowPromotingToCurves)
3016 : {
3017 17 : if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
3018 4 : return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
3019 :
3020 13 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3021 3 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3022 :
3023 10 : if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3024 6 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3025 : }
3026 :
3027 : // One is subclass of the other one
3028 4 : if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3029 : {
3030 0 : return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3031 : }
3032 4 : else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3033 : {
3034 0 : return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3035 : }
3036 :
3037 : // Nothing apparently in common.
3038 4 : return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3039 : }
3040 :
3041 : /**
3042 : * \fn void OGRGeometry::flattenTo2D();
3043 : *
3044 : * \brief Convert geometry to strictly 2D.
3045 : * In a sense this converts all Z coordinates
3046 : * to 0.0.
3047 : *
3048 : * This method is the same as the C function OGR_G_FlattenTo2D().
3049 : */
3050 :
3051 : /************************************************************************/
3052 : /* OGR_G_FlattenTo2D() */
3053 : /************************************************************************/
3054 : /**
3055 : * \brief Convert geometry to strictly 2D.
3056 : * In a sense this converts all Z coordinates
3057 : * to 0.0.
3058 : *
3059 : * This function is the same as the CPP method OGRGeometry::flattenTo2D().
3060 : *
3061 : * @param hGeom handle on the geometry to convert.
3062 : */
3063 :
3064 31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
3065 :
3066 : {
3067 31 : OGRGeometry::FromHandle(hGeom)->flattenTo2D();
3068 31 : }
3069 :
3070 : /************************************************************************/
3071 : /* exportToGML() */
3072 : /************************************************************************/
3073 :
3074 : /**
3075 : * \fn char *OGRGeometry::exportToGML( const char* const *
3076 : * papszOptions = NULL ) const;
3077 : *
3078 : * \brief Convert a geometry into GML format.
3079 : *
3080 : * The GML geometry is expressed directly in terms of GML basic data
3081 : * types assuming the this is available in the gml namespace. The returned
3082 : * string should be freed with CPLFree() when no longer required.
3083 : *
3084 : * The supported options are :
3085 : * <ul>
3086 : * <li> FORMAT=GML2/GML3/GML32 (GML2 or GML32 added in GDAL 2.1).
3087 : * If not set, it will default to GML 2.1.2 output.
3088 : * </li>
3089 : * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
3090 : * To use gml:Curve element for linestrings.
3091 : * Otherwise gml:LineString will be used .
3092 : * </li>
3093 : * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
3094 : * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES.
3095 : * If YES, SRS with EPSG authority will be written with the
3096 : * "urn:ogc:def:crs:EPSG::" prefix.
3097 : * In the case the SRS should be treated as lat/long or
3098 : * northing/easting, then the function will take care of coordinate order
3099 : * swapping if the data axis to CRS axis mapping indicates it.
3100 : * If set to NO, SRS with EPSG authority will be written with the "EPSG:"
3101 : * prefix, even if they are in lat/long order.
3102 : * </li>
3103 : * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3, added
3104 : * in GDAL 2.2). Defaults to OGC_URN. If SHORT, then srsName will be in
3105 : * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
3106 : * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
3107 : * then srsName will be in the form
3108 : * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
3109 : * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
3110 : * or northing/easting, then the function will take care of coordinate
3111 : * order swapping if the data axis to CRS axis mapping indicates it.
3112 : * </li>
3113 : * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
3114 : * top-level geometry element with the provided value.
3115 : * Required for GML 3.2 compatibility.
3116 : * </li>
3117 : * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
3118 : * FORMAT=GML3/GML32, GDAL >= 2.0) Default to POSLIST.
3119 : * For 2.5D geometries, define the location where to attach the
3120 : * srsDimension attribute.
3121 : * There are diverging implementations. Some put in on the
3122 : * <gml:posList> element, other on the top geometry element.
3123 : * </li>
3124 : * <li> NAMESPACE_DECL=YES/NO. If set to YES,
3125 : * xmlns:gml="http://www.opengis.net/gml" will be added to the root node
3126 : * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
3127 : * </li>
3128 : * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
3129 : * Resolution for the coordinate precision of the X and Y coordinates.
3130 : * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
3131 : * to 5 decimal digits. 0 for the default behavior.
3132 : * </li>
3133 : * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
3134 : * Resolution for the coordinate precision of the Z coordinates.
3135 : * Expressed in the units of the Z axis of the SRS.
3136 : * 0 for the default behavior.
3137 : * </li>
3138 : * </ul>
3139 : *
3140 : * This method is the same as the C function OGR_G_ExportToGMLEx().
3141 : *
3142 : * @param papszOptions NULL-terminated list of options.
3143 : * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
3144 : */
3145 :
3146 251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
3147 : {
3148 251 : return OGR_G_ExportToGMLEx(
3149 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3150 251 : const_cast<char **>(papszOptions));
3151 : }
3152 :
3153 : /************************************************************************/
3154 : /* exportToKML() */
3155 : /************************************************************************/
3156 :
3157 : /**
3158 : * \fn char *OGRGeometry::exportToKML() const;
3159 : *
3160 : * \brief Convert a geometry into KML format.
3161 : *
3162 : * The returned string should be freed with CPLFree() when no longer required.
3163 : *
3164 : * This method is the same as the C function OGR_G_ExportToKML().
3165 : *
3166 : * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
3167 : */
3168 :
3169 0 : char *OGRGeometry::exportToKML() const
3170 : {
3171 0 : return OGR_G_ExportToKML(
3172 0 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
3173 : }
3174 :
3175 : /************************************************************************/
3176 : /* exportToJson() */
3177 : /************************************************************************/
3178 :
3179 : /**
3180 : * \fn char *OGRGeometry::exportToJson() const;
3181 : *
3182 : * \brief Convert a geometry into GeoJSON format.
3183 : *
3184 : * The returned string should be freed with CPLFree() when no longer required.
3185 : *
3186 : * The following options are supported :
3187 : * <ul>
3188 : * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
3189 : * (added in GDAL 3.9)</li>
3190 : * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
3191 : * (added in GDAL 3.9)</li>
3192 : * </ul>
3193 : *
3194 : * This method is the same as the C function OGR_G_ExportToJson().
3195 : *
3196 : * @param papszOptions Null terminated list of options, or null (added in 3.9)
3197 : * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
3198 : */
3199 :
3200 42 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
3201 : {
3202 42 : OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3203 42 : return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3204 42 : const_cast<char **>(papszOptions));
3205 : }
3206 :
3207 : /************************************************************************/
3208 : /* OGRSetGenerate_DB2_V72_BYTE_ORDER() */
3209 : /************************************************************************/
3210 :
3211 : /**
3212 : * \brief Special entry point to enable the hack for generating DB2 V7.2 style
3213 : * WKB.
3214 : *
3215 : * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
3216 : * order in WKB. This entry point is used to turn on or off the generation of
3217 : * such WKB.
3218 : */
3219 4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
3220 :
3221 : {
3222 : #if defined(HACK_FOR_IBM_DB2_V72)
3223 4 : OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
3224 4 : return OGRERR_NONE;
3225 : #else
3226 : if (bGenerate_DB2_V72_BYTE_ORDER)
3227 : return OGRERR_FAILURE;
3228 : else
3229 : return OGRERR_NONE;
3230 : #endif
3231 : }
3232 :
3233 : /************************************************************************/
3234 : /* OGRGetGenerate_DB2_V72_BYTE_ORDER() */
3235 : /* */
3236 : /* This is a special entry point to get the value of static flag */
3237 : /* OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER. */
3238 : /************************************************************************/
3239 0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
3240 : {
3241 0 : return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
3242 : }
3243 :
3244 : /************************************************************************/
3245 : /* createGEOSContext() */
3246 : /************************************************************************/
3247 :
3248 : /** Create a new GEOS context.
3249 : * @return a new GEOS context (to be freed with freeGEOSContext())
3250 : */
3251 77787 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
3252 : {
3253 : #ifndef HAVE_GEOS
3254 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3255 : return nullptr;
3256 : #else
3257 77787 : return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
3258 : #endif
3259 : }
3260 :
3261 : /************************************************************************/
3262 : /* freeGEOSContext() */
3263 : /************************************************************************/
3264 :
3265 : /** Destroy a GEOS context.
3266 : * @param hGEOSCtxt GEOS context
3267 : */
3268 79745 : void OGRGeometry::freeGEOSContext(
3269 : UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt)
3270 : {
3271 : #ifdef HAVE_GEOS
3272 79745 : if (hGEOSCtxt != nullptr)
3273 : {
3274 79764 : finishGEOS_r(hGEOSCtxt);
3275 : }
3276 : #endif
3277 79763 : }
3278 :
3279 : #ifdef HAVE_GEOS
3280 :
3281 : /************************************************************************/
3282 : /* convertToGEOSGeom() */
3283 : /************************************************************************/
3284 :
3285 230080 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3286 : OGRGeometry *poGeom)
3287 : {
3288 230080 : GEOSGeom hGeom = nullptr;
3289 230080 : const size_t nDataSize = poGeom->WkbSize();
3290 : unsigned char *pabyData =
3291 230092 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3292 : #if GEOS_VERSION_MAJOR > 3 || \
3293 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3294 230110 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3295 : #else
3296 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3297 : #endif
3298 230110 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3299 229181 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3300 230073 : CPLFree(pabyData);
3301 230090 : return hGeom;
3302 : }
3303 : #endif
3304 :
3305 : /************************************************************************/
3306 : /* exportToGEOS() */
3307 : /************************************************************************/
3308 :
3309 : /** Returns a GEOSGeom object corresponding to the geometry.
3310 : *
3311 : * @param hGEOSCtxt GEOS context
3312 : * @param bRemoveEmptyParts Whether empty parts of the geometry should be
3313 : * removed before exporting to GEOS (GDAL >= 3.10)
3314 : * @return a GEOSGeom object corresponding to the geometry (to be freed with
3315 : * GEOSGeom_destroy_r()), or NULL in case of error
3316 : */
3317 : GEOSGeom
3318 230084 : OGRGeometry::exportToGEOS(UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt,
3319 : UNUSED_IF_NO_GEOS bool bRemoveEmptyParts) const
3320 :
3321 : {
3322 : #ifndef HAVE_GEOS
3323 :
3324 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3325 : return nullptr;
3326 :
3327 : #else
3328 :
3329 230084 : if (hGEOSCtxt == nullptr)
3330 0 : return nullptr;
3331 :
3332 230084 : const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
3333 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3334 : // POINT EMPTY is exported to WKB as if it were POINT(0 0),
3335 : // so that particular case is necessary.
3336 : if (eType == wkbPoint && IsEmpty())
3337 : {
3338 : return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
3339 : }
3340 : #endif
3341 :
3342 230088 : GEOSGeom hGeom = nullptr;
3343 :
3344 230088 : OGRGeometry *poLinearGeom = nullptr;
3345 230088 : if (hasCurveGeometry())
3346 : {
3347 858 : poLinearGeom = getLinearGeometry();
3348 858 : if (bRemoveEmptyParts)
3349 0 : poLinearGeom->removeEmptyParts();
3350 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3351 : // GEOS < 3.12 doesn't support M dimension
3352 : if (poLinearGeom->IsMeasured())
3353 : poLinearGeom->setMeasured(FALSE);
3354 : #endif
3355 : }
3356 : else
3357 : {
3358 229225 : poLinearGeom = const_cast<OGRGeometry *>(this);
3359 : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3360 : // GEOS < 3.12 doesn't support M dimension
3361 : if (IsMeasured())
3362 : {
3363 : poLinearGeom = clone();
3364 : if (bRemoveEmptyParts)
3365 : poLinearGeom->removeEmptyParts();
3366 : poLinearGeom->setMeasured(FALSE);
3367 : }
3368 : else
3369 : #endif
3370 229225 : if (bRemoveEmptyParts && hasEmptyParts())
3371 : {
3372 1 : poLinearGeom = clone();
3373 1 : poLinearGeom->removeEmptyParts();
3374 : }
3375 : }
3376 230088 : if (eType == wkbTriangle)
3377 : {
3378 97 : OGRPolygon oPolygon(*(poLinearGeom->toPolygon()));
3379 97 : hGeom = convertToGEOSGeom(hGEOSCtxt, &oPolygon);
3380 : }
3381 229991 : else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3382 : {
3383 1703 : OGRGeometry *poGC = OGRGeometryFactory::forceTo(
3384 850 : poLinearGeom->clone(),
3385 : OGR_GT_SetModifier(wkbGeometryCollection, poLinearGeom->Is3D(),
3386 : poLinearGeom->IsMeasured()),
3387 : nullptr);
3388 850 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGC);
3389 850 : delete poGC;
3390 : }
3391 229138 : else if (eType == wkbGeometryCollection)
3392 : {
3393 134 : bool bCanConvertToMultiPoly = true;
3394 : // bool bMustConvertToMultiPoly = true;
3395 : const OGRGeometryCollection *poGC =
3396 134 : poLinearGeom->toGeometryCollection();
3397 311 : for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
3398 : {
3399 : const OGRwkbGeometryType eSubGeomType =
3400 250 : wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
3401 250 : if (eSubGeomType == wkbPolyhedralSurface || eSubGeomType == wkbTIN)
3402 : {
3403 : // bMustConvertToMultiPoly = true;
3404 : }
3405 165 : else if (eSubGeomType != wkbMultiPolygon &&
3406 : eSubGeomType != wkbPolygon)
3407 : {
3408 73 : bCanConvertToMultiPoly = false;
3409 73 : break;
3410 : }
3411 : }
3412 134 : if (bCanConvertToMultiPoly /* && bMustConvertToMultiPoly */)
3413 : {
3414 122 : OGRGeometry *poMultiPolygon = OGRGeometryFactory::forceTo(
3415 61 : poLinearGeom->clone(),
3416 : OGR_GT_SetModifier(wkbMultiPolygon, poLinearGeom->Is3D(),
3417 : poLinearGeom->IsMeasured()),
3418 : nullptr);
3419 61 : OGRGeometry *poGCDest = OGRGeometryFactory::forceTo(
3420 : poMultiPolygon,
3421 : OGR_GT_SetModifier(wkbGeometryCollection, poLinearGeom->Is3D(),
3422 : poLinearGeom->IsMeasured()),
3423 : nullptr);
3424 61 : hGeom = convertToGEOSGeom(hGEOSCtxt, poGCDest);
3425 61 : delete poGCDest;
3426 : }
3427 : else
3428 : {
3429 73 : hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
3430 : }
3431 : }
3432 : else
3433 : {
3434 229004 : hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
3435 : }
3436 :
3437 230078 : if (poLinearGeom != this)
3438 859 : delete poLinearGeom;
3439 :
3440 230087 : return hGeom;
3441 :
3442 : #endif // HAVE_GEOS
3443 : }
3444 :
3445 : /************************************************************************/
3446 : /* hasCurveGeometry() */
3447 : /************************************************************************/
3448 :
3449 : /**
3450 : * \brief Returns if this geometry is or has curve geometry.
3451 : *
3452 : * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
3453 : * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
3454 : *
3455 : * If bLookForNonLinear is set to TRUE, it will be actually looked if
3456 : * the geometry or its subgeometries are or contain a non-linear
3457 : * geometry in them. In which case, if the method returns TRUE, it
3458 : * means that getLinearGeometry() would return an approximate version
3459 : * of the geometry. Otherwise, getLinearGeometry() would do a
3460 : * conversion, but with just converting container type, like
3461 : * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
3462 : * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
3463 : * conversion.
3464 : *
3465 : * This method is the same as the C function OGR_G_HasCurveGeometry().
3466 : *
3467 : * @param bLookForNonLinear set it to TRUE to check if the geometry is
3468 : * or contains a CIRCULARSTRING.
3469 : *
3470 : * @return TRUE if this geometry is or has curve geometry.
3471 : *
3472 : * @since GDAL 2.0
3473 : */
3474 :
3475 275801 : OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3476 : {
3477 275801 : return FALSE;
3478 : }
3479 :
3480 : /************************************************************************/
3481 : /* getLinearGeometry() */
3482 : /************************************************************************/
3483 :
3484 : /**
3485 : * \brief Return, possibly approximate, non-curve version of this geometry.
3486 : *
3487 : * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
3488 : * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
3489 : *
3490 : * The ownership of the returned geometry belongs to the caller.
3491 : *
3492 : * The reverse method is OGRGeometry::getCurveGeometry().
3493 : *
3494 : * This method is the same as the C function OGR_G_GetLinearGeometry().
3495 : *
3496 : * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
3497 : * arc, zero to use the default setting.
3498 : * @param papszOptions options as a null-terminated list of strings.
3499 : * See OGRGeometryFactory::curveToLineString() for
3500 : * valid options.
3501 : *
3502 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3503 : *
3504 : * @since GDAL 2.0
3505 : */
3506 :
3507 : OGRGeometry *
3508 87 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3509 : CPL_UNUSED const char *const *papszOptions) const
3510 : {
3511 87 : return clone();
3512 : }
3513 :
3514 : /************************************************************************/
3515 : /* getCurveGeometry() */
3516 : /************************************************************************/
3517 :
3518 : /**
3519 : * \brief Return curve version of this geometry.
3520 : *
3521 : * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
3522 : * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
3523 : * curve geometries.
3524 : *
3525 : * If the geometry has no curve portion, the returned geometry will be a clone
3526 : * of it.
3527 : *
3528 : * The ownership of the returned geometry belongs to the caller.
3529 : *
3530 : * The reverse method is OGRGeometry::getLinearGeometry().
3531 : *
3532 : * This function is the same as C function OGR_G_GetCurveGeometry().
3533 : *
3534 : * @param papszOptions options as a null-terminated list of strings.
3535 : * Unused for now. Must be set to NULL.
3536 : *
3537 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3538 : *
3539 : * @since GDAL 2.0
3540 : */
3541 :
3542 : OGRGeometry *
3543 5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
3544 : {
3545 5 : return clone();
3546 : }
3547 :
3548 : /************************************************************************/
3549 : /* Distance() */
3550 : /************************************************************************/
3551 :
3552 : /**
3553 : * \brief Compute distance between two geometries.
3554 : *
3555 : * Returns the shortest distance between the two geometries. The distance is
3556 : * expressed into the same unit as the coordinates of the geometries.
3557 : *
3558 : * This method is the same as the C function OGR_G_Distance().
3559 : *
3560 : * This method is built on the GEOS library, check it for the definition
3561 : * of the geometry operation.
3562 : * If OGR is built without the GEOS library, this method will always fail,
3563 : * issuing a CPLE_NotSupported error.
3564 : *
3565 : * @param poOtherGeom the other geometry to compare against.
3566 : *
3567 : * @return the distance between the geometries or -1 if an error occurs.
3568 : */
3569 :
3570 25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
3571 :
3572 : {
3573 25 : if (nullptr == poOtherGeom)
3574 : {
3575 0 : CPLDebug("OGR",
3576 : "OGRGeometry::Distance called with NULL geometry pointer");
3577 0 : return -1.0;
3578 : }
3579 :
3580 25 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
3581 : {
3582 : #ifndef HAVE_SFCGAL
3583 :
3584 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3585 0 : return -1.0;
3586 :
3587 : #else
3588 :
3589 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3590 : if (poThis == nullptr)
3591 : return -1.0;
3592 :
3593 : sfcgal_geometry_t *poOther =
3594 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3595 : if (poOther == nullptr)
3596 : {
3597 : sfcgal_geometry_delete(poThis);
3598 : return -1.0;
3599 : }
3600 :
3601 : const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
3602 :
3603 : sfcgal_geometry_delete(poThis);
3604 : sfcgal_geometry_delete(poOther);
3605 :
3606 : return dfDistance > 0.0 ? dfDistance : -1.0;
3607 :
3608 : #endif
3609 : }
3610 :
3611 : else
3612 : {
3613 : #ifndef HAVE_GEOS
3614 :
3615 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3616 : return -1.0;
3617 :
3618 : #else
3619 :
3620 25 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3621 : // GEOSGeom is a pointer
3622 25 : GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
3623 25 : GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
3624 :
3625 25 : int bIsErr = 0;
3626 25 : double dfDistance = 0.0;
3627 :
3628 25 : if (hThis != nullptr && hOther != nullptr)
3629 : {
3630 25 : bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
3631 : }
3632 :
3633 25 : GEOSGeom_destroy_r(hGEOSCtxt, hThis);
3634 25 : GEOSGeom_destroy_r(hGEOSCtxt, hOther);
3635 25 : freeGEOSContext(hGEOSCtxt);
3636 :
3637 25 : if (bIsErr > 0)
3638 : {
3639 25 : return dfDistance;
3640 : }
3641 :
3642 : /* Calculations error */
3643 0 : return -1.0;
3644 :
3645 : #endif /* HAVE_GEOS */
3646 : }
3647 : }
3648 :
3649 : /************************************************************************/
3650 : /* OGR_G_Distance() */
3651 : /************************************************************************/
3652 : /**
3653 : * \brief Compute distance between two geometries.
3654 : *
3655 : * Returns the shortest distance between the two geometries. The distance is
3656 : * expressed into the same unit as the coordinates of the geometries.
3657 : *
3658 : * This function is the same as the C++ method OGRGeometry::Distance().
3659 : *
3660 : * This function is built on the GEOS library, check it for the definition
3661 : * of the geometry operation.
3662 : * If OGR is built without the GEOS library, this function will always fail,
3663 : * issuing a CPLE_NotSupported error.
3664 : *
3665 : * @param hFirst the first geometry to compare against.
3666 : * @param hOther the other geometry to compare against.
3667 : *
3668 : * @return the distance between the geometries or -1 if an error occurs.
3669 : */
3670 :
3671 2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
3672 :
3673 : {
3674 2 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
3675 :
3676 4 : return OGRGeometry::FromHandle(hFirst)->Distance(
3677 2 : OGRGeometry::FromHandle(hOther));
3678 : }
3679 :
3680 : /************************************************************************/
3681 : /* Distance3D() */
3682 : /************************************************************************/
3683 :
3684 : /**
3685 : * \brief Returns the 3D distance between two geometries
3686 : *
3687 : * The distance is expressed into the same unit as the coordinates of the
3688 : * geometries.
3689 : *
3690 : * This method is built on the SFCGAL library, check it for the definition
3691 : * of the geometry operation.
3692 : * If OGR is built without the SFCGAL library, this method will always return
3693 : * -1.0
3694 : *
3695 : * This function is the same as the C function OGR_G_Distance3D().
3696 : *
3697 : * @return distance between the two geometries
3698 : * @since GDAL 2.2
3699 : */
3700 :
3701 1 : double OGRGeometry::Distance3D(
3702 : UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
3703 : {
3704 1 : if (poOtherGeom == nullptr)
3705 : {
3706 0 : CPLDebug("OGR",
3707 : "OGRTriangle::Distance3D called with NULL geometry pointer");
3708 0 : return -1.0;
3709 : }
3710 :
3711 1 : if (!(poOtherGeom->Is3D() && Is3D()))
3712 : {
3713 0 : CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
3714 : "geometry(geometries)");
3715 0 : return -1.0;
3716 : }
3717 :
3718 : #ifndef HAVE_SFCGAL
3719 :
3720 1 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3721 1 : return -1.0;
3722 :
3723 : #else
3724 :
3725 : sfcgal_init();
3726 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3727 : if (poThis == nullptr)
3728 : return -1.0;
3729 :
3730 : sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3731 : if (poOther == nullptr)
3732 : {
3733 : sfcgal_geometry_delete(poThis);
3734 : return -1.0;
3735 : }
3736 :
3737 : const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
3738 :
3739 : sfcgal_geometry_delete(poThis);
3740 : sfcgal_geometry_delete(poOther);
3741 :
3742 : return dfDistance > 0 ? dfDistance : -1.0;
3743 :
3744 : #endif
3745 : }
3746 :
3747 : /************************************************************************/
3748 : /* OGR_G_Distance3D() */
3749 : /************************************************************************/
3750 : /**
3751 : * \brief Returns the 3D distance between two geometries
3752 : *
3753 : * The distance is expressed into the same unit as the coordinates of the
3754 : * geometries.
3755 : *
3756 : * This method is built on the SFCGAL library, check it for the definition
3757 : * of the geometry operation.
3758 : * If OGR is built without the SFCGAL library, this method will always return
3759 : * -1.0
3760 : *
3761 : * This function is the same as the C++ method OGRGeometry::Distance3D().
3762 : *
3763 : * @param hFirst the first geometry to compare against.
3764 : * @param hOther the other geometry to compare against.
3765 : * @return distance between the two geometries
3766 : * @since GDAL 2.2
3767 : *
3768 : * @return the distance between the geometries or -1 if an error occurs.
3769 : */
3770 :
3771 1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
3772 :
3773 : {
3774 1 : VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
3775 :
3776 2 : return OGRGeometry::FromHandle(hFirst)->Distance3D(
3777 1 : OGRGeometry::FromHandle(hOther));
3778 : }
3779 :
3780 : /************************************************************************/
3781 : /* OGRGeometryRebuildCurves() */
3782 : /************************************************************************/
3783 :
3784 : #ifdef HAVE_GEOS
3785 3780 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
3786 : const OGRGeometry *poOtherGeom,
3787 : OGRGeometry *poOGRProduct)
3788 : {
3789 7562 : if (poOGRProduct != nullptr &&
3790 7484 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
3791 3704 : (poGeom->hasCurveGeometry(true) ||
3792 2684 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
3793 : {
3794 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
3795 8 : delete poOGRProduct;
3796 8 : return poCurveGeom;
3797 : }
3798 3773 : return poOGRProduct;
3799 : }
3800 :
3801 : /************************************************************************/
3802 : /* BuildGeometryFromGEOS() */
3803 : /************************************************************************/
3804 :
3805 3689 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
3806 : GEOSGeom hGeosProduct,
3807 : const OGRGeometry *poSelf,
3808 : const OGRGeometry *poOtherGeom)
3809 : {
3810 3689 : OGRGeometry *poOGRProduct = nullptr;
3811 3689 : if (hGeosProduct != nullptr)
3812 : {
3813 : poOGRProduct =
3814 3688 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
3815 3687 : if (poOGRProduct != nullptr &&
3816 9035 : poSelf->getSpatialReference() != nullptr &&
3817 1661 : (poOtherGeom == nullptr ||
3818 1661 : (poOtherGeom->getSpatialReference() != nullptr &&
3819 1531 : poOtherGeom->getSpatialReference()->IsSame(
3820 : poSelf->getSpatialReference()))))
3821 : {
3822 1557 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
3823 : }
3824 : poOGRProduct =
3825 3687 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
3826 3687 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
3827 : }
3828 3687 : return poOGRProduct;
3829 : }
3830 :
3831 : /************************************************************************/
3832 : /* BuildGeometryFromTwoGeoms() */
3833 : /************************************************************************/
3834 :
3835 2757 : static OGRGeometry *BuildGeometryFromTwoGeoms(
3836 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3837 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
3838 : const GEOSGeometry *,
3839 : const GEOSGeometry *))
3840 : {
3841 2757 : OGRGeometry *poOGRProduct = nullptr;
3842 :
3843 2757 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3844 2757 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3845 2757 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3846 2757 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3847 : {
3848 : GEOSGeom hGeosProduct =
3849 2757 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3850 :
3851 : poOGRProduct =
3852 2756 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
3853 : }
3854 2755 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3855 2757 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3856 2757 : poSelf->freeGEOSContext(hGEOSCtxt);
3857 :
3858 2756 : return poOGRProduct;
3859 : }
3860 :
3861 : /************************************************************************/
3862 : /* OGRGEOSBooleanPredicate() */
3863 : /************************************************************************/
3864 :
3865 21467 : static OGRBoolean OGRGEOSBooleanPredicate(
3866 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3867 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
3868 : const GEOSGeometry *))
3869 : {
3870 21467 : OGRBoolean bResult = FALSE;
3871 :
3872 21467 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3873 21467 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3874 21467 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3875 21467 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3876 : {
3877 20959 : bResult = pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3878 : }
3879 21467 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3880 21467 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3881 21467 : poSelf->freeGEOSContext(hGEOSCtxt);
3882 :
3883 21467 : return bResult;
3884 : }
3885 :
3886 : #endif // HAVE_GEOS
3887 :
3888 : /************************************************************************/
3889 : /* MakeValid() */
3890 : /************************************************************************/
3891 :
3892 : /**
3893 : * \brief Attempts to make an invalid geometry valid without losing vertices.
3894 : *
3895 : * Already-valid geometries are cloned without further intervention
3896 : * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
3897 : * may be subject to non-significant transformations, such as duplicated point
3898 : * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
3899 : * geometry collections could be returned a single geometry. GDAL 3.10
3900 : * returns the same type of geometry).
3901 : *
3902 : * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
3903 : * post-processing step is often desired.
3904 : *
3905 : * This method is the same as the C function OGR_G_MakeValid().
3906 : *
3907 : * This function is built on the GEOS >= 3.8 library, check it for the
3908 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
3909 : * library, this function will return a clone of the input geometry if it is
3910 : * valid, or NULL if it is invalid
3911 : *
3912 : * @param papszOptions NULL terminated list of options, or NULL. The following
3913 : * options are available:
3914 : * <ul>
3915 : * <li>METHOD=LINEWORK/STRUCTURE.
3916 : * LINEWORK is the default method, which combines all rings into a set of
3917 : * noded lines and then extracts valid polygons from that linework.
3918 : * The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
3919 : * all rings valid, then merges shells and
3920 : * subtracts holes from shells to generate valid result. Assumes that
3921 : * holes and shells are correctly categorized.</li>
3922 : * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
3923 : * NO (default): collapses are converted to empty geometries
3924 : * YES: collapses are converted to a valid geometry of lower dimension.</li>
3925 : * </ul>
3926 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3927 : *
3928 : * @since GDAL 3.0
3929 : */
3930 96 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
3931 : {
3932 : (void)papszOptions;
3933 : #ifndef HAVE_GEOS
3934 : if (IsValid())
3935 : return clone();
3936 :
3937 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3938 : return nullptr;
3939 : #else
3940 96 : if (IsSFCGALCompatible())
3941 : {
3942 0 : if (IsValid())
3943 0 : return clone();
3944 : }
3945 96 : else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
3946 : {
3947 2 : GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
3948 2 : OGRBoolean bIsValid = FALSE;
3949 2 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
3950 2 : if (hGeosGeom)
3951 : {
3952 2 : bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom);
3953 2 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
3954 : }
3955 2 : freeGEOSContext(hGEOSCtxt);
3956 2 : if (bIsValid)
3957 1 : return clone();
3958 : }
3959 :
3960 95 : const bool bStructureMethod = EQUAL(
3961 : CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
3962 95 : CPL_IGNORE_RET_VAL(bStructureMethod);
3963 : #if !(GEOS_VERSION_MAJOR > 3 || \
3964 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
3965 : if (bStructureMethod)
3966 : {
3967 : CPLError(CE_Failure, CPLE_NotSupported,
3968 : "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
3969 : return nullptr;
3970 : }
3971 : #endif
3972 :
3973 95 : OGRGeometry *poOGRProduct = nullptr;
3974 :
3975 95 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3976 95 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
3977 95 : if (hGeosGeom != nullptr)
3978 : {
3979 : GEOSGeom hGEOSRet;
3980 : #if GEOS_VERSION_MAJOR > 3 || \
3981 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
3982 94 : if (bStructureMethod)
3983 : {
3984 : GEOSMakeValidParams *params =
3985 7 : GEOSMakeValidParams_create_r(hGEOSCtxt);
3986 7 : CPLAssert(params);
3987 7 : GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
3988 : GEOS_MAKE_VALID_STRUCTURE);
3989 7 : GEOSMakeValidParams_setKeepCollapsed_r(
3990 : hGEOSCtxt, params,
3991 7 : CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
3992 7 : hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
3993 7 : GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
3994 : }
3995 : else
3996 : #endif
3997 : {
3998 87 : hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
3999 : }
4000 94 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4001 :
4002 94 : if (hGEOSRet != nullptr)
4003 : {
4004 : poOGRProduct =
4005 94 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
4006 94 : if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
4007 6 : poOGRProduct->assignSpatialReference(getSpatialReference());
4008 : poOGRProduct =
4009 94 : OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
4010 94 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
4011 :
4012 : #if GEOS_VERSION_MAJOR > 3 || \
4013 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4014 : // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
4015 : // if the input is a multiple geometry
4016 94 : if (poOGRProduct && bStructureMethod &&
4017 192 : OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
4018 4 : !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
4019 : wkbGeometryCollection))
4020 : {
4021 3 : poOGRProduct = OGRGeometryFactory::forceTo(poOGRProduct,
4022 3 : getGeometryType());
4023 : }
4024 : #endif
4025 : }
4026 : }
4027 95 : freeGEOSContext(hGEOSCtxt);
4028 :
4029 95 : return poOGRProduct;
4030 : #endif
4031 : }
4032 :
4033 : /************************************************************************/
4034 : /* OGR_G_MakeValid() */
4035 : /************************************************************************/
4036 :
4037 : /**
4038 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4039 : *
4040 : * Already-valid geometries are cloned without further intervention.
4041 : *
4042 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4043 : *
4044 : * This function is built on the GEOS >= 3.8 library, check it for the
4045 : * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4046 : * library, this function will return a clone of the input geometry if it is
4047 : * valid, or NULL if it is invalid
4048 : *
4049 : * @param hGeom The Geometry to make valid.
4050 : *
4051 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4052 : * or NULL if an error occurs.
4053 : *
4054 : * @since GDAL 3.0
4055 : */
4056 :
4057 0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
4058 :
4059 : {
4060 0 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
4061 :
4062 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
4063 : }
4064 :
4065 : /************************************************************************/
4066 : /* OGR_G_MakeValidEx() */
4067 : /************************************************************************/
4068 :
4069 : /**
4070 : * \brief Attempts to make an invalid geometry valid without losing vertices.
4071 : *
4072 : * Already-valid geometries are cloned without further intervention.
4073 : *
4074 : * This function is the same as the C++ method OGRGeometry::MakeValid().
4075 : *
4076 : * See documentation of that method for possible options.
4077 : *
4078 : * @param hGeom The Geometry to make valid.
4079 : * @param papszOptions Options.
4080 : *
4081 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4082 : * or NULL if an error occurs.
4083 : *
4084 : * @since GDAL 3.4
4085 : */
4086 :
4087 11 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
4088 :
4089 : {
4090 11 : VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
4091 :
4092 11 : return OGRGeometry::ToHandle(
4093 22 : OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
4094 : }
4095 :
4096 : /************************************************************************/
4097 : /* Normalize() */
4098 : /************************************************************************/
4099 :
4100 : /**
4101 : * \brief Attempts to bring geometry into normalized/canonical form.
4102 : *
4103 : * This method is the same as the C function OGR_G_Normalize().
4104 : *
4105 : * This function is built on the GEOS library; check it for the definition
4106 : * of the geometry operation.
4107 : * If OGR is built without the GEOS library, this function will always fail,
4108 : * issuing a CPLE_NotSupported error.
4109 : *
4110 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4111 : *
4112 : * @since GDAL 3.3
4113 : */
4114 35 : OGRGeometry *OGRGeometry::Normalize() const
4115 : {
4116 : #ifndef HAVE_GEOS
4117 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4118 : return nullptr;
4119 : #else
4120 35 : OGRGeometry *poOGRProduct = nullptr;
4121 :
4122 35 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4123 35 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4124 35 : if (hGeosGeom != nullptr)
4125 : {
4126 :
4127 35 : int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
4128 :
4129 35 : if (hGEOSRet == 0)
4130 : {
4131 : poOGRProduct =
4132 35 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
4133 : }
4134 : else
4135 : {
4136 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4137 : }
4138 : }
4139 35 : freeGEOSContext(hGEOSCtxt);
4140 :
4141 35 : return poOGRProduct;
4142 : #endif
4143 : }
4144 :
4145 : /************************************************************************/
4146 : /* OGR_G_Normalize() */
4147 : /************************************************************************/
4148 :
4149 : /**
4150 : * \brief Attempts to bring geometry into normalized/canonical form.
4151 : *
4152 : * This function is the same as the C++ method OGRGeometry::Normalize().
4153 : *
4154 : * This function is built on the GEOS library; check it for the definition
4155 : * of the geometry operation.
4156 : * If OGR is built without the GEOS library, this function will always fail,
4157 : * issuing a CPLE_NotSupported error.
4158 : * @param hGeom The Geometry to normalize.
4159 : *
4160 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4161 : * or NULL if an error occurs.
4162 : *
4163 : * @since GDAL 3.3
4164 : */
4165 :
4166 5 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
4167 :
4168 : {
4169 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
4170 :
4171 5 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
4172 : }
4173 :
4174 : /************************************************************************/
4175 : /* ConvexHull() */
4176 : /************************************************************************/
4177 :
4178 : /**
4179 : * \brief Compute convex hull.
4180 : *
4181 : * A new geometry object is created and returned containing the convex
4182 : * hull of the geometry on which the method is invoked.
4183 : *
4184 : * This method is the same as the C function OGR_G_ConvexHull().
4185 : *
4186 : * This method is built on the GEOS library, check it for the definition
4187 : * of the geometry operation.
4188 : * If OGR is built without the GEOS library, this method will always fail,
4189 : * issuing a CPLE_NotSupported error.
4190 : *
4191 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4192 : */
4193 :
4194 3 : OGRGeometry *OGRGeometry::ConvexHull() const
4195 :
4196 : {
4197 3 : if (IsSFCGALCompatible())
4198 : {
4199 : #ifndef HAVE_SFCGAL
4200 :
4201 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4202 0 : return nullptr;
4203 :
4204 : #else
4205 :
4206 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4207 : if (poThis == nullptr)
4208 : return nullptr;
4209 :
4210 : sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
4211 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4212 : if (h_prodGeom)
4213 : h_prodGeom->assignSpatialReference(getSpatialReference());
4214 :
4215 : sfcgal_geometry_delete(poThis);
4216 : sfcgal_geometry_delete(poRes);
4217 :
4218 : return h_prodGeom;
4219 :
4220 : #endif
4221 : }
4222 :
4223 : else
4224 : {
4225 : #ifndef HAVE_GEOS
4226 :
4227 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4228 : return nullptr;
4229 :
4230 : #else
4231 :
4232 3 : OGRGeometry *poOGRProduct = nullptr;
4233 :
4234 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4235 3 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4236 3 : if (hGeosGeom != nullptr)
4237 : {
4238 3 : GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
4239 3 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4240 :
4241 : poOGRProduct =
4242 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4243 : }
4244 3 : freeGEOSContext(hGEOSCtxt);
4245 :
4246 3 : return poOGRProduct;
4247 :
4248 : #endif /* HAVE_GEOS */
4249 : }
4250 : }
4251 :
4252 : /************************************************************************/
4253 : /* OGR_G_ConvexHull() */
4254 : /************************************************************************/
4255 : /**
4256 : * \brief Compute convex hull.
4257 : *
4258 : * A new geometry object is created and returned containing the convex
4259 : * hull of the geometry on which the method is invoked.
4260 : *
4261 : * This function is the same as the C++ method OGRGeometry::ConvexHull().
4262 : *
4263 : * This function is built on the GEOS library, check it for the definition
4264 : * of the geometry operation.
4265 : * If OGR is built without the GEOS library, this function will always fail,
4266 : * issuing a CPLE_NotSupported error.
4267 : *
4268 : * @param hTarget The Geometry to calculate the convex hull of.
4269 : *
4270 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4271 : * or NULL if an error occurs.
4272 : */
4273 :
4274 1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
4275 :
4276 : {
4277 1 : VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
4278 :
4279 1 : return OGRGeometry::ToHandle(
4280 2 : OGRGeometry::FromHandle(hTarget)->ConvexHull());
4281 : }
4282 :
4283 : /************************************************************************/
4284 : /* ConcaveHull() */
4285 : /************************************************************************/
4286 :
4287 : /**
4288 : * \brief Compute "concave hull" of a geometry.
4289 : *
4290 : * The concave hull is fully contained within the convex hull and also
4291 : * contains all the points of the input, but in a smaller area.
4292 : * The area ratio is the ratio of the area of the convex hull and the concave
4293 : * hull. Frequently used to convert a multi-point into a polygonal area.
4294 : * that contains all the points in the input Geometry.
4295 : *
4296 : * A new geometry object is created and returned containing the concave
4297 : * hull of the geometry on which the method is invoked.
4298 : *
4299 : * This method is the same as the C function OGR_G_ConcaveHull().
4300 : *
4301 : * This method is built on the GEOS >= 3.11 library
4302 : * If OGR is built without the GEOS >= 3.11 library, this method will always
4303 : * fail, issuing a CPLE_NotSupported error.
4304 : *
4305 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4306 : * @param bAllowHoles Whether holes are allowed.
4307 : *
4308 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4309 : *
4310 : * @since GDAL 3.6
4311 : */
4312 :
4313 2 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
4314 : {
4315 : #ifndef HAVE_GEOS
4316 : (void)dfRatio;
4317 : (void)bAllowHoles;
4318 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4319 : return nullptr;
4320 : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4321 : (void)dfRatio;
4322 : (void)bAllowHoles;
4323 : CPLError(CE_Failure, CPLE_NotSupported,
4324 : "GEOS 3.11 or later needed for ConcaveHull.");
4325 : return nullptr;
4326 : #else
4327 2 : OGRGeometry *poOGRProduct = nullptr;
4328 :
4329 2 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4330 2 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4331 2 : if (hGeosGeom != nullptr)
4332 : {
4333 : GEOSGeom hGeosHull =
4334 2 : GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
4335 2 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4336 :
4337 : poOGRProduct =
4338 2 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4339 : }
4340 2 : freeGEOSContext(hGEOSCtxt);
4341 :
4342 2 : return poOGRProduct;
4343 : #endif /* HAVE_GEOS */
4344 : }
4345 :
4346 : /************************************************************************/
4347 : /* OGR_G_ConcaveHull() */
4348 : /************************************************************************/
4349 : /**
4350 : * \brief Compute "concave hull" of a geometry.
4351 : *
4352 : * The concave hull is fully contained within the convex hull and also
4353 : * contains all the points of the input, but in a smaller area.
4354 : * The area ratio is the ratio of the area of the convex hull and the concave
4355 : * hull. Frequently used to convert a multi-point into a polygonal area.
4356 : * that contains all the points in the input Geometry.
4357 : *
4358 : * A new geometry object is created and returned containing the convex
4359 : * hull of the geometry on which the function is invoked.
4360 : *
4361 : * This function is the same as the C++ method OGRGeometry::ConcaveHull().
4362 : *
4363 : * This function is built on the GEOS >= 3.11 library
4364 : * If OGR is built without the GEOS >= 3.11 library, this function will always
4365 : * fail, issuing a CPLE_NotSupported error.
4366 : *
4367 : * @param hTarget The Geometry to calculate the concave hull of.
4368 : * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4369 : * @param bAllowHoles Whether holes are allowed.
4370 : *
4371 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4372 : * or NULL if an error occurs.
4373 : *
4374 : * @since GDAL 3.6
4375 : */
4376 :
4377 2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
4378 : bool bAllowHoles)
4379 :
4380 : {
4381 2 : VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
4382 :
4383 2 : return OGRGeometry::ToHandle(
4384 4 : OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
4385 : }
4386 :
4387 : /************************************************************************/
4388 : /* Boundary() */
4389 : /************************************************************************/
4390 :
4391 : /**
4392 : * \brief Compute boundary.
4393 : *
4394 : * A new geometry object is created and returned containing the boundary
4395 : * of the geometry on which the method is invoked.
4396 : *
4397 : * This method is the same as the C function OGR_G_Boundary().
4398 : *
4399 : * This method is built on the GEOS library, check it for the definition
4400 : * of the geometry operation.
4401 : * If OGR is built without the GEOS library, this method will always fail,
4402 : * issuing a CPLE_NotSupported error.
4403 : *
4404 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4405 : *
4406 : * @since OGR 1.8.0
4407 : */
4408 :
4409 6 : OGRGeometry *OGRGeometry::Boundary() const
4410 :
4411 : {
4412 : #ifndef HAVE_GEOS
4413 :
4414 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4415 : return nullptr;
4416 :
4417 : #else
4418 :
4419 6 : OGRGeometry *poOGRProduct = nullptr;
4420 :
4421 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4422 6 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4423 6 : if (hGeosGeom != nullptr)
4424 : {
4425 6 : GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
4426 6 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4427 :
4428 : poOGRProduct =
4429 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4430 : }
4431 6 : freeGEOSContext(hGEOSCtxt);
4432 :
4433 6 : return poOGRProduct;
4434 :
4435 : #endif // HAVE_GEOS
4436 : }
4437 :
4438 : //! @cond Doxygen_Suppress
4439 : /**
4440 : * \brief Compute boundary (deprecated)
4441 : *
4442 : * @deprecated
4443 : *
4444 : * @see Boundary()
4445 : */
4446 0 : OGRGeometry *OGRGeometry::getBoundary() const
4447 :
4448 : {
4449 0 : return Boundary();
4450 : }
4451 :
4452 : //! @endcond
4453 :
4454 : /************************************************************************/
4455 : /* OGR_G_Boundary() */
4456 : /************************************************************************/
4457 : /**
4458 : * \brief Compute boundary.
4459 : *
4460 : * A new geometry object is created and returned containing the boundary
4461 : * of the geometry on which the method is invoked.
4462 : *
4463 : * This function is the same as the C++ method OGR_G_Boundary().
4464 : *
4465 : * This function is built on the GEOS library, check it for the definition
4466 : * of the geometry operation.
4467 : * If OGR is built without the GEOS library, this function will always fail,
4468 : * issuing a CPLE_NotSupported error.
4469 : *
4470 : * @param hTarget The Geometry to calculate the boundary of.
4471 : *
4472 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4473 : * or NULL if an error occurs.
4474 : *
4475 : * @since OGR 1.8.0
4476 : */
4477 6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
4478 :
4479 : {
4480 6 : VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
4481 :
4482 6 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4483 : }
4484 :
4485 : /**
4486 : * \brief Compute boundary (deprecated)
4487 : *
4488 : * @deprecated
4489 : *
4490 : * @see OGR_G_Boundary()
4491 : */
4492 0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
4493 :
4494 : {
4495 0 : VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
4496 :
4497 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4498 : }
4499 :
4500 : /************************************************************************/
4501 : /* Buffer() */
4502 : /************************************************************************/
4503 :
4504 : /**
4505 : * \brief Compute buffer of geometry.
4506 : *
4507 : * Builds a new geometry containing the buffer region around the geometry
4508 : * on which it is invoked. The buffer is a polygon containing the region within
4509 : * the buffer distance of the original geometry.
4510 : *
4511 : * Some buffer sections are properly described as curves, but are converted to
4512 : * approximate polygons. The nQuadSegs parameter can be used to control how
4513 : * many segments should be used to define a 90 degree curve - a quadrant of a
4514 : * circle. A value of 30 is a reasonable default. Large values result in
4515 : * large numbers of vertices in the resulting buffer geometry while small
4516 : * numbers reduce the accuracy of the result.
4517 : *
4518 : * This method is the same as the C function OGR_G_Buffer().
4519 : *
4520 : * This method is built on the GEOS library, check it for the definition
4521 : * of the geometry operation.
4522 : * If OGR is built without the GEOS library, this method will always fail,
4523 : * issuing a CPLE_NotSupported error.
4524 : *
4525 : * @param dfDist the buffer distance to be applied. Should be expressed into
4526 : * the same unit as the coordinates of the geometry.
4527 : *
4528 : * @param nQuadSegs the number of segments used to approximate a 90
4529 : * degree (quadrant) of curvature.
4530 : *
4531 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4532 : */
4533 :
4534 38 : OGRGeometry *OGRGeometry::Buffer(UNUSED_IF_NO_GEOS double dfDist,
4535 : UNUSED_IF_NO_GEOS int nQuadSegs) const
4536 :
4537 : {
4538 : #ifndef HAVE_GEOS
4539 :
4540 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4541 : return nullptr;
4542 :
4543 : #else
4544 :
4545 38 : OGRGeometry *poOGRProduct = nullptr;
4546 :
4547 38 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4548 38 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4549 38 : if (hGeosGeom != nullptr)
4550 : {
4551 : GEOSGeom hGeosProduct =
4552 38 : GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
4553 38 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4554 :
4555 : poOGRProduct =
4556 38 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4557 : }
4558 38 : freeGEOSContext(hGEOSCtxt);
4559 :
4560 38 : return poOGRProduct;
4561 :
4562 : #endif // HAVE_GEOS
4563 : }
4564 :
4565 : /************************************************************************/
4566 : /* OGR_G_Buffer() */
4567 : /************************************************************************/
4568 :
4569 : /**
4570 : * \brief Compute buffer of geometry.
4571 : *
4572 : * Builds a new geometry containing the buffer region around the geometry
4573 : * on which it is invoked. The buffer is a polygon containing the region within
4574 : * the buffer distance of the original geometry.
4575 : *
4576 : * Some buffer sections are properly described as curves, but are converted to
4577 : * approximate polygons. The nQuadSegs parameter can be used to control how
4578 : * many segments should be used to define a 90 degree curve - a quadrant of a
4579 : * circle. A value of 30 is a reasonable default. Large values result in
4580 : * large numbers of vertices in the resulting buffer geometry while small
4581 : * numbers reduce the accuracy of the result.
4582 : *
4583 : * This function is the same as the C++ method OGRGeometry::Buffer().
4584 : *
4585 : * This function is built on the GEOS library, check it for the definition
4586 : * of the geometry operation.
4587 : * If OGR is built without the GEOS library, this function will always fail,
4588 : * issuing a CPLE_NotSupported error.
4589 : *
4590 : * @param hTarget the geometry.
4591 : * @param dfDist the buffer distance to be applied. Should be expressed into
4592 : * the same unit as the coordinates of the geometry.
4593 : *
4594 : * @param nQuadSegs the number of segments used to approximate a 90 degree
4595 : * (quadrant) of curvature.
4596 : *
4597 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4598 : * or NULL if an error occurs.
4599 : */
4600 :
4601 38 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
4602 :
4603 : {
4604 38 : VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
4605 :
4606 38 : return OGRGeometry::ToHandle(
4607 76 : OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
4608 : }
4609 :
4610 : /**
4611 : * \brief Compute buffer of geometry.
4612 : *
4613 : * Builds a new geometry containing the buffer region around the geometry
4614 : * on which it is invoked. The buffer is a polygon containing the region within
4615 : * the buffer distance of the original geometry.
4616 : *
4617 : * This function is built on the GEOS library, check it for the definition
4618 : * of the geometry operation.
4619 : * If OGR is built without the GEOS library, this function will always fail,
4620 : * issuing a CPLE_NotSupported error.
4621 : *
4622 : * The following options are supported. See the GEOS library for more detailed
4623 : * descriptions.
4624 : *
4625 : * <ul>
4626 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
4627 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
4628 : * <li>MITRE_LIMIT=double</li>
4629 : * <li>QUADRANT_SEGMENTS=int</li>
4630 : * <li>SINGLE_SIDED=YES/NO</li>
4631 : * </ul>
4632 : *
4633 : * This function is the same as the C function OGR_G_BufferEx().
4634 : *
4635 : * @param dfDist the buffer distance to be applied. Should be expressed into
4636 : * the same unit as the coordinates of the geometry.
4637 : * @param papszOptions NULL terminated list of options (may be NULL)
4638 : *
4639 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4640 : *
4641 : * @since GDAL 3.10
4642 : */
4643 :
4644 : OGRGeometry *
4645 19 : OGRGeometry::BufferEx(UNUSED_IF_NO_GEOS double dfDist,
4646 : UNUSED_IF_NO_GEOS CSLConstList papszOptions) const
4647 : {
4648 : #ifndef HAVE_GEOS
4649 :
4650 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4651 : return nullptr;
4652 :
4653 : #else
4654 19 : OGRGeometry *poOGRProduct = nullptr;
4655 19 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4656 :
4657 19 : auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
4658 19 : bool bParamsAreValid = true;
4659 :
4660 70 : for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
4661 : {
4662 51 : if (EQUAL(pszParam, "ENDCAP_STYLE"))
4663 : {
4664 : int nStyle;
4665 9 : if (EQUAL(pszValue, "ROUND"))
4666 : {
4667 6 : nStyle = GEOSBUF_CAP_ROUND;
4668 : }
4669 3 : else if (EQUAL(pszValue, "FLAT"))
4670 : {
4671 1 : nStyle = GEOSBUF_CAP_FLAT;
4672 : }
4673 2 : else if (EQUAL(pszValue, "SQUARE"))
4674 : {
4675 1 : nStyle = GEOSBUF_CAP_SQUARE;
4676 : }
4677 : else
4678 : {
4679 1 : bParamsAreValid = false;
4680 1 : CPLError(CE_Failure, CPLE_NotSupported,
4681 : "Invalid value for ENDCAP_STYLE: %s", pszValue);
4682 2 : break;
4683 : }
4684 :
4685 8 : if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
4686 : {
4687 0 : bParamsAreValid = false;
4688 : }
4689 : }
4690 42 : else if (EQUAL(pszParam, "JOIN_STYLE"))
4691 : {
4692 : int nStyle;
4693 9 : if (EQUAL(pszValue, "ROUND"))
4694 : {
4695 5 : nStyle = GEOSBUF_JOIN_ROUND;
4696 : }
4697 4 : else if (EQUAL(pszValue, "MITRE"))
4698 : {
4699 3 : nStyle = GEOSBUF_JOIN_MITRE;
4700 : }
4701 1 : else if (EQUAL(pszValue, "BEVEL"))
4702 : {
4703 0 : nStyle = GEOSBUF_JOIN_BEVEL;
4704 : }
4705 : else
4706 : {
4707 1 : bParamsAreValid = false;
4708 1 : CPLError(CE_Failure, CPLE_NotSupported,
4709 : "Invalid value for JOIN_STYLE: %s", pszValue);
4710 1 : break;
4711 : }
4712 :
4713 8 : if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
4714 : {
4715 0 : bParamsAreValid = false;
4716 0 : break;
4717 : }
4718 : }
4719 33 : else if (EQUAL(pszParam, "MITRE_LIMIT"))
4720 : {
4721 : try
4722 : {
4723 : std::size_t end;
4724 14 : double dfLimit = std::stod(pszValue, &end);
4725 :
4726 8 : if (end != strlen(pszValue))
4727 : {
4728 0 : throw std::invalid_argument("");
4729 : }
4730 :
4731 8 : if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
4732 : dfLimit))
4733 : {
4734 0 : bParamsAreValid = false;
4735 0 : break;
4736 : }
4737 : }
4738 4 : catch (const std::invalid_argument &)
4739 : {
4740 2 : bParamsAreValid = false;
4741 2 : CPLError(CE_Failure, CPLE_IllegalArg,
4742 : "Invalid value for MITRE_LIMIT: %s", pszValue);
4743 : }
4744 0 : catch (const std::out_of_range &)
4745 : {
4746 0 : bParamsAreValid = false;
4747 0 : CPLError(CE_Failure, CPLE_IllegalArg,
4748 : "Invalid value for MITRE_LIMIT: %s", pszValue);
4749 : }
4750 : }
4751 23 : else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
4752 : {
4753 : try
4754 : {
4755 : std::size_t end;
4756 22 : int nQuadSegs = std::stoi(pszValue, &end, 10);
4757 :
4758 10 : if (end != strlen(pszValue))
4759 : {
4760 0 : throw std::invalid_argument("");
4761 : }
4762 :
4763 10 : if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
4764 : nQuadSegs))
4765 : {
4766 0 : bParamsAreValid = false;
4767 0 : break;
4768 : }
4769 : }
4770 6 : catch (const std::invalid_argument &)
4771 : {
4772 3 : bParamsAreValid = false;
4773 3 : CPLError(CE_Failure, CPLE_IllegalArg,
4774 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
4775 : }
4776 2 : catch (const std::out_of_range &)
4777 : {
4778 1 : bParamsAreValid = false;
4779 1 : CPLError(CE_Failure, CPLE_IllegalArg,
4780 : "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
4781 : }
4782 : }
4783 9 : else if (EQUAL(pszParam, "SINGLE_SIDED"))
4784 : {
4785 8 : bool bSingleSided = CPLTestBool(pszValue);
4786 :
4787 8 : if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
4788 : bSingleSided))
4789 : {
4790 0 : bParamsAreValid = false;
4791 0 : break;
4792 : }
4793 : }
4794 : else
4795 : {
4796 1 : bParamsAreValid = false;
4797 1 : CPLError(CE_Failure, CPLE_NotSupported,
4798 : "Unsupported buffer option: %s", pszValue);
4799 : }
4800 : }
4801 :
4802 19 : if (bParamsAreValid)
4803 : {
4804 10 : GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4805 10 : if (hGeosGeom != nullptr)
4806 : {
4807 : GEOSGeom hGeosProduct =
4808 10 : GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
4809 10 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4810 :
4811 10 : if (hGeosProduct != nullptr)
4812 : {
4813 10 : poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
4814 : this, nullptr);
4815 : }
4816 : }
4817 : }
4818 :
4819 19 : GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
4820 19 : freeGEOSContext(hGEOSCtxt);
4821 19 : return poOGRProduct;
4822 : #endif
4823 : }
4824 :
4825 : /**
4826 : * \brief Compute buffer of geometry.
4827 : *
4828 : * Builds a new geometry containing the buffer region around the geometry
4829 : * on which it is invoked. The buffer is a polygon containing the region within
4830 : * the buffer distance of the original geometry.
4831 : *
4832 : * This function is built on the GEOS library, check it for the definition
4833 : * of the geometry operation.
4834 : * If OGR is built without the GEOS library, this function will always fail,
4835 : * issuing a CPLE_NotSupported error.
4836 : *
4837 : * The following options are supported. See the GEOS library for more detailed
4838 : * descriptions.
4839 : *
4840 : * <ul>
4841 : * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
4842 : * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
4843 : * <li>MITRE_LIMIT=double</li>
4844 : * <li>QUADRANT_SEGMENTS=int</li>
4845 : * <li>SINGLE_SIDED=YES/NO</li>
4846 : * </ul>
4847 : *
4848 : * This function is the same as the C++ method OGRGeometry::BufferEx().
4849 : *
4850 : * @param hTarget the geometry.
4851 : * @param dfDist the buffer distance to be applied. Should be expressed into
4852 : * the same unit as the coordinates of the geometry.
4853 : * @param papszOptions NULL terminated list of options (may be NULL)
4854 : *
4855 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4856 : * or NULL if an error occurs.
4857 : *
4858 : * @since GDAL 3.10
4859 : */
4860 :
4861 12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
4862 : CSLConstList papszOptions)
4863 :
4864 : {
4865 12 : VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
4866 :
4867 12 : return OGRGeometry::ToHandle(
4868 24 : OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
4869 : }
4870 :
4871 : /************************************************************************/
4872 : /* Intersection() */
4873 : /************************************************************************/
4874 :
4875 : /**
4876 : * \brief Compute intersection.
4877 : *
4878 : * Generates a new geometry which is the region of intersection of the
4879 : * two geometries operated on. The Intersects() method can be used to test if
4880 : * two geometries intersect.
4881 : *
4882 : * Geometry validity is not checked. In case you are unsure of the validity
4883 : * of the input geometries, call IsValid() before, otherwise the result might
4884 : * be wrong.
4885 : *
4886 : * This method is the same as the C function OGR_G_Intersection().
4887 : *
4888 : * This method is built on the GEOS library, check it for the definition
4889 : * of the geometry operation.
4890 : * If OGR is built without the GEOS library, this method will always fail,
4891 : * issuing a CPLE_NotSupported error.
4892 : *
4893 : * @param poOtherGeom the other geometry intersected with "this" geometry.
4894 : *
4895 : * @return a new geometry to be freed by the caller, or NULL if there is no
4896 : * intersection or if an error occurs.
4897 : *
4898 : */
4899 :
4900 : OGRGeometry *
4901 1936 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
4902 :
4903 : {
4904 1936 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
4905 : {
4906 : #ifndef HAVE_SFCGAL
4907 :
4908 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4909 0 : return nullptr;
4910 :
4911 : #else
4912 :
4913 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4914 : if (poThis == nullptr)
4915 : return nullptr;
4916 :
4917 : sfcgal_geometry_t *poOther =
4918 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
4919 : if (poOther == nullptr)
4920 : {
4921 : sfcgal_geometry_delete(poThis);
4922 : return nullptr;
4923 : }
4924 :
4925 : sfcgal_geometry_t *poRes =
4926 : sfcgal_geometry_intersection_3d(poThis, poOther);
4927 : OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4928 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
4929 : poOtherGeom->getSpatialReference() != nullptr &&
4930 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
4931 : h_prodGeom->assignSpatialReference(getSpatialReference());
4932 :
4933 : sfcgal_geometry_delete(poThis);
4934 : sfcgal_geometry_delete(poOther);
4935 : sfcgal_geometry_delete(poRes);
4936 :
4937 : return h_prodGeom;
4938 :
4939 : #endif
4940 : }
4941 :
4942 : else
4943 : {
4944 : #ifndef HAVE_GEOS
4945 :
4946 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4947 : return nullptr;
4948 :
4949 : #else
4950 1936 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
4951 : #endif /* HAVE_GEOS */
4952 : }
4953 : }
4954 :
4955 : /************************************************************************/
4956 : /* OGR_G_Intersection() */
4957 : /************************************************************************/
4958 :
4959 : /**
4960 : * \brief Compute intersection.
4961 : *
4962 : * Generates a new geometry which is the region of intersection of the
4963 : * two geometries operated on. The OGR_G_Intersects() function can be used to
4964 : * test if two geometries intersect.
4965 : *
4966 : * Geometry validity is not checked. In case you are unsure of the validity
4967 : * of the input geometries, call IsValid() before, otherwise the result might
4968 : * be wrong.
4969 : *
4970 : * This function is the same as the C++ method OGRGeometry::Intersection().
4971 : *
4972 : * This function is built on the GEOS library, check it for the definition
4973 : * of the geometry operation.
4974 : * If OGR is built without the GEOS library, this function will always fail,
4975 : * issuing a CPLE_NotSupported error.
4976 : *
4977 : * @param hThis the geometry.
4978 : * @param hOther the other geometry.
4979 : *
4980 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4981 : * or NULL if there is not intersection of if an error occurs.
4982 : */
4983 :
4984 12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
4985 :
4986 : {
4987 12 : VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
4988 :
4989 24 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
4990 24 : OGRGeometry::FromHandle(hOther)));
4991 : }
4992 :
4993 : /************************************************************************/
4994 : /* Union() */
4995 : /************************************************************************/
4996 :
4997 : /**
4998 : * \brief Compute union.
4999 : *
5000 : * Generates a new geometry which is the region of union of the
5001 : * two geometries operated on.
5002 : *
5003 : * Geometry validity is not checked. In case you are unsure of the validity
5004 : * of the input geometries, call IsValid() before, otherwise the result might
5005 : * be wrong.
5006 : *
5007 : * This method is the same as the C function OGR_G_Union().
5008 : *
5009 : * This method is built on the GEOS library, check it for the definition
5010 : * of the geometry operation.
5011 : * If OGR is built without the GEOS library, this method will always fail,
5012 : * issuing a CPLE_NotSupported error.
5013 : *
5014 : * @param poOtherGeom the other geometry unioned with "this" geometry.
5015 : *
5016 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5017 : */
5018 :
5019 : OGRGeometry *
5020 68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5021 :
5022 : {
5023 68 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5024 : {
5025 : #ifndef HAVE_SFCGAL
5026 :
5027 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5028 0 : return nullptr;
5029 :
5030 : #else
5031 :
5032 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5033 : if (poThis == nullptr)
5034 : return nullptr;
5035 :
5036 : sfcgal_geometry_t *poOther =
5037 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5038 : if (poOther == nullptr)
5039 : {
5040 : sfcgal_geometry_delete(poThis);
5041 : return nullptr;
5042 : }
5043 :
5044 : sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
5045 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5046 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5047 : poOtherGeom->getSpatialReference() != nullptr &&
5048 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5049 : h_prodGeom->assignSpatialReference(getSpatialReference());
5050 :
5051 : sfcgal_geometry_delete(poThis);
5052 : sfcgal_geometry_delete(poOther);
5053 : sfcgal_geometry_delete(poRes);
5054 :
5055 : return h_prodGeom;
5056 :
5057 : #endif
5058 : }
5059 :
5060 : else
5061 : {
5062 : #ifndef HAVE_GEOS
5063 :
5064 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5065 : return nullptr;
5066 :
5067 : #else
5068 68 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
5069 : #endif /* HAVE_GEOS */
5070 : }
5071 : }
5072 :
5073 : /************************************************************************/
5074 : /* OGR_G_Union() */
5075 : /************************************************************************/
5076 :
5077 : /**
5078 : * \brief Compute union.
5079 : *
5080 : * Generates a new geometry which is the region of union of the
5081 : * two geometries operated on.
5082 : *
5083 : * Geometry validity is not checked. In case you are unsure of the validity
5084 : * of the input geometries, call IsValid() before, otherwise the result might
5085 : * be wrong.
5086 : *
5087 : * This function is the same as the C++ method OGRGeometry::Union().
5088 : *
5089 : * This function is built on the GEOS library, check it for the definition
5090 : * of the geometry operation.
5091 : * If OGR is built without the GEOS library, this function will always fail,
5092 : * issuing a CPLE_NotSupported error.
5093 : *
5094 : * @param hThis the geometry.
5095 : * @param hOther the other geometry.
5096 : *
5097 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5098 : * or NULL if an error occurs.
5099 : */
5100 :
5101 10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
5102 :
5103 : {
5104 10 : VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
5105 :
5106 10 : return OGRGeometry::ToHandle(
5107 20 : OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
5108 : }
5109 :
5110 : /************************************************************************/
5111 : /* UnionCascaded() */
5112 : /************************************************************************/
5113 :
5114 : /**
5115 : * \brief Compute union using cascading.
5116 : *
5117 : * Geometry validity is not checked. In case you are unsure of the validity
5118 : * of the input geometries, call IsValid() before, otherwise the result might
5119 : * be wrong.
5120 : *
5121 : * The input geometry must be a MultiPolygon.
5122 : *
5123 : * This method is the same as the C function OGR_G_UnionCascaded().
5124 : *
5125 : * This method is built on the GEOS library, check it for the definition
5126 : * of the geometry operation.
5127 : * If OGR is built without the GEOS library, this method will always fail,
5128 : * issuing a CPLE_NotSupported error.
5129 : *
5130 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5131 : *
5132 : * @since OGR 1.8.0
5133 : *
5134 : * @deprecated Use UnaryUnion() instead
5135 : */
5136 :
5137 2 : OGRGeometry *OGRGeometry::UnionCascaded() const
5138 :
5139 : {
5140 : #ifndef HAVE_GEOS
5141 :
5142 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5143 : return nullptr;
5144 : #else
5145 :
5146 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5147 : if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
5148 : {
5149 : // GEOS < 3.11 crashes on an empty multipolygon input
5150 : auto poRet = new OGRGeometryCollection();
5151 : poRet->assignSpatialReference(getSpatialReference());
5152 : return poRet;
5153 : }
5154 : #endif
5155 2 : OGRGeometry *poOGRProduct = nullptr;
5156 :
5157 2 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5158 2 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5159 2 : if (hThisGeosGeom != nullptr)
5160 : {
5161 2 : GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
5162 2 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5163 :
5164 : poOGRProduct =
5165 2 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5166 : }
5167 2 : freeGEOSContext(hGEOSCtxt);
5168 :
5169 2 : return poOGRProduct;
5170 :
5171 : #endif // HAVE_GEOS
5172 : }
5173 :
5174 : /************************************************************************/
5175 : /* OGR_G_UnionCascaded() */
5176 : /************************************************************************/
5177 :
5178 : /**
5179 : * \brief Compute union using cascading.
5180 : *
5181 : * Geometry validity is not checked. In case you are unsure of the validity
5182 : * of the input geometries, call IsValid() before, otherwise the result might
5183 : * be wrong.
5184 : *
5185 : * The input geometry must be a MultiPolygon.
5186 : *
5187 : * This function is the same as the C++ method OGRGeometry::UnionCascaded().
5188 : *
5189 : * This function is built on the GEOS library, check it for the definition
5190 : * of the geometry operation.
5191 : * If OGR is built without the GEOS library, this function will always fail,
5192 : * issuing a CPLE_NotSupported error.
5193 : *
5194 : * @param hThis the geometry.
5195 : *
5196 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5197 : * or NULL if an error occurs.
5198 : *
5199 : * @deprecated Use OGR_G_UnaryUnion() instead
5200 : */
5201 :
5202 2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
5203 :
5204 : {
5205 2 : VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
5206 :
5207 2 : return OGRGeometry::ToHandle(
5208 4 : OGRGeometry::FromHandle(hThis)->UnionCascaded());
5209 : }
5210 :
5211 : /************************************************************************/
5212 : /* UnaryUnion() */
5213 : /************************************************************************/
5214 :
5215 : /**
5216 : * \brief Returns the union of all components of a single geometry.
5217 : *
5218 : * Usually used to convert a collection into the smallest set of polygons that
5219 : * cover the same area.
5220 : *
5221 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5222 : *
5223 : * This method is the same as the C function OGR_G_UnaryUnion().
5224 : *
5225 : * This method is built on the GEOS library, check it for the definition
5226 : * of the geometry operation.
5227 : * If OGR is built without the GEOS library, this method will always fail,
5228 : * issuing a CPLE_NotSupported error.
5229 : *
5230 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5231 : *
5232 : * @since GDAL 3.7
5233 : */
5234 :
5235 625 : OGRGeometry *OGRGeometry::UnaryUnion() const
5236 :
5237 : {
5238 : #ifndef HAVE_GEOS
5239 :
5240 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5241 : return nullptr;
5242 : #else
5243 :
5244 : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5245 : if (IsEmpty())
5246 : {
5247 : // GEOS < 3.11 crashes on an empty geometry
5248 : auto poRet = new OGRGeometryCollection();
5249 : poRet->assignSpatialReference(getSpatialReference());
5250 : return poRet;
5251 : }
5252 : #endif
5253 625 : OGRGeometry *poOGRProduct = nullptr;
5254 :
5255 625 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5256 625 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5257 625 : if (hThisGeosGeom != nullptr)
5258 : {
5259 625 : GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5260 625 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5261 :
5262 : poOGRProduct =
5263 625 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5264 : }
5265 625 : freeGEOSContext(hGEOSCtxt);
5266 :
5267 625 : return poOGRProduct;
5268 :
5269 : #endif // HAVE_GEOS
5270 : }
5271 :
5272 : /************************************************************************/
5273 : /* OGR_G_UnaryUnion() */
5274 : /************************************************************************/
5275 :
5276 : /**
5277 : * \brief Returns the union of all components of a single geometry.
5278 : *
5279 : * Usually used to convert a collection into the smallest set of polygons that
5280 : * cover the same area.
5281 : *
5282 : * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5283 : *
5284 : * Geometry validity is not checked. In case you are unsure of the validity
5285 : * of the input geometries, call IsValid() before, otherwise the result might
5286 : * be wrong.
5287 : *
5288 : * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5289 : *
5290 : * This function is built on the GEOS library, check it for the definition
5291 : * of the geometry operation.
5292 : * If OGR is built without the GEOS library, this function will always fail,
5293 : * issuing a CPLE_NotSupported error.
5294 : *
5295 : * @param hThis the geometry.
5296 : *
5297 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5298 : * or NULL if an error occurs.
5299 : *
5300 : * @since GDAL 3.7
5301 : */
5302 :
5303 3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5304 :
5305 : {
5306 3 : VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5307 :
5308 3 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5309 : }
5310 :
5311 : /************************************************************************/
5312 : /* Difference() */
5313 : /************************************************************************/
5314 :
5315 : /**
5316 : * \brief Compute difference.
5317 : *
5318 : * Generates a new geometry which is the region of this geometry with the
5319 : * region of the second geometry removed.
5320 : *
5321 : * Geometry validity is not checked. In case you are unsure of the validity
5322 : * of the input geometries, call IsValid() before, otherwise the result might
5323 : * be wrong.
5324 : *
5325 : * This method is the same as the C function OGR_G_Difference().
5326 : *
5327 : * This method is built on the GEOS library, check it for the definition
5328 : * of the geometry operation.
5329 : * If OGR is built without the GEOS library, this method will always fail,
5330 : * issuing a CPLE_NotSupported error.
5331 : *
5332 : * @param poOtherGeom the other geometry removed from "this" geometry.
5333 : *
5334 : * @return a new geometry to be freed by the caller, or NULL if the difference
5335 : * is empty or if an error occurs.
5336 : */
5337 :
5338 : OGRGeometry *
5339 746 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5340 :
5341 : {
5342 746 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5343 : {
5344 : #ifndef HAVE_SFCGAL
5345 :
5346 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5347 0 : return nullptr;
5348 :
5349 : #else
5350 :
5351 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5352 : if (poThis == nullptr)
5353 : return nullptr;
5354 :
5355 : sfcgal_geometry_t *poOther =
5356 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5357 : if (poOther == nullptr)
5358 : {
5359 : sfcgal_geometry_delete(poThis);
5360 : return nullptr;
5361 : }
5362 :
5363 : sfcgal_geometry_t *poRes =
5364 : sfcgal_geometry_difference_3d(poThis, poOther);
5365 : OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5366 : if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5367 : poOtherGeom->getSpatialReference() != nullptr &&
5368 : poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5369 : h_prodGeom->assignSpatialReference(getSpatialReference());
5370 :
5371 : sfcgal_geometry_delete(poThis);
5372 : sfcgal_geometry_delete(poOther);
5373 : sfcgal_geometry_delete(poRes);
5374 :
5375 : return h_prodGeom;
5376 :
5377 : #endif
5378 : }
5379 :
5380 : else
5381 : {
5382 : #ifndef HAVE_GEOS
5383 :
5384 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5385 : return nullptr;
5386 :
5387 : #else
5388 746 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5389 : #endif /* HAVE_GEOS */
5390 : }
5391 : }
5392 :
5393 : /************************************************************************/
5394 : /* OGR_G_Difference() */
5395 : /************************************************************************/
5396 :
5397 : /**
5398 : * \brief Compute difference.
5399 : *
5400 : * Generates a new geometry which is the region of this geometry with the
5401 : * region of the other geometry removed.
5402 : *
5403 : * Geometry validity is not checked. In case you are unsure of the validity
5404 : * of the input geometries, call IsValid() before, otherwise the result might
5405 : * be wrong.
5406 : *
5407 : * This function is the same as the C++ method OGRGeometry::Difference().
5408 : *
5409 : * This function is built on the GEOS library, check it for the definition
5410 : * of the geometry operation.
5411 : * If OGR is built without the GEOS library, this function will always fail,
5412 : * issuing a CPLE_NotSupported error.
5413 : *
5414 : * @param hThis the geometry.
5415 : * @param hOther the other geometry.
5416 : *
5417 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5418 : * or NULL if the difference is empty or if an error occurs.
5419 : */
5420 :
5421 6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5422 :
5423 : {
5424 6 : VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5425 :
5426 12 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5427 12 : OGRGeometry::FromHandle(hOther)));
5428 : }
5429 :
5430 : /************************************************************************/
5431 : /* SymDifference() */
5432 : /************************************************************************/
5433 :
5434 : /**
5435 : * \brief Compute symmetric difference.
5436 : *
5437 : * Generates a new geometry which is the symmetric difference of this
5438 : * geometry and the second geometry passed into the method.
5439 : *
5440 : * Geometry validity is not checked. In case you are unsure of the validity
5441 : * of the input geometries, call IsValid() before, otherwise the result might
5442 : * be wrong.
5443 : *
5444 : * This method is the same as the C function OGR_G_SymDifference().
5445 : *
5446 : * This method is built on the GEOS library, check it for the definition
5447 : * of the geometry operation.
5448 : * If OGR is built without the GEOS library, this method will always fail,
5449 : * issuing a CPLE_NotSupported error.
5450 : *
5451 : * @param poOtherGeom the other geometry.
5452 : *
5453 : * @return a new geometry to be freed by the caller, or NULL if the difference
5454 : * is empty or if an error occurs.
5455 : *
5456 : * @since OGR 1.8.0
5457 : */
5458 :
5459 7 : OGRGeometry *OGRGeometry::SymDifference(
5460 : UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5461 :
5462 : {
5463 7 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5464 : {
5465 : #ifndef HAVE_SFCGAL
5466 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5467 0 : return nullptr;
5468 : #else
5469 : OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5470 : if (poFirstDifference == nullptr)
5471 : return nullptr;
5472 :
5473 : OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5474 : if (poOtherDifference == nullptr)
5475 : {
5476 : delete poFirstDifference;
5477 : return nullptr;
5478 : }
5479 :
5480 : OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5481 : delete poFirstDifference;
5482 : delete poOtherDifference;
5483 : return poSymDiff;
5484 : #endif
5485 : }
5486 :
5487 : #ifndef HAVE_GEOS
5488 :
5489 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5490 : return nullptr;
5491 :
5492 : #else
5493 7 : return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5494 : #endif // HAVE_GEOS
5495 : }
5496 :
5497 : //! @cond Doxygen_Suppress
5498 : /**
5499 : * \brief Compute symmetric difference (deprecated)
5500 : *
5501 : * @deprecated
5502 : *
5503 : * @see OGRGeometry::SymDifference()
5504 : */
5505 : OGRGeometry *
5506 0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5507 :
5508 : {
5509 0 : return SymDifference(poOtherGeom);
5510 : }
5511 :
5512 : //! @endcond
5513 :
5514 : /************************************************************************/
5515 : /* OGR_G_SymDifference() */
5516 : /************************************************************************/
5517 :
5518 : /**
5519 : * \brief Compute symmetric difference.
5520 : *
5521 : * Generates a new geometry which is the symmetric difference of this
5522 : * geometry and the other geometry.
5523 : *
5524 : * Geometry validity is not checked. In case you are unsure of the validity
5525 : * of the input geometries, call IsValid() before, otherwise the result might
5526 : * be wrong.
5527 : *
5528 : * This function is the same as the C++ method
5529 : * OGRGeometry::SymmetricDifference().
5530 : *
5531 : * This function is built on the GEOS library, check it for the definition
5532 : * of the geometry operation.
5533 : * If OGR is built without the GEOS library, this function will always fail,
5534 : * issuing a CPLE_NotSupported error.
5535 : *
5536 : * @param hThis the geometry.
5537 : * @param hOther the other geometry.
5538 : *
5539 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5540 : * or NULL if the difference is empty or if an error occurs.
5541 : *
5542 : * @since OGR 1.8.0
5543 : */
5544 :
5545 7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5546 :
5547 : {
5548 7 : VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5549 :
5550 14 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5551 14 : OGRGeometry::FromHandle(hOther)));
5552 : }
5553 :
5554 : /**
5555 : * \brief Compute symmetric difference (deprecated)
5556 : *
5557 : * @deprecated
5558 : *
5559 : * @see OGR_G_SymmetricDifference()
5560 : */
5561 0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5562 :
5563 : {
5564 0 : VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5565 :
5566 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5567 0 : OGRGeometry::FromHandle(hOther)));
5568 : }
5569 :
5570 : /************************************************************************/
5571 : /* Disjoint() */
5572 : /************************************************************************/
5573 :
5574 : /**
5575 : * \brief Test for disjointness.
5576 : *
5577 : * Tests if this geometry and the other passed into the method are disjoint.
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 method is the same as the C function OGR_G_Disjoint().
5584 : *
5585 : * This method 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 method will always fail,
5588 : * issuing a CPLE_NotSupported error.
5589 : *
5590 : * @param poOtherGeom the geometry to compare to this geometry.
5591 : *
5592 : * @return TRUE if they are disjoint, otherwise FALSE.
5593 : */
5594 :
5595 : OGRBoolean
5596 8 : OGRGeometry::Disjoint(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5597 :
5598 : {
5599 : #ifndef HAVE_GEOS
5600 :
5601 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5602 : return FALSE;
5603 :
5604 : #else
5605 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
5606 : #endif // HAVE_GEOS
5607 : }
5608 :
5609 : /************************************************************************/
5610 : /* OGR_G_Disjoint() */
5611 : /************************************************************************/
5612 :
5613 : /**
5614 : * \brief Test for disjointness.
5615 : *
5616 : * Tests if this geometry and the other geometry are disjoint.
5617 : *
5618 : * Geometry validity is not checked. In case you are unsure of the validity
5619 : * of the input geometries, call IsValid() before, otherwise the result might
5620 : * be wrong.
5621 : *
5622 : * This function is the same as the C++ method OGRGeometry::Disjoint().
5623 : *
5624 : * This function is built on the GEOS library, check it for the definition
5625 : * of the geometry operation.
5626 : * If OGR is built without the GEOS library, this function will always fail,
5627 : * issuing a CPLE_NotSupported error.
5628 : *
5629 : * @param hThis the geometry to compare.
5630 : * @param hOther the other geometry to compare.
5631 : *
5632 : * @return TRUE if they are disjoint, otherwise FALSE.
5633 : */
5634 8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
5635 :
5636 : {
5637 8 : VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
5638 :
5639 16 : return OGRGeometry::FromHandle(hThis)->Disjoint(
5640 8 : OGRGeometry::FromHandle(hOther));
5641 : }
5642 :
5643 : /************************************************************************/
5644 : /* Touches() */
5645 : /************************************************************************/
5646 :
5647 : /**
5648 : * \brief Test for touching.
5649 : *
5650 : * Tests if this geometry and the other passed into the method are touching.
5651 : *
5652 : * Geometry validity is not checked. In case you are unsure of the validity
5653 : * of the input geometries, call IsValid() before, otherwise the result might
5654 : * be wrong.
5655 : *
5656 : * This method is the same as the C function OGR_G_Touches().
5657 : *
5658 : * This method is built on the GEOS library, check it for the definition
5659 : * of the geometry operation.
5660 : * If OGR is built without the GEOS library, this method will always fail,
5661 : * issuing a CPLE_NotSupported error.
5662 : *
5663 : * @param poOtherGeom the geometry to compare to this geometry.
5664 : *
5665 : * @return TRUE if they are touching, otherwise FALSE.
5666 : */
5667 :
5668 : OGRBoolean
5669 11 : OGRGeometry::Touches(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5670 :
5671 : {
5672 : #ifndef HAVE_GEOS
5673 :
5674 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5675 : return FALSE;
5676 :
5677 : #else
5678 11 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
5679 : #endif // HAVE_GEOS
5680 : }
5681 :
5682 : /************************************************************************/
5683 : /* OGR_G_Touches() */
5684 : /************************************************************************/
5685 : /**
5686 : * \brief Test for touching.
5687 : *
5688 : * Tests if this geometry and the other geometry are touching.
5689 : *
5690 : * Geometry validity is not checked. In case you are unsure of the validity
5691 : * of the input geometries, call IsValid() before, otherwise the result might
5692 : * be wrong.
5693 : *
5694 : * This function is the same as the C++ method OGRGeometry::Touches().
5695 : *
5696 : * This function is built on the GEOS library, check it for the definition
5697 : * of the geometry operation.
5698 : * If OGR is built without the GEOS library, this function will always fail,
5699 : * issuing a CPLE_NotSupported error.
5700 : *
5701 : * @param hThis the geometry to compare.
5702 : * @param hOther the other geometry to compare.
5703 : *
5704 : * @return TRUE if they are touching, otherwise FALSE.
5705 : */
5706 :
5707 8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
5708 :
5709 : {
5710 8 : VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
5711 :
5712 16 : return OGRGeometry::FromHandle(hThis)->Touches(
5713 8 : OGRGeometry::FromHandle(hOther));
5714 : }
5715 :
5716 : /************************************************************************/
5717 : /* Crosses() */
5718 : /************************************************************************/
5719 :
5720 : /**
5721 : * \brief Test for crossing.
5722 : *
5723 : * Tests if this geometry and the other passed into the method are crossing.
5724 : *
5725 : * Geometry validity is not checked. In case you are unsure of the validity
5726 : * of the input geometries, call IsValid() before, otherwise the result might
5727 : * be wrong.
5728 : *
5729 : * This method is the same as the C function OGR_G_Crosses().
5730 : *
5731 : * This method is built on the GEOS library, check it for the definition
5732 : * of the geometry operation.
5733 : * If OGR is built without the GEOS library, this method will always fail,
5734 : * issuing a CPLE_NotSupported error.
5735 : *
5736 : * @param poOtherGeom the geometry to compare to this geometry.
5737 : *
5738 : * @return TRUE if they are crossing, otherwise FALSE.
5739 : */
5740 :
5741 : OGRBoolean
5742 8 : OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5743 :
5744 : {
5745 8 : if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5746 : {
5747 : #ifndef HAVE_SFCGAL
5748 :
5749 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5750 0 : return FALSE;
5751 :
5752 : #else
5753 :
5754 : sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5755 : if (poThis == nullptr)
5756 : return FALSE;
5757 :
5758 : sfcgal_geometry_t *poOther =
5759 : OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5760 : if (poOther == nullptr)
5761 : {
5762 : sfcgal_geometry_delete(poThis);
5763 : return FALSE;
5764 : }
5765 :
5766 : int res = sfcgal_geometry_intersects_3d(poThis, poOther);
5767 :
5768 : sfcgal_geometry_delete(poThis);
5769 : sfcgal_geometry_delete(poOther);
5770 :
5771 : return (res == 1) ? TRUE : FALSE;
5772 :
5773 : #endif
5774 : }
5775 :
5776 : else
5777 : {
5778 :
5779 : #ifndef HAVE_GEOS
5780 :
5781 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5782 : return FALSE;
5783 :
5784 : #else
5785 8 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
5786 : #endif /* HAVE_GEOS */
5787 : }
5788 : }
5789 :
5790 : /************************************************************************/
5791 : /* OGR_G_Crosses() */
5792 : /************************************************************************/
5793 : /**
5794 : * \brief Test for crossing.
5795 : *
5796 : * Tests if this geometry and the other geometry are crossing.
5797 : *
5798 : * Geometry validity is not checked. In case you are unsure of the validity
5799 : * of the input geometries, call IsValid() before, otherwise the result might
5800 : * be wrong.
5801 : *
5802 : * This function is the same as the C++ method OGRGeometry::Crosses().
5803 : *
5804 : * This function is built on the GEOS library, check it for the definition
5805 : * of the geometry operation.
5806 : * If OGR is built without the GEOS library, this function will always fail,
5807 : * issuing a CPLE_NotSupported error.
5808 : *
5809 : * @param hThis the geometry to compare.
5810 : * @param hOther the other geometry to compare.
5811 : *
5812 : * @return TRUE if they are crossing, otherwise FALSE.
5813 : */
5814 :
5815 8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
5816 :
5817 : {
5818 8 : VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
5819 :
5820 16 : return OGRGeometry::FromHandle(hThis)->Crosses(
5821 8 : OGRGeometry::FromHandle(hOther));
5822 : }
5823 :
5824 : /************************************************************************/
5825 : /* Within() */
5826 : /************************************************************************/
5827 :
5828 : /**
5829 : * \brief Test for containment.
5830 : *
5831 : * Tests if actual geometry object is within the passed geometry.
5832 : *
5833 : * Geometry validity is not checked. In case you are unsure of the validity
5834 : * of the input geometries, call IsValid() before, otherwise the result might
5835 : * be wrong.
5836 : *
5837 : * This method is the same as the C function OGR_G_Within().
5838 : *
5839 : * This method is built on the GEOS library, check it for the definition
5840 : * of the geometry operation.
5841 : * If OGR is built without the GEOS library, this method will always fail,
5842 : * issuing a CPLE_NotSupported error.
5843 : *
5844 : * @param poOtherGeom the geometry to compare to this geometry.
5845 : *
5846 : * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
5847 : */
5848 :
5849 : OGRBoolean
5850 21386 : OGRGeometry::Within(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5851 :
5852 : {
5853 : #ifndef HAVE_GEOS
5854 :
5855 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5856 : return FALSE;
5857 :
5858 : #else
5859 21386 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
5860 : #endif // HAVE_GEOS
5861 : }
5862 :
5863 : /************************************************************************/
5864 : /* OGR_G_Within() */
5865 : /************************************************************************/
5866 :
5867 : /**
5868 : * \brief Test for containment.
5869 : *
5870 : * Tests if this geometry is within the other geometry.
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 function is the same as the C++ method OGRGeometry::Within().
5877 : *
5878 : * This function 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 function will always fail,
5881 : * issuing a CPLE_NotSupported error.
5882 : *
5883 : * @param hThis the geometry to compare.
5884 : * @param hOther the other geometry to compare.
5885 : *
5886 : * @return TRUE if hThis is within hOther, otherwise FALSE.
5887 : */
5888 6343 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
5889 :
5890 : {
5891 6343 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
5892 :
5893 12686 : return OGRGeometry::FromHandle(hThis)->Within(
5894 6343 : OGRGeometry::FromHandle(hOther));
5895 : }
5896 :
5897 : /************************************************************************/
5898 : /* Contains() */
5899 : /************************************************************************/
5900 :
5901 : /**
5902 : * \brief Test for containment.
5903 : *
5904 : * Tests if actual geometry object contains the passed geometry.
5905 : *
5906 : * Geometry validity is not checked. In case you are unsure of the validity
5907 : * of the input geometries, call IsValid() before, otherwise the result might
5908 : * be wrong.
5909 : *
5910 : * This method is the same as the C function OGR_G_Contains().
5911 : *
5912 : * This method is built on the GEOS library, check it for the definition
5913 : * of the geometry operation.
5914 : * If OGR is built without the GEOS library, this method will always fail,
5915 : * issuing a CPLE_NotSupported error.
5916 : *
5917 : * @param poOtherGeom the geometry to compare to this geometry.
5918 : *
5919 : * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
5920 : */
5921 :
5922 : OGRBoolean
5923 47 : OGRGeometry::Contains(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5924 :
5925 : {
5926 : #ifndef HAVE_GEOS
5927 :
5928 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5929 : return FALSE;
5930 :
5931 : #else
5932 47 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
5933 : #endif // HAVE_GEOS
5934 : }
5935 :
5936 : /************************************************************************/
5937 : /* OGR_G_Contains() */
5938 : /************************************************************************/
5939 :
5940 : /**
5941 : * \brief Test for containment.
5942 : *
5943 : * Tests if this geometry contains the other geometry.
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 function is the same as the C++ method OGRGeometry::Contains().
5950 : *
5951 : * This function 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 function will always fail,
5954 : * issuing a CPLE_NotSupported error.
5955 : *
5956 : * @param hThis the geometry to compare.
5957 : * @param hOther the other geometry to compare.
5958 : *
5959 : * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
5960 : */
5961 11 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
5962 :
5963 : {
5964 11 : VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
5965 :
5966 22 : return OGRGeometry::FromHandle(hThis)->Contains(
5967 11 : OGRGeometry::FromHandle(hOther));
5968 : }
5969 :
5970 : /************************************************************************/
5971 : /* Overlaps() */
5972 : /************************************************************************/
5973 :
5974 : /**
5975 : * \brief Test for overlap.
5976 : *
5977 : * Tests if this geometry and the other passed into the method overlap, that is
5978 : * their intersection has a non-zero area.
5979 : *
5980 : * Geometry validity is not checked. In case you are unsure of the validity
5981 : * of the input geometries, call IsValid() before, otherwise the result might
5982 : * be wrong.
5983 : *
5984 : * This method is the same as the C function OGR_G_Overlaps().
5985 : *
5986 : * This method is built on the GEOS library, check it for the definition
5987 : * of the geometry operation.
5988 : * If OGR is built without the GEOS library, this method will always fail,
5989 : * issuing a CPLE_NotSupported error.
5990 : *
5991 : * @param poOtherGeom the geometry to compare to this geometry.
5992 : *
5993 : * @return TRUE if they are overlapping, otherwise FALSE.
5994 : */
5995 :
5996 : OGRBoolean
5997 7 : OGRGeometry::Overlaps(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5998 :
5999 : {
6000 : #ifndef HAVE_GEOS
6001 :
6002 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6003 : return FALSE;
6004 :
6005 : #else
6006 7 : return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6007 : #endif // HAVE_GEOS
6008 : }
6009 :
6010 : /************************************************************************/
6011 : /* OGR_G_Overlaps() */
6012 : /************************************************************************/
6013 : /**
6014 : * \brief Test for overlap.
6015 : *
6016 : * Tests if this geometry and the other geometry overlap, that is their
6017 : * intersection has a non-zero area.
6018 : *
6019 : * Geometry validity is not checked. In case you are unsure of the validity
6020 : * of the input geometries, call IsValid() before, otherwise the result might
6021 : * be wrong.
6022 : *
6023 : * This function is the same as the C++ method OGRGeometry::Overlaps().
6024 : *
6025 : * This function is built on the GEOS library, check it for the definition
6026 : * of the geometry operation.
6027 : * If OGR is built without the GEOS library, this function will always fail,
6028 : * issuing a CPLE_NotSupported error.
6029 : *
6030 : * @param hThis the geometry to compare.
6031 : * @param hOther the other geometry to compare.
6032 : *
6033 : * @return TRUE if they are overlapping, otherwise FALSE.
6034 : */
6035 :
6036 7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6037 :
6038 : {
6039 7 : VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6040 :
6041 14 : return OGRGeometry::FromHandle(hThis)->Overlaps(
6042 7 : OGRGeometry::FromHandle(hOther));
6043 : }
6044 :
6045 : /************************************************************************/
6046 : /* closeRings() */
6047 : /************************************************************************/
6048 :
6049 : /**
6050 : * \brief Force rings to be closed.
6051 : *
6052 : * If this geometry, or any contained geometries has polygon rings that
6053 : * are not closed, they will be closed by adding the starting point at
6054 : * the end.
6055 : */
6056 :
6057 1248 : void OGRGeometry::closeRings()
6058 : {
6059 1248 : }
6060 :
6061 : /************************************************************************/
6062 : /* OGR_G_CloseRings() */
6063 : /************************************************************************/
6064 :
6065 : /**
6066 : * \brief Force rings to be closed.
6067 : *
6068 : * If this geometry, or any contained geometries has polygon rings that
6069 : * are not closed, they will be closed by adding the starting point at
6070 : * the end.
6071 : *
6072 : * @param hGeom handle to the geometry.
6073 : */
6074 :
6075 6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
6076 :
6077 : {
6078 6 : VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6079 :
6080 6 : OGRGeometry::FromHandle(hGeom)->closeRings();
6081 : }
6082 :
6083 : /************************************************************************/
6084 : /* Centroid() */
6085 : /************************************************************************/
6086 :
6087 : /**
6088 : * \brief Compute the geometry centroid.
6089 : *
6090 : * The centroid location is applied to the passed in OGRPoint object.
6091 : * The centroid is not necessarily within the geometry.
6092 : *
6093 : * This method relates to the SFCOM ISurface::get_Centroid() method
6094 : * however the current implementation based on GEOS can operate on other
6095 : * geometry types such as multipoint, linestring, geometrycollection such as
6096 : * multipolygons.
6097 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6098 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6099 : * (multipolygons).
6100 : *
6101 : * This function is the same as the C function OGR_G_Centroid().
6102 : *
6103 : * This function is built on the GEOS library, check it for the definition
6104 : * of the geometry operation.
6105 : * If OGR is built without the GEOS library, this function will always fail,
6106 : * issuing a CPLE_NotSupported error.
6107 : *
6108 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6109 : *
6110 : * @since OGR 1.8.0 as a OGRGeometry method (previously was restricted
6111 : * to OGRPolygon)
6112 : */
6113 :
6114 5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6115 :
6116 : {
6117 5 : if (poPoint == nullptr)
6118 0 : return OGRERR_FAILURE;
6119 :
6120 : #ifndef HAVE_GEOS
6121 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6122 : return OGRERR_FAILURE;
6123 :
6124 : #else
6125 :
6126 5 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6127 : GEOSGeom hThisGeosGeom =
6128 5 : exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6129 :
6130 5 : if (hThisGeosGeom != nullptr)
6131 : {
6132 5 : GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6133 5 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6134 :
6135 5 : if (hOtherGeosGeom == nullptr)
6136 : {
6137 0 : freeGEOSContext(hGEOSCtxt);
6138 0 : return OGRERR_FAILURE;
6139 : }
6140 :
6141 : OGRGeometry *poCentroidGeom =
6142 5 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6143 :
6144 5 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6145 :
6146 5 : if (poCentroidGeom == nullptr)
6147 : {
6148 0 : freeGEOSContext(hGEOSCtxt);
6149 0 : return OGRERR_FAILURE;
6150 : }
6151 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6152 : {
6153 0 : delete poCentroidGeom;
6154 0 : freeGEOSContext(hGEOSCtxt);
6155 0 : return OGRERR_FAILURE;
6156 : }
6157 :
6158 5 : if (getSpatialReference() != nullptr)
6159 0 : poCentroidGeom->assignSpatialReference(getSpatialReference());
6160 :
6161 5 : OGRPoint *poCentroid = poCentroidGeom->toPoint();
6162 :
6163 5 : if (!poCentroid->IsEmpty())
6164 : {
6165 4 : poPoint->setX(poCentroid->getX());
6166 4 : poPoint->setY(poCentroid->getY());
6167 : }
6168 : else
6169 : {
6170 1 : poPoint->empty();
6171 : }
6172 :
6173 5 : delete poCentroidGeom;
6174 :
6175 5 : freeGEOSContext(hGEOSCtxt);
6176 5 : return OGRERR_NONE;
6177 : }
6178 : else
6179 : {
6180 0 : freeGEOSContext(hGEOSCtxt);
6181 0 : return OGRERR_FAILURE;
6182 : }
6183 :
6184 : #endif // HAVE_GEOS
6185 : }
6186 :
6187 : /************************************************************************/
6188 : /* OGR_G_Centroid() */
6189 : /************************************************************************/
6190 :
6191 : /**
6192 : * \brief Compute the geometry centroid.
6193 : *
6194 : * The centroid location is applied to the passed in OGRPoint object.
6195 : * The centroid is not necessarily within the geometry.
6196 : *
6197 : * This method relates to the SFCOM ISurface::get_Centroid() method
6198 : * however the current implementation based on GEOS can operate on other
6199 : * geometry types such as multipoint, linestring, geometrycollection such as
6200 : * multipolygons.
6201 : * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6202 : * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6203 : * (multipolygons).
6204 : *
6205 : * This function is the same as the C++ method OGRGeometry::Centroid().
6206 : *
6207 : * This function is built on the GEOS library, check it for the definition
6208 : * of the geometry operation.
6209 : * If OGR is built without the GEOS library, this function will always fail,
6210 : * issuing a CPLE_NotSupported error.
6211 : *
6212 : * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6213 : */
6214 :
6215 5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6216 :
6217 : {
6218 5 : VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6219 :
6220 5 : OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6221 5 : if (poCentroidGeom == nullptr)
6222 0 : return OGRERR_FAILURE;
6223 5 : if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6224 : {
6225 0 : CPLError(CE_Failure, CPLE_AppDefined,
6226 : "Passed wrong geometry type as centroid argument.");
6227 0 : return OGRERR_FAILURE;
6228 : }
6229 :
6230 5 : return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6231 : }
6232 :
6233 : /************************************************************************/
6234 : /* OGR_G_PointOnSurface() */
6235 : /************************************************************************/
6236 :
6237 : /**
6238 : * \brief Returns a point guaranteed to lie on the surface.
6239 : *
6240 : * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6241 : * however the current implementation based on GEOS can operate on other
6242 : * geometry types than the types that are supported by SQL/MM-Part 3 :
6243 : * surfaces (polygons) and multisurfaces (multipolygons).
6244 : *
6245 : * This method is built on the GEOS library, check it for the definition
6246 : * of the geometry operation.
6247 : * If OGR is built without the GEOS library, this method will always fail,
6248 : * issuing a CPLE_NotSupported error.
6249 : *
6250 : * @param hGeom the geometry to operate on.
6251 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6252 : * or NULL if an error occurs.
6253 : *
6254 : * @since OGR 1.10
6255 : */
6256 :
6257 4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6258 :
6259 : {
6260 4 : VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6261 :
6262 : #ifndef HAVE_GEOS
6263 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6264 : return nullptr;
6265 : #else
6266 :
6267 4 : OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6268 :
6269 4 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6270 4 : GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6271 :
6272 4 : if (hThisGeosGeom != nullptr)
6273 : {
6274 : GEOSGeom hOtherGeosGeom =
6275 4 : GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6276 4 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6277 :
6278 4 : if (hOtherGeosGeom == nullptr)
6279 : {
6280 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6281 0 : return nullptr;
6282 : }
6283 :
6284 : OGRGeometry *poInsidePointGeom =
6285 4 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6286 :
6287 4 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6288 :
6289 4 : if (poInsidePointGeom == nullptr)
6290 : {
6291 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6292 0 : return nullptr;
6293 : }
6294 4 : if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6295 : {
6296 0 : delete poInsidePointGeom;
6297 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6298 0 : return nullptr;
6299 : }
6300 :
6301 4 : if (poThis->getSpatialReference() != nullptr)
6302 0 : poInsidePointGeom->assignSpatialReference(
6303 0 : poThis->getSpatialReference());
6304 :
6305 4 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6306 4 : return OGRGeometry::ToHandle(poInsidePointGeom);
6307 : }
6308 :
6309 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
6310 0 : return nullptr;
6311 : #endif
6312 : }
6313 :
6314 : /************************************************************************/
6315 : /* PointOnSurfaceInternal() */
6316 : /************************************************************************/
6317 :
6318 : //! @cond Doxygen_Suppress
6319 0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6320 : {
6321 0 : if (poPoint == nullptr || poPoint->IsEmpty())
6322 0 : return OGRERR_FAILURE;
6323 :
6324 0 : OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6325 : OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6326 0 : if (hInsidePoint == nullptr)
6327 0 : return OGRERR_FAILURE;
6328 :
6329 0 : OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6330 0 : if (poInsidePoint->IsEmpty())
6331 : {
6332 0 : poPoint->empty();
6333 : }
6334 : else
6335 : {
6336 0 : poPoint->setX(poInsidePoint->getX());
6337 0 : poPoint->setY(poInsidePoint->getY());
6338 : }
6339 :
6340 0 : OGR_G_DestroyGeometry(hInsidePoint);
6341 :
6342 0 : return OGRERR_NONE;
6343 : }
6344 :
6345 : //! @endcond
6346 :
6347 : /************************************************************************/
6348 : /* Simplify() */
6349 : /************************************************************************/
6350 :
6351 : /**
6352 : * \brief Simplify the geometry.
6353 : *
6354 : * This function is the same as the C function OGR_G_Simplify().
6355 : *
6356 : * This function is built on the GEOS library, check it for the definition
6357 : * of the geometry operation.
6358 : * If OGR is built without the GEOS library, this function will always fail,
6359 : * issuing a CPLE_NotSupported error.
6360 : *
6361 : * @param dTolerance the distance tolerance for the simplification.
6362 : *
6363 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6364 : *
6365 : * @since OGR 1.8.0
6366 : */
6367 :
6368 55 : OGRGeometry *OGRGeometry::Simplify(UNUSED_IF_NO_GEOS double dTolerance) const
6369 :
6370 : {
6371 : #ifndef HAVE_GEOS
6372 :
6373 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6374 : return nullptr;
6375 :
6376 : #else
6377 55 : OGRGeometry *poOGRProduct = nullptr;
6378 :
6379 55 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6380 55 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6381 55 : if (hThisGeosGeom != nullptr)
6382 : {
6383 : GEOSGeom hGeosProduct =
6384 55 : GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6385 55 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6386 : poOGRProduct =
6387 55 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6388 : }
6389 55 : freeGEOSContext(hGEOSCtxt);
6390 55 : return poOGRProduct;
6391 :
6392 : #endif // HAVE_GEOS
6393 : }
6394 :
6395 : /************************************************************************/
6396 : /* OGR_G_Simplify() */
6397 : /************************************************************************/
6398 :
6399 : /**
6400 : * \brief Compute a simplified geometry.
6401 : *
6402 : * This function is the same as the C++ method OGRGeometry::Simplify().
6403 : *
6404 : * This function is built on the GEOS library, check it for the definition
6405 : * of the geometry operation.
6406 : * If OGR is built without the GEOS library, this function will always fail,
6407 : * issuing a CPLE_NotSupported error.
6408 : *
6409 : * @param hThis the geometry.
6410 : * @param dTolerance the distance tolerance for the simplification.
6411 : *
6412 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6413 : * or NULL if an error occurs.
6414 : *
6415 : * @since OGR 1.8.0
6416 : */
6417 :
6418 1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6419 :
6420 : {
6421 1 : VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6422 1 : return OGRGeometry::ToHandle(
6423 2 : OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6424 : }
6425 :
6426 : /************************************************************************/
6427 : /* SimplifyPreserveTopology() */
6428 : /************************************************************************/
6429 :
6430 : /**
6431 : * \brief Simplify the geometry while preserving topology.
6432 : *
6433 : * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6434 : *
6435 : * This function is built on the GEOS library, check it for the definition
6436 : * of the geometry operation.
6437 : * If OGR is built without the GEOS library, this function will always fail,
6438 : * issuing a CPLE_NotSupported error.
6439 : *
6440 : * @param dTolerance the distance tolerance for the simplification.
6441 : *
6442 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6443 : *
6444 : * @since OGR 1.9.0
6445 : */
6446 :
6447 : OGRGeometry *
6448 17 : OGRGeometry::SimplifyPreserveTopology(UNUSED_IF_NO_GEOS double dTolerance) const
6449 :
6450 : {
6451 : #ifndef HAVE_GEOS
6452 :
6453 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6454 : return nullptr;
6455 :
6456 : #else
6457 17 : OGRGeometry *poOGRProduct = nullptr;
6458 :
6459 17 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6460 17 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6461 17 : if (hThisGeosGeom != nullptr)
6462 : {
6463 17 : GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6464 : hGEOSCtxt, hThisGeosGeom, dTolerance);
6465 17 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6466 : poOGRProduct =
6467 17 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6468 : }
6469 17 : freeGEOSContext(hGEOSCtxt);
6470 17 : return poOGRProduct;
6471 :
6472 : #endif // HAVE_GEOS
6473 : }
6474 :
6475 : /************************************************************************/
6476 : /* OGR_G_SimplifyPreserveTopology() */
6477 : /************************************************************************/
6478 :
6479 : /**
6480 : * \brief Simplify the geometry while preserving topology.
6481 : *
6482 : * This function is the same as the C++ method
6483 : * OGRGeometry::SimplifyPreserveTopology().
6484 : *
6485 : * This function is built on the GEOS library, check it for the definition
6486 : * of the geometry operation.
6487 : * If OGR is built without the GEOS library, this function will always fail,
6488 : * issuing a CPLE_NotSupported error.
6489 : *
6490 : * @param hThis the geometry.
6491 : * @param dTolerance the distance tolerance for the simplification.
6492 : *
6493 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6494 : * or NULL if an error occurs.
6495 : *
6496 : * @since OGR 1.9.0
6497 : */
6498 :
6499 1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6500 : double dTolerance)
6501 :
6502 : {
6503 1 : VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6504 1 : return OGRGeometry::ToHandle(
6505 1 : OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6506 : }
6507 :
6508 : /************************************************************************/
6509 : /* roundCoordinates() */
6510 : /************************************************************************/
6511 :
6512 : /** Round coordinates of the geometry to the specified precision.
6513 : *
6514 : * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6515 : * will return valid geometries, whereas roundCoordinates() does not make
6516 : * such guarantee and may return geometries with invalidities, if they are
6517 : * not compatible of the specified precision. roundCoordinates() supports
6518 : * curve geometries, whereas SetPrecision() does not currently.
6519 : *
6520 : * One use case for roundCoordinates() is to undo the effect of
6521 : * quantizeCoordinates().
6522 : *
6523 : * @param sPrecision Contains the precision requirements.
6524 : * @since GDAL 3.9
6525 : */
6526 39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6527 : {
6528 : struct Rounder : public OGRDefaultGeometryVisitor
6529 : {
6530 : const OGRGeomCoordinatePrecision &m_precision;
6531 : const double m_invXYResolution;
6532 : const double m_invZResolution;
6533 : const double m_invMResolution;
6534 :
6535 39 : explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6536 39 : : m_precision(sPrecisionIn),
6537 39 : m_invXYResolution(m_precision.dfXYResolution !=
6538 : OGRGeomCoordinatePrecision::UNKNOWN
6539 39 : ? 1.0 / m_precision.dfXYResolution
6540 : : 0.0),
6541 39 : m_invZResolution(m_precision.dfZResolution !=
6542 : OGRGeomCoordinatePrecision::UNKNOWN
6543 39 : ? 1.0 / m_precision.dfZResolution
6544 : : 0.0),
6545 39 : m_invMResolution(m_precision.dfMResolution !=
6546 : OGRGeomCoordinatePrecision::UNKNOWN
6547 39 : ? 1.0 / m_precision.dfMResolution
6548 117 : : 0.0)
6549 : {
6550 39 : }
6551 :
6552 : using OGRDefaultGeometryVisitor::visit;
6553 :
6554 379 : void visit(OGRPoint *poPoint) override
6555 : {
6556 379 : if (m_precision.dfXYResolution !=
6557 : OGRGeomCoordinatePrecision::UNKNOWN)
6558 : {
6559 379 : poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6560 379 : m_precision.dfXYResolution);
6561 379 : poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6562 379 : m_precision.dfXYResolution);
6563 : }
6564 758 : if (m_precision.dfZResolution !=
6565 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6566 4 : poPoint->Is3D())
6567 : {
6568 4 : poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6569 4 : m_precision.dfZResolution);
6570 : }
6571 758 : if (m_precision.dfMResolution !=
6572 383 : OGRGeomCoordinatePrecision::UNKNOWN &&
6573 4 : poPoint->IsMeasured())
6574 : {
6575 4 : poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6576 4 : m_precision.dfMResolution);
6577 : }
6578 379 : }
6579 : };
6580 :
6581 78 : Rounder rounder(sPrecision);
6582 39 : accept(&rounder);
6583 39 : }
6584 :
6585 : /************************************************************************/
6586 : /* SetPrecision() */
6587 : /************************************************************************/
6588 :
6589 : /** Set the geometry's precision, rounding all its coordinates to the precision
6590 : * grid, and making sure the geometry is still valid.
6591 : *
6592 : * This is a stronger version of roundCoordinates().
6593 : *
6594 : * Note that at time of writing GEOS does no supported curve geometries. So
6595 : * currently if this function is called on such a geometry, OGR will first call
6596 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6597 : * that it is unlikely to yield to the expected result.
6598 : *
6599 : * This function is the same as the C function OGR_G_SetPrecision().
6600 : *
6601 : * This function is built on the GEOSGeom_setPrecision_r() function of the
6602 : * GEOS library. Check it for the definition of the geometry operation.
6603 : * If OGR is built without the GEOS library, this function will always fail,
6604 : * issuing a CPLE_NotSupported error.
6605 : *
6606 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
6607 : * precision.
6608 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
6609 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
6610 : *
6611 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6612 : *
6613 : * @since GDAL 3.9
6614 : */
6615 :
6616 6 : OGRGeometry *OGRGeometry::SetPrecision(UNUSED_IF_NO_GEOS double dfGridSize,
6617 : UNUSED_IF_NO_GEOS int nFlags) const
6618 : {
6619 : #ifndef HAVE_GEOS
6620 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6621 : return nullptr;
6622 :
6623 : #else
6624 6 : OGRGeometry *poOGRProduct = nullptr;
6625 :
6626 6 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6627 6 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6628 6 : if (hThisGeosGeom != nullptr)
6629 : {
6630 6 : GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
6631 : hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
6632 6 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6633 : poOGRProduct =
6634 6 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6635 : }
6636 6 : freeGEOSContext(hGEOSCtxt);
6637 6 : return poOGRProduct;
6638 :
6639 : #endif // HAVE_GEOS
6640 : }
6641 :
6642 : /************************************************************************/
6643 : /* OGR_G_SetPrecision() */
6644 : /************************************************************************/
6645 :
6646 : /** Set the geometry's precision, rounding all its coordinates to the precision
6647 : * grid, and making sure the geometry is still valid.
6648 : *
6649 : * This is a stronger version of roundCoordinates().
6650 : *
6651 : * Note that at time of writing GEOS does no supported curve geometries. So
6652 : * currently if this function is called on such a geometry, OGR will first call
6653 : * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6654 : * that it is unlikely to yield to the expected result.
6655 : *
6656 : * This function is the same as the C++ method OGRGeometry::SetPrecision().
6657 : *
6658 : * This function is built on the GEOSGeom_setPrecision_r() function of the
6659 : * GEOS library. Check it for the definition of the geometry operation.
6660 : * If OGR is built without the GEOS library, this function will always fail,
6661 : * issuing a CPLE_NotSupported error.
6662 : *
6663 : * @param hThis the geometry.
6664 : * @param dfGridSize size of the precision grid, or 0 for FLOATING
6665 : * precision.
6666 : * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
6667 : * and OGR_GEOS_PREC_KEEP_COLLAPSED
6668 : *
6669 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6670 : * or NULL if an error occurs.
6671 : *
6672 : * @since GDAL 3.9
6673 : */
6674 1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
6675 : int nFlags)
6676 : {
6677 1 : VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
6678 1 : return OGRGeometry::ToHandle(
6679 1 : OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
6680 : }
6681 :
6682 : /************************************************************************/
6683 : /* DelaunayTriangulation() */
6684 : /************************************************************************/
6685 :
6686 : /**
6687 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
6688 : *
6689 : * This function is the same as the C function OGR_G_DelaunayTriangulation().
6690 : *
6691 : * This function is built on the GEOS library, v3.4 or above.
6692 : * If OGR is built without the GEOS library, this function will always fail,
6693 : * issuing a CPLE_NotSupported error.
6694 : *
6695 : * @param dfTolerance optional snapping tolerance to use for improved robustness
6696 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
6697 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
6698 : *
6699 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6700 : *
6701 : * @since OGR 2.1
6702 : */
6703 :
6704 : #ifndef HAVE_GEOS
6705 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
6706 : int /*bOnlyEdges*/) const
6707 : {
6708 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6709 : return nullptr;
6710 : }
6711 : #else
6712 1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
6713 : int bOnlyEdges) const
6714 : {
6715 1 : OGRGeometry *poOGRProduct = nullptr;
6716 :
6717 1 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6718 1 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6719 1 : if (hThisGeosGeom != nullptr)
6720 : {
6721 1 : GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
6722 : hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
6723 1 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6724 : poOGRProduct =
6725 1 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6726 : }
6727 1 : freeGEOSContext(hGEOSCtxt);
6728 1 : return poOGRProduct;
6729 : }
6730 : #endif
6731 :
6732 : /************************************************************************/
6733 : /* OGR_G_DelaunayTriangulation() */
6734 : /************************************************************************/
6735 :
6736 : /**
6737 : * \brief Return a Delaunay triangulation of the vertices of the geometry.
6738 : *
6739 : * This function is the same as the C++ method
6740 : * OGRGeometry::DelaunayTriangulation().
6741 : *
6742 : * This function is built on the GEOS library, v3.4 or above.
6743 : * If OGR is built without the GEOS library, this function will always fail,
6744 : * issuing a CPLE_NotSupported error.
6745 : *
6746 : * @param hThis the geometry.
6747 : * @param dfTolerance optional snapping tolerance to use for improved robustness
6748 : * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
6749 : * return a GEOMETRYCOLLECTION containing triangular POLYGONs.
6750 : *
6751 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6752 : * or NULL if an error occurs.
6753 : *
6754 : * @since OGR 2.1
6755 : */
6756 :
6757 1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
6758 : int bOnlyEdges)
6759 :
6760 : {
6761 1 : VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
6762 :
6763 1 : return OGRGeometry::ToHandle(
6764 1 : OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
6765 2 : bOnlyEdges));
6766 : }
6767 :
6768 : /************************************************************************/
6769 : /* ConstrainedDelaunayTriangulation() */
6770 : /************************************************************************/
6771 :
6772 : /**
6773 : * \brief Return a constrained Delaunay triangulation of the vertices of the
6774 : * given polygon(s). For non-polygonal inputs, silently returns an empty
6775 : * geometry collection.
6776 : *
6777 : * This function is the same as the C function
6778 : * OGR_G_ConstrainedDelaunayTriangulation().
6779 : *
6780 : * This function is built on the GEOS library, v3.10 or above.
6781 : * If OGR is built without the GEOS library, this function will always fail,
6782 : * issuing a CPLE_NotSupported error.
6783 : *
6784 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6785 : *
6786 : * @since OGR 3.12
6787 : */
6788 :
6789 3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
6790 : {
6791 : #ifndef HAVE_GEOS
6792 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6793 : return nullptr;
6794 : #elif !(GEOS_VERSION_MAJOR > 3 || \
6795 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
6796 : CPLError(
6797 : CE_Failure, CPLE_NotSupported,
6798 : "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
6799 : return nullptr;
6800 : #else
6801 :
6802 3 : OGRGeometry *poOGRProduct = nullptr;
6803 :
6804 3 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6805 3 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6806 3 : if (hThisGeosGeom != nullptr)
6807 : {
6808 : GEOSGeom hGeosProduct =
6809 3 : GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
6810 3 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6811 : poOGRProduct =
6812 3 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6813 : }
6814 3 : freeGEOSContext(hGEOSCtxt);
6815 3 : return poOGRProduct;
6816 : #endif
6817 : }
6818 :
6819 : /************************************************************************/
6820 : /* OGR_G_ConstrainedDelaunayTriangulation() */
6821 : /************************************************************************/
6822 :
6823 : /**
6824 : * \brief Return a constrained Delaunay triangulation of the vertices of the
6825 : * given polygon(s). For non-polygonal inputs, silently returns an empty
6826 : * geometry collection.
6827 : *
6828 : * This function is the same as the C++ method
6829 : * OGRGeometry::ConstrainedDelaunayTriangulation().
6830 : *
6831 : * This function is built on the GEOS library, v3.10 or above.
6832 : * If OGR is built without the GEOS library, this function will always fail,
6833 : * issuing a CPLE_NotSupported error.
6834 : *
6835 : * @param hThis the geometry.
6836 : *
6837 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6838 : * or NULL if an error occurs.
6839 : *
6840 : * @since OGR 3.12
6841 : */
6842 :
6843 3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
6844 : {
6845 3 : VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
6846 :
6847 3 : return OGRGeometry::ToHandle(
6848 6 : OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
6849 : }
6850 :
6851 : /************************************************************************/
6852 : /* Polygonize() */
6853 : /************************************************************************/
6854 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
6855 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
6856 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
6857 : /* AMBIENTALE */
6858 : /************************************************************************/
6859 :
6860 : /**
6861 : * \brief Polygonizes a set of sparse edges.
6862 : *
6863 : * A new geometry object is created and returned containing a collection
6864 : * of reassembled Polygons: NULL will be returned if the input collection
6865 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
6866 : * into Polygons is impossible due to topological inconsistencies.
6867 : *
6868 : * This method is the same as the C function OGR_G_Polygonize().
6869 : *
6870 : * This method is built on the GEOS library, check it for the definition
6871 : * of the geometry operation.
6872 : * If OGR is built without the GEOS library, this method will always fail,
6873 : * issuing a CPLE_NotSupported error.
6874 : *
6875 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6876 : *
6877 : * @since OGR 1.9.0
6878 : */
6879 :
6880 117 : OGRGeometry *OGRGeometry::Polygonize() const
6881 :
6882 : {
6883 : #ifndef HAVE_GEOS
6884 :
6885 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6886 : return nullptr;
6887 :
6888 : #else
6889 :
6890 117 : const OGRGeometryCollection *poColl = nullptr;
6891 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
6892 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
6893 116 : poColl = toGeometryCollection();
6894 : else
6895 1 : return nullptr;
6896 :
6897 116 : const int nCount = poColl->getNumGeometries();
6898 :
6899 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
6900 116 : bool bError = false;
6901 :
6902 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6903 :
6904 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
6905 747 : for (int ig = 0; ig < nCount; ig++)
6906 : {
6907 631 : GEOSGeom hGeosGeom = nullptr;
6908 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
6909 1262 : if (poChild == nullptr ||
6910 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
6911 1 : bError = true;
6912 : else
6913 : {
6914 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
6915 630 : if (hGeosGeom == nullptr)
6916 0 : bError = true;
6917 : }
6918 631 : pahGeosGeomList[ig] = hGeosGeom;
6919 : }
6920 :
6921 116 : if (!bError)
6922 : {
6923 : GEOSGeom hGeosPolygs =
6924 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
6925 :
6926 : poPolygsOGRGeom =
6927 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
6928 : }
6929 :
6930 747 : for (int ig = 0; ig < nCount; ig++)
6931 : {
6932 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
6933 631 : if (hGeosGeom != nullptr)
6934 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
6935 : }
6936 116 : delete[] pahGeosGeomList;
6937 116 : freeGEOSContext(hGEOSCtxt);
6938 :
6939 116 : return poPolygsOGRGeom;
6940 :
6941 : #endif // HAVE_GEOS
6942 : }
6943 :
6944 : /************************************************************************/
6945 : /* OGR_G_Polygonize() */
6946 : /************************************************************************/
6947 : /**
6948 : * \brief Polygonizes a set of sparse edges.
6949 : *
6950 : * A new geometry object is created and returned containing a collection
6951 : * of reassembled Polygons: NULL will be returned if the input collection
6952 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
6953 : * into Polygons is impossible due to topological inconsistencies.
6954 : *
6955 : * This function is the same as the C++ method OGRGeometry::Polygonize().
6956 : *
6957 : * This function is built on the GEOS library, check it for the definition
6958 : * of the geometry operation.
6959 : * If OGR is built without the GEOS library, this function will always fail,
6960 : * issuing a CPLE_NotSupported error.
6961 : *
6962 : * @param hTarget The Geometry to be polygonized.
6963 : *
6964 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6965 : * or NULL if an error occurs.
6966 : *
6967 : * @since OGR 1.9.0
6968 : */
6969 :
6970 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
6971 :
6972 : {
6973 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
6974 :
6975 3 : return OGRGeometry::ToHandle(
6976 6 : OGRGeometry::FromHandle(hTarget)->Polygonize());
6977 : }
6978 :
6979 : /************************************************************************/
6980 : /* BuildArea() */
6981 : /************************************************************************/
6982 :
6983 : /**
6984 : * \brief Polygonize a linework assuming inner polygons are holes.
6985 : *
6986 : * This method is the same as the C function OGR_G_BuildArea().
6987 : *
6988 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
6989 : * Additionally, holes are dropped and the result is unified producing
6990 : * a single Polygon or a MultiPolygon.
6991 : *
6992 : * A new geometry object is created and returned: NULL on failure,
6993 : * empty GeometryCollection if the input geometry cannot be polygonized,
6994 : * Polygon or MultiPolygon on success.
6995 : *
6996 : * This method is built on the GEOSBuildArea_r() function of the GEOS
6997 : * library, check it for the definition of the geometry operation.
6998 : * If OGR is built without the GEOS library, this method will always fail,
6999 : * issuing a CPLE_NotSupported error.
7000 : *
7001 : * @return a newly allocated geometry now owned by the caller,
7002 : * or NULL on failure.
7003 : *
7004 : * @since OGR 3.11
7005 : */
7006 :
7007 14 : OGRGeometry *OGRGeometry::BuildArea() const
7008 :
7009 : {
7010 : #ifndef HAVE_GEOS
7011 :
7012 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7013 : return nullptr;
7014 :
7015 : #else
7016 :
7017 14 : OGRGeometry *poPolygsOGRGeom = nullptr;
7018 :
7019 14 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7020 14 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7021 14 : if (hThisGeosGeom != nullptr)
7022 : {
7023 14 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7024 : poPolygsOGRGeom =
7025 14 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7026 14 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7027 : }
7028 14 : freeGEOSContext(hGEOSCtxt);
7029 :
7030 14 : return poPolygsOGRGeom;
7031 :
7032 : #endif // HAVE_GEOS
7033 : }
7034 :
7035 : /************************************************************************/
7036 : /* OGR_G_BuildArea() */
7037 : /************************************************************************/
7038 :
7039 : /**
7040 : * \brief Polygonize a linework assuming inner polygons are holes.
7041 : *
7042 : * This function is the same as the C++ method OGRGeometry::BuildArea().
7043 : *
7044 : * Polygonization is performed similarly to OGR_G_Polygonize().
7045 : * Additionally, holes are dropped and the result is unified producing
7046 : * a single Polygon or a MultiPolygon.
7047 : *
7048 : * A new geometry object is created and returned: NULL on failure,
7049 : * empty GeometryCollection if the input geometry cannot be polygonized,
7050 : * Polygon or MultiPolygon on success.
7051 : *
7052 : * This function is built on the GEOSBuildArea_r() function of the GEOS
7053 : * library, check it for the definition of the geometry operation.
7054 : * If OGR is built without the GEOS library, this function will always fail,
7055 : * issuing a CPLE_NotSupported error.
7056 : *
7057 : * @param hGeom handle on the geometry to polygonize.
7058 : *
7059 : * @return a handle on newly allocated geometry now owned by the caller,
7060 : * or NULL on failure.
7061 : *
7062 : * @since OGR 3.11
7063 : */
7064 :
7065 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7066 :
7067 : {
7068 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7069 :
7070 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7071 : }
7072 :
7073 : /************************************************************************/
7074 : /* swapXY() */
7075 : /************************************************************************/
7076 :
7077 : /**
7078 : * \brief Swap x and y coordinates.
7079 : *
7080 : * @since OGR 1.8.0
7081 : */
7082 :
7083 0 : void OGRGeometry::swapXY()
7084 :
7085 : {
7086 0 : }
7087 :
7088 : /************************************************************************/
7089 : /* swapXY() */
7090 : /************************************************************************/
7091 :
7092 : /**
7093 : * \brief Swap x and y coordinates.
7094 : *
7095 : * @param hGeom geometry.
7096 : * @since OGR 2.3.0
7097 : */
7098 :
7099 2 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7100 : {
7101 2 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7102 :
7103 2 : OGRGeometry::FromHandle(hGeom)->swapXY();
7104 : }
7105 :
7106 : /************************************************************************/
7107 : /* Prepared geometry API */
7108 : /************************************************************************/
7109 :
7110 : #if defined(HAVE_GEOS)
7111 : struct _OGRPreparedGeometry
7112 : {
7113 : GEOSContextHandle_t hGEOSCtxt;
7114 : GEOSGeom hGEOSGeom;
7115 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7116 : };
7117 : #endif
7118 :
7119 : /************************************************************************/
7120 : /* OGRHasPreparedGeometrySupport() */
7121 : /************************************************************************/
7122 :
7123 : /** Returns if GEOS has prepared geometry support.
7124 : * @return TRUE or FALSE
7125 : */
7126 1 : int OGRHasPreparedGeometrySupport()
7127 : {
7128 : #if defined(HAVE_GEOS)
7129 1 : return TRUE;
7130 : #else
7131 : return FALSE;
7132 : #endif
7133 : }
7134 :
7135 : /************************************************************************/
7136 : /* OGRCreatePreparedGeometry() */
7137 : /************************************************************************/
7138 :
7139 : /** Creates a prepared geometry.
7140 : *
7141 : * To free with OGRDestroyPreparedGeometry()
7142 : *
7143 : * @param hGeom input geometry to prepare.
7144 : * @return handle to a prepared geometry.
7145 : * @since GDAL 3.3
7146 : */
7147 : OGRPreparedGeometryH
7148 52352 : OGRCreatePreparedGeometry(UNUSED_IF_NO_GEOS OGRGeometryH hGeom)
7149 : {
7150 : #if defined(HAVE_GEOS)
7151 52352 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7152 52352 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7153 52352 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7154 52352 : if (hGEOSGeom == nullptr)
7155 : {
7156 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7157 0 : return nullptr;
7158 : }
7159 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7160 52352 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7161 52352 : if (poPreparedGEOSGeom == nullptr)
7162 : {
7163 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7164 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7165 0 : return nullptr;
7166 : }
7167 :
7168 52352 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7169 52352 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7170 52352 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7171 52352 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7172 :
7173 52352 : return poPreparedGeom;
7174 : #else
7175 : return nullptr;
7176 : #endif
7177 : }
7178 :
7179 : /************************************************************************/
7180 : /* OGRDestroyPreparedGeometry() */
7181 : /************************************************************************/
7182 :
7183 : /** Destroys a prepared geometry.
7184 : * @param hPreparedGeom prepared geometry.
7185 : * @since GDAL 3.3
7186 : */
7187 52398 : void OGRDestroyPreparedGeometry(
7188 : UNUSED_IF_NO_GEOS OGRPreparedGeometryH hPreparedGeom)
7189 : {
7190 : #if defined(HAVE_GEOS)
7191 52398 : if (hPreparedGeom != nullptr)
7192 : {
7193 52352 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7194 : hPreparedGeom->poPreparedGEOSGeom);
7195 52352 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7196 52352 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7197 52352 : delete hPreparedGeom;
7198 : }
7199 : #endif
7200 52398 : }
7201 :
7202 : /************************************************************************/
7203 : /* OGRPreparedGeometryIntersects() */
7204 : /************************************************************************/
7205 :
7206 : /** Returns whether a prepared geometry intersects with a geometry.
7207 : * @param hPreparedGeom prepared geometry.
7208 : * @param hOtherGeom other geometry.
7209 : * @return TRUE or FALSE.
7210 : * @since GDAL 3.3
7211 : */
7212 5226 : int OGRPreparedGeometryIntersects(
7213 : UNUSED_IF_NO_GEOS const OGRPreparedGeometryH hPreparedGeom,
7214 : UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
7215 : {
7216 : #if defined(HAVE_GEOS)
7217 5226 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7218 5226 : if (hPreparedGeom == nullptr ||
7219 : poOtherGeom == nullptr
7220 : // The check for IsEmpty() is for buggy GEOS versions.
7221 : // See https://github.com/libgeos/geos/pull/423
7222 10452 : || poOtherGeom->IsEmpty())
7223 : {
7224 1 : return FALSE;
7225 : }
7226 :
7227 : GEOSGeom hGEOSOtherGeom =
7228 5225 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7229 5225 : if (hGEOSOtherGeom == nullptr)
7230 0 : return FALSE;
7231 :
7232 5225 : const bool bRet = CPL_TO_BOOL(GEOSPreparedIntersects_r(
7233 : hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
7234 : hGEOSOtherGeom));
7235 5225 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7236 :
7237 5225 : return bRet;
7238 : #else
7239 : return FALSE;
7240 : #endif
7241 : }
7242 :
7243 : /** Returns whether a prepared geometry contains a geometry.
7244 : * @param hPreparedGeom prepared geometry.
7245 : * @param hOtherGeom other geometry.
7246 : * @return TRUE or FALSE.
7247 : */
7248 120116 : int OGRPreparedGeometryContains(UNUSED_IF_NO_GEOS const OGRPreparedGeometryH
7249 : hPreparedGeom,
7250 : UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
7251 : {
7252 : #if defined(HAVE_GEOS)
7253 120116 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7254 120116 : if (hPreparedGeom == nullptr ||
7255 : poOtherGeom == nullptr
7256 : // The check for IsEmpty() is for buggy GEOS versions.
7257 : // See https://github.com/libgeos/geos/pull/423
7258 240232 : || poOtherGeom->IsEmpty())
7259 : {
7260 1 : return FALSE;
7261 : }
7262 :
7263 : GEOSGeom hGEOSOtherGeom =
7264 120115 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7265 120115 : if (hGEOSOtherGeom == nullptr)
7266 0 : return FALSE;
7267 :
7268 120115 : const bool bRet = CPL_TO_BOOL(GEOSPreparedContains_r(
7269 : hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
7270 : hGEOSOtherGeom));
7271 120115 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7272 :
7273 120115 : return bRet;
7274 : #else
7275 : return FALSE;
7276 : #endif
7277 : }
7278 :
7279 : /************************************************************************/
7280 : /* OGRGeometryFromEWKB() */
7281 : /************************************************************************/
7282 :
7283 1433 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7284 : int bIsPostGIS1_EWKB)
7285 :
7286 : {
7287 1433 : OGRGeometry *poGeometry = nullptr;
7288 :
7289 1433 : size_t nWKBSize = 0;
7290 1433 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7291 1433 : if (pabyWKB == nullptr)
7292 0 : return nullptr;
7293 :
7294 : /* -------------------------------------------------------------------- */
7295 : /* Try to ingest the geometry. */
7296 : /* -------------------------------------------------------------------- */
7297 1433 : (void)OGRGeometryFactory::createFromWkb(
7298 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7299 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7300 :
7301 1433 : return poGeometry;
7302 : }
7303 :
7304 : /************************************************************************/
7305 : /* OGRGeometryFromHexEWKB() */
7306 : /************************************************************************/
7307 :
7308 1431 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7309 : int bIsPostGIS1_EWKB)
7310 :
7311 : {
7312 1431 : if (pszBytea == nullptr)
7313 0 : return nullptr;
7314 :
7315 1431 : int nWKBLength = 0;
7316 1431 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7317 :
7318 : OGRGeometry *poGeometry =
7319 1431 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7320 :
7321 1431 : CPLFree(pabyWKB);
7322 :
7323 1431 : return poGeometry;
7324 : }
7325 :
7326 : /************************************************************************/
7327 : /* OGRGeometryToHexEWKB() */
7328 : /************************************************************************/
7329 :
7330 1059 : char *OGRGeometryToHexEWKB(OGRGeometry *poGeometry, int nSRSId,
7331 : int nPostGISMajor, int nPostGISMinor)
7332 : {
7333 1059 : const size_t nWkbSize = poGeometry->WkbSize();
7334 1059 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7335 1059 : if (pabyWKB == nullptr)
7336 0 : return CPLStrdup("");
7337 :
7338 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7339 1795 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7340 618 : poGeometry->IsEmpty())
7341 : {
7342 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7343 : OGRERR_NONE)
7344 : {
7345 0 : CPLFree(pabyWKB);
7346 0 : return CPLStrdup("");
7347 : }
7348 : }
7349 1057 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7350 : (nPostGISMajor < 2)
7351 : ? wkbVariantPostGIS1
7352 1057 : : wkbVariantOldOgc) != OGRERR_NONE)
7353 : {
7354 0 : CPLFree(pabyWKB);
7355 0 : return CPLStrdup("");
7356 : }
7357 :
7358 : // When converting to hex, each byte takes 2 hex characters. In addition
7359 : // we add in 8 characters to represent the SRID integer in hex, and
7360 : // one for a null terminator.
7361 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7362 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7363 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7364 1059 : if (nWkbSize >
7365 1059 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7366 : {
7367 0 : CPLFree(pabyWKB);
7368 0 : return CPLStrdup("");
7369 : }
7370 1059 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7371 1059 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7372 1059 : if (pszTextBuf == nullptr)
7373 : {
7374 0 : CPLFree(pabyWKB);
7375 0 : return CPLStrdup("");
7376 : }
7377 1059 : char *pszTextBufCurrent = pszTextBuf;
7378 :
7379 : // Convert the 1st byte, which is the endianness flag, to hex.
7380 1059 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7381 1059 : strcpy(pszTextBufCurrent, pszHex);
7382 1059 : CPLFree(pszHex);
7383 1059 : pszTextBufCurrent += 2;
7384 :
7385 : // Next, get the geom type which is bytes 2 through 5.
7386 : GUInt32 geomType;
7387 1059 : memcpy(&geomType, pabyWKB + 1, 4);
7388 :
7389 : // Now add the SRID flag if an SRID is provided.
7390 1059 : if (nSRSId > 0)
7391 : {
7392 : // Change the flag to wkbNDR (little) endianness.
7393 529 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7394 529 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7395 : // Apply the flag.
7396 529 : geomType = geomType | nGSrsFlag;
7397 : }
7398 :
7399 : // Now write the geom type which is 4 bytes.
7400 1059 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7401 1059 : strcpy(pszTextBufCurrent, pszHex);
7402 1059 : CPLFree(pszHex);
7403 1059 : pszTextBufCurrent += 8;
7404 :
7405 : // Now include SRID if provided.
7406 1059 : if (nSRSId > 0)
7407 : {
7408 : // Force the srsid to wkbNDR (little) endianness.
7409 529 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7410 529 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7411 : reinterpret_cast<const GByte *>(&nGSRSId));
7412 529 : strcpy(pszTextBufCurrent, pszHex);
7413 529 : CPLFree(pszHex);
7414 529 : pszTextBufCurrent += 8;
7415 : }
7416 :
7417 : // Copy the rest of the data over - subtract
7418 : // 5 since we already copied 5 bytes above.
7419 1059 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7420 1059 : CPLFree(pabyWKB);
7421 1059 : if (!pszHex || pszHex[0] == 0)
7422 : {
7423 0 : CPLFree(pszTextBuf);
7424 0 : return pszHex;
7425 : }
7426 1059 : strcpy(pszTextBufCurrent, pszHex);
7427 1059 : CPLFree(pszHex);
7428 :
7429 1059 : return pszTextBuf;
7430 : }
7431 :
7432 : /************************************************************************/
7433 : /* importPreambleFromWkb() */
7434 : /************************************************************************/
7435 :
7436 : //! @cond Doxygen_Suppress
7437 157221 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7438 : size_t nSize,
7439 : OGRwkbByteOrder &eByteOrder,
7440 : OGRwkbVariant eWkbVariant)
7441 : {
7442 157221 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7443 0 : return OGRERR_NOT_ENOUGH_DATA;
7444 :
7445 : /* -------------------------------------------------------------------- */
7446 : /* Get the byte order byte. */
7447 : /* -------------------------------------------------------------------- */
7448 157221 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7449 157221 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7450 0 : return OGRERR_CORRUPT_DATA;
7451 157221 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7452 :
7453 : /* -------------------------------------------------------------------- */
7454 : /* Get the geometry feature type. */
7455 : /* -------------------------------------------------------------------- */
7456 : OGRwkbGeometryType eGeometryType;
7457 : const OGRErr err =
7458 157221 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7459 157223 : if (wkbHasZ(eGeometryType))
7460 62311 : flags |= OGR_G_3D;
7461 157223 : if (wkbHasM(eGeometryType))
7462 59690 : flags |= OGR_G_MEASURED;
7463 :
7464 157223 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7465 0 : return OGRERR_CORRUPT_DATA;
7466 :
7467 157221 : return OGRERR_NONE;
7468 : }
7469 :
7470 : /************************************************************************/
7471 : /* importPreambleOfCollectionFromWkb() */
7472 : /* */
7473 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7474 : /* OGRCurvePolygon and OGRGeometryCollection. */
7475 : /************************************************************************/
7476 :
7477 74562 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7478 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7479 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7480 : OGRwkbVariant eWkbVariant)
7481 : {
7482 74562 : nGeomCount = 0;
7483 :
7484 : OGRErr eErr =
7485 74562 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7486 74564 : if (eErr != OGRERR_NONE)
7487 0 : return eErr;
7488 :
7489 : /* -------------------------------------------------------------------- */
7490 : /* Clear existing Geoms. */
7491 : /* -------------------------------------------------------------------- */
7492 74564 : int _flags = flags; // flags set in importPreambleFromWkb
7493 74564 : empty(); // may reset flags etc.
7494 :
7495 : // restore
7496 74562 : if (_flags & OGR_G_3D)
7497 59269 : set3D(TRUE);
7498 74562 : if (_flags & OGR_G_MEASURED)
7499 56768 : setMeasured(TRUE);
7500 :
7501 : /* -------------------------------------------------------------------- */
7502 : /* Get the sub-geometry count. */
7503 : /* -------------------------------------------------------------------- */
7504 74563 : memcpy(&nGeomCount, pabyData + 5, 4);
7505 :
7506 74563 : if (OGR_SWAP(eByteOrder))
7507 393 : nGeomCount = CPL_SWAP32(nGeomCount);
7508 :
7509 148992 : if (nGeomCount < 0 ||
7510 74428 : static_cast<size_t>(nGeomCount) >
7511 74428 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7512 : {
7513 134 : nGeomCount = 0;
7514 134 : return OGRERR_CORRUPT_DATA;
7515 : }
7516 74430 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7517 :
7518 : // Each ring has a minimum of nMinSubGeomSize bytes.
7519 74430 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7520 : {
7521 910 : CPLError(CE_Failure, CPLE_AppDefined,
7522 : "Length of input WKB is too small");
7523 910 : nGeomCount = 0;
7524 910 : return OGRERR_NOT_ENOUGH_DATA;
7525 : }
7526 :
7527 73520 : nDataOffset = 9;
7528 73520 : if (nSize != static_cast<size_t>(-1))
7529 : {
7530 73500 : CPLAssert(nSize >= nDataOffset);
7531 73500 : nSize -= nDataOffset;
7532 : }
7533 :
7534 73520 : return OGRERR_NONE;
7535 : }
7536 :
7537 : /************************************************************************/
7538 : /* importCurveCollectionFromWkt() */
7539 : /* */
7540 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7541 : /* OGRMultiCurve. */
7542 : /************************************************************************/
7543 :
7544 1422 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7545 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7546 : int bAllowCurve, int bAllowCompoundCurve,
7547 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7548 :
7549 : {
7550 1422 : int bHasZ = FALSE;
7551 1422 : int bHasM = FALSE;
7552 1422 : bool bIsEmpty = false;
7553 1422 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7554 1422 : flags = 0;
7555 1422 : if (eErr != OGRERR_NONE)
7556 14 : return eErr;
7557 1408 : if (bHasZ)
7558 201 : flags |= OGR_G_3D;
7559 1408 : if (bHasM)
7560 132 : flags |= OGR_G_MEASURED;
7561 1408 : if (bIsEmpty)
7562 109 : return OGRERR_NONE;
7563 :
7564 : char szToken[OGR_WKT_TOKEN_MAX];
7565 1299 : const char *pszInput = *ppszInput;
7566 1299 : eErr = OGRERR_NONE;
7567 :
7568 : // Skip first '('.
7569 1299 : pszInput = OGRWktReadToken(pszInput, szToken);
7570 :
7571 : /* ==================================================================== */
7572 : /* Read each curve in turn. Note that we try to reuse the same */
7573 : /* point list buffer from curve to curve to cut down on */
7574 : /* allocate/deallocate overhead. */
7575 : /* ==================================================================== */
7576 1299 : OGRRawPoint *paoPoints = nullptr;
7577 1299 : int nMaxPoints = 0;
7578 1299 : double *padfZ = nullptr;
7579 :
7580 639 : do
7581 : {
7582 :
7583 : /* --------------------------------------------------------------------
7584 : */
7585 : /* Get the first token, which should be the geometry type. */
7586 : /* --------------------------------------------------------------------
7587 : */
7588 1938 : const char *pszInputBefore = pszInput;
7589 1938 : pszInput = OGRWktReadToken(pszInput, szToken);
7590 :
7591 : /* --------------------------------------------------------------------
7592 : */
7593 : /* Do the import. */
7594 : /* --------------------------------------------------------------------
7595 : */
7596 1938 : OGRCurve *poCurve = nullptr;
7597 1938 : if (EQUAL(szToken, "("))
7598 : {
7599 1392 : OGRLineString *poLine = new OGRLineString();
7600 1392 : poCurve = poLine;
7601 1392 : pszInput = pszInputBefore;
7602 1392 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
7603 : paoPoints, nMaxPoints, padfZ);
7604 : }
7605 546 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
7606 : {
7607 16 : poCurve = new OGRLineString();
7608 : }
7609 : // Accept LINESTRING(), but this is an extension to the BNF, also
7610 : // accepted by PostGIS.
7611 530 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
7612 515 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
7613 515 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
7614 1218 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
7615 158 : (bAllowCompoundCurve &&
7616 158 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
7617 : {
7618 492 : OGRGeometry *poGeom = nullptr;
7619 492 : pszInput = pszInputBefore;
7620 : eErr =
7621 492 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
7622 492 : if (poGeom == nullptr)
7623 : {
7624 1 : eErr = OGRERR_CORRUPT_DATA;
7625 : }
7626 : else
7627 : {
7628 491 : poCurve = poGeom->toCurve();
7629 : }
7630 : }
7631 : else
7632 : {
7633 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
7634 : szToken);
7635 38 : eErr = OGRERR_CORRUPT_DATA;
7636 : }
7637 :
7638 : // If this has M it is an error if poGeom does not have M.
7639 1938 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
7640 0 : eErr = OGRERR_CORRUPT_DATA;
7641 :
7642 1938 : if (eErr == OGRERR_NONE)
7643 1893 : eErr = pfnAddCurveDirectly(this, poCurve);
7644 1938 : if (eErr != OGRERR_NONE)
7645 : {
7646 55 : delete poCurve;
7647 55 : break;
7648 : }
7649 :
7650 : /* --------------------------------------------------------------------
7651 : */
7652 : /* Read the delimiter following the surface. */
7653 : /* --------------------------------------------------------------------
7654 : */
7655 1883 : pszInput = OGRWktReadToken(pszInput, szToken);
7656 1883 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
7657 :
7658 1299 : CPLFree(paoPoints);
7659 1299 : CPLFree(padfZ);
7660 :
7661 : /* -------------------------------------------------------------------- */
7662 : /* freak if we don't get a closing bracket. */
7663 : /* -------------------------------------------------------------------- */
7664 :
7665 1299 : if (eErr != OGRERR_NONE)
7666 55 : return eErr;
7667 :
7668 1244 : if (szToken[0] != ')')
7669 9 : return OGRERR_CORRUPT_DATA;
7670 :
7671 1235 : *ppszInput = pszInput;
7672 1235 : return OGRERR_NONE;
7673 : }
7674 :
7675 : //! @endcond
7676 :
7677 : /************************************************************************/
7678 : /* OGR_GT_Flatten() */
7679 : /************************************************************************/
7680 : /**
7681 : * \brief Returns the 2D geometry type corresponding to the passed geometry
7682 : * type.
7683 : *
7684 : * This function is intended to work with geometry types as old-style 99-402
7685 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
7686 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
7687 : *
7688 : * @param eType Input geometry type
7689 : *
7690 : * @return 2D geometry type corresponding to the passed geometry type.
7691 : *
7692 : * @since GDAL 2.0
7693 : */
7694 :
7695 7655160 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
7696 : {
7697 7655160 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
7698 7655160 : if (eType >= 1000 && eType < 2000) // ISO Z.
7699 2776860 : return static_cast<OGRwkbGeometryType>(eType - 1000);
7700 4878290 : if (eType >= 2000 && eType < 3000) // ISO M.
7701 5973 : return static_cast<OGRwkbGeometryType>(eType - 2000);
7702 4872320 : if (eType >= 3000 && eType < 4000) // ISO ZM.
7703 136147 : return static_cast<OGRwkbGeometryType>(eType - 3000);
7704 4736170 : return eType;
7705 : }
7706 :
7707 : /************************************************************************/
7708 : /* OGR_GT_HasZ() */
7709 : /************************************************************************/
7710 : /**
7711 : * \brief Return if the geometry type is a 3D geometry type.
7712 : *
7713 : * @param eType Input geometry type
7714 : *
7715 : * @return TRUE if the geometry type is a 3D geometry type.
7716 : *
7717 : * @since GDAL 2.0
7718 : */
7719 :
7720 2056780 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
7721 : {
7722 2056780 : if (eType & wkb25DBitInternalUse)
7723 156087 : return TRUE;
7724 1900690 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
7725 266 : return TRUE;
7726 1900430 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
7727 121527 : return TRUE;
7728 1778900 : return FALSE;
7729 : }
7730 :
7731 : /************************************************************************/
7732 : /* OGR_GT_HasM() */
7733 : /************************************************************************/
7734 : /**
7735 : * \brief Return if the geometry type is a measured type.
7736 : *
7737 : * @param eType Input geometry type
7738 : *
7739 : * @return TRUE if the geometry type is a measured type.
7740 : *
7741 : * @since GDAL 2.1
7742 : */
7743 :
7744 2116520 : int OGR_GT_HasM(OGRwkbGeometryType eType)
7745 : {
7746 2116520 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
7747 2563 : return TRUE;
7748 2113960 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
7749 121177 : return TRUE;
7750 1992780 : return FALSE;
7751 : }
7752 :
7753 : /************************************************************************/
7754 : /* OGR_GT_SetZ() */
7755 : /************************************************************************/
7756 : /**
7757 : * \brief Returns the 3D geometry type corresponding to the passed geometry
7758 : * type.
7759 : *
7760 : * @param eType Input geometry type
7761 : *
7762 : * @return 3D geometry type corresponding to the passed geometry type.
7763 : *
7764 : * @since GDAL 2.0
7765 : */
7766 :
7767 5635 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
7768 : {
7769 5635 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
7770 496 : return eType;
7771 5139 : if (eType <= wkbGeometryCollection)
7772 5042 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
7773 : else
7774 97 : return static_cast<OGRwkbGeometryType>(eType + 1000);
7775 : }
7776 :
7777 : /************************************************************************/
7778 : /* OGR_GT_SetM() */
7779 : /************************************************************************/
7780 : /**
7781 : * \brief Returns the measured geometry type corresponding to the passed
7782 : * geometry type.
7783 : *
7784 : * @param eType Input geometry type
7785 : *
7786 : * @return measured geometry type corresponding to the passed geometry type.
7787 : *
7788 : * @since GDAL 2.1
7789 : */
7790 :
7791 1992 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
7792 : {
7793 1992 : if (OGR_GT_HasM(eType) || eType == wkbNone)
7794 258 : return eType;
7795 1734 : if (eType & wkb25DBitInternalUse)
7796 : {
7797 713 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
7798 713 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
7799 : }
7800 1734 : return static_cast<OGRwkbGeometryType>(eType + 2000);
7801 : }
7802 :
7803 : /************************************************************************/
7804 : /* OGR_GT_SetModifier() */
7805 : /************************************************************************/
7806 : /**
7807 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
7808 : *
7809 : * @param eType Input geometry type
7810 : * @param bHasZ TRUE if the output geometry type must be 3D.
7811 : * @param bHasM TRUE if the output geometry type must be measured.
7812 : *
7813 : * @return Output geometry type.
7814 : *
7815 : * @since GDAL 2.0
7816 : */
7817 :
7818 5298 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
7819 : int bHasM)
7820 : {
7821 5298 : if (bHasZ && bHasM)
7822 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
7823 4956 : else if (bHasM)
7824 331 : return OGR_GT_SetM(wkbFlatten(eType));
7825 4625 : else if (bHasZ)
7826 2022 : return OGR_GT_SetZ(wkbFlatten(eType));
7827 : else
7828 2603 : return wkbFlatten(eType);
7829 : }
7830 :
7831 : /************************************************************************/
7832 : /* OGR_GT_IsSubClassOf) */
7833 : /************************************************************************/
7834 : /**
7835 : * \brief Returns if a type is a subclass of another one
7836 : *
7837 : * @param eType Type.
7838 : * @param eSuperType Super type
7839 : *
7840 : * @return TRUE if eType is a subclass of eSuperType.
7841 : *
7842 : * @since GDAL 2.0
7843 : */
7844 :
7845 120747 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
7846 : {
7847 120747 : eSuperType = wkbFlatten(eSuperType);
7848 120747 : eType = wkbFlatten(eType);
7849 :
7850 120747 : if (eSuperType == eType || eSuperType == wkbUnknown)
7851 17552 : return TRUE;
7852 :
7853 103195 : if (eSuperType == wkbGeometryCollection)
7854 28688 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
7855 58828 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
7856 30140 : eType == wkbMultiSurface;
7857 :
7858 73055 : if (eSuperType == wkbCurvePolygon)
7859 19636 : return eType == wkbPolygon || eType == wkbTriangle;
7860 :
7861 53419 : if (eSuperType == wkbMultiCurve)
7862 249 : return eType == wkbMultiLineString;
7863 :
7864 53170 : if (eSuperType == wkbMultiSurface)
7865 288 : return eType == wkbMultiPolygon;
7866 :
7867 52882 : if (eSuperType == wkbCurve)
7868 22106 : return eType == wkbLineString || eType == wkbCircularString ||
7869 22106 : eType == wkbCompoundCurve;
7870 :
7871 30776 : if (eSuperType == wkbSurface)
7872 3483 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
7873 7114 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
7874 3631 : eType == wkbTIN;
7875 :
7876 27145 : if (eSuperType == wkbPolygon)
7877 221 : return eType == wkbTriangle;
7878 :
7879 26924 : if (eSuperType == wkbPolyhedralSurface)
7880 13879 : return eType == wkbTIN;
7881 :
7882 13045 : return FALSE;
7883 : }
7884 :
7885 : /************************************************************************/
7886 : /* OGR_GT_GetCollection() */
7887 : /************************************************************************/
7888 : /**
7889 : * \brief Returns the collection type that can contain the passed geometry type
7890 : *
7891 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
7892 : * wkbLineString->wkbMultiLineString,
7893 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
7894 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
7895 : * wkbCurvePolygon->wkbMultiSurface.
7896 : * In other cases, wkbUnknown is returned
7897 : *
7898 : * Passed Z, M, ZM flag is preserved.
7899 : *
7900 : *
7901 : * @param eType Input geometry type
7902 : *
7903 : * @return the collection type that can contain the passed geometry type or
7904 : * wkbUnknown
7905 : *
7906 : * @since GDAL 2.0
7907 : */
7908 :
7909 2615 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
7910 : {
7911 2615 : const bool bHasZ = wkbHasZ(eType);
7912 2615 : const bool bHasM = wkbHasM(eType);
7913 2615 : if (eType == wkbNone)
7914 1 : return wkbNone;
7915 2614 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
7916 2614 : if (eFGType == wkbPoint)
7917 47 : eType = wkbMultiPoint;
7918 :
7919 2567 : else if (eFGType == wkbLineString)
7920 174 : eType = wkbMultiLineString;
7921 :
7922 2393 : else if (eFGType == wkbPolygon)
7923 961 : eType = wkbMultiPolygon;
7924 :
7925 1432 : else if (eFGType == wkbTriangle)
7926 7 : eType = wkbTIN;
7927 :
7928 1425 : else if (OGR_GT_IsCurve(eFGType))
7929 175 : eType = wkbMultiCurve;
7930 :
7931 1250 : else if (OGR_GT_IsSurface(eFGType))
7932 958 : eType = wkbMultiSurface;
7933 :
7934 : else
7935 292 : return wkbUnknown;
7936 :
7937 2322 : if (bHasZ)
7938 3 : eType = wkbSetZ(eType);
7939 2322 : if (bHasM)
7940 2 : eType = wkbSetM(eType);
7941 :
7942 2322 : return eType;
7943 : }
7944 :
7945 : /************************************************************************/
7946 : /* OGR_GT_GetSingle() */
7947 : /************************************************************************/
7948 : /**
7949 : * \brief Returns the non-collection type that be contained in the passed
7950 : * geometry type.
7951 : *
7952 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
7953 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
7954 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
7955 : * wkbGeometryCollection -> wkbUnknown
7956 : * In other cases, the original geometry is returned.
7957 : *
7958 : * Passed Z, M, ZM flag is preserved.
7959 : *
7960 : *
7961 : * @param eType Input geometry type
7962 : *
7963 : * @return the the non-collection type that be contained in the passed geometry
7964 : * type or wkbUnknown
7965 : *
7966 : * @since GDAL 3.11
7967 : */
7968 :
7969 43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
7970 : {
7971 43 : const bool bHasZ = wkbHasZ(eType);
7972 43 : const bool bHasM = wkbHasM(eType);
7973 43 : if (eType == wkbNone)
7974 1 : return wkbNone;
7975 42 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
7976 42 : if (eFGType == wkbMultiPoint)
7977 8 : eType = wkbPoint;
7978 :
7979 34 : else if (eFGType == wkbMultiLineString)
7980 4 : eType = wkbLineString;
7981 :
7982 30 : else if (eFGType == wkbMultiPolygon)
7983 2 : eType = wkbPolygon;
7984 :
7985 28 : else if (eFGType == wkbMultiCurve)
7986 2 : eType = wkbCompoundCurve;
7987 :
7988 26 : else if (eFGType == wkbMultiSurface)
7989 2 : eType = wkbCurvePolygon;
7990 :
7991 24 : else if (eFGType == wkbGeometryCollection)
7992 1 : return wkbUnknown;
7993 :
7994 41 : if (bHasZ)
7995 3 : eType = wkbSetZ(eType);
7996 41 : if (bHasM)
7997 2 : eType = wkbSetM(eType);
7998 :
7999 41 : return eType;
8000 : }
8001 :
8002 : /************************************************************************/
8003 : /* OGR_GT_GetCurve() */
8004 : /************************************************************************/
8005 : /**
8006 : * \brief Returns the curve geometry type that can contain the passed geometry
8007 : * type
8008 : *
8009 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8010 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8011 : * and wkbMultiLineString->wkbMultiCurve.
8012 : * In other cases, the passed geometry is returned.
8013 : *
8014 : * Passed Z, M, ZM flag is preserved.
8015 : *
8016 : * @param eType Input geometry type
8017 : *
8018 : * @return the curve type that can contain the passed geometry type
8019 : *
8020 : * @since GDAL 2.0
8021 : */
8022 :
8023 35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8024 : {
8025 35 : const bool bHasZ = wkbHasZ(eType);
8026 35 : const bool bHasM = wkbHasM(eType);
8027 35 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8028 :
8029 35 : if (eFGType == wkbLineString)
8030 3 : eType = wkbCompoundCurve;
8031 :
8032 32 : else if (eFGType == wkbPolygon)
8033 1 : eType = wkbCurvePolygon;
8034 :
8035 31 : else if (eFGType == wkbTriangle)
8036 0 : eType = wkbCurvePolygon;
8037 :
8038 31 : else if (eFGType == wkbMultiLineString)
8039 6 : eType = wkbMultiCurve;
8040 :
8041 25 : else if (eFGType == wkbMultiPolygon)
8042 4 : eType = wkbMultiSurface;
8043 :
8044 35 : if (bHasZ)
8045 4 : eType = wkbSetZ(eType);
8046 35 : if (bHasM)
8047 4 : eType = wkbSetM(eType);
8048 :
8049 35 : return eType;
8050 : }
8051 :
8052 : /************************************************************************/
8053 : /* OGR_GT_GetLinear() */
8054 : /************************************************************************/
8055 : /**
8056 : * \brief Returns the non-curve geometry type that can contain the passed
8057 : * geometry type
8058 : *
8059 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8060 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8061 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8062 : * In other cases, the passed geometry is returned.
8063 : *
8064 : * Passed Z, M, ZM flag is preserved.
8065 : *
8066 : * @param eType Input geometry type
8067 : *
8068 : * @return the non-curve type that can contain the passed geometry type
8069 : *
8070 : * @since GDAL 2.0
8071 : */
8072 :
8073 770 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8074 : {
8075 770 : const bool bHasZ = wkbHasZ(eType);
8076 770 : const bool bHasM = wkbHasM(eType);
8077 770 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
8078 :
8079 770 : if (OGR_GT_IsCurve(eFGType))
8080 46 : eType = wkbLineString;
8081 :
8082 724 : else if (OGR_GT_IsSurface(eFGType))
8083 43 : eType = wkbPolygon;
8084 :
8085 681 : else if (eFGType == wkbMultiCurve)
8086 185 : eType = wkbMultiLineString;
8087 :
8088 496 : else if (eFGType == wkbMultiSurface)
8089 164 : eType = wkbMultiPolygon;
8090 :
8091 770 : if (bHasZ)
8092 154 : eType = wkbSetZ(eType);
8093 770 : if (bHasM)
8094 101 : eType = wkbSetM(eType);
8095 :
8096 770 : return eType;
8097 : }
8098 :
8099 : /************************************************************************/
8100 : /* OGR_GT_IsCurve() */
8101 : /************************************************************************/
8102 :
8103 : /**
8104 : * \brief Return if a geometry type is an instance of Curve
8105 : *
8106 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8107 : * and their Z/M/ZM variant.
8108 : *
8109 : * @param eGeomType the geometry type
8110 : * @return TRUE if the geometry type is an instance of Curve
8111 : *
8112 : * @since GDAL 2.0
8113 : */
8114 :
8115 22097 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8116 : {
8117 22097 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8118 : }
8119 :
8120 : /************************************************************************/
8121 : /* OGR_GT_IsSurface() */
8122 : /************************************************************************/
8123 :
8124 : /**
8125 : * \brief Return if a geometry type is an instance of Surface
8126 : *
8127 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8128 : * and their Z/M/ZM variant.
8129 : *
8130 : * @param eGeomType the geometry type
8131 : * @return TRUE if the geometry type is an instance of Surface
8132 : *
8133 : * @since GDAL 2.0
8134 : */
8135 :
8136 3625 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8137 : {
8138 3625 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8139 : }
8140 :
8141 : /************************************************************************/
8142 : /* OGR_GT_IsNonLinear() */
8143 : /************************************************************************/
8144 :
8145 : /**
8146 : * \brief Return if a geometry type is a non-linear geometry type.
8147 : *
8148 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8149 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8150 : * Z/M variants.
8151 : *
8152 : * @param eGeomType the geometry type
8153 : * @return TRUE if the geometry type is a non-linear geometry type.
8154 : *
8155 : * @since GDAL 2.0
8156 : */
8157 :
8158 109402 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8159 : {
8160 109402 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8161 109394 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8162 109332 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8163 218796 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8164 109402 : eFGeomType == wkbMultiSurface;
8165 : }
8166 :
8167 : /************************************************************************/
8168 : /* CastToError() */
8169 : /************************************************************************/
8170 :
8171 : //! @cond Doxygen_Suppress
8172 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8173 : {
8174 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8175 0 : poGeom->getGeometryName());
8176 0 : delete poGeom;
8177 0 : return nullptr;
8178 : }
8179 :
8180 : //! @endcond
8181 :
8182 : /************************************************************************/
8183 : /* OGRexportToSFCGAL() */
8184 : /************************************************************************/
8185 :
8186 : //! @cond Doxygen_Suppress
8187 : sfcgal_geometry_t *
8188 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8189 : {
8190 : #ifdef HAVE_SFCGAL
8191 :
8192 : sfcgal_init();
8193 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8194 :
8195 : const auto exportToSFCGALViaWKB =
8196 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8197 : {
8198 : if (!geom)
8199 : return nullptr;
8200 :
8201 : // Get WKB size and allocate buffer
8202 : size_t nSize = geom->WkbSize();
8203 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8204 :
8205 : // Set export options with NDR byte order
8206 : OGRwkbExportOptions oOptions;
8207 : oOptions.eByteOrder = wkbNDR;
8208 : // and ISO to avoid wkb25DBit for Z geometries
8209 : oOptions.eWkbVariant = wkbVariantIso;
8210 :
8211 : // Export to WKB
8212 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8213 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8214 : {
8215 : sfcgalGeom = sfcgal_io_read_wkb(
8216 : reinterpret_cast<const char *>(pabyWkb), nSize);
8217 : }
8218 :
8219 : CPLFree(pabyWkb);
8220 : return sfcgalGeom;
8221 : };
8222 :
8223 : // Handle special cases
8224 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8225 : {
8226 : std::unique_ptr<OGRLineString> poLS(
8227 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8228 : return exportToSFCGALViaWKB(poLS.get());
8229 : }
8230 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8231 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8232 : {
8233 : std::unique_ptr<OGRLineString> poLS(
8234 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8235 : ->toLineString());
8236 : return exportToSFCGALViaWKB(poLS.get());
8237 : }
8238 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8239 : {
8240 : std::unique_ptr<OGRPolygon> poPolygon(
8241 : OGRGeometryFactory::forceToPolygon(
8242 : poGeom->clone()->toCurvePolygon())
8243 : ->toPolygon());
8244 : return exportToSFCGALViaWKB(poPolygon.get());
8245 : }
8246 : else
8247 : {
8248 : // Default case - direct export
8249 : return exportToSFCGALViaWKB(poGeom);
8250 : }
8251 : #else
8252 : char *buffer = nullptr;
8253 :
8254 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8255 : // Polygon
8256 :
8257 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8258 : {
8259 : // cast it to LineString and get the WKT
8260 : std::unique_ptr<OGRLineString> poLS(
8261 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8262 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8263 : {
8264 : sfcgal_geometry_t *_geometry =
8265 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8266 : CPLFree(buffer);
8267 : return _geometry;
8268 : }
8269 : else
8270 : {
8271 : CPLFree(buffer);
8272 : return nullptr;
8273 : }
8274 : }
8275 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8276 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8277 : {
8278 : // convert it to LineString and get the WKT
8279 : std::unique_ptr<OGRLineString> poLS(
8280 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8281 : ->toLineString());
8282 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8283 : {
8284 : sfcgal_geometry_t *_geometry =
8285 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8286 : CPLFree(buffer);
8287 : return _geometry;
8288 : }
8289 : else
8290 : {
8291 : CPLFree(buffer);
8292 : return nullptr;
8293 : }
8294 : }
8295 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8296 : {
8297 : // convert it to Polygon and get the WKT
8298 : std::unique_ptr<OGRPolygon> poPolygon(
8299 : OGRGeometryFactory::forceToPolygon(
8300 : poGeom->clone()->toCurvePolygon())
8301 : ->toPolygon());
8302 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8303 : {
8304 : sfcgal_geometry_t *_geometry =
8305 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8306 : CPLFree(buffer);
8307 : return _geometry;
8308 : }
8309 : else
8310 : {
8311 : CPLFree(buffer);
8312 : return nullptr;
8313 : }
8314 : }
8315 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8316 : {
8317 : sfcgal_geometry_t *_geometry =
8318 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8319 : CPLFree(buffer);
8320 : return _geometry;
8321 : }
8322 : else
8323 : {
8324 : CPLFree(buffer);
8325 : return nullptr;
8326 : }
8327 : #endif
8328 : #else
8329 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8330 0 : return nullptr;
8331 : #endif
8332 : }
8333 :
8334 : //! @endcond
8335 :
8336 : /************************************************************************/
8337 : /* SFCGALexportToOGR() */
8338 : /************************************************************************/
8339 :
8340 : //! @cond Doxygen_Suppress
8341 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8342 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8343 : {
8344 : #ifdef HAVE_SFCGAL
8345 : if (geometry == nullptr)
8346 : return nullptr;
8347 :
8348 : sfcgal_init();
8349 : char *pabySFCGAL = nullptr;
8350 : size_t nLength = 0;
8351 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8352 :
8353 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8354 :
8355 : if (pabySFCGAL == nullptr || nLength == 0)
8356 : return nullptr;
8357 :
8358 : OGRGeometry *poGeom = nullptr;
8359 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8360 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8361 : nLength);
8362 :
8363 : free(pabySFCGAL);
8364 :
8365 : if (eErr == OGRERR_NONE)
8366 : {
8367 : return poGeom;
8368 : }
8369 : else
8370 : {
8371 : return nullptr;
8372 : }
8373 : #else
8374 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8375 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8376 : memcpy(pszWKT, pabySFCGAL, nLength);
8377 : pszWKT[nLength] = 0;
8378 : free(pabySFCGAL);
8379 :
8380 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8381 :
8382 : OGRGeometry *poGeom = nullptr;
8383 : if (geom_type == SFCGAL_TYPE_POINT)
8384 : {
8385 : poGeom = new OGRPoint();
8386 : }
8387 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8388 : {
8389 : poGeom = new OGRLineString();
8390 : }
8391 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8392 : {
8393 : poGeom = new OGRPolygon();
8394 : }
8395 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8396 : {
8397 : poGeom = new OGRMultiPoint();
8398 : }
8399 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8400 : {
8401 : poGeom = new OGRMultiLineString();
8402 : }
8403 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8404 : {
8405 : poGeom = new OGRMultiPolygon();
8406 : }
8407 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8408 : {
8409 : poGeom = new OGRGeometryCollection();
8410 : }
8411 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8412 : {
8413 : poGeom = new OGRTriangle();
8414 : }
8415 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8416 : {
8417 : poGeom = new OGRPolyhedralSurface();
8418 : }
8419 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8420 : {
8421 : poGeom = new OGRTriangulatedSurface();
8422 : }
8423 : else
8424 : {
8425 : CPLFree(pszWKT);
8426 : return nullptr;
8427 : }
8428 :
8429 : const char *pszWKTTmp = pszWKT;
8430 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8431 : {
8432 : CPLFree(pszWKT);
8433 : return poGeom;
8434 : }
8435 : else
8436 : {
8437 : delete poGeom;
8438 : CPLFree(pszWKT);
8439 : return nullptr;
8440 : }
8441 : #endif
8442 : #else
8443 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8444 0 : return nullptr;
8445 : #endif
8446 : }
8447 :
8448 : //! @endcond
8449 :
8450 : //! @cond Doxygen_Suppress
8451 7689 : OGRBoolean OGRGeometry::IsSFCGALCompatible() const
8452 : {
8453 7689 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8454 7701 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8455 : eGType == wkbTIN)
8456 : {
8457 2 : return TRUE;
8458 : }
8459 7699 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8460 : {
8461 6 : const OGRGeometryCollection *poGC = toGeometryCollection();
8462 9 : bool bIsSFCGALCompatible = false;
8463 9 : for (auto &&poSubGeom : *poGC)
8464 : {
8465 : OGRwkbGeometryType eSubGeomType =
8466 9 : wkbFlatten(poSubGeom->getGeometryType());
8467 9 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8468 : {
8469 0 : bIsSFCGALCompatible = true;
8470 : }
8471 9 : else if (eSubGeomType != wkbMultiPolygon)
8472 : {
8473 9 : bIsSFCGALCompatible = false;
8474 9 : break;
8475 : }
8476 : }
8477 9 : return bIsSFCGALCompatible;
8478 : }
8479 7693 : return FALSE;
8480 : }
8481 :
8482 : //! @endcond
8483 :
8484 : /************************************************************************/
8485 : /* roundCoordinatesIEEE754() */
8486 : /************************************************************************/
8487 :
8488 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8489 : * double-precision binary representation.
8490 : *
8491 : * Determines the number of bits (N) required to represent a coordinate value
8492 : * with a specified number of digits after the decimal point, and then sets all
8493 : * but the N most significant bits to zero. The resulting coordinate value will
8494 : * still round to the original value (e.g. after roundCoordinates()), but will
8495 : * have improved compressiblity.
8496 : *
8497 : * @param options Contains the precision requirements.
8498 : * @since GDAL 3.9
8499 : */
8500 1 : void OGRGeometry::roundCoordinatesIEEE754(
8501 : const OGRGeomCoordinateBinaryPrecision &options)
8502 : {
8503 : struct Quantizer : public OGRDefaultGeometryVisitor
8504 : {
8505 : const OGRGeomCoordinateBinaryPrecision &m_options;
8506 :
8507 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8508 1 : : m_options(optionsIn)
8509 : {
8510 1 : }
8511 :
8512 : using OGRDefaultGeometryVisitor::visit;
8513 :
8514 3 : void visit(OGRPoint *poPoint) override
8515 : {
8516 3 : if (m_options.nXYBitPrecision != INT_MIN)
8517 : {
8518 : uint64_t i;
8519 : double d;
8520 3 : d = poPoint->getX();
8521 3 : memcpy(&i, &d, sizeof(i));
8522 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8523 3 : memcpy(&d, &i, sizeof(i));
8524 3 : poPoint->setX(d);
8525 3 : d = poPoint->getY();
8526 3 : memcpy(&i, &d, sizeof(i));
8527 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8528 3 : memcpy(&d, &i, sizeof(i));
8529 3 : poPoint->setY(d);
8530 : }
8531 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8532 : {
8533 : uint64_t i;
8534 : double d;
8535 3 : d = poPoint->getZ();
8536 3 : memcpy(&i, &d, sizeof(i));
8537 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8538 3 : memcpy(&d, &i, sizeof(i));
8539 3 : poPoint->setZ(d);
8540 : }
8541 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8542 : {
8543 : uint64_t i;
8544 : double d;
8545 3 : d = poPoint->getM();
8546 3 : memcpy(&i, &d, sizeof(i));
8547 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8548 3 : memcpy(&d, &i, sizeof(i));
8549 3 : poPoint->setM(d);
8550 : }
8551 3 : }
8552 : };
8553 :
8554 2 : Quantizer quantizer(options);
8555 1 : accept(&quantizer);
8556 1 : }
8557 :
8558 : /************************************************************************/
8559 : /* visit() */
8560 : /************************************************************************/
8561 :
8562 105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8563 : {
8564 1248 : for (auto &&oPoint : *poGeom)
8565 : {
8566 1143 : oPoint.accept(this);
8567 : }
8568 105 : }
8569 :
8570 104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8571 : {
8572 104 : _visit(poGeom);
8573 104 : }
8574 :
8575 80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8576 : {
8577 80 : visit(poGeom->toUpperClass());
8578 80 : }
8579 :
8580 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8581 : {
8582 1 : _visit(poGeom);
8583 1 : }
8584 :
8585 78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8586 : {
8587 159 : for (auto &&poSubGeom : *poGeom)
8588 81 : poSubGeom->accept(this);
8589 78 : }
8590 :
8591 77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8592 : {
8593 77 : visit(poGeom->toUpperClass());
8594 77 : }
8595 :
8596 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8597 : {
8598 1 : visit(poGeom->toUpperClass());
8599 1 : }
8600 :
8601 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8602 : {
8603 8 : visit(poGeom->toUpperClass());
8604 8 : }
8605 :
8606 14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8607 : {
8608 14 : visit(poGeom->toUpperClass());
8609 14 : }
8610 :
8611 26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
8612 : {
8613 75 : for (auto &&poSubGeom : *poGeom)
8614 49 : poSubGeom->accept(this);
8615 26 : }
8616 :
8617 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
8618 : {
8619 2 : for (auto &&poSubGeom : *poGeom)
8620 1 : poSubGeom->accept(this);
8621 1 : }
8622 :
8623 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
8624 : {
8625 1 : visit(poGeom->toUpperClass());
8626 1 : }
8627 :
8628 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
8629 : {
8630 1 : visit(poGeom->toUpperClass());
8631 1 : }
8632 :
8633 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
8634 : {
8635 2 : visit(poGeom->toUpperClass());
8636 2 : }
8637 :
8638 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
8639 : {
8640 4 : for (auto &&poSubGeom : *poGeom)
8641 2 : poSubGeom->accept(this);
8642 2 : }
8643 :
8644 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
8645 : {
8646 1 : visit(poGeom->toUpperClass());
8647 1 : }
8648 :
8649 116 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
8650 : {
8651 2727 : for (auto &&oPoint : *poGeom)
8652 : {
8653 2611 : oPoint.accept(this);
8654 : }
8655 116 : }
8656 :
8657 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
8658 : {
8659 110 : _visit(poGeom);
8660 110 : }
8661 :
8662 99 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
8663 : {
8664 99 : visit(poGeom->toUpperClass());
8665 99 : }
8666 :
8667 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
8668 : {
8669 6 : _visit(poGeom);
8670 6 : }
8671 :
8672 101 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
8673 : {
8674 203 : for (auto &&poSubGeom : *poGeom)
8675 102 : poSubGeom->accept(this);
8676 101 : }
8677 :
8678 98 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
8679 : {
8680 98 : visit(poGeom->toUpperClass());
8681 98 : }
8682 :
8683 40 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
8684 : {
8685 40 : visit(poGeom->toUpperClass());
8686 40 : }
8687 :
8688 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
8689 : {
8690 1 : visit(poGeom->toUpperClass());
8691 1 : }
8692 :
8693 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
8694 : {
8695 1 : visit(poGeom->toUpperClass());
8696 1 : }
8697 :
8698 45 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
8699 : {
8700 217 : for (auto &&poSubGeom : *poGeom)
8701 172 : poSubGeom->accept(this);
8702 45 : }
8703 :
8704 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
8705 : {
8706 14 : for (auto &&poSubGeom : *poGeom)
8707 11 : poSubGeom->accept(this);
8708 3 : }
8709 :
8710 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
8711 : {
8712 1 : visit(poGeom->toUpperClass());
8713 1 : }
8714 :
8715 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
8716 : {
8717 1 : visit(poGeom->toUpperClass());
8718 1 : }
8719 :
8720 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
8721 : {
8722 2 : visit(poGeom->toUpperClass());
8723 2 : }
8724 :
8725 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
8726 : {
8727 4 : for (auto &&poSubGeom : *poGeom)
8728 2 : poSubGeom->accept(this);
8729 2 : }
8730 :
8731 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
8732 : {
8733 1 : visit(poGeom->toUpperClass());
8734 1 : }
8735 :
8736 : /************************************************************************/
8737 : /* OGRGeometryUniquePtrDeleter */
8738 : /************************************************************************/
8739 :
8740 : //! @cond Doxygen_Suppress
8741 1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
8742 : {
8743 1333 : delete poGeom;
8744 1333 : }
8745 :
8746 : //! @endcond
8747 :
8748 : /************************************************************************/
8749 : /* OGRPreparedGeometryUniquePtrDeleter */
8750 : /************************************************************************/
8751 :
8752 : //! @cond Doxygen_Suppress
8753 145 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
8754 : OGRPreparedGeometry *poPreparedGeom) const
8755 : {
8756 145 : OGRDestroyPreparedGeometry(poPreparedGeom);
8757 145 : }
8758 :
8759 : //! @endcond
8760 :
8761 : /************************************************************************/
8762 : /* HomogenizeDimensionalityWith() */
8763 : /************************************************************************/
8764 :
8765 : //! @cond Doxygen_Suppress
8766 3220930 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
8767 : {
8768 3220930 : if (poOtherGeom->Is3D() && !Is3D())
8769 1328580 : set3D(TRUE);
8770 :
8771 3220930 : if (poOtherGeom->IsMeasured() && !IsMeasured())
8772 846 : setMeasured(TRUE);
8773 :
8774 3220940 : if (!poOtherGeom->Is3D() && Is3D())
8775 297 : poOtherGeom->set3D(TRUE);
8776 :
8777 3220940 : if (!poOtherGeom->IsMeasured() && IsMeasured())
8778 35 : poOtherGeom->setMeasured(TRUE);
8779 3220940 : }
8780 :
8781 : //! @endcond
8782 :
8783 : /************************************************************************/
8784 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
8785 : /************************************************************************/
8786 :
8787 : /** Set binary precision options from resolution.
8788 : *
8789 : * @since GDAL 3.9
8790 : */
8791 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
8792 : const OGRGeomCoordinatePrecision &prec)
8793 : {
8794 16 : if (prec.dfXYResolution != 0)
8795 : {
8796 16 : nXYBitPrecision =
8797 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
8798 : }
8799 16 : if (prec.dfZResolution != 0)
8800 : {
8801 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
8802 : }
8803 16 : if (prec.dfMResolution != 0)
8804 : {
8805 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
8806 : }
8807 16 : }
8808 :
8809 : /************************************************************************/
8810 : /* OGRwkbExportOptionsCreate() */
8811 : /************************************************************************/
8812 :
8813 : /**
8814 : * \brief Create geometry WKB export options.
8815 : *
8816 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
8817 : *
8818 : * @return object to be freed with OGRwkbExportOptionsDestroy().
8819 : * @since GDAL 3.9
8820 : */
8821 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
8822 : {
8823 2 : return new OGRwkbExportOptions;
8824 : }
8825 :
8826 : /************************************************************************/
8827 : /* OGRwkbExportOptionsDestroy() */
8828 : /************************************************************************/
8829 :
8830 : /**
8831 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
8832 : *
8833 : * @param psOptions WKB export options
8834 : * @since GDAL 3.9
8835 : */
8836 :
8837 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
8838 : {
8839 2 : delete psOptions;
8840 2 : }
8841 :
8842 : /************************************************************************/
8843 : /* OGRwkbExportOptionsSetByteOrder() */
8844 : /************************************************************************/
8845 :
8846 : /**
8847 : * \brief Set the WKB byte order.
8848 : *
8849 : * @param psOptions WKB export options
8850 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
8851 : * Intel)
8852 : * @since GDAL 3.9
8853 : */
8854 :
8855 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
8856 : OGRwkbByteOrder eByteOrder)
8857 : {
8858 1 : psOptions->eByteOrder = eByteOrder;
8859 1 : }
8860 :
8861 : /************************************************************************/
8862 : /* OGRwkbExportOptionsSetVariant() */
8863 : /************************************************************************/
8864 :
8865 : /**
8866 : * \brief Set the WKB variant
8867 : *
8868 : * @param psOptions WKB export options
8869 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
8870 : * wkbVariantPostGIS1
8871 : * @since GDAL 3.9
8872 : */
8873 :
8874 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
8875 : OGRwkbVariant eWkbVariant)
8876 : {
8877 1 : psOptions->eWkbVariant = eWkbVariant;
8878 1 : }
8879 :
8880 : /************************************************************************/
8881 : /* OGRwkbExportOptionsSetPrecision() */
8882 : /************************************************************************/
8883 :
8884 : /**
8885 : * \brief Set precision options
8886 : *
8887 : * @param psOptions WKB export options
8888 : * @param hPrecisionOptions Precision options (might be null to reset them)
8889 : * @since GDAL 3.9
8890 : */
8891 :
8892 1 : void OGRwkbExportOptionsSetPrecision(
8893 : OGRwkbExportOptions *psOptions,
8894 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
8895 : {
8896 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
8897 1 : if (hPrecisionOptions)
8898 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
8899 1 : }
8900 :
8901 : /************************************************************************/
8902 : /* IsRectangle() */
8903 : /************************************************************************/
8904 :
8905 : /**
8906 : * \brief Returns whether the geometry is a polygon with 4 corners forming
8907 : * a rectangle.
8908 : *
8909 : * @since GDAL 3.10
8910 : */
8911 52348 : bool OGRGeometry::IsRectangle() const
8912 : {
8913 52348 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
8914 265 : return false;
8915 :
8916 52083 : const OGRPolygon *poPoly = toPolygon();
8917 :
8918 52082 : if (poPoly->getNumInteriorRings() != 0)
8919 19 : return false;
8920 :
8921 52064 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
8922 52064 : if (!poRing)
8923 4 : return false;
8924 :
8925 52060 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
8926 190 : return false;
8927 :
8928 : // If the ring has 5 points, the last should be the first.
8929 103699 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
8930 51829 : poRing->getY(0) != poRing->getY(4)))
8931 1 : return false;
8932 :
8933 : // Polygon with first segment in "y" direction.
8934 103056 : if (poRing->getX(0) == poRing->getX(1) &&
8935 102373 : poRing->getY(1) == poRing->getY(2) &&
8936 154242 : poRing->getX(2) == poRing->getX(3) &&
8937 51186 : poRing->getY(3) == poRing->getY(0))
8938 51186 : return true;
8939 :
8940 : // Polygon with first segment in "x" direction.
8941 1301 : if (poRing->getY(0) == poRing->getY(1) &&
8942 1236 : poRing->getX(1) == poRing->getX(2) &&
8943 1919 : poRing->getY(2) == poRing->getY(3) &&
8944 618 : poRing->getX(3) == poRing->getX(0))
8945 618 : return true;
8946 :
8947 65 : return false;
8948 : }
8949 :
8950 : /************************************************************************/
8951 : /* hasEmptyParts() */
8952 : /************************************************************************/
8953 :
8954 : /**
8955 : * \brief Returns whether a geometry has empty parts/rings.
8956 : *
8957 : * Returns true if removeEmptyParts() will modify the geometry.
8958 : *
8959 : * This is different from IsEmpty().
8960 : *
8961 : * @since GDAL 3.10
8962 : */
8963 20 : bool OGRGeometry::hasEmptyParts() const
8964 : {
8965 20 : return false;
8966 : }
8967 :
8968 : /************************************************************************/
8969 : /* removeEmptyParts() */
8970 : /************************************************************************/
8971 :
8972 : /**
8973 : * \brief Remove empty parts/rings from this geometry.
8974 : *
8975 : * @since GDAL 3.10
8976 : */
8977 17 : void OGRGeometry::removeEmptyParts()
8978 : {
8979 17 : }
8980 :
8981 : /************************************************************************/
8982 : /* ~IOGRGeometryVisitor() */
8983 : /************************************************************************/
8984 :
8985 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
8986 :
8987 : /************************************************************************/
8988 : /* ~IOGRConstGeometryVisitor() */
8989 : /************************************************************************/
8990 :
8991 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
|