Coverage Report

Created: 2025-06-22 06:59

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