Coverage Report

Created: 2025-08-28 06:57

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