Coverage Report

Created: 2026-04-01 06:20

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