Coverage Report

Created: 2026-02-14 06:52

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