Coverage Report

Created: 2025-06-13 06:29

/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
22.3k
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
22.3k
{
135
22.3k
    if (poSRS != nullptr)
136
0
        const_cast<OGRSpatialReference *>(poSRS)->Release();
137
22.3k
}
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
14.7k
{
512
    // Do in that order to properly handle poSR == poSRS
513
14.7k
    if (poSR != nullptr)
514
0
        const_cast<OGRSpatialReference *>(poSR)->Reference();
515
14.7k
    if (poSRS != nullptr)
516
0
        const_cast<OGRSpatialReference *>(poSRS)->Release();
517
518
14.7k
    poSRS = poSR;
519
14.7k
}
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
552
{
969
552
    return (flags & OGR_G_3D) ? 3 : 2;
970
552
}
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
1.13k
{
1113
1.13k
    if (nNewDimension == 2)
1114
1.13k
        flags &= ~OGR_G_3D;
1115
0
    else
1116
0
        flags |= OGR_G_3D;
1117
1.13k
    return setMeasured(FALSE);
1118
1.13k
}
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.96k
{
1137
1.96k
    if (bIs3D)
1138
1.96k
        flags |= OGR_G_3D;
1139
0
    else
1140
0
        flags &= ~OGR_G_3D;
1141
1.96k
    return true;
1142
1.96k
}
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
2.23k
{
1162
2.23k
    if (bIsMeasured)
1163
1.06k
        flags |= OGR_G_MEASURED;
1164
1.17k
    else
1165
1.17k
        flags &= ~OGR_G_MEASURED;
1166
2.23k
    return true;
1167
2.23k
}
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
4.34k
{
1811
4.34k
    const char *pszInput = *ppszInput;
1812
1813
    /* -------------------------------------------------------------------- */
1814
    /*      Clear existing Geoms.                                           */
1815
    /* -------------------------------------------------------------------- */
1816
4.34k
    empty();
1817
4.34k
    *pbIsEmpty = false;
1818
1819
    /* -------------------------------------------------------------------- */
1820
    /*      Read and verify the type keyword, and ensure it matches the     */
1821
    /*      actual type of this container.                                  */
1822
    /* -------------------------------------------------------------------- */
1823
4.34k
    bool bHasM = false;
1824
4.34k
    bool bHasZ = false;
1825
4.34k
    bool bAlreadyGotDimension = false;
1826
1827
4.34k
    char szToken[OGR_WKT_TOKEN_MAX] = {};
1828
4.34k
    pszInput = OGRWktReadToken(pszInput, szToken);
1829
4.34k
    if (szToken[0] != '\0')
1830
4.34k
    {
1831
        // Postgis EWKT: POINTM instead of POINT M.
1832
        // Current QGIS versions (at least <= 3.38) also export POINTZ.
1833
4.34k
        const size_t nTokenLen = strlen(szToken);
1834
4.34k
        if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
1835
106
        {
1836
106
            szToken[nTokenLen - 1] = '\0';
1837
106
            bHasM = true;
1838
106
            bAlreadyGotDimension = true;
1839
1840
106
            if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
1841
106
                                  szToken[nTokenLen - 2] == 'z'))
1842
0
            {
1843
0
                bHasZ = true;
1844
0
                szToken[nTokenLen - 2] = '\0';
1845
0
            }
1846
106
        }
1847
4.24k
        else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
1848
171
        {
1849
171
            szToken[nTokenLen - 1] = '\0';
1850
171
            bHasZ = true;
1851
171
            bAlreadyGotDimension = true;
1852
171
        }
1853
4.34k
    }
1854
1855
4.34k
    if (!EQUAL(szToken, getGeometryName()))
1856
14
        return OGRERR_CORRUPT_DATA;
1857
1858
    /* -------------------------------------------------------------------- */
1859
    /*      Check for Z, M or ZM                                            */
1860
    /* -------------------------------------------------------------------- */
1861
4.33k
    if (!bAlreadyGotDimension)
1862
4.05k
    {
1863
4.05k
        const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1864
4.05k
        if (EQUAL(szToken, "Z"))
1865
396
        {
1866
396
            pszInput = pszNewInput;
1867
396
            bHasZ = true;
1868
396
        }
1869
3.66k
        else if (EQUAL(szToken, "M"))
1870
1.06k
        {
1871
1.06k
            pszInput = pszNewInput;
1872
1.06k
            bHasM = true;
1873
1.06k
        }
1874
2.59k
        else if (EQUAL(szToken, "ZM"))
1875
56
        {
1876
56
            pszInput = pszNewInput;
1877
56
            bHasZ = true;
1878
56
            bHasM = true;
1879
56
        }
1880
4.05k
    }
1881
4.33k
    *pbHasZ = bHasZ;
1882
4.33k
    *pbHasM = bHasM;
1883
1884
    /* -------------------------------------------------------------------- */
1885
    /*      Check for EMPTY ...                                             */
1886
    /* -------------------------------------------------------------------- */
1887
4.33k
    const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
1888
4.33k
    if (EQUAL(szToken, "EMPTY"))
1889
85
    {
1890
85
        *ppszInput = pszNewInput;
1891
85
        *pbIsEmpty = true;
1892
85
        if (bHasZ)
1893
33
            set3D(TRUE);
1894
85
        if (bHasM)
1895
72
            setMeasured(TRUE);
1896
85
        return OGRERR_NONE;
1897
85
    }
1898
1899
4.24k
    if (!EQUAL(szToken, "("))
1900
24
        return OGRERR_CORRUPT_DATA;
1901
1902
4.22k
    if (!bHasZ && !bHasM)
1903
2.50k
    {
1904
        // Test for old-style XXXXXXXXX(EMPTY).
1905
2.50k
        pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1906
2.50k
        if (EQUAL(szToken, "EMPTY"))
1907
19
        {
1908
19
            pszNewInput = OGRWktReadToken(pszNewInput, szToken);
1909
1910
19
            if (EQUAL(szToken, ","))
1911
17
            {
1912
                // This is OK according to SFSQL SPEC.
1913
17
            }
1914
2
            else if (!EQUAL(szToken, ")"))
1915
1
            {
1916
1
                return OGRERR_CORRUPT_DATA;
1917
1
            }
1918
1
            else
1919
1
            {
1920
1
                *ppszInput = pszNewInput;
1921
1
                empty();
1922
1
                *pbIsEmpty = true;
1923
1
                return OGRERR_NONE;
1924
1
            }
1925
19
        }
1926
2.50k
    }
1927
1928
4.22k
    *ppszInput = pszInput;
1929
1930
4.22k
    return OGRERR_NONE;
1931
4.22k
}
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
10.9k
{
2093
10.9k
    VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
2094
2095
10.9k
    return OGRGeometry::FromHandle(hGeom)->getGeometryType();
2096
10.9k
}
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 geometry has no points, otherwise FALSE.
2484
 */
2485
2486
OGRBoolean OGRGeometry::IsRing() const
2487
2488
0
{
2489
0
#ifndef HAVE_GEOS
2490
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2491
0
    return FALSE;
2492
2493
#else
2494
2495
    OGRBoolean bResult = FALSE;
2496
2497
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2498
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2499
2500
    if (hThisGeosGeom != nullptr)
2501
    {
2502
        bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom);
2503
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2504
    }
2505
    freeGEOSContext(hGEOSCtxt);
2506
2507
    return bResult;
2508
2509
#endif  // HAVE_GEOS
2510
0
}
2511
2512
/************************************************************************/
2513
/*                            OGR_G_IsRing()                            */
2514
/************************************************************************/
2515
2516
/**
2517
 * \brief Test if the geometry is a ring
2518
 *
2519
 * This function is the same as the C++ method OGRGeometry::IsRing().
2520
 *
2521
 * This function is built on the GEOS library, check it for the definition
2522
 * of the geometry operation.
2523
 * If OGR is built without the GEOS library, this function will always return
2524
 * FALSE.
2525
 *
2526
 * @param hGeom The Geometry to test.
2527
 *
2528
 * @return TRUE if the geometry has no points, otherwise FALSE.
2529
 */
2530
2531
int OGR_G_IsRing(OGRGeometryH hGeom)
2532
2533
0
{
2534
0
    VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
2535
2536
0
    return OGRGeometry::FromHandle(hGeom)->IsRing();
2537
0
}
2538
2539
/************************************************************************/
2540
/*                     OGRFromOGCGeomType()                             */
2541
/************************************************************************/
2542
2543
/** Map OGC geometry format type to corresponding OGR constants.
2544
 * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
2545
 * @return OGR constant.
2546
 */
2547
OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
2548
62
{
2549
62
    OGRwkbGeometryType eType = wkbUnknown;
2550
62
    bool bConvertTo3D = false;
2551
62
    bool bIsMeasured = false;
2552
62
    if (*pszGeomType != '\0')
2553
61
    {
2554
61
        char ch = pszGeomType[strlen(pszGeomType) - 1];
2555
61
        if (ch == 'm' || ch == 'M')
2556
5
        {
2557
5
            bIsMeasured = true;
2558
5
            if (strlen(pszGeomType) > 1)
2559
4
                ch = pszGeomType[strlen(pszGeomType) - 2];
2560
5
        }
2561
61
        if (ch == 'z' || ch == 'Z')
2562
2
        {
2563
2
            bConvertTo3D = true;
2564
2
        }
2565
61
    }
2566
2567
62
    if (STARTS_WITH_CI(pszGeomType, "POINT"))
2568
0
        eType = wkbPoint;
2569
62
    else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
2570
0
        eType = wkbLineString;
2571
62
    else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
2572
1
        eType = wkbPolygon;
2573
61
    else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
2574
1
        eType = wkbMultiPoint;
2575
60
    else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
2576
0
        eType = wkbMultiLineString;
2577
60
    else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
2578
0
        eType = wkbMultiPolygon;
2579
60
    else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
2580
0
        eType = wkbGeometryCollection;
2581
60
    else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
2582
16
        eType = wkbCircularString;
2583
44
    else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
2584
0
        eType = wkbCompoundCurve;
2585
44
    else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
2586
1
        eType = wkbCurvePolygon;
2587
43
    else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
2588
0
        eType = wkbMultiCurve;
2589
43
    else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
2590
0
        eType = wkbMultiSurface;
2591
43
    else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
2592
0
        eType = wkbTriangle;
2593
43
    else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
2594
0
        eType = wkbPolyhedralSurface;
2595
43
    else if (STARTS_WITH_CI(pszGeomType, "TIN"))
2596
0
        eType = wkbTIN;
2597
43
    else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
2598
2
        eType = wkbCurve;
2599
41
    else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
2600
0
        eType = wkbSurface;
2601
41
    else
2602
41
        eType = wkbUnknown;
2603
2604
62
    if (bConvertTo3D)
2605
2
        eType = wkbSetZ(eType);
2606
62
    if (bIsMeasured)
2607
5
        eType = wkbSetM(eType);
2608
2609
62
    return eType;
2610
62
}
2611
2612
/************************************************************************/
2613
/*                     OGRToOGCGeomType()                               */
2614
/************************************************************************/
2615
2616
/** Map OGR geometry format constants to corresponding OGC geometry type.
2617
 * @param eGeomType OGR geometry type
2618
 * @param bCamelCase Whether the return should be like "MultiPoint"
2619
 *        (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
2620
 * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
2621
 *               Default is false.
2622
 * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
2623
 *                       suffix. Default is false.
2624
 * @return string with OGC geometry type (without dimensionality)
2625
 */
2626
const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
2627
                             bool bAddZM, bool bSpaceBeforeZM)
2628
0
{
2629
0
    const char *pszRet = "";
2630
0
    switch (wkbFlatten(eGeomType))
2631
0
    {
2632
0
        case wkbUnknown:
2633
0
            pszRet = "Geometry";
2634
0
            break;
2635
0
        case wkbPoint:
2636
0
            pszRet = "Point";
2637
0
            break;
2638
0
        case wkbLineString:
2639
0
            pszRet = "LineString";
2640
0
            break;
2641
0
        case wkbPolygon:
2642
0
            pszRet = "Polygon";
2643
0
            break;
2644
0
        case wkbMultiPoint:
2645
0
            pszRet = "MultiPoint";
2646
0
            break;
2647
0
        case wkbMultiLineString:
2648
0
            pszRet = "MultiLineString";
2649
0
            break;
2650
0
        case wkbMultiPolygon:
2651
0
            pszRet = "MultiPolygon";
2652
0
            break;
2653
0
        case wkbGeometryCollection:
2654
0
            pszRet = "GeometryCollection";
2655
0
            break;
2656
0
        case wkbCircularString:
2657
0
            pszRet = "CircularString";
2658
0
            break;
2659
0
        case wkbCompoundCurve:
2660
0
            pszRet = "CompoundCurve";
2661
0
            break;
2662
0
        case wkbCurvePolygon:
2663
0
            pszRet = "CurvePolygon";
2664
0
            break;
2665
0
        case wkbMultiCurve:
2666
0
            pszRet = "MultiCurve";
2667
0
            break;
2668
0
        case wkbMultiSurface:
2669
0
            pszRet = "MultiSurface";
2670
0
            break;
2671
0
        case wkbTriangle:
2672
0
            pszRet = "Triangle";
2673
0
            break;
2674
0
        case wkbPolyhedralSurface:
2675
0
            pszRet = "PolyhedralSurface";
2676
0
            break;
2677
0
        case wkbTIN:
2678
0
            pszRet = "Tin";
2679
0
            break;
2680
0
        case wkbCurve:
2681
0
            pszRet = "Curve";
2682
0
            break;
2683
0
        case wkbSurface:
2684
0
            pszRet = "Surface";
2685
0
            break;
2686
0
        default:
2687
0
            break;
2688
0
    }
2689
0
    if (bAddZM)
2690
0
    {
2691
0
        const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
2692
0
        const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
2693
0
        if (bHasZ || bHasM)
2694
0
        {
2695
0
            if (bSpaceBeforeZM)
2696
0
                pszRet = CPLSPrintf("%s ", pszRet);
2697
0
            if (bHasZ)
2698
0
                pszRet = CPLSPrintf("%sZ", pszRet);
2699
0
            if (bHasM)
2700
0
                pszRet = CPLSPrintf("%sM", pszRet);
2701
0
        }
2702
0
    }
2703
0
    if (!bCamelCase)
2704
0
        pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
2705
0
    return pszRet;
2706
0
}
2707
2708
/************************************************************************/
2709
/*                       OGRGeometryTypeToName()                        */
2710
/************************************************************************/
2711
2712
/**
2713
 * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
2714
 * value.  The returned value should not be modified, or freed by the
2715
 * application.
2716
 *
2717
 * This function is C callable.
2718
 *
2719
 * @param eType the geometry type.
2720
 *
2721
 * @return internal human readable string, or NULL on failure.
2722
 */
2723
2724
const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
2725
2726
0
{
2727
0
    bool b3D = wkbHasZ(eType);
2728
0
    bool bMeasured = wkbHasM(eType);
2729
2730
0
    switch (wkbFlatten(eType))
2731
0
    {
2732
0
        case wkbUnknown:
2733
0
            if (b3D && bMeasured)
2734
0
                return "3D Measured Unknown (any)";
2735
0
            else if (b3D)
2736
0
                return "3D Unknown (any)";
2737
0
            else if (bMeasured)
2738
0
                return "Measured Unknown (any)";
2739
0
            else
2740
0
                return "Unknown (any)";
2741
2742
0
        case wkbPoint:
2743
0
            if (b3D && bMeasured)
2744
0
                return "3D Measured Point";
2745
0
            else if (b3D)
2746
0
                return "3D Point";
2747
0
            else if (bMeasured)
2748
0
                return "Measured Point";
2749
0
            else
2750
0
                return "Point";
2751
2752
0
        case wkbLineString:
2753
0
            if (b3D && bMeasured)
2754
0
                return "3D Measured Line String";
2755
0
            else if (b3D)
2756
0
                return "3D Line String";
2757
0
            else if (bMeasured)
2758
0
                return "Measured Line String";
2759
0
            else
2760
0
                return "Line String";
2761
2762
0
        case wkbPolygon:
2763
0
            if (b3D && bMeasured)
2764
0
                return "3D Measured Polygon";
2765
0
            else if (b3D)
2766
0
                return "3D Polygon";
2767
0
            else if (bMeasured)
2768
0
                return "Measured Polygon";
2769
0
            else
2770
0
                return "Polygon";
2771
2772
0
        case wkbMultiPoint:
2773
0
            if (b3D && bMeasured)
2774
0
                return "3D Measured Multi Point";
2775
0
            else if (b3D)
2776
0
                return "3D Multi Point";
2777
0
            else if (bMeasured)
2778
0
                return "Measured Multi Point";
2779
0
            else
2780
0
                return "Multi Point";
2781
2782
0
        case wkbMultiLineString:
2783
0
            if (b3D && bMeasured)
2784
0
                return "3D Measured Multi Line String";
2785
0
            else if (b3D)
2786
0
                return "3D Multi Line String";
2787
0
            else if (bMeasured)
2788
0
                return "Measured Multi Line String";
2789
0
            else
2790
0
                return "Multi Line String";
2791
2792
0
        case wkbMultiPolygon:
2793
0
            if (b3D && bMeasured)
2794
0
                return "3D Measured Multi Polygon";
2795
0
            else if (b3D)
2796
0
                return "3D Multi Polygon";
2797
0
            else if (bMeasured)
2798
0
                return "Measured Multi Polygon";
2799
0
            else
2800
0
                return "Multi Polygon";
2801
2802
0
        case wkbGeometryCollection:
2803
0
            if (b3D && bMeasured)
2804
0
                return "3D Measured Geometry Collection";
2805
0
            else if (b3D)
2806
0
                return "3D Geometry Collection";
2807
0
            else if (bMeasured)
2808
0
                return "Measured Geometry Collection";
2809
0
            else
2810
0
                return "Geometry Collection";
2811
2812
0
        case wkbCircularString:
2813
0
            if (b3D && bMeasured)
2814
0
                return "3D Measured Circular String";
2815
0
            else if (b3D)
2816
0
                return "3D Circular String";
2817
0
            else if (bMeasured)
2818
0
                return "Measured Circular String";
2819
0
            else
2820
0
                return "Circular String";
2821
2822
0
        case wkbCompoundCurve:
2823
0
            if (b3D && bMeasured)
2824
0
                return "3D Measured Compound Curve";
2825
0
            else if (b3D)
2826
0
                return "3D Compound Curve";
2827
0
            else if (bMeasured)
2828
0
                return "Measured Compound Curve";
2829
0
            else
2830
0
                return "Compound Curve";
2831
2832
0
        case wkbCurvePolygon:
2833
0
            if (b3D && bMeasured)
2834
0
                return "3D Measured Curve Polygon";
2835
0
            else if (b3D)
2836
0
                return "3D Curve Polygon";
2837
0
            else if (bMeasured)
2838
0
                return "Measured Curve Polygon";
2839
0
            else
2840
0
                return "Curve Polygon";
2841
2842
0
        case wkbMultiCurve:
2843
0
            if (b3D && bMeasured)
2844
0
                return "3D Measured Multi Curve";
2845
0
            else if (b3D)
2846
0
                return "3D Multi Curve";
2847
0
            else if (bMeasured)
2848
0
                return "Measured Multi Curve";
2849
0
            else
2850
0
                return "Multi Curve";
2851
2852
0
        case wkbMultiSurface:
2853
0
            if (b3D && bMeasured)
2854
0
                return "3D Measured Multi Surface";
2855
0
            else if (b3D)
2856
0
                return "3D Multi Surface";
2857
0
            else if (bMeasured)
2858
0
                return "Measured Multi Surface";
2859
0
            else
2860
0
                return "Multi Surface";
2861
2862
0
        case wkbCurve:
2863
0
            if (b3D && bMeasured)
2864
0
                return "3D Measured Curve";
2865
0
            else if (b3D)
2866
0
                return "3D Curve";
2867
0
            else if (bMeasured)
2868
0
                return "Measured Curve";
2869
0
            else
2870
0
                return "Curve";
2871
2872
0
        case wkbSurface:
2873
0
            if (b3D && bMeasured)
2874
0
                return "3D Measured Surface";
2875
0
            else if (b3D)
2876
0
                return "3D Surface";
2877
0
            else if (bMeasured)
2878
0
                return "Measured Surface";
2879
0
            else
2880
0
                return "Surface";
2881
2882
0
        case wkbTriangle:
2883
0
            if (b3D && bMeasured)
2884
0
                return "3D Measured Triangle";
2885
0
            else if (b3D)
2886
0
                return "3D Triangle";
2887
0
            else if (bMeasured)
2888
0
                return "Measured Triangle";
2889
0
            else
2890
0
                return "Triangle";
2891
2892
0
        case wkbPolyhedralSurface:
2893
0
            if (b3D && bMeasured)
2894
0
                return "3D Measured PolyhedralSurface";
2895
0
            else if (b3D)
2896
0
                return "3D PolyhedralSurface";
2897
0
            else if (bMeasured)
2898
0
                return "Measured PolyhedralSurface";
2899
0
            else
2900
0
                return "PolyhedralSurface";
2901
2902
0
        case wkbTIN:
2903
0
            if (b3D && bMeasured)
2904
0
                return "3D Measured TIN";
2905
0
            else if (b3D)
2906
0
                return "3D TIN";
2907
0
            else if (bMeasured)
2908
0
                return "Measured TIN";
2909
0
            else
2910
0
                return "TIN";
2911
2912
0
        case wkbNone:
2913
0
            return "None";
2914
2915
0
        default:
2916
0
        {
2917
0
            return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
2918
0
        }
2919
0
    }
2920
0
}
2921
2922
/************************************************************************/
2923
/*                       OGRMergeGeometryTypes()                        */
2924
/************************************************************************/
2925
2926
/**
2927
 * \brief Find common geometry type.
2928
 *
2929
 * Given two geometry types, find the most specific common
2930
 * type.  Normally used repeatedly with the geometries in a
2931
 * layer to try and establish the most specific geometry type
2932
 * that can be reported for the layer.
2933
 *
2934
 * NOTE: wkbUnknown is the "worst case" indicating a mixture of
2935
 * geometry types with nothing in common but the base geometry
2936
 * type.  wkbNone should be used to indicate that no geometries
2937
 * have been encountered yet, and means the first geometry
2938
 * encountered will establish the preliminary type.
2939
 *
2940
 * @param eMain the first input geometry type.
2941
 * @param eExtra the second input geometry type.
2942
 *
2943
 * @return the merged geometry type.
2944
 */
2945
2946
OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
2947
                                         OGRwkbGeometryType eExtra)
2948
2949
0
{
2950
0
    return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
2951
0
}
2952
2953
/**
2954
 * \brief Find common geometry type.
2955
 *
2956
 * Given two geometry types, find the most specific common
2957
 * type.  Normally used repeatedly with the geometries in a
2958
 * layer to try and establish the most specific geometry type
2959
 * that can be reported for the layer.
2960
 *
2961
 * NOTE: wkbUnknown is the "worst case" indicating a mixture of
2962
 * geometry types with nothing in common but the base geometry
2963
 * type.  wkbNone should be used to indicate that no geometries
2964
 * have been encountered yet, and means the first geometry
2965
 * encountered will establish the preliminary type.
2966
 *
2967
 * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
2968
 * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
2969
 * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
2970
 * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
2971
 *
2972
 * @param eMain the first input geometry type.
2973
 * @param eExtra the second input geometry type.
2974
 * @param bAllowPromotingToCurves determine if promotion to curve type
2975
 * must be done.
2976
 *
2977
 * @return the merged geometry type.
2978
 *
2979
 * @since GDAL 2.0
2980
 */
2981
2982
OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
2983
                                           OGRwkbGeometryType eExtra,
2984
                                           int bAllowPromotingToCurves)
2985
2986
0
{
2987
0
    OGRwkbGeometryType eFMain = wkbFlatten(eMain);
2988
0
    OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
2989
2990
0
    const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
2991
0
    const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
2992
2993
0
    if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
2994
0
        return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
2995
2996
0
    if (eFMain == wkbNone)
2997
0
        return eExtra;
2998
2999
0
    if (eFExtra == wkbNone)
3000
0
        return eMain;
3001
3002
0
    if (eFMain == eFExtra)
3003
0
    {
3004
0
        return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3005
0
    }
3006
3007
0
    if (bAllowPromotingToCurves)
3008
0
    {
3009
0
        if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
3010
0
            return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
3011
3012
0
        if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3013
0
            return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3014
3015
0
        if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3016
0
            return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3017
0
    }
3018
3019
    // One is subclass of the other one
3020
0
    if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
3021
0
    {
3022
0
        return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
3023
0
    }
3024
0
    else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
3025
0
    {
3026
0
        return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
3027
0
    }
3028
3029
    // Nothing apparently in common.
3030
0
    return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
3031
0
}
3032
3033
/**
3034
 * \fn void OGRGeometry::flattenTo2D();
3035
 *
3036
 * \brief Convert geometry to strictly 2D.
3037
 * In a sense this converts all Z coordinates
3038
 * to 0.0.
3039
 *
3040
 * This method is the same as the C function OGR_G_FlattenTo2D().
3041
 */
3042
3043
/************************************************************************/
3044
/*                         OGR_G_FlattenTo2D()                          */
3045
/************************************************************************/
3046
/**
3047
 * \brief Convert geometry to strictly 2D.
3048
 * In a sense this converts all Z coordinates
3049
 * to 0.0.
3050
 *
3051
 * This function is the same as the CPP method OGRGeometry::flattenTo2D().
3052
 *
3053
 * @param hGeom handle on the geometry to convert.
3054
 */
3055
3056
void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
3057
3058
0
{
3059
0
    OGRGeometry::FromHandle(hGeom)->flattenTo2D();
3060
0
}
3061
3062
/************************************************************************/
3063
/*                            exportToGML()                             */
3064
/************************************************************************/
3065
3066
/**
3067
 * \fn char *OGRGeometry::exportToGML( const char* const *
3068
 * papszOptions = NULL ) const;
3069
 *
3070
 * \brief Convert a geometry into GML format.
3071
 *
3072
 * The GML geometry is expressed directly in terms of GML basic data
3073
 * types assuming the this is available in the gml namespace.  The returned
3074
 * string should be freed with CPLFree() when no longer required.
3075
 *
3076
 * The supported options are :
3077
 * <ul>
3078
 * <li> FORMAT=GML2/GML3/GML32 (GML2 or GML32 added in GDAL 2.1).
3079
 *      If not set, it will default to GML 2.1.2 output.
3080
 * </li>
3081
 * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
3082
 *      To use gml:Curve element for linestrings.
3083
 *      Otherwise gml:LineString will be used .
3084
 * </li>
3085
 * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
3086
 *      SRSNAME_FORMAT in GDAL &gt;=2.2). Defaults to YES.
3087
 *      If YES, SRS with EPSG authority will be written with the
3088
 *      "urn:ogc:def:crs:EPSG::" prefix.
3089
 *      In the case the SRS should be treated as lat/long or
3090
 *      northing/easting, then the function will take care of coordinate order
3091
 *      swapping if the data axis to CRS axis mapping indicates it.
3092
 *      If set to NO, SRS with EPSG authority will be written with the "EPSG:"
3093
 *      prefix, even if they are in lat/long order.
3094
 * </li>
3095
 * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3, added
3096
 *      in GDAL 2.2). Defaults to OGC_URN.  If SHORT, then srsName will be in
3097
 *      the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
3098
 *      in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
3099
 *      then srsName will be in the form
3100
 *      http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
3101
 *      OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
3102
 *      or northing/easting, then the function will take care of coordinate
3103
 *      order swapping if the data axis to CRS axis mapping indicates it.
3104
 * </li>
3105
 * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
3106
 *      top-level geometry element with the provided value.
3107
 *      Required for GML 3.2 compatibility.
3108
 * </li>
3109
 * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
3110
 *      FORMAT=GML3/GML32, GDAL >= 2.0) Default to POSLIST.
3111
 *      For 2.5D geometries, define the location where to attach the
3112
 *      srsDimension attribute.
3113
 *      There are diverging implementations. Some put in on the
3114
 *      &lt;gml:posList&gt; element, other on the top geometry element.
3115
 * </li>
3116
 * <li> NAMESPACE_DECL=YES/NO. If set to YES,
3117
 *      xmlns:gml="http://www.opengis.net/gml" will be added to the root node
3118
 *      for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
3119
 * </li>
3120
 * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
3121
 *      Resolution for the coordinate precision of the X and Y coordinates.
3122
 *      Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
3123
 *      to 5 decimal digits. 0 for the default behavior.
3124
 * </li>
3125
 * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
3126
 *      Resolution for the coordinate precision of the Z coordinates.
3127
 *      Expressed in the units of the Z axis of the SRS.
3128
 *      0 for the default behavior.
3129
 * </li>
3130
 * </ul>
3131
 *
3132
 * This method is the same as the C function OGR_G_ExportToGMLEx().
3133
 *
3134
 * @param papszOptions NULL-terminated list of options.
3135
 * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
3136
 */
3137
3138
char *OGRGeometry::exportToGML(const char *const *papszOptions) const
3139
0
{
3140
0
    return OGR_G_ExportToGMLEx(
3141
0
        OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
3142
0
        const_cast<char **>(papszOptions));
3143
0
}
3144
3145
/************************************************************************/
3146
/*                            exportToKML()                             */
3147
/************************************************************************/
3148
3149
/**
3150
 * \fn char *OGRGeometry::exportToKML() const;
3151
 *
3152
 * \brief Convert a geometry into KML format.
3153
 *
3154
 * The returned string should be freed with CPLFree() when no longer required.
3155
 *
3156
 * This method is the same as the C function OGR_G_ExportToKML().
3157
 *
3158
 * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
3159
 */
3160
3161
char *OGRGeometry::exportToKML() const
3162
0
{
3163
0
    return OGR_G_ExportToKML(
3164
0
        OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
3165
0
}
3166
3167
/************************************************************************/
3168
/*                            exportToJson()                             */
3169
/************************************************************************/
3170
3171
/**
3172
 * \fn char *OGRGeometry::exportToJson() const;
3173
 *
3174
 * \brief Convert a geometry into GeoJSON format.
3175
 *
3176
 * The returned string should be freed with CPLFree() when no longer required.
3177
 *
3178
 * The following options are supported :
3179
 * <ul>
3180
 * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
3181
 * (added in GDAL 3.9)</li>
3182
 * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
3183
 * (added in GDAL 3.9)</li>
3184
 * </ul>
3185
 *
3186
 * This method is the same as the C function OGR_G_ExportToJson().
3187
 *
3188
 * @param papszOptions Null terminated list of options, or null (added in 3.9)
3189
 * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
3190
 */
3191
3192
char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
3193
0
{
3194
0
    OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
3195
0
    return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
3196
0
                                const_cast<char **>(papszOptions));
3197
0
}
3198
3199
/************************************************************************/
3200
/*                 OGRSetGenerate_DB2_V72_BYTE_ORDER()                  */
3201
/************************************************************************/
3202
3203
/**
3204
 * \brief Special entry point to enable the hack for generating DB2 V7.2 style
3205
 * WKB.
3206
 *
3207
 * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
3208
 * order in WKB.  This entry point is used to turn on or off the generation of
3209
 * such WKB.
3210
 */
3211
OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
3212
3213
0
{
3214
0
#if defined(HACK_FOR_IBM_DB2_V72)
3215
0
    OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
3216
0
    return OGRERR_NONE;
3217
#else
3218
    if (bGenerate_DB2_V72_BYTE_ORDER)
3219
        return OGRERR_FAILURE;
3220
    else
3221
        return OGRERR_NONE;
3222
#endif
3223
0
}
3224
3225
/************************************************************************/
3226
/*                 OGRGetGenerate_DB2_V72_BYTE_ORDER()                  */
3227
/*                                                                      */
3228
/*      This is a special entry point to get the value of static flag   */
3229
/*      OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER.                      */
3230
/************************************************************************/
3231
int OGRGetGenerate_DB2_V72_BYTE_ORDER()
3232
0
{
3233
0
    return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
3234
0
}
3235
3236
/************************************************************************/
3237
/*                          createGEOSContext()                         */
3238
/************************************************************************/
3239
3240
/** Create a new GEOS context.
3241
 * @return a new GEOS context (to be freed with freeGEOSContext())
3242
 */
3243
GEOSContextHandle_t OGRGeometry::createGEOSContext()
3244
0
{
3245
0
#ifndef HAVE_GEOS
3246
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3247
0
    return nullptr;
3248
#else
3249
    return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
3250
#endif
3251
0
}
3252
3253
/************************************************************************/
3254
/*                          freeGEOSContext()                           */
3255
/************************************************************************/
3256
3257
/** Destroy a GEOS context.
3258
 * @param hGEOSCtxt GEOS context
3259
 */
3260
void OGRGeometry::freeGEOSContext(
3261
    UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt)
3262
0
{
3263
#ifdef HAVE_GEOS
3264
    if (hGEOSCtxt != nullptr)
3265
    {
3266
        finishGEOS_r(hGEOSCtxt);
3267
    }
3268
#endif
3269
0
}
3270
3271
#ifdef HAVE_GEOS
3272
3273
/************************************************************************/
3274
/*                          convertToGEOSGeom()                         */
3275
/************************************************************************/
3276
3277
static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
3278
                                  OGRGeometry *poGeom)
3279
{
3280
    GEOSGeom hGeom = nullptr;
3281
    const size_t nDataSize = poGeom->WkbSize();
3282
    unsigned char *pabyData =
3283
        static_cast<unsigned char *>(CPLMalloc(nDataSize));
3284
#if GEOS_VERSION_MAJOR > 3 ||                                                  \
3285
    (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
3286
    OGRwkbVariant eWkbVariant = wkbVariantIso;
3287
#else
3288
    OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
3289
#endif
3290
    if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
3291
        hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
3292
    CPLFree(pabyData);
3293
    return hGeom;
3294
}
3295
#endif
3296
3297
/************************************************************************/
3298
/*                            exportToGEOS()                            */
3299
/************************************************************************/
3300
3301
/** Returns a GEOSGeom object corresponding to the geometry.
3302
 *
3303
 * @param hGEOSCtxt GEOS context
3304
 * @param bRemoveEmptyParts Whether empty parts of the geometry should be
3305
 * removed before exporting to GEOS (GDAL >= 3.10)
3306
 * @return a GEOSGeom object corresponding to the geometry (to be freed with
3307
 * GEOSGeom_destroy_r()), or NULL in case of error
3308
 */
3309
GEOSGeom
3310
OGRGeometry::exportToGEOS(UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt,
3311
                          UNUSED_IF_NO_GEOS bool bRemoveEmptyParts) const
3312
3313
0
{
3314
0
#ifndef HAVE_GEOS
3315
3316
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3317
0
    return nullptr;
3318
3319
#else
3320
3321
    if (hGEOSCtxt == nullptr)
3322
        return nullptr;
3323
3324
    const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
3325
#if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3326
    // POINT EMPTY is exported to WKB as if it were POINT(0 0),
3327
    // so that particular case is necessary.
3328
    if (eType == wkbPoint && IsEmpty())
3329
    {
3330
        return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
3331
    }
3332
#endif
3333
3334
    GEOSGeom hGeom = nullptr;
3335
3336
    OGRGeometry *poLinearGeom = nullptr;
3337
    if (hasCurveGeometry())
3338
    {
3339
        poLinearGeom = getLinearGeometry();
3340
        if (bRemoveEmptyParts)
3341
            poLinearGeom->removeEmptyParts();
3342
#if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3343
        // GEOS < 3.12 doesn't support M dimension
3344
        if (poLinearGeom->IsMeasured())
3345
            poLinearGeom->setMeasured(FALSE);
3346
#endif
3347
    }
3348
    else
3349
    {
3350
        poLinearGeom = const_cast<OGRGeometry *>(this);
3351
#if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
3352
        // GEOS < 3.12 doesn't support M dimension
3353
        if (IsMeasured())
3354
        {
3355
            poLinearGeom = clone();
3356
            if (bRemoveEmptyParts)
3357
                poLinearGeom->removeEmptyParts();
3358
            poLinearGeom->setMeasured(FALSE);
3359
        }
3360
        else
3361
#endif
3362
            if (bRemoveEmptyParts && hasEmptyParts())
3363
        {
3364
            poLinearGeom = clone();
3365
            poLinearGeom->removeEmptyParts();
3366
        }
3367
    }
3368
    if (eType == wkbTriangle)
3369
    {
3370
        OGRPolygon oPolygon(*(poLinearGeom->toPolygon()));
3371
        hGeom = convertToGEOSGeom(hGEOSCtxt, &oPolygon);
3372
    }
3373
    else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
3374
    {
3375
        OGRGeometry *poGC = OGRGeometryFactory::forceTo(
3376
            poLinearGeom->clone(),
3377
            OGR_GT_SetModifier(wkbGeometryCollection, poLinearGeom->Is3D(),
3378
                               poLinearGeom->IsMeasured()),
3379
            nullptr);
3380
        hGeom = convertToGEOSGeom(hGEOSCtxt, poGC);
3381
        delete poGC;
3382
    }
3383
    else if (eType == wkbGeometryCollection)
3384
    {
3385
        bool bCanConvertToMultiPoly = true;
3386
        // bool bMustConvertToMultiPoly = true;
3387
        const OGRGeometryCollection *poGC =
3388
            poLinearGeom->toGeometryCollection();
3389
        for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
3390
        {
3391
            const OGRwkbGeometryType eSubGeomType =
3392
                wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
3393
            if (eSubGeomType == wkbPolyhedralSurface || eSubGeomType == wkbTIN)
3394
            {
3395
                // bMustConvertToMultiPoly = true;
3396
            }
3397
            else if (eSubGeomType != wkbMultiPolygon &&
3398
                     eSubGeomType != wkbPolygon)
3399
            {
3400
                bCanConvertToMultiPoly = false;
3401
                break;
3402
            }
3403
        }
3404
        if (bCanConvertToMultiPoly /* && bMustConvertToMultiPoly */)
3405
        {
3406
            OGRGeometry *poMultiPolygon = OGRGeometryFactory::forceTo(
3407
                poLinearGeom->clone(),
3408
                OGR_GT_SetModifier(wkbMultiPolygon, poLinearGeom->Is3D(),
3409
                                   poLinearGeom->IsMeasured()),
3410
                nullptr);
3411
            OGRGeometry *poGCDest = OGRGeometryFactory::forceTo(
3412
                poMultiPolygon,
3413
                OGR_GT_SetModifier(wkbGeometryCollection, poLinearGeom->Is3D(),
3414
                                   poLinearGeom->IsMeasured()),
3415
                nullptr);
3416
            hGeom = convertToGEOSGeom(hGEOSCtxt, poGCDest);
3417
            delete poGCDest;
3418
        }
3419
        else
3420
        {
3421
            hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
3422
        }
3423
    }
3424
    else
3425
    {
3426
        hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
3427
    }
3428
3429
    if (poLinearGeom != this)
3430
        delete poLinearGeom;
3431
3432
    return hGeom;
3433
3434
#endif  // HAVE_GEOS
3435
0
}
3436
3437
/************************************************************************/
3438
/*                         hasCurveGeometry()                           */
3439
/************************************************************************/
3440
3441
/**
3442
 * \brief Returns if this geometry is or has curve geometry.
3443
 *
3444
 * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
3445
 * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
3446
 *
3447
 * If bLookForNonLinear is set to TRUE, it will be actually looked if
3448
 * the geometry or its subgeometries are or contain a non-linear
3449
 * geometry in them. In which case, if the method returns TRUE, it
3450
 * means that getLinearGeometry() would return an approximate version
3451
 * of the geometry. Otherwise, getLinearGeometry() would do a
3452
 * conversion, but with just converting container type, like
3453
 * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
3454
 * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
3455
 * conversion.
3456
 *
3457
 * This method is the same as the C function OGR_G_HasCurveGeometry().
3458
 *
3459
 * @param bLookForNonLinear set it to TRUE to check if the geometry is
3460
 * or contains a CIRCULARSTRING.
3461
 *
3462
 * @return TRUE if this geometry is or has curve geometry.
3463
 *
3464
 * @since GDAL 2.0
3465
 */
3466
3467
OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
3468
718
{
3469
718
    return FALSE;
3470
718
}
3471
3472
/************************************************************************/
3473
/*                         getLinearGeometry()                        */
3474
/************************************************************************/
3475
3476
/**
3477
 * \brief Return, possibly approximate, non-curve version of this geometry.
3478
 *
3479
 * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
3480
 * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
3481
 *
3482
 * The ownership of the returned geometry belongs to the caller.
3483
 *
3484
 * The reverse method is OGRGeometry::getCurveGeometry().
3485
 *
3486
 * This method is the same as the C function OGR_G_GetLinearGeometry().
3487
 *
3488
 * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
3489
 * arc, zero to use the default setting.
3490
 * @param papszOptions options as a null-terminated list of strings.
3491
 *                     See OGRGeometryFactory::curveToLineString() for
3492
 *                     valid options.
3493
 *
3494
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3495
 *
3496
 * @since GDAL 2.0
3497
 */
3498
3499
OGRGeometry *
3500
OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
3501
                               CPL_UNUSED const char *const *papszOptions) const
3502
0
{
3503
0
    return clone();
3504
0
}
3505
3506
/************************************************************************/
3507
/*                             getCurveGeometry()                       */
3508
/************************************************************************/
3509
3510
/**
3511
 * \brief Return curve version of this geometry.
3512
 *
3513
 * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
3514
 * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
3515
 * curve geometries.
3516
 *
3517
 * If the geometry has no curve portion, the returned geometry will be a clone
3518
 * of it.
3519
 *
3520
 * The ownership of the returned geometry belongs to the caller.
3521
 *
3522
 * The reverse method is OGRGeometry::getLinearGeometry().
3523
 *
3524
 * This function is the same as C function OGR_G_GetCurveGeometry().
3525
 *
3526
 * @param papszOptions options as a null-terminated list of strings.
3527
 *                     Unused for now. Must be set to NULL.
3528
 *
3529
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3530
 *
3531
 * @since GDAL 2.0
3532
 */
3533
3534
OGRGeometry *
3535
OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
3536
0
{
3537
0
    return clone();
3538
0
}
3539
3540
/************************************************************************/
3541
/*                              Distance()                              */
3542
/************************************************************************/
3543
3544
/**
3545
 * \brief Compute distance between two geometries.
3546
 *
3547
 * Returns the shortest distance between the two geometries. The distance is
3548
 * expressed into the same unit as the coordinates of the geometries.
3549
 *
3550
 * This method is the same as the C function OGR_G_Distance().
3551
 *
3552
 * This method is built on the GEOS library, check it for the definition
3553
 * of the geometry operation.
3554
 * If OGR is built without the GEOS library, this method will always fail,
3555
 * issuing a CPLE_NotSupported error.
3556
 *
3557
 * @param poOtherGeom the other geometry to compare against.
3558
 *
3559
 * @return the distance between the geometries or -1 if an error occurs.
3560
 */
3561
3562
double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
3563
3564
0
{
3565
0
    if (nullptr == poOtherGeom)
3566
0
    {
3567
0
        CPLDebug("OGR",
3568
0
                 "OGRGeometry::Distance called with NULL geometry pointer");
3569
0
        return -1.0;
3570
0
    }
3571
3572
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
3573
0
    {
3574
0
#ifndef HAVE_SFCGAL
3575
3576
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3577
0
        return -1.0;
3578
3579
#else
3580
3581
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3582
        if (poThis == nullptr)
3583
            return -1.0;
3584
3585
        sfcgal_geometry_t *poOther =
3586
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3587
        if (poOther == nullptr)
3588
        {
3589
            sfcgal_geometry_delete(poThis);
3590
            return -1.0;
3591
        }
3592
3593
        const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
3594
3595
        sfcgal_geometry_delete(poThis);
3596
        sfcgal_geometry_delete(poOther);
3597
3598
        return dfDistance > 0.0 ? dfDistance : -1.0;
3599
3600
#endif
3601
0
    }
3602
3603
0
    else
3604
0
    {
3605
0
#ifndef HAVE_GEOS
3606
3607
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3608
0
        return -1.0;
3609
3610
#else
3611
3612
        GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3613
        // GEOSGeom is a pointer
3614
        GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
3615
        GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
3616
3617
        int bIsErr = 0;
3618
        double dfDistance = 0.0;
3619
3620
        if (hThis != nullptr && hOther != nullptr)
3621
        {
3622
            bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
3623
        }
3624
3625
        GEOSGeom_destroy_r(hGEOSCtxt, hThis);
3626
        GEOSGeom_destroy_r(hGEOSCtxt, hOther);
3627
        freeGEOSContext(hGEOSCtxt);
3628
3629
        if (bIsErr > 0)
3630
        {
3631
            return dfDistance;
3632
        }
3633
3634
        /* Calculations error */
3635
        return -1.0;
3636
3637
#endif /* HAVE_GEOS */
3638
0
    }
3639
0
}
3640
3641
/************************************************************************/
3642
/*                           OGR_G_Distance()                           */
3643
/************************************************************************/
3644
/**
3645
 * \brief Compute distance between two geometries.
3646
 *
3647
 * Returns the shortest distance between the two geometries. The distance is
3648
 * expressed into the same unit as the coordinates of the geometries.
3649
 *
3650
 * This function is the same as the C++ method OGRGeometry::Distance().
3651
 *
3652
 * This function is built on the GEOS library, check it for the definition
3653
 * of the geometry operation.
3654
 * If OGR is built without the GEOS library, this function will always fail,
3655
 * issuing a CPLE_NotSupported error.
3656
 *
3657
 * @param hFirst the first geometry to compare against.
3658
 * @param hOther the other geometry to compare against.
3659
 *
3660
 * @return the distance between the geometries or -1 if an error occurs.
3661
 */
3662
3663
double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
3664
3665
0
{
3666
0
    VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
3667
3668
0
    return OGRGeometry::FromHandle(hFirst)->Distance(
3669
0
        OGRGeometry::FromHandle(hOther));
3670
0
}
3671
3672
/************************************************************************/
3673
/*                             Distance3D()                             */
3674
/************************************************************************/
3675
3676
/**
3677
 * \brief Returns the 3D distance between two geometries
3678
 *
3679
 * The distance is expressed into the same unit as the coordinates of the
3680
 * geometries.
3681
 *
3682
 * This method is built on the SFCGAL library, check it for the definition
3683
 * of the geometry operation.
3684
 * If OGR is built without the SFCGAL library, this method will always return
3685
 * -1.0
3686
 *
3687
 * This function is the same as the C function OGR_G_Distance3D().
3688
 *
3689
 * @return distance between the two geometries
3690
 * @since GDAL 2.2
3691
 */
3692
3693
double OGRGeometry::Distance3D(
3694
    UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
3695
0
{
3696
0
    if (poOtherGeom == nullptr)
3697
0
    {
3698
0
        CPLDebug("OGR",
3699
0
                 "OGRTriangle::Distance3D called with NULL geometry pointer");
3700
0
        return -1.0;
3701
0
    }
3702
3703
0
    if (!(poOtherGeom->Is3D() && Is3D()))
3704
0
    {
3705
0
        CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
3706
0
                        "geometry(geometries)");
3707
0
        return -1.0;
3708
0
    }
3709
3710
0
#ifndef HAVE_SFCGAL
3711
3712
0
    CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
3713
0
    return -1.0;
3714
3715
#else
3716
3717
    sfcgal_init();
3718
    sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
3719
    if (poThis == nullptr)
3720
        return -1.0;
3721
3722
    sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
3723
    if (poOther == nullptr)
3724
    {
3725
        sfcgal_geometry_delete(poThis);
3726
        return -1.0;
3727
    }
3728
3729
    const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
3730
3731
    sfcgal_geometry_delete(poThis);
3732
    sfcgal_geometry_delete(poOther);
3733
3734
    return dfDistance > 0 ? dfDistance : -1.0;
3735
3736
#endif
3737
0
}
3738
3739
/************************************************************************/
3740
/*                           OGR_G_Distance3D()                         */
3741
/************************************************************************/
3742
/**
3743
 * \brief Returns the 3D distance between two geometries
3744
 *
3745
 * The distance is expressed into the same unit as the coordinates of the
3746
 * geometries.
3747
 *
3748
 * This method is built on the SFCGAL library, check it for the definition
3749
 * of the geometry operation.
3750
 * If OGR is built without the SFCGAL library, this method will always return
3751
 * -1.0
3752
 *
3753
 * This function is the same as the C++ method OGRGeometry::Distance3D().
3754
 *
3755
 * @param hFirst the first geometry to compare against.
3756
 * @param hOther the other geometry to compare against.
3757
 * @return distance between the two geometries
3758
 * @since GDAL 2.2
3759
 *
3760
 * @return the distance between the geometries or -1 if an error occurs.
3761
 */
3762
3763
double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
3764
3765
0
{
3766
0
    VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
3767
3768
0
    return OGRGeometry::FromHandle(hFirst)->Distance3D(
3769
0
        OGRGeometry::FromHandle(hOther));
3770
0
}
3771
3772
/************************************************************************/
3773
/*                       OGRGeometryRebuildCurves()                     */
3774
/************************************************************************/
3775
3776
#ifdef HAVE_GEOS
3777
static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
3778
                                             const OGRGeometry *poOtherGeom,
3779
                                             OGRGeometry *poOGRProduct)
3780
{
3781
    if (poOGRProduct != nullptr &&
3782
        wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
3783
        (poGeom->hasCurveGeometry(true) ||
3784
         (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
3785
    {
3786
        OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
3787
        delete poOGRProduct;
3788
        return poCurveGeom;
3789
    }
3790
    return poOGRProduct;
3791
}
3792
3793
/************************************************************************/
3794
/*                       BuildGeometryFromGEOS()                        */
3795
/************************************************************************/
3796
3797
static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
3798
                                          GEOSGeom hGeosProduct,
3799
                                          const OGRGeometry *poSelf,
3800
                                          const OGRGeometry *poOtherGeom)
3801
{
3802
    OGRGeometry *poOGRProduct = nullptr;
3803
    if (hGeosProduct != nullptr)
3804
    {
3805
        poOGRProduct =
3806
            OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
3807
        if (poOGRProduct != nullptr &&
3808
            poSelf->getSpatialReference() != nullptr &&
3809
            (poOtherGeom == nullptr ||
3810
             (poOtherGeom->getSpatialReference() != nullptr &&
3811
              poOtherGeom->getSpatialReference()->IsSame(
3812
                  poSelf->getSpatialReference()))))
3813
        {
3814
            poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
3815
        }
3816
        poOGRProduct =
3817
            OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
3818
        GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
3819
    }
3820
    return poOGRProduct;
3821
}
3822
3823
/************************************************************************/
3824
/*                     BuildGeometryFromTwoGeoms()                      */
3825
/************************************************************************/
3826
3827
static OGRGeometry *BuildGeometryFromTwoGeoms(
3828
    const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3829
    GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
3830
                                       const GEOSGeometry *,
3831
                                       const GEOSGeometry *))
3832
{
3833
    OGRGeometry *poOGRProduct = nullptr;
3834
3835
    GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3836
    GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3837
    GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3838
    if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3839
    {
3840
        GEOSGeom hGeosProduct =
3841
            pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3842
3843
        poOGRProduct =
3844
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
3845
    }
3846
    GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3847
    GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3848
    poSelf->freeGEOSContext(hGEOSCtxt);
3849
3850
    return poOGRProduct;
3851
}
3852
3853
/************************************************************************/
3854
/*                       OGRGEOSBooleanPredicate()                      */
3855
/************************************************************************/
3856
3857
static OGRBoolean OGRGEOSBooleanPredicate(
3858
    const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
3859
    char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
3860
                              const GEOSGeometry *))
3861
{
3862
    OGRBoolean bResult = FALSE;
3863
3864
    GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
3865
    GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
3866
    GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
3867
    if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
3868
    {
3869
        bResult = pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
3870
    }
3871
    GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
3872
    GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
3873
    poSelf->freeGEOSContext(hGEOSCtxt);
3874
3875
    return bResult;
3876
}
3877
3878
#endif  // HAVE_GEOS
3879
3880
/************************************************************************/
3881
/*                            MakeValid()                               */
3882
/************************************************************************/
3883
3884
/**
3885
 * \brief Attempts to make an invalid geometry valid without losing vertices.
3886
 *
3887
 * Already-valid geometries are cloned without further intervention
3888
 * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
3889
 * may be subject to non-significant transformations, such as duplicated point
3890
 * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
3891
 * geometry collections could be returned a single geometry. GDAL 3.10
3892
 * returns the same type of geometry).
3893
 *
3894
 * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
3895
 * post-processing step is often desired.
3896
 *
3897
 * This method is the same as the C function OGR_G_MakeValid().
3898
 *
3899
 * This function is built on the GEOS >= 3.8 library, check it for the
3900
 * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
3901
 * library, this function will return a clone of the input geometry if it is
3902
 * valid, or NULL if it is invalid
3903
 *
3904
 * @param papszOptions NULL terminated list of options, or NULL. The following
3905
 * options are available:
3906
 * <ul>
3907
 * <li>METHOD=LINEWORK/STRUCTURE.
3908
 *     LINEWORK is the default method, which combines all rings into a set of
3909
 *     noded lines and then extracts valid polygons from that linework.
3910
 *     The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
3911
 *     all rings valid, then merges shells and
3912
 *     subtracts holes from shells to generate valid result. Assumes that
3913
 *     holes and shells are correctly categorized.</li>
3914
 * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
3915
 *     NO (default): collapses are converted to empty geometries
3916
 *     YES: collapses are converted to a valid geometry of lower dimension.</li>
3917
 * </ul>
3918
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
3919
 *
3920
 * @since GDAL 3.0
3921
 */
3922
OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
3923
0
{
3924
0
    (void)papszOptions;
3925
0
#ifndef HAVE_GEOS
3926
0
    if (IsValid())
3927
0
        return clone();
3928
3929
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
3930
0
    return nullptr;
3931
#else
3932
    if (IsSFCGALCompatible())
3933
    {
3934
        if (IsValid())
3935
            return clone();
3936
    }
3937
    else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
3938
    {
3939
        GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
3940
        OGRBoolean bIsValid = FALSE;
3941
        GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
3942
        if (hGeosGeom)
3943
        {
3944
            bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom);
3945
            GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
3946
        }
3947
        freeGEOSContext(hGEOSCtxt);
3948
        if (bIsValid)
3949
            return clone();
3950
    }
3951
3952
    const bool bStructureMethod = EQUAL(
3953
        CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
3954
    CPL_IGNORE_RET_VAL(bStructureMethod);
3955
#if !(GEOS_VERSION_MAJOR > 3 ||                                                \
3956
      (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
3957
    if (bStructureMethod)
3958
    {
3959
        CPLError(CE_Failure, CPLE_NotSupported,
3960
                 "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
3961
        return nullptr;
3962
    }
3963
#endif
3964
3965
    OGRGeometry *poOGRProduct = nullptr;
3966
3967
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
3968
    GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
3969
    if (hGeosGeom != nullptr)
3970
    {
3971
        GEOSGeom hGEOSRet;
3972
#if GEOS_VERSION_MAJOR > 3 ||                                                  \
3973
    (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
3974
        if (bStructureMethod)
3975
        {
3976
            GEOSMakeValidParams *params =
3977
                GEOSMakeValidParams_create_r(hGEOSCtxt);
3978
            CPLAssert(params);
3979
            GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
3980
                                            GEOS_MAKE_VALID_STRUCTURE);
3981
            GEOSMakeValidParams_setKeepCollapsed_r(
3982
                hGEOSCtxt, params,
3983
                CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
3984
            hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
3985
            GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
3986
        }
3987
        else
3988
#endif
3989
        {
3990
            hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
3991
        }
3992
        GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
3993
3994
        if (hGEOSRet != nullptr)
3995
        {
3996
            poOGRProduct =
3997
                OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
3998
            if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
3999
                poOGRProduct->assignSpatialReference(getSpatialReference());
4000
            poOGRProduct =
4001
                OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
4002
            GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
4003
4004
#if GEOS_VERSION_MAJOR > 3 ||                                                  \
4005
    (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
4006
            // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
4007
            // if the input is a multiple geometry
4008
            if (poOGRProduct && bStructureMethod &&
4009
                OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
4010
                !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
4011
                                     wkbGeometryCollection))
4012
            {
4013
                poOGRProduct = OGRGeometryFactory::forceTo(poOGRProduct,
4014
                                                           getGeometryType());
4015
            }
4016
#endif
4017
        }
4018
    }
4019
    freeGEOSContext(hGEOSCtxt);
4020
4021
    return poOGRProduct;
4022
#endif
4023
0
}
4024
4025
/************************************************************************/
4026
/*                         OGR_G_MakeValid()                            */
4027
/************************************************************************/
4028
4029
/**
4030
 * \brief Attempts to make an invalid geometry valid without losing vertices.
4031
 *
4032
 * Already-valid geometries are cloned without further intervention.
4033
 *
4034
 * This function is the same as the C++ method OGRGeometry::MakeValid().
4035
 *
4036
 * This function is built on the GEOS >= 3.8 library, check it for the
4037
 * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
4038
 * library, this function will return a clone of the input geometry if it is
4039
 * valid, or NULL if it is invalid
4040
 *
4041
 * @param hGeom The Geometry to make valid.
4042
 *
4043
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4044
 * or NULL if an error occurs.
4045
 *
4046
 * @since GDAL 3.0
4047
 */
4048
4049
OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
4050
4051
0
{
4052
0
    VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
4053
4054
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
4055
0
}
4056
4057
/************************************************************************/
4058
/*                         OGR_G_MakeValidEx()                            */
4059
/************************************************************************/
4060
4061
/**
4062
 * \brief Attempts to make an invalid geometry valid without losing vertices.
4063
 *
4064
 * Already-valid geometries are cloned without further intervention.
4065
 *
4066
 * This function is the same as the C++ method OGRGeometry::MakeValid().
4067
 *
4068
 * See documentation of that method for possible options.
4069
 *
4070
 * @param hGeom The Geometry to make valid.
4071
 * @param papszOptions Options.
4072
 *
4073
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4074
 * or NULL if an error occurs.
4075
 *
4076
 * @since GDAL 3.4
4077
 */
4078
4079
OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
4080
4081
0
{
4082
0
    VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
4083
4084
0
    return OGRGeometry::ToHandle(
4085
0
        OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
4086
0
}
4087
4088
/************************************************************************/
4089
/*                            Normalize()                               */
4090
/************************************************************************/
4091
4092
/**
4093
 * \brief Attempts to bring geometry into normalized/canonical form.
4094
 *
4095
 * This method is the same as the C function OGR_G_Normalize().
4096
 *
4097
 * This function is built on the GEOS library; check it for the definition
4098
 * of the geometry operation.
4099
 * If OGR is built without the GEOS library, this function will always fail,
4100
 * issuing a CPLE_NotSupported error.
4101
 *
4102
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4103
 *
4104
 * @since GDAL 3.3
4105
 */
4106
OGRGeometry *OGRGeometry::Normalize() const
4107
0
{
4108
0
#ifndef HAVE_GEOS
4109
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4110
0
    return nullptr;
4111
#else
4112
    OGRGeometry *poOGRProduct = nullptr;
4113
4114
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4115
    GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4116
    if (hGeosGeom != nullptr)
4117
    {
4118
4119
        int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
4120
4121
        if (hGEOSRet == 0)
4122
        {
4123
            poOGRProduct =
4124
                BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
4125
        }
4126
        else
4127
        {
4128
            GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4129
        }
4130
    }
4131
    freeGEOSContext(hGEOSCtxt);
4132
4133
    return poOGRProduct;
4134
#endif
4135
0
}
4136
4137
/************************************************************************/
4138
/*                         OGR_G_Normalize()                            */
4139
/************************************************************************/
4140
4141
/**
4142
 * \brief Attempts to bring geometry into normalized/canonical form.
4143
 *
4144
 * This function is the same as the C++ method OGRGeometry::Normalize().
4145
 *
4146
 * This function is built on the GEOS library; check it for the definition
4147
 * of the geometry operation.
4148
 * If OGR is built without the GEOS library, this function will always fail,
4149
 * issuing a CPLE_NotSupported error.
4150
 * @param hGeom The Geometry to normalize.
4151
 *
4152
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4153
 * or NULL if an error occurs.
4154
 *
4155
 * @since GDAL 3.3
4156
 */
4157
4158
OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
4159
4160
0
{
4161
0
    VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
4162
4163
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
4164
0
}
4165
4166
/************************************************************************/
4167
/*                             ConvexHull()                             */
4168
/************************************************************************/
4169
4170
/**
4171
 * \brief Compute convex hull.
4172
 *
4173
 * A new geometry object is created and returned containing the convex
4174
 * hull of the geometry on which the method is invoked.
4175
 *
4176
 * This method is the same as the C function OGR_G_ConvexHull().
4177
 *
4178
 * This method is built on the GEOS library, check it for the definition
4179
 * of the geometry operation.
4180
 * If OGR is built without the GEOS library, this method will always fail,
4181
 * issuing a CPLE_NotSupported error.
4182
 *
4183
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4184
 */
4185
4186
OGRGeometry *OGRGeometry::ConvexHull() const
4187
4188
0
{
4189
0
    if (IsSFCGALCompatible())
4190
0
    {
4191
0
#ifndef HAVE_SFCGAL
4192
4193
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4194
0
        return nullptr;
4195
4196
#else
4197
4198
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4199
        if (poThis == nullptr)
4200
            return nullptr;
4201
4202
        sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
4203
        OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4204
        if (h_prodGeom)
4205
            h_prodGeom->assignSpatialReference(getSpatialReference());
4206
4207
        sfcgal_geometry_delete(poThis);
4208
        sfcgal_geometry_delete(poRes);
4209
4210
        return h_prodGeom;
4211
4212
#endif
4213
0
    }
4214
4215
0
    else
4216
0
    {
4217
0
#ifndef HAVE_GEOS
4218
4219
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4220
0
        return nullptr;
4221
4222
#else
4223
4224
        OGRGeometry *poOGRProduct = nullptr;
4225
4226
        GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4227
        GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4228
        if (hGeosGeom != nullptr)
4229
        {
4230
            GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
4231
            GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4232
4233
            poOGRProduct =
4234
                BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4235
        }
4236
        freeGEOSContext(hGEOSCtxt);
4237
4238
        return poOGRProduct;
4239
4240
#endif /* HAVE_GEOS */
4241
0
    }
4242
0
}
4243
4244
/************************************************************************/
4245
/*                          OGR_G_ConvexHull()                          */
4246
/************************************************************************/
4247
/**
4248
 * \brief Compute convex hull.
4249
 *
4250
 * A new geometry object is created and returned containing the convex
4251
 * hull of the geometry on which the method is invoked.
4252
 *
4253
 * This function is the same as the C++ method OGRGeometry::ConvexHull().
4254
 *
4255
 * This function is built on the GEOS library, check it for the definition
4256
 * of the geometry operation.
4257
 * If OGR is built without the GEOS library, this function will always fail,
4258
 * issuing a CPLE_NotSupported error.
4259
 *
4260
 * @param hTarget The Geometry to calculate the convex hull of.
4261
 *
4262
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4263
 * or NULL if an error occurs.
4264
 */
4265
4266
OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
4267
4268
0
{
4269
0
    VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
4270
4271
0
    return OGRGeometry::ToHandle(
4272
0
        OGRGeometry::FromHandle(hTarget)->ConvexHull());
4273
0
}
4274
4275
/************************************************************************/
4276
/*                             ConcaveHull()                            */
4277
/************************************************************************/
4278
4279
/**
4280
 * \brief Compute "concave hull" of a geometry.
4281
 *
4282
 * The concave hull is fully contained within the convex hull and also
4283
 * contains all the points of the input, but in a smaller area.
4284
 * The area ratio is the ratio of the area of the convex hull and the concave
4285
 * hull. Frequently used to convert a multi-point into a polygonal area.
4286
 * that contains all the points in the input Geometry.
4287
 *
4288
 * A new geometry object is created and returned containing the concave
4289
 * hull of the geometry on which the method is invoked.
4290
 *
4291
 * This method is the same as the C function OGR_G_ConcaveHull().
4292
 *
4293
 * This method is built on the GEOS >= 3.11 library
4294
 * If OGR is built without the GEOS >= 3.11 librray, this method will always
4295
 * fail, issuing a CPLE_NotSupported error.
4296
 *
4297
 * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4298
 * @param bAllowHoles Whether holes are allowed.
4299
 *
4300
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4301
 *
4302
 * @since GDAL 3.6
4303
 */
4304
4305
OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
4306
0
{
4307
0
#ifndef HAVE_GEOS
4308
0
    (void)dfRatio;
4309
0
    (void)bAllowHoles;
4310
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4311
0
    return nullptr;
4312
#elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
4313
    (void)dfRatio;
4314
    (void)bAllowHoles;
4315
    CPLError(CE_Failure, CPLE_NotSupported,
4316
             "GEOS 3.11 or later needed for ConcaveHull.");
4317
    return nullptr;
4318
#else
4319
    OGRGeometry *poOGRProduct = nullptr;
4320
4321
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4322
    GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4323
    if (hGeosGeom != nullptr)
4324
    {
4325
        GEOSGeom hGeosHull =
4326
            GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
4327
        GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4328
4329
        poOGRProduct =
4330
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
4331
    }
4332
    freeGEOSContext(hGEOSCtxt);
4333
4334
    return poOGRProduct;
4335
#endif /* HAVE_GEOS */
4336
0
}
4337
4338
/************************************************************************/
4339
/*                          OGR_G_ConcaveHull()                         */
4340
/************************************************************************/
4341
/**
4342
 * \brief Compute "concave hull" of a geometry.
4343
 *
4344
 * The concave hull is fully contained within the convex hull and also
4345
 * contains all the points of the input, but in a smaller area.
4346
 * The area ratio is the ratio of the area of the convex hull and the concave
4347
 * hull. Frequently used to convert a multi-point into a polygonal area.
4348
 * that contains all the points in the input Geometry.
4349
 *
4350
 * A new geometry object is created and returned containing the convex
4351
 * hull of the geometry on which the function is invoked.
4352
 *
4353
 * This function is the same as the C++ method OGRGeometry::ConcaveHull().
4354
 *
4355
 * This function is built on the GEOS >= 3.11 library
4356
 * If OGR is built without the GEOS >= 3.11 librray, this function will always
4357
 * fail, issuing a CPLE_NotSupported error.
4358
 *
4359
 * @param hTarget The Geometry to calculate the concave hull of.
4360
 * @param dfRatio Ratio of the area of the convex hull and the concave hull.
4361
 * @param bAllowHoles Whether holes are allowed.
4362
 *
4363
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4364
 * or NULL if an error occurs.
4365
 *
4366
 * @since GDAL 3.6
4367
 */
4368
4369
OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
4370
                               bool bAllowHoles)
4371
4372
0
{
4373
0
    VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
4374
4375
0
    return OGRGeometry::ToHandle(
4376
0
        OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
4377
0
}
4378
4379
/************************************************************************/
4380
/*                            Boundary()                                */
4381
/************************************************************************/
4382
4383
/**
4384
 * \brief Compute boundary.
4385
 *
4386
 * A new geometry object is created and returned containing the boundary
4387
 * of the geometry on which the method is invoked.
4388
 *
4389
 * This method is the same as the C function OGR_G_Boundary().
4390
 *
4391
 * This method is built on the GEOS library, check it for the definition
4392
 * of the geometry operation.
4393
 * If OGR is built without the GEOS library, this method will always fail,
4394
 * issuing a CPLE_NotSupported error.
4395
 *
4396
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4397
 *
4398
 * @since OGR 1.8.0
4399
 */
4400
4401
OGRGeometry *OGRGeometry::Boundary() const
4402
4403
0
{
4404
0
#ifndef HAVE_GEOS
4405
4406
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4407
0
    return nullptr;
4408
4409
#else
4410
4411
    OGRGeometry *poOGRProduct = nullptr;
4412
4413
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4414
    GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4415
    if (hGeosGeom != nullptr)
4416
    {
4417
        GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
4418
        GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4419
4420
        poOGRProduct =
4421
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4422
    }
4423
    freeGEOSContext(hGEOSCtxt);
4424
4425
    return poOGRProduct;
4426
4427
#endif  // HAVE_GEOS
4428
0
}
4429
4430
//! @cond Doxygen_Suppress
4431
/**
4432
 * \brief Compute boundary (deprecated)
4433
 *
4434
 * @deprecated
4435
 *
4436
 * @see Boundary()
4437
 */
4438
OGRGeometry *OGRGeometry::getBoundary() const
4439
4440
0
{
4441
0
    return Boundary();
4442
0
}
4443
4444
//! @endcond
4445
4446
/************************************************************************/
4447
/*                         OGR_G_Boundary()                             */
4448
/************************************************************************/
4449
/**
4450
 * \brief Compute boundary.
4451
 *
4452
 * A new geometry object is created and returned containing the boundary
4453
 * of the geometry on which the method is invoked.
4454
 *
4455
 * This function is the same as the C++ method OGR_G_Boundary().
4456
 *
4457
 * This function is built on the GEOS library, check it for the definition
4458
 * of the geometry operation.
4459
 * If OGR is built without the GEOS library, this function will always fail,
4460
 * issuing a CPLE_NotSupported error.
4461
 *
4462
 * @param hTarget The Geometry to calculate the boundary of.
4463
 *
4464
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4465
 * or NULL if an error occurs.
4466
 *
4467
 * @since OGR 1.8.0
4468
 */
4469
OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
4470
4471
0
{
4472
0
    VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
4473
4474
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4475
0
}
4476
4477
/**
4478
 * \brief Compute boundary (deprecated)
4479
 *
4480
 * @deprecated
4481
 *
4482
 * @see OGR_G_Boundary()
4483
 */
4484
OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
4485
4486
0
{
4487
0
    VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
4488
4489
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
4490
0
}
4491
4492
/************************************************************************/
4493
/*                               Buffer()                               */
4494
/************************************************************************/
4495
4496
/**
4497
 * \brief Compute buffer of geometry.
4498
 *
4499
 * Builds a new geometry containing the buffer region around the geometry
4500
 * on which it is invoked.  The buffer is a polygon containing the region within
4501
 * the buffer distance of the original geometry.
4502
 *
4503
 * Some buffer sections are properly described as curves, but are converted to
4504
 * approximate polygons.  The nQuadSegs parameter can be used to control how
4505
 * many segments should be used to define a 90 degree curve - a quadrant of a
4506
 * circle.  A value of 30 is a reasonable default.  Large values result in
4507
 * large numbers of vertices in the resulting buffer geometry while small
4508
 * numbers reduce the accuracy of the result.
4509
 *
4510
 * This method is the same as the C function OGR_G_Buffer().
4511
 *
4512
 * This method is built on the GEOS library, check it for the definition
4513
 * of the geometry operation.
4514
 * If OGR is built without the GEOS library, this method will always fail,
4515
 * issuing a CPLE_NotSupported error.
4516
 *
4517
 * @param dfDist the buffer distance to be applied. Should be expressed into
4518
 *               the same unit as the coordinates of the geometry.
4519
 *
4520
 * @param nQuadSegs the number of segments used to approximate a 90
4521
 * degree (quadrant) of curvature.
4522
 *
4523
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4524
 */
4525
4526
OGRGeometry *OGRGeometry::Buffer(UNUSED_IF_NO_GEOS double dfDist,
4527
                                 UNUSED_IF_NO_GEOS int nQuadSegs) const
4528
4529
0
{
4530
0
#ifndef HAVE_GEOS
4531
4532
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4533
0
    return nullptr;
4534
4535
#else
4536
4537
    OGRGeometry *poOGRProduct = nullptr;
4538
4539
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4540
    GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4541
    if (hGeosGeom != nullptr)
4542
    {
4543
        GEOSGeom hGeosProduct =
4544
            GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
4545
        GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4546
4547
        poOGRProduct =
4548
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
4549
    }
4550
    freeGEOSContext(hGEOSCtxt);
4551
4552
    return poOGRProduct;
4553
4554
#endif  // HAVE_GEOS
4555
0
}
4556
4557
/************************************************************************/
4558
/*                            OGR_G_Buffer()                            */
4559
/************************************************************************/
4560
4561
/**
4562
 * \brief Compute buffer of geometry.
4563
 *
4564
 * Builds a new geometry containing the buffer region around the geometry
4565
 * on which it is invoked.  The buffer is a polygon containing the region within
4566
 * the buffer distance of the original geometry.
4567
 *
4568
 * Some buffer sections are properly described as curves, but are converted to
4569
 * approximate polygons.  The nQuadSegs parameter can be used to control how
4570
 * many segments should be used to define a 90 degree curve - a quadrant of a
4571
 * circle.  A value of 30 is a reasonable default.  Large values result in
4572
 * large numbers of vertices in the resulting buffer geometry while small
4573
 * numbers reduce the accuracy of the result.
4574
 *
4575
 * This function is the same as the C++ method OGRGeometry::Buffer().
4576
 *
4577
 * This function is built on the GEOS library, check it for the definition
4578
 * of the geometry operation.
4579
 * If OGR is built without the GEOS library, this function will always fail,
4580
 * issuing a CPLE_NotSupported error.
4581
 *
4582
 * @param hTarget the geometry.
4583
 * @param dfDist the buffer distance to be applied. Should be expressed into
4584
 *               the same unit as the coordinates of the geometry.
4585
 *
4586
 * @param nQuadSegs the number of segments used to approximate a 90 degree
4587
 * (quadrant) of curvature.
4588
 *
4589
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4590
 * or NULL if an error occurs.
4591
 */
4592
4593
OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
4594
4595
0
{
4596
0
    VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
4597
4598
0
    return OGRGeometry::ToHandle(
4599
0
        OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
4600
0
}
4601
4602
/**
4603
 * \brief Compute buffer of geometry.
4604
 *
4605
 * Builds a new geometry containing the buffer region around the geometry
4606
 * on which it is invoked.  The buffer is a polygon containing the region within
4607
 * the buffer distance of the original geometry.
4608
 *
4609
 * This function is built on the GEOS library, check it for the definition
4610
 * of the geometry operation.
4611
 * If OGR is built without the GEOS library, this function will always fail,
4612
 * issuing a CPLE_NotSupported error.
4613
 *
4614
 * The following options are supported. See the GEOS library for more detailed
4615
 * descriptions.
4616
 *
4617
 * <ul>
4618
 * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
4619
 * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
4620
 * <li>MITRE_LIMIT=double</li>
4621
 * <li>QUADRANT_SEGMENTS=int</li>
4622
 * <li>SINGLE_SIDED=YES/NO</li>
4623
 * </ul>
4624
 *
4625
 * This function is the same as the C function OGR_G_BufferEx().
4626
 *
4627
 * @param dfDist the buffer distance to be applied. Should be expressed into
4628
 *               the same unit as the coordinates of the geometry.
4629
 * @param papszOptions NULL terminated list of options (may be NULL)
4630
 *
4631
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
4632
 *
4633
 * @since GDAL 3.10
4634
 */
4635
4636
OGRGeometry *
4637
OGRGeometry::BufferEx(UNUSED_IF_NO_GEOS double dfDist,
4638
                      UNUSED_IF_NO_GEOS CSLConstList papszOptions) const
4639
0
{
4640
0
#ifndef HAVE_GEOS
4641
4642
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4643
0
    return nullptr;
4644
4645
#else
4646
    OGRGeometry *poOGRProduct = nullptr;
4647
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
4648
4649
    auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
4650
    bool bParamsAreValid = true;
4651
4652
    for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
4653
    {
4654
        if (EQUAL(pszParam, "ENDCAP_STYLE"))
4655
        {
4656
            int nStyle;
4657
            if (EQUAL(pszValue, "ROUND"))
4658
            {
4659
                nStyle = GEOSBUF_CAP_ROUND;
4660
            }
4661
            else if (EQUAL(pszValue, "FLAT"))
4662
            {
4663
                nStyle = GEOSBUF_CAP_FLAT;
4664
            }
4665
            else if (EQUAL(pszValue, "SQUARE"))
4666
            {
4667
                nStyle = GEOSBUF_CAP_SQUARE;
4668
            }
4669
            else
4670
            {
4671
                bParamsAreValid = false;
4672
                CPLError(CE_Failure, CPLE_NotSupported,
4673
                         "Invalid value for ENDCAP_STYLE: %s", pszValue);
4674
                break;
4675
            }
4676
4677
            if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
4678
            {
4679
                bParamsAreValid = false;
4680
            }
4681
        }
4682
        else if (EQUAL(pszParam, "JOIN_STYLE"))
4683
        {
4684
            int nStyle;
4685
            if (EQUAL(pszValue, "ROUND"))
4686
            {
4687
                nStyle = GEOSBUF_JOIN_ROUND;
4688
            }
4689
            else if (EQUAL(pszValue, "MITRE"))
4690
            {
4691
                nStyle = GEOSBUF_JOIN_MITRE;
4692
            }
4693
            else if (EQUAL(pszValue, "BEVEL"))
4694
            {
4695
                nStyle = GEOSBUF_JOIN_BEVEL;
4696
            }
4697
            else
4698
            {
4699
                bParamsAreValid = false;
4700
                CPLError(CE_Failure, CPLE_NotSupported,
4701
                         "Invalid value for JOIN_STYLE: %s", pszValue);
4702
                break;
4703
            }
4704
4705
            if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
4706
            {
4707
                bParamsAreValid = false;
4708
                break;
4709
            }
4710
        }
4711
        else if (EQUAL(pszParam, "MITRE_LIMIT"))
4712
        {
4713
            try
4714
            {
4715
                std::size_t end;
4716
                double dfLimit = std::stod(pszValue, &end);
4717
4718
                if (end != strlen(pszValue))
4719
                {
4720
                    throw std::invalid_argument("");
4721
                }
4722
4723
                if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
4724
                                                      dfLimit))
4725
                {
4726
                    bParamsAreValid = false;
4727
                    break;
4728
                }
4729
            }
4730
            catch (const std::invalid_argument &)
4731
            {
4732
                bParamsAreValid = false;
4733
                CPLError(CE_Failure, CPLE_IllegalArg,
4734
                         "Invalid value for MITRE_LIMIT: %s", pszValue);
4735
            }
4736
            catch (const std::out_of_range &)
4737
            {
4738
                bParamsAreValid = false;
4739
                CPLError(CE_Failure, CPLE_IllegalArg,
4740
                         "Invalid value for MITRE_LIMIT: %s", pszValue);
4741
            }
4742
        }
4743
        else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
4744
        {
4745
            try
4746
            {
4747
                std::size_t end;
4748
                int nQuadSegs = std::stoi(pszValue, &end, 10);
4749
4750
                if (end != strlen(pszValue))
4751
                {
4752
                    throw std::invalid_argument("");
4753
                }
4754
4755
                if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
4756
                                                            nQuadSegs))
4757
                {
4758
                    bParamsAreValid = false;
4759
                    break;
4760
                }
4761
            }
4762
            catch (const std::invalid_argument &)
4763
            {
4764
                bParamsAreValid = false;
4765
                CPLError(CE_Failure, CPLE_IllegalArg,
4766
                         "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
4767
            }
4768
            catch (const std::out_of_range &)
4769
            {
4770
                bParamsAreValid = false;
4771
                CPLError(CE_Failure, CPLE_IllegalArg,
4772
                         "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
4773
            }
4774
        }
4775
        else if (EQUAL(pszParam, "SINGLE_SIDED"))
4776
        {
4777
            bool bSingleSided = CPLTestBool(pszValue);
4778
4779
            if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
4780
                                                   bSingleSided))
4781
            {
4782
                bParamsAreValid = false;
4783
                break;
4784
            }
4785
        }
4786
        else
4787
        {
4788
            bParamsAreValid = false;
4789
            CPLError(CE_Failure, CPLE_NotSupported,
4790
                     "Unsupported buffer option: %s", pszValue);
4791
        }
4792
    }
4793
4794
    if (bParamsAreValid)
4795
    {
4796
        GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
4797
        if (hGeosGeom != nullptr)
4798
        {
4799
            GEOSGeom hGeosProduct =
4800
                GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
4801
            GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
4802
4803
            if (hGeosProduct != nullptr)
4804
            {
4805
                poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
4806
                                                     this, nullptr);
4807
            }
4808
        }
4809
    }
4810
4811
    GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
4812
    freeGEOSContext(hGEOSCtxt);
4813
    return poOGRProduct;
4814
#endif
4815
0
}
4816
4817
/**
4818
 * \brief Compute buffer of geometry.
4819
 *
4820
 * Builds a new geometry containing the buffer region around the geometry
4821
 * on which it is invoked.  The buffer is a polygon containing the region within
4822
 * the buffer distance of the original geometry.
4823
 *
4824
 * This function is built on the GEOS library, check it for the definition
4825
 * of the geometry operation.
4826
 * If OGR is built without the GEOS library, this function will always fail,
4827
 * issuing a CPLE_NotSupported error.
4828
 *
4829
 * The following options are supported. See the GEOS library for more detailed
4830
 * descriptions.
4831
 *
4832
 * <ul>
4833
 * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
4834
 * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
4835
 * <li>MITRE_LIMIT=double</li>
4836
 * <li>QUADRANT_SEGMENTS=int</li>
4837
 * <li>SINGLE_SIDED=YES/NO</li>
4838
 * </ul>
4839
 *
4840
 * This function is the same as the C++ method OGRGeometry::BufferEx().
4841
 *
4842
 * @param hTarget the geometry.
4843
 * @param dfDist the buffer distance to be applied. Should be expressed into
4844
 *               the same unit as the coordinates of the geometry.
4845
 * @param papszOptions NULL terminated list of options (may be NULL)
4846
 *
4847
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4848
 * or NULL if an error occurs.
4849
 *
4850
 * @since GDAL 3.10
4851
 */
4852
4853
OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
4854
                            CSLConstList papszOptions)
4855
4856
0
{
4857
0
    VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
4858
4859
0
    return OGRGeometry::ToHandle(
4860
0
        OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
4861
0
}
4862
4863
/************************************************************************/
4864
/*                            Intersection()                            */
4865
/************************************************************************/
4866
4867
/**
4868
 * \brief Compute intersection.
4869
 *
4870
 * Generates a new geometry which is the region of intersection of the
4871
 * two geometries operated on.  The Intersects() method can be used to test if
4872
 * two geometries intersect.
4873
 *
4874
 * Geometry validity is not checked. In case you are unsure of the validity
4875
 * of the input geometries, call IsValid() before, otherwise the result might
4876
 * be wrong.
4877
 *
4878
 * This method is the same as the C function OGR_G_Intersection().
4879
 *
4880
 * This method is built on the GEOS library, check it for the definition
4881
 * of the geometry operation.
4882
 * If OGR is built without the GEOS library, this method will always fail,
4883
 * issuing a CPLE_NotSupported error.
4884
 *
4885
 * @param poOtherGeom the other geometry intersected with "this" geometry.
4886
 *
4887
 * @return a new geometry to be freed by the caller, or NULL if there is no
4888
 * intersection or if an error occurs.
4889
 *
4890
 */
4891
4892
OGRGeometry *
4893
OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
4894
4895
0
{
4896
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
4897
0
    {
4898
0
#ifndef HAVE_SFCGAL
4899
4900
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
4901
0
        return nullptr;
4902
4903
#else
4904
4905
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
4906
        if (poThis == nullptr)
4907
            return nullptr;
4908
4909
        sfcgal_geometry_t *poOther =
4910
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
4911
        if (poOther == nullptr)
4912
        {
4913
            sfcgal_geometry_delete(poThis);
4914
            return nullptr;
4915
        }
4916
4917
        sfcgal_geometry_t *poRes =
4918
            sfcgal_geometry_intersection_3d(poThis, poOther);
4919
        OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
4920
        if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
4921
            poOtherGeom->getSpatialReference() != nullptr &&
4922
            poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
4923
            h_prodGeom->assignSpatialReference(getSpatialReference());
4924
4925
        sfcgal_geometry_delete(poThis);
4926
        sfcgal_geometry_delete(poOther);
4927
        sfcgal_geometry_delete(poRes);
4928
4929
        return h_prodGeom;
4930
4931
#endif
4932
0
    }
4933
4934
0
    else
4935
0
    {
4936
0
#ifndef HAVE_GEOS
4937
4938
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
4939
0
        return nullptr;
4940
4941
#else
4942
        return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
4943
#endif /* HAVE_GEOS */
4944
0
    }
4945
0
}
4946
4947
/************************************************************************/
4948
/*                         OGR_G_Intersection()                         */
4949
/************************************************************************/
4950
4951
/**
4952
 * \brief Compute intersection.
4953
 *
4954
 * Generates a new geometry which is the region of intersection of the
4955
 * two geometries operated on.  The OGR_G_Intersects() function can be used to
4956
 * test if two geometries intersect.
4957
 *
4958
 * Geometry validity is not checked. In case you are unsure of the validity
4959
 * of the input geometries, call IsValid() before, otherwise the result might
4960
 * be wrong.
4961
 *
4962
 * This function is the same as the C++ method OGRGeometry::Intersection().
4963
 *
4964
 * This function is built on the GEOS library, check it for the definition
4965
 * of the geometry operation.
4966
 * If OGR is built without the GEOS library, this function will always fail,
4967
 * issuing a CPLE_NotSupported error.
4968
 *
4969
 * @param hThis the geometry.
4970
 * @param hOther the other geometry.
4971
 *
4972
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
4973
 * or NULL if there is not intersection of if an error occurs.
4974
 */
4975
4976
OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
4977
4978
0
{
4979
0
    VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
4980
4981
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
4982
0
        OGRGeometry::FromHandle(hOther)));
4983
0
}
4984
4985
/************************************************************************/
4986
/*                               Union()                                */
4987
/************************************************************************/
4988
4989
/**
4990
 * \brief Compute union.
4991
 *
4992
 * Generates a new geometry which is the region of union of the
4993
 * two geometries operated on.
4994
 *
4995
 * Geometry validity is not checked. In case you are unsure of the validity
4996
 * of the input geometries, call IsValid() before, otherwise the result might
4997
 * be wrong.
4998
 *
4999
 * This method is the same as the C function OGR_G_Union().
5000
 *
5001
 * This method is built on the GEOS library, check it for the definition
5002
 * of the geometry operation.
5003
 * If OGR is built without the GEOS library, this method will always fail,
5004
 * issuing a CPLE_NotSupported error.
5005
 *
5006
 * @param poOtherGeom the other geometry unioned with "this" geometry.
5007
 *
5008
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5009
 */
5010
5011
OGRGeometry *
5012
OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5013
5014
0
{
5015
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5016
0
    {
5017
0
#ifndef HAVE_SFCGAL
5018
5019
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5020
0
        return nullptr;
5021
5022
#else
5023
5024
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5025
        if (poThis == nullptr)
5026
            return nullptr;
5027
5028
        sfcgal_geometry_t *poOther =
5029
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5030
        if (poOther == nullptr)
5031
        {
5032
            sfcgal_geometry_delete(poThis);
5033
            return nullptr;
5034
        }
5035
5036
        sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
5037
        OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5038
        if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5039
            poOtherGeom->getSpatialReference() != nullptr &&
5040
            poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5041
            h_prodGeom->assignSpatialReference(getSpatialReference());
5042
5043
        sfcgal_geometry_delete(poThis);
5044
        sfcgal_geometry_delete(poOther);
5045
        sfcgal_geometry_delete(poRes);
5046
5047
        return h_prodGeom;
5048
5049
#endif
5050
0
    }
5051
5052
0
    else
5053
0
    {
5054
0
#ifndef HAVE_GEOS
5055
5056
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5057
0
        return nullptr;
5058
5059
#else
5060
        return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
5061
#endif /* HAVE_GEOS */
5062
0
    }
5063
0
}
5064
5065
/************************************************************************/
5066
/*                            OGR_G_Union()                             */
5067
/************************************************************************/
5068
5069
/**
5070
 * \brief Compute union.
5071
 *
5072
 * Generates a new geometry which is the region of union of the
5073
 * two geometries operated on.
5074
 *
5075
 * Geometry validity is not checked. In case you are unsure of the validity
5076
 * of the input geometries, call IsValid() before, otherwise the result might
5077
 * be wrong.
5078
 *
5079
 * This function is the same as the C++ method OGRGeometry::Union().
5080
 *
5081
 * This function is built on the GEOS library, check it for the definition
5082
 * of the geometry operation.
5083
 * If OGR is built without the GEOS library, this function will always fail,
5084
 * issuing a CPLE_NotSupported error.
5085
 *
5086
 * @param hThis the geometry.
5087
 * @param hOther the other geometry.
5088
 *
5089
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5090
 * or NULL if an error occurs.
5091
 */
5092
5093
OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
5094
5095
0
{
5096
0
    VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
5097
5098
0
    return OGRGeometry::ToHandle(
5099
0
        OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
5100
0
}
5101
5102
/************************************************************************/
5103
/*                               UnionCascaded()                        */
5104
/************************************************************************/
5105
5106
/**
5107
 * \brief Compute union using cascading.
5108
 *
5109
 * Geometry validity is not checked. In case you are unsure of the validity
5110
 * of the input geometries, call IsValid() before, otherwise the result might
5111
 * be wrong.
5112
 *
5113
 * The input geometry must be a MultiPolygon.
5114
 *
5115
 * This method is the same as the C function OGR_G_UnionCascaded().
5116
 *
5117
 * This method is built on the GEOS library, check it for the definition
5118
 * of the geometry operation.
5119
 * If OGR is built without the GEOS library, this method will always fail,
5120
 * issuing a CPLE_NotSupported error.
5121
 *
5122
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5123
 *
5124
 * @since OGR 1.8.0
5125
 *
5126
 * @deprecated Use UnaryUnion() instead
5127
 */
5128
5129
OGRGeometry *OGRGeometry::UnionCascaded() const
5130
5131
0
{
5132
0
#ifndef HAVE_GEOS
5133
5134
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5135
0
    return nullptr;
5136
#else
5137
5138
#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5139
    if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
5140
    {
5141
        // GEOS < 3.11 crashes on an empty multipolygon input
5142
        auto poRet = new OGRGeometryCollection();
5143
        poRet->assignSpatialReference(getSpatialReference());
5144
        return poRet;
5145
    }
5146
#endif
5147
    OGRGeometry *poOGRProduct = nullptr;
5148
5149
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5150
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5151
    if (hThisGeosGeom != nullptr)
5152
    {
5153
        GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
5154
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5155
5156
        poOGRProduct =
5157
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5158
    }
5159
    freeGEOSContext(hGEOSCtxt);
5160
5161
    return poOGRProduct;
5162
5163
#endif  // HAVE_GEOS
5164
0
}
5165
5166
/************************************************************************/
5167
/*                            OGR_G_UnionCascaded()                     */
5168
/************************************************************************/
5169
5170
/**
5171
 * \brief Compute union using cascading.
5172
 *
5173
 * Geometry validity is not checked. In case you are unsure of the validity
5174
 * of the input geometries, call IsValid() before, otherwise the result might
5175
 * be wrong.
5176
 *
5177
 * The input geometry must be a MultiPolygon.
5178
 *
5179
 * This function is the same as the C++ method OGRGeometry::UnionCascaded().
5180
 *
5181
 * This function is built on the GEOS library, check it for the definition
5182
 * of the geometry operation.
5183
 * If OGR is built without the GEOS library, this function will always fail,
5184
 * issuing a CPLE_NotSupported error.
5185
 *
5186
 * @param hThis the geometry.
5187
 *
5188
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5189
 * or NULL if an error occurs.
5190
 *
5191
 * @deprecated Use OGR_G_UnaryUnion() instead
5192
 */
5193
5194
OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
5195
5196
0
{
5197
0
    VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
5198
5199
0
    return OGRGeometry::ToHandle(
5200
0
        OGRGeometry::FromHandle(hThis)->UnionCascaded());
5201
0
}
5202
5203
/************************************************************************/
5204
/*                               UnaryUnion()                           */
5205
/************************************************************************/
5206
5207
/**
5208
 * \brief Returns the union of all components of a single geometry.
5209
 *
5210
 * Usually used to convert a collection into the smallest set of polygons that
5211
 * cover the same area.
5212
 *
5213
 * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5214
 *
5215
 * This method is the same as the C function OGR_G_UnaryUnion().
5216
 *
5217
 * This method is built on the GEOS library, check it for the definition
5218
 * of the geometry operation.
5219
 * If OGR is built without the GEOS library, this method will always fail,
5220
 * issuing a CPLE_NotSupported error.
5221
 *
5222
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
5223
 *
5224
 * @since GDAL 3.7
5225
 */
5226
5227
OGRGeometry *OGRGeometry::UnaryUnion() const
5228
5229
0
{
5230
0
#ifndef HAVE_GEOS
5231
5232
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5233
0
    return nullptr;
5234
#else
5235
5236
#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
5237
    if (IsEmpty())
5238
    {
5239
        // GEOS < 3.11 crashes on an empty geometry
5240
        auto poRet = new OGRGeometryCollection();
5241
        poRet->assignSpatialReference(getSpatialReference());
5242
        return poRet;
5243
    }
5244
#endif
5245
    OGRGeometry *poOGRProduct = nullptr;
5246
5247
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
5248
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
5249
    if (hThisGeosGeom != nullptr)
5250
    {
5251
        GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
5252
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
5253
5254
        poOGRProduct =
5255
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
5256
    }
5257
    freeGEOSContext(hGEOSCtxt);
5258
5259
    return poOGRProduct;
5260
5261
#endif  // HAVE_GEOS
5262
0
}
5263
5264
/************************************************************************/
5265
/*                            OGR_G_UnaryUnion()                        */
5266
/************************************************************************/
5267
5268
/**
5269
 * \brief Returns the union of all components of a single geometry.
5270
 *
5271
 * Usually used to convert a collection into the smallest set of polygons that
5272
 * cover the same area.
5273
 *
5274
 * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
5275
 *
5276
 * Geometry validity is not checked. In case you are unsure of the validity
5277
 * of the input geometries, call IsValid() before, otherwise the result might
5278
 * be wrong.
5279
 *
5280
 * This function is the same as the C++ method OGRGeometry::UnaryUnion().
5281
 *
5282
 * This function is built on the GEOS library, check it for the definition
5283
 * of the geometry operation.
5284
 * If OGR is built without the GEOS library, this function will always fail,
5285
 * issuing a CPLE_NotSupported error.
5286
 *
5287
 * @param hThis the geometry.
5288
 *
5289
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5290
 * or NULL if an error occurs.
5291
 *
5292
 * @since GDAL 3.7
5293
 */
5294
5295
OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
5296
5297
0
{
5298
0
    VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
5299
5300
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
5301
0
}
5302
5303
/************************************************************************/
5304
/*                             Difference()                             */
5305
/************************************************************************/
5306
5307
/**
5308
 * \brief Compute difference.
5309
 *
5310
 * Generates a new geometry which is the region of this geometry with the
5311
 * region of the second geometry removed.
5312
 *
5313
 * Geometry validity is not checked. In case you are unsure of the validity
5314
 * of the input geometries, call IsValid() before, otherwise the result might
5315
 * be wrong.
5316
 *
5317
 * This method is the same as the C function OGR_G_Difference().
5318
 *
5319
 * This method is built on the GEOS library, check it for the definition
5320
 * of the geometry operation.
5321
 * If OGR is built without the GEOS library, this method will always fail,
5322
 * issuing a CPLE_NotSupported error.
5323
 *
5324
 * @param poOtherGeom the other geometry removed from "this" geometry.
5325
 *
5326
 * @return a new geometry to be freed by the caller, or NULL if the difference
5327
 * is empty or if an error occurs.
5328
 */
5329
5330
OGRGeometry *
5331
OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5332
5333
0
{
5334
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5335
0
    {
5336
0
#ifndef HAVE_SFCGAL
5337
5338
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5339
0
        return nullptr;
5340
5341
#else
5342
5343
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5344
        if (poThis == nullptr)
5345
            return nullptr;
5346
5347
        sfcgal_geometry_t *poOther =
5348
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5349
        if (poOther == nullptr)
5350
        {
5351
            sfcgal_geometry_delete(poThis);
5352
            return nullptr;
5353
        }
5354
5355
        sfcgal_geometry_t *poRes =
5356
            sfcgal_geometry_difference_3d(poThis, poOther);
5357
        OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
5358
        if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
5359
            poOtherGeom->getSpatialReference() != nullptr &&
5360
            poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
5361
            h_prodGeom->assignSpatialReference(getSpatialReference());
5362
5363
        sfcgal_geometry_delete(poThis);
5364
        sfcgal_geometry_delete(poOther);
5365
        sfcgal_geometry_delete(poRes);
5366
5367
        return h_prodGeom;
5368
5369
#endif
5370
0
    }
5371
5372
0
    else
5373
0
    {
5374
0
#ifndef HAVE_GEOS
5375
5376
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5377
0
        return nullptr;
5378
5379
#else
5380
        return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
5381
#endif /* HAVE_GEOS */
5382
0
    }
5383
0
}
5384
5385
/************************************************************************/
5386
/*                          OGR_G_Difference()                          */
5387
/************************************************************************/
5388
5389
/**
5390
 * \brief Compute difference.
5391
 *
5392
 * Generates a new geometry which is the region of this geometry with the
5393
 * region of the other geometry removed.
5394
 *
5395
 * Geometry validity is not checked. In case you are unsure of the validity
5396
 * of the input geometries, call IsValid() before, otherwise the result might
5397
 * be wrong.
5398
 *
5399
 * This function is the same as the C++ method OGRGeometry::Difference().
5400
 *
5401
 * This function is built on the GEOS library, check it for the definition
5402
 * of the geometry operation.
5403
 * If OGR is built without the GEOS library, this function will always fail,
5404
 * issuing a CPLE_NotSupported error.
5405
 *
5406
 * @param hThis the geometry.
5407
 * @param hOther the other geometry.
5408
 *
5409
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5410
 * or NULL if the difference is empty or if an error occurs.
5411
 */
5412
5413
OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
5414
5415
0
{
5416
0
    VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
5417
5418
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
5419
0
        OGRGeometry::FromHandle(hOther)));
5420
0
}
5421
5422
/************************************************************************/
5423
/*                        SymDifference()                               */
5424
/************************************************************************/
5425
5426
/**
5427
 * \brief Compute symmetric difference.
5428
 *
5429
 * Generates a new geometry which is the symmetric difference of this
5430
 * geometry and the second geometry passed into the method.
5431
 *
5432
 * Geometry validity is not checked. In case you are unsure of the validity
5433
 * of the input geometries, call IsValid() before, otherwise the result might
5434
 * be wrong.
5435
 *
5436
 * This method is the same as the C function OGR_G_SymDifference().
5437
 *
5438
 * This method is built on the GEOS library, check it for the definition
5439
 * of the geometry operation.
5440
 * If OGR is built without the GEOS library, this method will always fail,
5441
 * issuing a CPLE_NotSupported error.
5442
 *
5443
 * @param poOtherGeom the other geometry.
5444
 *
5445
 * @return a new geometry to be freed by the caller, or NULL if the difference
5446
 * is empty or if an error occurs.
5447
 *
5448
 * @since OGR 1.8.0
5449
 */
5450
5451
OGRGeometry *OGRGeometry::SymDifference(
5452
    UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5453
5454
0
{
5455
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5456
0
    {
5457
0
#ifndef HAVE_SFCGAL
5458
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5459
0
        return nullptr;
5460
#else
5461
        OGRGeometry *poFirstDifference = Difference(poOtherGeom);
5462
        if (poFirstDifference == nullptr)
5463
            return nullptr;
5464
5465
        OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
5466
        if (poOtherDifference == nullptr)
5467
        {
5468
            delete poFirstDifference;
5469
            return nullptr;
5470
        }
5471
5472
        OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
5473
        delete poFirstDifference;
5474
        delete poOtherDifference;
5475
        return poSymDiff;
5476
#endif
5477
0
    }
5478
5479
0
#ifndef HAVE_GEOS
5480
5481
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5482
0
    return nullptr;
5483
5484
#else
5485
    return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
5486
#endif  // HAVE_GEOS
5487
0
}
5488
5489
//! @cond Doxygen_Suppress
5490
/**
5491
 * \brief Compute symmetric difference (deprecated)
5492
 *
5493
 * @deprecated
5494
 *
5495
 * @see OGRGeometry::SymDifference()
5496
 */
5497
OGRGeometry *
5498
OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
5499
5500
0
{
5501
0
    return SymDifference(poOtherGeom);
5502
0
}
5503
5504
//! @endcond
5505
5506
/************************************************************************/
5507
/*                      OGR_G_SymDifference()                           */
5508
/************************************************************************/
5509
5510
/**
5511
 * \brief Compute symmetric difference.
5512
 *
5513
 * Generates a new geometry which is the symmetric difference of this
5514
 * geometry and the other geometry.
5515
 *
5516
 * Geometry validity is not checked. In case you are unsure of the validity
5517
 * of the input geometries, call IsValid() before, otherwise the result might
5518
 * be wrong.
5519
 *
5520
 * This function is the same as the C++ method
5521
 * OGRGeometry::SymmetricDifference().
5522
 *
5523
 * This function is built on the GEOS library, check it for the definition
5524
 * of the geometry operation.
5525
 * If OGR is built without the GEOS library, this function will always fail,
5526
 * issuing a CPLE_NotSupported error.
5527
 *
5528
 * @param hThis the geometry.
5529
 * @param hOther the other geometry.
5530
 *
5531
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
5532
 * or NULL if the difference is empty or if an error occurs.
5533
 *
5534
 * @since OGR 1.8.0
5535
 */
5536
5537
OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5538
5539
0
{
5540
0
    VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
5541
5542
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5543
0
        OGRGeometry::FromHandle(hOther)));
5544
0
}
5545
5546
/**
5547
 * \brief Compute symmetric difference (deprecated)
5548
 *
5549
 * @deprecated
5550
 *
5551
 * @see OGR_G_SymmetricDifference()
5552
 */
5553
OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
5554
5555
0
{
5556
0
    VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
5557
5558
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
5559
0
        OGRGeometry::FromHandle(hOther)));
5560
0
}
5561
5562
/************************************************************************/
5563
/*                              Disjoint()                              */
5564
/************************************************************************/
5565
5566
/**
5567
 * \brief Test for disjointness.
5568
 *
5569
 * Tests if this geometry and the other passed into the method are disjoint.
5570
 *
5571
 * Geometry validity is not checked. In case you are unsure of the validity
5572
 * of the input geometries, call IsValid() before, otherwise the result might
5573
 * be wrong.
5574
 *
5575
 * This method is the same as the C function OGR_G_Disjoint().
5576
 *
5577
 * This method is built on the GEOS library, check it for the definition
5578
 * of the geometry operation.
5579
 * If OGR is built without the GEOS library, this method will always fail,
5580
 * issuing a CPLE_NotSupported error.
5581
 *
5582
 * @param poOtherGeom the geometry to compare to this geometry.
5583
 *
5584
 * @return TRUE if they are disjoint, otherwise FALSE.
5585
 */
5586
5587
OGRBoolean
5588
OGRGeometry::Disjoint(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5589
5590
0
{
5591
0
#ifndef HAVE_GEOS
5592
5593
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5594
0
    return FALSE;
5595
5596
#else
5597
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
5598
#endif  // HAVE_GEOS
5599
0
}
5600
5601
/************************************************************************/
5602
/*                           OGR_G_Disjoint()                           */
5603
/************************************************************************/
5604
5605
/**
5606
 * \brief Test for disjointness.
5607
 *
5608
 * Tests if this geometry and the other geometry are disjoint.
5609
 *
5610
 * Geometry validity is not checked. In case you are unsure of the validity
5611
 * of the input geometries, call IsValid() before, otherwise the result might
5612
 * be wrong.
5613
 *
5614
 * This function is the same as the C++ method OGRGeometry::Disjoint().
5615
 *
5616
 * This function is built on the GEOS library, check it for the definition
5617
 * of the geometry operation.
5618
 * If OGR is built without the GEOS library, this function will always fail,
5619
 * issuing a CPLE_NotSupported error.
5620
 *
5621
 * @param hThis the geometry to compare.
5622
 * @param hOther the other geometry to compare.
5623
 *
5624
 * @return TRUE if they are disjoint, otherwise FALSE.
5625
 */
5626
int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
5627
5628
0
{
5629
0
    VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
5630
5631
0
    return OGRGeometry::FromHandle(hThis)->Disjoint(
5632
0
        OGRGeometry::FromHandle(hOther));
5633
0
}
5634
5635
/************************************************************************/
5636
/*                              Touches()                               */
5637
/************************************************************************/
5638
5639
/**
5640
 * \brief Test for touching.
5641
 *
5642
 * Tests if this geometry and the other passed into the method are touching.
5643
 *
5644
 * Geometry validity is not checked. In case you are unsure of the validity
5645
 * of the input geometries, call IsValid() before, otherwise the result might
5646
 * be wrong.
5647
 *
5648
 * This method is the same as the C function OGR_G_Touches().
5649
 *
5650
 * This method is built on the GEOS library, check it for the definition
5651
 * of the geometry operation.
5652
 * If OGR is built without the GEOS library, this method will always fail,
5653
 * issuing a CPLE_NotSupported error.
5654
 *
5655
 * @param poOtherGeom the geometry to compare to this geometry.
5656
 *
5657
 * @return TRUE if they are touching, otherwise FALSE.
5658
 */
5659
5660
OGRBoolean
5661
OGRGeometry::Touches(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5662
5663
0
{
5664
0
#ifndef HAVE_GEOS
5665
5666
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5667
0
    return FALSE;
5668
5669
#else
5670
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
5671
#endif  // HAVE_GEOS
5672
0
}
5673
5674
/************************************************************************/
5675
/*                           OGR_G_Touches()                            */
5676
/************************************************************************/
5677
/**
5678
 * \brief Test for touching.
5679
 *
5680
 * Tests if this geometry and the other geometry are touching.
5681
 *
5682
 * Geometry validity is not checked. In case you are unsure of the validity
5683
 * of the input geometries, call IsValid() before, otherwise the result might
5684
 * be wrong.
5685
 *
5686
 * This function is the same as the C++ method OGRGeometry::Touches().
5687
 *
5688
 * This function is built on the GEOS library, check it for the definition
5689
 * of the geometry operation.
5690
 * If OGR is built without the GEOS library, this function will always fail,
5691
 * issuing a CPLE_NotSupported error.
5692
 *
5693
 * @param hThis the geometry to compare.
5694
 * @param hOther the other geometry to compare.
5695
 *
5696
 * @return TRUE if they are touching, otherwise FALSE.
5697
 */
5698
5699
int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
5700
5701
0
{
5702
0
    VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
5703
5704
0
    return OGRGeometry::FromHandle(hThis)->Touches(
5705
0
        OGRGeometry::FromHandle(hOther));
5706
0
}
5707
5708
/************************************************************************/
5709
/*                              Crosses()                               */
5710
/************************************************************************/
5711
5712
/**
5713
 * \brief Test for crossing.
5714
 *
5715
 * Tests if this geometry and the other passed into the method are crossing.
5716
 *
5717
 * Geometry validity is not checked. In case you are unsure of the validity
5718
 * of the input geometries, call IsValid() before, otherwise the result might
5719
 * be wrong.
5720
 *
5721
 * This method is the same as the C function OGR_G_Crosses().
5722
 *
5723
 * This method is built on the GEOS library, check it for the definition
5724
 * of the geometry operation.
5725
 * If OGR is built without the GEOS library, this method will always fail,
5726
 * issuing a CPLE_NotSupported error.
5727
 *
5728
 * @param poOtherGeom the geometry to compare to this geometry.
5729
 *
5730
 * @return TRUE if they are crossing, otherwise FALSE.
5731
 */
5732
5733
OGRBoolean
5734
OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
5735
5736
0
{
5737
0
    if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
5738
0
    {
5739
0
#ifndef HAVE_SFCGAL
5740
5741
0
        CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
5742
0
        return FALSE;
5743
5744
#else
5745
5746
        sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
5747
        if (poThis == nullptr)
5748
            return FALSE;
5749
5750
        sfcgal_geometry_t *poOther =
5751
            OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
5752
        if (poOther == nullptr)
5753
        {
5754
            sfcgal_geometry_delete(poThis);
5755
            return FALSE;
5756
        }
5757
5758
        int res = sfcgal_geometry_intersects_3d(poThis, poOther);
5759
5760
        sfcgal_geometry_delete(poThis);
5761
        sfcgal_geometry_delete(poOther);
5762
5763
        return (res == 1) ? TRUE : FALSE;
5764
5765
#endif
5766
0
    }
5767
5768
0
    else
5769
0
    {
5770
5771
0
#ifndef HAVE_GEOS
5772
5773
0
        CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5774
0
        return FALSE;
5775
5776
#else
5777
        return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
5778
#endif /* HAVE_GEOS */
5779
0
    }
5780
0
}
5781
5782
/************************************************************************/
5783
/*                           OGR_G_Crosses()                            */
5784
/************************************************************************/
5785
/**
5786
 * \brief Test for crossing.
5787
 *
5788
 * Tests if this geometry and the other geometry are crossing.
5789
 *
5790
 * Geometry validity is not checked. In case you are unsure of the validity
5791
 * of the input geometries, call IsValid() before, otherwise the result might
5792
 * be wrong.
5793
 *
5794
 * This function is the same as the C++ method OGRGeometry::Crosses().
5795
 *
5796
 * This function is built on the GEOS library, check it for the definition
5797
 * of the geometry operation.
5798
 * If OGR is built without the GEOS library, this function will always fail,
5799
 * issuing a CPLE_NotSupported error.
5800
 *
5801
 * @param hThis the geometry to compare.
5802
 * @param hOther the other geometry to compare.
5803
 *
5804
 * @return TRUE if they are crossing, otherwise FALSE.
5805
 */
5806
5807
int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
5808
5809
0
{
5810
0
    VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
5811
5812
0
    return OGRGeometry::FromHandle(hThis)->Crosses(
5813
0
        OGRGeometry::FromHandle(hOther));
5814
0
}
5815
5816
/************************************************************************/
5817
/*                               Within()                               */
5818
/************************************************************************/
5819
5820
/**
5821
 * \brief Test for containment.
5822
 *
5823
 * Tests if actual geometry object is within the passed geometry.
5824
 *
5825
 * Geometry validity is not checked. In case you are unsure of the validity
5826
 * of the input geometries, call IsValid() before, otherwise the result might
5827
 * be wrong.
5828
 *
5829
 * This method is the same as the C function OGR_G_Within().
5830
 *
5831
 * This method is built on the GEOS library, check it for the definition
5832
 * of the geometry operation.
5833
 * If OGR is built without the GEOS library, this method will always fail,
5834
 * issuing a CPLE_NotSupported error.
5835
 *
5836
 * @param poOtherGeom the geometry to compare to this geometry.
5837
 *
5838
 * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
5839
 */
5840
5841
OGRBoolean
5842
OGRGeometry::Within(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5843
5844
0
{
5845
0
#ifndef HAVE_GEOS
5846
5847
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5848
0
    return FALSE;
5849
5850
#else
5851
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
5852
#endif  // HAVE_GEOS
5853
0
}
5854
5855
/************************************************************************/
5856
/*                            OGR_G_Within()                            */
5857
/************************************************************************/
5858
5859
/**
5860
 * \brief Test for containment.
5861
 *
5862
 * Tests if this geometry is within the other geometry.
5863
 *
5864
 * Geometry validity is not checked. In case you are unsure of the validity
5865
 * of the input geometries, call IsValid() before, otherwise the result might
5866
 * be wrong.
5867
 *
5868
 * This function is the same as the C++ method OGRGeometry::Within().
5869
 *
5870
 * This function is built on the GEOS library, check it for the definition
5871
 * of the geometry operation.
5872
 * If OGR is built without the GEOS library, this function will always fail,
5873
 * issuing a CPLE_NotSupported error.
5874
 *
5875
 * @param hThis the geometry to compare.
5876
 * @param hOther the other geometry to compare.
5877
 *
5878
 * @return TRUE if hThis is within hOther, otherwise FALSE.
5879
 */
5880
int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
5881
5882
0
{
5883
0
    VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
5884
5885
0
    return OGRGeometry::FromHandle(hThis)->Within(
5886
0
        OGRGeometry::FromHandle(hOther));
5887
0
}
5888
5889
/************************************************************************/
5890
/*                              Contains()                              */
5891
/************************************************************************/
5892
5893
/**
5894
 * \brief Test for containment.
5895
 *
5896
 * Tests if actual geometry object contains the passed geometry.
5897
 *
5898
 * Geometry validity is not checked. In case you are unsure of the validity
5899
 * of the input geometries, call IsValid() before, otherwise the result might
5900
 * be wrong.
5901
 *
5902
 * This method is the same as the C function OGR_G_Contains().
5903
 *
5904
 * This method is built on the GEOS library, check it for the definition
5905
 * of the geometry operation.
5906
 * If OGR is built without the GEOS library, this method will always fail,
5907
 * issuing a CPLE_NotSupported error.
5908
 *
5909
 * @param poOtherGeom the geometry to compare to this geometry.
5910
 *
5911
 * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
5912
 */
5913
5914
OGRBoolean
5915
OGRGeometry::Contains(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5916
5917
0
{
5918
0
#ifndef HAVE_GEOS
5919
5920
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5921
0
    return FALSE;
5922
5923
#else
5924
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
5925
#endif  // HAVE_GEOS
5926
0
}
5927
5928
/************************************************************************/
5929
/*                            OGR_G_Contains()                            */
5930
/************************************************************************/
5931
5932
/**
5933
 * \brief Test for containment.
5934
 *
5935
 * Tests if this geometry contains the other geometry.
5936
 *
5937
 * Geometry validity is not checked. In case you are unsure of the validity
5938
 * of the input geometries, call IsValid() before, otherwise the result might
5939
 * be wrong.
5940
 *
5941
 * This function is the same as the C++ method OGRGeometry::Contains().
5942
 *
5943
 * This function is built on the GEOS library, check it for the definition
5944
 * of the geometry operation.
5945
 * If OGR is built without the GEOS library, this function will always fail,
5946
 * issuing a CPLE_NotSupported error.
5947
 *
5948
 * @param hThis the geometry to compare.
5949
 * @param hOther the other geometry to compare.
5950
 *
5951
 * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
5952
 */
5953
int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
5954
5955
0
{
5956
0
    VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
5957
5958
0
    return OGRGeometry::FromHandle(hThis)->Contains(
5959
0
        OGRGeometry::FromHandle(hOther));
5960
0
}
5961
5962
/************************************************************************/
5963
/*                              Overlaps()                              */
5964
/************************************************************************/
5965
5966
/**
5967
 * \brief Test for overlap.
5968
 *
5969
 * Tests if this geometry and the other passed into the method overlap, that is
5970
 * their intersection has a non-zero area.
5971
 *
5972
 * Geometry validity is not checked. In case you are unsure of the validity
5973
 * of the input geometries, call IsValid() before, otherwise the result might
5974
 * be wrong.
5975
 *
5976
 * This method is the same as the C function OGR_G_Overlaps().
5977
 *
5978
 * This method is built on the GEOS library, check it for the definition
5979
 * of the geometry operation.
5980
 * If OGR is built without the GEOS library, this method will always fail,
5981
 * issuing a CPLE_NotSupported error.
5982
 *
5983
 * @param poOtherGeom the geometry to compare to this geometry.
5984
 *
5985
 * @return TRUE if they are overlapping, otherwise FALSE.
5986
 */
5987
5988
OGRBoolean
5989
OGRGeometry::Overlaps(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
5990
5991
0
{
5992
0
#ifndef HAVE_GEOS
5993
5994
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
5995
0
    return FALSE;
5996
5997
#else
5998
    return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
5999
#endif  // HAVE_GEOS
6000
0
}
6001
6002
/************************************************************************/
6003
/*                           OGR_G_Overlaps()                           */
6004
/************************************************************************/
6005
/**
6006
 * \brief Test for overlap.
6007
 *
6008
 * Tests if this geometry and the other geometry overlap, that is their
6009
 * intersection has a non-zero area.
6010
 *
6011
 * Geometry validity is not checked. In case you are unsure of the validity
6012
 * of the input geometries, call IsValid() before, otherwise the result might
6013
 * be wrong.
6014
 *
6015
 * This function is the same as the C++ method OGRGeometry::Overlaps().
6016
 *
6017
 * This function is built on the GEOS library, check it for the definition
6018
 * of the geometry operation.
6019
 * If OGR is built without the GEOS library, this function will always fail,
6020
 * issuing a CPLE_NotSupported error.
6021
 *
6022
 * @param hThis the geometry to compare.
6023
 * @param hOther the other geometry to compare.
6024
 *
6025
 * @return TRUE if they are overlapping, otherwise FALSE.
6026
 */
6027
6028
int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
6029
6030
0
{
6031
0
    VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
6032
6033
0
    return OGRGeometry::FromHandle(hThis)->Overlaps(
6034
0
        OGRGeometry::FromHandle(hOther));
6035
0
}
6036
6037
/************************************************************************/
6038
/*                             closeRings()                             */
6039
/************************************************************************/
6040
6041
/**
6042
 * \brief Force rings to be closed.
6043
 *
6044
 * If this geometry, or any contained geometries has polygon rings that
6045
 * are not closed, they will be closed by adding the starting point at
6046
 * the end.
6047
 */
6048
6049
void OGRGeometry::closeRings()
6050
0
{
6051
0
}
6052
6053
/************************************************************************/
6054
/*                          OGR_G_CloseRings()                          */
6055
/************************************************************************/
6056
6057
/**
6058
 * \brief Force rings to be closed.
6059
 *
6060
 * If this geometry, or any contained geometries has polygon rings that
6061
 * are not closed, they will be closed by adding the starting point at
6062
 * the end.
6063
 *
6064
 * @param hGeom handle to the geometry.
6065
 */
6066
6067
void OGR_G_CloseRings(OGRGeometryH hGeom)
6068
6069
0
{
6070
0
    VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
6071
6072
0
    OGRGeometry::FromHandle(hGeom)->closeRings();
6073
0
}
6074
6075
/************************************************************************/
6076
/*                              Centroid()                              */
6077
/************************************************************************/
6078
6079
/**
6080
 * \brief Compute the geometry centroid.
6081
 *
6082
 * The centroid location is applied to the passed in OGRPoint object.
6083
 * The centroid is not necessarily within the geometry.
6084
 *
6085
 * This method relates to the SFCOM ISurface::get_Centroid() method
6086
 * however the current implementation based on GEOS can operate on other
6087
 * geometry types such as multipoint, linestring, geometrycollection such as
6088
 * multipolygons.
6089
 * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6090
 * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6091
 * (multipolygons).
6092
 *
6093
 * This function is the same as the C function OGR_G_Centroid().
6094
 *
6095
 * This function is built on the GEOS library, check it for the definition
6096
 * of the geometry operation.
6097
 * If OGR is built without the GEOS library, this function will always fail,
6098
 * issuing a CPLE_NotSupported error.
6099
 *
6100
 * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6101
 *
6102
 * @since OGR 1.8.0 as a OGRGeometry method (previously was restricted
6103
 * to OGRPolygon)
6104
 */
6105
6106
OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
6107
6108
0
{
6109
0
    if (poPoint == nullptr)
6110
0
        return OGRERR_FAILURE;
6111
6112
0
#ifndef HAVE_GEOS
6113
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6114
0
    return OGRERR_FAILURE;
6115
6116
#else
6117
6118
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6119
    GEOSGeom hThisGeosGeom =
6120
        exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
6121
6122
    if (hThisGeosGeom != nullptr)
6123
    {
6124
        GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
6125
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6126
6127
        if (hOtherGeosGeom == nullptr)
6128
        {
6129
            freeGEOSContext(hGEOSCtxt);
6130
            return OGRERR_FAILURE;
6131
        }
6132
6133
        OGRGeometry *poCentroidGeom =
6134
            OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6135
6136
        GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6137
6138
        if (poCentroidGeom == nullptr)
6139
        {
6140
            freeGEOSContext(hGEOSCtxt);
6141
            return OGRERR_FAILURE;
6142
        }
6143
        if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6144
        {
6145
            delete poCentroidGeom;
6146
            freeGEOSContext(hGEOSCtxt);
6147
            return OGRERR_FAILURE;
6148
        }
6149
6150
        if (getSpatialReference() != nullptr)
6151
            poCentroidGeom->assignSpatialReference(getSpatialReference());
6152
6153
        OGRPoint *poCentroid = poCentroidGeom->toPoint();
6154
6155
        if (!poCentroid->IsEmpty())
6156
        {
6157
            poPoint->setX(poCentroid->getX());
6158
            poPoint->setY(poCentroid->getY());
6159
        }
6160
        else
6161
        {
6162
            poPoint->empty();
6163
        }
6164
6165
        delete poCentroidGeom;
6166
6167
        freeGEOSContext(hGEOSCtxt);
6168
        return OGRERR_NONE;
6169
    }
6170
    else
6171
    {
6172
        freeGEOSContext(hGEOSCtxt);
6173
        return OGRERR_FAILURE;
6174
    }
6175
6176
#endif  // HAVE_GEOS
6177
0
}
6178
6179
/************************************************************************/
6180
/*                           OGR_G_Centroid()                           */
6181
/************************************************************************/
6182
6183
/**
6184
 * \brief Compute the geometry centroid.
6185
 *
6186
 * The centroid location is applied to the passed in OGRPoint object.
6187
 * The centroid is not necessarily within the geometry.
6188
 *
6189
 * This method relates to the SFCOM ISurface::get_Centroid() method
6190
 * however the current implementation based on GEOS can operate on other
6191
 * geometry types such as multipoint, linestring, geometrycollection such as
6192
 * multipolygons.
6193
 * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
6194
 * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
6195
 * (multipolygons).
6196
 *
6197
 * This function is the same as the C++ method OGRGeometry::Centroid().
6198
 *
6199
 * This function is built on the GEOS library, check it for the definition
6200
 * of the geometry operation.
6201
 * If OGR is built without the GEOS library, this function will always fail,
6202
 * issuing a CPLE_NotSupported error.
6203
 *
6204
 * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
6205
 */
6206
6207
int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
6208
6209
0
{
6210
0
    VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
6211
6212
0
    OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
6213
0
    if (poCentroidGeom == nullptr)
6214
0
        return OGRERR_FAILURE;
6215
0
    if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
6216
0
    {
6217
0
        CPLError(CE_Failure, CPLE_AppDefined,
6218
0
                 "Passed wrong geometry type as centroid argument.");
6219
0
        return OGRERR_FAILURE;
6220
0
    }
6221
6222
0
    return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
6223
0
}
6224
6225
/************************************************************************/
6226
/*                        OGR_G_PointOnSurface()                        */
6227
/************************************************************************/
6228
6229
/**
6230
 * \brief Returns a point guaranteed to lie on the surface.
6231
 *
6232
 * This method relates to the SFCOM ISurface::get_PointOnSurface() method
6233
 * however the current implementation based on GEOS can operate on other
6234
 * geometry types than the types that are supported by SQL/MM-Part 3 :
6235
 * surfaces (polygons) and multisurfaces (multipolygons).
6236
 *
6237
 * This method is built on the GEOS library, check it for the definition
6238
 * of the geometry operation.
6239
 * If OGR is built without the GEOS library, this method will always fail,
6240
 * issuing a CPLE_NotSupported error.
6241
 *
6242
 * @param hGeom the geometry to operate on.
6243
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6244
 * or NULL if an error occurs.
6245
 *
6246
 * @since OGR 1.10
6247
 */
6248
6249
OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
6250
6251
0
{
6252
0
    VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
6253
6254
0
#ifndef HAVE_GEOS
6255
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6256
0
    return nullptr;
6257
#else
6258
6259
    OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
6260
6261
    GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
6262
    GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
6263
6264
    if (hThisGeosGeom != nullptr)
6265
    {
6266
        GEOSGeom hOtherGeosGeom =
6267
            GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
6268
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6269
6270
        if (hOtherGeosGeom == nullptr)
6271
        {
6272
            OGRGeometry::freeGEOSContext(hGEOSCtxt);
6273
            return nullptr;
6274
        }
6275
6276
        OGRGeometry *poInsidePointGeom =
6277
            OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
6278
6279
        GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
6280
6281
        if (poInsidePointGeom == nullptr)
6282
        {
6283
            OGRGeometry::freeGEOSContext(hGEOSCtxt);
6284
            return nullptr;
6285
        }
6286
        if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
6287
        {
6288
            delete poInsidePointGeom;
6289
            OGRGeometry::freeGEOSContext(hGEOSCtxt);
6290
            return nullptr;
6291
        }
6292
6293
        if (poThis->getSpatialReference() != nullptr)
6294
            poInsidePointGeom->assignSpatialReference(
6295
                poThis->getSpatialReference());
6296
6297
        OGRGeometry::freeGEOSContext(hGEOSCtxt);
6298
        return OGRGeometry::ToHandle(poInsidePointGeom);
6299
    }
6300
6301
    OGRGeometry::freeGEOSContext(hGEOSCtxt);
6302
    return nullptr;
6303
#endif
6304
0
}
6305
6306
/************************************************************************/
6307
/*                          PointOnSurfaceInternal()                    */
6308
/************************************************************************/
6309
6310
//! @cond Doxygen_Suppress
6311
OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
6312
0
{
6313
0
    if (poPoint == nullptr || poPoint->IsEmpty())
6314
0
        return OGRERR_FAILURE;
6315
6316
0
    OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
6317
0
        OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
6318
0
    if (hInsidePoint == nullptr)
6319
0
        return OGRERR_FAILURE;
6320
6321
0
    OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
6322
0
    if (poInsidePoint->IsEmpty())
6323
0
    {
6324
0
        poPoint->empty();
6325
0
    }
6326
0
    else
6327
0
    {
6328
0
        poPoint->setX(poInsidePoint->getX());
6329
0
        poPoint->setY(poInsidePoint->getY());
6330
0
    }
6331
6332
0
    OGR_G_DestroyGeometry(hInsidePoint);
6333
6334
0
    return OGRERR_NONE;
6335
0
}
6336
6337
//! @endcond
6338
6339
/************************************************************************/
6340
/*                              Simplify()                              */
6341
/************************************************************************/
6342
6343
/**
6344
 * \brief Simplify the geometry.
6345
 *
6346
 * This function is the same as the C function OGR_G_Simplify().
6347
 *
6348
 * This function is built on the GEOS library, check it for the definition
6349
 * of the geometry operation.
6350
 * If OGR is built without the GEOS library, this function will always fail,
6351
 * issuing a CPLE_NotSupported error.
6352
 *
6353
 * @param dTolerance the distance tolerance for the simplification.
6354
 *
6355
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6356
 *
6357
 * @since OGR 1.8.0
6358
 */
6359
6360
OGRGeometry *OGRGeometry::Simplify(UNUSED_IF_NO_GEOS double dTolerance) const
6361
6362
0
{
6363
0
#ifndef HAVE_GEOS
6364
6365
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6366
0
    return nullptr;
6367
6368
#else
6369
    OGRGeometry *poOGRProduct = nullptr;
6370
6371
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6372
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6373
    if (hThisGeosGeom != nullptr)
6374
    {
6375
        GEOSGeom hGeosProduct =
6376
            GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
6377
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6378
        poOGRProduct =
6379
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6380
    }
6381
    freeGEOSContext(hGEOSCtxt);
6382
    return poOGRProduct;
6383
6384
#endif  // HAVE_GEOS
6385
0
}
6386
6387
/************************************************************************/
6388
/*                         OGR_G_Simplify()                             */
6389
/************************************************************************/
6390
6391
/**
6392
 * \brief Compute a simplified geometry.
6393
 *
6394
 * This function is the same as the C++ method OGRGeometry::Simplify().
6395
 *
6396
 * This function is built on the GEOS library, check it for the definition
6397
 * of the geometry operation.
6398
 * If OGR is built without the GEOS library, this function will always fail,
6399
 * issuing a CPLE_NotSupported error.
6400
 *
6401
 * @param hThis the geometry.
6402
 * @param dTolerance the distance tolerance for the simplification.
6403
 *
6404
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6405
 * or NULL if an error occurs.
6406
 *
6407
 * @since OGR 1.8.0
6408
 */
6409
6410
OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
6411
6412
0
{
6413
0
    VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
6414
0
    return OGRGeometry::ToHandle(
6415
0
        OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
6416
0
}
6417
6418
/************************************************************************/
6419
/*                         SimplifyPreserveTopology()                   */
6420
/************************************************************************/
6421
6422
/**
6423
 * \brief Simplify the geometry while preserving topology.
6424
 *
6425
 * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
6426
 *
6427
 * This function is built on the GEOS library, check it for the definition
6428
 * of the geometry operation.
6429
 * If OGR is built without the GEOS library, this function will always fail,
6430
 * issuing a CPLE_NotSupported error.
6431
 *
6432
 * @param dTolerance the distance tolerance for the simplification.
6433
 *
6434
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6435
 *
6436
 * @since OGR 1.9.0
6437
 */
6438
6439
OGRGeometry *
6440
OGRGeometry::SimplifyPreserveTopology(UNUSED_IF_NO_GEOS double dTolerance) const
6441
6442
0
{
6443
0
#ifndef HAVE_GEOS
6444
6445
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6446
0
    return nullptr;
6447
6448
#else
6449
    OGRGeometry *poOGRProduct = nullptr;
6450
6451
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6452
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6453
    if (hThisGeosGeom != nullptr)
6454
    {
6455
        GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
6456
            hGEOSCtxt, hThisGeosGeom, dTolerance);
6457
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6458
        poOGRProduct =
6459
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6460
    }
6461
    freeGEOSContext(hGEOSCtxt);
6462
    return poOGRProduct;
6463
6464
#endif  // HAVE_GEOS
6465
0
}
6466
6467
/************************************************************************/
6468
/*                     OGR_G_SimplifyPreserveTopology()                 */
6469
/************************************************************************/
6470
6471
/**
6472
 * \brief Simplify the geometry while preserving topology.
6473
 *
6474
 * This function is the same as the C++ method
6475
 * OGRGeometry::SimplifyPreserveTopology().
6476
 *
6477
 * This function is built on the GEOS library, check it for the definition
6478
 * of the geometry operation.
6479
 * If OGR is built without the GEOS library, this function will always fail,
6480
 * issuing a CPLE_NotSupported error.
6481
 *
6482
 * @param hThis the geometry.
6483
 * @param dTolerance the distance tolerance for the simplification.
6484
 *
6485
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6486
 * or NULL if an error occurs.
6487
 *
6488
 * @since OGR 1.9.0
6489
 */
6490
6491
OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
6492
                                            double dTolerance)
6493
6494
0
{
6495
0
    VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
6496
0
    return OGRGeometry::ToHandle(
6497
0
        OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
6498
0
}
6499
6500
/************************************************************************/
6501
/*                           roundCoordinates()                         */
6502
/************************************************************************/
6503
6504
/** Round coordinates of the geometry to the specified precision.
6505
 *
6506
 * Note that this is not the same as OGRGeometry::SetPrecision(). The later
6507
 * will return valid geometries, whereas roundCoordinates() does not make
6508
 * such guarantee and may return geometries with invalidities, if they are
6509
 * not compatible of the specified precision. roundCoordinates() supports
6510
 * curve geometries, whereas SetPrecision() does not currently.
6511
 *
6512
 * One use case for roundCoordinates() is to undo the effect of
6513
 * quantizeCoordinates().
6514
 *
6515
 * @param sPrecision Contains the precision requirements.
6516
 * @since GDAL 3.9
6517
 */
6518
void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
6519
0
{
6520
0
    struct Rounder : public OGRDefaultGeometryVisitor
6521
0
    {
6522
0
        const OGRGeomCoordinatePrecision &m_precision;
6523
0
        const double m_invXYResolution;
6524
0
        const double m_invZResolution;
6525
0
        const double m_invMResolution;
6526
6527
0
        explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
6528
0
            : m_precision(sPrecisionIn),
6529
0
              m_invXYResolution(m_precision.dfXYResolution !=
6530
0
                                        OGRGeomCoordinatePrecision::UNKNOWN
6531
0
                                    ? 1.0 / m_precision.dfXYResolution
6532
0
                                    : 0.0),
6533
0
              m_invZResolution(m_precision.dfZResolution !=
6534
0
                                       OGRGeomCoordinatePrecision::UNKNOWN
6535
0
                                   ? 1.0 / m_precision.dfZResolution
6536
0
                                   : 0.0),
6537
0
              m_invMResolution(m_precision.dfMResolution !=
6538
0
                                       OGRGeomCoordinatePrecision::UNKNOWN
6539
0
                                   ? 1.0 / m_precision.dfMResolution
6540
0
                                   : 0.0)
6541
0
        {
6542
0
        }
6543
6544
0
        using OGRDefaultGeometryVisitor::visit;
6545
6546
0
        void visit(OGRPoint *poPoint) override
6547
0
        {
6548
0
            if (m_precision.dfXYResolution !=
6549
0
                OGRGeomCoordinatePrecision::UNKNOWN)
6550
0
            {
6551
0
                poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
6552
0
                              m_precision.dfXYResolution);
6553
0
                poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
6554
0
                              m_precision.dfXYResolution);
6555
0
            }
6556
0
            if (m_precision.dfZResolution !=
6557
0
                    OGRGeomCoordinatePrecision::UNKNOWN &&
6558
0
                poPoint->Is3D())
6559
0
            {
6560
0
                poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
6561
0
                              m_precision.dfZResolution);
6562
0
            }
6563
0
            if (m_precision.dfMResolution !=
6564
0
                    OGRGeomCoordinatePrecision::UNKNOWN &&
6565
0
                poPoint->IsMeasured())
6566
0
            {
6567
0
                poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
6568
0
                              m_precision.dfMResolution);
6569
0
            }
6570
0
        }
6571
0
    };
6572
6573
0
    Rounder rounder(sPrecision);
6574
0
    accept(&rounder);
6575
0
}
6576
6577
/************************************************************************/
6578
/*                           SetPrecision()                             */
6579
/************************************************************************/
6580
6581
/** Set the geometry's precision, rounding all its coordinates to the precision
6582
 * grid, and making sure the geometry is still valid.
6583
 *
6584
 * This is a stronger version of roundCoordinates().
6585
 *
6586
 * Note that at time of writing GEOS does no supported curve geometries. So
6587
 * currently if this function is called on such a geometry, OGR will first call
6588
 * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6589
 * that it is unlikely to yield to the expected result.
6590
 *
6591
 * This function is the same as the C function OGR_G_SetPrecision().
6592
 *
6593
 * This function is built on the GEOSGeom_setPrecision_r() function of the
6594
 * GEOS library. Check it for the definition of the geometry operation.
6595
 * If OGR is built without the GEOS library, this function will always fail,
6596
 * issuing a CPLE_NotSupported error.
6597
 *
6598
 * @param dfGridSize size of the precision grid, or 0 for FLOATING
6599
 *                 precision.
6600
 * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
6601
 *               and OGR_GEOS_PREC_KEEP_COLLAPSED
6602
 *
6603
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6604
 *
6605
 * @since GDAL 3.9
6606
 */
6607
6608
OGRGeometry *OGRGeometry::SetPrecision(UNUSED_IF_NO_GEOS double dfGridSize,
6609
                                       UNUSED_IF_NO_GEOS int nFlags) const
6610
0
{
6611
0
#ifndef HAVE_GEOS
6612
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6613
0
    return nullptr;
6614
6615
#else
6616
    OGRGeometry *poOGRProduct = nullptr;
6617
6618
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6619
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6620
    if (hThisGeosGeom != nullptr)
6621
    {
6622
        GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
6623
            hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
6624
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6625
        poOGRProduct =
6626
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6627
    }
6628
    freeGEOSContext(hGEOSCtxt);
6629
    return poOGRProduct;
6630
6631
#endif  // HAVE_GEOS
6632
0
}
6633
6634
/************************************************************************/
6635
/*                         OGR_G_SetPrecision()                         */
6636
/************************************************************************/
6637
6638
/** Set the geometry's precision, rounding all its coordinates to the precision
6639
 * grid, and making sure the geometry is still valid.
6640
 *
6641
 * This is a stronger version of roundCoordinates().
6642
 *
6643
 * Note that at time of writing GEOS does no supported curve geometries. So
6644
 * currently if this function is called on such a geometry, OGR will first call
6645
 * getLinearGeometry() on the input and getCurveGeometry() on the output, but
6646
 * that it is unlikely to yield to the expected result.
6647
 *
6648
 * This function is the same as the C++ method OGRGeometry::SetPrecision().
6649
 *
6650
 * This function is built on the GEOSGeom_setPrecision_r() function of the
6651
 * GEOS library. Check it for the definition of the geometry operation.
6652
 * If OGR is built without the GEOS library, this function will always fail,
6653
 * issuing a CPLE_NotSupported error.
6654
 *
6655
 * @param hThis the geometry.
6656
 * @param dfGridSize size of the precision grid, or 0 for FLOATING
6657
 *                 precision.
6658
 * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
6659
 *               and OGR_GEOS_PREC_KEEP_COLLAPSED
6660
 *
6661
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6662
 * or NULL if an error occurs.
6663
 *
6664
 * @since GDAL 3.9
6665
 */
6666
OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
6667
                                int nFlags)
6668
0
{
6669
0
    VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
6670
0
    return OGRGeometry::ToHandle(
6671
0
        OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
6672
0
}
6673
6674
/************************************************************************/
6675
/*                         DelaunayTriangulation()                      */
6676
/************************************************************************/
6677
6678
/**
6679
 * \brief Return a Delaunay triangulation of the vertices of the geometry.
6680
 *
6681
 * This function is the same as the C function OGR_G_DelaunayTriangulation().
6682
 *
6683
 * This function is built on the GEOS library, v3.4 or above.
6684
 * If OGR is built without the GEOS library, this function will always fail,
6685
 * issuing a CPLE_NotSupported error.
6686
 *
6687
 * @param dfTolerance optional snapping tolerance to use for improved robustness
6688
 * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
6689
 *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
6690
 *
6691
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6692
 *
6693
 * @since OGR 2.1
6694
 */
6695
6696
#ifndef HAVE_GEOS
6697
OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
6698
                                                int /*bOnlyEdges*/) const
6699
0
{
6700
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6701
0
    return nullptr;
6702
0
}
6703
#else
6704
OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
6705
                                                int bOnlyEdges) const
6706
{
6707
    OGRGeometry *poOGRProduct = nullptr;
6708
6709
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6710
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6711
    if (hThisGeosGeom != nullptr)
6712
    {
6713
        GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
6714
            hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
6715
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6716
        poOGRProduct =
6717
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
6718
    }
6719
    freeGEOSContext(hGEOSCtxt);
6720
    return poOGRProduct;
6721
}
6722
#endif
6723
6724
/************************************************************************/
6725
/*                     OGR_G_DelaunayTriangulation()                    */
6726
/************************************************************************/
6727
6728
/**
6729
 * \brief Return a Delaunay triangulation of the vertices of the geometry.
6730
 *
6731
 * This function is the same as the C++ method
6732
 * OGRGeometry::DelaunayTriangulation().
6733
 *
6734
 * This function is built on the GEOS library, v3.4 or above.
6735
 * If OGR is built without the GEOS library, this function will always fail,
6736
 * issuing a CPLE_NotSupported error.
6737
 *
6738
 * @param hThis the geometry.
6739
 * @param dfTolerance optional snapping tolerance to use for improved robustness
6740
 * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
6741
 *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
6742
 *
6743
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6744
 * or NULL if an error occurs.
6745
 *
6746
 * @since OGR 2.1
6747
 */
6748
6749
OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
6750
                                         int bOnlyEdges)
6751
6752
0
{
6753
0
    VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
6754
6755
0
    return OGRGeometry::ToHandle(
6756
0
        OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
6757
0
                                                              bOnlyEdges));
6758
0
}
6759
6760
/************************************************************************/
6761
/*                             Polygonize()                             */
6762
/************************************************************************/
6763
/* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
6764
/* Developed for Faunalia (http://www.faunalia.it) with funding from    */
6765
/* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
6766
/*                   AMBIENTALE                                         */
6767
/************************************************************************/
6768
6769
/**
6770
 * \brief Polygonizes a set of sparse edges.
6771
 *
6772
 * A new geometry object is created and returned containing a collection
6773
 * of reassembled Polygons: NULL will be returned if the input collection
6774
 * doesn't corresponds to a MultiLinestring, or when reassembling Edges
6775
 * into Polygons is impossible due to topological inconsistencies.
6776
 *
6777
 * This method is the same as the C function OGR_G_Polygonize().
6778
 *
6779
 * This method is built on the GEOS library, check it for the definition
6780
 * of the geometry operation.
6781
 * If OGR is built without the GEOS library, this method will always fail,
6782
 * issuing a CPLE_NotSupported error.
6783
 *
6784
 * @return a new geometry to be freed by the caller, or NULL if an error occurs.
6785
 *
6786
 * @since OGR 1.9.0
6787
 */
6788
6789
OGRGeometry *OGRGeometry::Polygonize() const
6790
6791
0
{
6792
0
#ifndef HAVE_GEOS
6793
6794
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6795
0
    return nullptr;
6796
6797
#else
6798
6799
    const OGRGeometryCollection *poColl = nullptr;
6800
    if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
6801
        wkbFlatten(getGeometryType()) == wkbMultiLineString)
6802
        poColl = toGeometryCollection();
6803
    else
6804
        return nullptr;
6805
6806
    const int nCount = poColl->getNumGeometries();
6807
6808
    OGRGeometry *poPolygsOGRGeom = nullptr;
6809
    bool bError = false;
6810
6811
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6812
6813
    GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
6814
    for (int ig = 0; ig < nCount; ig++)
6815
    {
6816
        GEOSGeom hGeosGeom = nullptr;
6817
        const OGRGeometry *poChild = poColl->getGeometryRef(ig);
6818
        if (poChild == nullptr ||
6819
            wkbFlatten(poChild->getGeometryType()) != wkbLineString)
6820
            bError = true;
6821
        else
6822
        {
6823
            hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
6824
            if (hGeosGeom == nullptr)
6825
                bError = true;
6826
        }
6827
        pahGeosGeomList[ig] = hGeosGeom;
6828
    }
6829
6830
    if (!bError)
6831
    {
6832
        GEOSGeom hGeosPolygs =
6833
            GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
6834
6835
        poPolygsOGRGeom =
6836
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
6837
    }
6838
6839
    for (int ig = 0; ig < nCount; ig++)
6840
    {
6841
        GEOSGeom hGeosGeom = pahGeosGeomList[ig];
6842
        if (hGeosGeom != nullptr)
6843
            GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
6844
    }
6845
    delete[] pahGeosGeomList;
6846
    freeGEOSContext(hGEOSCtxt);
6847
6848
    return poPolygsOGRGeom;
6849
6850
#endif  // HAVE_GEOS
6851
0
}
6852
6853
/************************************************************************/
6854
/*                          OGR_G_Polygonize()                          */
6855
/************************************************************************/
6856
/**
6857
 * \brief Polygonizes a set of sparse edges.
6858
 *
6859
 * A new geometry object is created and returned containing a collection
6860
 * of reassembled Polygons: NULL will be returned if the input collection
6861
 * doesn't corresponds to a MultiLinestring, or when reassembling Edges
6862
 * into Polygons is impossible due to topological inconsistencies.
6863
 *
6864
 * This function is the same as the C++ method OGRGeometry::Polygonize().
6865
 *
6866
 * This function is built on the GEOS library, check it for the definition
6867
 * of the geometry operation.
6868
 * If OGR is built without the GEOS library, this function will always fail,
6869
 * issuing a CPLE_NotSupported error.
6870
 *
6871
 * @param hTarget The Geometry to be polygonized.
6872
 *
6873
 * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
6874
 * or NULL if an error occurs.
6875
 *
6876
 * @since OGR 1.9.0
6877
 */
6878
6879
OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
6880
6881
0
{
6882
0
    VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
6883
6884
0
    return OGRGeometry::ToHandle(
6885
0
        OGRGeometry::FromHandle(hTarget)->Polygonize());
6886
0
}
6887
6888
/************************************************************************/
6889
/*                             BuildArea()                              */
6890
/************************************************************************/
6891
6892
/**
6893
 * \brief Polygonize a linework assuming inner polygons are holes.
6894
 *
6895
 * This method is the same as the C function OGR_G_BuildArea().
6896
 *
6897
 * Polygonization is performed similarly to OGRGeometry::Polygonize().
6898
 * Additionally, holes are dropped and the result is unified producing
6899
 * a single Polygon or a MultiPolygon.
6900
 *
6901
 * A new geometry object is created and returned: NULL on failure,
6902
 * empty GeometryCollection if the input geometry cannot be polygonized,
6903
 * Polygon or MultiPolygon on success.
6904
 *
6905
 * This method is built on the GEOSBuildArea_r() function of the GEOS
6906
 * library, check it for the definition of the geometry operation.
6907
 * If OGR is built without the GEOS library, this method will always fail,
6908
 * issuing a CPLE_NotSupported error.
6909
 *
6910
 * @return a newly allocated geometry now owned by the caller,
6911
 *         or NULL on failure.
6912
 *
6913
 * @since OGR 3.11
6914
 */
6915
6916
OGRGeometry *OGRGeometry::BuildArea() const
6917
6918
0
{
6919
0
#ifndef HAVE_GEOS
6920
6921
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
6922
0
    return nullptr;
6923
6924
#else
6925
6926
    OGRGeometry *poPolygsOGRGeom = nullptr;
6927
6928
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
6929
    GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
6930
    if (hThisGeosGeom != nullptr)
6931
    {
6932
        GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
6933
        poPolygsOGRGeom =
6934
            BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
6935
        GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
6936
    }
6937
    freeGEOSContext(hGEOSCtxt);
6938
6939
    return poPolygsOGRGeom;
6940
6941
#endif  // HAVE_GEOS
6942
0
}
6943
6944
/************************************************************************/
6945
/*                          OGR_G_BuildArea()                           */
6946
/************************************************************************/
6947
6948
/**
6949
 * \brief Polygonize a linework assuming inner polygons are holes.
6950
 *
6951
 * This function is the same as the C++ method OGRGeometry::BuildArea().
6952
 *
6953
 * Polygonization is performed similarly to OGR_G_Polygonize().
6954
 * Additionally, holes are dropped and the result is unified producing
6955
 * a single Polygon or a MultiPolygon.
6956
 *
6957
 * A new geometry object is created and returned: NULL on failure,
6958
 * empty GeometryCollection if the input geometry cannot be polygonized,
6959
 * Polygon or MultiPolygon on success.
6960
 *
6961
 * This function is built on the GEOSBuildArea_r() function of the GEOS
6962
 * library, check it for the definition of the geometry operation.
6963
 * If OGR is built without the GEOS library, this function will always fail,
6964
 * issuing a CPLE_NotSupported error.
6965
 *
6966
 * @param hGeom handle on the geometry to polygonize.
6967
 *
6968
 * @return a handle on newly allocated geometry now owned by the caller,
6969
 *         or NULL on failure.
6970
 *
6971
 * @since OGR 3.11
6972
 */
6973
6974
OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
6975
6976
0
{
6977
0
    VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
6978
6979
0
    return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
6980
0
}
6981
6982
/************************************************************************/
6983
/*                               swapXY()                               */
6984
/************************************************************************/
6985
6986
/**
6987
 * \brief Swap x and y coordinates.
6988
 *
6989
 * @since OGR 1.8.0
6990
 */
6991
6992
void OGRGeometry::swapXY()
6993
6994
0
{
6995
0
}
6996
6997
/************************************************************************/
6998
/*                               swapXY()                               */
6999
/************************************************************************/
7000
7001
/**
7002
 * \brief Swap x and y coordinates.
7003
 *
7004
 * @param hGeom geometry.
7005
 * @since OGR 2.3.0
7006
 */
7007
7008
void OGR_G_SwapXY(OGRGeometryH hGeom)
7009
0
{
7010
0
    VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
7011
7012
0
    OGRGeometry::FromHandle(hGeom)->swapXY();
7013
0
}
7014
7015
/************************************************************************/
7016
/*                        Prepared geometry API                         */
7017
/************************************************************************/
7018
7019
#if defined(HAVE_GEOS)
7020
struct _OGRPreparedGeometry
7021
{
7022
    GEOSContextHandle_t hGEOSCtxt;
7023
    GEOSGeom hGEOSGeom;
7024
    const GEOSPreparedGeometry *poPreparedGEOSGeom;
7025
};
7026
#endif
7027
7028
/************************************************************************/
7029
/*                       OGRHasPreparedGeometrySupport()                */
7030
/************************************************************************/
7031
7032
/** Returns if GEOS has prepared geometry support.
7033
 * @return TRUE or FALSE
7034
 */
7035
int OGRHasPreparedGeometrySupport()
7036
0
{
7037
#if defined(HAVE_GEOS)
7038
    return TRUE;
7039
#else
7040
0
    return FALSE;
7041
0
#endif
7042
0
}
7043
7044
/************************************************************************/
7045
/*                         OGRCreatePreparedGeometry()                  */
7046
/************************************************************************/
7047
7048
/** Creates a prepared geometry.
7049
 *
7050
 * To free with OGRDestroyPreparedGeometry()
7051
 *
7052
 * @param hGeom input geometry to prepare.
7053
 * @return handle to a prepared geometry.
7054
 * @since GDAL 3.3
7055
 */
7056
OGRPreparedGeometryH
7057
OGRCreatePreparedGeometry(UNUSED_IF_NO_GEOS OGRGeometryH hGeom)
7058
0
{
7059
#if defined(HAVE_GEOS)
7060
    OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
7061
    GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
7062
    GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
7063
    if (hGEOSGeom == nullptr)
7064
    {
7065
        OGRGeometry::freeGEOSContext(hGEOSCtxt);
7066
        return nullptr;
7067
    }
7068
    const GEOSPreparedGeometry *poPreparedGEOSGeom =
7069
        GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
7070
    if (poPreparedGEOSGeom == nullptr)
7071
    {
7072
        GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
7073
        OGRGeometry::freeGEOSContext(hGEOSCtxt);
7074
        return nullptr;
7075
    }
7076
7077
    OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
7078
    poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
7079
    poPreparedGeom->hGEOSGeom = hGEOSGeom;
7080
    poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
7081
7082
    return poPreparedGeom;
7083
#else
7084
0
    return nullptr;
7085
0
#endif
7086
0
}
7087
7088
/************************************************************************/
7089
/*                        OGRDestroyPreparedGeometry()                  */
7090
/************************************************************************/
7091
7092
/** Destroys a prepared geometry.
7093
 * @param hPreparedGeom prepared geometry.
7094
 * @since GDAL 3.3
7095
 */
7096
void OGRDestroyPreparedGeometry(
7097
    UNUSED_IF_NO_GEOS OGRPreparedGeometryH hPreparedGeom)
7098
0
{
7099
#if defined(HAVE_GEOS)
7100
    if (hPreparedGeom != nullptr)
7101
    {
7102
        GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
7103
                                   hPreparedGeom->poPreparedGEOSGeom);
7104
        GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
7105
        OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
7106
        delete hPreparedGeom;
7107
    }
7108
#endif
7109
0
}
7110
7111
/************************************************************************/
7112
/*                      OGRPreparedGeometryIntersects()                 */
7113
/************************************************************************/
7114
7115
/** Returns whether a prepared geometry intersects with a geometry.
7116
 * @param hPreparedGeom prepared geometry.
7117
 * @param hOtherGeom other geometry.
7118
 * @return TRUE or FALSE.
7119
 * @since GDAL 3.3
7120
 */
7121
int OGRPreparedGeometryIntersects(
7122
    UNUSED_IF_NO_GEOS const OGRPreparedGeometryH hPreparedGeom,
7123
    UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
7124
0
{
7125
#if defined(HAVE_GEOS)
7126
    OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7127
    if (hPreparedGeom == nullptr ||
7128
        poOtherGeom == nullptr
7129
        // The check for IsEmpty() is for buggy GEOS versions.
7130
        // See https://github.com/libgeos/geos/pull/423
7131
        || poOtherGeom->IsEmpty())
7132
    {
7133
        return FALSE;
7134
    }
7135
7136
    GEOSGeom hGEOSOtherGeom =
7137
        poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7138
    if (hGEOSOtherGeom == nullptr)
7139
        return FALSE;
7140
7141
    const bool bRet = CPL_TO_BOOL(GEOSPreparedIntersects_r(
7142
        hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
7143
        hGEOSOtherGeom));
7144
    GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7145
7146
    return bRet;
7147
#else
7148
0
    return FALSE;
7149
0
#endif
7150
0
}
7151
7152
/** Returns whether a prepared geometry contains a geometry.
7153
 * @param hPreparedGeom prepared geometry.
7154
 * @param hOtherGeom other geometry.
7155
 * @return TRUE or FALSE.
7156
 */
7157
int OGRPreparedGeometryContains(UNUSED_IF_NO_GEOS const OGRPreparedGeometryH
7158
                                    hPreparedGeom,
7159
                                UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
7160
0
{
7161
#if defined(HAVE_GEOS)
7162
    OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
7163
    if (hPreparedGeom == nullptr ||
7164
        poOtherGeom == nullptr
7165
        // The check for IsEmpty() is for buggy GEOS versions.
7166
        // See https://github.com/libgeos/geos/pull/423
7167
        || poOtherGeom->IsEmpty())
7168
    {
7169
        return FALSE;
7170
    }
7171
7172
    GEOSGeom hGEOSOtherGeom =
7173
        poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
7174
    if (hGEOSOtherGeom == nullptr)
7175
        return FALSE;
7176
7177
    const bool bRet = CPL_TO_BOOL(GEOSPreparedContains_r(
7178
        hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
7179
        hGEOSOtherGeom));
7180
    GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
7181
7182
    return bRet;
7183
#else
7184
0
    return FALSE;
7185
0
#endif
7186
0
}
7187
7188
/************************************************************************/
7189
/*                       OGRGeometryFromEWKB()                          */
7190
/************************************************************************/
7191
7192
OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
7193
                                 int bIsPostGIS1_EWKB)
7194
7195
0
{
7196
0
    OGRGeometry *poGeometry = nullptr;
7197
7198
0
    size_t nWKBSize = 0;
7199
0
    const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
7200
0
    if (pabyWKB == nullptr)
7201
0
        return nullptr;
7202
7203
    /* -------------------------------------------------------------------- */
7204
    /*      Try to ingest the geometry.                                     */
7205
    /* -------------------------------------------------------------------- */
7206
0
    (void)OGRGeometryFactory::createFromWkb(
7207
0
        pabyWKB, nullptr, &poGeometry, nWKBSize,
7208
0
        (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
7209
7210
0
    return poGeometry;
7211
0
}
7212
7213
/************************************************************************/
7214
/*                     OGRGeometryFromHexEWKB()                         */
7215
/************************************************************************/
7216
7217
OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
7218
                                    int bIsPostGIS1_EWKB)
7219
7220
0
{
7221
0
    if (pszBytea == nullptr)
7222
0
        return nullptr;
7223
7224
0
    int nWKBLength = 0;
7225
0
    GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
7226
7227
0
    OGRGeometry *poGeometry =
7228
0
        OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
7229
7230
0
    CPLFree(pabyWKB);
7231
7232
0
    return poGeometry;
7233
0
}
7234
7235
/************************************************************************/
7236
/*                       OGRGeometryToHexEWKB()                         */
7237
/************************************************************************/
7238
7239
char *OGRGeometryToHexEWKB(OGRGeometry *poGeometry, int nSRSId,
7240
                           int nPostGISMajor, int nPostGISMinor)
7241
0
{
7242
0
    const size_t nWkbSize = poGeometry->WkbSize();
7243
0
    GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
7244
0
    if (pabyWKB == nullptr)
7245
0
        return CPLStrdup("");
7246
7247
0
    if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
7248
0
        wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
7249
0
        poGeometry->IsEmpty())
7250
0
    {
7251
0
        if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
7252
0
            OGRERR_NONE)
7253
0
        {
7254
0
            CPLFree(pabyWKB);
7255
0
            return CPLStrdup("");
7256
0
        }
7257
0
    }
7258
0
    else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
7259
0
                                     (nPostGISMajor < 2)
7260
0
                                         ? wkbVariantPostGIS1
7261
0
                                         : wkbVariantOldOgc) != OGRERR_NONE)
7262
0
    {
7263
0
        CPLFree(pabyWKB);
7264
0
        return CPLStrdup("");
7265
0
    }
7266
7267
    // When converting to hex, each byte takes 2 hex characters.  In addition
7268
    // we add in 8 characters to represent the SRID integer in hex, and
7269
    // one for a null terminator.
7270
    // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
7271
    // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
7272
    // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
7273
0
    if (nWkbSize >
7274
0
        static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
7275
0
    {
7276
0
        CPLFree(pabyWKB);
7277
0
        return CPLStrdup("");
7278
0
    }
7279
0
    const size_t nTextSize = nWkbSize * 2 + 8 + 1;
7280
0
    char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
7281
0
    if (pszTextBuf == nullptr)
7282
0
    {
7283
0
        CPLFree(pabyWKB);
7284
0
        return CPLStrdup("");
7285
0
    }
7286
0
    char *pszTextBufCurrent = pszTextBuf;
7287
7288
    // Convert the 1st byte, which is the endianness flag, to hex.
7289
0
    char *pszHex = CPLBinaryToHex(1, pabyWKB);
7290
0
    strcpy(pszTextBufCurrent, pszHex);
7291
0
    CPLFree(pszHex);
7292
0
    pszTextBufCurrent += 2;
7293
7294
    // Next, get the geom type which is bytes 2 through 5.
7295
0
    GUInt32 geomType;
7296
0
    memcpy(&geomType, pabyWKB + 1, 4);
7297
7298
    // Now add the SRID flag if an SRID is provided.
7299
0
    if (nSRSId > 0)
7300
0
    {
7301
        // Change the flag to wkbNDR (little) endianness.
7302
0
        constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
7303
0
        GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
7304
        // Apply the flag.
7305
0
        geomType = geomType | nGSrsFlag;
7306
0
    }
7307
7308
    // Now write the geom type which is 4 bytes.
7309
0
    pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
7310
0
    strcpy(pszTextBufCurrent, pszHex);
7311
0
    CPLFree(pszHex);
7312
0
    pszTextBufCurrent += 8;
7313
7314
    // Now include SRID if provided.
7315
0
    if (nSRSId > 0)
7316
0
    {
7317
        // Force the srsid to wkbNDR (little) endianness.
7318
0
        const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
7319
0
        pszHex = CPLBinaryToHex(sizeof(nGSRSId),
7320
0
                                reinterpret_cast<const GByte *>(&nGSRSId));
7321
0
        strcpy(pszTextBufCurrent, pszHex);
7322
0
        CPLFree(pszHex);
7323
0
        pszTextBufCurrent += 8;
7324
0
    }
7325
7326
    // Copy the rest of the data over - subtract
7327
    // 5 since we already copied 5 bytes above.
7328
0
    pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
7329
0
    CPLFree(pabyWKB);
7330
0
    if (!pszHex || pszHex[0] == 0)
7331
0
    {
7332
0
        CPLFree(pszTextBuf);
7333
0
        return pszHex;
7334
0
    }
7335
0
    strcpy(pszTextBufCurrent, pszHex);
7336
0
    CPLFree(pszHex);
7337
7338
0
    return pszTextBuf;
7339
0
}
7340
7341
/************************************************************************/
7342
/*                       importPreambleFromWkb()                       */
7343
/************************************************************************/
7344
7345
//! @cond Doxygen_Suppress
7346
OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
7347
                                          size_t nSize,
7348
                                          OGRwkbByteOrder &eByteOrder,
7349
                                          OGRwkbVariant eWkbVariant)
7350
0
{
7351
0
    if (nSize < 9 && nSize != static_cast<size_t>(-1))
7352
0
        return OGRERR_NOT_ENOUGH_DATA;
7353
7354
    /* -------------------------------------------------------------------- */
7355
    /*      Get the byte order byte.                                        */
7356
    /* -------------------------------------------------------------------- */
7357
0
    int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
7358
0
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
7359
0
        return OGRERR_CORRUPT_DATA;
7360
0
    eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
7361
7362
    /* -------------------------------------------------------------------- */
7363
    /*      Get the geometry feature type.                                  */
7364
    /* -------------------------------------------------------------------- */
7365
0
    OGRwkbGeometryType eGeometryType;
7366
0
    const OGRErr err =
7367
0
        OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
7368
0
    if (wkbHasZ(eGeometryType))
7369
0
        flags |= OGR_G_3D;
7370
0
    if (wkbHasM(eGeometryType))
7371
0
        flags |= OGR_G_MEASURED;
7372
7373
0
    if (err != OGRERR_NONE || eGeometryType != getGeometryType())
7374
0
        return OGRERR_CORRUPT_DATA;
7375
7376
0
    return OGRERR_NONE;
7377
0
}
7378
7379
/************************************************************************/
7380
/*                    importPreambleOfCollectionFromWkb()              */
7381
/*                                                                      */
7382
/*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
7383
/*      OGRCurvePolygon and OGRGeometryCollection.                      */
7384
/************************************************************************/
7385
7386
OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
7387
    const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
7388
    OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
7389
    OGRwkbVariant eWkbVariant)
7390
0
{
7391
0
    nGeomCount = 0;
7392
7393
0
    OGRErr eErr =
7394
0
        importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
7395
0
    if (eErr != OGRERR_NONE)
7396
0
        return eErr;
7397
7398
    /* -------------------------------------------------------------------- */
7399
    /*      Clear existing Geoms.                                           */
7400
    /* -------------------------------------------------------------------- */
7401
0
    int _flags = flags;  // flags set in importPreambleFromWkb
7402
0
    empty();             // may reset flags etc.
7403
7404
    // restore
7405
0
    if (_flags & OGR_G_3D)
7406
0
        set3D(TRUE);
7407
0
    if (_flags & OGR_G_MEASURED)
7408
0
        setMeasured(TRUE);
7409
7410
    /* -------------------------------------------------------------------- */
7411
    /*      Get the sub-geometry count.                                     */
7412
    /* -------------------------------------------------------------------- */
7413
0
    memcpy(&nGeomCount, pabyData + 5, 4);
7414
7415
0
    if (OGR_SWAP(eByteOrder))
7416
0
        nGeomCount = CPL_SWAP32(nGeomCount);
7417
7418
0
    if (nGeomCount < 0 ||
7419
0
        static_cast<size_t>(nGeomCount) >
7420
0
            std::numeric_limits<size_t>::max() / nMinSubGeomSize)
7421
0
    {
7422
0
        nGeomCount = 0;
7423
0
        return OGRERR_CORRUPT_DATA;
7424
0
    }
7425
0
    const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
7426
7427
    // Each ring has a minimum of nMinSubGeomSize bytes.
7428
0
    if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
7429
0
    {
7430
0
        CPLError(CE_Failure, CPLE_AppDefined,
7431
0
                 "Length of input WKB is too small");
7432
0
        nGeomCount = 0;
7433
0
        return OGRERR_NOT_ENOUGH_DATA;
7434
0
    }
7435
7436
0
    nDataOffset = 9;
7437
0
    if (nSize != static_cast<size_t>(-1))
7438
0
    {
7439
0
        CPLAssert(nSize >= nDataOffset);
7440
0
        nSize -= nDataOffset;
7441
0
    }
7442
7443
0
    return OGRERR_NONE;
7444
0
}
7445
7446
/************************************************************************/
7447
/*                      importCurveCollectionFromWkt()                  */
7448
/*                                                                      */
7449
/*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
7450
/*      OGRMultiCurve.                                                  */
7451
/************************************************************************/
7452
7453
OGRErr OGRGeometry::importCurveCollectionFromWkt(
7454
    const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
7455
    int bAllowCurve, int bAllowCompoundCurve,
7456
    OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
7457
7458
561
{
7459
561
    int bHasZ = FALSE;
7460
561
    int bHasM = FALSE;
7461
561
    bool bIsEmpty = false;
7462
561
    OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
7463
561
    flags = 0;
7464
561
    if (eErr != OGRERR_NONE)
7465
8
        return eErr;
7466
553
    if (bHasZ)
7467
36
        flags |= OGR_G_3D;
7468
553
    if (bHasM)
7469
119
        flags |= OGR_G_MEASURED;
7470
553
    if (bIsEmpty)
7471
45
        return OGRERR_NONE;
7472
7473
508
    char szToken[OGR_WKT_TOKEN_MAX];
7474
508
    const char *pszInput = *ppszInput;
7475
508
    eErr = OGRERR_NONE;
7476
7477
    // Skip first '('.
7478
508
    pszInput = OGRWktReadToken(pszInput, szToken);
7479
7480
    /* ==================================================================== */
7481
    /*      Read each curve in turn.  Note that we try to reuse the same    */
7482
    /*      point list buffer from curve to curve to cut down on            */
7483
    /*      allocate/deallocate overhead.                                   */
7484
    /* ==================================================================== */
7485
508
    OGRRawPoint *paoPoints = nullptr;
7486
508
    int nMaxPoints = 0;
7487
508
    double *padfZ = nullptr;
7488
7489
508
    do
7490
3.67k
    {
7491
7492
        /* --------------------------------------------------------------------
7493
         */
7494
        /*      Get the first token, which should be the geometry type. */
7495
        /* --------------------------------------------------------------------
7496
         */
7497
3.67k
        const char *pszInputBefore = pszInput;
7498
3.67k
        pszInput = OGRWktReadToken(pszInput, szToken);
7499
7500
        /* --------------------------------------------------------------------
7501
         */
7502
        /*      Do the import. */
7503
        /* --------------------------------------------------------------------
7504
         */
7505
3.67k
        OGRCurve *poCurve = nullptr;
7506
3.67k
        if (EQUAL(szToken, "("))
7507
3.60k
        {
7508
3.60k
            OGRLineString *poLine = new OGRLineString();
7509
3.60k
            poCurve = poLine;
7510
3.60k
            pszInput = pszInputBefore;
7511
3.60k
            eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
7512
3.60k
                                                 paoPoints, nMaxPoints, padfZ);
7513
3.60k
        }
7514
65
        else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
7515
0
        {
7516
0
            poCurve = new OGRLineString();
7517
0
        }
7518
        // Accept LINESTRING(), but this is an extension to the BNF, also
7519
        // accepted by PostGIS.
7520
65
        else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
7521
65
                 (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
7522
64
                  !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
7523
64
                  OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
7524
65
                 (bAllowCompoundCurve &&
7525
46
                  STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
7526
19
        {
7527
19
            OGRGeometry *poGeom = nullptr;
7528
19
            pszInput = pszInputBefore;
7529
19
            eErr =
7530
19
                OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
7531
19
            if (poGeom == nullptr)
7532
9
            {
7533
9
                eErr = OGRERR_CORRUPT_DATA;
7534
9
            }
7535
10
            else
7536
10
            {
7537
10
                poCurve = poGeom->toCurve();
7538
10
            }
7539
19
        }
7540
46
        else
7541
46
        {
7542
46
            CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
7543
46
                     szToken);
7544
46
            eErr = OGRERR_CORRUPT_DATA;
7545
46
        }
7546
7547
        // If this has M it is an error if poGeom does not have M.
7548
3.67k
        if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
7549
17
            eErr = OGRERR_CORRUPT_DATA;
7550
7551
3.67k
        if (eErr == OGRERR_NONE)
7552
3.22k
            eErr = pfnAddCurveDirectly(this, poCurve);
7553
3.67k
        if (eErr != OGRERR_NONE)
7554
465
        {
7555
465
            delete poCurve;
7556
465
            break;
7557
465
        }
7558
7559
        /* --------------------------------------------------------------------
7560
         */
7561
        /*      Read the delimiter following the surface. */
7562
        /* --------------------------------------------------------------------
7563
         */
7564
3.20k
        pszInput = OGRWktReadToken(pszInput, szToken);
7565
3.20k
    } while (szToken[0] == ',' && eErr == OGRERR_NONE);
7566
7567
508
    CPLFree(paoPoints);
7568
508
    CPLFree(padfZ);
7569
7570
    /* -------------------------------------------------------------------- */
7571
    /*      freak if we don't get a closing bracket.                        */
7572
    /* -------------------------------------------------------------------- */
7573
7574
508
    if (eErr != OGRERR_NONE)
7575
465
        return eErr;
7576
7577
43
    if (szToken[0] != ')')
7578
11
        return OGRERR_CORRUPT_DATA;
7579
7580
32
    *ppszInput = pszInput;
7581
32
    return OGRERR_NONE;
7582
43
}
7583
7584
//! @endcond
7585
7586
/************************************************************************/
7587
/*                          OGR_GT_Flatten()                            */
7588
/************************************************************************/
7589
/**
7590
 * \brief Returns the 2D geometry type corresponding to the passed geometry
7591
 * type.
7592
 *
7593
 * This function is intended to work with geometry types as old-style 99-402
7594
 * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
7595
 * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
7596
 *
7597
 * @param eType Input geometry type
7598
 *
7599
 * @return 2D geometry type corresponding to the passed geometry type.
7600
 *
7601
 * @since GDAL 2.0
7602
 */
7603
7604
OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
7605
170k
{
7606
170k
    eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
7607
170k
    if (eType >= 1000 && eType < 2000)  // ISO Z.
7608
15
        return static_cast<OGRwkbGeometryType>(eType - 1000);
7609
170k
    if (eType >= 2000 && eType < 3000)  // ISO M.
7610
6.82k
        return static_cast<OGRwkbGeometryType>(eType - 2000);
7611
163k
    if (eType >= 3000 && eType < 4000)  // ISO ZM.
7612
36.9k
        return static_cast<OGRwkbGeometryType>(eType - 3000);
7613
126k
    return eType;
7614
163k
}
7615
7616
/************************************************************************/
7617
/*                          OGR_GT_HasZ()                               */
7618
/************************************************************************/
7619
/**
7620
 * \brief Return if the geometry type is a 3D geometry type.
7621
 *
7622
 * @param eType Input geometry type
7623
 *
7624
 * @return TRUE if the geometry type is a 3D geometry type.
7625
 *
7626
 * @since GDAL 2.0
7627
 */
7628
7629
int OGR_GT_HasZ(OGRwkbGeometryType eType)
7630
153
{
7631
153
    if (eType & wkb25DBitInternalUse)
7632
0
        return TRUE;
7633
153
    if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
7634
0
        return TRUE;
7635
153
    if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
7636
0
        return TRUE;
7637
153
    return FALSE;
7638
153
}
7639
7640
/************************************************************************/
7641
/*                          OGR_GT_HasM()                               */
7642
/************************************************************************/
7643
/**
7644
 * \brief Return if the geometry type is a measured type.
7645
 *
7646
 * @param eType Input geometry type
7647
 *
7648
 * @return TRUE if the geometry type is a measured type.
7649
 *
7650
 * @since GDAL 2.1
7651
 */
7652
7653
int OGR_GT_HasM(OGRwkbGeometryType eType)
7654
156
{
7655
156
    if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
7656
0
        return TRUE;
7657
156
    if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
7658
0
        return TRUE;
7659
156
    return FALSE;
7660
156
}
7661
7662
/************************************************************************/
7663
/*                           OGR_GT_SetZ()                              */
7664
/************************************************************************/
7665
/**
7666
 * \brief Returns the 3D geometry type corresponding to the passed geometry
7667
 * type.
7668
 *
7669
 * @param eType Input geometry type
7670
 *
7671
 * @return 3D geometry type corresponding to the passed geometry type.
7672
 *
7673
 * @since GDAL 2.0
7674
 */
7675
7676
OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
7677
2
{
7678
2
    if (OGR_GT_HasZ(eType) || eType == wkbNone)
7679
0
        return eType;
7680
2
    if (eType <= wkbGeometryCollection)
7681
1
        return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
7682
1
    else
7683
1
        return static_cast<OGRwkbGeometryType>(eType + 1000);
7684
2
}
7685
7686
/************************************************************************/
7687
/*                           OGR_GT_SetM()                              */
7688
/************************************************************************/
7689
/**
7690
 * \brief Returns the measured geometry type corresponding to the passed
7691
 * geometry type.
7692
 *
7693
 * @param eType Input geometry type
7694
 *
7695
 * @return measured geometry type corresponding to the passed geometry type.
7696
 *
7697
 * @since GDAL 2.1
7698
 */
7699
7700
OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
7701
5
{
7702
5
    if (OGR_GT_HasM(eType) || eType == wkbNone)
7703
0
        return eType;
7704
5
    if (eType & wkb25DBitInternalUse)
7705
0
    {
7706
0
        eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
7707
0
        eType = static_cast<OGRwkbGeometryType>(eType + 1000);
7708
0
    }
7709
5
    return static_cast<OGRwkbGeometryType>(eType + 2000);
7710
5
}
7711
7712
/************************************************************************/
7713
/*                        OGR_GT_SetModifier()                          */
7714
/************************************************************************/
7715
/**
7716
 * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
7717
 *
7718
 * @param eType Input geometry type
7719
 * @param bHasZ TRUE if the output geometry type must be 3D.
7720
 * @param bHasM TRUE if the output geometry type must be measured.
7721
 *
7722
 * @return Output geometry type.
7723
 *
7724
 * @since GDAL 2.0
7725
 */
7726
7727
OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
7728
                                      int bHasM)
7729
0
{
7730
0
    if (bHasZ && bHasM)
7731
0
        return OGR_GT_SetM(OGR_GT_SetZ(eType));
7732
0
    else if (bHasM)
7733
0
        return OGR_GT_SetM(wkbFlatten(eType));
7734
0
    else if (bHasZ)
7735
0
        return OGR_GT_SetZ(wkbFlatten(eType));
7736
0
    else
7737
0
        return wkbFlatten(eType);
7738
0
}
7739
7740
/************************************************************************/
7741
/*                        OGR_GT_IsSubClassOf)                          */
7742
/************************************************************************/
7743
/**
7744
 * \brief Returns if a type is a subclass of another one
7745
 *
7746
 * @param eType Type.
7747
 * @param eSuperType Super type
7748
 *
7749
 * @return TRUE if eType is a subclass of eSuperType.
7750
 *
7751
 * @since GDAL 2.0
7752
 */
7753
7754
int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
7755
49.3k
{
7756
49.3k
    eSuperType = wkbFlatten(eSuperType);
7757
49.3k
    eType = wkbFlatten(eType);
7758
7759
49.3k
    if (eSuperType == eType || eSuperType == wkbUnknown)
7760
2
        return TRUE;
7761
7762
49.3k
    if (eSuperType == wkbGeometryCollection)
7763
11.5k
        return eType == wkbMultiPoint || eType == wkbMultiLineString ||
7764
11.5k
               eType == wkbMultiPolygon || eType == wkbMultiCurve ||
7765
11.5k
               eType == wkbMultiSurface;
7766
7767
37.7k
    if (eSuperType == wkbCurvePolygon)
7768
21.2k
        return eType == wkbPolygon || eType == wkbTriangle;
7769
7770
16.4k
    if (eSuperType == wkbMultiCurve)
7771
0
        return eType == wkbMultiLineString;
7772
7773
16.4k
    if (eSuperType == wkbMultiSurface)
7774
0
        return eType == wkbMultiPolygon;
7775
7776
16.4k
    if (eSuperType == wkbCurve)
7777
4.92k
        return eType == wkbLineString || eType == wkbCircularString ||
7778
4.92k
               eType == wkbCompoundCurve;
7779
7780
11.5k
    if (eSuperType == wkbSurface)
7781
0
        return eType == wkbCurvePolygon || eType == wkbPolygon ||
7782
0
               eType == wkbTriangle || eType == wkbPolyhedralSurface ||
7783
0
               eType == wkbTIN;
7784
7785
11.5k
    if (eSuperType == wkbPolygon)
7786
0
        return eType == wkbTriangle;
7787
7788
11.5k
    if (eSuperType == wkbPolyhedralSurface)
7789
0
        return eType == wkbTIN;
7790
7791
11.5k
    return FALSE;
7792
11.5k
}
7793
7794
/************************************************************************/
7795
/*                       OGR_GT_GetCollection()                         */
7796
/************************************************************************/
7797
/**
7798
 * \brief Returns the collection type that can contain the passed geometry type
7799
 *
7800
 * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
7801
 * wkbLineString->wkbMultiLineString,
7802
 * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
7803
 * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
7804
 * wkbCurvePolygon->wkbMultiSurface.
7805
 * In other cases, wkbUnknown is returned
7806
 *
7807
 * Passed Z, M, ZM flag is preserved.
7808
 *
7809
 *
7810
 * @param eType Input geometry type
7811
 *
7812
 * @return the collection type that can contain the passed geometry type or
7813
 * wkbUnknown
7814
 *
7815
 * @since GDAL 2.0
7816
 */
7817
7818
OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
7819
0
{
7820
0
    const bool bHasZ = wkbHasZ(eType);
7821
0
    const bool bHasM = wkbHasM(eType);
7822
0
    if (eType == wkbNone)
7823
0
        return wkbNone;
7824
0
    OGRwkbGeometryType eFGType = wkbFlatten(eType);
7825
0
    if (eFGType == wkbPoint)
7826
0
        eType = wkbMultiPoint;
7827
7828
0
    else if (eFGType == wkbLineString)
7829
0
        eType = wkbMultiLineString;
7830
7831
0
    else if (eFGType == wkbPolygon)
7832
0
        eType = wkbMultiPolygon;
7833
7834
0
    else if (eFGType == wkbTriangle)
7835
0
        eType = wkbTIN;
7836
7837
0
    else if (OGR_GT_IsCurve(eFGType))
7838
0
        eType = wkbMultiCurve;
7839
7840
0
    else if (OGR_GT_IsSurface(eFGType))
7841
0
        eType = wkbMultiSurface;
7842
7843
0
    else
7844
0
        return wkbUnknown;
7845
7846
0
    if (bHasZ)
7847
0
        eType = wkbSetZ(eType);
7848
0
    if (bHasM)
7849
0
        eType = wkbSetM(eType);
7850
7851
0
    return eType;
7852
0
}
7853
7854
/************************************************************************/
7855
/*                         OGR_GT_GetSingle()                           */
7856
/************************************************************************/
7857
/**
7858
 * \brief Returns the non-collection type that be contained in the passed
7859
 * geometry type.
7860
 *
7861
 * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
7862
 * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
7863
 * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
7864
 * wkbGeometryCollection -> wkbUnknown
7865
 * In other cases, the original geometry is returned.
7866
 *
7867
 * Passed Z, M, ZM flag is preserved.
7868
 *
7869
 *
7870
 * @param eType Input geometry type
7871
 *
7872
 * @return the the non-collection type that be contained in the passed geometry
7873
 * type or wkbUnknown
7874
 *
7875
 * @since GDAL 3.11
7876
 */
7877
7878
OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
7879
0
{
7880
0
    const bool bHasZ = wkbHasZ(eType);
7881
0
    const bool bHasM = wkbHasM(eType);
7882
0
    if (eType == wkbNone)
7883
0
        return wkbNone;
7884
0
    const OGRwkbGeometryType eFGType = wkbFlatten(eType);
7885
0
    if (eFGType == wkbMultiPoint)
7886
0
        eType = wkbPoint;
7887
7888
0
    else if (eFGType == wkbMultiLineString)
7889
0
        eType = wkbLineString;
7890
7891
0
    else if (eFGType == wkbMultiPolygon)
7892
0
        eType = wkbPolygon;
7893
7894
0
    else if (eFGType == wkbMultiCurve)
7895
0
        eType = wkbCompoundCurve;
7896
7897
0
    else if (eFGType == wkbMultiSurface)
7898
0
        eType = wkbCurvePolygon;
7899
7900
0
    else if (eFGType == wkbGeometryCollection)
7901
0
        return wkbUnknown;
7902
7903
0
    if (bHasZ)
7904
0
        eType = wkbSetZ(eType);
7905
0
    if (bHasM)
7906
0
        eType = wkbSetM(eType);
7907
7908
0
    return eType;
7909
0
}
7910
7911
/************************************************************************/
7912
/*                        OGR_GT_GetCurve()                             */
7913
/************************************************************************/
7914
/**
7915
 * \brief Returns the curve geometry type that can contain the passed geometry
7916
 * type
7917
 *
7918
 * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
7919
 * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
7920
 * and wkbMultiLineString->wkbMultiCurve.
7921
 * In other cases, the passed geometry is returned.
7922
 *
7923
 * Passed Z, M, ZM flag is preserved.
7924
 *
7925
 * @param eType Input geometry type
7926
 *
7927
 * @return the curve type that can contain the passed geometry type
7928
 *
7929
 * @since GDAL 2.0
7930
 */
7931
7932
OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
7933
0
{
7934
0
    const bool bHasZ = wkbHasZ(eType);
7935
0
    const bool bHasM = wkbHasM(eType);
7936
0
    OGRwkbGeometryType eFGType = wkbFlatten(eType);
7937
7938
0
    if (eFGType == wkbLineString)
7939
0
        eType = wkbCompoundCurve;
7940
7941
0
    else if (eFGType == wkbPolygon)
7942
0
        eType = wkbCurvePolygon;
7943
7944
0
    else if (eFGType == wkbTriangle)
7945
0
        eType = wkbCurvePolygon;
7946
7947
0
    else if (eFGType == wkbMultiLineString)
7948
0
        eType = wkbMultiCurve;
7949
7950
0
    else if (eFGType == wkbMultiPolygon)
7951
0
        eType = wkbMultiSurface;
7952
7953
0
    if (bHasZ)
7954
0
        eType = wkbSetZ(eType);
7955
0
    if (bHasM)
7956
0
        eType = wkbSetM(eType);
7957
7958
0
    return eType;
7959
0
}
7960
7961
/************************************************************************/
7962
/*                        OGR_GT_GetLinear()                          */
7963
/************************************************************************/
7964
/**
7965
 * \brief Returns the non-curve geometry type that can contain the passed
7966
 * geometry type
7967
 *
7968
 * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
7969
 * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
7970
 * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
7971
 * In other cases, the passed geometry is returned.
7972
 *
7973
 * Passed Z, M, ZM flag is preserved.
7974
 *
7975
 * @param eType Input geometry type
7976
 *
7977
 * @return the non-curve type that can contain the passed geometry type
7978
 *
7979
 * @since GDAL 2.0
7980
 */
7981
7982
OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
7983
0
{
7984
0
    const bool bHasZ = wkbHasZ(eType);
7985
0
    const bool bHasM = wkbHasM(eType);
7986
0
    OGRwkbGeometryType eFGType = wkbFlatten(eType);
7987
7988
0
    if (OGR_GT_IsCurve(eFGType))
7989
0
        eType = wkbLineString;
7990
7991
0
    else if (OGR_GT_IsSurface(eFGType))
7992
0
        eType = wkbPolygon;
7993
7994
0
    else if (eFGType == wkbMultiCurve)
7995
0
        eType = wkbMultiLineString;
7996
7997
0
    else if (eFGType == wkbMultiSurface)
7998
0
        eType = wkbMultiPolygon;
7999
8000
0
    if (bHasZ)
8001
0
        eType = wkbSetZ(eType);
8002
0
    if (bHasM)
8003
0
        eType = wkbSetM(eType);
8004
8005
0
    return eType;
8006
0
}
8007
8008
/************************************************************************/
8009
/*                           OGR_GT_IsCurve()                           */
8010
/************************************************************************/
8011
8012
/**
8013
 * \brief Return if a geometry type is an instance of Curve
8014
 *
8015
 * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
8016
 * and their Z/M/ZM variant.
8017
 *
8018
 * @param eGeomType the geometry type
8019
 * @return TRUE if the geometry type is an instance of Curve
8020
 *
8021
 * @since GDAL 2.0
8022
 */
8023
8024
int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
8025
4.92k
{
8026
4.92k
    return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
8027
4.92k
}
8028
8029
/************************************************************************/
8030
/*                         OGR_GT_IsSurface()                           */
8031
/************************************************************************/
8032
8033
/**
8034
 * \brief Return if a geometry type is an instance of Surface
8035
 *
8036
 * Such geometry type are wkbCurvePolygon and wkbPolygon
8037
 * and their Z/M/ZM variant.
8038
 *
8039
 * @param eGeomType the geometry type
8040
 * @return TRUE if the geometry type is an instance of Surface
8041
 *
8042
 * @since GDAL 2.0
8043
 */
8044
8045
int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
8046
0
{
8047
0
    return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
8048
0
}
8049
8050
/************************************************************************/
8051
/*                          OGR_GT_IsNonLinear()                        */
8052
/************************************************************************/
8053
8054
/**
8055
 * \brief Return if a geometry type is a non-linear geometry type.
8056
 *
8057
 * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
8058
 * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
8059
 * Z/M variants.
8060
 *
8061
 * @param eGeomType the geometry type
8062
 * @return TRUE if the geometry type is a non-linear geometry type.
8063
 *
8064
 * @since GDAL 2.0
8065
 */
8066
8067
int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
8068
0
{
8069
0
    OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
8070
0
    return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
8071
0
           eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
8072
0
           eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
8073
0
           eFGeomType == wkbMultiSurface;
8074
0
}
8075
8076
/************************************************************************/
8077
/*                          CastToError()                               */
8078
/************************************************************************/
8079
8080
//! @cond Doxygen_Suppress
8081
OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
8082
0
{
8083
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
8084
0
             poGeom->getGeometryName());
8085
0
    delete poGeom;
8086
0
    return nullptr;
8087
0
}
8088
8089
//! @endcond
8090
8091
/************************************************************************/
8092
/*                          OGRexportToSFCGAL()                         */
8093
/************************************************************************/
8094
8095
//! @cond Doxygen_Suppress
8096
sfcgal_geometry_t *
8097
OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
8098
0
{
8099
#ifdef HAVE_SFCGAL
8100
8101
    sfcgal_init();
8102
#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8103
8104
    const auto exportToSFCGALViaWKB =
8105
        [](const OGRGeometry *geom) -> sfcgal_geometry_t *
8106
    {
8107
        if (!geom)
8108
            return nullptr;
8109
8110
        // Get WKB size and allocate buffer
8111
        size_t nSize = geom->WkbSize();
8112
        unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
8113
8114
        // Set export options with NDR byte order
8115
        OGRwkbExportOptions oOptions;
8116
        oOptions.eByteOrder = wkbNDR;
8117
        // and ISO to avoid wkb25DBit for Z geometries
8118
        oOptions.eWkbVariant = wkbVariantIso;
8119
8120
        // Export to WKB
8121
        sfcgal_geometry_t *sfcgalGeom = nullptr;
8122
        if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
8123
        {
8124
            sfcgalGeom = sfcgal_io_read_wkb(
8125
                reinterpret_cast<const char *>(pabyWkb), nSize);
8126
        }
8127
8128
        CPLFree(pabyWkb);
8129
        return sfcgalGeom;
8130
    };
8131
8132
    // Handle special cases
8133
    if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8134
    {
8135
        std::unique_ptr<OGRLineString> poLS(
8136
            OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8137
        return exportToSFCGALViaWKB(poLS.get());
8138
    }
8139
    else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8140
             EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8141
    {
8142
        std::unique_ptr<OGRLineString> poLS(
8143
            OGRGeometryFactory::forceToLineString(poGeom->clone())
8144
                ->toLineString());
8145
        return exportToSFCGALViaWKB(poLS.get());
8146
    }
8147
    else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8148
    {
8149
        std::unique_ptr<OGRPolygon> poPolygon(
8150
            OGRGeometryFactory::forceToPolygon(
8151
                poGeom->clone()->toCurvePolygon())
8152
                ->toPolygon());
8153
        return exportToSFCGALViaWKB(poPolygon.get());
8154
    }
8155
    else
8156
    {
8157
        // Default case - direct export
8158
        return exportToSFCGALViaWKB(poGeom);
8159
    }
8160
#else
8161
    char *buffer = nullptr;
8162
8163
    // special cases - LinearRing, Circular String, Compound Curve, Curve
8164
    // Polygon
8165
8166
    if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
8167
    {
8168
        // cast it to LineString and get the WKT
8169
        std::unique_ptr<OGRLineString> poLS(
8170
            OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
8171
        if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8172
        {
8173
            sfcgal_geometry_t *_geometry =
8174
                sfcgal_io_read_wkt(buffer, strlen(buffer));
8175
            CPLFree(buffer);
8176
            return _geometry;
8177
        }
8178
        else
8179
        {
8180
            CPLFree(buffer);
8181
            return nullptr;
8182
        }
8183
    }
8184
    else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
8185
             EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
8186
    {
8187
        // convert it to LineString and get the WKT
8188
        std::unique_ptr<OGRLineString> poLS(
8189
            OGRGeometryFactory::forceToLineString(poGeom->clone())
8190
                ->toLineString());
8191
        if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
8192
        {
8193
            sfcgal_geometry_t *_geometry =
8194
                sfcgal_io_read_wkt(buffer, strlen(buffer));
8195
            CPLFree(buffer);
8196
            return _geometry;
8197
        }
8198
        else
8199
        {
8200
            CPLFree(buffer);
8201
            return nullptr;
8202
        }
8203
    }
8204
    else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
8205
    {
8206
        // convert it to Polygon and get the WKT
8207
        std::unique_ptr<OGRPolygon> poPolygon(
8208
            OGRGeometryFactory::forceToPolygon(
8209
                poGeom->clone()->toCurvePolygon())
8210
                ->toPolygon());
8211
        if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
8212
        {
8213
            sfcgal_geometry_t *_geometry =
8214
                sfcgal_io_read_wkt(buffer, strlen(buffer));
8215
            CPLFree(buffer);
8216
            return _geometry;
8217
        }
8218
        else
8219
        {
8220
            CPLFree(buffer);
8221
            return nullptr;
8222
        }
8223
    }
8224
    else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
8225
    {
8226
        sfcgal_geometry_t *_geometry =
8227
            sfcgal_io_read_wkt(buffer, strlen(buffer));
8228
        CPLFree(buffer);
8229
        return _geometry;
8230
    }
8231
    else
8232
    {
8233
        CPLFree(buffer);
8234
        return nullptr;
8235
    }
8236
#endif
8237
#else
8238
0
    CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8239
0
    return nullptr;
8240
0
#endif
8241
0
}
8242
8243
//! @endcond
8244
8245
/************************************************************************/
8246
/*                          SFCGALexportToOGR()                         */
8247
/************************************************************************/
8248
8249
//! @cond Doxygen_Suppress
8250
OGRGeometry *OGRGeometry::SFCGALexportToOGR(
8251
    UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
8252
0
{
8253
#ifdef HAVE_SFCGAL
8254
    if (geometry == nullptr)
8255
        return nullptr;
8256
8257
    sfcgal_init();
8258
    char *pabySFCGAL = nullptr;
8259
    size_t nLength = 0;
8260
#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
8261
8262
    sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
8263
8264
    if (pabySFCGAL == nullptr || nLength == 0)
8265
        return nullptr;
8266
8267
    OGRGeometry *poGeom = nullptr;
8268
    OGRErr eErr = OGRGeometryFactory::createFromWkb(
8269
        reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
8270
        nLength);
8271
8272
    free(pabySFCGAL);
8273
8274
    if (eErr == OGRERR_NONE)
8275
    {
8276
        return poGeom;
8277
    }
8278
    else
8279
    {
8280
        return nullptr;
8281
    }
8282
#else
8283
    sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
8284
    char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
8285
    memcpy(pszWKT, pabySFCGAL, nLength);
8286
    pszWKT[nLength] = 0;
8287
    free(pabySFCGAL);
8288
8289
    sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
8290
8291
    OGRGeometry *poGeom = nullptr;
8292
    if (geom_type == SFCGAL_TYPE_POINT)
8293
    {
8294
        poGeom = new OGRPoint();
8295
    }
8296
    else if (geom_type == SFCGAL_TYPE_LINESTRING)
8297
    {
8298
        poGeom = new OGRLineString();
8299
    }
8300
    else if (geom_type == SFCGAL_TYPE_POLYGON)
8301
    {
8302
        poGeom = new OGRPolygon();
8303
    }
8304
    else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
8305
    {
8306
        poGeom = new OGRMultiPoint();
8307
    }
8308
    else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
8309
    {
8310
        poGeom = new OGRMultiLineString();
8311
    }
8312
    else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
8313
    {
8314
        poGeom = new OGRMultiPolygon();
8315
    }
8316
    else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
8317
    {
8318
        poGeom = new OGRGeometryCollection();
8319
    }
8320
    else if (geom_type == SFCGAL_TYPE_TRIANGLE)
8321
    {
8322
        poGeom = new OGRTriangle();
8323
    }
8324
    else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
8325
    {
8326
        poGeom = new OGRPolyhedralSurface();
8327
    }
8328
    else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
8329
    {
8330
        poGeom = new OGRTriangulatedSurface();
8331
    }
8332
    else
8333
    {
8334
        CPLFree(pszWKT);
8335
        return nullptr;
8336
    }
8337
8338
    const char *pszWKTTmp = pszWKT;
8339
    if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
8340
    {
8341
        CPLFree(pszWKT);
8342
        return poGeom;
8343
    }
8344
    else
8345
    {
8346
        delete poGeom;
8347
        CPLFree(pszWKT);
8348
        return nullptr;
8349
    }
8350
#endif
8351
#else
8352
0
    CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
8353
0
    return nullptr;
8354
0
#endif
8355
0
}
8356
8357
//! @endcond
8358
8359
//! @cond Doxygen_Suppress
8360
OGRBoolean OGRGeometry::IsSFCGALCompatible() const
8361
0
{
8362
0
    const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
8363
0
    if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
8364
0
        eGType == wkbTIN)
8365
0
    {
8366
0
        return TRUE;
8367
0
    }
8368
0
    if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
8369
0
    {
8370
0
        const OGRGeometryCollection *poGC = toGeometryCollection();
8371
0
        bool bIsSFCGALCompatible = false;
8372
0
        for (auto &&poSubGeom : *poGC)
8373
0
        {
8374
0
            OGRwkbGeometryType eSubGeomType =
8375
0
                wkbFlatten(poSubGeom->getGeometryType());
8376
0
            if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
8377
0
            {
8378
0
                bIsSFCGALCompatible = true;
8379
0
            }
8380
0
            else if (eSubGeomType != wkbMultiPolygon)
8381
0
            {
8382
0
                bIsSFCGALCompatible = false;
8383
0
                break;
8384
0
            }
8385
0
        }
8386
0
        return bIsSFCGALCompatible;
8387
0
    }
8388
0
    return FALSE;
8389
0
}
8390
8391
//! @endcond
8392
8393
/************************************************************************/
8394
/*                    roundCoordinatesIEEE754()                         */
8395
/************************************************************************/
8396
8397
/** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
8398
 * double-precision binary representation.
8399
 *
8400
 * Determines the number of bits (N) required to represent a coordinate value
8401
 * with a specified number of digits after the decimal point, and then sets all
8402
 * but the N most significant bits to zero. The resulting coordinate value will
8403
 * still round to the original value (e.g. after roundCoordinates()), but will
8404
 * have improved compressiblity.
8405
 *
8406
 * @param options Contains the precision requirements.
8407
 * @since GDAL 3.9
8408
 */
8409
void OGRGeometry::roundCoordinatesIEEE754(
8410
    const OGRGeomCoordinateBinaryPrecision &options)
8411
0
{
8412
0
    struct Quantizer : public OGRDefaultGeometryVisitor
8413
0
    {
8414
0
        const OGRGeomCoordinateBinaryPrecision &m_options;
8415
8416
0
        explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
8417
0
            : m_options(optionsIn)
8418
0
        {
8419
0
        }
8420
8421
0
        using OGRDefaultGeometryVisitor::visit;
8422
8423
0
        void visit(OGRPoint *poPoint) override
8424
0
        {
8425
0
            if (m_options.nXYBitPrecision != INT_MIN)
8426
0
            {
8427
0
                uint64_t i;
8428
0
                double d;
8429
0
                d = poPoint->getX();
8430
0
                memcpy(&i, &d, sizeof(i));
8431
0
                i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8432
0
                memcpy(&d, &i, sizeof(i));
8433
0
                poPoint->setX(d);
8434
0
                d = poPoint->getY();
8435
0
                memcpy(&i, &d, sizeof(i));
8436
0
                i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
8437
0
                memcpy(&d, &i, sizeof(i));
8438
0
                poPoint->setY(d);
8439
0
            }
8440
0
            if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
8441
0
            {
8442
0
                uint64_t i;
8443
0
                double d;
8444
0
                d = poPoint->getZ();
8445
0
                memcpy(&i, &d, sizeof(i));
8446
0
                i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
8447
0
                memcpy(&d, &i, sizeof(i));
8448
0
                poPoint->setZ(d);
8449
0
            }
8450
0
            if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
8451
0
            {
8452
0
                uint64_t i;
8453
0
                double d;
8454
0
                d = poPoint->getM();
8455
0
                memcpy(&i, &d, sizeof(i));
8456
0
                i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
8457
0
                memcpy(&d, &i, sizeof(i));
8458
0
                poPoint->setM(d);
8459
0
            }
8460
0
        }
8461
0
    };
8462
8463
0
    Quantizer quantizer(options);
8464
0
    accept(&quantizer);
8465
0
}
8466
8467
/************************************************************************/
8468
/*                             visit()                                  */
8469
/************************************************************************/
8470
8471
void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
8472
0
{
8473
0
    for (auto &&oPoint : *poGeom)
8474
0
    {
8475
0
        oPoint.accept(this);
8476
0
    }
8477
0
}
8478
8479
void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
8480
0
{
8481
0
    _visit(poGeom);
8482
0
}
8483
8484
void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
8485
0
{
8486
0
    visit(poGeom->toUpperClass());
8487
0
}
8488
8489
void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
8490
0
{
8491
0
    _visit(poGeom);
8492
0
}
8493
8494
void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
8495
0
{
8496
0
    for (auto &&poSubGeom : *poGeom)
8497
0
        poSubGeom->accept(this);
8498
0
}
8499
8500
void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
8501
0
{
8502
0
    visit(poGeom->toUpperClass());
8503
0
}
8504
8505
void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
8506
0
{
8507
0
    visit(poGeom->toUpperClass());
8508
0
}
8509
8510
void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
8511
0
{
8512
0
    visit(poGeom->toUpperClass());
8513
0
}
8514
8515
void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
8516
0
{
8517
0
    visit(poGeom->toUpperClass());
8518
0
}
8519
8520
void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
8521
0
{
8522
0
    for (auto &&poSubGeom : *poGeom)
8523
0
        poSubGeom->accept(this);
8524
0
}
8525
8526
void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
8527
0
{
8528
0
    for (auto &&poSubGeom : *poGeom)
8529
0
        poSubGeom->accept(this);
8530
0
}
8531
8532
void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
8533
0
{
8534
0
    visit(poGeom->toUpperClass());
8535
0
}
8536
8537
void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
8538
0
{
8539
0
    visit(poGeom->toUpperClass());
8540
0
}
8541
8542
void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
8543
0
{
8544
0
    visit(poGeom->toUpperClass());
8545
0
}
8546
8547
void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
8548
0
{
8549
0
    for (auto &&poSubGeom : *poGeom)
8550
0
        poSubGeom->accept(this);
8551
0
}
8552
8553
void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
8554
0
{
8555
0
    visit(poGeom->toUpperClass());
8556
0
}
8557
8558
void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
8559
0
{
8560
0
    for (auto &&oPoint : *poGeom)
8561
0
    {
8562
0
        oPoint.accept(this);
8563
0
    }
8564
0
}
8565
8566
void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
8567
0
{
8568
0
    _visit(poGeom);
8569
0
}
8570
8571
void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
8572
0
{
8573
0
    visit(poGeom->toUpperClass());
8574
0
}
8575
8576
void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
8577
0
{
8578
0
    _visit(poGeom);
8579
0
}
8580
8581
void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
8582
0
{
8583
0
    for (auto &&poSubGeom : *poGeom)
8584
0
        poSubGeom->accept(this);
8585
0
}
8586
8587
void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
8588
0
{
8589
0
    visit(poGeom->toUpperClass());
8590
0
}
8591
8592
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
8593
0
{
8594
0
    visit(poGeom->toUpperClass());
8595
0
}
8596
8597
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
8598
0
{
8599
0
    visit(poGeom->toUpperClass());
8600
0
}
8601
8602
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
8603
0
{
8604
0
    visit(poGeom->toUpperClass());
8605
0
}
8606
8607
void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
8608
0
{
8609
0
    for (auto &&poSubGeom : *poGeom)
8610
0
        poSubGeom->accept(this);
8611
0
}
8612
8613
void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
8614
0
{
8615
0
    for (auto &&poSubGeom : *poGeom)
8616
0
        poSubGeom->accept(this);
8617
0
}
8618
8619
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
8620
0
{
8621
0
    visit(poGeom->toUpperClass());
8622
0
}
8623
8624
void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
8625
0
{
8626
0
    visit(poGeom->toUpperClass());
8627
0
}
8628
8629
void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
8630
0
{
8631
0
    visit(poGeom->toUpperClass());
8632
0
}
8633
8634
void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
8635
0
{
8636
0
    for (auto &&poSubGeom : *poGeom)
8637
0
        poSubGeom->accept(this);
8638
0
}
8639
8640
void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
8641
0
{
8642
0
    visit(poGeom->toUpperClass());
8643
0
}
8644
8645
/************************************************************************/
8646
/*                     OGRGeometryUniquePtrDeleter                      */
8647
/************************************************************************/
8648
8649
//! @cond Doxygen_Suppress
8650
void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
8651
0
{
8652
0
    delete poGeom;
8653
0
}
8654
8655
//! @endcond
8656
8657
/************************************************************************/
8658
/*                  OGRPreparedGeometryUniquePtrDeleter                 */
8659
/************************************************************************/
8660
8661
//! @cond Doxygen_Suppress
8662
void OGRPreparedGeometryUniquePtrDeleter::operator()(
8663
    OGRPreparedGeometry *poPreparedGeom) const
8664
0
{
8665
0
    OGRDestroyPreparedGeometry(poPreparedGeom);
8666
0
}
8667
8668
//! @endcond
8669
8670
/************************************************************************/
8671
/*                     HomogenizeDimensionalityWith()                  */
8672
/************************************************************************/
8673
8674
//! @cond Doxygen_Suppress
8675
void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
8676
11.9k
{
8677
11.9k
    if (poOtherGeom->Is3D() && !Is3D())
8678
822
        set3D(TRUE);
8679
8680
11.9k
    if (poOtherGeom->IsMeasured() && !IsMeasured())
8681
336
        setMeasured(TRUE);
8682
8683
11.9k
    if (!poOtherGeom->Is3D() && Is3D())
8684
1.57k
        poOtherGeom->set3D(TRUE);
8685
8686
11.9k
    if (!poOtherGeom->IsMeasured() && IsMeasured())
8687
1.46k
        poOtherGeom->setMeasured(TRUE);
8688
11.9k
}
8689
8690
//! @endcond
8691
8692
/************************************************************************/
8693
/*                  OGRGeomCoordinateBinaryPrecision::SetFrom()         */
8694
/************************************************************************/
8695
8696
/** Set binary precision options from resolution.
8697
 *
8698
 * @since GDAL 3.9
8699
 */
8700
void OGRGeomCoordinateBinaryPrecision::SetFrom(
8701
    const OGRGeomCoordinatePrecision &prec)
8702
0
{
8703
0
    if (prec.dfXYResolution != 0)
8704
0
    {
8705
0
        nXYBitPrecision =
8706
0
            static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
8707
0
    }
8708
0
    if (prec.dfZResolution != 0)
8709
0
    {
8710
0
        nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
8711
0
    }
8712
0
    if (prec.dfMResolution != 0)
8713
0
    {
8714
0
        nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
8715
0
    }
8716
0
}
8717
8718
/************************************************************************/
8719
/*                        OGRwkbExportOptionsCreate()                   */
8720
/************************************************************************/
8721
8722
/**
8723
 * \brief Create geometry WKB export options.
8724
 *
8725
 * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
8726
 *
8727
 * @return object to be freed with OGRwkbExportOptionsDestroy().
8728
 * @since GDAL 3.9
8729
 */
8730
OGRwkbExportOptions *OGRwkbExportOptionsCreate()
8731
0
{
8732
0
    return new OGRwkbExportOptions;
8733
0
}
8734
8735
/************************************************************************/
8736
/*                        OGRwkbExportOptionsDestroy()                  */
8737
/************************************************************************/
8738
8739
/**
8740
 * \brief Destroy object returned by OGRwkbExportOptionsCreate()
8741
 *
8742
 * @param psOptions WKB export options
8743
 * @since GDAL 3.9
8744
 */
8745
8746
void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
8747
0
{
8748
0
    delete psOptions;
8749
0
}
8750
8751
/************************************************************************/
8752
/*                   OGRwkbExportOptionsSetByteOrder()                  */
8753
/************************************************************************/
8754
8755
/**
8756
 * \brief Set the WKB byte order.
8757
 *
8758
 * @param psOptions WKB export options
8759
 * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
8760
 * Intel)
8761
 * @since GDAL 3.9
8762
 */
8763
8764
void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
8765
                                     OGRwkbByteOrder eByteOrder)
8766
0
{
8767
0
    psOptions->eByteOrder = eByteOrder;
8768
0
}
8769
8770
/************************************************************************/
8771
/*                   OGRwkbExportOptionsSetVariant()                    */
8772
/************************************************************************/
8773
8774
/**
8775
 * \brief Set the WKB variant
8776
 *
8777
 * @param psOptions WKB export options
8778
 * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
8779
 * wkbVariantPostGIS1
8780
 * @since GDAL 3.9
8781
 */
8782
8783
void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
8784
                                   OGRwkbVariant eWkbVariant)
8785
0
{
8786
0
    psOptions->eWkbVariant = eWkbVariant;
8787
0
}
8788
8789
/************************************************************************/
8790
/*                   OGRwkbExportOptionsSetPrecision()                  */
8791
/************************************************************************/
8792
8793
/**
8794
 * \brief Set precision options
8795
 *
8796
 * @param psOptions WKB export options
8797
 * @param hPrecisionOptions Precision options (might be null to reset them)
8798
 * @since GDAL 3.9
8799
 */
8800
8801
void OGRwkbExportOptionsSetPrecision(
8802
    OGRwkbExportOptions *psOptions,
8803
    OGRGeomCoordinatePrecisionH hPrecisionOptions)
8804
0
{
8805
0
    psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
8806
0
    if (hPrecisionOptions)
8807
0
        psOptions->sPrecision.SetFrom(*hPrecisionOptions);
8808
0
}
8809
8810
/************************************************************************/
8811
/*                             IsRectangle()                            */
8812
/************************************************************************/
8813
8814
/**
8815
 * \brief Returns whether the geometry is a polygon with 4 corners forming
8816
 * a rectangle.
8817
 *
8818
 * @since GDAL 3.10
8819
 */
8820
bool OGRGeometry::IsRectangle() const
8821
0
{
8822
0
    if (wkbFlatten(getGeometryType()) != wkbPolygon)
8823
0
        return false;
8824
8825
0
    const OGRPolygon *poPoly = toPolygon();
8826
8827
0
    if (poPoly->getNumInteriorRings() != 0)
8828
0
        return false;
8829
8830
0
    const OGRLinearRing *poRing = poPoly->getExteriorRing();
8831
0
    if (!poRing)
8832
0
        return false;
8833
8834
0
    if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
8835
0
        return false;
8836
8837
    // If the ring has 5 points, the last should be the first.
8838
0
    if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
8839
0
                                        poRing->getY(0) != poRing->getY(4)))
8840
0
        return false;
8841
8842
    // Polygon with first segment in "y" direction.
8843
0
    if (poRing->getX(0) == poRing->getX(1) &&
8844
0
        poRing->getY(1) == poRing->getY(2) &&
8845
0
        poRing->getX(2) == poRing->getX(3) &&
8846
0
        poRing->getY(3) == poRing->getY(0))
8847
0
        return true;
8848
8849
    // Polygon with first segment in "x" direction.
8850
0
    if (poRing->getY(0) == poRing->getY(1) &&
8851
0
        poRing->getX(1) == poRing->getX(2) &&
8852
0
        poRing->getY(2) == poRing->getY(3) &&
8853
0
        poRing->getX(3) == poRing->getX(0))
8854
0
        return true;
8855
8856
0
    return false;
8857
0
}
8858
8859
/************************************************************************/
8860
/*                           hasEmptyParts()                            */
8861
/************************************************************************/
8862
8863
/**
8864
 * \brief Returns whether a geometry has empty parts/rings.
8865
 *
8866
 * Returns true if removeEmptyParts() will modify the geometry.
8867
 *
8868
 * This is different from IsEmpty().
8869
 *
8870
 * @since GDAL 3.10
8871
 */
8872
bool OGRGeometry::hasEmptyParts() const
8873
0
{
8874
0
    return false;
8875
0
}
8876
8877
/************************************************************************/
8878
/*                          removeEmptyParts()                          */
8879
/************************************************************************/
8880
8881
/**
8882
 * \brief Remove empty parts/rings from this geometry.
8883
 *
8884
 * @since GDAL 3.10
8885
 */
8886
void OGRGeometry::removeEmptyParts()
8887
0
{
8888
0
}
8889
8890
/************************************************************************/
8891
/*                      ~IOGRGeometryVisitor()                          */
8892
/************************************************************************/
8893
8894
0
IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
8895
8896
/************************************************************************/
8897
/*                    ~IOGRConstGeometryVisitor()                       */
8898
/************************************************************************/
8899
8900
0
IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;