Coverage Report

Created: 2026-06-30 08:33

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
96.7k
{
84
96.7k
    return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
85
96.7k
}
86
87
bool OGRWktOptions::getDefaultRound()
88
96.7k
{
89
96.7k
    return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
90
96.7k
}
91
92
/************************************************************************/
93
/*                            OGRGeometry()                             */
94
/************************************************************************/
95
96
656M
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
47.7M
    : poSRS(other.poSRS), flags(other.flags)
108
47.7M
{
109
47.7M
    if (poSRS != nullptr)
110
394k
        const_cast<OGRSpatialReference *>(poSRS)->Reference();
111
47.7M
}
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
1.82M
    : poSRS(other.poSRS), flags(other.flags)
125
1.82M
{
126
1.82M
    other.poSRS = nullptr;
127
1.82M
}
128
129
/************************************************************************/
130
/*                            ~OGRGeometry()                            */
131
/************************************************************************/
132
133
OGRGeometry::~OGRGeometry()
134
135
706M
{
136
706M
    if (poSRS != nullptr)
137
1.58M
        const_cast<OGRSpatialReference *>(poSRS)->Release();
138
706M
}
139
140
/************************************************************************/
141
/*                    operator=( const OGRGeometry&)                    */
142
/************************************************************************/
143
144
/**
145
 * \brief Assignment operator.
146
 */
147
148
OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
149
5.02k
{
150
5.02k
    if (this != &other)
151
5.02k
    {
152
5.02k
        empty();
153
5.02k
        assignSpatialReference(other.getSpatialReference());
154
5.02k
        flags = other.flags;
155
5.02k
    }
156
5.02k
    return *this;
157
5.02k
}
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
393M
{
171
393M
    if (this != &other)
172
393M
    {
173
393M
        poSRS = other.poSRS;
174
393M
        other.poSRS = nullptr;
175
393M
        flags = other.flags;
176
393M
    }
177
393M
    return *this;
178
393M
}
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
12.7M
{
508
    // Do in that order to properly handle poSR == poSRS
509
12.7M
    if (poSR != nullptr)
510
1.60M
        const_cast<OGRSpatialReference *>(poSR)->Reference();
511
12.7M
    if (poSRS != nullptr)
512
410k
        const_cast<OGRSpatialReference *>(poSRS)->Release();
513
514
12.7M
    poSRS = poSR;
515
12.7M
}
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
    delete 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
1.23M
{
855
1.23M
    OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
856
857
1.23M
    if (flags & OGR_G_3D)
858
1.13M
        nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
859
1.23M
    if (flags & OGR_G_MEASURED)
860
1.08M
        nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
861
862
1.23M
    return nGType;
863
1.23M
}
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
4.63M
{
965
4.63M
    return (flags & OGR_G_3D) ? 3 : 2;
966
4.63M
}
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
15.4M
{
983
15.4M
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
984
14.2M
        return 4;
985
1.16M
    else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
986
695k
        return 3;
987
472k
    else
988
472k
        return 2;
989
15.4M
}
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
15.2k
{
1013
15.2k
    VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
1014
1015
15.2k
    return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
1016
15.2k
}
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
7.74k
{
1058
7.74k
    VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
1059
1060
7.74k
    return OGRGeometry::FromHandle(hGeom)->Is3D();
1061
7.74k
}
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
1.11M
{
1105
1.11M
    if (nNewDimension == 2)
1106
1.04M
        flags &= ~OGR_G_3D;
1107
76.5k
    else
1108
76.5k
        flags |= OGR_G_3D;
1109
1.11M
    return setMeasured(FALSE);
1110
1.11M
}
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
2.64M
{
1128
2.64M
    if (bIs3D)
1129
2.45M
        flags |= OGR_G_3D;
1130
187k
    else
1131
187k
        flags &= ~OGR_G_3D;
1132
2.64M
    return true;
1133
2.64M
}
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
3.12M
{
1152
3.12M
    if (bIsMeasured)
1153
1.35M
        flags |= OGR_G_MEASURED;
1154
1.76M
    else
1155
1.76M
        flags &= ~OGR_G_MEASURED;
1156
3.12M
    return true;
1157
3.12M
}
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
5.61k
{
1369
5.61k
    VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
1370
1371
5.61k
    const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
1372
5.61k
    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
5.61k
    return static_cast<int>(nSize);
1380
5.61k
}
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
26.4k
{
1436
26.4k
    VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
1437
1438
26.4k
    OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
1439
26.4k
}
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
36.9k
{
1600
36.9k
    OGRwkbExportOptions sOptions;
1601
36.9k
    sOptions.eByteOrder = eByteOrder;
1602
36.9k
    sOptions.eWkbVariant = eWkbVariant;
1603
36.9k
    return exportToWkb(pabyData, &sOptions);
1604
36.9k
}
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
5.61k
{
1638
5.61k
    VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
1639
1640
5.61k
    return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
1641
5.61k
}
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
5.61k
{
1674
5.61k
    VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
1675
1676
5.61k
    return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
1677
5.61k
                                                       wkbVariantIso);
1678
5.61k
}
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
1.08M
{
1796
1.08M
    const char *pszInput = *ppszInput;
1797
1798
    /* -------------------------------------------------------------------- */
1799
    /*      Clear existing Geoms.                                           */
1800
    /* -------------------------------------------------------------------- */
1801
1.08M
    empty();
1802
1.08M
    *pbIsEmpty = false;
1803
1804
    /* -------------------------------------------------------------------- */
1805
    /*      Read and verify the type keyword, and ensure it matches the     */
1806
    /*      actual type of this container.                                  */
1807
    /* -------------------------------------------------------------------- */
1808
1.08M
    bool bHasM = false;
1809
1.08M
    bool bHasZ = false;
1810
1.08M
    bool bAlreadyGotDimension = false;
1811
1812
1.08M
    char szToken[OGR_WKT_TOKEN_MAX] = {};
1813
1.08M
    pszInput = OGRWktReadToken(pszInput, szToken);
1814
1.08M
    if (szToken[0] != '\0')
1815
1.08M
    {
1816
        // Postgis EWKT: POINTM instead of POINT M.
1817
        // Current QGIS versions (at least <= 3.38) also export POINTZ.
1818
1.08M
        const size_t nTokenLen = strlen(szToken);
1819
1.08M
        if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
1820
87.4k
        {
1821
87.4k
            szToken[nTokenLen - 1] = '\0';
1822
87.4k
            bHasM = true;
1823
87.4k
            bAlreadyGotDimension = true;
1824
1825
87.4k
            if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
1826
85.8k
                                  szToken[nTokenLen - 2] == 'z'))
1827
2.94k
            {
1828
2.94k
                bHasZ = true;
1829
2.94k
                szToken[nTokenLen - 2] = '\0';
1830
2.94k
            }
1831
87.4k
        }
1832
1.00M
        else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
1833
9.93k
        {
1834
9.93k
            szToken[nTokenLen - 1] = '\0';
1835
9.93k
            bHasZ = true;
1836
9.93k
            bAlreadyGotDimension = true;
1837
9.93k
        }
1838
1.08M
    }
1839
1840
1.08M
    if (!EQUAL(szToken, getGeometryName()))
1841
23.4k
        return OGRERR_CORRUPT_DATA;
1842
1843
    /* -------------------------------------------------------------------- */
1844
    /*      Check for Z, M or ZM                                            */
1845
    /* -------------------------------------------------------------------- */
1846
1.06M
    if (!bAlreadyGotDimension)
1847
971k
    {
1848
971k
        const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1849
971k
        if (EQUAL(szToken, "Z"))
1850
21.4k
        {
1851
21.4k
            pszInput = pszNewInput;
1852
21.4k
            bHasZ = true;
1853
21.4k
        }
1854
950k
        else if (EQUAL(szToken, "M"))
1855
29.1k
        {
1856
29.1k
            pszInput = pszNewInput;
1857
29.1k
            bHasM = true;
1858
29.1k
        }
1859
921k
        else if (EQUAL(szToken, "ZM"))
1860
32.3k
        {
1861
32.3k
            pszInput = pszNewInput;
1862
32.3k
            bHasZ = true;
1863
32.3k
            bHasM = true;
1864
32.3k
        }
1865
971k
    }
1866
1.06M
    *pbHasZ = bHasZ;
1867
1.06M
    *pbHasM = bHasM;
1868
1869
    /* -------------------------------------------------------------------- */
1870
    /*      Check for EMPTY ...                                             */
1871
    /* -------------------------------------------------------------------- */
1872
1.06M
    const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1873
1.06M
    if (EQUAL(szToken, "EMPTY"))
1874
32.2k
    {
1875
32.2k
        *ppszInput = pszNewInput;
1876
32.2k
        *pbIsEmpty = true;
1877
32.2k
        if (bHasZ)
1878
11.3k
            set3D(TRUE);
1879
32.2k
        if (bHasM)
1880
8.67k
            setMeasured(TRUE);
1881
32.2k
        return OGRERR_NONE;
1882
32.2k
    }
1883
1884
1.03M
    if (!EQUAL(szToken, "("))
1885
60.4k
        return OGRERR_CORRUPT_DATA;
1886
1887
972k
    if (!bHasZ && !bHasM)
1888
825k
    {
1889
        // Test for old-style XXXXXXXXX(EMPTY).
1890
825k
        pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1891
825k
        if (EQUAL(szToken, "EMPTY"))
1892
4.86k
        {
1893
4.86k
            pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1894
1895
4.86k
            if (EQUAL(szToken, ","))
1896
1.59k
            {
1897
                // This is OK according to SFSQL SPEC.
1898
1.59k
            }
1899
3.26k
            else if (!EQUAL(szToken, ")"))
1900
818
            {
1901
818
                return OGRERR_CORRUPT_DATA;
1902
818
            }
1903
2.44k
            else
1904
2.44k
            {
1905
2.44k
                *ppszInput = pszNewInput;
1906
2.44k
                empty();
1907
2.44k
                *pbIsEmpty = true;
1908
2.44k
                return OGRERR_NONE;
1909
2.44k
            }
1910
4.86k
        }
1911
825k
    }
1912
1913
969k
    *ppszInput = pszInput;
1914
1915
969k
    return OGRERR_NONE;
1916
972k
}
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
1.52M
{
1932
1.52M
    std::string s(" ");
1933
1934
1.52M
    if (variant == wkbVariantIso)
1935
1.18M
    {
1936
1.18M
        if (flags & OGR_G_3D)
1937
1.13M
            s += "Z";
1938
1.18M
        if (flags & OGR_G_MEASURED)
1939
1.08M
            s += "M";
1940
1.18M
    }
1941
1.52M
    if (s.size() > 1)
1942
1.16M
        s += " ";
1943
1.52M
    return s;
1944
1.52M
}
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
24.1k
{
1970
24.1k
    OGRWktOptions opts;
1971
24.1k
    opts.variant = variant;
1972
24.1k
    OGRErr err(OGRERR_NONE);
1973
1974
24.1k
    std::string wkt = exportToWkt(opts, &err);
1975
24.1k
    *ppszDstText = CPLStrdup(wkt.data());
1976
24.1k
    return err;
1977
24.1k
}
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
5.61k
{
2006
5.61k
    VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
2007
2008
5.61k
    return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
2009
5.61k
}
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
5.61k
{
2038
5.61k
    VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
2039
2040
5.61k
    return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
2041
5.61k
                                                       wkbVariantIso);
2042
5.61k
}
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
159k
{
2077
159k
    VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2078
2079
159k
    return OGRGeometry::FromHandle(hGeom)->getGeometryType();
2080
159k
}
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
8
{
2287
8
    if (posReason)
2288
8
        posReason->clear();
2289
2290
8
    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
8
    {
2335
8
#ifndef HAVE_GEOS
2336
8
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2337
8
        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
8
    }
2419
8
}
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
62.9k
{
2648
62.9k
    OGRwkbGeometryType eType = wkbUnknown;
2649
62.9k
    bool bConvertTo3D = false;
2650
62.9k
    bool bIsMeasured = false;
2651
62.9k
    if (*pszGeomType != '\0')
2652
56.8k
    {
2653
56.8k
        char ch = pszGeomType[strlen(pszGeomType) - 1];
2654
56.8k
        if (ch == 'm' || ch == 'M')
2655
8.75k
        {
2656
8.75k
            bIsMeasured = true;
2657
8.75k
            if (strlen(pszGeomType) > 1)
2658
7.19k
                ch = pszGeomType[strlen(pszGeomType) - 2];
2659
8.75k
        }
2660
56.8k
        if (ch == 'z' || ch == 'Z')
2661
15.5k
        {
2662
15.5k
            bConvertTo3D = true;
2663
15.5k
        }
2664
56.8k
    }
2665
2666
62.9k
    if (STARTS_WITH_CI(pszGeomType, "POINT"))
2667
1.94k
        eType = wkbPoint;
2668
61.0k
    else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2669
35
        eType = wkbLineString;
2670
61.0k
    else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2671
1.75k
        eType = wkbPolygon;
2672
59.2k
    else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2673
5.16k
        eType = wkbMultiPoint;
2674
54.0k
    else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2675
293
        eType = wkbMultiLineString;
2676
53.7k
    else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2677
2.56k
        eType = wkbMultiPolygon;
2678
51.2k
    else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2679
634
        eType = wkbGeometryCollection;
2680
50.6k
    else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2681
9.28k
        eType = wkbCircularString;
2682
41.3k
    else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2683
0
        eType = wkbCompoundCurve;
2684
41.3k
    else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2685
1.45k
        eType = wkbCurvePolygon;
2686
39.8k
    else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2687
919
        eType = wkbMultiCurve;
2688
38.9k
    else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2689
460
        eType = wkbMultiSurface;
2690
38.4k
    else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2691
852
        eType = wkbTriangle;
2692
37.6k
    else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2693
517
        eType = wkbPolyhedralSurface;
2694
37.1k
    else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2695
3.59k
        eType = wkbTIN;
2696
33.5k
    else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2697
2.81k
        eType = wkbCurve;
2698
30.7k
    else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2699
471
        eType = wkbSurface;
2700
30.2k
    else
2701
30.2k
        eType = wkbUnknown;
2702
2703
62.9k
    if (bConvertTo3D)
2704
15.5k
        eType = wkbSetZ(eType);
2705
62.9k
    if (bIsMeasured)
2706
8.75k
        eType = wkbSetM(eType);
2707
2708
62.9k
    return eType;
2709
62.9k
}
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
7.00k
{
2728
7.00k
    const char *pszRet = "";
2729
7.00k
    switch (wkbFlatten(eGeomType))
2730
7.00k
    {
2731
4.11k
        case wkbUnknown:
2732
4.11k
            pszRet = "Geometry";
2733
4.11k
            break;
2734
229
        case wkbPoint:
2735
229
            pszRet = "Point";
2736
229
            break;
2737
10
        case wkbLineString:
2738
10
            pszRet = "LineString";
2739
10
            break;
2740
46
        case wkbPolygon:
2741
46
            pszRet = "Polygon";
2742
46
            break;
2743
72
        case wkbMultiPoint:
2744
72
            pszRet = "MultiPoint";
2745
72
            break;
2746
9
        case wkbMultiLineString:
2747
9
            pszRet = "MultiLineString";
2748
9
            break;
2749
401
        case wkbMultiPolygon:
2750
401
            pszRet = "MultiPolygon";
2751
401
            break;
2752
8
        case wkbGeometryCollection:
2753
8
            pszRet = "GeometryCollection";
2754
8
            break;
2755
115
        case wkbCircularString:
2756
115
            pszRet = "CircularString";
2757
115
            break;
2758
155
        case wkbCompoundCurve:
2759
155
            pszRet = "CompoundCurve";
2760
155
            break;
2761
159
        case wkbCurvePolygon:
2762
159
            pszRet = "CurvePolygon";
2763
159
            break;
2764
115
        case wkbMultiCurve:
2765
115
            pszRet = "MultiCurve";
2766
115
            break;
2767
22
        case wkbMultiSurface:
2768
22
            pszRet = "MultiSurface";
2769
22
            break;
2770
15
        case wkbTriangle:
2771
15
            pszRet = "Triangle";
2772
15
            break;
2773
92
        case wkbPolyhedralSurface:
2774
92
            pszRet = "PolyhedralSurface";
2775
92
            break;
2776
98
        case wkbTIN:
2777
98
            pszRet = "Tin";
2778
98
            break;
2779
406
        case wkbCurve:
2780
406
            pszRet = "Curve";
2781
406
            break;
2782
31
        case wkbSurface:
2783
31
            pszRet = "Surface";
2784
31
            break;
2785
901
        default:
2786
901
            break;
2787
7.00k
    }
2788
7.00k
    if (bAddZM)
2789
13
    {
2790
13
        const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2791
13
        const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2792
13
        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
13
    }
2802
7.00k
    if (!bCamelCase)
2803
6.98k
        pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2804
7.00k
    return pszRet;
2805
7.00k
}
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
11.8k
{
2826
11.8k
    bool b3D = wkbHasZ(eType);
2827
11.8k
    bool bMeasured = wkbHasM(eType);
2828
2829
11.8k
    switch (wkbFlatten(eType))
2830
11.8k
    {
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
571
        case wkbPoint:
2842
571
            if (b3D && bMeasured)
2843
408
                return "3D Measured Point";
2844
163
            else if (b3D)
2845
0
                return "3D Point";
2846
163
            else if (bMeasured)
2847
139
                return "Measured Point";
2848
24
            else
2849
24
                return "Point";
2850
2851
231
        case wkbLineString:
2852
231
            if (b3D && bMeasured)
2853
188
                return "3D Measured Line String";
2854
43
            else if (b3D)
2855
0
                return "3D Line String";
2856
43
            else if (bMeasured)
2857
42
                return "Measured Line String";
2858
1
            else
2859
1
                return "Line String";
2860
2861
433
        case wkbPolygon:
2862
433
            if (b3D && bMeasured)
2863
321
                return "3D Measured Polygon";
2864
112
            else if (b3D)
2865
0
                return "3D Polygon";
2866
112
            else if (bMeasured)
2867
110
                return "Measured Polygon";
2868
2
            else
2869
2
                return "Polygon";
2870
2871
9
        case wkbMultiPoint:
2872
9
            if (b3D && bMeasured)
2873
0
                return "3D Measured Multi Point";
2874
9
            else if (b3D)
2875
0
                return "3D Multi Point";
2876
9
            else if (bMeasured)
2877
0
                return "Measured Multi Point";
2878
9
            else
2879
9
                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
1
        case wkbMultiPolygon:
2892
1
            if (b3D && bMeasured)
2893
0
                return "3D Measured Multi Polygon";
2894
1
            else if (b3D)
2895
0
                return "3D Multi Polygon";
2896
1
            else if (bMeasured)
2897
0
                return "Measured Multi Polygon";
2898
1
            else
2899
1
                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
1.88k
        case wkbCircularString:
2912
1.88k
            if (b3D && bMeasured)
2913
758
                return "3D Measured Circular String";
2914
1.12k
            else if (b3D)
2915
442
                return "3D Circular String";
2916
683
            else if (bMeasured)
2917
122
                return "Measured Circular String";
2918
561
            else
2919
561
                return "Circular String";
2920
2921
2.94k
        case wkbCompoundCurve:
2922
2.94k
            if (b3D && bMeasured)
2923
301
                return "3D Measured Compound Curve";
2924
2.64k
            else if (b3D)
2925
136
                return "3D Compound Curve";
2926
2.50k
            else if (bMeasured)
2927
154
                return "Measured Compound Curve";
2928
2.35k
            else
2929
2.35k
                return "Compound Curve";
2930
2931
951
        case wkbCurvePolygon:
2932
951
            if (b3D && bMeasured)
2933
630
                return "3D Measured Curve Polygon";
2934
321
            else if (b3D)
2935
125
                return "3D Curve Polygon";
2936
196
            else if (bMeasured)
2937
135
                return "Measured Curve Polygon";
2938
61
            else
2939
61
                return "Curve Polygon";
2940
2941
708
        case wkbMultiCurve:
2942
708
            if (b3D && bMeasured)
2943
332
                return "3D Measured Multi Curve";
2944
376
            else if (b3D)
2945
192
                return "3D Multi Curve";
2946
184
            else if (bMeasured)
2947
102
                return "Measured Multi Curve";
2948
82
            else
2949
82
                return "Multi Curve";
2950
2951
630
        case wkbMultiSurface:
2952
630
            if (b3D && bMeasured)
2953
425
                return "3D Measured Multi Surface";
2954
205
            else if (b3D)
2955
64
                return "3D Multi Surface";
2956
141
            else if (bMeasured)
2957
96
                return "Measured Multi Surface";
2958
45
            else
2959
45
                return "Multi Surface";
2960
2961
1
        case wkbCurve:
2962
1
            if (b3D && bMeasured)
2963
0
                return "3D Measured Curve";
2964
1
            else if (b3D)
2965
0
                return "3D Curve";
2966
1
            else if (bMeasured)
2967
0
                return "Measured Curve";
2968
1
            else
2969
1
                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
258
        case wkbTriangle:
2982
258
            if (b3D && bMeasured)
2983
52
                return "3D Measured Triangle";
2984
206
            else if (b3D)
2985
71
                return "3D Triangle";
2986
135
            else if (bMeasured)
2987
19
                return "Measured Triangle";
2988
116
            else
2989
116
                return "Triangle";
2990
2991
1.35k
        case wkbPolyhedralSurface:
2992
1.35k
            if (b3D && bMeasured)
2993
284
                return "3D Measured PolyhedralSurface";
2994
1.07k
            else if (b3D)
2995
527
                return "3D PolyhedralSurface";
2996
548
            else if (bMeasured)
2997
35
                return "Measured PolyhedralSurface";
2998
513
            else
2999
513
                return "PolyhedralSurface";
3000
3001
1.74k
        case wkbTIN:
3002
1.74k
            if (b3D && bMeasured)
3003
160
                return "3D Measured TIN";
3004
1.58k
            else if (b3D)
3005
378
                return "3D TIN";
3006
1.21k
            else if (bMeasured)
3007
25
                return "Measured TIN";
3008
1.18k
            else
3009
1.18k
                return "TIN";
3010
3011
100
        case wkbNone:
3012
100
            return "None";
3013
3014
0
        default:
3015
0
        {
3016
0
            return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
3017
1.74k
        }
3018
11.8k
    }
3019
11.8k
}
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
5.01k
{
3085
5.01k
    OGRwkbGeometryType eFMain = wkbFlatten(eMain);
3086
5.01k
    OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
3087
3088
5.01k
    const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
3089
5.01k
    const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
3090
3091
5.01k
    if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
3092
63
        return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3093
3094
4.95k
    if (eFMain == wkbNone)
3095
0
        return eExtra;
3096
3097
4.95k
    if (eFExtra == wkbNone)
3098
0
        return eMain;
3099
3100
4.95k
    if (eFMain == eFExtra)
3101
4.52k
    {
3102
4.52k
        return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3103
4.52k
    }
3104
3105
429
    if (bAllowPromotingToCurves)
3106
429
    {
3107
429
        if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
3108
0
            return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
3109
429
    }
3110
3111
    // One is subclass of the other one
3112
429
    if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3113
0
    {
3114
0
        return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3115
0
    }
3116
429
    else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3117
0
    {
3118
0
        return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3119
0
    }
3120
3121
429
    if (OGR_GT_GetSingle(eFMain) == eFExtra)
3122
384
    {
3123
384
        return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3124
384
    }
3125
45
    else if (OGR_GT_GetSingle(eFExtra) == eFMain)
3126
42
    {
3127
42
        return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3128
42
    }
3129
3130
    // Nothing apparently in common.
3131
3
    return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3132
429
}
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
14.8k
{
3241
14.8k
    return OGR_G_ExportToGMLEx(
3242
14.8k
        OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3243
14.8k
        const_cast<char **>(papszOptions));
3244
14.8k
}
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
166k
{
3295
166k
    OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3296
166k
    return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3297
166k
                                const_cast<char **>(papszOptions));
3298
166k
}
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
6.09M
{
3727
6.09M
    return FALSE;
3728
6.09M
}
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
889
{
3760
889
    return clone();
3761
889
}
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 *OGRGeometry::UnaryUnion() const
5652
5653
0
{
5654
0
#ifndef HAVE_GEOS
5655
5656
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5657
0
    return nullptr;
5658
#else
5659
5660
#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5661
    if (IsEmpty())
5662
    {
5663
        // GEOS < 3.11 crashes on an empty geometry
5664
        auto poRet = new OGRGeometryCollection();
5665
        poRet->assignSpatialReference(getSpatialReference());
5666
        return poRet;
5667
    }
5668
#endif
5669
    OGRGeometry *poOGRProduct = nullptr;
5670
5671
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5672
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5673
    if (hThisGeosGeom != nullptr)
5674
    {
5675
        GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5676
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5677
5678
        poOGRProduct =
5679
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5680
    }
5681
    freeGEOSContext(hGEOSCtxt);
5682
5683
    return poOGRProduct;
5684
5685
#endif  // HAVE_GEOS
5686
0
}
5687
5688
/************************************************************************/
5689
/*                          OGR_G_UnaryUnion()                          */
5690
/************************************************************************/
5691
5692
/**
5693
 * \brief Returns the union of all components of a single geometry.
5694
 *
5695
 * Usually used to convert a collection into the smallest set of polygons that
5696
 * cover the same area.
5697
 *
5698
 * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5699
 *
5700
 * Geometry validity is not checked. In case you are unsure of the validity
5701
 * of the input geometries, call IsValid() before, otherwise the result might
5702
 * be wrong.
5703
 *
5704
 * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5705
 *
5706
 * This function is built on the GEOS library, check it for the definition
5707
 * of the geometry operation.
5708
 * If OGR is built without the GEOS library, this function will always fail,
5709
 * issuing a CPLE_NotSupported error.
5710
 *
5711
 * @param hThis the geometry.
5712
 *
5713
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5714
 * or NULL if an error occurs.
5715
 *
5716
 * @since GDAL 3.7
5717
 */
5718
5719
OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5720
5721
0
{
5722
0
    VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5723
5724
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5725
0
}
5726
5727
/************************************************************************/
5728
/*                             Difference()                             */
5729
/************************************************************************/
5730
5731
/**
5732
 * \brief Compute difference.
5733
 *
5734
 * Generates a new geometry which is the region of this geometry with the
5735
 * region of the second geometry removed.
5736
 *
5737
 * Geometry validity is not checked. In case you are unsure of the validity
5738
 * of the input geometries, call IsValid() before, otherwise the result might
5739
 * be wrong.
5740
 *
5741
 * This method is the same as the C function OGR_G_Difference().
5742
 *
5743
 * This method is built on the GEOS library, check it for the definition
5744
 * of the geometry operation.
5745
 * If OGR is built without the GEOS library, this method will always fail,
5746
 * issuing a CPLE_NotSupported error.
5747
 *
5748
 * @param poOtherGeom the other geometry removed from "this" geometry.
5749
 *
5750
 * @return a new geometry to be freed by the caller, or NULL if the difference
5751
 * is empty or if an error occurs.
5752
 */
5753
5754
OGRGeometry *
5755
OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5756
5757
0
{
5758
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5759
0
    {
5760
0
#ifndef HAVE_SFCGAL
5761
5762
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5763
0
        return nullptr;
5764
5765
#else
5766
5767
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5768
        if (poThis == nullptr)
5769
            return nullptr;
5770
5771
        sfcgal_geometry_t *poOther =
5772
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5773
        if (poOther == nullptr)
5774
        {
5775
            sfcgal_geometry_delete(poThis);
5776
            return nullptr;
5777
        }
5778
5779
        sfcgal_geometry_t *poRes =
5780
            sfcgal_geometry_difference_3d(poThis, poOther);
5781
        OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5782
        if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5783
            poOtherGeom->getSpatialReference() != nullptr &&
5784
            poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5785
            h_prodGeom->assignSpatialReference(getSpatialReference());
5786
5787
        sfcgal_geometry_delete(poThis);
5788
        sfcgal_geometry_delete(poOther);
5789
        sfcgal_geometry_delete(poRes);
5790
5791
        return h_prodGeom;
5792
5793
#endif
5794
0
    }
5795
5796
0
    else
5797
0
    {
5798
0
#ifndef HAVE_GEOS
5799
5800
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5801
0
        return nullptr;
5802
5803
#else
5804
        return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5805
#endif /* HAVE_GEOS */
5806
0
    }
5807
0
}
5808
5809
/************************************************************************/
5810
/*                          OGR_G_Difference()                          */
5811
/************************************************************************/
5812
5813
/**
5814
 * \brief Compute difference.
5815
 *
5816
 * Generates a new geometry which is the region of this geometry with the
5817
 * region of the other geometry removed.
5818
 *
5819
 * Geometry validity is not checked. In case you are unsure of the validity
5820
 * of the input geometries, call IsValid() before, otherwise the result might
5821
 * be wrong.
5822
 *
5823
 * This function is the same as the C++ method OGRGeometry::Difference().
5824
 *
5825
 * This function is built on the GEOS library, check it for the definition
5826
 * of the geometry operation.
5827
 * If OGR is built without the GEOS library, this function will always fail,
5828
 * issuing a CPLE_NotSupported error.
5829
 *
5830
 * @param hThis the geometry.
5831
 * @param hOther the other geometry.
5832
 *
5833
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5834
 * or NULL if the difference is empty or if an error occurs.
5835
 */
5836
5837
OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5838
5839
0
{
5840
0
    VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5841
5842
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5843
0
        OGRGeometry::FromHandle(hOther)));
5844
0
}
5845
5846
/************************************************************************/
5847
/*                           SymDifference()                            */
5848
/************************************************************************/
5849
5850
/**
5851
 * \brief Compute symmetric difference.
5852
 *
5853
 * Generates a new geometry which is the symmetric difference of this
5854
 * geometry and the second geometry passed into the method.
5855
 *
5856
 * Geometry validity is not checked. In case you are unsure of the validity
5857
 * of the input geometries, call IsValid() before, otherwise the result might
5858
 * be wrong.
5859
 *
5860
 * This method is the same as the C function OGR_G_SymDifference().
5861
 *
5862
 * This method is built on the GEOS library, check it for the definition
5863
 * of the geometry operation.
5864
 * If OGR is built without the GEOS library, this method will always fail,
5865
 * issuing a CPLE_NotSupported error.
5866
 *
5867
 * @param poOtherGeom the other geometry.
5868
 *
5869
 * @return a new geometry to be freed by the caller, or NULL if the difference
5870
 * is empty or if an error occurs.
5871
 *
5872
 */
5873
5874
OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
5875
5876
0
{
5877
0
    (void)poOtherGeom;
5878
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5879
0
    {
5880
0
#ifndef HAVE_SFCGAL
5881
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5882
0
        return nullptr;
5883
#else
5884
        OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5885
        if (poFirstDifference == nullptr)
5886
            return nullptr;
5887
5888
        OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5889
        if (poOtherDifference == nullptr)
5890
        {
5891
            delete poFirstDifference;
5892
            return nullptr;
5893
        }
5894
5895
        OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5896
        delete poFirstDifference;
5897
        delete poOtherDifference;
5898
        return poSymDiff;
5899
#endif
5900
0
    }
5901
5902
0
#ifndef HAVE_GEOS
5903
5904
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5905
0
    return nullptr;
5906
5907
#else
5908
    return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5909
#endif  // HAVE_GEOS
5910
0
}
5911
5912
//! @cond Doxygen_Suppress
5913
/**
5914
 * \brief Compute symmetric difference (deprecated)
5915
 *
5916
 * @deprecated
5917
 *
5918
 * @see OGRGeometry::SymDifference()
5919
 */
5920
OGRGeometry *
5921
OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5922
5923
0
{
5924
0
    return SymDifference(poOtherGeom);
5925
0
}
5926
5927
//! @endcond
5928
5929
/************************************************************************/
5930
/*                        OGR_G_SymDifference()                         */
5931
/************************************************************************/
5932
5933
/**
5934
 * \brief Compute symmetric difference.
5935
 *
5936
 * Generates a new geometry which is the symmetric difference of this
5937
 * geometry and the other geometry.
5938
 *
5939
 * Geometry validity is not checked. In case you are unsure of the validity
5940
 * of the input geometries, call IsValid() before, otherwise the result might
5941
 * be wrong.
5942
 *
5943
 * This function is the same as the C++ method
5944
 * OGRGeometry::SymmetricDifference().
5945
 *
5946
 * This function is built on the GEOS library, check it for the definition
5947
 * of the geometry operation.
5948
 * If OGR is built without the GEOS library, this function will always fail,
5949
 * issuing a CPLE_NotSupported error.
5950
 *
5951
 * @param hThis the geometry.
5952
 * @param hOther the other geometry.
5953
 *
5954
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5955
 * or NULL if the difference is empty or if an error occurs.
5956
 *
5957
 */
5958
5959
OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5960
5961
0
{
5962
0
    VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5963
5964
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5965
0
        OGRGeometry::FromHandle(hOther)));
5966
0
}
5967
5968
/**
5969
 * \brief Compute symmetric difference (deprecated)
5970
 *
5971
 * @deprecated
5972
 *
5973
 * @see OGR_G_SymmetricDifference()
5974
 */
5975
OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5976
5977
0
{
5978
0
    VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5979
5980
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5981
0
        OGRGeometry::FromHandle(hOther)));
5982
0
}
5983
5984
/************************************************************************/
5985
/*                              Disjoint()                              */
5986
/************************************************************************/
5987
5988
/**
5989
 * \brief Test for disjointness.
5990
 *
5991
 * Tests if this geometry and the other passed into the method are disjoint.
5992
 *
5993
 * Geometry validity is not checked. In case you are unsure of the validity
5994
 * of the input geometries, call IsValid() before, otherwise the result might
5995
 * be wrong.
5996
 *
5997
 * This method is the same as the C function OGR_G_Disjoint().
5998
 *
5999
 * This method is built on the GEOS library, check it for the definition
6000
 * of the geometry operation.
6001
 * If OGR is built without the GEOS library, this method will always fail,
6002
 * issuing a CPLE_NotSupported error.
6003
 *
6004
 * @param poOtherGeom the geometry to compare to this geometry.
6005
 *
6006
 * @return TRUE if they are disjoint, otherwise FALSE.
6007
 */
6008
6009
bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
6010
6011
0
{
6012
0
    (void)poOtherGeom;
6013
0
#ifndef HAVE_GEOS
6014
6015
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6016
0
    return FALSE;
6017
6018
#else
6019
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
6020
#endif  // HAVE_GEOS
6021
0
}
6022
6023
/************************************************************************/
6024
/*                           OGR_G_Disjoint()                           */
6025
/************************************************************************/
6026
6027
/**
6028
 * \brief Test for disjointness.
6029
 *
6030
 * Tests if this geometry and the other geometry are disjoint.
6031
 *
6032
 * Geometry validity is not checked. In case you are unsure of the validity
6033
 * of the input geometries, call IsValid() before, otherwise the result might
6034
 * be wrong.
6035
 *
6036
 * This function is the same as the C++ method OGRGeometry::Disjoint().
6037
 *
6038
 * This function is built on the GEOS library, check it for the definition
6039
 * of the geometry operation.
6040
 * If OGR is built without the GEOS library, this function will always fail,
6041
 * issuing a CPLE_NotSupported error.
6042
 *
6043
 * @param hThis the geometry to compare.
6044
 * @param hOther the other geometry to compare.
6045
 *
6046
 * @return TRUE if they are disjoint, otherwise FALSE.
6047
 */
6048
int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
6049
6050
0
{
6051
0
    VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
6052
6053
0
    return OGRGeometry::FromHandle(hThis)->Disjoint(
6054
0
        OGRGeometry::FromHandle(hOther));
6055
0
}
6056
6057
/************************************************************************/
6058
/*                              Touches()                               */
6059
/************************************************************************/
6060
6061
/**
6062
 * \brief Test for touching.
6063
 *
6064
 * Tests if this geometry and the other passed into the method are touching.
6065
 *
6066
 * Geometry validity is not checked. In case you are unsure of the validity
6067
 * of the input geometries, call IsValid() before, otherwise the result might
6068
 * be wrong.
6069
 *
6070
 * This method is the same as the C function OGR_G_Touches().
6071
 *
6072
 * This method is built on the GEOS library, check it for the definition
6073
 * of the geometry operation.
6074
 * If OGR is built without the GEOS library, this method will always fail,
6075
 * issuing a CPLE_NotSupported error.
6076
 *
6077
 * @param poOtherGeom the geometry to compare to this geometry.
6078
 *
6079
 * @return TRUE if they are touching, otherwise FALSE.
6080
 */
6081
6082
bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
6083
6084
0
{
6085
0
    (void)poOtherGeom;
6086
0
#ifndef HAVE_GEOS
6087
6088
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6089
0
    return FALSE;
6090
6091
#else
6092
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
6093
#endif  // HAVE_GEOS
6094
0
}
6095
6096
/************************************************************************/
6097
/*                           OGR_G_Touches()                            */
6098
/************************************************************************/
6099
/**
6100
 * \brief Test for touching.
6101
 *
6102
 * Tests if this geometry and the other geometry are touching.
6103
 *
6104
 * Geometry validity is not checked. In case you are unsure of the validity
6105
 * of the input geometries, call IsValid() before, otherwise the result might
6106
 * be wrong.
6107
 *
6108
 * This function is the same as the C++ method OGRGeometry::Touches().
6109
 *
6110
 * This function is built on the GEOS library, check it for the definition
6111
 * of the geometry operation.
6112
 * If OGR is built without the GEOS library, this function will always fail,
6113
 * issuing a CPLE_NotSupported error.
6114
 *
6115
 * @param hThis the geometry to compare.
6116
 * @param hOther the other geometry to compare.
6117
 *
6118
 * @return TRUE if they are touching, otherwise FALSE.
6119
 */
6120
6121
int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
6122
6123
0
{
6124
0
    VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
6125
6126
0
    return OGRGeometry::FromHandle(hThis)->Touches(
6127
0
        OGRGeometry::FromHandle(hOther));
6128
0
}
6129
6130
/************************************************************************/
6131
/*                              Crosses()                               */
6132
/************************************************************************/
6133
6134
/**
6135
 * \brief Test for crossing.
6136
 *
6137
 * Tests if this geometry and the other passed into the method are crossing.
6138
 *
6139
 * Geometry validity is not checked. In case you are unsure of the validity
6140
 * of the input geometries, call IsValid() before, otherwise the result might
6141
 * be wrong.
6142
 *
6143
 * This method is the same as the C function OGR_G_Crosses().
6144
 *
6145
 * This method is built on the GEOS library, check it for the definition
6146
 * of the geometry operation.
6147
 * If OGR is built without the GEOS library, this method will always fail,
6148
 * issuing a CPLE_NotSupported error.
6149
 *
6150
 * @param poOtherGeom the geometry to compare to this geometry.
6151
 *
6152
 * @return TRUE if they are crossing, otherwise FALSE.
6153
 */
6154
6155
bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
6156
6157
0
{
6158
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
6159
0
    {
6160
0
#ifndef HAVE_SFCGAL
6161
6162
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
6163
0
        return FALSE;
6164
6165
#else
6166
6167
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
6168
        if (poThis == nullptr)
6169
            return FALSE;
6170
6171
        sfcgal_geometry_t *poOther =
6172
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
6173
        if (poOther == nullptr)
6174
        {
6175
            sfcgal_geometry_delete(poThis);
6176
            return FALSE;
6177
        }
6178
6179
        int res = sfcgal_geometry_intersects_3d(poThis, poOther);
6180
6181
        sfcgal_geometry_delete(poThis);
6182
        sfcgal_geometry_delete(poOther);
6183
6184
        return (res == 1) ? TRUE : FALSE;
6185
6186
#endif
6187
0
    }
6188
6189
0
    else
6190
0
    {
6191
6192
0
#ifndef HAVE_GEOS
6193
6194
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6195
0
        return FALSE;
6196
6197
#else
6198
        return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
6199
#endif /* HAVE_GEOS */
6200
0
    }
6201
0
}
6202
6203
/************************************************************************/
6204
/*                           OGR_G_Crosses()                            */
6205
/************************************************************************/
6206
/**
6207
 * \brief Test for crossing.
6208
 *
6209
 * Tests if this geometry and the other geometry are crossing.
6210
 *
6211
 * Geometry validity is not checked. In case you are unsure of the validity
6212
 * of the input geometries, call IsValid() before, otherwise the result might
6213
 * be wrong.
6214
 *
6215
 * This function is the same as the C++ method OGRGeometry::Crosses().
6216
 *
6217
 * This function is built on the GEOS library, check it for the definition
6218
 * of the geometry operation.
6219
 * If OGR is built without the GEOS library, this function will always fail,
6220
 * issuing a CPLE_NotSupported error.
6221
 *
6222
 * @param hThis the geometry to compare.
6223
 * @param hOther the other geometry to compare.
6224
 *
6225
 * @return TRUE if they are crossing, otherwise FALSE.
6226
 */
6227
6228
int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
6229
6230
0
{
6231
0
    VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
6232
6233
0
    return OGRGeometry::FromHandle(hThis)->Crosses(
6234
0
        OGRGeometry::FromHandle(hOther));
6235
0
}
6236
6237
/************************************************************************/
6238
/*                               Within()                               */
6239
/************************************************************************/
6240
6241
/**
6242
 * \brief Test for containment.
6243
 *
6244
 * Tests if actual geometry object is within the passed geometry.
6245
 *
6246
 * Geometry validity is not checked. In case you are unsure of the validity
6247
 * of the input geometries, call IsValid() before, otherwise the result might
6248
 * be wrong.
6249
 *
6250
 * This method is the same as the C function OGR_G_Within().
6251
 *
6252
 * This method is built on the GEOS library, check it for the definition
6253
 * of the geometry operation.
6254
 * If OGR is built without the GEOS library, this method will always fail,
6255
 * issuing a CPLE_NotSupported error.
6256
 *
6257
 * @param poOtherGeom the geometry to compare to this geometry.
6258
 *
6259
 * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
6260
 */
6261
6262
bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
6263
6264
0
{
6265
0
    (void)poOtherGeom;
6266
0
#ifndef HAVE_GEOS
6267
6268
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6269
0
    return FALSE;
6270
6271
#else
6272
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
6273
#endif  // HAVE_GEOS
6274
0
}
6275
6276
/************************************************************************/
6277
/*                            OGR_G_Within()                            */
6278
/************************************************************************/
6279
6280
/**
6281
 * \brief Test for containment.
6282
 *
6283
 * Tests if this geometry is within the other geometry.
6284
 *
6285
 * Geometry validity is not checked. In case you are unsure of the validity
6286
 * of the input geometries, call IsValid() before, otherwise the result might
6287
 * be wrong.
6288
 *
6289
 * This function is the same as the C++ method OGRGeometry::Within().
6290
 *
6291
 * This function is built on the GEOS library, check it for the definition
6292
 * of the geometry operation.
6293
 * If OGR is built without the GEOS library, this function will always fail,
6294
 * issuing a CPLE_NotSupported error.
6295
 *
6296
 * @param hThis the geometry to compare.
6297
 * @param hOther the other geometry to compare.
6298
 *
6299
 * @return TRUE if hThis is within hOther, otherwise FALSE.
6300
 */
6301
int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
6302
6303
0
{
6304
0
    VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
6305
6306
0
    return OGRGeometry::FromHandle(hThis)->Within(
6307
0
        OGRGeometry::FromHandle(hOther));
6308
0
}
6309
6310
/************************************************************************/
6311
/*                              Contains()                              */
6312
/************************************************************************/
6313
6314
/**
6315
 * \brief Test for containment.
6316
 *
6317
 * Tests if actual geometry object contains the passed geometry.
6318
 *
6319
 * Geometry validity is not checked. In case you are unsure of the validity
6320
 * of the input geometries, call IsValid() before, otherwise the result might
6321
 * be wrong.
6322
 *
6323
 * This method is the same as the C function OGR_G_Contains().
6324
 *
6325
 * This method is built on the GEOS library, check it for the definition
6326
 * of the geometry operation.
6327
 * If OGR is built without the GEOS library, this method will always fail,
6328
 * issuing a CPLE_NotSupported error.
6329
 *
6330
 * @param poOtherGeom the geometry to compare to this geometry.
6331
 *
6332
 * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
6333
 */
6334
6335
bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
6336
6337
0
{
6338
0
    (void)poOtherGeom;
6339
0
#ifndef HAVE_GEOS
6340
6341
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6342
0
    return FALSE;
6343
6344
#else
6345
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
6346
#endif  // HAVE_GEOS
6347
0
}
6348
6349
/************************************************************************/
6350
/*                           OGR_G_Contains()                           */
6351
/************************************************************************/
6352
6353
/**
6354
 * \brief Test for containment.
6355
 *
6356
 * Tests if this geometry contains the other geometry.
6357
 *
6358
 * Geometry validity is not checked. In case you are unsure of the validity
6359
 * of the input geometries, call IsValid() before, otherwise the result might
6360
 * be wrong.
6361
 *
6362
 * This function is the same as the C++ method OGRGeometry::Contains().
6363
 *
6364
 * This function is built on the GEOS library, check it for the definition
6365
 * of the geometry operation.
6366
 * If OGR is built without the GEOS library, this function will always fail,
6367
 * issuing a CPLE_NotSupported error.
6368
 *
6369
 * @param hThis the geometry to compare.
6370
 * @param hOther the other geometry to compare.
6371
 *
6372
 * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
6373
 */
6374
int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
6375
6376
0
{
6377
0
    VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
6378
6379
0
    return OGRGeometry::FromHandle(hThis)->Contains(
6380
0
        OGRGeometry::FromHandle(hOther));
6381
0
}
6382
6383
/************************************************************************/
6384
/*                              Overlaps()                              */
6385
/************************************************************************/
6386
6387
/**
6388
 * \brief Test for overlap.
6389
 *
6390
 * Tests if this geometry and the other passed into the method overlap, that is
6391
 * their intersection has a non-zero area.
6392
 *
6393
 * Geometry validity is not checked. In case you are unsure of the validity
6394
 * of the input geometries, call IsValid() before, otherwise the result might
6395
 * be wrong.
6396
 *
6397
 * This method is the same as the C function OGR_G_Overlaps().
6398
 *
6399
 * This method is built on the GEOS library, check it for the definition
6400
 * of the geometry operation.
6401
 * If OGR is built without the GEOS library, this method will always fail,
6402
 * issuing a CPLE_NotSupported error.
6403
 *
6404
 * @param poOtherGeom the geometry to compare to this geometry.
6405
 *
6406
 * @return TRUE if they are overlapping, otherwise FALSE.
6407
 */
6408
6409
bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
6410
6411
0
{
6412
0
    (void)poOtherGeom;
6413
0
#ifndef HAVE_GEOS
6414
6415
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6416
0
    return FALSE;
6417
6418
#else
6419
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
6420
#endif  // HAVE_GEOS
6421
0
}
6422
6423
/************************************************************************/
6424
/*                           OGR_G_Overlaps()                           */
6425
/************************************************************************/
6426
/**
6427
 * \brief Test for overlap.
6428
 *
6429
 * Tests if this geometry and the other geometry overlap, that is their
6430
 * intersection has a non-zero area.
6431
 *
6432
 * Geometry validity is not checked. In case you are unsure of the validity
6433
 * of the input geometries, call IsValid() before, otherwise the result might
6434
 * be wrong.
6435
 *
6436
 * This function is the same as the C++ method OGRGeometry::Overlaps().
6437
 *
6438
 * This function is built on the GEOS library, check it for the definition
6439
 * of the geometry operation.
6440
 * If OGR is built without the GEOS library, this function will always fail,
6441
 * issuing a CPLE_NotSupported error.
6442
 *
6443
 * @param hThis the geometry to compare.
6444
 * @param hOther the other geometry to compare.
6445
 *
6446
 * @return TRUE if they are overlapping, otherwise FALSE.
6447
 */
6448
6449
int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6450
6451
0
{
6452
0
    VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6453
6454
0
    return OGRGeometry::FromHandle(hThis)->Overlaps(
6455
0
        OGRGeometry::FromHandle(hOther));
6456
0
}
6457
6458
/************************************************************************/
6459
/*                             closeRings()                             */
6460
/************************************************************************/
6461
6462
/**
6463
 * \brief Force rings to be closed.
6464
 *
6465
 * If this geometry, or any contained geometries has polygon rings that
6466
 * are not closed, they will be closed by adding the starting point at
6467
 * the end.
6468
 */
6469
6470
void OGRGeometry::closeRings()
6471
4.43k
{
6472
4.43k
}
6473
6474
/************************************************************************/
6475
/*                          OGR_G_CloseRings()                          */
6476
/************************************************************************/
6477
6478
/**
6479
 * \brief Force rings to be closed.
6480
 *
6481
 * If this geometry, or any contained geometries has polygon rings that
6482
 * are not closed, they will be closed by adding the starting point at
6483
 * the end.
6484
 *
6485
 * @param hGeom handle to the geometry.
6486
 */
6487
6488
void OGR_G_CloseRings(OGRGeometryH hGeom)
6489
6490
0
{
6491
0
    VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6492
6493
0
    OGRGeometry::FromHandle(hGeom)->closeRings();
6494
0
}
6495
6496
/************************************************************************/
6497
/*                              Centroid()                              */
6498
/************************************************************************/
6499
6500
/**
6501
 * \brief Compute the geometry centroid.
6502
 *
6503
 * The centroid location is applied to the passed in OGRPoint object.
6504
 * The centroid is not necessarily within the geometry.
6505
 *
6506
 * This method relates to the SFCOM ISurface::get_Centroid() method
6507
 * however the current implementation based on GEOS can operate on other
6508
 * geometry types such as multipoint, linestring, geometrycollection such as
6509
 * multipolygons.
6510
 * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6511
 * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6512
 * (multipolygons).
6513
 *
6514
 * This function is the same as the C function OGR_G_Centroid().
6515
 *
6516
 * This function is built on the GEOS library, check it for the definition
6517
 * of the geometry operation.
6518
 * If OGR is built without the GEOS library, this function will always fail,
6519
 * issuing a CPLE_NotSupported error.
6520
 *
6521
 * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6522
 *
6523
 * to OGRPolygon)
6524
 */
6525
6526
OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6527
6528
0
{
6529
0
    if (poPoint == nullptr)
6530
0
        return OGRERR_FAILURE;
6531
6532
0
#ifndef HAVE_GEOS
6533
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6534
0
    return OGRERR_FAILURE;
6535
6536
#else
6537
6538
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6539
    GEOSGeom hThisGeosGeom =
6540
        exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6541
6542
    if (hThisGeosGeom != nullptr)
6543
    {
6544
        GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6545
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6546
6547
        if (hOtherGeosGeom == nullptr)
6548
        {
6549
            freeGEOSContext(hGEOSCtxt);
6550
            return OGRERR_FAILURE;
6551
        }
6552
6553
        OGRGeometry *poCentroidGeom =
6554
            OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6555
6556
        GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6557
6558
        if (poCentroidGeom == nullptr)
6559
        {
6560
            freeGEOSContext(hGEOSCtxt);
6561
            return OGRERR_FAILURE;
6562
        }
6563
        if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6564
        {
6565
            delete poCentroidGeom;
6566
            freeGEOSContext(hGEOSCtxt);
6567
            return OGRERR_FAILURE;
6568
        }
6569
6570
        if (getSpatialReference() != nullptr)
6571
            poCentroidGeom->assignSpatialReference(getSpatialReference());
6572
6573
        OGRPoint *poCentroid = poCentroidGeom->toPoint();
6574
6575
        if (!poCentroid->IsEmpty())
6576
        {
6577
            poPoint->setX(poCentroid->getX());
6578
            poPoint->setY(poCentroid->getY());
6579
        }
6580
        else
6581
        {
6582
            poPoint->empty();
6583
        }
6584
6585
        delete poCentroidGeom;
6586
6587
        freeGEOSContext(hGEOSCtxt);
6588
        return OGRERR_NONE;
6589
    }
6590
    else
6591
    {
6592
        freeGEOSContext(hGEOSCtxt);
6593
        return OGRERR_FAILURE;
6594
    }
6595
6596
#endif  // HAVE_GEOS
6597
0
}
6598
6599
/************************************************************************/
6600
/*                           OGR_G_Centroid()                           */
6601
/************************************************************************/
6602
6603
/**
6604
 * \brief Compute the geometry centroid.
6605
 *
6606
 * The centroid location is applied to the passed in OGRPoint object.
6607
 * The centroid is not necessarily within the geometry.
6608
 *
6609
 * This method relates to the SFCOM ISurface::get_Centroid() method
6610
 * however the current implementation based on GEOS can operate on other
6611
 * geometry types such as multipoint, linestring, geometrycollection such as
6612
 * multipolygons.
6613
 * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6614
 * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6615
 * (multipolygons).
6616
 *
6617
 * This function is the same as the C++ method OGRGeometry::Centroid().
6618
 *
6619
 * This function is built on the GEOS library, check it for the definition
6620
 * of the geometry operation.
6621
 * If OGR is built without the GEOS library, this function will always fail,
6622
 * issuing a CPLE_NotSupported error.
6623
 *
6624
 * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6625
 */
6626
6627
int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6628
6629
0
{
6630
0
    VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6631
6632
0
    OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6633
0
    if (poCentroidGeom == nullptr)
6634
0
        return OGRERR_FAILURE;
6635
0
    if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6636
0
    {
6637
0
        CPLError(CE_Failure, CPLE_AppDefined,
6638
0
                 "Passed wrong geometry type as centroid argument.");
6639
0
        return OGRERR_FAILURE;
6640
0
    }
6641
6642
0
    return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6643
0
}
6644
6645
/************************************************************************/
6646
/*                        OGR_G_PointOnSurface()                        */
6647
/************************************************************************/
6648
6649
/**
6650
 * \brief Returns a point guaranteed to lie on the surface.
6651
 *
6652
 * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6653
 * however the current implementation based on GEOS can operate on other
6654
 * geometry types than the types that are supported by SQL/MM-Part 3 :
6655
 * surfaces (polygons) and multisurfaces (multipolygons).
6656
 *
6657
 * This method is built on the GEOS library, check it for the definition
6658
 * of the geometry operation.
6659
 * If OGR is built without the GEOS library, this method will always fail,
6660
 * issuing a CPLE_NotSupported error.
6661
 *
6662
 * @param hGeom the geometry to operate on.
6663
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6664
 * or NULL if an error occurs.
6665
 *
6666
 */
6667
6668
OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6669
6670
0
{
6671
0
    VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6672
6673
0
#ifndef HAVE_GEOS
6674
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6675
0
    return nullptr;
6676
#else
6677
6678
    OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6679
6680
    GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6681
    GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6682
6683
    if (hThisGeosGeom != nullptr)
6684
    {
6685
        GEOSGeom hOtherGeosGeom =
6686
            GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6687
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6688
6689
        if (hOtherGeosGeom == nullptr)
6690
        {
6691
            OGRGeometry::freeGEOSContext(hGEOSCtxt);
6692
            return nullptr;
6693
        }
6694
6695
        OGRGeometry *poInsidePointGeom =
6696
            OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6697
6698
        GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6699
6700
        if (poInsidePointGeom == nullptr)
6701
        {
6702
            OGRGeometry::freeGEOSContext(hGEOSCtxt);
6703
            return nullptr;
6704
        }
6705
        if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6706
        {
6707
            delete poInsidePointGeom;
6708
            OGRGeometry::freeGEOSContext(hGEOSCtxt);
6709
            return nullptr;
6710
        }
6711
6712
        if (poThis->getSpatialReference() != nullptr)
6713
            poInsidePointGeom->assignSpatialReference(
6714
                poThis->getSpatialReference());
6715
6716
        OGRGeometry::freeGEOSContext(hGEOSCtxt);
6717
        return OGRGeometry::ToHandle(poInsidePointGeom);
6718
    }
6719
6720
    OGRGeometry::freeGEOSContext(hGEOSCtxt);
6721
    return nullptr;
6722
#endif
6723
0
}
6724
6725
/************************************************************************/
6726
/*                       PointOnSurfaceInternal()                       */
6727
/************************************************************************/
6728
6729
//! @cond Doxygen_Suppress
6730
OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6731
0
{
6732
0
    if (poPoint == nullptr || poPoint->IsEmpty())
6733
0
        return OGRERR_FAILURE;
6734
6735
0
    OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6736
0
        OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6737
0
    if (hInsidePoint == nullptr)
6738
0
        return OGRERR_FAILURE;
6739
6740
0
    OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6741
0
    if (poInsidePoint->IsEmpty())
6742
0
    {
6743
0
        poPoint->empty();
6744
0
    }
6745
0
    else
6746
0
    {
6747
0
        poPoint->setX(poInsidePoint->getX());
6748
0
        poPoint->setY(poInsidePoint->getY());
6749
0
    }
6750
6751
0
    OGR_G_DestroyGeometry(hInsidePoint);
6752
6753
0
    return OGRERR_NONE;
6754
0
}
6755
6756
//! @endcond
6757
6758
/************************************************************************/
6759
/*                              Simplify()                              */
6760
/************************************************************************/
6761
6762
/**
6763
 * \brief Simplify the geometry.
6764
 *
6765
 * This function is the same as the C function OGR_G_Simplify().
6766
 *
6767
 * This function is built on the GEOS library, check it for the definition
6768
 * of the geometry operation.
6769
 * If OGR is built without the GEOS library, this function will always fail,
6770
 * issuing a CPLE_NotSupported error.
6771
 *
6772
 * @param dTolerance the distance tolerance for the simplification.
6773
 *
6774
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6775
 *
6776
 */
6777
6778
OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
6779
6780
0
{
6781
0
    (void)dTolerance;
6782
0
#ifndef HAVE_GEOS
6783
6784
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6785
0
    return nullptr;
6786
6787
#else
6788
    OGRGeometry *poOGRProduct = nullptr;
6789
6790
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6791
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6792
    if (hThisGeosGeom != nullptr)
6793
    {
6794
        GEOSGeom hGeosProduct =
6795
            GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6796
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6797
        poOGRProduct =
6798
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6799
    }
6800
    freeGEOSContext(hGEOSCtxt);
6801
    return poOGRProduct;
6802
6803
#endif  // HAVE_GEOS
6804
0
}
6805
6806
/************************************************************************/
6807
/*                           OGR_G_Simplify()                           */
6808
/************************************************************************/
6809
6810
/**
6811
 * \brief Compute a simplified geometry.
6812
 *
6813
 * This function is the same as the C++ method OGRGeometry::Simplify().
6814
 *
6815
 * This function is built on the GEOS library, check it for the definition
6816
 * of the geometry operation.
6817
 * If OGR is built without the GEOS library, this function will always fail,
6818
 * issuing a CPLE_NotSupported error.
6819
 *
6820
 * @param hThis the geometry.
6821
 * @param dTolerance the distance tolerance for the simplification.
6822
 *
6823
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6824
 * or NULL if an error occurs.
6825
 *
6826
 */
6827
6828
OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6829
6830
0
{
6831
0
    VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6832
0
    return OGRGeometry::ToHandle(
6833
0
        OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6834
0
}
6835
6836
/************************************************************************/
6837
/*                      SimplifyPreserveTopology()                      */
6838
/************************************************************************/
6839
6840
/**
6841
 * \brief Simplify the geometry while preserving topology.
6842
 *
6843
 * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6844
 *
6845
 * This function is built on the GEOS library, check it for the definition
6846
 * of the geometry operation.
6847
 * If OGR is built without the GEOS library, this function will always fail,
6848
 * issuing a CPLE_NotSupported error.
6849
 *
6850
 * @param dTolerance the distance tolerance for the simplification.
6851
 *
6852
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6853
 *
6854
 */
6855
6856
OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
6857
6858
0
{
6859
0
    (void)dTolerance;
6860
0
#ifndef HAVE_GEOS
6861
6862
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6863
0
    return nullptr;
6864
6865
#else
6866
    OGRGeometry *poOGRProduct = nullptr;
6867
6868
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6869
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6870
    if (hThisGeosGeom != nullptr)
6871
    {
6872
        GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6873
            hGEOSCtxt, hThisGeosGeom, dTolerance);
6874
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6875
        poOGRProduct =
6876
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6877
    }
6878
    freeGEOSContext(hGEOSCtxt);
6879
    return poOGRProduct;
6880
6881
#endif  // HAVE_GEOS
6882
0
}
6883
6884
/************************************************************************/
6885
/*                   OGR_G_SimplifyPreserveTopology()                   */
6886
/************************************************************************/
6887
6888
/**
6889
 * \brief Simplify the geometry while preserving topology.
6890
 *
6891
 * This function is the same as the C++ method
6892
 * OGRGeometry::SimplifyPreserveTopology().
6893
 *
6894
 * This function is built on the GEOS library, check it for the definition
6895
 * of the geometry operation.
6896
 * If OGR is built without the GEOS library, this function will always fail,
6897
 * issuing a CPLE_NotSupported error.
6898
 *
6899
 * @param hThis the geometry.
6900
 * @param dTolerance the distance tolerance for the simplification.
6901
 *
6902
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6903
 * or NULL if an error occurs.
6904
 *
6905
 */
6906
6907
OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6908
                                            double dTolerance)
6909
6910
0
{
6911
0
    VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6912
0
    return OGRGeometry::ToHandle(
6913
0
        OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6914
0
}
6915
6916
/************************************************************************/
6917
/*                          roundCoordinates()                          */
6918
/************************************************************************/
6919
6920
/** Round coordinates of the geometry to the specified precision.
6921
 *
6922
 * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6923
 * will return valid geometries, whereas roundCoordinates() does not make
6924
 * such guarantee and may return geometries with invalidities, if they are
6925
 * not compatible with the specified precision. roundCoordinates() supports
6926
 * curve geometries, whereas SetPrecision() does not currently.
6927
 *
6928
 * One use case for roundCoordinates() is to undo the effect of
6929
 * quantizeCoordinates().
6930
 *
6931
 * @param sPrecision Contains the precision requirements.
6932
 * @since GDAL 3.9
6933
 */
6934
void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6935
0
{
6936
0
    struct Rounder : public OGRDefaultGeometryVisitor
6937
0
    {
6938
0
        const OGRGeomCoordinatePrecision &m_precision;
6939
0
        const double m_invXYResolution;
6940
0
        const double m_invZResolution;
6941
0
        const double m_invMResolution;
6942
6943
0
        explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6944
0
            : m_precision(sPrecisionIn),
6945
0
              m_invXYResolution(m_precision.dfXYResolution !=
6946
0
                                        OGRGeomCoordinatePrecision::UNKNOWN
6947
0
                                    ? 1.0 / m_precision.dfXYResolution
6948
0
                                    : 0.0),
6949
0
              m_invZResolution(m_precision.dfZResolution !=
6950
0
                                       OGRGeomCoordinatePrecision::UNKNOWN
6951
0
                                   ? 1.0 / m_precision.dfZResolution
6952
0
                                   : 0.0),
6953
0
              m_invMResolution(m_precision.dfMResolution !=
6954
0
                                       OGRGeomCoordinatePrecision::UNKNOWN
6955
0
                                   ? 1.0 / m_precision.dfMResolution
6956
0
                                   : 0.0)
6957
0
        {
6958
0
        }
6959
6960
0
        using OGRDefaultGeometryVisitor::visit;
6961
6962
0
        void visit(OGRPoint *poPoint) override
6963
0
        {
6964
0
            if (m_precision.dfXYResolution !=
6965
0
                OGRGeomCoordinatePrecision::UNKNOWN)
6966
0
            {
6967
0
                poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6968
0
                              m_precision.dfXYResolution);
6969
0
                poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6970
0
                              m_precision.dfXYResolution);
6971
0
            }
6972
0
            if (m_precision.dfZResolution !=
6973
0
                    OGRGeomCoordinatePrecision::UNKNOWN &&
6974
0
                poPoint->Is3D())
6975
0
            {
6976
0
                poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6977
0
                              m_precision.dfZResolution);
6978
0
            }
6979
0
            if (m_precision.dfMResolution !=
6980
0
                    OGRGeomCoordinatePrecision::UNKNOWN &&
6981
0
                poPoint->IsMeasured())
6982
0
            {
6983
0
                poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6984
0
                              m_precision.dfMResolution);
6985
0
            }
6986
0
        }
6987
0
    };
6988
6989
0
    Rounder rounder(sPrecision);
6990
0
    accept(&rounder);
6991
0
}
6992
6993
/************************************************************************/
6994
/*                            SetPrecision()                            */
6995
/************************************************************************/
6996
6997
/** Set the geometry's precision, rounding all its coordinates to the precision
6998
 * grid, and making sure the geometry is still valid.
6999
 *
7000
 * This is a stronger version of roundCoordinates().
7001
 *
7002
 * Note that at time of writing GEOS does no supported curve geometries. So
7003
 * currently if this function is called on such a geometry, OGR will first call
7004
 * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7005
 * that it is unlikely to yield to the expected result.
7006
 *
7007
 * This function is the same as the C function OGR_G_SetPrecision().
7008
 *
7009
 * This function is built on the GEOSGeom_setPrecision_r() function of the
7010
 * GEOS library. Check it for the definition of the geometry operation.
7011
 * If OGR is built without the GEOS library, this function will always fail,
7012
 * issuing a CPLE_NotSupported error.
7013
 *
7014
 * @param dfGridSize size of the precision grid, or 0 for FLOATING
7015
 *                 precision.
7016
 * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7017
 *               and OGR_GEOS_PREC_KEEP_COLLAPSED
7018
 *
7019
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7020
 *
7021
 * @since GDAL 3.9
7022
 */
7023
7024
OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
7025
0
{
7026
0
    (void)dfGridSize;
7027
0
    (void)nFlags;
7028
0
#ifndef HAVE_GEOS
7029
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7030
0
    return nullptr;
7031
7032
#else
7033
    OGRGeometry *poOGRProduct = nullptr;
7034
7035
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7036
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7037
    if (hThisGeosGeom != nullptr)
7038
    {
7039
        GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
7040
            hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
7041
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7042
        poOGRProduct =
7043
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7044
    }
7045
    freeGEOSContext(hGEOSCtxt);
7046
    return poOGRProduct;
7047
7048
#endif  // HAVE_GEOS
7049
0
}
7050
7051
/************************************************************************/
7052
/*                         OGR_G_SetPrecision()                         */
7053
/************************************************************************/
7054
7055
/** Set the geometry's precision, rounding all its coordinates to the precision
7056
 * grid, and making sure the geometry is still valid.
7057
 *
7058
 * This is a stronger version of roundCoordinates().
7059
 *
7060
 * Note that at time of writing GEOS does no supported curve geometries. So
7061
 * currently if this function is called on such a geometry, OGR will first call
7062
 * getLinearGeometry() on the input and getCurveGeometry() on the output, but
7063
 * that it is unlikely to yield to the expected result.
7064
 *
7065
 * This function is the same as the C++ method OGRGeometry::SetPrecision().
7066
 *
7067
 * This function is built on the GEOSGeom_setPrecision_r() function of the
7068
 * GEOS library. Check it for the definition of the geometry operation.
7069
 * If OGR is built without the GEOS library, this function will always fail,
7070
 * issuing a CPLE_NotSupported error.
7071
 *
7072
 * @param hThis the geometry.
7073
 * @param dfGridSize size of the precision grid, or 0 for FLOATING
7074
 *                 precision.
7075
 * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
7076
 *               and OGR_GEOS_PREC_KEEP_COLLAPSED
7077
 *
7078
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7079
 * or NULL if an error occurs.
7080
 *
7081
 * @since GDAL 3.9
7082
 */
7083
OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
7084
                                int nFlags)
7085
0
{
7086
0
    VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
7087
0
    return OGRGeometry::ToHandle(
7088
0
        OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
7089
0
}
7090
7091
/************************************************************************/
7092
/*                       DelaunayTriangulation()                        */
7093
/************************************************************************/
7094
7095
/**
7096
 * \brief Return a Delaunay triangulation of the vertices of the geometry.
7097
 *
7098
 * This function is the same as the C function OGR_G_DelaunayTriangulation().
7099
 *
7100
 * This function is built on the GEOS library, v3.4 or above.
7101
 * If OGR is built without the GEOS library, this function will always fail,
7102
 * issuing a CPLE_NotSupported error.
7103
 *
7104
 * @param dfTolerance optional snapping tolerance to use for improved robustness
7105
 * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7106
 *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7107
 *
7108
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7109
 */
7110
7111
#ifndef HAVE_GEOS
7112
OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
7113
                                                int /*bOnlyEdges*/) const
7114
0
{
7115
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7116
0
    return nullptr;
7117
0
}
7118
#else
7119
OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
7120
                                                int bOnlyEdges) const
7121
{
7122
    OGRGeometry *poOGRProduct = nullptr;
7123
7124
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7125
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7126
    if (hThisGeosGeom != nullptr)
7127
    {
7128
        GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
7129
            hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
7130
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7131
        poOGRProduct =
7132
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7133
    }
7134
    freeGEOSContext(hGEOSCtxt);
7135
    return poOGRProduct;
7136
}
7137
#endif
7138
7139
/************************************************************************/
7140
/*                    OGR_G_DelaunayTriangulation()                     */
7141
/************************************************************************/
7142
7143
/**
7144
 * \brief Return a Delaunay triangulation of the vertices of the geometry.
7145
 *
7146
 * This function is the same as the C++ method
7147
 * OGRGeometry::DelaunayTriangulation().
7148
 *
7149
 * This function is built on the GEOS library, v3.4 or above.
7150
 * If OGR is built without the GEOS library, this function will always fail,
7151
 * issuing a CPLE_NotSupported error.
7152
 *
7153
 * @param hThis the geometry.
7154
 * @param dfTolerance optional snapping tolerance to use for improved robustness
7155
 * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
7156
 *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
7157
 *
7158
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7159
 * or NULL if an error occurs.
7160
 */
7161
7162
OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
7163
                                         int bOnlyEdges)
7164
7165
0
{
7166
0
    VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
7167
7168
0
    return OGRGeometry::ToHandle(
7169
0
        OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
7170
0
                                                              bOnlyEdges));
7171
0
}
7172
7173
/************************************************************************/
7174
/*                  ConstrainedDelaunayTriangulation()                  */
7175
/************************************************************************/
7176
7177
/**
7178
 * \brief Return a constrained Delaunay triangulation of the vertices of the
7179
 * given polygon(s). For non-polygonal inputs, silently returns an empty
7180
 * geometry collection.
7181
 *
7182
 * This function is the same as the C function
7183
 * OGR_G_ConstrainedDelaunayTriangulation().
7184
 *
7185
 * This function is built on the GEOS library, v3.10 or above.
7186
 * If OGR is built without the GEOS library, this function will always fail,
7187
 * issuing a CPLE_NotSupported error.
7188
 *
7189
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7190
 *
7191
 * @since OGR 3.12
7192
 */
7193
7194
OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
7195
0
{
7196
0
#ifndef HAVE_GEOS
7197
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7198
0
    return nullptr;
7199
#elif !(GEOS_VERSION_MAJOR > 3 ||                                              \
7200
        (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
7201
    CPLError(
7202
        CE_Failure, CPLE_NotSupported,
7203
        "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
7204
    return nullptr;
7205
#else
7206
7207
    OGRGeometry *poOGRProduct = nullptr;
7208
7209
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7210
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7211
    if (hThisGeosGeom != nullptr)
7212
    {
7213
        GEOSGeom hGeosProduct =
7214
            GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
7215
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7216
        poOGRProduct =
7217
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
7218
    }
7219
    freeGEOSContext(hGEOSCtxt);
7220
    return poOGRProduct;
7221
#endif
7222
0
}
7223
7224
/************************************************************************/
7225
/*               OGR_G_ConstrainedDelaunayTriangulation()               */
7226
/************************************************************************/
7227
7228
/**
7229
 * \brief Return a constrained Delaunay triangulation of the vertices of the
7230
 * given polygon(s). For non-polygonal inputs, silently returns an empty
7231
 * geometry collection.
7232
 *
7233
 * This function is the same as the C++ method
7234
 * OGRGeometry::ConstrainedDelaunayTriangulation().
7235
 *
7236
 * This function is built on the GEOS library, v3.10 or above.
7237
 * If OGR is built without the GEOS library, this function will always fail,
7238
 * issuing a CPLE_NotSupported error.
7239
 *
7240
 * @param hThis the geometry.
7241
 *
7242
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7243
 * or NULL if an error occurs.
7244
 *
7245
 * @since OGR 3.12
7246
 */
7247
7248
OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
7249
0
{
7250
0
    VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
7251
7252
0
    return OGRGeometry::ToHandle(
7253
0
        OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
7254
0
}
7255
7256
/************************************************************************/
7257
/*                             Polygonize()                             */
7258
/************************************************************************/
7259
/* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
7260
/* Developed for Faunalia (http://www.faunalia.it) with funding from    */
7261
/* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
7262
/*                   AMBIENTALE                                         */
7263
/************************************************************************/
7264
7265
/**
7266
 * \brief Polygonizes a set of sparse edges.
7267
 *
7268
 * A new geometry object is created and returned containing a collection
7269
 * of reassembled Polygons: NULL will be returned if the input collection
7270
 * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7271
 * into Polygons is impossible due to topological inconsistencies.
7272
 *
7273
 * This method is the same as the C function OGR_G_Polygonize().
7274
 *
7275
 * This method is built on the GEOS library, check it for the definition
7276
 * of the geometry operation.
7277
 * If OGR is built without the GEOS library, this method will always fail,
7278
 * issuing a CPLE_NotSupported error.
7279
 *
7280
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
7281
 *
7282
 */
7283
7284
OGRGeometry *OGRGeometry::Polygonize() const
7285
7286
0
{
7287
0
#ifndef HAVE_GEOS
7288
7289
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7290
0
    return nullptr;
7291
7292
#else
7293
7294
    const OGRGeometryCollection *poColl = nullptr;
7295
    if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
7296
        wkbFlatten(getGeometryType()) == wkbMultiLineString)
7297
        poColl = toGeometryCollection();
7298
    else
7299
        return nullptr;
7300
7301
    const int nCount = poColl->getNumGeometries();
7302
7303
    OGRGeometry *poPolygsOGRGeom = nullptr;
7304
    bool bError = false;
7305
7306
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7307
7308
    GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
7309
    for (int ig = 0; ig < nCount; ig++)
7310
    {
7311
        GEOSGeom hGeosGeom = nullptr;
7312
        const OGRGeometry *poChild = poColl->getGeometryRef(ig);
7313
        if (poChild == nullptr ||
7314
            wkbFlatten(poChild->getGeometryType()) != wkbLineString)
7315
            bError = true;
7316
        else
7317
        {
7318
            hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
7319
            if (hGeosGeom == nullptr)
7320
                bError = true;
7321
        }
7322
        pahGeosGeomList[ig] = hGeosGeom;
7323
    }
7324
7325
    if (!bError)
7326
    {
7327
        GEOSGeom hGeosPolygs =
7328
            GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
7329
7330
        poPolygsOGRGeom =
7331
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7332
    }
7333
7334
    for (int ig = 0; ig < nCount; ig++)
7335
    {
7336
        GEOSGeom hGeosGeom = pahGeosGeomList[ig];
7337
        if (hGeosGeom != nullptr)
7338
            GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
7339
    }
7340
    delete[] pahGeosGeomList;
7341
    freeGEOSContext(hGEOSCtxt);
7342
7343
    return poPolygsOGRGeom;
7344
7345
#endif  // HAVE_GEOS
7346
0
}
7347
7348
/************************************************************************/
7349
/*                          OGR_G_Polygonize()                          */
7350
/************************************************************************/
7351
/**
7352
 * \brief Polygonizes a set of sparse edges.
7353
 *
7354
 * A new geometry object is created and returned containing a collection
7355
 * of reassembled Polygons: NULL will be returned if the input collection
7356
 * doesn't corresponds to a MultiLinestring, or when reassembling Edges
7357
 * into Polygons is impossible due to topological inconsistencies.
7358
 *
7359
 * This function is the same as the C++ method OGRGeometry::Polygonize().
7360
 *
7361
 * This function is built on the GEOS library, check it for the definition
7362
 * of the geometry operation.
7363
 * If OGR is built without the GEOS library, this function will always fail,
7364
 * issuing a CPLE_NotSupported error.
7365
 *
7366
 * @param hTarget The Geometry to be polygonized.
7367
 *
7368
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
7369
 * or NULL if an error occurs.
7370
 *
7371
 */
7372
7373
OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
7374
7375
0
{
7376
0
    VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
7377
7378
0
    return OGRGeometry::ToHandle(
7379
0
        OGRGeometry::FromHandle(hTarget)->Polygonize());
7380
0
}
7381
7382
/************************************************************************/
7383
/*                             BuildArea()                              */
7384
/************************************************************************/
7385
7386
/**
7387
 * \brief Polygonize a linework assuming inner polygons are holes.
7388
 *
7389
 * This method is the same as the C function OGR_G_BuildArea().
7390
 *
7391
 * Polygonization is performed similarly to OGRGeometry::Polygonize().
7392
 * Additionally, holes are dropped and the result is unified producing
7393
 * a single Polygon or a MultiPolygon.
7394
 *
7395
 * A new geometry object is created and returned: NULL on failure,
7396
 * empty GeometryCollection if the input geometry cannot be polygonized,
7397
 * Polygon or MultiPolygon on success.
7398
 *
7399
 * This method is built on the GEOSBuildArea_r() function of the GEOS
7400
 * library, check it for the definition of the geometry operation.
7401
 * If OGR is built without the GEOS library, this method will always fail,
7402
 * issuing a CPLE_NotSupported error.
7403
 *
7404
 * @return a newly allocated geometry now owned by the caller,
7405
 *         or NULL on failure.
7406
 *
7407
 * @since OGR 3.11
7408
 */
7409
7410
OGRGeometry *OGRGeometry::BuildArea() const
7411
7412
0
{
7413
0
#ifndef HAVE_GEOS
7414
7415
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
7416
0
    return nullptr;
7417
7418
#else
7419
7420
    OGRGeometry *poPolygsOGRGeom = nullptr;
7421
7422
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
7423
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
7424
    if (hThisGeosGeom != nullptr)
7425
    {
7426
        GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
7427
        poPolygsOGRGeom =
7428
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
7429
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
7430
    }
7431
    freeGEOSContext(hGEOSCtxt);
7432
7433
    return poPolygsOGRGeom;
7434
7435
#endif  // HAVE_GEOS
7436
0
}
7437
7438
/************************************************************************/
7439
/*                          OGR_G_BuildArea()                           */
7440
/************************************************************************/
7441
7442
/**
7443
 * \brief Polygonize a linework assuming inner polygons are holes.
7444
 *
7445
 * This function is the same as the C++ method OGRGeometry::BuildArea().
7446
 *
7447
 * Polygonization is performed similarly to OGR_G_Polygonize().
7448
 * Additionally, holes are dropped and the result is unified producing
7449
 * a single Polygon or a MultiPolygon.
7450
 *
7451
 * A new geometry object is created and returned: NULL on failure,
7452
 * empty GeometryCollection if the input geometry cannot be polygonized,
7453
 * Polygon or MultiPolygon on success.
7454
 *
7455
 * This function is built on the GEOSBuildArea_r() function of the GEOS
7456
 * library, check it for the definition of the geometry operation.
7457
 * If OGR is built without the GEOS library, this function will always fail,
7458
 * issuing a CPLE_NotSupported error.
7459
 *
7460
 * @param hGeom handle on the geometry to polygonize.
7461
 *
7462
 * @return a handle on newly allocated geometry now owned by the caller,
7463
 *         or NULL on failure.
7464
 *
7465
 * @since OGR 3.11
7466
 */
7467
7468
OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
7469
7470
0
{
7471
0
    VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
7472
7473
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
7474
0
}
7475
7476
/************************************************************************/
7477
/*                               swapXY()                               */
7478
/************************************************************************/
7479
7480
/**
7481
 * \brief Swap x and y coordinates.
7482
 *
7483
 */
7484
7485
void OGRGeometry::swapXY()
7486
7487
0
{
7488
0
}
7489
7490
/************************************************************************/
7491
/*                               swapXY()                               */
7492
/************************************************************************/
7493
7494
/**
7495
 * \brief Swap x and y coordinates.
7496
 *
7497
 * @param hGeom geometry.
7498
 */
7499
7500
void OGR_G_SwapXY(OGRGeometryH hGeom)
7501
0
{
7502
0
    VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7503
7504
0
    OGRGeometry::FromHandle(hGeom)->swapXY();
7505
0
}
7506
7507
/************************************************************************/
7508
/*                        Prepared geometry API                         */
7509
/************************************************************************/
7510
7511
#if defined(HAVE_GEOS)
7512
struct _OGRPreparedGeometry
7513
{
7514
    GEOSContextHandle_t hGEOSCtxt;
7515
    GEOSGeom hGEOSGeom;
7516
    const GEOSPreparedGeometry *poPreparedGEOSGeom;
7517
};
7518
#endif
7519
7520
/************************************************************************/
7521
/*                   OGRHasPreparedGeometrySupport()                    */
7522
/************************************************************************/
7523
7524
/** Returns if GEOS has prepared geometry support.
7525
 * @return TRUE or FALSE
7526
 */
7527
int OGRHasPreparedGeometrySupport()
7528
0
{
7529
#if defined(HAVE_GEOS)
7530
    return TRUE;
7531
#else
7532
0
    return FALSE;
7533
0
#endif
7534
0
}
7535
7536
/************************************************************************/
7537
/*                     OGRCreatePreparedGeometry()                      */
7538
/************************************************************************/
7539
7540
/** Creates a prepared geometry.
7541
 *
7542
 * To free with OGRDestroyPreparedGeometry()
7543
 *
7544
 * @param hGeom input geometry to prepare.
7545
 * @return handle to a prepared geometry.
7546
 * @since GDAL 3.3
7547
 */
7548
OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
7549
3.01k
{
7550
3.01k
    (void)hGeom;
7551
#if defined(HAVE_GEOS)
7552
    OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7553
    GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7554
    GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7555
    if (hGEOSGeom == nullptr)
7556
    {
7557
        OGRGeometry::freeGEOSContext(hGEOSCtxt);
7558
        return nullptr;
7559
    }
7560
    const GEOSPreparedGeometry *poPreparedGEOSGeom =
7561
        GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7562
    if (poPreparedGEOSGeom == nullptr)
7563
    {
7564
        GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7565
        OGRGeometry::freeGEOSContext(hGEOSCtxt);
7566
        return nullptr;
7567
    }
7568
7569
    OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7570
    poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7571
    poPreparedGeom->hGEOSGeom = hGEOSGeom;
7572
    poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7573
7574
    return poPreparedGeom;
7575
#else
7576
3.01k
    return nullptr;
7577
3.01k
#endif
7578
3.01k
}
7579
7580
/************************************************************************/
7581
/*                     OGRDestroyPreparedGeometry()                     */
7582
/************************************************************************/
7583
7584
/** Destroys a prepared geometry.
7585
 * @param hPreparedGeom prepared geometry.
7586
 * @since GDAL 3.3
7587
 */
7588
void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
7589
0
{
7590
0
    (void)hPreparedGeom;
7591
#if defined(HAVE_GEOS)
7592
    if (hPreparedGeom != nullptr)
7593
    {
7594
        GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7595
                                   hPreparedGeom->poPreparedGEOSGeom);
7596
        GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7597
        OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7598
        delete hPreparedGeom;
7599
    }
7600
#endif
7601
0
}
7602
7603
/************************************************************************/
7604
/*                   OGRPreparedGeometryIntersects()                    */
7605
/************************************************************************/
7606
7607
/** Returns whether a prepared geometry intersects with a geometry.
7608
 * @param hPreparedGeom prepared geometry.
7609
 * @param hOtherGeom other geometry.
7610
 * @return TRUE or FALSE.
7611
 * @since GDAL 3.3
7612
 */
7613
int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
7614
                                  const OGRGeometryH hOtherGeom)
7615
0
{
7616
0
    (void)hPreparedGeom;
7617
0
    (void)hOtherGeom;
7618
#if defined(HAVE_GEOS)
7619
    OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7620
    if (hPreparedGeom == nullptr ||
7621
        poOtherGeom == nullptr
7622
        // The check for IsEmpty() is for buggy GEOS versions.
7623
        // See https://github.com/libgeos/geos/pull/423
7624
        || poOtherGeom->IsEmpty())
7625
    {
7626
        return FALSE;
7627
    }
7628
7629
    GEOSGeom hGEOSOtherGeom =
7630
        poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7631
    if (hGEOSOtherGeom == nullptr)
7632
        return FALSE;
7633
7634
    const bool bRet =
7635
        GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
7636
                                 hPreparedGeom->poPreparedGEOSGeom,
7637
                                 hGEOSOtherGeom) == 1;
7638
    GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7639
7640
    return bRet;
7641
#else
7642
0
    return FALSE;
7643
0
#endif
7644
0
}
7645
7646
/** Returns whether a prepared geometry contains a geometry.
7647
 * @param hPreparedGeom prepared geometry.
7648
 * @param hOtherGeom other geometry.
7649
 * @return TRUE or FALSE.
7650
 */
7651
int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
7652
                                const OGRGeometryH hOtherGeom)
7653
0
{
7654
0
    (void)hPreparedGeom;
7655
0
    (void)hOtherGeom;
7656
#if defined(HAVE_GEOS)
7657
    OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7658
    if (hPreparedGeom == nullptr ||
7659
        poOtherGeom == nullptr
7660
        // The check for IsEmpty() is for buggy GEOS versions.
7661
        // See https://github.com/libgeos/geos/pull/423
7662
        || poOtherGeom->IsEmpty())
7663
    {
7664
        return FALSE;
7665
    }
7666
7667
    GEOSGeom hGEOSOtherGeom =
7668
        poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7669
    if (hGEOSOtherGeom == nullptr)
7670
        return FALSE;
7671
7672
    const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
7673
                                             hPreparedGeom->poPreparedGEOSGeom,
7674
                                             hGEOSOtherGeom) == 1;
7675
    GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7676
7677
    return bRet;
7678
#else
7679
0
    return FALSE;
7680
0
#endif
7681
0
}
7682
7683
/************************************************************************/
7684
/*                        OGRGeometryFromEWKB()                         */
7685
/************************************************************************/
7686
7687
OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7688
                                 int bIsPostGIS1_EWKB)
7689
7690
2.87M
{
7691
2.87M
    OGRGeometry *poGeometry = nullptr;
7692
7693
2.87M
    size_t nWKBSize = 0;
7694
2.87M
    const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7695
2.87M
    if (pabyWKB == nullptr)
7696
984k
        return nullptr;
7697
7698
    /* -------------------------------------------------------------------- */
7699
    /*      Try to ingest the geometry.                                     */
7700
    /* -------------------------------------------------------------------- */
7701
1.89M
    (void)OGRGeometryFactory::createFromWkb(
7702
1.89M
        pabyWKB, nullptr, &poGeometry, nWKBSize,
7703
1.89M
        (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7704
7705
1.89M
    return poGeometry;
7706
2.87M
}
7707
7708
/************************************************************************/
7709
/*                       OGRGeometryFromHexEWKB()                       */
7710
/************************************************************************/
7711
7712
OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7713
                                    int bIsPostGIS1_EWKB)
7714
7715
2.87M
{
7716
2.87M
    if (pszBytea == nullptr)
7717
0
        return nullptr;
7718
7719
2.87M
    int nWKBLength = 0;
7720
2.87M
    GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7721
7722
2.87M
    OGRGeometry *poGeometry =
7723
2.87M
        OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7724
7725
2.87M
    CPLFree(pabyWKB);
7726
7727
2.87M
    return poGeometry;
7728
2.87M
}
7729
7730
/************************************************************************/
7731
/*                        OGRGeometryToHexEWKB()                        */
7732
/************************************************************************/
7733
7734
char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
7735
                           int nPostGISMajor, int nPostGISMinor)
7736
8.57k
{
7737
8.57k
    const size_t nWkbSize = poGeometry->WkbSize();
7738
8.57k
    GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7739
8.57k
    if (pabyWKB == nullptr)
7740
0
        return CPLStrdup("");
7741
7742
8.57k
    if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7743
8.57k
        wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7744
1.72k
        poGeometry->IsEmpty())
7745
0
    {
7746
0
        if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7747
0
            OGRERR_NONE)
7748
0
        {
7749
0
            CPLFree(pabyWKB);
7750
0
            return CPLStrdup("");
7751
0
        }
7752
0
    }
7753
8.57k
    else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7754
8.57k
                                     (nPostGISMajor < 2)
7755
8.57k
                                         ? wkbVariantPostGIS1
7756
8.57k
                                         : wkbVariantOldOgc) != OGRERR_NONE)
7757
0
    {
7758
0
        CPLFree(pabyWKB);
7759
0
        return CPLStrdup("");
7760
0
    }
7761
7762
    // When converting to hex, each byte takes 2 hex characters.  In addition
7763
    // we add in 8 characters to represent the SRID integer in hex, and
7764
    // one for a null terminator.
7765
    // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7766
    // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7767
    // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7768
8.57k
    if (nWkbSize >
7769
8.57k
        static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7770
0
    {
7771
0
        CPLFree(pabyWKB);
7772
0
        return CPLStrdup("");
7773
0
    }
7774
8.57k
    const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7775
8.57k
    char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7776
8.57k
    if (pszTextBuf == nullptr)
7777
0
    {
7778
0
        CPLFree(pabyWKB);
7779
0
        return CPLStrdup("");
7780
0
    }
7781
8.57k
    char *pszTextBufCurrent = pszTextBuf;
7782
7783
    // Convert the 1st byte, which is the endianness flag, to hex.
7784
8.57k
    char *pszHex = CPLBinaryToHex(1, pabyWKB);
7785
8.57k
    strcpy(pszTextBufCurrent, pszHex);
7786
8.57k
    CPLFree(pszHex);
7787
8.57k
    pszTextBufCurrent += 2;
7788
7789
    // Next, get the geom type which is bytes 2 through 5.
7790
8.57k
    GUInt32 geomType;
7791
8.57k
    memcpy(&geomType, pabyWKB + 1, 4);
7792
7793
    // Now add the SRID flag if an SRID is provided.
7794
8.57k
    if (nSRSId > 0)
7795
416
    {
7796
        // Change the flag to wkbNDR (little) endianness.
7797
416
        constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7798
416
        GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7799
        // Apply the flag.
7800
416
        geomType = geomType | nGSrsFlag;
7801
416
    }
7802
7803
    // Now write the geom type which is 4 bytes.
7804
8.57k
    pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7805
8.57k
    strcpy(pszTextBufCurrent, pszHex);
7806
8.57k
    CPLFree(pszHex);
7807
8.57k
    pszTextBufCurrent += 8;
7808
7809
    // Now include SRID if provided.
7810
8.57k
    if (nSRSId > 0)
7811
416
    {
7812
        // Force the srsid to wkbNDR (little) endianness.
7813
416
        const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7814
416
        pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7815
416
                                reinterpret_cast<const GByte *>(&nGSRSId));
7816
416
        strcpy(pszTextBufCurrent, pszHex);
7817
416
        CPLFree(pszHex);
7818
416
        pszTextBufCurrent += 8;
7819
416
    }
7820
7821
    // Copy the rest of the data over - subtract
7822
    // 5 since we already copied 5 bytes above.
7823
8.57k
    pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7824
8.57k
    CPLFree(pabyWKB);
7825
8.57k
    if (!pszHex || pszHex[0] == 0)
7826
0
    {
7827
0
        CPLFree(pszTextBuf);
7828
0
        return pszHex;
7829
0
    }
7830
8.57k
    strcpy(pszTextBufCurrent, pszHex);
7831
8.57k
    CPLFree(pszHex);
7832
7833
8.57k
    return pszTextBuf;
7834
8.57k
}
7835
7836
/************************************************************************/
7837
/*                       importPreambleFromWkb()                        */
7838
/************************************************************************/
7839
7840
//! @cond Doxygen_Suppress
7841
OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7842
                                          size_t nSize,
7843
                                          OGRwkbByteOrder &eByteOrder,
7844
                                          OGRwkbVariant eWkbVariant)
7845
1.66M
{
7846
1.66M
    if (nSize < 9 && nSize != static_cast<size_t>(-1))
7847
0
        return OGRERR_NOT_ENOUGH_DATA;
7848
7849
    /* -------------------------------------------------------------------- */
7850
    /*      Get the byte order byte.                                        */
7851
    /* -------------------------------------------------------------------- */
7852
1.66M
    int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7853
1.66M
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7854
0
        return OGRERR_CORRUPT_DATA;
7855
1.66M
    eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7856
7857
    /* -------------------------------------------------------------------- */
7858
    /*      Get the geometry feature type.                                  */
7859
    /* -------------------------------------------------------------------- */
7860
1.66M
    OGRwkbGeometryType eGeometryType;
7861
1.66M
    const OGRErr err =
7862
1.66M
        OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7863
1.66M
    if (wkbHasZ(eGeometryType))
7864
539k
        flags |= OGR_G_3D;
7865
1.66M
    if (wkbHasM(eGeometryType))
7866
843k
        flags |= OGR_G_MEASURED;
7867
7868
1.66M
    if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7869
0
        return OGRERR_CORRUPT_DATA;
7870
7871
1.66M
    return OGRERR_NONE;
7872
1.66M
}
7873
7874
/************************************************************************/
7875
/*                    importPreambleOfCollectionFromWkb()              */
7876
/*                                                                      */
7877
/*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
7878
/*      OGRCurvePolygon and OGRGeometryCollection.                      */
7879
/************************************************************************/
7880
7881
OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7882
    const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7883
    OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7884
    OGRwkbVariant eWkbVariant)
7885
1.51M
{
7886
1.51M
    nGeomCount = 0;
7887
7888
1.51M
    OGRErr eErr =
7889
1.51M
        importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7890
1.51M
    if (eErr != OGRERR_NONE)
7891
0
        return eErr;
7892
7893
    /* -------------------------------------------------------------------- */
7894
    /*      Clear existing Geoms.                                           */
7895
    /* -------------------------------------------------------------------- */
7896
1.51M
    int _flags = flags;  // flags set in importPreambleFromWkb
7897
1.51M
    empty();             // may reset flags etc.
7898
7899
    // restore
7900
1.51M
    if (_flags & OGR_G_3D)
7901
504k
        set3D(TRUE);
7902
1.51M
    if (_flags & OGR_G_MEASURED)
7903
806k
        setMeasured(TRUE);
7904
7905
    /* -------------------------------------------------------------------- */
7906
    /*      Get the sub-geometry count.                                     */
7907
    /* -------------------------------------------------------------------- */
7908
1.51M
    memcpy(&nGeomCount, pabyData + 5, 4);
7909
7910
1.51M
    if (OGR_SWAP(eByteOrder))
7911
1.48M
        nGeomCount = CPL_SWAP32(nGeomCount);
7912
7913
1.51M
    if (nGeomCount < 0 ||
7914
1.49M
        static_cast<size_t>(nGeomCount) >
7915
1.49M
            std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7916
20.5k
    {
7917
20.5k
        nGeomCount = 0;
7918
20.5k
        return OGRERR_CORRUPT_DATA;
7919
20.5k
    }
7920
1.49M
    const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7921
7922
    // Each ring has a minimum of nMinSubGeomSize bytes.
7923
1.49M
    if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7924
146k
    {
7925
146k
        CPLError(CE_Failure, CPLE_AppDefined,
7926
146k
                 "Length of input WKB is too small");
7927
146k
        nGeomCount = 0;
7928
146k
        return OGRERR_NOT_ENOUGH_DATA;
7929
146k
    }
7930
7931
1.34M
    nDataOffset = 9;
7932
1.34M
    if (nSize != static_cast<size_t>(-1))
7933
1.34M
    {
7934
1.34M
        CPLAssert(nSize >= nDataOffset);
7935
1.34M
        nSize -= nDataOffset;
7936
1.34M
    }
7937
7938
1.34M
    return OGRERR_NONE;
7939
1.49M
}
7940
7941
/************************************************************************/
7942
/*                      importCurveCollectionFromWkt()                  */
7943
/*                                                                      */
7944
/*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
7945
/*      OGRMultiCurve.                                                  */
7946
/************************************************************************/
7947
7948
OGRErr OGRGeometry::importCurveCollectionFromWkt(
7949
    const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7950
    int bAllowCurve, int bAllowCompoundCurve,
7951
    OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7952
7953
139k
{
7954
139k
    int bHasZ = FALSE;
7955
139k
    int bHasM = FALSE;
7956
139k
    bool bIsEmpty = false;
7957
139k
    OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7958
139k
    flags = 0;
7959
139k
    if (eErr != OGRERR_NONE)
7960
18.8k
        return eErr;
7961
120k
    if (bHasZ)
7962
6.89k
        flags |= OGR_G_3D;
7963
120k
    if (bHasM)
7964
18.6k
        flags |= OGR_G_MEASURED;
7965
120k
    if (bIsEmpty)
7966
7.07k
        return OGRERR_NONE;
7967
7968
113k
    char szToken[OGR_WKT_TOKEN_MAX];
7969
113k
    const char *pszInput = *ppszInput;
7970
113k
    eErr = OGRERR_NONE;
7971
7972
    // Skip first '('.
7973
113k
    pszInput = OGRWktReadToken(pszInput, szToken);
7974
7975
    /* ==================================================================== */
7976
    /*      Read each curve in turn.  Note that we try to reuse the same    */
7977
    /*      point list buffer from curve to curve to cut down on            */
7978
    /*      allocate/deallocate overhead.                                   */
7979
    /* ==================================================================== */
7980
113k
    OGRRawPoint *paoPoints = nullptr;
7981
113k
    int nMaxPoints = 0;
7982
113k
    double *padfZ = nullptr;
7983
7984
113k
    do
7985
163k
    {
7986
7987
        /* --------------------------------------------------------------------
7988
         */
7989
        /*      Get the first token, which should be the geometry type. */
7990
        /* --------------------------------------------------------------------
7991
         */
7992
163k
        const char *pszInputBefore = pszInput;
7993
163k
        pszInput = OGRWktReadToken(pszInput, szToken);
7994
7995
        /* --------------------------------------------------------------------
7996
         */
7997
        /*      Do the import. */
7998
        /* --------------------------------------------------------------------
7999
         */
8000
163k
        OGRCurve *poCurve = nullptr;
8001
163k
        if (EQUAL(szToken, "("))
8002
87.0k
        {
8003
87.0k
            OGRLineString *poLine = new OGRLineString();
8004
87.0k
            poCurve = poLine;
8005
87.0k
            pszInput = pszInputBefore;
8006
87.0k
            eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
8007
87.0k
                                                 paoPoints, nMaxPoints, padfZ);
8008
87.0k
        }
8009
76.2k
        else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
8010
1.16k
        {
8011
1.16k
            poCurve = new OGRLineString();
8012
1.16k
        }
8013
        // Accept LINESTRING(), but this is an extension to the BNF, also
8014
        // accepted by PostGIS.
8015
75.0k
        else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
8016
68.8k
                 (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
8017
68.8k
                  !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
8018
56.9k
                  OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
8019
56.8k
                 (bAllowCompoundCurve &&
8020
56.8k
                  STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
8021
28.3k
        {
8022
28.3k
            OGRGeometry *poGeom = nullptr;
8023
28.3k
            pszInput = pszInputBefore;
8024
28.3k
            eErr =
8025
28.3k
                OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
8026
28.3k
            if (poGeom == nullptr)
8027
18.2k
            {
8028
18.2k
                eErr = OGRERR_CORRUPT_DATA;
8029
18.2k
            }
8030
10.1k
            else
8031
10.1k
            {
8032
10.1k
                poCurve = poGeom->toCurve();
8033
10.1k
            }
8034
28.3k
        }
8035
46.6k
        else
8036
46.6k
        {
8037
46.6k
            CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
8038
46.6k
                     szToken);
8039
46.6k
            eErr = OGRERR_CORRUPT_DATA;
8040
46.6k
        }
8041
8042
        // If this has M it is an error if poGeom does not have M.
8043
163k
        if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
8044
9.19k
            eErr = OGRERR_CORRUPT_DATA;
8045
8046
163k
        if (eErr == OGRERR_NONE)
8047
70.4k
            eErr = pfnAddCurveDirectly(this, poCurve);
8048
163k
        if (eErr != OGRERR_NONE)
8049
100k
        {
8050
100k
            delete poCurve;
8051
100k
            break;
8052
100k
        }
8053
8054
        /* --------------------------------------------------------------------
8055
         */
8056
        /*      Read the delimiter following the surface. */
8057
        /* --------------------------------------------------------------------
8058
         */
8059
62.4k
        pszInput = OGRWktReadToken(pszInput, szToken);
8060
62.4k
    } while (szToken[0] == ',' && eErr == OGRERR_NONE);
8061
8062
113k
    CPLFree(paoPoints);
8063
113k
    CPLFree(padfZ);
8064
8065
    /* -------------------------------------------------------------------- */
8066
    /*      freak if we don't get a closing bracket.                        */
8067
    /* -------------------------------------------------------------------- */
8068
8069
113k
    if (eErr != OGRERR_NONE)
8070
100k
        return eErr;
8071
8072
12.4k
    if (szToken[0] != ')')
8073
8.47k
        return OGRERR_CORRUPT_DATA;
8074
8075
3.94k
    *ppszInput = pszInput;
8076
3.94k
    return OGRERR_NONE;
8077
12.4k
}
8078
8079
//! @endcond
8080
8081
/************************************************************************/
8082
/*                           OGR_GT_Flatten()                           */
8083
/************************************************************************/
8084
/**
8085
 * \brief Returns the 2D geometry type corresponding to the passed geometry
8086
 * type.
8087
 *
8088
 * This function is intended to work with geometry types as old-style 99-402
8089
 * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
8090
 * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
8091
 *
8092
 * @param eType Input geometry type
8093
 *
8094
 * @return 2D geometry type corresponding to the passed geometry type.
8095
 *
8096
 */
8097
8098
OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
8099
32.1M
{
8100
32.1M
    eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
8101
32.1M
    if (eType >= 1000 && eType < 2000)  // ISO Z.
8102
401k
        return static_cast<OGRwkbGeometryType>(eType - 1000);
8103
31.7M
    if (eType >= 2000 && eType < 3000)  // ISO M.
8104
1.89M
        return static_cast<OGRwkbGeometryType>(eType - 2000);
8105
29.8M
    if (eType >= 3000 && eType < 4000)  // ISO ZM.
8106
5.41M
        return static_cast<OGRwkbGeometryType>(eType - 3000);
8107
24.4M
    return eType;
8108
29.8M
}
8109
8110
/************************************************************************/
8111
/*                            OGR_GT_HasZ()                             */
8112
/************************************************************************/
8113
/**
8114
 * \brief Return if the geometry type is a 3D geometry type.
8115
 *
8116
 * @param eType Input geometry type
8117
 *
8118
 * @return TRUE if the geometry type is a 3D geometry type.
8119
 *
8120
 */
8121
8122
int OGR_GT_HasZ(OGRwkbGeometryType eType)
8123
9.81M
{
8124
9.81M
    if (eType & wkb25DBitInternalUse)
8125
3.25M
        return TRUE;
8126
6.55M
    if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
8127
244k
        return TRUE;
8128
6.31M
    if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
8129
2.07M
        return TRUE;
8130
4.23M
    return FALSE;
8131
6.31M
}
8132
8133
/************************************************************************/
8134
/*                            OGR_GT_HasM()                             */
8135
/************************************************************************/
8136
/**
8137
 * \brief Return if the geometry type is a measured type.
8138
 *
8139
 * @param eType Input geometry type
8140
 *
8141
 * @return TRUE if the geometry type is a measured type.
8142
 *
8143
 */
8144
8145
int OGR_GT_HasM(OGRwkbGeometryType eType)
8146
5.61M
{
8147
5.61M
    if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
8148
1.39M
        return TRUE;
8149
4.21M
    if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
8150
1.27M
        return TRUE;
8151
2.94M
    return FALSE;
8152
4.21M
}
8153
8154
/************************************************************************/
8155
/*                            OGR_GT_SetZ()                             */
8156
/************************************************************************/
8157
/**
8158
 * \brief Returns the 3D geometry type corresponding to the passed geometry
8159
 * type.
8160
 *
8161
 * @param eType Input geometry type
8162
 *
8163
 * @return 3D geometry type corresponding to the passed geometry type.
8164
 *
8165
 */
8166
8167
OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
8168
351k
{
8169
351k
    if (OGR_GT_HasZ(eType) || eType == wkbNone)
8170
11.7k
        return eType;
8171
339k
    if (eType <= wkbGeometryCollection)
8172
334k
        return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
8173
5.57k
    else
8174
5.57k
        return static_cast<OGRwkbGeometryType>(eType + 1000);
8175
339k
}
8176
8177
/************************************************************************/
8178
/*                            OGR_GT_SetM()                             */
8179
/************************************************************************/
8180
/**
8181
 * \brief Returns the measured geometry type corresponding to the passed
8182
 * geometry type.
8183
 *
8184
 * @param eType Input geometry type
8185
 *
8186
 * @return measured geometry type corresponding to the passed geometry type.
8187
 *
8188
 */
8189
8190
OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
8191
344k
{
8192
344k
    if (OGR_GT_HasM(eType) || eType == wkbNone)
8193
24.8k
        return eType;
8194
319k
    if (eType & wkb25DBitInternalUse)
8195
299k
    {
8196
299k
        eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
8197
299k
        eType = static_cast<OGRwkbGeometryType>(eType + 1000);
8198
299k
    }
8199
319k
    return static_cast<OGRwkbGeometryType>(eType + 2000);
8200
344k
}
8201
8202
/************************************************************************/
8203
/*                         OGR_GT_SetModifier()                         */
8204
/************************************************************************/
8205
/**
8206
 * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
8207
 *
8208
 * @param eType Input geometry type
8209
 * @param bHasZ TRUE if the output geometry type must be 3D.
8210
 * @param bHasM TRUE if the output geometry type must be measured.
8211
 *
8212
 * @return Output geometry type.
8213
 *
8214
 */
8215
8216
OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
8217
                                      int bHasM)
8218
5.19k
{
8219
5.19k
    if (bHasZ && bHasM)
8220
0
        return OGR_GT_SetM(OGR_GT_SetZ(eType));
8221
5.19k
    else if (bHasM)
8222
0
        return OGR_GT_SetM(wkbFlatten(eType));
8223
5.19k
    else if (bHasZ)
8224
156
        return OGR_GT_SetZ(wkbFlatten(eType));
8225
5.04k
    else
8226
5.04k
        return wkbFlatten(eType);
8227
5.19k
}
8228
8229
/************************************************************************/
8230
/*                         OGR_GT_IsSubClassOf)                         */
8231
/************************************************************************/
8232
/**
8233
 * \brief Returns if a type is a subclass of another one
8234
 *
8235
 * @param eType Type.
8236
 * @param eSuperType Super type
8237
 *
8238
 * @return TRUE if eType is a subclass of eSuperType.
8239
 *
8240
 */
8241
8242
int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
8243
3.71M
{
8244
3.71M
    eSuperType = wkbFlatten(eSuperType);
8245
3.71M
    eType = wkbFlatten(eType);
8246
8247
3.71M
    if (eSuperType == eType || eSuperType == wkbUnknown)
8248
128k
        return TRUE;
8249
8250
3.58M
    if (eSuperType == wkbGeometryCollection)
8251
1.24M
        return eType == wkbMultiPoint || eType == wkbMultiLineString ||
8252
1.17M
               eType == wkbMultiPolygon || eType == wkbMultiCurve ||
8253
1.15M
               eType == wkbMultiSurface;
8254
8255
2.34M
    if (eSuperType == wkbCurvePolygon)
8256
393k
        return eType == wkbPolygon || eType == wkbTriangle;
8257
8258
1.95M
    if (eSuperType == wkbMultiCurve)
8259
7.21k
        return eType == wkbMultiLineString;
8260
8261
1.94M
    if (eSuperType == wkbMultiSurface)
8262
3.41k
        return eType == wkbMultiPolygon;
8263
8264
1.94M
    if (eSuperType == wkbCurve)
8265
1.36M
        return eType == wkbLineString || eType == wkbCircularString ||
8266
387k
               eType == wkbCompoundCurve;
8267
8268
583k
    if (eSuperType == wkbSurface)
8269
158k
        return eType == wkbCurvePolygon || eType == wkbPolygon ||
8270
102k
               eType == wkbTriangle || eType == wkbPolyhedralSurface ||
8271
95.3k
               eType == wkbTIN;
8272
8273
424k
    if (eSuperType == wkbPolygon)
8274
5.34k
        return eType == wkbTriangle;
8275
8276
419k
    if (eSuperType == wkbPolyhedralSurface)
8277
223k
        return eType == wkbTIN;
8278
8279
195k
    return FALSE;
8280
419k
}
8281
8282
/************************************************************************/
8283
/*                        OGR_GT_GetCollection()                        */
8284
/************************************************************************/
8285
/**
8286
 * \brief Returns the collection type that can contain the passed geometry type
8287
 *
8288
 * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
8289
 * wkbLineString->wkbMultiLineString,
8290
 * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
8291
 * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
8292
 * wkbCurvePolygon->wkbMultiSurface.
8293
 * In other cases, wkbUnknown is returned
8294
 *
8295
 * Passed Z, M, ZM flag is preserved.
8296
 *
8297
 *
8298
 * @param eType Input geometry type
8299
 *
8300
 * @return the collection type that can contain the passed geometry type or
8301
 * wkbUnknown
8302
 *
8303
 */
8304
8305
OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
8306
188k
{
8307
188k
    const bool bHasZ = wkbHasZ(eType);
8308
188k
    const bool bHasM = wkbHasM(eType);
8309
188k
    if (eType == wkbNone)
8310
0
        return wkbNone;
8311
188k
    OGRwkbGeometryType eFGType = wkbFlatten(eType);
8312
188k
    if (eFGType == wkbPoint)
8313
1.70k
        eType = wkbMultiPoint;
8314
8315
186k
    else if (eFGType == wkbLineString)
8316
3.22k
        eType = wkbMultiLineString;
8317
8318
183k
    else if (eFGType == wkbPolygon)
8319
648
        eType = wkbMultiPolygon;
8320
8321
182k
    else if (eFGType == wkbTriangle)
8322
286
        eType = wkbTIN;
8323
8324
182k
    else if (OGR_GT_IsCurve(eFGType))
8325
179k
        eType = wkbMultiCurve;
8326
8327
3.20k
    else if (OGR_GT_IsSurface(eFGType))
8328
3.19k
        eType = wkbMultiSurface;
8329
8330
10
    else
8331
10
        return wkbUnknown;
8332
8333
188k
    if (bHasZ)
8334
129
        eType = wkbSetZ(eType);
8335
188k
    if (bHasM)
8336
22
        eType = wkbSetM(eType);
8337
8338
188k
    return eType;
8339
188k
}
8340
8341
/************************************************************************/
8342
/*                          OGR_GT_GetSingle()                          */
8343
/************************************************************************/
8344
/**
8345
 * \brief Returns the non-collection type that be contained in the passed
8346
 * geometry type.
8347
 *
8348
 * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
8349
 * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
8350
 * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
8351
 * wkbGeometryCollection -> wkbUnknown
8352
 * In other cases, the original geometry is returned.
8353
 *
8354
 * Passed Z, M, ZM flag is preserved.
8355
 *
8356
 *
8357
 * @param eType Input geometry type
8358
 *
8359
 * @return the the non-collection type that be contained in the passed geometry
8360
 * type or wkbUnknown
8361
 *
8362
 * @since GDAL 3.11
8363
 */
8364
8365
OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
8366
474
{
8367
474
    const bool bHasZ = wkbHasZ(eType);
8368
474
    const bool bHasM = wkbHasM(eType);
8369
474
    if (eType == wkbNone)
8370
0
        return wkbNone;
8371
474
    const OGRwkbGeometryType eFGType = wkbFlatten(eType);
8372
474
    if (eFGType == wkbMultiPoint)
8373
8
        eType = wkbPoint;
8374
8375
466
    else if (eFGType == wkbMultiLineString)
8376
0
        eType = wkbLineString;
8377
8378
466
    else if (eFGType == wkbMultiPolygon)
8379
422
        eType = wkbPolygon;
8380
8381
44
    else if (eFGType == wkbMultiCurve)
8382
0
        eType = wkbCompoundCurve;
8383
8384
44
    else if (eFGType == wkbMultiSurface)
8385
0
        eType = wkbCurvePolygon;
8386
8387
44
    else if (eFGType == wkbGeometryCollection)
8388
0
        return wkbUnknown;
8389
8390
474
    if (bHasZ)
8391
0
        eType = wkbSetZ(eType);
8392
474
    if (bHasM)
8393
0
        eType = wkbSetM(eType);
8394
8395
474
    return eType;
8396
474
}
8397
8398
/************************************************************************/
8399
/*                          OGR_GT_GetCurve()                           */
8400
/************************************************************************/
8401
/**
8402
 * \brief Returns the curve geometry type that can contain the passed geometry
8403
 * type
8404
 *
8405
 * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
8406
 * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
8407
 * and wkbMultiLineString->wkbMultiCurve.
8408
 * In other cases, the passed geometry is returned.
8409
 *
8410
 * Passed Z, M, ZM flag is preserved.
8411
 *
8412
 * @param eType Input geometry type
8413
 *
8414
 * @return the curve type that can contain the passed geometry type
8415
 *
8416
 */
8417
8418
OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
8419
0
{
8420
0
    const bool bHasZ = wkbHasZ(eType);
8421
0
    const bool bHasM = wkbHasM(eType);
8422
0
    OGRwkbGeometryType eFGType = wkbFlatten(eType);
8423
8424
0
    if (eFGType == wkbLineString)
8425
0
        eType = wkbCompoundCurve;
8426
8427
0
    else if (eFGType == wkbPolygon)
8428
0
        eType = wkbCurvePolygon;
8429
8430
0
    else if (eFGType == wkbTriangle)
8431
0
        eType = wkbCurvePolygon;
8432
8433
0
    else if (eFGType == wkbMultiLineString)
8434
0
        eType = wkbMultiCurve;
8435
8436
0
    else if (eFGType == wkbMultiPolygon)
8437
0
        eType = wkbMultiSurface;
8438
8439
0
    if (bHasZ)
8440
0
        eType = wkbSetZ(eType);
8441
0
    if (bHasM)
8442
0
        eType = wkbSetM(eType);
8443
8444
0
    return eType;
8445
0
}
8446
8447
/************************************************************************/
8448
/*                          OGR_GT_GetLinear()                          */
8449
/************************************************************************/
8450
/**
8451
 * \brief Returns the non-curve geometry type that can contain the passed
8452
 * geometry type
8453
 *
8454
 * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
8455
 * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
8456
 * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
8457
 * In other cases, the passed geometry is returned.
8458
 *
8459
 * Passed Z, M, ZM flag is preserved.
8460
 *
8461
 * @param eType Input geometry type
8462
 *
8463
 * @return the non-curve type that can contain the passed geometry type
8464
 *
8465
 */
8466
8467
OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
8468
499k
{
8469
499k
    const bool bHasZ = wkbHasZ(eType);
8470
499k
    const bool bHasM = wkbHasM(eType);
8471
499k
    OGRwkbGeometryType eFGType = wkbFlatten(eType);
8472
8473
499k
    if (OGR_GT_IsCurve(eFGType))
8474
344k
        eType = wkbLineString;
8475
8476
155k
    else if (OGR_GT_IsSurface(eFGType))
8477
61.1k
        eType = wkbPolygon;
8478
8479
93.9k
    else if (eFGType == wkbMultiCurve)
8480
53.7k
        eType = wkbMultiLineString;
8481
8482
40.2k
    else if (eFGType == wkbMultiSurface)
8483
2.40k
        eType = wkbMultiPolygon;
8484
8485
499k
    if (bHasZ)
8486
296k
        eType = wkbSetZ(eType);
8487
499k
    if (bHasM)
8488
302k
        eType = wkbSetM(eType);
8489
8490
499k
    return eType;
8491
499k
}
8492
8493
/************************************************************************/
8494
/*                           OGR_GT_IsCurve()                           */
8495
/************************************************************************/
8496
8497
/**
8498
 * \brief Return if a geometry type is an instance of Curve
8499
 *
8500
 * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8501
 * and their Z/M/ZM variant.
8502
 *
8503
 * @param eGeomType the geometry type
8504
 * @return TRUE if the geometry type is an instance of Curve
8505
 *
8506
 */
8507
8508
int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8509
1.36M
{
8510
1.36M
    return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8511
1.36M
}
8512
8513
/************************************************************************/
8514
/*                          OGR_GT_IsSurface()                          */
8515
/************************************************************************/
8516
8517
/**
8518
 * \brief Return if a geometry type is an instance of Surface
8519
 *
8520
 * Such geometry type are wkbCurvePolygon and wkbPolygon
8521
 * and their Z/M/ZM variant.
8522
 *
8523
 * @param eGeomType the geometry type
8524
 * @return TRUE if the geometry type is an instance of Surface
8525
 *
8526
 */
8527
8528
int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8529
158k
{
8530
158k
    return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8531
158k
}
8532
8533
/************************************************************************/
8534
/*                         OGR_GT_IsNonLinear()                         */
8535
/************************************************************************/
8536
8537
/**
8538
 * \brief Return if a geometry type is a non-linear geometry type.
8539
 *
8540
 * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8541
 * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8542
 * Z/M variants.
8543
 *
8544
 * @param eGeomType the geometry type
8545
 * @return TRUE if the geometry type is a non-linear geometry type.
8546
 *
8547
 */
8548
8549
int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8550
486k
{
8551
486k
    OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8552
486k
    return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8553
485k
           eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8554
469k
           eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8555
466k
           eFGeomType == wkbMultiSurface;
8556
486k
}
8557
8558
/************************************************************************/
8559
/*                            CastToError()                             */
8560
/************************************************************************/
8561
8562
//! @cond Doxygen_Suppress
8563
OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8564
0
{
8565
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8566
0
             poGeom->getGeometryName());
8567
0
    delete poGeom;
8568
0
    return nullptr;
8569
0
}
8570
8571
//! @endcond
8572
8573
/************************************************************************/
8574
/*                         OGRexportToSFCGAL()                          */
8575
/************************************************************************/
8576
8577
//! @cond Doxygen_Suppress
8578
sfcgal_geometry_t *
8579
OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8580
0
{
8581
#ifdef HAVE_SFCGAL
8582
8583
    sfcgal_init();
8584
#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8585
8586
    const auto exportToSFCGALViaWKB =
8587
        [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8588
    {
8589
        if (!geom)
8590
            return nullptr;
8591
8592
        // Get WKB size and allocate buffer
8593
        size_t nSize = geom->WkbSize();
8594
        unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8595
8596
        // Set export options with NDR byte order
8597
        OGRwkbExportOptions oOptions;
8598
        oOptions.eByteOrder = wkbNDR;
8599
        // and ISO to avoid wkb25DBit for Z geometries
8600
        oOptions.eWkbVariant = wkbVariantIso;
8601
8602
        // Export to WKB
8603
        sfcgal_geometry_t *sfcgalGeom = nullptr;
8604
        if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8605
        {
8606
            sfcgalGeom = sfcgal_io_read_wkb(
8607
                reinterpret_cast<const char *>(pabyWkb), nSize);
8608
        }
8609
8610
        CPLFree(pabyWkb);
8611
        return sfcgalGeom;
8612
    };
8613
8614
    // Handle special cases
8615
    if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8616
    {
8617
        std::unique_ptr<OGRLineString> poLS(
8618
            OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8619
        return exportToSFCGALViaWKB(poLS.get());
8620
    }
8621
    else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8622
             EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8623
    {
8624
        std::unique_ptr<OGRLineString> poLS(
8625
            OGRGeometryFactory::forceToLineString(poGeom->clone())
8626
                ->toLineString());
8627
        return exportToSFCGALViaWKB(poLS.get());
8628
    }
8629
    else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8630
    {
8631
        std::unique_ptr<OGRPolygon> poPolygon(
8632
            OGRGeometryFactory::forceToPolygon(
8633
                poGeom->clone()->toCurvePolygon())
8634
                ->toPolygon());
8635
        return exportToSFCGALViaWKB(poPolygon.get());
8636
    }
8637
    else
8638
    {
8639
        // Default case - direct export
8640
        return exportToSFCGALViaWKB(poGeom);
8641
    }
8642
#else
8643
    char *buffer = nullptr;
8644
8645
    // special cases - LinearRing, Circular String, Compound Curve, Curve
8646
    // Polygon
8647
8648
    if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8649
    {
8650
        // cast it to LineString and get the WKT
8651
        std::unique_ptr<OGRLineString> poLS(
8652
            OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8653
        if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8654
        {
8655
            sfcgal_geometry_t *_geometry =
8656
                sfcgal_io_read_wkt(buffer, strlen(buffer));
8657
            CPLFree(buffer);
8658
            return _geometry;
8659
        }
8660
        else
8661
        {
8662
            CPLFree(buffer);
8663
            return nullptr;
8664
        }
8665
    }
8666
    else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8667
             EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8668
    {
8669
        // convert it to LineString and get the WKT
8670
        std::unique_ptr<OGRLineString> poLS(
8671
            OGRGeometryFactory::forceToLineString(poGeom->clone())
8672
                ->toLineString());
8673
        if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8674
        {
8675
            sfcgal_geometry_t *_geometry =
8676
                sfcgal_io_read_wkt(buffer, strlen(buffer));
8677
            CPLFree(buffer);
8678
            return _geometry;
8679
        }
8680
        else
8681
        {
8682
            CPLFree(buffer);
8683
            return nullptr;
8684
        }
8685
    }
8686
    else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8687
    {
8688
        // convert it to Polygon and get the WKT
8689
        std::unique_ptr<OGRPolygon> poPolygon(
8690
            OGRGeometryFactory::forceToPolygon(
8691
                poGeom->clone()->toCurvePolygon())
8692
                ->toPolygon());
8693
        if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8694
        {
8695
            sfcgal_geometry_t *_geometry =
8696
                sfcgal_io_read_wkt(buffer, strlen(buffer));
8697
            CPLFree(buffer);
8698
            return _geometry;
8699
        }
8700
        else
8701
        {
8702
            CPLFree(buffer);
8703
            return nullptr;
8704
        }
8705
    }
8706
    else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8707
    {
8708
        sfcgal_geometry_t *_geometry =
8709
            sfcgal_io_read_wkt(buffer, strlen(buffer));
8710
        CPLFree(buffer);
8711
        return _geometry;
8712
    }
8713
    else
8714
    {
8715
        CPLFree(buffer);
8716
        return nullptr;
8717
    }
8718
#endif
8719
#else
8720
0
    CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8721
0
    return nullptr;
8722
0
#endif
8723
0
}
8724
8725
//! @endcond
8726
8727
/************************************************************************/
8728
/*                         SFCGALexportToOGR()                          */
8729
/************************************************************************/
8730
8731
//! @cond Doxygen_Suppress
8732
OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8733
    UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8734
0
{
8735
#ifdef HAVE_SFCGAL
8736
    if (geometry == nullptr)
8737
        return nullptr;
8738
8739
    sfcgal_init();
8740
    char *pabySFCGAL = nullptr;
8741
    size_t nLength = 0;
8742
#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
8743
8744
    sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8745
8746
    if (pabySFCGAL == nullptr || nLength == 0)
8747
        return nullptr;
8748
8749
    OGRGeometry *poGeom = nullptr;
8750
    OGRErr eErr = OGRGeometryFactory::createFromWkb(
8751
        reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8752
        nLength);
8753
8754
    free(pabySFCGAL);
8755
8756
    if (eErr == OGRERR_NONE)
8757
    {
8758
        return poGeom;
8759
    }
8760
    else
8761
    {
8762
        return nullptr;
8763
    }
8764
#else
8765
    sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8766
    char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8767
    memcpy(pszWKT, pabySFCGAL, nLength);
8768
    pszWKT[nLength] = 0;
8769
    free(pabySFCGAL);
8770
8771
    sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8772
8773
    OGRGeometry *poGeom = nullptr;
8774
    if (geom_type == SFCGAL_TYPE_POINT)
8775
    {
8776
        poGeom = new OGRPoint();
8777
    }
8778
    else if (geom_type == SFCGAL_TYPE_LINESTRING)
8779
    {
8780
        poGeom = new OGRLineString();
8781
    }
8782
    else if (geom_type == SFCGAL_TYPE_POLYGON)
8783
    {
8784
        poGeom = new OGRPolygon();
8785
    }
8786
    else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8787
    {
8788
        poGeom = new OGRMultiPoint();
8789
    }
8790
    else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8791
    {
8792
        poGeom = new OGRMultiLineString();
8793
    }
8794
    else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8795
    {
8796
        poGeom = new OGRMultiPolygon();
8797
    }
8798
    else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8799
    {
8800
        poGeom = new OGRGeometryCollection();
8801
    }
8802
    else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8803
    {
8804
        poGeom = new OGRTriangle();
8805
    }
8806
    else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8807
    {
8808
        poGeom = new OGRPolyhedralSurface();
8809
    }
8810
    else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8811
    {
8812
        poGeom = new OGRTriangulatedSurface();
8813
    }
8814
    else
8815
    {
8816
        CPLFree(pszWKT);
8817
        return nullptr;
8818
    }
8819
8820
    const char *pszWKTTmp = pszWKT;
8821
    if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8822
    {
8823
        CPLFree(pszWKT);
8824
        return poGeom;
8825
    }
8826
    else
8827
    {
8828
        delete poGeom;
8829
        CPLFree(pszWKT);
8830
        return nullptr;
8831
    }
8832
#endif
8833
#else
8834
0
    CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8835
0
    return nullptr;
8836
0
#endif
8837
0
}
8838
8839
//! @endcond
8840
8841
//! @cond Doxygen_Suppress
8842
bool OGRGeometry::IsSFCGALCompatible() const
8843
8
{
8844
8
    const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8845
8
    if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8846
8
        eGType == wkbTIN)
8847
0
    {
8848
0
        return TRUE;
8849
0
    }
8850
8
    if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8851
0
    {
8852
0
        const OGRGeometryCollection *poGC = toGeometryCollection();
8853
0
        bool bIsSFCGALCompatible = false;
8854
0
        for (auto &&poSubGeom : *poGC)
8855
0
        {
8856
0
            OGRwkbGeometryType eSubGeomType =
8857
0
                wkbFlatten(poSubGeom->getGeometryType());
8858
0
            if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8859
0
            {
8860
0
                bIsSFCGALCompatible = true;
8861
0
            }
8862
0
            else if (eSubGeomType != wkbMultiPolygon)
8863
0
            {
8864
0
                bIsSFCGALCompatible = false;
8865
0
                break;
8866
0
            }
8867
0
        }
8868
0
        return bIsSFCGALCompatible;
8869
0
    }
8870
8
    return FALSE;
8871
8
}
8872
8873
//! @endcond
8874
8875
/************************************************************************/
8876
/*                      roundCoordinatesIEEE754()                       */
8877
/************************************************************************/
8878
8879
/** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8880
 * double-precision binary representation.
8881
 *
8882
 * Determines the number of bits (N) required to represent a coordinate value
8883
 * with a specified number of digits after the decimal point, and then sets all
8884
 * but the N most significant bits to zero. The resulting coordinate value will
8885
 * still round to the original value (e.g. after roundCoordinates()), but will
8886
 * have improved compressiblity.
8887
 *
8888
 * @param options Contains the precision requirements.
8889
 * @since GDAL 3.9
8890
 */
8891
void OGRGeometry::roundCoordinatesIEEE754(
8892
    const OGRGeomCoordinateBinaryPrecision &options)
8893
0
{
8894
0
    struct Quantizer : public OGRDefaultGeometryVisitor
8895
0
    {
8896
0
        const OGRGeomCoordinateBinaryPrecision &m_options;
8897
8898
0
        explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8899
0
            : m_options(optionsIn)
8900
0
        {
8901
0
        }
8902
8903
0
        using OGRDefaultGeometryVisitor::visit;
8904
8905
0
        void visit(OGRPoint *poPoint) override
8906
0
        {
8907
0
            if (m_options.nXYBitPrecision != INT_MIN)
8908
0
            {
8909
0
                uint64_t i;
8910
0
                double d;
8911
0
                d = poPoint->getX();
8912
0
                memcpy(&i, &d, sizeof(i));
8913
0
                i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8914
0
                memcpy(&d, &i, sizeof(i));
8915
0
                poPoint->setX(d);
8916
0
                d = poPoint->getY();
8917
0
                memcpy(&i, &d, sizeof(i));
8918
0
                i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8919
0
                memcpy(&d, &i, sizeof(i));
8920
0
                poPoint->setY(d);
8921
0
            }
8922
0
            if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8923
0
            {
8924
0
                uint64_t i;
8925
0
                double d;
8926
0
                d = poPoint->getZ();
8927
0
                memcpy(&i, &d, sizeof(i));
8928
0
                i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8929
0
                memcpy(&d, &i, sizeof(i));
8930
0
                poPoint->setZ(d);
8931
0
            }
8932
0
            if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8933
0
            {
8934
0
                uint64_t i;
8935
0
                double d;
8936
0
                d = poPoint->getM();
8937
0
                memcpy(&i, &d, sizeof(i));
8938
0
                i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8939
0
                memcpy(&d, &i, sizeof(i));
8940
0
                poPoint->setM(d);
8941
0
            }
8942
0
        }
8943
0
    };
8944
8945
0
    Quantizer quantizer(options);
8946
0
    accept(&quantizer);
8947
0
}
8948
8949
/************************************************************************/
8950
/*                               visit()                                */
8951
/************************************************************************/
8952
8953
void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8954
0
{
8955
0
    for (auto &&oPoint : *poGeom)
8956
0
    {
8957
0
        oPoint.accept(this);
8958
0
    }
8959
0
}
8960
8961
void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8962
0
{
8963
0
    _visit(poGeom);
8964
0
}
8965
8966
void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8967
0
{
8968
0
    visit(poGeom->toUpperClass());
8969
0
}
8970
8971
void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8972
0
{
8973
0
    _visit(poGeom);
8974
0
}
8975
8976
void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8977
0
{
8978
0
    for (auto &&poSubGeom : *poGeom)
8979
0
        poSubGeom->accept(this);
8980
0
}
8981
8982
void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8983
0
{
8984
0
    visit(poGeom->toUpperClass());
8985
0
}
8986
8987
void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8988
0
{
8989
0
    visit(poGeom->toUpperClass());
8990
0
}
8991
8992
void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8993
0
{
8994
0
    visit(poGeom->toUpperClass());
8995
0
}
8996
8997
void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8998
0
{
8999
0
    visit(poGeom->toUpperClass());
9000
0
}
9001
9002
void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
9003
0
{
9004
0
    for (auto &&poSubGeom : *poGeom)
9005
0
        poSubGeom->accept(this);
9006
0
}
9007
9008
void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
9009
0
{
9010
0
    for (auto &&poSubGeom : *poGeom)
9011
0
        poSubGeom->accept(this);
9012
0
}
9013
9014
void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
9015
0
{
9016
0
    visit(poGeom->toUpperClass());
9017
0
}
9018
9019
void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
9020
0
{
9021
0
    visit(poGeom->toUpperClass());
9022
0
}
9023
9024
void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
9025
0
{
9026
0
    visit(poGeom->toUpperClass());
9027
0
}
9028
9029
void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
9030
0
{
9031
0
    for (auto &&poSubGeom : *poGeom)
9032
0
        poSubGeom->accept(this);
9033
0
}
9034
9035
void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
9036
0
{
9037
0
    visit(poGeom->toUpperClass());
9038
0
}
9039
9040
void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
9041
2.34k
{
9042
2.34k
    for (auto &&oPoint : *poGeom)
9043
65.8k
    {
9044
65.8k
        oPoint.accept(this);
9045
65.8k
    }
9046
2.34k
}
9047
9048
void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
9049
1.89k
{
9050
1.89k
    _visit(poGeom);
9051
1.89k
}
9052
9053
void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
9054
545
{
9055
545
    visit(poGeom->toUpperClass());
9056
545
}
9057
9058
void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
9059
450
{
9060
450
    _visit(poGeom);
9061
450
}
9062
9063
void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
9064
565
{
9065
565
    for (auto &&poSubGeom : *poGeom)
9066
545
        poSubGeom->accept(this);
9067
565
}
9068
9069
void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
9070
542
{
9071
542
    visit(poGeom->toUpperClass());
9072
542
}
9073
9074
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
9075
205
{
9076
205
    visit(poGeom->toUpperClass());
9077
205
}
9078
9079
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
9080
1.05k
{
9081
1.05k
    visit(poGeom->toUpperClass());
9082
1.05k
}
9083
9084
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
9085
109
{
9086
109
    visit(poGeom->toUpperClass());
9087
109
}
9088
9089
void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
9090
1.98k
{
9091
1.98k
    for (auto &&poSubGeom : *poGeom)
9092
599
        poSubGeom->accept(this);
9093
1.98k
}
9094
9095
void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
9096
705
{
9097
705
    for (auto &&poSubGeom : *poGeom)
9098
0
        poSubGeom->accept(this);
9099
705
}
9100
9101
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
9102
266
{
9103
266
    visit(poGeom->toUpperClass());
9104
266
}
9105
9106
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
9107
6
{
9108
6
    visit(poGeom->toUpperClass());
9109
6
}
9110
9111
void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
9112
41
{
9113
41
    visit(poGeom->toUpperClass());
9114
41
}
9115
9116
void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
9117
196
{
9118
196
    for (auto &&poSubGeom : *poGeom)
9119
42
        poSubGeom->accept(this);
9120
196
}
9121
9122
void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
9123
101
{
9124
101
    visit(poGeom->toUpperClass());
9125
101
}
9126
9127
/************************************************************************/
9128
/*                     OGRGeometryUniquePtrDeleter                      */
9129
/************************************************************************/
9130
9131
//! @cond Doxygen_Suppress
9132
void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
9133
0
{
9134
0
    delete poGeom;
9135
0
}
9136
9137
//! @endcond
9138
9139
/************************************************************************/
9140
/*                 OGRPreparedGeometryUniquePtrDeleter                  */
9141
/************************************************************************/
9142
9143
//! @cond Doxygen_Suppress
9144
void OGRPreparedGeometryUniquePtrDeleter::operator()(
9145
    OGRPreparedGeometry *poPreparedGeom) const
9146
0
{
9147
0
    OGRDestroyPreparedGeometry(poPreparedGeom);
9148
0
}
9149
9150
//! @endcond
9151
9152
/************************************************************************/
9153
/*                    HomogenizeDimensionalityWith()                    */
9154
/************************************************************************/
9155
9156
//! @cond Doxygen_Suppress
9157
void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
9158
7.98M
{
9159
7.98M
    if (poOtherGeom->Is3D() && !Is3D())
9160
706k
        set3D(TRUE);
9161
9162
7.98M
    if (poOtherGeom->IsMeasured() && !IsMeasured())
9163
242k
        setMeasured(TRUE);
9164
9165
7.98M
    if (!poOtherGeom->Is3D() && Is3D())
9166
213k
        poOtherGeom->set3D(TRUE);
9167
9168
7.98M
    if (!poOtherGeom->IsMeasured() && IsMeasured())
9169
92.4k
        poOtherGeom->setMeasured(TRUE);
9170
7.98M
}
9171
9172
//! @endcond
9173
9174
/************************************************************************/
9175
/*             OGRGeomCoordinateBinaryPrecision::SetFrom()              */
9176
/************************************************************************/
9177
9178
/** Set binary precision options from resolution.
9179
 *
9180
 * @since GDAL 3.9
9181
 */
9182
void OGRGeomCoordinateBinaryPrecision::SetFrom(
9183
    const OGRGeomCoordinatePrecision &prec)
9184
0
{
9185
0
    if (prec.dfXYResolution != 0)
9186
0
    {
9187
0
        nXYBitPrecision =
9188
0
            static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
9189
0
    }
9190
0
    if (prec.dfZResolution != 0)
9191
0
    {
9192
0
        nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
9193
0
    }
9194
0
    if (prec.dfMResolution != 0)
9195
0
    {
9196
0
        nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
9197
0
    }
9198
0
}
9199
9200
/************************************************************************/
9201
/*                     OGRwkbExportOptionsCreate()                      */
9202
/************************************************************************/
9203
9204
/**
9205
 * \brief Create geometry WKB export options.
9206
 *
9207
 * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
9208
 *
9209
 * @return object to be freed with OGRwkbExportOptionsDestroy().
9210
 * @since GDAL 3.9
9211
 */
9212
OGRwkbExportOptions *OGRwkbExportOptionsCreate()
9213
0
{
9214
0
    return new OGRwkbExportOptions;
9215
0
}
9216
9217
/************************************************************************/
9218
/*                     OGRwkbExportOptionsDestroy()                     */
9219
/************************************************************************/
9220
9221
/**
9222
 * \brief Destroy object returned by OGRwkbExportOptionsCreate()
9223
 *
9224
 * @param psOptions WKB export options
9225
 * @since GDAL 3.9
9226
 */
9227
9228
void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
9229
0
{
9230
0
    delete psOptions;
9231
0
}
9232
9233
/************************************************************************/
9234
/*                  OGRwkbExportOptionsSetByteOrder()                   */
9235
/************************************************************************/
9236
9237
/**
9238
 * \brief Set the WKB byte order.
9239
 *
9240
 * @param psOptions WKB export options
9241
 * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
9242
 * Intel)
9243
 * @since GDAL 3.9
9244
 */
9245
9246
void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
9247
                                     OGRwkbByteOrder eByteOrder)
9248
0
{
9249
0
    psOptions->eByteOrder = eByteOrder;
9250
0
}
9251
9252
/************************************************************************/
9253
/*                   OGRwkbExportOptionsSetVariant()                    */
9254
/************************************************************************/
9255
9256
/**
9257
 * \brief Set the WKB variant
9258
 *
9259
 * @param psOptions WKB export options
9260
 * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
9261
 * wkbVariantPostGIS1
9262
 * @since GDAL 3.9
9263
 */
9264
9265
void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
9266
                                   OGRwkbVariant eWkbVariant)
9267
0
{
9268
0
    psOptions->eWkbVariant = eWkbVariant;
9269
0
}
9270
9271
/************************************************************************/
9272
/*                  OGRwkbExportOptionsSetPrecision()                   */
9273
/************************************************************************/
9274
9275
/**
9276
 * \brief Set precision options
9277
 *
9278
 * @param psOptions WKB export options
9279
 * @param hPrecisionOptions Precision options (might be null to reset them)
9280
 * @since GDAL 3.9
9281
 */
9282
9283
void OGRwkbExportOptionsSetPrecision(
9284
    OGRwkbExportOptions *psOptions,
9285
    OGRGeomCoordinatePrecisionH hPrecisionOptions)
9286
0
{
9287
0
    psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
9288
0
    if (hPrecisionOptions)
9289
0
        psOptions->sPrecision.SetFrom(*hPrecisionOptions);
9290
0
}
9291
9292
/************************************************************************/
9293
/*                            IsRectangle()                             */
9294
/************************************************************************/
9295
9296
/**
9297
 * \brief Returns whether the geometry is a polygon with 4 corners forming
9298
 * a rectangle.
9299
 *
9300
 * @since GDAL 3.10
9301
 */
9302
bool OGRGeometry::IsRectangle() const
9303
3.01k
{
9304
3.01k
    if (wkbFlatten(getGeometryType()) != wkbPolygon)
9305
0
        return false;
9306
9307
3.01k
    const OGRPolygon *poPoly = toPolygon();
9308
9309
3.01k
    if (poPoly->getNumInteriorRings() != 0)
9310
0
        return false;
9311
9312
3.01k
    const OGRLinearRing *poRing = poPoly->getExteriorRing();
9313
3.01k
    if (!poRing)
9314
0
        return false;
9315
9316
3.01k
    if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
9317
0
        return false;
9318
9319
    // If the ring has 5 points, the last should be the first.
9320
3.01k
    if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
9321
3.01k
                                        poRing->getY(0) != poRing->getY(4)))
9322
0
        return false;
9323
9324
    // Polygon with first segment in "y" direction.
9325
3.01k
    if (poRing->getX(0) == poRing->getX(1) &&
9326
3.01k
        poRing->getY(1) == poRing->getY(2) &&
9327
3.01k
        poRing->getX(2) == poRing->getX(3) &&
9328
3.01k
        poRing->getY(3) == poRing->getY(0))
9329
3.01k
        return true;
9330
9331
    // Polygon with first segment in "x" direction.
9332
0
    if (poRing->getY(0) == poRing->getY(1) &&
9333
0
        poRing->getX(1) == poRing->getX(2) &&
9334
0
        poRing->getY(2) == poRing->getY(3) &&
9335
0
        poRing->getX(3) == poRing->getX(0))
9336
0
        return true;
9337
9338
0
    return false;
9339
0
}
9340
9341
/************************************************************************/
9342
/*                           hasEmptyParts()                            */
9343
/************************************************************************/
9344
9345
/**
9346
 * \brief Returns whether a geometry has empty parts/rings.
9347
 *
9348
 * Returns true if removeEmptyParts() will modify the geometry.
9349
 *
9350
 * This is different from IsEmpty().
9351
 *
9352
 * @since GDAL 3.10
9353
 */
9354
bool OGRGeometry::hasEmptyParts() const
9355
0
{
9356
0
    return false;
9357
0
}
9358
9359
/************************************************************************/
9360
/*                          removeEmptyParts()                          */
9361
/************************************************************************/
9362
9363
/**
9364
 * \brief Remove empty parts/rings from this geometry.
9365
 *
9366
 * @since GDAL 3.10
9367
 */
9368
void OGRGeometry::removeEmptyParts()
9369
0
{
9370
0
}
9371
9372
/************************************************************************/
9373
/*                        ~IOGRGeometryVisitor()                        */
9374
/************************************************************************/
9375
9376
0
IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
9377
9378
/************************************************************************/
9379
/*                     ~IOGRConstGeometryVisitor()                      */
9380
/************************************************************************/
9381
9382
6.43k
IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;