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