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