Coverage Report

Created: 2025-11-16 06:25

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