Coverage Report

Created: 2025-06-09 07:43

/src/gdal/ogr/ogrpolyhedralsurface.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRPolyhedralSurface geometry class.
5
 * Author:   Avyav Kumar Singh <avyavkumar at gmail dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2016, Avyav Kumar Singh <avyavkumar at gmail dot com>
9
 * Copyright (c) 2016, Even Rouault <even.roauult at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "ogr_geometry.h"
15
#include "ogr_p.h"
16
#include "ogr_sfcgal.h"
17
#include "ogr_api.h"
18
#include "ogr_libs.h"
19
20
#include <new>
21
22
/************************************************************************/
23
/*         OGRPolyhedralSurface( const OGRPolyhedralSurface& )          */
24
/************************************************************************/
25
26
/**
27
 * \brief Copy constructor.
28
 *
29
 */
30
31
0
OGRPolyhedralSurface::OGRPolyhedralSurface(const OGRPolyhedralSurface &) =
32
    default;
33
34
/************************************************************************/
35
/*                 operator=( const OGRPolyhedralSurface&)              */
36
/************************************************************************/
37
38
/**
39
 * \brief Assignment operator.
40
 *
41
 */
42
43
OGRPolyhedralSurface &
44
OGRPolyhedralSurface::operator=(const OGRPolyhedralSurface &other)
45
0
{
46
0
    if (this != &other)
47
0
    {
48
0
        OGRSurface::operator=(other);
49
0
        oMP = other.oMP;
50
0
    }
51
0
    return *this;
52
0
}
53
54
/************************************************************************/
55
/*                               clone()                                */
56
/************************************************************************/
57
58
OGRPolyhedralSurface *OGRPolyhedralSurface::clone() const
59
60
0
{
61
0
    return new (std::nothrow) OGRPolyhedralSurface(*this);
62
0
}
63
64
/************************************************************************/
65
/*                          getGeometryName()                           */
66
/************************************************************************/
67
68
const char *OGRPolyhedralSurface::getGeometryName() const
69
0
{
70
0
    return "POLYHEDRALSURFACE";
71
0
}
72
73
/************************************************************************/
74
/*                          getGeometryType()                           */
75
/************************************************************************/
76
77
/**
78
 * \brief Returns the WKB Type of PolyhedralSurface
79
 *
80
 */
81
82
OGRwkbGeometryType OGRPolyhedralSurface::getGeometryType() const
83
0
{
84
0
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
85
0
        return wkbPolyhedralSurfaceZM;
86
0
    else if (flags & OGR_G_MEASURED)
87
0
        return wkbPolyhedralSurfaceM;
88
0
    else if (flags & OGR_G_3D)
89
0
        return wkbPolyhedralSurfaceZ;
90
0
    else
91
0
        return wkbPolyhedralSurface;
92
0
}
93
94
/************************************************************************/
95
/*                              WkbSize()                               */
96
/************************************************************************/
97
98
size_t OGRPolyhedralSurface::WkbSize() const
99
0
{
100
0
    size_t nSize = 9;
101
0
    for (auto &&poSubGeom : *this)
102
0
    {
103
0
        nSize += poSubGeom->WkbSize();
104
0
    }
105
0
    return nSize;
106
0
}
107
108
/************************************************************************/
109
/*                            getDimension()                            */
110
/************************************************************************/
111
112
int OGRPolyhedralSurface::getDimension() const
113
0
{
114
0
    return 2;
115
0
}
116
117
/************************************************************************/
118
/*                               empty()                                */
119
/************************************************************************/
120
121
void OGRPolyhedralSurface::empty()
122
0
{
123
0
    if (oMP.papoGeoms != nullptr)
124
0
    {
125
0
        for (auto &&poSubGeom : *this)
126
0
        {
127
0
            delete poSubGeom;
128
0
        }
129
0
        CPLFree(oMP.papoGeoms);
130
0
    }
131
0
    oMP.nGeomCount = 0;
132
0
    oMP.papoGeoms = nullptr;
133
0
}
134
135
/************************************************************************/
136
/*                            getEnvelope()                             */
137
/************************************************************************/
138
139
void OGRPolyhedralSurface::getEnvelope(OGREnvelope *psEnvelope) const
140
0
{
141
0
    oMP.getEnvelope(psEnvelope);
142
0
}
143
144
/************************************************************************/
145
/*                            getEnvelope()                             */
146
/************************************************************************/
147
148
void OGRPolyhedralSurface::getEnvelope(OGREnvelope3D *psEnvelope) const
149
0
{
150
0
    oMP.getEnvelope(psEnvelope);
151
0
}
152
153
/************************************************************************/
154
/*                           importFromWkb()                            */
155
/************************************************************************/
156
157
OGRErr OGRPolyhedralSurface::importFromWkb(const unsigned char *pabyData,
158
                                           size_t nSize,
159
                                           OGRwkbVariant eWkbVariant,
160
                                           size_t &nBytesConsumedOut)
161
0
{
162
0
    nBytesConsumedOut = 0;
163
0
    oMP.nGeomCount = 0;
164
0
    OGRwkbByteOrder eByteOrder = wkbXDR;
165
0
    size_t nDataOffset = 0;
166
0
    OGRErr eErr = importPreambleOfCollectionFromWkb(
167
0
        pabyData, nSize, nDataOffset, eByteOrder, 9, oMP.nGeomCount,
168
0
        eWkbVariant);
169
170
0
    if (eErr != OGRERR_NONE)
171
0
        return eErr;
172
173
0
    oMP.papoGeoms = reinterpret_cast<OGRGeometry **>(
174
0
        VSI_CALLOC_VERBOSE(sizeof(void *), oMP.nGeomCount));
175
0
    if (oMP.nGeomCount != 0 && oMP.papoGeoms == nullptr)
176
0
    {
177
0
        oMP.nGeomCount = 0;
178
0
        return OGRERR_NOT_ENOUGH_MEMORY;
179
0
    }
180
181
    /* -------------------------------------------------------------------- */
182
    /*      Get the Geoms.                                                  */
183
    /* -------------------------------------------------------------------- */
184
0
    for (int iGeom = 0; iGeom < oMP.nGeomCount; iGeom++)
185
0
    {
186
        // Parse the polygons
187
0
        const unsigned char *pabySubData = pabyData + nDataOffset;
188
0
        if (nSize < 9 && nSize != static_cast<size_t>(-1))
189
0
            return OGRERR_NOT_ENOUGH_DATA;
190
191
0
        OGRwkbGeometryType eSubGeomType;
192
0
        eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
193
0
        if (eErr != OGRERR_NONE)
194
0
            return eErr;
195
196
0
        if (!isCompatibleSubType(eSubGeomType))
197
0
        {
198
0
            oMP.nGeomCount = iGeom;
199
0
            CPLDebug("OGR",
200
0
                     "Cannot add geometry of type (%d) to "
201
0
                     "geometry of type (%d)",
202
0
                     eSubGeomType, getGeometryType());
203
0
            return OGRERR_CORRUPT_DATA;
204
0
        }
205
206
0
        OGRGeometry *poSubGeom = nullptr;
207
0
        size_t nSubGeomBytesConsumed = 0;
208
0
        eErr = OGRGeometryFactory::createFromWkb(pabySubData, nullptr,
209
0
                                                 &poSubGeom, nSize, eWkbVariant,
210
0
                                                 nSubGeomBytesConsumed);
211
212
0
        if (eErr != OGRERR_NONE)
213
0
        {
214
0
            oMP.nGeomCount = iGeom;
215
0
            delete poSubGeom;
216
0
            return eErr;
217
0
        }
218
219
0
        oMP.papoGeoms[iGeom] = poSubGeom;
220
221
0
        if (oMP.papoGeoms[iGeom]->Is3D())
222
0
            flags |= OGR_G_3D;
223
0
        if (oMP.papoGeoms[iGeom]->IsMeasured())
224
0
            flags |= OGR_G_MEASURED;
225
226
0
        CPLAssert(nSubGeomBytesConsumed > 0);
227
0
        if (nSize != static_cast<size_t>(-1))
228
0
        {
229
0
            CPLAssert(nSize >= nSubGeomBytesConsumed);
230
0
            nSize -= nSubGeomBytesConsumed;
231
0
        }
232
233
0
        nDataOffset += nSubGeomBytesConsumed;
234
0
    }
235
0
    nBytesConsumedOut = nDataOffset;
236
237
0
    return OGRERR_NONE;
238
0
}
239
240
/************************************************************************/
241
/*                            exportToWkb()                             */
242
/************************************************************************/
243
244
OGRErr
245
OGRPolyhedralSurface::exportToWkb(unsigned char *pabyData,
246
                                  const OGRwkbExportOptions *psOptions) const
247
248
0
{
249
0
    if (!psOptions)
250
0
    {
251
0
        static const OGRwkbExportOptions defaultOptions;
252
0
        psOptions = &defaultOptions;
253
0
    }
254
255
    /* -------------------------------------------------------------------- */
256
    /*      Set the byte order.                                             */
257
    /* -------------------------------------------------------------------- */
258
0
    pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
259
0
        static_cast<unsigned char>(psOptions->eByteOrder));
260
261
    /* -------------------------------------------------------------------- */
262
    /*      Set the geometry feature type, ensuring that 3D flag is         */
263
    /*      preserved.                                                      */
264
    /* -------------------------------------------------------------------- */
265
0
    GUInt32 nGType = getIsoGeometryType();
266
267
0
    if (OGR_SWAP(psOptions->eByteOrder))
268
0
    {
269
0
        nGType = CPL_SWAP32(nGType);
270
0
    }
271
272
0
    memcpy(pabyData + 1, &nGType, 4);
273
274
    // Copy the raw data
275
0
    if (OGR_SWAP(psOptions->eByteOrder))
276
0
    {
277
0
        int nCount = CPL_SWAP32(oMP.nGeomCount);
278
0
        memcpy(pabyData + 5, &nCount, 4);
279
0
    }
280
0
    else
281
0
        memcpy(pabyData + 5, &oMP.nGeomCount, 4);
282
283
0
    size_t nOffset = 9;
284
285
    // serialize each of the geometries
286
0
    for (auto &&poSubGeom : *this)
287
0
    {
288
0
        poSubGeom->exportToWkb(pabyData + nOffset, psOptions);
289
0
        nOffset += poSubGeom->WkbSize();
290
0
    }
291
292
0
    return OGRERR_NONE;
293
0
}
294
295
/************************************************************************/
296
/*                           importFromWkt()                            */
297
/*              Instantiate from well known text format.                */
298
/************************************************************************/
299
300
OGRErr OGRPolyhedralSurface::importFromWkt(const char **ppszInput)
301
0
{
302
0
    int bHasZ = FALSE, bHasM = FALSE;
303
0
    bool bIsEmpty = false;
304
0
    OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
305
0
    flags = 0;
306
0
    if (eErr != OGRERR_NONE)
307
0
        return eErr;
308
0
    if (bHasZ)
309
0
        flags |= OGR_G_3D;
310
0
    if (bHasM)
311
0
        flags |= OGR_G_MEASURED;
312
0
    if (bIsEmpty)
313
0
        return OGRERR_NONE;
314
315
0
    char szToken[OGR_WKT_TOKEN_MAX];
316
0
    const char *pszInput = *ppszInput;
317
318
    /* Skip first '(' */
319
0
    pszInput = OGRWktReadToken(pszInput, szToken);
320
321
    /* ==================================================================== */
322
    /*      Read each surface in turn.  Note that we try to reuse the same  */
323
    /*      point list buffer from ring to ring to cut down on              */
324
    /*      allocate/deallocate overhead.                                   */
325
    /* ==================================================================== */
326
0
    OGRRawPoint *paoPoints = nullptr;
327
0
    int nMaxPoints = 0;
328
0
    double *padfZ = nullptr;
329
330
0
    do
331
0
    {
332
333
        /* --------------------------------------------------------------------
334
         */
335
        /*      Get the first token, which should be the geometry type. */
336
        /* --------------------------------------------------------------------
337
         */
338
0
        const char *pszInputBefore = pszInput;
339
0
        pszInput = OGRWktReadToken(pszInput, szToken);
340
341
0
        OGRSurface *poSurface = nullptr;
342
343
        /* --------------------------------------------------------------------
344
         */
345
        /*      Do the import. */
346
        /* --------------------------------------------------------------------
347
         */
348
0
        if (EQUAL(szToken, "("))
349
0
        {
350
0
            OGRPolygon *poPolygon =
351
0
                OGRGeometryFactory::createGeometry(getSubGeometryType())
352
0
                    ->toPolygon();
353
0
            poSurface = poPolygon;
354
0
            pszInput = pszInputBefore;
355
0
            eErr = poPolygon->importFromWKTListOnly(
356
0
                &pszInput, bHasZ, bHasM, paoPoints, nMaxPoints, padfZ);
357
0
        }
358
0
        else
359
0
        {
360
0
            CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
361
0
                     szToken);
362
0
            eErr = OGRERR_CORRUPT_DATA;
363
0
            break;
364
0
        }
365
366
0
        if (eErr == OGRERR_NONE)
367
0
            eErr = oMP._addGeometryDirectlyWithExpectedSubGeometryType(
368
0
                poSurface, getSubGeometryType());
369
0
        if (eErr != OGRERR_NONE)
370
0
        {
371
0
            delete poSurface;
372
0
            break;
373
0
        }
374
375
        // Read the delimiter following the surface.
376
0
        pszInput = OGRWktReadToken(pszInput, szToken);
377
378
0
    } while (szToken[0] == ',' && eErr == OGRERR_NONE);
379
380
0
    CPLFree(paoPoints);
381
0
    CPLFree(padfZ);
382
383
    // Check for a closing bracket
384
0
    if (eErr != OGRERR_NONE)
385
0
        return eErr;
386
387
0
    if (szToken[0] != ')')
388
0
        return OGRERR_CORRUPT_DATA;
389
390
0
    set3D(oMP.Is3D());
391
0
    setMeasured(oMP.IsMeasured());
392
393
0
    *ppszInput = pszInput;
394
0
    return OGRERR_NONE;
395
0
}
396
397
/************************************************************************/
398
/*                            exportToWkt()                             */
399
/************************************************************************/
400
401
std::string OGRPolyhedralSurface::exportToWkt(const OGRWktOptions &opts,
402
                                              OGRErr *err) const
403
0
{
404
0
    OGRWktOptions optsModified(opts);
405
0
    optsModified.variant = wkbVariantIso;
406
0
    return exportToWktInternal(optsModified, err);
407
0
}
408
409
//! @cond Doxygen_Suppress
410
std::string OGRPolyhedralSurface::exportToWktInternal(const OGRWktOptions &opts,
411
                                                      OGRErr *err) const
412
0
{
413
0
    try
414
0
    {
415
0
        std::string wkt(getGeometryName());
416
0
        wkt += wktTypeString(opts.variant);
417
0
        bool first = true;
418
419
0
        for (int i = 0; i < oMP.nGeomCount; ++i)
420
0
        {
421
0
            OGRGeometry *geom = oMP.papoGeoms[i];
422
423
0
            OGRErr subgeomErr = OGRERR_NONE;
424
0
            std::string tempWkt = geom->exportToWkt(opts, &subgeomErr);
425
0
            if (subgeomErr != OGRERR_NONE)
426
0
            {
427
0
                if (err)
428
0
                    *err = subgeomErr;
429
0
                return std::string();
430
0
            }
431
432
0
            auto pos = tempWkt.find('(');
433
434
            // Skip empty geoms
435
0
            if (pos == std::string::npos)
436
0
                continue;
437
0
            if (first)
438
0
                wkt += '(';
439
0
            else
440
0
                wkt += ',';
441
0
            first = false;
442
443
            // Extract the '( ... )' part of the child geometry.
444
0
            wkt += tempWkt.substr(pos);
445
0
        }
446
447
0
        if (err)
448
0
            *err = OGRERR_NONE;
449
0
        if (first)
450
0
            wkt += "EMPTY";
451
0
        else
452
0
            wkt += ')';
453
0
        return wkt;
454
0
    }
455
0
    catch (const std::bad_alloc &e)
456
0
    {
457
0
        CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
458
0
        if (err)
459
0
            *err = OGRERR_FAILURE;
460
0
        return std::string();
461
0
    }
462
0
}
463
464
//! @endcond
465
466
/************************************************************************/
467
/*                            flattenTo2D()                             */
468
/************************************************************************/
469
470
void OGRPolyhedralSurface::flattenTo2D()
471
0
{
472
0
    oMP.flattenTo2D();
473
474
0
    flags &= ~OGR_G_3D;
475
0
    flags &= ~OGR_G_MEASURED;
476
0
}
477
478
/************************************************************************/
479
/*                             transform()                              */
480
/************************************************************************/
481
482
OGRErr OGRPolyhedralSurface::transform(OGRCoordinateTransformation *poCT)
483
0
{
484
0
    return oMP.transform(poCT);
485
0
}
486
487
/************************************************************************/
488
/*                      GetCasterToPolygon()                            */
489
/************************************************************************/
490
491
//! @cond Doxygen_Suppress
492
static OGRPolygon *CasterToPolygon(OGRSurface *poGeom)
493
0
{
494
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
495
0
             poGeom->getGeometryName());
496
0
    delete poGeom;
497
0
    return nullptr;
498
0
}
499
500
OGRSurfaceCasterToPolygon OGRPolyhedralSurface::GetCasterToPolygon() const
501
0
{
502
0
    return ::CasterToPolygon;
503
0
}
504
505
//! @endcond
506
507
/************************************************************************/
508
/*                      OGRSurfaceCasterToCurvePolygon()                */
509
/************************************************************************/
510
511
//! @cond Doxygen_Suppress
512
static OGRCurvePolygon *CasterToCurvePolygon(OGRSurface *poGeom)
513
0
{
514
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
515
0
             poGeom->getGeometryName());
516
0
    delete poGeom;
517
0
    return nullptr;
518
0
}
519
520
OGRSurfaceCasterToCurvePolygon
521
OGRPolyhedralSurface::GetCasterToCurvePolygon() const
522
0
{
523
0
    return ::CasterToCurvePolygon;
524
0
}
525
526
//! @endcond
527
528
/************************************************************************/
529
/*                         isCompatibleSubType()                        */
530
/************************************************************************/
531
532
//! @cond Doxygen_Suppress
533
OGRBoolean
534
OGRPolyhedralSurface::isCompatibleSubType(OGRwkbGeometryType eSubType) const
535
0
{
536
0
    return wkbFlatten(eSubType) == wkbPolygon;
537
0
}
538
539
//! @endcond
540
541
/************************************************************************/
542
/*                         getSubGeometryName()                         */
543
/************************************************************************/
544
545
//! @cond Doxygen_Suppress
546
const char *OGRPolyhedralSurface::getSubGeometryName() const
547
0
{
548
0
    return "POLYGON";
549
0
}
550
551
//! @endcond
552
553
/************************************************************************/
554
/*                         getSubGeometryType()                         */
555
/************************************************************************/
556
557
//! @cond Doxygen_Suppress
558
OGRwkbGeometryType OGRPolyhedralSurface::getSubGeometryType() const
559
0
{
560
0
    return wkbPolygon;
561
0
}
562
563
//! @endcond
564
565
/************************************************************************/
566
/*                               Equals()                               */
567
/************************************************************************/
568
569
OGRBoolean OGRPolyhedralSurface::Equals(const OGRGeometry *poOther) const
570
0
{
571
572
0
    if (poOther == this)
573
0
        return TRUE;
574
575
0
    if (poOther->getGeometryType() != getGeometryType())
576
0
        return FALSE;
577
578
0
    if (IsEmpty() && poOther->IsEmpty())
579
0
        return TRUE;
580
581
0
    auto poOMP = poOther->toPolyhedralSurface();
582
0
    if (oMP.getNumGeometries() != poOMP->oMP.getNumGeometries())
583
0
        return FALSE;
584
585
0
    for (int iGeom = 0; iGeom < oMP.nGeomCount; iGeom++)
586
0
    {
587
0
        if (!oMP.getGeometryRef(iGeom)->Equals(
588
0
                poOMP->oMP.getGeometryRef(iGeom)))
589
0
            return FALSE;
590
0
    }
591
592
0
    return TRUE;
593
0
}
594
595
/************************************************************************/
596
/*                              get_Area()                              */
597
/************************************************************************/
598
599
/**
600
 * \brief Returns the area enclosed
601
 *
602
 * This method is built on the SFCGAL library, check it for the definition
603
 * of the geometry operation.
604
 * If OGR is built without the SFCGAL library, this method will always return
605
 * -1.0
606
 *
607
 * @return area enclosed by the PolyhedralSurface
608
 */
609
610
double OGRPolyhedralSurface::get_Area() const
611
0
{
612
0
#ifndef HAVE_SFCGAL
613
614
0
    CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
615
0
    return -1.0;
616
617
#else
618
619
    sfcgal_init();
620
    sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
621
    if (poThis == nullptr)
622
        return -1.0;
623
624
    double area = sfcgal_geometry_area_3d(poThis);
625
626
    sfcgal_geometry_delete(poThis);
627
628
    return (area > 0) ? area : -1.0;
629
630
#endif
631
0
}
632
633
/************************************************************************/
634
/*                        get_GeodesicArea()                            */
635
/************************************************************************/
636
637
double OGRPolyhedralSurface::get_GeodesicArea(const OGRSpatialReference *) const
638
0
{
639
0
    if (IsEmpty())
640
0
        return 0;
641
642
0
    CPLError(CE_Failure, CPLE_NotSupported,
643
0
             "get_GeodesicArea() not implemented for PolyhedralSurface");
644
0
    return -1;
645
0
}
646
647
/************************************************************************/
648
/*                            get_Length()                              */
649
/************************************************************************/
650
651
double OGRPolyhedralSurface::get_Length() const
652
0
{
653
0
    if (IsEmpty())
654
0
        return 0;
655
656
0
    CPLError(CE_Failure, CPLE_NotSupported,
657
0
             "get_Length() not implemented for PolyhedralSurface");
658
0
    return 0;
659
0
}
660
661
/************************************************************************/
662
/*                        get_GeodesicLength()                          */
663
/************************************************************************/
664
665
double
666
OGRPolyhedralSurface::get_GeodesicLength(const OGRSpatialReference *) const
667
0
{
668
0
    if (IsEmpty())
669
0
        return 0;
670
671
0
    CPLError(CE_Failure, CPLE_NotSupported,
672
0
             "get_GeodesicLength() not implemented for PolyhedralSurface");
673
0
    return -1;
674
0
}
675
676
/************************************************************************/
677
/*                           PointOnSurface()                           */
678
/************************************************************************/
679
680
OGRErr OGRPolyhedralSurface::PointOnSurface(OGRPoint *poPoint) const
681
0
{
682
0
    return PointOnSurfaceInternal(poPoint);
683
0
}
684
685
/************************************************************************/
686
/*                     GetCasterToMultiPolygon()                        */
687
/************************************************************************/
688
//! @cond Doxygen_Suppress
689
OGRPolyhedralSurfaceCastToMultiPolygon
690
OGRPolyhedralSurface::GetCasterToMultiPolygon() const
691
0
{
692
0
    return OGRPolyhedralSurface::CastToMultiPolygonImpl;
693
0
}
694
695
/************************************************************************/
696
/*                      CastToMultiPolygonImpl()                        */
697
/************************************************************************/
698
699
OGRMultiPolygon *
700
OGRPolyhedralSurface::CastToMultiPolygonImpl(OGRPolyhedralSurface *poPS)
701
0
{
702
0
    OGRMultiPolygon *poMultiPolygon = new OGRMultiPolygon(poPS->oMP);
703
0
    poMultiPolygon->assignSpatialReference(poPS->getSpatialReference());
704
0
    delete poPS;
705
0
    return poMultiPolygon;
706
0
}
707
708
//! @endcond
709
710
/************************************************************************/
711
/*                         CastToMultiPolygon()                         */
712
/************************************************************************/
713
714
/**
715
 * \brief Casts the OGRPolyhedralSurface to an OGRMultiPolygon
716
 *
717
 * The passed in geometry is consumed and a new one returned (or NULL in case
718
 * of failure)
719
 *
720
 * @param poPS the input geometry - ownership is passed to the method.
721
 * @return new geometry.
722
 */
723
724
OGRMultiPolygon *
725
OGRPolyhedralSurface::CastToMultiPolygon(OGRPolyhedralSurface *poPS)
726
0
{
727
0
    OGRPolyhedralSurfaceCastToMultiPolygon pfn =
728
0
        poPS->GetCasterToMultiPolygon();
729
0
    return pfn(poPS);
730
0
}
731
732
/************************************************************************/
733
/*                            addGeometry()                             */
734
/************************************************************************/
735
736
/**
737
 * \brief Add a new geometry to a collection.
738
 *
739
 * Only a POLYGON can be added to a POLYHEDRALSURFACE.
740
 *
741
 * @return OGRErr OGRERR_NONE if the polygon is successfully added
742
 */
743
744
OGRErr OGRPolyhedralSurface::addGeometry(const OGRGeometry *poNewGeom)
745
0
{
746
0
    if (!isCompatibleSubType(poNewGeom->getGeometryType()))
747
0
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
748
749
0
    OGRGeometry *poClone = poNewGeom->clone();
750
0
    OGRErr eErr;
751
752
0
    if (poClone == nullptr)
753
0
        return OGRERR_FAILURE;
754
755
0
    eErr = addGeometryDirectly(poClone);
756
757
0
    if (eErr != OGRERR_NONE)
758
0
        delete poClone;
759
760
0
    return eErr;
761
0
}
762
763
/************************************************************************/
764
/*                        addGeometryDirectly()                         */
765
/************************************************************************/
766
767
/**
768
 * \brief Add a geometry directly to the container.
769
 *
770
 * Ownership of the passed geometry is taken by the container rather than
771
 * cloning as addCurve() does, but only if the method is successful.
772
 * If the method fails, ownership still belongs to the caller.
773
 *
774
 * This method is the same as the C function OGR_G_AddGeometryDirectly().
775
 *
776
 * There is no SFCOM analog to this method.
777
 *
778
 * @param poNewGeom geometry to add to the container.
779
 *
780
 * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
781
 * the geometry type is illegal for the type of geometry container.
782
 */
783
784
OGRErr OGRPolyhedralSurface::addGeometryDirectly(OGRGeometry *poNewGeom)
785
0
{
786
0
    if (!isCompatibleSubType(poNewGeom->getGeometryType()))
787
0
    {
788
0
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
789
0
    }
790
791
0
    HomogenizeDimensionalityWith(poNewGeom);
792
793
0
    OGRGeometry **papoNewGeoms =
794
0
        static_cast<OGRGeometry **>(VSI_REALLOC_VERBOSE(
795
0
            oMP.papoGeoms, sizeof(void *) * (oMP.nGeomCount + 1)));
796
0
    if (papoNewGeoms == nullptr)
797
0
        return OGRERR_FAILURE;
798
799
0
    oMP.papoGeoms = papoNewGeoms;
800
0
    oMP.papoGeoms[oMP.nGeomCount] = poNewGeom;
801
0
    oMP.nGeomCount++;
802
803
0
    return OGRERR_NONE;
804
0
}
805
806
/************************************************************************/
807
/*                            addGeometry()                             */
808
/************************************************************************/
809
810
/**
811
 * \brief Add a geometry directly to the container.
812
 *
813
 * There is no SFCOM analog to this method.
814
 *
815
 * @param geom geometry to add to the container.
816
 *
817
 * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
818
 * the geometry type is illegal for the type of geometry container.
819
 */
820
821
OGRErr OGRPolyhedralSurface::addGeometry(std::unique_ptr<OGRGeometry> geom)
822
0
{
823
0
    OGRGeometry *poGeom = geom.release();
824
0
    OGRErr eErr = addGeometryDirectly(poGeom);
825
0
    if (eErr != OGRERR_NONE)
826
0
        delete poGeom;
827
0
    return eErr;
828
0
}
829
830
/************************************************************************/
831
/*                          getNumGeometries()                          */
832
/************************************************************************/
833
834
/**
835
 * \brief Fetch number of geometries in PolyhedralSurface
836
 *
837
 * @return count of children geometries.  May be zero.
838
 */
839
840
int OGRPolyhedralSurface::getNumGeometries() const
841
0
{
842
0
    return oMP.nGeomCount;
843
0
}
844
845
/************************************************************************/
846
/*                         getGeometryRef()                             */
847
/************************************************************************/
848
849
/**
850
 * \brief Fetch geometry from container.
851
 *
852
 * This method returns a pointer to an geometry within the container.  The
853
 * returned geometry remains owned by the container, and should not be
854
 * modified.  The pointer is only valid until the next change to the
855
 * geometry container.  Use IGeometry::clone() to make a copy.
856
 *
857
 * @param i the index of the geometry to fetch, between 0 and
858
 *          getNumGeometries() - 1.
859
 * @return pointer to requested geometry.
860
 */
861
862
OGRPolygon *OGRPolyhedralSurface::getGeometryRef(int i)
863
0
{
864
0
    return oMP.papoGeoms[i]->toPolygon();
865
0
}
866
867
/************************************************************************/
868
/*                         getGeometryRef()                             */
869
/************************************************************************/
870
871
/**
872
 * \brief Fetch geometry from container.
873
 *
874
 * This method returns a pointer to an geometry within the container.  The
875
 * returned geometry remains owned by the container, and should not be
876
 * modified.  The pointer is only valid until the next change to the
877
 * geometry container.  Use IGeometry::clone() to make a copy.
878
 *
879
 * @param i the index of the geometry to fetch, between 0 and
880
 *          getNumGeometries() - 1.
881
 * @return pointer to requested geometry.
882
 */
883
884
const OGRPolygon *OGRPolyhedralSurface::getGeometryRef(int i) const
885
0
{
886
0
    return oMP.papoGeoms[i]->toPolygon();
887
0
}
888
889
/************************************************************************/
890
/*                               IsEmpty()                              */
891
/************************************************************************/
892
893
/**
894
 * \brief Checks if the PolyhedralSurface is empty
895
 *
896
 * @return TRUE if the PolyhedralSurface is empty, FALSE otherwise
897
 */
898
899
OGRBoolean OGRPolyhedralSurface::IsEmpty() const
900
0
{
901
0
    return oMP.IsEmpty();
902
0
}
903
904
/************************************************************************/
905
/*                                 set3D()                              */
906
/************************************************************************/
907
908
/**
909
 * \brief Set the type as 3D geometry
910
 */
911
912
bool OGRPolyhedralSurface::set3D(OGRBoolean bIs3D)
913
0
{
914
0
    return oMP.set3D(bIs3D) && OGRGeometry::set3D(bIs3D);
915
0
}
916
917
/************************************************************************/
918
/*                             setMeasured()                            */
919
/************************************************************************/
920
921
/**
922
 * \brief Set the type as Measured
923
 */
924
925
bool OGRPolyhedralSurface::setMeasured(OGRBoolean bIsMeasured)
926
0
{
927
0
    return oMP.setMeasured(bIsMeasured) &&
928
0
           OGRGeometry::setMeasured(bIsMeasured);
929
0
}
930
931
/************************************************************************/
932
/*                       setCoordinateDimension()                       */
933
/************************************************************************/
934
935
/**
936
 * \brief Set the coordinate dimension.
937
 *
938
 * This method sets the explicit coordinate dimension.  Setting the coordinate
939
 * dimension of a geometry to 2 should zero out any existing Z values.
940
 * This will also remove the M dimension if present before this call.
941
 *
942
 * @param nNewDimension New coordinate dimension value, either 2 or 3.
943
 * @return (since 3.10) true in case of success, false in case of memory allocation error
944
 */
945
946
bool OGRPolyhedralSurface::setCoordinateDimension(int nNewDimension)
947
0
{
948
0
    return oMP.setCoordinateDimension(nNewDimension) &&
949
0
           OGRGeometry::setCoordinateDimension(nNewDimension);
950
0
}
951
952
/************************************************************************/
953
/*                               swapXY()                               */
954
/************************************************************************/
955
956
/**
957
 * \brief Swap x and y coordinates.
958
 */
959
960
void OGRPolyhedralSurface::swapXY()
961
0
{
962
0
    oMP.swapXY();
963
0
}
964
965
/************************************************************************/
966
/*                         hasCurveGeometry()                           */
967
/************************************************************************/
968
969
OGRBoolean OGRPolyhedralSurface::hasCurveGeometry(int) const
970
0
{
971
0
    return FALSE;
972
0
}
973
974
/************************************************************************/
975
/*                          removeGeometry()                            */
976
/************************************************************************/
977
978
/**
979
 * \brief Remove a geometry from the container.
980
 *
981
 * Removing a geometry will cause the geometry count to drop by one, and all
982
 * "higher" geometries will shuffle down one in index.
983
 *
984
 * @param iGeom the index of the geometry to delete.  A value of -1 is a
985
 * special flag meaning that all geometries should be removed.
986
 *
987
 * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
988
 * not.  The default is TRUE as the container is considered to own the
989
 * geometries in it.
990
 *
991
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
992
 * out of range.
993
 */
994
995
OGRErr OGRPolyhedralSurface::removeGeometry(int iGeom, int bDelete)
996
0
{
997
0
    return oMP.removeGeometry(iGeom, bDelete);
998
0
}
999
1000
/************************************************************************/
1001
/*                           hasEmptyParts()                            */
1002
/************************************************************************/
1003
1004
bool OGRPolyhedralSurface::hasEmptyParts() const
1005
0
{
1006
0
    return oMP.hasEmptyParts();
1007
0
}
1008
1009
/************************************************************************/
1010
/*                          removeEmptyParts()                          */
1011
/************************************************************************/
1012
1013
void OGRPolyhedralSurface::removeEmptyParts()
1014
0
{
1015
0
    oMP.removeEmptyParts();
1016
0
}
1017
1018
/************************************************************************/
1019
/*                       assignSpatialReference()                       */
1020
/************************************************************************/
1021
1022
void OGRPolyhedralSurface::assignSpatialReference(
1023
    const OGRSpatialReference *poSR)
1024
0
{
1025
0
    OGRGeometry::assignSpatialReference(poSR);
1026
0
    oMP.assignSpatialReference(poSR);
1027
0
}