Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrmultisurface.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRMultiSurface class.
5
 * Author:   Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_geometry.h"
15
16
#include <cstddef>
17
18
#include "cpl_conv.h"
19
#include "cpl_error.h"
20
#include "ogr_api.h"
21
#include "ogr_core.h"
22
#include "ogr_p.h"
23
24
/************************************************************************/
25
/*              OGRMultiSurface( const OGRMultiSurface& )               */
26
/************************************************************************/
27
28
/**
29
 * \brief Copy constructor.
30
 */
31
32
0
OGRMultiSurface::OGRMultiSurface(const OGRMultiSurface &) = default;
33
34
/************************************************************************/
35
/*                  operator=( const OGRMultiCurve&)                    */
36
/************************************************************************/
37
38
/**
39
 * \brief Assignment operator.
40
 */
41
42
OGRMultiSurface &OGRMultiSurface::operator=(const OGRMultiSurface &other)
43
0
{
44
0
    if (this != &other)
45
0
    {
46
0
        OGRGeometryCollection::operator=(other);
47
0
    }
48
0
    return *this;
49
0
}
50
51
/************************************************************************/
52
/*                               clone()                                */
53
/************************************************************************/
54
55
OGRMultiSurface *OGRMultiSurface::clone() const
56
57
0
{
58
0
    auto ret = new (std::nothrow) OGRMultiSurface(*this);
59
0
    if (ret)
60
0
    {
61
0
        if (ret->WkbSize() != WkbSize())
62
0
        {
63
0
            delete ret;
64
0
            ret = nullptr;
65
0
        }
66
0
    }
67
0
    return ret;
68
0
}
69
70
/************************************************************************/
71
/*                          getGeometryType()                           */
72
/************************************************************************/
73
74
OGRwkbGeometryType OGRMultiSurface::getGeometryType() const
75
76
1
{
77
1
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
78
1
        return wkbMultiSurfaceZM;
79
0
    else if (flags & OGR_G_MEASURED)
80
0
        return wkbMultiSurfaceM;
81
0
    else if (flags & OGR_G_3D)
82
0
        return wkbMultiSurfaceZ;
83
0
    else
84
0
        return wkbMultiSurface;
85
1
}
86
87
/************************************************************************/
88
/*                            getDimension()                            */
89
/************************************************************************/
90
91
int OGRMultiSurface::getDimension() const
92
93
0
{
94
0
    return 2;
95
0
}
96
97
/************************************************************************/
98
/*                          getGeometryName()                           */
99
/************************************************************************/
100
101
const char *OGRMultiSurface::getGeometryName() const
102
103
2
{
104
2
    return "MULTISURFACE";
105
2
}
106
107
/************************************************************************/
108
/*                          isCompatibleSubType()                       */
109
/************************************************************************/
110
111
OGRBoolean
112
OGRMultiSurface::isCompatibleSubType(OGRwkbGeometryType eGeomType) const
113
7
{
114
7
    OGRwkbGeometryType eFlattenGeomType = wkbFlatten(eGeomType);
115
7
    return eFlattenGeomType == wkbPolygon ||
116
0
           eFlattenGeomType == wkbCurvePolygon;
117
7
}
118
119
/************************************************************************/
120
/*                           importFromWkt()                            */
121
/*                                                                      */
122
/*      Instantiate from well known text format.                        */
123
/************************************************************************/
124
125
OGRErr OGRMultiSurface::importFromWkt(const char **ppszInput)
126
127
528
{
128
528
    int bHasZ = FALSE;
129
528
    int bHasM = FALSE;
130
528
    bool bIsEmpty = false;
131
528
    OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
132
528
    flags = 0;
133
528
    if (eErr != OGRERR_NONE)
134
2
        return eErr;
135
526
    if (bHasZ)
136
52
        flags |= OGR_G_3D;
137
526
    if (bHasM)
138
177
        flags |= OGR_G_MEASURED;
139
526
    if (bIsEmpty)
140
0
        return OGRERR_NONE;
141
142
526
    char szToken[OGR_WKT_TOKEN_MAX] = {};
143
526
    const char *pszInput = *ppszInput;
144
526
    eErr = OGRERR_NONE;
145
146
    // Skip first '('.
147
526
    pszInput = OGRWktReadToken(pszInput, szToken);
148
149
    /* ==================================================================== */
150
    /*      Read each surface in turn.  Note that we try to reuse the same  */
151
    /*      point list buffer from ring to ring to cut down on              */
152
    /*      allocate/deallocate overhead.                                   */
153
    /* ==================================================================== */
154
526
    OGRRawPoint *paoPoints = nullptr;
155
526
    int nMaxPoints = 0;
156
526
    double *padfZ = nullptr;
157
158
526
    do
159
1.94k
    {
160
        /* --------------------------------------------------------------------
161
         */
162
        /*      Get the first token, which should be the geometry type. */
163
        /* --------------------------------------------------------------------
164
         */
165
1.94k
        const char *pszInputBefore = pszInput;
166
1.94k
        pszInput = OGRWktReadToken(pszInput, szToken);
167
168
1.94k
        OGRSurface *poSurface = nullptr;
169
170
        /* --------------------------------------------------------------------
171
         */
172
        /*      Do the import. */
173
        /* --------------------------------------------------------------------
174
         */
175
1.94k
        if (EQUAL(szToken, "("))
176
1.41k
        {
177
1.41k
            OGRPolygon *poPolygon = new OGRPolygon();
178
1.41k
            poSurface = poPolygon;
179
1.41k
            pszInput = pszInputBefore;
180
1.41k
            eErr = poPolygon->importFromWKTListOnly(
181
1.41k
                &pszInput, bHasZ, bHasM, paoPoints, nMaxPoints, padfZ);
182
1.41k
        }
183
534
        else if (EQUAL(szToken, "EMPTY"))
184
24
        {
185
24
            poSurface = new OGRPolygon();
186
24
        }
187
        // We accept POLYGON() but this is an extension to the BNF, also
188
        // accepted by PostGIS.
189
510
        else if (STARTS_WITH_CI(szToken, "POLYGON") ||
190
9
                 STARTS_WITH_CI(szToken, "CURVEPOLYGON"))
191
501
        {
192
501
            OGRGeometry *poGeom = nullptr;
193
501
            pszInput = pszInputBefore;
194
501
            eErr =
195
501
                OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
196
501
            if (poGeom == nullptr)
197
4
            {
198
4
                eErr = OGRERR_CORRUPT_DATA;
199
4
                break;
200
4
            }
201
497
            poSurface = poGeom->toSurface();
202
497
        }
203
9
        else
204
9
        {
205
9
            CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
206
9
                     szToken);
207
9
            eErr = OGRERR_CORRUPT_DATA;
208
9
            break;
209
9
        }
210
211
1.93k
        if (eErr == OGRERR_NONE)
212
1.92k
            eErr = addGeometryDirectly(poSurface);
213
1.93k
        if (eErr != OGRERR_NONE)
214
16
        {
215
16
            delete poSurface;
216
16
            break;
217
16
        }
218
219
        /* --------------------------------------------------------------------
220
         */
221
        /*      Read the delimiter following the surface. */
222
        /* --------------------------------------------------------------------
223
         */
224
1.92k
        pszInput = OGRWktReadToken(pszInput, szToken);
225
1.92k
    } while (szToken[0] == ',' && eErr == OGRERR_NONE);
226
227
526
    CPLFree(paoPoints);
228
526
    CPLFree(padfZ);
229
230
    /* -------------------------------------------------------------------- */
231
    /*      freak if we don't get a closing bracket.                        */
232
    /* -------------------------------------------------------------------- */
233
234
526
    if (eErr != OGRERR_NONE)
235
29
        return eErr;
236
237
497
    if (szToken[0] != ')')
238
2
        return OGRERR_CORRUPT_DATA;
239
240
495
    *ppszInput = pszInput;
241
495
    return OGRERR_NONE;
242
497
}
243
244
/************************************************************************/
245
/*                            exportToWkt()                             */
246
/************************************************************************/
247
248
std::string OGRMultiSurface::exportToWkt(const OGRWktOptions &opts,
249
                                         OGRErr *err) const
250
0
{
251
0
    OGRWktOptions optsModified(opts);
252
0
    optsModified.variant = wkbVariantIso;
253
0
    return exportToWktInternal(optsModified, err, "POLYGON");
254
0
}
255
256
/************************************************************************/
257
/*                         hasCurveGeometry()                           */
258
/************************************************************************/
259
260
OGRBoolean OGRMultiSurface::hasCurveGeometry(int bLookForNonLinear) const
261
1
{
262
1
    if (bLookForNonLinear)
263
0
        return OGRGeometryCollection::hasCurveGeometry(TRUE);
264
1
    return TRUE;
265
1
}
266
267
/************************************************************************/
268
/*                            PointOnSurface()                          */
269
/************************************************************************/
270
271
/** \brief This method relates to the SFCOM
272
 * IMultiSurface::get_PointOnSurface() method.
273
 *
274
 * NOTE: Only implemented when GEOS included in build.
275
 *
276
 * @param poPoint point to be set with an internal point.
277
 *
278
 * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise.
279
 */
280
281
OGRErr OGRMultiSurface::PointOnSurface(OGRPoint *poPoint) const
282
0
{
283
0
    return PointOnSurfaceInternal(poPoint);
284
0
}
285
286
/************************************************************************/
287
/*                         CastToMultiPolygon()                         */
288
/************************************************************************/
289
290
/**
291
 * \brief Cast to multipolygon.
292
 *
293
 * This method should only be called if the multisurface actually only contains
294
 * instances of OGRPolygon. This can be verified if hasCurveGeometry(TRUE)
295
 * returns FALSE. It is not intended to approximate curve polygons. For that
296
 * use getLinearGeometry().
297
 *
298
 * The passed in geometry is consumed and a new one returned (or NULL in case
299
 * of failure).
300
 *
301
 * @param poMS the input geometry - ownership is passed to the method.
302
 * @return new geometry.
303
 */
304
305
OGRMultiPolygon *OGRMultiSurface::CastToMultiPolygon(OGRMultiSurface *poMS)
306
0
{
307
0
    for (auto &&poSubGeom : *poMS)
308
0
    {
309
0
        poSubGeom = OGRSurface::CastToPolygon(poSubGeom);
310
0
        if (poSubGeom == nullptr)
311
0
        {
312
0
            delete poMS;
313
0
            return nullptr;
314
0
        }
315
0
    }
316
317
0
    OGRMultiPolygon *poMP = new OGRMultiPolygon();
318
0
    TransferMembersAndDestroy(poMS, poMP);
319
0
    return poMP;
320
0
}