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 11142 : int OGRWktOptions::getDefaultPrecision()
83 : {
84 11142 : return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
85 : }
86 :
87 11238 : bool OGRWktOptions::getDefaultRound()
88 : {
89 11238 : 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 2135120 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
112 2135120 : : poSRS(other.poSRS), flags(other.flags)
113 : {
114 2135120 : if (poSRS != nullptr)
115 486250 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
116 2135120 : }
117 :
118 : /************************************************************************/
119 : /* OGRGeometry( OGRGeometry&& ) */
120 : /************************************************************************/
121 :
122 : /**
123 : * \brief Move constructor.
124 : *
125 : * @since GDAL 3.11
126 : */
127 :
128 112145 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
129 112145 : : poSRS(other.poSRS), flags(other.flags)
130 : {
131 112145 : other.poSRS = nullptr;
132 112145 : }
133 :
134 : /************************************************************************/
135 : /* ~OGRGeometry() */
136 : /************************************************************************/
137 :
138 25615900 : OGRGeometry::~OGRGeometry()
139 :
140 : {
141 12807900 : if (poSRS != nullptr)
142 3988420 : const_cast<OGRSpatialReference *>(poSRS)->Release();
143 12807900 : }
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 85555 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
180 : {
181 85555 : if (this != &other)
182 : {
183 85555 : poSRS = other.poSRS;
184 85555 : other.poSRS = nullptr;
185 85555 : flags = other.flags;
186 : }
187 85555 : 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 5474460 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
516 :
517 : {
518 : // Do in that order to properly handle poSR == poSRS
519 5474460 : if (poSR != nullptr)
520 3531150 : const_cast<OGRSpatialReference *>(poSR)->Reference();
521 5474460 : if (poSRS != nullptr)
522 28740 : const_cast<OGRSpatialReference *>(poSRS)->Release();
523 :
524 5474460 : poSRS = poSR;
525 5474460 : }
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 17 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
834 : OGRCoordinateTransformationH hTransform)
835 :
836 : {
837 17 : VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
838 :
839 34 : return OGRGeometry::FromHandle(hGeom)->transform(
840 17 : 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 692980 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
864 : {
865 692980 : OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
866 :
867 692906 : if (flags & OGR_G_3D)
868 212663 : nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
869 692906 : if (flags & OGR_G_MEASURED)
870 26023 : nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
871 :
872 692906 : 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 1019800 : int OGRGeometry::getCoordinateDimension() const
973 :
974 : {
975 1019800 : 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 100581 : int OGRGeometry::CoordinateDimension() const
992 :
993 : {
994 100581 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
995 7297 : return 4;
996 93284 : else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
997 5932 : return 3;
998 : else
999 87352 : 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 706 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
1022 :
1023 : {
1024 706 : VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
1025 :
1026 706 : 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 35163 : int OGR_G_Is3D(OGRGeometryH hGeom)
1069 :
1070 : {
1071 35163 : VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1072 :
1073 35163 : 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 38044 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
1090 :
1091 : {
1092 38044 : VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
1093 :
1094 38044 : 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 65155 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
1117 :
1118 : {
1119 65155 : if (nNewDimension == 2)
1120 64667 : flags &= ~OGR_G_3D;
1121 : else
1122 488 : flags |= OGR_G_3D;
1123 65155 : 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 1581470 : bool OGRGeometry::set3D(OGRBoolean bIs3D)
1141 :
1142 : {
1143 1581470 : if (bIs3D)
1144 1576380 : flags |= OGR_G_3D;
1145 : else
1146 5090 : flags &= ~OGR_G_3D;
1147 1581470 : 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 409557 : bool OGRGeometry::setMeasured(OGRBoolean bIsMeasured)
1166 :
1167 : {
1168 409557 : if (bIsMeasured)
1169 137610 : flags |= OGR_G_MEASURED;
1170 : else
1171 271947 : flags &= ~OGR_G_MEASURED;
1172 409557 : 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 28188 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
1311 :
1312 : {
1313 28188 : VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
1314 :
1315 28188 : 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 56376 : return OGRGeometry::FromHandle(hGeom)->Equals(
1323 28188 : 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 13344 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
1452 :
1453 : {
1454 13344 : VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1455 :
1456 13344 : 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 259254 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
1617 : unsigned char *pabyData,
1618 : OGRwkbVariant eWkbVariant) const
1619 : {
1620 259254 : OGRwkbExportOptions sOptions;
1621 259254 : sOptions.eByteOrder = eByteOrder;
1622 259254 : sOptions.eWkbVariant = eWkbVariant;
1623 518433 : 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 122682 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
1815 : int *pbHasM, bool *pbIsEmpty)
1816 : {
1817 122682 : const char *pszInput = *ppszInput;
1818 :
1819 : /* -------------------------------------------------------------------- */
1820 : /* Clear existing Geoms. */
1821 : /* -------------------------------------------------------------------- */
1822 122682 : empty();
1823 122682 : *pbIsEmpty = false;
1824 :
1825 : /* -------------------------------------------------------------------- */
1826 : /* Read and verify the type keyword, and ensure it matches the */
1827 : /* actual type of this container. */
1828 : /* -------------------------------------------------------------------- */
1829 122682 : bool bHasM = false;
1830 122682 : bool bHasZ = false;
1831 122682 : bool bAlreadyGotDimension = false;
1832 :
1833 122682 : char szToken[OGR_WKT_TOKEN_MAX] = {};
1834 122682 : pszInput = OGRWktReadToken(pszInput, szToken);
1835 122682 : 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 122682 : const size_t nTokenLen = strlen(szToken);
1840 122682 : 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 122671 : 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 122682 : if (!EQUAL(szToken, getGeometryName()))
1862 0 : return OGRERR_CORRUPT_DATA;
1863 :
1864 : /* -------------------------------------------------------------------- */
1865 : /* Check for Z, M or ZM */
1866 : /* -------------------------------------------------------------------- */
1867 122682 : if (!bAlreadyGotDimension)
1868 : {
1869 122666 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1870 122666 : if (EQUAL(szToken, "Z"))
1871 : {
1872 1359 : pszInput = pszNewInput;
1873 1359 : bHasZ = true;
1874 : }
1875 121307 : else if (EQUAL(szToken, "M"))
1876 : {
1877 321 : pszInput = pszNewInput;
1878 321 : bHasM = true;
1879 : }
1880 120986 : else if (EQUAL(szToken, "ZM"))
1881 : {
1882 478 : pszInput = pszNewInput;
1883 478 : bHasZ = true;
1884 478 : bHasM = true;
1885 : }
1886 : }
1887 122682 : *pbHasZ = bHasZ;
1888 122682 : *pbHasM = bHasM;
1889 :
1890 : /* -------------------------------------------------------------------- */
1891 : /* Check for EMPTY ... */
1892 : /* -------------------------------------------------------------------- */
1893 122682 : const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1894 122682 : 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 121140 : if (!EQUAL(szToken, "("))
1906 35 : return OGRERR_CORRUPT_DATA;
1907 :
1908 121105 : if (!bHasZ && !bHasM)
1909 : {
1910 : // Test for old-style XXXXXXXXX(EMPTY).
1911 119104 : pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1912 119104 : 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 121061 : *ppszInput = pszInput;
1935 :
1936 121061 : 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 14136 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
1952 : {
1953 14136 : std::string s(" ");
1954 :
1955 14136 : if (variant == wkbVariantIso)
1956 : {
1957 9108 : if (flags & OGR_G_3D)
1958 1852 : s += "Z";
1959 9108 : if (flags & OGR_G_MEASURED)
1960 1088 : s += "M";
1961 : }
1962 14136 : if (s.size() > 1)
1963 2359 : s += " ";
1964 14136 : 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 8843 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
1990 : {
1991 8843 : OGRWktOptions opts;
1992 8843 : opts.variant = variant;
1993 8843 : OGRErr err(OGRERR_NONE);
1994 :
1995 8843 : std::string wkt = exportToWkt(opts, &err);
1996 8843 : *ppszDstText = CPLStrdup(wkt.data());
1997 17686 : 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 2479 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
2025 :
2026 : {
2027 2479 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2028 :
2029 2479 : 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 5415 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
2058 :
2059 : {
2060 5415 : VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2061 :
2062 5415 : return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2063 5415 : 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 5230 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
2097 :
2098 : {
2099 5230 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2100 :
2101 5230 : 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 16689 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
2133 :
2134 : {
2135 16689 : VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
2136 :
2137 16689 : 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 2177 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
2278 :
2279 : {
2280 2177 : VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
2281 :
2282 2177 : 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 2005 : OGRBoolean OGRGeometry::IsValid() const
2304 :
2305 : {
2306 2005 : 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 1971 : 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 1971 : initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
2353 :
2354 2011 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2355 :
2356 1958 : if (hThisGeosGeom != nullptr)
2357 : {
2358 1903 : 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 1918 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2368 : }
2369 2038 : freeGEOSContext(hGEOSCtxt);
2370 :
2371 1967 : 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 2615 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2556 : {
2557 2615 : OGRwkbGeometryType eType = wkbUnknown;
2558 2615 : bool bConvertTo3D = false;
2559 2615 : bool bIsMeasured = false;
2560 2615 : if (*pszGeomType != '\0')
2561 : {
2562 2609 : char ch = pszGeomType[strlen(pszGeomType) - 1];
2563 2609 : 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 2609 : if (ch == 'z' || ch == 'Z')
2570 : {
2571 27 : bConvertTo3D = true;
2572 : }
2573 : }
2574 :
2575 2615 : if (STARTS_WITH_CI(pszGeomType, "POINT"))
2576 572 : eType = wkbPoint;
2577 2043 : else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2578 182 : eType = wkbLineString;
2579 1861 : else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2580 732 : eType = wkbPolygon;
2581 1129 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2582 23 : eType = wkbMultiPoint;
2583 1106 : else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2584 13 : eType = wkbMultiLineString;
2585 1093 : else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2586 73 : eType = wkbMultiPolygon;
2587 1020 : else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2588 4 : eType = wkbGeometryCollection;
2589 1016 : else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2590 333 : eType = wkbCircularString;
2591 683 : else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2592 0 : eType = wkbCompoundCurve;
2593 683 : else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2594 16 : eType = wkbCurvePolygon;
2595 667 : else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2596 2 : eType = wkbMultiCurve;
2597 665 : else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2598 0 : eType = wkbMultiSurface;
2599 665 : else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2600 0 : eType = wkbTriangle;
2601 665 : else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2602 1 : eType = wkbPolyhedralSurface;
2603 664 : else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2604 5 : eType = wkbTIN;
2605 659 : else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2606 3 : eType = wkbCurve;
2607 656 : else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2608 3 : eType = wkbSurface;
2609 : else
2610 653 : eType = wkbUnknown;
2611 :
2612 2615 : if (bConvertTo3D)
2613 27 : eType = wkbSetZ(eType);
2614 2615 : if (bIsMeasured)
2615 2 : eType = wkbSetM(eType);
2616 :
2617 2615 : 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 2626 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2635 : bool bAddZM, bool bSpaceBeforeZM)
2636 : {
2637 2626 : const char *pszRet = "";
2638 2626 : switch (wkbFlatten(eGeomType))
2639 : {
2640 1477 : case wkbUnknown:
2641 1477 : pszRet = "Geometry";
2642 1477 : break;
2643 357 : case wkbPoint:
2644 357 : pszRet = "Point";
2645 357 : break;
2646 146 : case wkbLineString:
2647 146 : pszRet = "LineString";
2648 146 : break;
2649 311 : case wkbPolygon:
2650 311 : pszRet = "Polygon";
2651 311 : 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 2626 : 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 2626 : if (!bCamelCase)
2712 2580 : pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2713 2626 : 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 383 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2733 :
2734 : {
2735 383 : bool b3D = wkbHasZ(eType);
2736 383 : bool bMeasured = wkbHasM(eType);
2737 :
2738 383 : 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 52 : case wkbPoint:
2751 52 : if (b3D && bMeasured)
2752 2 : return "3D Measured Point";
2753 50 : else if (b3D)
2754 11 : return "3D Point";
2755 39 : else if (bMeasured)
2756 2 : return "Measured Point";
2757 : else
2758 37 : 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 60 : case wkbPolygon:
2771 60 : if (b3D && bMeasured)
2772 0 : return "3D Measured Polygon";
2773 60 : else if (b3D)
2774 8 : return "3D Polygon";
2775 52 : else if (bMeasured)
2776 0 : return "Measured Polygon";
2777 : else
2778 52 : 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 111 : case wkbNone:
2921 111 : 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 77803 : 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 77803 : 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 79746 : void OGRGeometry::freeGEOSContext(
3269 : UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt)
3270 : {
3271 : #ifdef HAVE_GEOS
3272 79746 : if (hGEOSCtxt != nullptr)
3273 : {
3274 79699 : finishGEOS_r(hGEOSCtxt);
3275 : }
3276 : #endif
3277 79788 : }
3278 :
3279 : #ifdef HAVE_GEOS
3280 :
3281 : /************************************************************************/
3282 : /* convertToGEOSGeom() */
3283 : /************************************************************************/
3284 :
3285 230002 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3286 : OGRGeometry *poGeom)
3287 : {
3288 230002 : GEOSGeom hGeom = nullptr;
3289 230002 : const size_t nDataSize = poGeom->WkbSize();
3290 : unsigned char *pabyData =
3291 230050 : static_cast<unsigned char *>(CPLMalloc(nDataSize));
3292 : #if GEOS_VERSION_MAJOR > 3 || \
3293 : (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3294 230080 : OGRwkbVariant eWkbVariant = wkbVariantIso;
3295 : #else
3296 : OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3297 : #endif
3298 230080 : if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3299 229119 : hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3300 230098 : CPLFree(pabyData);
3301 230118 : 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 230128 : 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 230128 : if (hGEOSCtxt == nullptr)
3330 0 : return nullptr;
3331 :
3332 230128 : 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 230086 : GEOSGeom hGeom = nullptr;
3343 :
3344 230086 : OGRGeometry *poLinearGeom = nullptr;
3345 230086 : 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 229217 : 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 229217 : if (bRemoveEmptyParts && hasEmptyParts())
3371 : {
3372 1 : poLinearGeom = clone();
3373 1 : poLinearGeom->removeEmptyParts();
3374 : }
3375 : }
3376 230050 : if (eType == wkbTriangle)
3377 : {
3378 97 : OGRPolygon oPolygon(*(poLinearGeom->toPolygon()));
3379 97 : hGeom = convertToGEOSGeom(hGEOSCtxt, &oPolygon);
3380 : }
3381 229953 : else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3382 : {
3383 1680 : 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 229123 : 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 228989 : hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
3435 : }
3436 :
3437 230085 : if (poLinearGeom != this)
3438 859 : delete poLinearGeom;
3439 :
3440 230021 : 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 275484 : OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3476 : {
3477 275484 : 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 79 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3509 : CPL_UNUSED const char *const *papszOptions) const
3510 : {
3511 79 : 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 3739 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
3786 : const OGRGeometry *poOtherGeom,
3787 : OGRGeometry *poOGRProduct)
3788 : {
3789 7478 : if (poOGRProduct != nullptr &&
3790 7402 : wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
3791 3663 : (poGeom->hasCurveGeometry(true) ||
3792 2645 : (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
3793 : {
3794 8 : OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
3795 8 : delete poOGRProduct;
3796 8 : return poCurveGeom;
3797 : }
3798 3731 : return poOGRProduct;
3799 : }
3800 :
3801 : /************************************************************************/
3802 : /* BuildGeometryFromGEOS() */
3803 : /************************************************************************/
3804 :
3805 3646 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
3806 : GEOSGeom hGeosProduct,
3807 : const OGRGeometry *poSelf,
3808 : const OGRGeometry *poOtherGeom)
3809 : {
3810 3646 : OGRGeometry *poOGRProduct = nullptr;
3811 3646 : if (hGeosProduct != nullptr)
3812 : {
3813 : poOGRProduct =
3814 3645 : OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
3815 3645 : if (poOGRProduct != nullptr &&
3816 8938 : poSelf->getSpatialReference() != nullptr &&
3817 1648 : (poOtherGeom == nullptr ||
3818 1648 : (poOtherGeom->getSpatialReference() != nullptr &&
3819 1520 : poOtherGeom->getSpatialReference()->IsSame(
3820 : poSelf->getSpatialReference()))))
3821 : {
3822 1546 : poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
3823 : }
3824 : poOGRProduct =
3825 3645 : OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
3826 3645 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
3827 : }
3828 3646 : return poOGRProduct;
3829 : }
3830 :
3831 : /************************************************************************/
3832 : /* BuildGeometryFromTwoGeoms() */
3833 : /************************************************************************/
3834 :
3835 2717 : static OGRGeometry *BuildGeometryFromTwoGeoms(
3836 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3837 : GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
3838 : const GEOSGeometry *,
3839 : const GEOSGeometry *))
3840 : {
3841 2717 : OGRGeometry *poOGRProduct = nullptr;
3842 :
3843 2717 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3844 2717 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3845 2717 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3846 2717 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3847 : {
3848 : GEOSGeom hGeosProduct =
3849 2717 : pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3850 :
3851 : poOGRProduct =
3852 2717 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
3853 : }
3854 2717 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3855 2717 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3856 2717 : poSelf->freeGEOSContext(hGEOSCtxt);
3857 :
3858 2717 : return poOGRProduct;
3859 : }
3860 :
3861 : /************************************************************************/
3862 : /* OGRGEOSBooleanPredicate() */
3863 : /************************************************************************/
3864 :
3865 21526 : static OGRBoolean OGRGEOSBooleanPredicate(
3866 : const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3867 : char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
3868 : const GEOSGeometry *))
3869 : {
3870 21526 : OGRBoolean bResult = FALSE;
3871 :
3872 21526 : GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3873 21526 : GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3874 21526 : GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3875 21526 : if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3876 : {
3877 21015 : bResult = pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3878 : }
3879 21526 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3880 21526 : GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3881 21526 : poSelf->freeGEOSContext(hGEOSCtxt);
3882 :
3883 21526 : 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 1896 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
4902 :
4903 : {
4904 1896 : 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 1896 : 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 21445 : 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 21445 : 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 6402 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
5889 :
5890 : {
5891 6402 : VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
5892 :
5893 12804 : return OGRGeometry::FromHandle(hThis)->Within(
5894 6402 : 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 : /* Polygonize() */
6770 : /************************************************************************/
6771 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
6772 : /* Developed for Faunalia (http://www.faunalia.it) with funding from */
6773 : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */
6774 : /* AMBIENTALE */
6775 : /************************************************************************/
6776 :
6777 : /**
6778 : * \brief Polygonizes a set of sparse edges.
6779 : *
6780 : * A new geometry object is created and returned containing a collection
6781 : * of reassembled Polygons: NULL will be returned if the input collection
6782 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
6783 : * into Polygons is impossible due to topological inconsistencies.
6784 : *
6785 : * This method is the same as the C function OGR_G_Polygonize().
6786 : *
6787 : * This method is built on the GEOS library, check it for the definition
6788 : * of the geometry operation.
6789 : * If OGR is built without the GEOS library, this method will always fail,
6790 : * issuing a CPLE_NotSupported error.
6791 : *
6792 : * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6793 : *
6794 : * @since OGR 1.9.0
6795 : */
6796 :
6797 117 : OGRGeometry *OGRGeometry::Polygonize() const
6798 :
6799 : {
6800 : #ifndef HAVE_GEOS
6801 :
6802 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6803 : return nullptr;
6804 :
6805 : #else
6806 :
6807 117 : const OGRGeometryCollection *poColl = nullptr;
6808 233 : if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
6809 116 : wkbFlatten(getGeometryType()) == wkbMultiLineString)
6810 116 : poColl = toGeometryCollection();
6811 : else
6812 1 : return nullptr;
6813 :
6814 116 : const int nCount = poColl->getNumGeometries();
6815 :
6816 116 : OGRGeometry *poPolygsOGRGeom = nullptr;
6817 116 : bool bError = false;
6818 :
6819 116 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6820 :
6821 116 : GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
6822 747 : for (int ig = 0; ig < nCount; ig++)
6823 : {
6824 631 : GEOSGeom hGeosGeom = nullptr;
6825 631 : const OGRGeometry *poChild = poColl->getGeometryRef(ig);
6826 1262 : if (poChild == nullptr ||
6827 631 : wkbFlatten(poChild->getGeometryType()) != wkbLineString)
6828 1 : bError = true;
6829 : else
6830 : {
6831 630 : hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
6832 630 : if (hGeosGeom == nullptr)
6833 0 : bError = true;
6834 : }
6835 631 : pahGeosGeomList[ig] = hGeosGeom;
6836 : }
6837 :
6838 116 : if (!bError)
6839 : {
6840 : GEOSGeom hGeosPolygs =
6841 115 : GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
6842 :
6843 : poPolygsOGRGeom =
6844 115 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
6845 : }
6846 :
6847 747 : for (int ig = 0; ig < nCount; ig++)
6848 : {
6849 631 : GEOSGeom hGeosGeom = pahGeosGeomList[ig];
6850 631 : if (hGeosGeom != nullptr)
6851 630 : GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
6852 : }
6853 116 : delete[] pahGeosGeomList;
6854 116 : freeGEOSContext(hGEOSCtxt);
6855 :
6856 116 : return poPolygsOGRGeom;
6857 :
6858 : #endif // HAVE_GEOS
6859 : }
6860 :
6861 : /************************************************************************/
6862 : /* OGR_G_Polygonize() */
6863 : /************************************************************************/
6864 : /**
6865 : * \brief Polygonizes a set of sparse edges.
6866 : *
6867 : * A new geometry object is created and returned containing a collection
6868 : * of reassembled Polygons: NULL will be returned if the input collection
6869 : * doesn't corresponds to a MultiLinestring, or when reassembling Edges
6870 : * into Polygons is impossible due to topological inconsistencies.
6871 : *
6872 : * This function is the same as the C++ method OGRGeometry::Polygonize().
6873 : *
6874 : * This function is built on the GEOS library, check it for the definition
6875 : * of the geometry operation.
6876 : * If OGR is built without the GEOS library, this function will always fail,
6877 : * issuing a CPLE_NotSupported error.
6878 : *
6879 : * @param hTarget The Geometry to be polygonized.
6880 : *
6881 : * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6882 : * or NULL if an error occurs.
6883 : *
6884 : * @since OGR 1.9.0
6885 : */
6886 :
6887 3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
6888 :
6889 : {
6890 3 : VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
6891 :
6892 3 : return OGRGeometry::ToHandle(
6893 6 : OGRGeometry::FromHandle(hTarget)->Polygonize());
6894 : }
6895 :
6896 : /************************************************************************/
6897 : /* BuildArea() */
6898 : /************************************************************************/
6899 :
6900 : /**
6901 : * \brief Polygonize a linework assuming inner polygons are holes.
6902 : *
6903 : * This method is the same as the C function OGR_G_BuildArea().
6904 : *
6905 : * Polygonization is performed similarly to OGRGeometry::Polygonize().
6906 : * Additionally, holes are dropped and the result is unified producing
6907 : * a single Polygon or a MultiPolygon.
6908 : *
6909 : * A new geometry object is created and returned: NULL on failure,
6910 : * empty GeometryCollection if the input geometry cannot be polygonized,
6911 : * Polygon or MultiPolygon on success.
6912 : *
6913 : * This method is built on the GEOSBuildArea_r() function of the GEOS
6914 : * library, check it for the definition of the geometry operation.
6915 : * If OGR is built without the GEOS library, this method will always fail,
6916 : * issuing a CPLE_NotSupported error.
6917 : *
6918 : * @return a newly allocated geometry now owned by the caller,
6919 : * or NULL on failure.
6920 : *
6921 : * @since OGR 3.11
6922 : */
6923 :
6924 14 : OGRGeometry *OGRGeometry::BuildArea() const
6925 :
6926 : {
6927 : #ifndef HAVE_GEOS
6928 :
6929 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6930 : return nullptr;
6931 :
6932 : #else
6933 :
6934 14 : OGRGeometry *poPolygsOGRGeom = nullptr;
6935 :
6936 14 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6937 14 : GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6938 14 : if (hThisGeosGeom != nullptr)
6939 : {
6940 14 : GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
6941 : poPolygsOGRGeom =
6942 14 : BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
6943 14 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6944 : }
6945 14 : freeGEOSContext(hGEOSCtxt);
6946 :
6947 14 : return poPolygsOGRGeom;
6948 :
6949 : #endif // HAVE_GEOS
6950 : }
6951 :
6952 : /************************************************************************/
6953 : /* OGR_G_BuildArea() */
6954 : /************************************************************************/
6955 :
6956 : /**
6957 : * \brief Polygonize a linework assuming inner polygons are holes.
6958 : *
6959 : * This function is the same as the C++ method OGRGeometry::BuildArea().
6960 : *
6961 : * Polygonization is performed similarly to OGR_G_Polygonize().
6962 : * Additionally, holes are dropped and the result is unified producing
6963 : * a single Polygon or a MultiPolygon.
6964 : *
6965 : * A new geometry object is created and returned: NULL on failure,
6966 : * empty GeometryCollection if the input geometry cannot be polygonized,
6967 : * Polygon or MultiPolygon on success.
6968 : *
6969 : * This function is built on the GEOSBuildArea_r() function of the GEOS
6970 : * library, check it for the definition of the geometry operation.
6971 : * If OGR is built without the GEOS library, this function will always fail,
6972 : * issuing a CPLE_NotSupported error.
6973 : *
6974 : * @param hGeom handle on the geometry to polygonize.
6975 : *
6976 : * @return a handle on newly allocated geometry now owned by the caller,
6977 : * or NULL on failure.
6978 : *
6979 : * @since OGR 3.11
6980 : */
6981 :
6982 0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
6983 :
6984 : {
6985 0 : VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
6986 :
6987 0 : return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
6988 : }
6989 :
6990 : /************************************************************************/
6991 : /* swapXY() */
6992 : /************************************************************************/
6993 :
6994 : /**
6995 : * \brief Swap x and y coordinates.
6996 : *
6997 : * @since OGR 1.8.0
6998 : */
6999 :
7000 0 : void OGRGeometry::swapXY()
7001 :
7002 : {
7003 0 : }
7004 :
7005 : /************************************************************************/
7006 : /* swapXY() */
7007 : /************************************************************************/
7008 :
7009 : /**
7010 : * \brief Swap x and y coordinates.
7011 : *
7012 : * @param hGeom geometry.
7013 : * @since OGR 2.3.0
7014 : */
7015 :
7016 2 : void OGR_G_SwapXY(OGRGeometryH hGeom)
7017 : {
7018 2 : VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7019 :
7020 2 : OGRGeometry::FromHandle(hGeom)->swapXY();
7021 : }
7022 :
7023 : /************************************************************************/
7024 : /* Prepared geometry API */
7025 : /************************************************************************/
7026 :
7027 : #if defined(HAVE_GEOS)
7028 : struct _OGRPreparedGeometry
7029 : {
7030 : GEOSContextHandle_t hGEOSCtxt;
7031 : GEOSGeom hGEOSGeom;
7032 : const GEOSPreparedGeometry *poPreparedGEOSGeom;
7033 : };
7034 : #endif
7035 :
7036 : /************************************************************************/
7037 : /* OGRHasPreparedGeometrySupport() */
7038 : /************************************************************************/
7039 :
7040 : /** Returns if GEOS has prepared geometry support.
7041 : * @return TRUE or FALSE
7042 : */
7043 1 : int OGRHasPreparedGeometrySupport()
7044 : {
7045 : #if defined(HAVE_GEOS)
7046 1 : return TRUE;
7047 : #else
7048 : return FALSE;
7049 : #endif
7050 : }
7051 :
7052 : /************************************************************************/
7053 : /* OGRCreatePreparedGeometry() */
7054 : /************************************************************************/
7055 :
7056 : /** Creates a prepared geometry.
7057 : *
7058 : * To free with OGRDestroyPreparedGeometry()
7059 : *
7060 : * @param hGeom input geometry to prepare.
7061 : * @return handle to a prepared geometry.
7062 : * @since GDAL 3.3
7063 : */
7064 : OGRPreparedGeometryH
7065 52352 : OGRCreatePreparedGeometry(UNUSED_IF_NO_GEOS OGRGeometryH hGeom)
7066 : {
7067 : #if defined(HAVE_GEOS)
7068 52352 : OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7069 52352 : GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7070 52352 : GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7071 52352 : if (hGEOSGeom == nullptr)
7072 : {
7073 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7074 0 : return nullptr;
7075 : }
7076 : const GEOSPreparedGeometry *poPreparedGEOSGeom =
7077 52352 : GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7078 52352 : if (poPreparedGEOSGeom == nullptr)
7079 : {
7080 0 : GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7081 0 : OGRGeometry::freeGEOSContext(hGEOSCtxt);
7082 0 : return nullptr;
7083 : }
7084 :
7085 52352 : OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7086 52352 : poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7087 52352 : poPreparedGeom->hGEOSGeom = hGEOSGeom;
7088 52352 : poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7089 :
7090 52352 : return poPreparedGeom;
7091 : #else
7092 : return nullptr;
7093 : #endif
7094 : }
7095 :
7096 : /************************************************************************/
7097 : /* OGRDestroyPreparedGeometry() */
7098 : /************************************************************************/
7099 :
7100 : /** Destroys a prepared geometry.
7101 : * @param hPreparedGeom prepared geometry.
7102 : * @since GDAL 3.3
7103 : */
7104 52398 : void OGRDestroyPreparedGeometry(
7105 : UNUSED_IF_NO_GEOS OGRPreparedGeometryH hPreparedGeom)
7106 : {
7107 : #if defined(HAVE_GEOS)
7108 52398 : if (hPreparedGeom != nullptr)
7109 : {
7110 52352 : GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7111 : hPreparedGeom->poPreparedGEOSGeom);
7112 52352 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7113 52352 : OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7114 52352 : delete hPreparedGeom;
7115 : }
7116 : #endif
7117 52398 : }
7118 :
7119 : /************************************************************************/
7120 : /* OGRPreparedGeometryIntersects() */
7121 : /************************************************************************/
7122 :
7123 : /** Returns whether a prepared geometry intersects with a geometry.
7124 : * @param hPreparedGeom prepared geometry.
7125 : * @param hOtherGeom other geometry.
7126 : * @return TRUE or FALSE.
7127 : * @since GDAL 3.3
7128 : */
7129 5235 : int OGRPreparedGeometryIntersects(
7130 : UNUSED_IF_NO_GEOS const OGRPreparedGeometryH hPreparedGeom,
7131 : UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
7132 : {
7133 : #if defined(HAVE_GEOS)
7134 5235 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7135 5235 : if (hPreparedGeom == nullptr ||
7136 : poOtherGeom == nullptr
7137 : // The check for IsEmpty() is for buggy GEOS versions.
7138 : // See https://github.com/libgeos/geos/pull/423
7139 10470 : || poOtherGeom->IsEmpty())
7140 : {
7141 1 : return FALSE;
7142 : }
7143 :
7144 : GEOSGeom hGEOSOtherGeom =
7145 5234 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7146 5234 : if (hGEOSOtherGeom == nullptr)
7147 0 : return FALSE;
7148 :
7149 5234 : const bool bRet = CPL_TO_BOOL(GEOSPreparedIntersects_r(
7150 : hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
7151 : hGEOSOtherGeom));
7152 5234 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7153 :
7154 5234 : return bRet;
7155 : #else
7156 : return FALSE;
7157 : #endif
7158 : }
7159 :
7160 : /** Returns whether a prepared geometry contains a geometry.
7161 : * @param hPreparedGeom prepared geometry.
7162 : * @param hOtherGeom other geometry.
7163 : * @return TRUE or FALSE.
7164 : */
7165 120116 : int OGRPreparedGeometryContains(UNUSED_IF_NO_GEOS const OGRPreparedGeometryH
7166 : hPreparedGeom,
7167 : UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
7168 : {
7169 : #if defined(HAVE_GEOS)
7170 120116 : OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7171 120116 : if (hPreparedGeom == nullptr ||
7172 : poOtherGeom == nullptr
7173 : // The check for IsEmpty() is for buggy GEOS versions.
7174 : // See https://github.com/libgeos/geos/pull/423
7175 240232 : || poOtherGeom->IsEmpty())
7176 : {
7177 1 : return FALSE;
7178 : }
7179 :
7180 : GEOSGeom hGEOSOtherGeom =
7181 120115 : poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7182 120115 : if (hGEOSOtherGeom == nullptr)
7183 0 : return FALSE;
7184 :
7185 120115 : const bool bRet = CPL_TO_BOOL(GEOSPreparedContains_r(
7186 : hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
7187 : hGEOSOtherGeom));
7188 120115 : GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7189 :
7190 120115 : return bRet;
7191 : #else
7192 : return FALSE;
7193 : #endif
7194 : }
7195 :
7196 : /************************************************************************/
7197 : /* OGRGeometryFromEWKB() */
7198 : /************************************************************************/
7199 :
7200 1429 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7201 : int bIsPostGIS1_EWKB)
7202 :
7203 : {
7204 1429 : OGRGeometry *poGeometry = nullptr;
7205 :
7206 1429 : size_t nWKBSize = 0;
7207 1429 : const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7208 1429 : if (pabyWKB == nullptr)
7209 0 : return nullptr;
7210 :
7211 : /* -------------------------------------------------------------------- */
7212 : /* Try to ingest the geometry. */
7213 : /* -------------------------------------------------------------------- */
7214 1429 : (void)OGRGeometryFactory::createFromWkb(
7215 : pabyWKB, nullptr, &poGeometry, nWKBSize,
7216 : (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7217 :
7218 1429 : return poGeometry;
7219 : }
7220 :
7221 : /************************************************************************/
7222 : /* OGRGeometryFromHexEWKB() */
7223 : /************************************************************************/
7224 :
7225 1427 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7226 : int bIsPostGIS1_EWKB)
7227 :
7228 : {
7229 1427 : if (pszBytea == nullptr)
7230 0 : return nullptr;
7231 :
7232 1427 : int nWKBLength = 0;
7233 1427 : GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7234 :
7235 : OGRGeometry *poGeometry =
7236 1427 : OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7237 :
7238 1427 : CPLFree(pabyWKB);
7239 :
7240 1427 : return poGeometry;
7241 : }
7242 :
7243 : /************************************************************************/
7244 : /* OGRGeometryToHexEWKB() */
7245 : /************************************************************************/
7246 :
7247 1059 : char *OGRGeometryToHexEWKB(OGRGeometry *poGeometry, int nSRSId,
7248 : int nPostGISMajor, int nPostGISMinor)
7249 : {
7250 1059 : const size_t nWkbSize = poGeometry->WkbSize();
7251 1059 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7252 1059 : if (pabyWKB == nullptr)
7253 0 : return CPLStrdup("");
7254 :
7255 118 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7256 1795 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7257 618 : poGeometry->IsEmpty())
7258 : {
7259 2 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7260 : OGRERR_NONE)
7261 : {
7262 0 : CPLFree(pabyWKB);
7263 0 : return CPLStrdup("");
7264 : }
7265 : }
7266 1057 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7267 : (nPostGISMajor < 2)
7268 : ? wkbVariantPostGIS1
7269 1057 : : wkbVariantOldOgc) != OGRERR_NONE)
7270 : {
7271 0 : CPLFree(pabyWKB);
7272 0 : return CPLStrdup("");
7273 : }
7274 :
7275 : // When converting to hex, each byte takes 2 hex characters. In addition
7276 : // we add in 8 characters to represent the SRID integer in hex, and
7277 : // one for a null terminator.
7278 : // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7279 : // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7280 : // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7281 1059 : if (nWkbSize >
7282 1059 : static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7283 : {
7284 0 : CPLFree(pabyWKB);
7285 0 : return CPLStrdup("");
7286 : }
7287 1059 : const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7288 1059 : char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7289 1059 : if (pszTextBuf == nullptr)
7290 : {
7291 0 : CPLFree(pabyWKB);
7292 0 : return CPLStrdup("");
7293 : }
7294 1059 : char *pszTextBufCurrent = pszTextBuf;
7295 :
7296 : // Convert the 1st byte, which is the endianness flag, to hex.
7297 1059 : char *pszHex = CPLBinaryToHex(1, pabyWKB);
7298 1059 : strcpy(pszTextBufCurrent, pszHex);
7299 1059 : CPLFree(pszHex);
7300 1059 : pszTextBufCurrent += 2;
7301 :
7302 : // Next, get the geom type which is bytes 2 through 5.
7303 : GUInt32 geomType;
7304 1059 : memcpy(&geomType, pabyWKB + 1, 4);
7305 :
7306 : // Now add the SRID flag if an SRID is provided.
7307 1059 : if (nSRSId > 0)
7308 : {
7309 : // Change the flag to wkbNDR (little) endianness.
7310 529 : constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7311 529 : GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7312 : // Apply the flag.
7313 529 : geomType = geomType | nGSrsFlag;
7314 : }
7315 :
7316 : // Now write the geom type which is 4 bytes.
7317 1059 : pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7318 1059 : strcpy(pszTextBufCurrent, pszHex);
7319 1059 : CPLFree(pszHex);
7320 1059 : pszTextBufCurrent += 8;
7321 :
7322 : // Now include SRID if provided.
7323 1059 : if (nSRSId > 0)
7324 : {
7325 : // Force the srsid to wkbNDR (little) endianness.
7326 529 : const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7327 529 : pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7328 : reinterpret_cast<const GByte *>(&nGSRSId));
7329 529 : strcpy(pszTextBufCurrent, pszHex);
7330 529 : CPLFree(pszHex);
7331 529 : pszTextBufCurrent += 8;
7332 : }
7333 :
7334 : // Copy the rest of the data over - subtract
7335 : // 5 since we already copied 5 bytes above.
7336 1059 : pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7337 1059 : CPLFree(pabyWKB);
7338 1059 : if (!pszHex || pszHex[0] == 0)
7339 : {
7340 0 : CPLFree(pszTextBuf);
7341 0 : return pszHex;
7342 : }
7343 1059 : strcpy(pszTextBufCurrent, pszHex);
7344 1059 : CPLFree(pszHex);
7345 :
7346 1059 : return pszTextBuf;
7347 : }
7348 :
7349 : /************************************************************************/
7350 : /* importPreambleFromWkb() */
7351 : /************************************************************************/
7352 :
7353 : //! @cond Doxygen_Suppress
7354 156990 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7355 : size_t nSize,
7356 : OGRwkbByteOrder &eByteOrder,
7357 : OGRwkbVariant eWkbVariant)
7358 : {
7359 156990 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
7360 0 : return OGRERR_NOT_ENOUGH_DATA;
7361 :
7362 : /* -------------------------------------------------------------------- */
7363 : /* Get the byte order byte. */
7364 : /* -------------------------------------------------------------------- */
7365 156990 : int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7366 156990 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7367 0 : return OGRERR_CORRUPT_DATA;
7368 156990 : eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7369 :
7370 : /* -------------------------------------------------------------------- */
7371 : /* Get the geometry feature type. */
7372 : /* -------------------------------------------------------------------- */
7373 : OGRwkbGeometryType eGeometryType;
7374 : const OGRErr err =
7375 156990 : OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7376 156990 : if (wkbHasZ(eGeometryType))
7377 62304 : flags |= OGR_G_3D;
7378 156990 : if (wkbHasM(eGeometryType))
7379 59690 : flags |= OGR_G_MEASURED;
7380 :
7381 156990 : if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7382 0 : return OGRERR_CORRUPT_DATA;
7383 :
7384 156990 : return OGRERR_NONE;
7385 : }
7386 :
7387 : /************************************************************************/
7388 : /* importPreambleOfCollectionFromWkb() */
7389 : /* */
7390 : /* Utility method for OGRSimpleCurve, OGRCompoundCurve, */
7391 : /* OGRCurvePolygon and OGRGeometryCollection. */
7392 : /************************************************************************/
7393 :
7394 74360 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7395 : const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7396 : OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7397 : OGRwkbVariant eWkbVariant)
7398 : {
7399 74360 : nGeomCount = 0;
7400 :
7401 : OGRErr eErr =
7402 74360 : importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7403 74360 : if (eErr != OGRERR_NONE)
7404 0 : return eErr;
7405 :
7406 : /* -------------------------------------------------------------------- */
7407 : /* Clear existing Geoms. */
7408 : /* -------------------------------------------------------------------- */
7409 74360 : int _flags = flags; // flags set in importPreambleFromWkb
7410 74360 : empty(); // may reset flags etc.
7411 :
7412 : // restore
7413 74360 : if (_flags & OGR_G_3D)
7414 59262 : set3D(TRUE);
7415 74360 : if (_flags & OGR_G_MEASURED)
7416 56768 : setMeasured(TRUE);
7417 :
7418 : /* -------------------------------------------------------------------- */
7419 : /* Get the sub-geometry count. */
7420 : /* -------------------------------------------------------------------- */
7421 74360 : memcpy(&nGeomCount, pabyData + 5, 4);
7422 :
7423 74360 : if (OGR_SWAP(eByteOrder))
7424 383 : nGeomCount = CPL_SWAP32(nGeomCount);
7425 :
7426 148586 : if (nGeomCount < 0 ||
7427 74226 : static_cast<size_t>(nGeomCount) >
7428 74226 : std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7429 : {
7430 134 : nGeomCount = 0;
7431 134 : return OGRERR_CORRUPT_DATA;
7432 : }
7433 74226 : const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7434 :
7435 : // Each ring has a minimum of nMinSubGeomSize bytes.
7436 74226 : if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7437 : {
7438 910 : CPLError(CE_Failure, CPLE_AppDefined,
7439 : "Length of input WKB is too small");
7440 910 : nGeomCount = 0;
7441 910 : return OGRERR_NOT_ENOUGH_DATA;
7442 : }
7443 :
7444 73316 : nDataOffset = 9;
7445 73316 : if (nSize != static_cast<size_t>(-1))
7446 : {
7447 73296 : CPLAssert(nSize >= nDataOffset);
7448 73296 : nSize -= nDataOffset;
7449 : }
7450 :
7451 73316 : return OGRERR_NONE;
7452 : }
7453 :
7454 : /************************************************************************/
7455 : /* importCurveCollectionFromWkt() */
7456 : /* */
7457 : /* Utility method for OGRCompoundCurve, OGRCurvePolygon and */
7458 : /* OGRMultiCurve. */
7459 : /************************************************************************/
7460 :
7461 1398 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
7462 : const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7463 : int bAllowCurve, int bAllowCompoundCurve,
7464 : OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7465 :
7466 : {
7467 1398 : int bHasZ = FALSE;
7468 1398 : int bHasM = FALSE;
7469 1398 : bool bIsEmpty = false;
7470 1398 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7471 1398 : flags = 0;
7472 1398 : if (eErr != OGRERR_NONE)
7473 14 : return eErr;
7474 1384 : if (bHasZ)
7475 189 : flags |= OGR_G_3D;
7476 1384 : if (bHasM)
7477 120 : flags |= OGR_G_MEASURED;
7478 1384 : if (bIsEmpty)
7479 109 : return OGRERR_NONE;
7480 :
7481 : char szToken[OGR_WKT_TOKEN_MAX];
7482 1275 : const char *pszInput = *ppszInput;
7483 1275 : eErr = OGRERR_NONE;
7484 :
7485 : // Skip first '('.
7486 1275 : pszInput = OGRWktReadToken(pszInput, szToken);
7487 :
7488 : /* ==================================================================== */
7489 : /* Read each curve in turn. Note that we try to reuse the same */
7490 : /* point list buffer from curve to curve to cut down on */
7491 : /* allocate/deallocate overhead. */
7492 : /* ==================================================================== */
7493 1275 : OGRRawPoint *paoPoints = nullptr;
7494 1275 : int nMaxPoints = 0;
7495 1275 : double *padfZ = nullptr;
7496 :
7497 611 : do
7498 : {
7499 :
7500 : /* --------------------------------------------------------------------
7501 : */
7502 : /* Get the first token, which should be the geometry type. */
7503 : /* --------------------------------------------------------------------
7504 : */
7505 1886 : const char *pszInputBefore = pszInput;
7506 1886 : pszInput = OGRWktReadToken(pszInput, szToken);
7507 :
7508 : /* --------------------------------------------------------------------
7509 : */
7510 : /* Do the import. */
7511 : /* --------------------------------------------------------------------
7512 : */
7513 1886 : OGRCurve *poCurve = nullptr;
7514 1886 : if (EQUAL(szToken, "("))
7515 : {
7516 1364 : OGRLineString *poLine = new OGRLineString();
7517 1364 : poCurve = poLine;
7518 1364 : pszInput = pszInputBefore;
7519 1364 : eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
7520 : paoPoints, nMaxPoints, padfZ);
7521 : }
7522 522 : else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
7523 : {
7524 16 : poCurve = new OGRLineString();
7525 : }
7526 : // Accept LINESTRING(), but this is an extension to the BNF, also
7527 : // accepted by PostGIS.
7528 506 : else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
7529 491 : (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
7530 491 : !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
7531 1162 : OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
7532 150 : (bAllowCompoundCurve &&
7533 150 : STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
7534 : {
7535 468 : OGRGeometry *poGeom = nullptr;
7536 468 : pszInput = pszInputBefore;
7537 : eErr =
7538 468 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
7539 468 : if (poGeom == nullptr)
7540 : {
7541 1 : eErr = OGRERR_CORRUPT_DATA;
7542 : }
7543 : else
7544 : {
7545 467 : poCurve = poGeom->toCurve();
7546 : }
7547 : }
7548 : else
7549 : {
7550 38 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
7551 : szToken);
7552 38 : eErr = OGRERR_CORRUPT_DATA;
7553 : }
7554 :
7555 : // If this has M it is an error if poGeom does not have M.
7556 1886 : if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
7557 0 : eErr = OGRERR_CORRUPT_DATA;
7558 :
7559 1886 : if (eErr == OGRERR_NONE)
7560 1841 : eErr = pfnAddCurveDirectly(this, poCurve);
7561 1886 : if (eErr != OGRERR_NONE)
7562 : {
7563 55 : delete poCurve;
7564 55 : break;
7565 : }
7566 :
7567 : /* --------------------------------------------------------------------
7568 : */
7569 : /* Read the delimiter following the surface. */
7570 : /* --------------------------------------------------------------------
7571 : */
7572 1831 : pszInput = OGRWktReadToken(pszInput, szToken);
7573 1831 : } while (szToken[0] == ',' && eErr == OGRERR_NONE);
7574 :
7575 1275 : CPLFree(paoPoints);
7576 1275 : CPLFree(padfZ);
7577 :
7578 : /* -------------------------------------------------------------------- */
7579 : /* freak if we don't get a closing bracket. */
7580 : /* -------------------------------------------------------------------- */
7581 :
7582 1275 : if (eErr != OGRERR_NONE)
7583 55 : return eErr;
7584 :
7585 1220 : if (szToken[0] != ')')
7586 9 : return OGRERR_CORRUPT_DATA;
7587 :
7588 1211 : *ppszInput = pszInput;
7589 1211 : return OGRERR_NONE;
7590 : }
7591 :
7592 : //! @endcond
7593 :
7594 : /************************************************************************/
7595 : /* OGR_GT_Flatten() */
7596 : /************************************************************************/
7597 : /**
7598 : * \brief Returns the 2D geometry type corresponding to the passed geometry
7599 : * type.
7600 : *
7601 : * This function is intended to work with geometry types as old-style 99-402
7602 : * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
7603 : * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
7604 : *
7605 : * @param eType Input geometry type
7606 : *
7607 : * @return 2D geometry type corresponding to the passed geometry type.
7608 : *
7609 : * @since GDAL 2.0
7610 : */
7611 :
7612 7483780 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
7613 : {
7614 7483780 : eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
7615 7483780 : if (eType >= 1000 && eType < 2000) // ISO Z.
7616 2708820 : return static_cast<OGRwkbGeometryType>(eType - 1000);
7617 4774970 : if (eType >= 2000 && eType < 3000) // ISO M.
7618 5754 : return static_cast<OGRwkbGeometryType>(eType - 2000);
7619 4769210 : if (eType >= 3000 && eType < 4000) // ISO ZM.
7620 135645 : return static_cast<OGRwkbGeometryType>(eType - 3000);
7621 4633570 : return eType;
7622 : }
7623 :
7624 : /************************************************************************/
7625 : /* OGR_GT_HasZ() */
7626 : /************************************************************************/
7627 : /**
7628 : * \brief Return if the geometry type is a 3D geometry type.
7629 : *
7630 : * @param eType Input geometry type
7631 : *
7632 : * @return TRUE if the geometry type is a 3D geometry type.
7633 : *
7634 : * @since GDAL 2.0
7635 : */
7636 :
7637 2016040 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
7638 : {
7639 2016040 : if (eType & wkb25DBitInternalUse)
7640 156348 : return TRUE;
7641 1859690 : if (eType >= 1000 && eType < 2000) // Accept 1000 for wkbUnknownZ.
7642 251 : return TRUE;
7643 1859440 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
7644 121235 : return TRUE;
7645 1738210 : return FALSE;
7646 : }
7647 :
7648 : /************************************************************************/
7649 : /* OGR_GT_HasM() */
7650 : /************************************************************************/
7651 : /**
7652 : * \brief Return if the geometry type is a measured type.
7653 : *
7654 : * @param eType Input geometry type
7655 : *
7656 : * @return TRUE if the geometry type is a measured type.
7657 : *
7658 : * @since GDAL 2.1
7659 : */
7660 :
7661 2072970 : int OGR_GT_HasM(OGRwkbGeometryType eType)
7662 : {
7663 2072970 : if (eType >= 2000 && eType < 3000) // Accept 2000 for wkbUnknownM.
7664 2549 : return TRUE;
7665 2070420 : if (eType >= 3000 && eType < 4000) // Accept 3000 for wkbUnknownZM.
7666 120897 : return TRUE;
7667 1949530 : return FALSE;
7668 : }
7669 :
7670 : /************************************************************************/
7671 : /* OGR_GT_SetZ() */
7672 : /************************************************************************/
7673 : /**
7674 : * \brief Returns the 3D geometry type corresponding to the passed geometry
7675 : * type.
7676 : *
7677 : * @param eType Input geometry type
7678 : *
7679 : * @return 3D geometry type corresponding to the passed geometry type.
7680 : *
7681 : * @since GDAL 2.0
7682 : */
7683 :
7684 5298 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
7685 : {
7686 5298 : if (OGR_GT_HasZ(eType) || eType == wkbNone)
7687 494 : return eType;
7688 4804 : if (eType <= wkbGeometryCollection)
7689 4745 : return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
7690 : else
7691 59 : return static_cast<OGRwkbGeometryType>(eType + 1000);
7692 : }
7693 :
7694 : /************************************************************************/
7695 : /* OGR_GT_SetM() */
7696 : /************************************************************************/
7697 : /**
7698 : * \brief Returns the measured geometry type corresponding to the passed
7699 : * geometry type.
7700 : *
7701 : * @param eType Input geometry type
7702 : *
7703 : * @return measured geometry type corresponding to the passed geometry type.
7704 : *
7705 : * @since GDAL 2.1
7706 : */
7707 :
7708 1929 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
7709 : {
7710 1929 : if (OGR_GT_HasM(eType) || eType == wkbNone)
7711 258 : return eType;
7712 1671 : if (eType & wkb25DBitInternalUse)
7713 : {
7714 700 : eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
7715 700 : eType = static_cast<OGRwkbGeometryType>(eType + 1000);
7716 : }
7717 1671 : return static_cast<OGRwkbGeometryType>(eType + 2000);
7718 : }
7719 :
7720 : /************************************************************************/
7721 : /* OGR_GT_SetModifier() */
7722 : /************************************************************************/
7723 : /**
7724 : * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
7725 : *
7726 : * @param eType Input geometry type
7727 : * @param bHasZ TRUE if the output geometry type must be 3D.
7728 : * @param bHasM TRUE if the output geometry type must be measured.
7729 : *
7730 : * @return Output geometry type.
7731 : *
7732 : * @since GDAL 2.0
7733 : */
7734 :
7735 5034 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
7736 : int bHasM)
7737 : {
7738 5034 : if (bHasZ && bHasM)
7739 342 : return OGR_GT_SetM(OGR_GT_SetZ(eType));
7740 4692 : else if (bHasM)
7741 331 : return OGR_GT_SetM(wkbFlatten(eType));
7742 4361 : else if (bHasZ)
7743 1758 : return OGR_GT_SetZ(wkbFlatten(eType));
7744 : else
7745 2603 : return wkbFlatten(eType);
7746 : }
7747 :
7748 : /************************************************************************/
7749 : /* OGR_GT_IsSubClassOf) */
7750 : /************************************************************************/
7751 : /**
7752 : * \brief Returns if a type is a subclass of another one
7753 : *
7754 : * @param eType Type.
7755 : * @param eSuperType Super type
7756 : *
7757 : * @return TRUE if eType is a subclass of eSuperType.
7758 : *
7759 : * @since GDAL 2.0
7760 : */
7761 :
7762 112266 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
7763 : {
7764 112266 : eSuperType = wkbFlatten(eSuperType);
7765 112266 : eType = wkbFlatten(eType);
7766 :
7767 112266 : if (eSuperType == eType || eSuperType == wkbUnknown)
7768 9190 : return TRUE;
7769 :
7770 103076 : if (eSuperType == wkbGeometryCollection)
7771 28595 : return eType == wkbMultiPoint || eType == wkbMultiLineString ||
7772 58642 : eType == wkbMultiPolygon || eType == wkbMultiCurve ||
7773 30047 : eType == wkbMultiSurface;
7774 :
7775 73029 : if (eSuperType == wkbCurvePolygon)
7776 19717 : return eType == wkbPolygon || eType == wkbTriangle;
7777 :
7778 53312 : if (eSuperType == wkbMultiCurve)
7779 244 : return eType == wkbMultiLineString;
7780 :
7781 53068 : if (eSuperType == wkbMultiSurface)
7782 283 : return eType == wkbMultiPolygon;
7783 :
7784 52785 : if (eSuperType == wkbCurve)
7785 22008 : return eType == wkbLineString || eType == wkbCircularString ||
7786 22008 : eType == wkbCompoundCurve;
7787 :
7788 30777 : if (eSuperType == wkbSurface)
7789 3457 : return eType == wkbCurvePolygon || eType == wkbPolygon ||
7790 7052 : eType == wkbTriangle || eType == wkbPolyhedralSurface ||
7791 3595 : eType == wkbTIN;
7792 :
7793 27182 : if (eSuperType == wkbPolygon)
7794 216 : return eType == wkbTriangle;
7795 :
7796 26966 : if (eSuperType == wkbPolyhedralSurface)
7797 13867 : return eType == wkbTIN;
7798 :
7799 13099 : return FALSE;
7800 : }
7801 :
7802 : /************************************************************************/
7803 : /* OGR_GT_GetCollection() */
7804 : /************************************************************************/
7805 : /**
7806 : * \brief Returns the collection type that can contain the passed geometry type
7807 : *
7808 : * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
7809 : * wkbLineString->wkbMultiLineString,
7810 : * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
7811 : * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
7812 : * wkbCurvePolygon->wkbMultiSurface.
7813 : * In other cases, wkbUnknown is returned
7814 : *
7815 : * Passed Z, M, ZM flag is preserved.
7816 : *
7817 : *
7818 : * @param eType Input geometry type
7819 : *
7820 : * @return the collection type that can contain the passed geometry type or
7821 : * wkbUnknown
7822 : *
7823 : * @since GDAL 2.0
7824 : */
7825 :
7826 2561 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
7827 : {
7828 2561 : const bool bHasZ = wkbHasZ(eType);
7829 2561 : const bool bHasM = wkbHasM(eType);
7830 2561 : if (eType == wkbNone)
7831 1 : return wkbNone;
7832 2560 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
7833 2560 : if (eFGType == wkbPoint)
7834 47 : eType = wkbMultiPoint;
7835 :
7836 2513 : else if (eFGType == wkbLineString)
7837 174 : eType = wkbMultiLineString;
7838 :
7839 2339 : else if (eFGType == wkbPolygon)
7840 931 : eType = wkbMultiPolygon;
7841 :
7842 1408 : else if (eFGType == wkbTriangle)
7843 7 : eType = wkbTIN;
7844 :
7845 1401 : else if (OGR_GT_IsCurve(eFGType))
7846 166 : eType = wkbMultiCurve;
7847 :
7848 1235 : else if (OGR_GT_IsSurface(eFGType))
7849 953 : eType = wkbMultiSurface;
7850 :
7851 : else
7852 282 : return wkbUnknown;
7853 :
7854 2278 : if (bHasZ)
7855 3 : eType = wkbSetZ(eType);
7856 2278 : if (bHasM)
7857 2 : eType = wkbSetM(eType);
7858 :
7859 2278 : return eType;
7860 : }
7861 :
7862 : /************************************************************************/
7863 : /* OGR_GT_GetSingle() */
7864 : /************************************************************************/
7865 : /**
7866 : * \brief Returns the non-collection type that be contained in the passed
7867 : * geometry type.
7868 : *
7869 : * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
7870 : * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
7871 : * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
7872 : * wkbGeometryCollection -> wkbUnknown
7873 : * In other cases, the original geometry is returned.
7874 : *
7875 : * Passed Z, M, ZM flag is preserved.
7876 : *
7877 : *
7878 : * @param eType Input geometry type
7879 : *
7880 : * @return the the non-collection type that be contained in the passed geometry
7881 : * type or wkbUnknown
7882 : *
7883 : * @since GDAL 3.11
7884 : */
7885 :
7886 43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
7887 : {
7888 43 : const bool bHasZ = wkbHasZ(eType);
7889 43 : const bool bHasM = wkbHasM(eType);
7890 43 : if (eType == wkbNone)
7891 1 : return wkbNone;
7892 42 : const OGRwkbGeometryType eFGType = wkbFlatten(eType);
7893 42 : if (eFGType == wkbMultiPoint)
7894 8 : eType = wkbPoint;
7895 :
7896 34 : else if (eFGType == wkbMultiLineString)
7897 4 : eType = wkbLineString;
7898 :
7899 30 : else if (eFGType == wkbMultiPolygon)
7900 2 : eType = wkbPolygon;
7901 :
7902 28 : else if (eFGType == wkbMultiCurve)
7903 2 : eType = wkbCompoundCurve;
7904 :
7905 26 : else if (eFGType == wkbMultiSurface)
7906 2 : eType = wkbCurvePolygon;
7907 :
7908 24 : else if (eFGType == wkbGeometryCollection)
7909 1 : return wkbUnknown;
7910 :
7911 41 : if (bHasZ)
7912 3 : eType = wkbSetZ(eType);
7913 41 : if (bHasM)
7914 2 : eType = wkbSetM(eType);
7915 :
7916 41 : return eType;
7917 : }
7918 :
7919 : /************************************************************************/
7920 : /* OGR_GT_GetCurve() */
7921 : /************************************************************************/
7922 : /**
7923 : * \brief Returns the curve geometry type that can contain the passed geometry
7924 : * type
7925 : *
7926 : * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
7927 : * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
7928 : * and wkbMultiLineString->wkbMultiCurve.
7929 : * In other cases, the passed geometry is returned.
7930 : *
7931 : * Passed Z, M, ZM flag is preserved.
7932 : *
7933 : * @param eType Input geometry type
7934 : *
7935 : * @return the curve type that can contain the passed geometry type
7936 : *
7937 : * @since GDAL 2.0
7938 : */
7939 :
7940 35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
7941 : {
7942 35 : const bool bHasZ = wkbHasZ(eType);
7943 35 : const bool bHasM = wkbHasM(eType);
7944 35 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
7945 :
7946 35 : if (eFGType == wkbLineString)
7947 3 : eType = wkbCompoundCurve;
7948 :
7949 32 : else if (eFGType == wkbPolygon)
7950 1 : eType = wkbCurvePolygon;
7951 :
7952 31 : else if (eFGType == wkbTriangle)
7953 0 : eType = wkbCurvePolygon;
7954 :
7955 31 : else if (eFGType == wkbMultiLineString)
7956 6 : eType = wkbMultiCurve;
7957 :
7958 25 : else if (eFGType == wkbMultiPolygon)
7959 4 : eType = wkbMultiSurface;
7960 :
7961 35 : if (bHasZ)
7962 4 : eType = wkbSetZ(eType);
7963 35 : if (bHasM)
7964 4 : eType = wkbSetM(eType);
7965 :
7966 35 : return eType;
7967 : }
7968 :
7969 : /************************************************************************/
7970 : /* OGR_GT_GetLinear() */
7971 : /************************************************************************/
7972 : /**
7973 : * \brief Returns the non-curve geometry type that can contain the passed
7974 : * geometry type
7975 : *
7976 : * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
7977 : * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
7978 : * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
7979 : * In other cases, the passed geometry is returned.
7980 : *
7981 : * Passed Z, M, ZM flag is preserved.
7982 : *
7983 : * @param eType Input geometry type
7984 : *
7985 : * @return the non-curve type that can contain the passed geometry type
7986 : *
7987 : * @since GDAL 2.0
7988 : */
7989 :
7990 740 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
7991 : {
7992 740 : const bool bHasZ = wkbHasZ(eType);
7993 740 : const bool bHasM = wkbHasM(eType);
7994 740 : OGRwkbGeometryType eFGType = wkbFlatten(eType);
7995 :
7996 740 : if (OGR_GT_IsCurve(eFGType))
7997 37 : eType = wkbLineString;
7998 :
7999 703 : else if (OGR_GT_IsSurface(eFGType))
8000 38 : eType = wkbPolygon;
8001 :
8002 665 : else if (eFGType == wkbMultiCurve)
8003 177 : eType = wkbMultiLineString;
8004 :
8005 488 : else if (eFGType == wkbMultiSurface)
8006 156 : eType = wkbMultiPolygon;
8007 :
8008 740 : if (bHasZ)
8009 139 : eType = wkbSetZ(eType);
8010 740 : if (bHasM)
8011 86 : eType = wkbSetM(eType);
8012 :
8013 740 : return eType;
8014 : }
8015 :
8016 : /************************************************************************/
8017 : /* OGR_GT_IsCurve() */
8018 : /************************************************************************/
8019 :
8020 : /**
8021 : * \brief Return if a geometry type is an instance of Curve
8022 : *
8023 : * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8024 : * and their Z/M/ZM variant.
8025 : *
8026 : * @param eGeomType the geometry type
8027 : * @return TRUE if the geometry type is an instance of Curve
8028 : *
8029 : * @since GDAL 2.0
8030 : */
8031 :
8032 21999 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8033 : {
8034 21999 : return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8035 : }
8036 :
8037 : /************************************************************************/
8038 : /* OGR_GT_IsSurface() */
8039 : /************************************************************************/
8040 :
8041 : /**
8042 : * \brief Return if a geometry type is an instance of Surface
8043 : *
8044 : * Such geometry type are wkbCurvePolygon and wkbPolygon
8045 : * and their Z/M/ZM variant.
8046 : *
8047 : * @param eGeomType the geometry type
8048 : * @return TRUE if the geometry type is an instance of Surface
8049 : *
8050 : * @since GDAL 2.0
8051 : */
8052 :
8053 3589 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8054 : {
8055 3589 : return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8056 : }
8057 :
8058 : /************************************************************************/
8059 : /* OGR_GT_IsNonLinear() */
8060 : /************************************************************************/
8061 :
8062 : /**
8063 : * \brief Return if a geometry type is a non-linear geometry type.
8064 : *
8065 : * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8066 : * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8067 : * Z/M variants.
8068 : *
8069 : * @param eGeomType the geometry type
8070 : * @return TRUE if the geometry type is a non-linear geometry type.
8071 : *
8072 : * @since GDAL 2.0
8073 : */
8074 :
8075 108852 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8076 : {
8077 108852 : OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8078 108844 : return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8079 108790 : eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8080 217696 : eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8081 108852 : eFGeomType == wkbMultiSurface;
8082 : }
8083 :
8084 : /************************************************************************/
8085 : /* CastToError() */
8086 : /************************************************************************/
8087 :
8088 : //! @cond Doxygen_Suppress
8089 0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8090 : {
8091 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8092 0 : poGeom->getGeometryName());
8093 0 : delete poGeom;
8094 0 : return nullptr;
8095 : }
8096 :
8097 : //! @endcond
8098 :
8099 : /************************************************************************/
8100 : /* OGRexportToSFCGAL() */
8101 : /************************************************************************/
8102 :
8103 : //! @cond Doxygen_Suppress
8104 : sfcgal_geometry_t *
8105 0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8106 : {
8107 : #ifdef HAVE_SFCGAL
8108 :
8109 : sfcgal_init();
8110 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8111 :
8112 : const auto exportToSFCGALViaWKB =
8113 : [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8114 : {
8115 : if (!geom)
8116 : return nullptr;
8117 :
8118 : // Get WKB size and allocate buffer
8119 : size_t nSize = geom->WkbSize();
8120 : unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8121 :
8122 : // Set export options with NDR byte order
8123 : OGRwkbExportOptions oOptions;
8124 : oOptions.eByteOrder = wkbNDR;
8125 : // and ISO to avoid wkb25DBit for Z geometries
8126 : oOptions.eWkbVariant = wkbVariantIso;
8127 :
8128 : // Export to WKB
8129 : sfcgal_geometry_t *sfcgalGeom = nullptr;
8130 : if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8131 : {
8132 : sfcgalGeom = sfcgal_io_read_wkb(
8133 : reinterpret_cast<const char *>(pabyWkb), nSize);
8134 : }
8135 :
8136 : CPLFree(pabyWkb);
8137 : return sfcgalGeom;
8138 : };
8139 :
8140 : // Handle special cases
8141 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8142 : {
8143 : std::unique_ptr<OGRLineString> poLS(
8144 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8145 : return exportToSFCGALViaWKB(poLS.get());
8146 : }
8147 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8148 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8149 : {
8150 : std::unique_ptr<OGRLineString> poLS(
8151 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8152 : ->toLineString());
8153 : return exportToSFCGALViaWKB(poLS.get());
8154 : }
8155 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8156 : {
8157 : std::unique_ptr<OGRPolygon> poPolygon(
8158 : OGRGeometryFactory::forceToPolygon(
8159 : poGeom->clone()->toCurvePolygon())
8160 : ->toPolygon());
8161 : return exportToSFCGALViaWKB(poPolygon.get());
8162 : }
8163 : else
8164 : {
8165 : // Default case - direct export
8166 : return exportToSFCGALViaWKB(poGeom);
8167 : }
8168 : #else
8169 : char *buffer = nullptr;
8170 :
8171 : // special cases - LinearRing, Circular String, Compound Curve, Curve
8172 : // Polygon
8173 :
8174 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8175 : {
8176 : // cast it to LineString and get the WKT
8177 : std::unique_ptr<OGRLineString> poLS(
8178 : OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8179 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8180 : {
8181 : sfcgal_geometry_t *_geometry =
8182 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8183 : CPLFree(buffer);
8184 : return _geometry;
8185 : }
8186 : else
8187 : {
8188 : CPLFree(buffer);
8189 : return nullptr;
8190 : }
8191 : }
8192 : else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8193 : EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8194 : {
8195 : // convert it to LineString and get the WKT
8196 : std::unique_ptr<OGRLineString> poLS(
8197 : OGRGeometryFactory::forceToLineString(poGeom->clone())
8198 : ->toLineString());
8199 : if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8200 : {
8201 : sfcgal_geometry_t *_geometry =
8202 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8203 : CPLFree(buffer);
8204 : return _geometry;
8205 : }
8206 : else
8207 : {
8208 : CPLFree(buffer);
8209 : return nullptr;
8210 : }
8211 : }
8212 : else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8213 : {
8214 : // convert it to Polygon and get the WKT
8215 : std::unique_ptr<OGRPolygon> poPolygon(
8216 : OGRGeometryFactory::forceToPolygon(
8217 : poGeom->clone()->toCurvePolygon())
8218 : ->toPolygon());
8219 : if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8220 : {
8221 : sfcgal_geometry_t *_geometry =
8222 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8223 : CPLFree(buffer);
8224 : return _geometry;
8225 : }
8226 : else
8227 : {
8228 : CPLFree(buffer);
8229 : return nullptr;
8230 : }
8231 : }
8232 : else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8233 : {
8234 : sfcgal_geometry_t *_geometry =
8235 : sfcgal_io_read_wkt(buffer, strlen(buffer));
8236 : CPLFree(buffer);
8237 : return _geometry;
8238 : }
8239 : else
8240 : {
8241 : CPLFree(buffer);
8242 : return nullptr;
8243 : }
8244 : #endif
8245 : #else
8246 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8247 0 : return nullptr;
8248 : #endif
8249 : }
8250 :
8251 : //! @endcond
8252 :
8253 : /************************************************************************/
8254 : /* SFCGALexportToOGR() */
8255 : /************************************************************************/
8256 :
8257 : //! @cond Doxygen_Suppress
8258 0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8259 : UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8260 : {
8261 : #ifdef HAVE_SFCGAL
8262 : if (geometry == nullptr)
8263 : return nullptr;
8264 :
8265 : sfcgal_init();
8266 : char *pabySFCGAL = nullptr;
8267 : size_t nLength = 0;
8268 : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8269 :
8270 : sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8271 :
8272 : if (pabySFCGAL == nullptr || nLength == 0)
8273 : return nullptr;
8274 :
8275 : OGRGeometry *poGeom = nullptr;
8276 : OGRErr eErr = OGRGeometryFactory::createFromWkb(
8277 : reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8278 : nLength);
8279 :
8280 : free(pabySFCGAL);
8281 :
8282 : if (eErr == OGRERR_NONE)
8283 : {
8284 : return poGeom;
8285 : }
8286 : else
8287 : {
8288 : return nullptr;
8289 : }
8290 : #else
8291 : sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8292 : char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8293 : memcpy(pszWKT, pabySFCGAL, nLength);
8294 : pszWKT[nLength] = 0;
8295 : free(pabySFCGAL);
8296 :
8297 : sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8298 :
8299 : OGRGeometry *poGeom = nullptr;
8300 : if (geom_type == SFCGAL_TYPE_POINT)
8301 : {
8302 : poGeom = new OGRPoint();
8303 : }
8304 : else if (geom_type == SFCGAL_TYPE_LINESTRING)
8305 : {
8306 : poGeom = new OGRLineString();
8307 : }
8308 : else if (geom_type == SFCGAL_TYPE_POLYGON)
8309 : {
8310 : poGeom = new OGRPolygon();
8311 : }
8312 : else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8313 : {
8314 : poGeom = new OGRMultiPoint();
8315 : }
8316 : else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8317 : {
8318 : poGeom = new OGRMultiLineString();
8319 : }
8320 : else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8321 : {
8322 : poGeom = new OGRMultiPolygon();
8323 : }
8324 : else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8325 : {
8326 : poGeom = new OGRGeometryCollection();
8327 : }
8328 : else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8329 : {
8330 : poGeom = new OGRTriangle();
8331 : }
8332 : else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8333 : {
8334 : poGeom = new OGRPolyhedralSurface();
8335 : }
8336 : else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8337 : {
8338 : poGeom = new OGRTriangulatedSurface();
8339 : }
8340 : else
8341 : {
8342 : CPLFree(pszWKT);
8343 : return nullptr;
8344 : }
8345 :
8346 : const char *pszWKTTmp = pszWKT;
8347 : if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8348 : {
8349 : CPLFree(pszWKT);
8350 : return poGeom;
8351 : }
8352 : else
8353 : {
8354 : delete poGeom;
8355 : CPLFree(pszWKT);
8356 : return nullptr;
8357 : }
8358 : #endif
8359 : #else
8360 0 : CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8361 0 : return nullptr;
8362 : #endif
8363 : }
8364 :
8365 : //! @endcond
8366 :
8367 : //! @cond Doxygen_Suppress
8368 7606 : OGRBoolean OGRGeometry::IsSFCGALCompatible() const
8369 : {
8370 7606 : const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8371 7552 : if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8372 : eGType == wkbTIN)
8373 : {
8374 2 : return TRUE;
8375 : }
8376 7550 : if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8377 : {
8378 52 : const OGRGeometryCollection *poGC = toGeometryCollection();
8379 9 : bool bIsSFCGALCompatible = false;
8380 9 : for (auto &&poSubGeom : *poGC)
8381 : {
8382 : OGRwkbGeometryType eSubGeomType =
8383 9 : wkbFlatten(poSubGeom->getGeometryType());
8384 9 : if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8385 : {
8386 0 : bIsSFCGALCompatible = true;
8387 : }
8388 9 : else if (eSubGeomType != wkbMultiPolygon)
8389 : {
8390 9 : bIsSFCGALCompatible = false;
8391 9 : break;
8392 : }
8393 : }
8394 9 : return bIsSFCGALCompatible;
8395 : }
8396 7498 : return FALSE;
8397 : }
8398 :
8399 : //! @endcond
8400 :
8401 : /************************************************************************/
8402 : /* roundCoordinatesIEEE754() */
8403 : /************************************************************************/
8404 :
8405 : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8406 : * double-precision binary representation.
8407 : *
8408 : * Determines the number of bits (N) required to represent a coordinate value
8409 : * with a specified number of digits after the decimal point, and then sets all
8410 : * but the N most significant bits to zero. The resulting coordinate value will
8411 : * still round to the original value (e.g. after roundCoordinates()), but will
8412 : * have improved compressiblity.
8413 : *
8414 : * @param options Contains the precision requirements.
8415 : * @since GDAL 3.9
8416 : */
8417 1 : void OGRGeometry::roundCoordinatesIEEE754(
8418 : const OGRGeomCoordinateBinaryPrecision &options)
8419 : {
8420 : struct Quantizer : public OGRDefaultGeometryVisitor
8421 : {
8422 : const OGRGeomCoordinateBinaryPrecision &m_options;
8423 :
8424 1 : explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8425 1 : : m_options(optionsIn)
8426 : {
8427 1 : }
8428 :
8429 : using OGRDefaultGeometryVisitor::visit;
8430 :
8431 3 : void visit(OGRPoint *poPoint) override
8432 : {
8433 3 : if (m_options.nXYBitPrecision != INT_MIN)
8434 : {
8435 : uint64_t i;
8436 : double d;
8437 3 : d = poPoint->getX();
8438 3 : memcpy(&i, &d, sizeof(i));
8439 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8440 3 : memcpy(&d, &i, sizeof(i));
8441 3 : poPoint->setX(d);
8442 3 : d = poPoint->getY();
8443 3 : memcpy(&i, &d, sizeof(i));
8444 3 : i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8445 3 : memcpy(&d, &i, sizeof(i));
8446 3 : poPoint->setY(d);
8447 : }
8448 3 : if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8449 : {
8450 : uint64_t i;
8451 : double d;
8452 3 : d = poPoint->getZ();
8453 3 : memcpy(&i, &d, sizeof(i));
8454 3 : i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8455 3 : memcpy(&d, &i, sizeof(i));
8456 3 : poPoint->setZ(d);
8457 : }
8458 3 : if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8459 : {
8460 : uint64_t i;
8461 : double d;
8462 3 : d = poPoint->getM();
8463 3 : memcpy(&i, &d, sizeof(i));
8464 3 : i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8465 3 : memcpy(&d, &i, sizeof(i));
8466 3 : poPoint->setM(d);
8467 : }
8468 3 : }
8469 : };
8470 :
8471 2 : Quantizer quantizer(options);
8472 1 : accept(&quantizer);
8473 1 : }
8474 :
8475 : /************************************************************************/
8476 : /* visit() */
8477 : /************************************************************************/
8478 :
8479 105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8480 : {
8481 1248 : for (auto &&oPoint : *poGeom)
8482 : {
8483 1143 : oPoint.accept(this);
8484 : }
8485 105 : }
8486 :
8487 104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8488 : {
8489 104 : _visit(poGeom);
8490 104 : }
8491 :
8492 80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8493 : {
8494 80 : visit(poGeom->toUpperClass());
8495 80 : }
8496 :
8497 1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8498 : {
8499 1 : _visit(poGeom);
8500 1 : }
8501 :
8502 78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8503 : {
8504 159 : for (auto &&poSubGeom : *poGeom)
8505 81 : poSubGeom->accept(this);
8506 78 : }
8507 :
8508 77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8509 : {
8510 77 : visit(poGeom->toUpperClass());
8511 77 : }
8512 :
8513 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8514 : {
8515 1 : visit(poGeom->toUpperClass());
8516 1 : }
8517 :
8518 8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8519 : {
8520 8 : visit(poGeom->toUpperClass());
8521 8 : }
8522 :
8523 14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8524 : {
8525 14 : visit(poGeom->toUpperClass());
8526 14 : }
8527 :
8528 26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
8529 : {
8530 75 : for (auto &&poSubGeom : *poGeom)
8531 49 : poSubGeom->accept(this);
8532 26 : }
8533 :
8534 1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
8535 : {
8536 2 : for (auto &&poSubGeom : *poGeom)
8537 1 : poSubGeom->accept(this);
8538 1 : }
8539 :
8540 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
8541 : {
8542 1 : visit(poGeom->toUpperClass());
8543 1 : }
8544 :
8545 1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
8546 : {
8547 1 : visit(poGeom->toUpperClass());
8548 1 : }
8549 :
8550 2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
8551 : {
8552 2 : visit(poGeom->toUpperClass());
8553 2 : }
8554 :
8555 2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
8556 : {
8557 4 : for (auto &&poSubGeom : *poGeom)
8558 2 : poSubGeom->accept(this);
8559 2 : }
8560 :
8561 1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
8562 : {
8563 1 : visit(poGeom->toUpperClass());
8564 1 : }
8565 :
8566 116 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
8567 : {
8568 2727 : for (auto &&oPoint : *poGeom)
8569 : {
8570 2611 : oPoint.accept(this);
8571 : }
8572 116 : }
8573 :
8574 110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
8575 : {
8576 110 : _visit(poGeom);
8577 110 : }
8578 :
8579 99 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
8580 : {
8581 99 : visit(poGeom->toUpperClass());
8582 99 : }
8583 :
8584 6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
8585 : {
8586 6 : _visit(poGeom);
8587 6 : }
8588 :
8589 101 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
8590 : {
8591 203 : for (auto &&poSubGeom : *poGeom)
8592 102 : poSubGeom->accept(this);
8593 101 : }
8594 :
8595 98 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
8596 : {
8597 98 : visit(poGeom->toUpperClass());
8598 98 : }
8599 :
8600 40 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
8601 : {
8602 40 : visit(poGeom->toUpperClass());
8603 40 : }
8604 :
8605 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
8606 : {
8607 1 : visit(poGeom->toUpperClass());
8608 1 : }
8609 :
8610 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
8611 : {
8612 1 : visit(poGeom->toUpperClass());
8613 1 : }
8614 :
8615 45 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
8616 : {
8617 217 : for (auto &&poSubGeom : *poGeom)
8618 172 : poSubGeom->accept(this);
8619 45 : }
8620 :
8621 3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
8622 : {
8623 14 : for (auto &&poSubGeom : *poGeom)
8624 11 : poSubGeom->accept(this);
8625 3 : }
8626 :
8627 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
8628 : {
8629 1 : visit(poGeom->toUpperClass());
8630 1 : }
8631 :
8632 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
8633 : {
8634 1 : visit(poGeom->toUpperClass());
8635 1 : }
8636 :
8637 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
8638 : {
8639 2 : visit(poGeom->toUpperClass());
8640 2 : }
8641 :
8642 2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
8643 : {
8644 4 : for (auto &&poSubGeom : *poGeom)
8645 2 : poSubGeom->accept(this);
8646 2 : }
8647 :
8648 1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
8649 : {
8650 1 : visit(poGeom->toUpperClass());
8651 1 : }
8652 :
8653 : /************************************************************************/
8654 : /* OGRGeometryUniquePtrDeleter */
8655 : /************************************************************************/
8656 :
8657 : //! @cond Doxygen_Suppress
8658 1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
8659 : {
8660 1333 : delete poGeom;
8661 1333 : }
8662 :
8663 : //! @endcond
8664 :
8665 : /************************************************************************/
8666 : /* OGRPreparedGeometryUniquePtrDeleter */
8667 : /************************************************************************/
8668 :
8669 : //! @cond Doxygen_Suppress
8670 145 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
8671 : OGRPreparedGeometry *poPreparedGeom) const
8672 : {
8673 145 : OGRDestroyPreparedGeometry(poPreparedGeom);
8674 145 : }
8675 :
8676 : //! @endcond
8677 :
8678 : /************************************************************************/
8679 : /* HomogenizeDimensionalityWith() */
8680 : /************************************************************************/
8681 :
8682 : //! @cond Doxygen_Suppress
8683 3150240 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
8684 : {
8685 3150240 : if (poOtherGeom->Is3D() && !Is3D())
8686 1293490 : set3D(TRUE);
8687 :
8688 3150220 : if (poOtherGeom->IsMeasured() && !IsMeasured())
8689 793 : setMeasured(TRUE);
8690 :
8691 3150220 : if (!poOtherGeom->Is3D() && Is3D())
8692 295 : poOtherGeom->set3D(TRUE);
8693 :
8694 3150220 : if (!poOtherGeom->IsMeasured() && IsMeasured())
8695 28 : poOtherGeom->setMeasured(TRUE);
8696 3150220 : }
8697 :
8698 : //! @endcond
8699 :
8700 : /************************************************************************/
8701 : /* OGRGeomCoordinateBinaryPrecision::SetFrom() */
8702 : /************************************************************************/
8703 :
8704 : /** Set binary precision options from resolution.
8705 : *
8706 : * @since GDAL 3.9
8707 : */
8708 16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
8709 : const OGRGeomCoordinatePrecision &prec)
8710 : {
8711 16 : if (prec.dfXYResolution != 0)
8712 : {
8713 16 : nXYBitPrecision =
8714 16 : static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
8715 : }
8716 16 : if (prec.dfZResolution != 0)
8717 : {
8718 12 : nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
8719 : }
8720 16 : if (prec.dfMResolution != 0)
8721 : {
8722 12 : nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
8723 : }
8724 16 : }
8725 :
8726 : /************************************************************************/
8727 : /* OGRwkbExportOptionsCreate() */
8728 : /************************************************************************/
8729 :
8730 : /**
8731 : * \brief Create geometry WKB export options.
8732 : *
8733 : * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
8734 : *
8735 : * @return object to be freed with OGRwkbExportOptionsDestroy().
8736 : * @since GDAL 3.9
8737 : */
8738 2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
8739 : {
8740 2 : return new OGRwkbExportOptions;
8741 : }
8742 :
8743 : /************************************************************************/
8744 : /* OGRwkbExportOptionsDestroy() */
8745 : /************************************************************************/
8746 :
8747 : /**
8748 : * \brief Destroy object returned by OGRwkbExportOptionsCreate()
8749 : *
8750 : * @param psOptions WKB export options
8751 : * @since GDAL 3.9
8752 : */
8753 :
8754 2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
8755 : {
8756 2 : delete psOptions;
8757 2 : }
8758 :
8759 : /************************************************************************/
8760 : /* OGRwkbExportOptionsSetByteOrder() */
8761 : /************************************************************************/
8762 :
8763 : /**
8764 : * \brief Set the WKB byte order.
8765 : *
8766 : * @param psOptions WKB export options
8767 : * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
8768 : * Intel)
8769 : * @since GDAL 3.9
8770 : */
8771 :
8772 1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
8773 : OGRwkbByteOrder eByteOrder)
8774 : {
8775 1 : psOptions->eByteOrder = eByteOrder;
8776 1 : }
8777 :
8778 : /************************************************************************/
8779 : /* OGRwkbExportOptionsSetVariant() */
8780 : /************************************************************************/
8781 :
8782 : /**
8783 : * \brief Set the WKB variant
8784 : *
8785 : * @param psOptions WKB export options
8786 : * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
8787 : * wkbVariantPostGIS1
8788 : * @since GDAL 3.9
8789 : */
8790 :
8791 1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
8792 : OGRwkbVariant eWkbVariant)
8793 : {
8794 1 : psOptions->eWkbVariant = eWkbVariant;
8795 1 : }
8796 :
8797 : /************************************************************************/
8798 : /* OGRwkbExportOptionsSetPrecision() */
8799 : /************************************************************************/
8800 :
8801 : /**
8802 : * \brief Set precision options
8803 : *
8804 : * @param psOptions WKB export options
8805 : * @param hPrecisionOptions Precision options (might be null to reset them)
8806 : * @since GDAL 3.9
8807 : */
8808 :
8809 1 : void OGRwkbExportOptionsSetPrecision(
8810 : OGRwkbExportOptions *psOptions,
8811 : OGRGeomCoordinatePrecisionH hPrecisionOptions)
8812 : {
8813 1 : psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
8814 1 : if (hPrecisionOptions)
8815 1 : psOptions->sPrecision.SetFrom(*hPrecisionOptions);
8816 1 : }
8817 :
8818 : /************************************************************************/
8819 : /* IsRectangle() */
8820 : /************************************************************************/
8821 :
8822 : /**
8823 : * \brief Returns whether the geometry is a polygon with 4 corners forming
8824 : * a rectangle.
8825 : *
8826 : * @since GDAL 3.10
8827 : */
8828 52348 : bool OGRGeometry::IsRectangle() const
8829 : {
8830 52348 : if (wkbFlatten(getGeometryType()) != wkbPolygon)
8831 265 : return false;
8832 :
8833 52083 : const OGRPolygon *poPoly = toPolygon();
8834 :
8835 52083 : if (poPoly->getNumInteriorRings() != 0)
8836 19 : return false;
8837 :
8838 52064 : const OGRLinearRing *poRing = poPoly->getExteriorRing();
8839 52064 : if (!poRing)
8840 4 : return false;
8841 :
8842 52060 : if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
8843 190 : return false;
8844 :
8845 : // If the ring has 5 points, the last should be the first.
8846 103699 : if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
8847 51829 : poRing->getY(0) != poRing->getY(4)))
8848 1 : return false;
8849 :
8850 : // Polygon with first segment in "y" direction.
8851 103058 : if (poRing->getX(0) == poRing->getX(1) &&
8852 102377 : poRing->getY(1) == poRing->getY(2) &&
8853 154246 : poRing->getX(2) == poRing->getX(3) &&
8854 51188 : poRing->getY(3) == poRing->getY(0))
8855 51188 : return true;
8856 :
8857 : // Polygon with first segment in "x" direction.
8858 1297 : if (poRing->getY(0) == poRing->getY(1) &&
8859 1232 : poRing->getX(1) == poRing->getX(2) &&
8860 1913 : poRing->getY(2) == poRing->getY(3) &&
8861 616 : poRing->getX(3) == poRing->getX(0))
8862 616 : return true;
8863 :
8864 65 : return false;
8865 : }
8866 :
8867 : /************************************************************************/
8868 : /* hasEmptyParts() */
8869 : /************************************************************************/
8870 :
8871 : /**
8872 : * \brief Returns whether a geometry has empty parts/rings.
8873 : *
8874 : * Returns true if removeEmptyParts() will modify the geometry.
8875 : *
8876 : * This is different from IsEmpty().
8877 : *
8878 : * @since GDAL 3.10
8879 : */
8880 20 : bool OGRGeometry::hasEmptyParts() const
8881 : {
8882 20 : return false;
8883 : }
8884 :
8885 : /************************************************************************/
8886 : /* removeEmptyParts() */
8887 : /************************************************************************/
8888 :
8889 : /**
8890 : * \brief Remove empty parts/rings from this geometry.
8891 : *
8892 : * @since GDAL 3.10
8893 : */
8894 17 : void OGRGeometry::removeEmptyParts()
8895 : {
8896 17 : }
8897 :
8898 : /************************************************************************/
8899 : /* ~IOGRGeometryVisitor() */
8900 : /************************************************************************/
8901 :
8902 : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
8903 :
8904 : /************************************************************************/
8905 : /* ~IOGRConstGeometryVisitor() */
8906 : /************************************************************************/
8907 :
8908 : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;
|