Coverage Report

Created: 2026-09-14 06:50

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