Coverage Report

Created: 2026-07-25 06:50

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
0
{
77
0
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
78
0
        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
0
}
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
0
{
104
0
    return "MULTISURFACE";
105
0
}
106
107
/************************************************************************/
108
/*                        isCompatibleSubType()                         */
109
/************************************************************************/
110
111
bool OGRMultiSurface::isCompatibleSubType(OGRwkbGeometryType eGeomType) const
112
0
{
113
0
    OGRwkbGeometryType eFlattenGeomType = wkbFlatten(eGeomType);
114
0
    return eFlattenGeomType == wkbPolygon ||
115
0
           eFlattenGeomType == wkbCurvePolygon;
116
0
}
117
118
/************************************************************************/
119
/*                           importFromWkt()                            */
120
/*                                                                      */
121
/*      Instantiate from well known text format.                        */
122
/************************************************************************/
123
124
OGRErr OGRMultiSurface::importFromWkt(const char **ppszInput)
125
126
0
{
127
0
    int bHasZ = FALSE;
128
0
    int bHasM = FALSE;
129
0
    bool bIsEmpty = false;
130
0
    OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
131
0
    flags = 0;
132
0
    if (eErr != OGRERR_NONE)
133
0
        return eErr;
134
0
    if (bHasZ)
135
0
        flags |= OGR_G_3D;
136
0
    if (bHasM)
137
0
        flags |= OGR_G_MEASURED;
138
0
    if (bIsEmpty)
139
0
        return OGRERR_NONE;
140
141
0
    char szToken[OGR_WKT_TOKEN_MAX] = {};
142
0
    const char *pszInput = *ppszInput;
143
0
    eErr = OGRERR_NONE;
144
145
    // Skip first '('.
146
0
    pszInput = OGRWktReadToken(pszInput, szToken);
147
148
    /* ==================================================================== */
149
    /*      Read each surface in turn.  Note that we try to reuse the same  */
150
    /*      point list buffer from ring to ring to cut down on              */
151
    /*      allocate/deallocate overhead.                                   */
152
    /* ==================================================================== */
153
0
    OGRRawPoint *paoPoints = nullptr;
154
0
    int nMaxPoints = 0;
155
0
    double *padfZ = nullptr;
156
157
0
    do
158
0
    {
159
        /* --------------------------------------------------------------------
160
         */
161
        /*      Get the first token, which should be the geometry type. */
162
        /* --------------------------------------------------------------------
163
         */
164
0
        const char *pszInputBefore = pszInput;
165
0
        pszInput = OGRWktReadToken(pszInput, szToken);
166
167
0
        OGRSurface *poSurface = nullptr;
168
169
        /* --------------------------------------------------------------------
170
         */
171
        /*      Do the import. */
172
        /* --------------------------------------------------------------------
173
         */
174
0
        if (EQUAL(szToken, "("))
175
0
        {
176
0
            OGRPolygon *poPolygon = new OGRPolygon();
177
0
            poSurface = poPolygon;
178
0
            pszInput = pszInputBefore;
179
0
            eErr = poPolygon->importFromWKTListOnly(
180
0
                &pszInput, bHasZ, bHasM, paoPoints, nMaxPoints, padfZ);
181
0
        }
182
0
        else if (EQUAL(szToken, "EMPTY"))
183
0
        {
184
0
            poSurface = new OGRPolygon();
185
0
        }
186
        // We accept POLYGON() but this is an extension to the BNF, also
187
        // accepted by PostGIS.
188
0
        else if (STARTS_WITH_CI(szToken, "POLYGON") ||
189
0
                 STARTS_WITH_CI(szToken, "CURVEPOLYGON"))
190
0
        {
191
0
            OGRGeometry *poGeom = nullptr;
192
0
            pszInput = pszInputBefore;
193
0
            eErr =
194
0
                OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
195
0
            if (poGeom == nullptr)
196
0
            {
197
0
                eErr = OGRERR_CORRUPT_DATA;
198
0
                break;
199
0
            }
200
0
            poSurface = poGeom->toSurface();
201
0
        }
202
0
        else
203
0
        {
204
0
            CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
205
0
                     szToken);
206
0
            eErr = OGRERR_CORRUPT_DATA;
207
0
            break;
208
0
        }
209
210
0
        if (eErr == OGRERR_NONE)
211
0
            eErr = addGeometryDirectly(poSurface);
212
0
        if (eErr != OGRERR_NONE)
213
0
        {
214
0
            delete poSurface;
215
0
            break;
216
0
        }
217
218
        /* --------------------------------------------------------------------
219
         */
220
        /*      Read the delimiter following the surface. */
221
        /* --------------------------------------------------------------------
222
         */
223
0
        pszInput = OGRWktReadToken(pszInput, szToken);
224
0
    } while (szToken[0] == ',' && eErr == OGRERR_NONE);
225
226
0
    CPLFree(paoPoints);
227
0
    CPLFree(padfZ);
228
229
    /* -------------------------------------------------------------------- */
230
    /*      freak if we don't get a closing bracket.                        */
231
    /* -------------------------------------------------------------------- */
232
233
0
    if (eErr != OGRERR_NONE)
234
0
        return eErr;
235
236
0
    if (szToken[0] != ')')
237
0
        return OGRERR_CORRUPT_DATA;
238
239
0
    *ppszInput = pszInput;
240
0
    return OGRERR_NONE;
241
0
}
242
243
/************************************************************************/
244
/*                            exportToWkt()                             */
245
/************************************************************************/
246
247
std::string OGRMultiSurface::exportToWkt(const OGRWktOptions &opts,
248
                                         OGRErr *err) const
249
0
{
250
0
    OGRWktOptions optsModified(opts);
251
0
    optsModified.variant = wkbVariantIso;
252
0
    return exportToWktInternal(optsModified, err, "POLYGON");
253
0
}
254
255
/************************************************************************/
256
/*                          hasCurveGeometry()                          */
257
/************************************************************************/
258
259
bool OGRMultiSurface::hasCurveGeometry(int bLookForNonLinear) const
260
0
{
261
0
    if (bLookForNonLinear)
262
0
        return OGRGeometryCollection::hasCurveGeometry(TRUE);
263
0
    return TRUE;
264
0
}
265
266
/************************************************************************/
267
/*                           PointOnSurface()                           */
268
/************************************************************************/
269
270
/** \brief This method relates to the SFCOM
271
 * IMultiSurface::get_PointOnSurface() method.
272
 *
273
 * NOTE: Only implemented when GEOS included in build.
274
 *
275
 * @param poPoint point to be set with an internal point.
276
 *
277
 * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise.
278
 */
279
280
OGRErr OGRMultiSurface::PointOnSurface(OGRPoint *poPoint) const
281
0
{
282
0
    return PointOnSurfaceInternal(poPoint);
283
0
}
284
285
/************************************************************************/
286
/*                         CastToMultiPolygon()                         */
287
/************************************************************************/
288
289
/**
290
 * \brief Cast to multipolygon.
291
 *
292
 * This method should only be called if the multisurface actually only contains
293
 * instances of OGRPolygon. This can be verified if hasCurveGeometry(TRUE)
294
 * returns FALSE. It is not intended to approximate curve polygons. For that
295
 * use getLinearGeometry().
296
 *
297
 * The passed in geometry is consumed and a new one returned (or NULL in case
298
 * of failure).
299
 *
300
 * @param poMS the input geometry - ownership is passed to the method.
301
 * @return new geometry.
302
 */
303
304
OGRMultiPolygon *OGRMultiSurface::CastToMultiPolygon(OGRMultiSurface *poMS)
305
0
{
306
0
    for (auto &&poSubGeom : *poMS)
307
0
    {
308
0
        poSubGeom = OGRSurface::CastToPolygon(poSubGeom);
309
0
        if (poSubGeom == nullptr)
310
0
        {
311
0
            delete poMS;
312
0
            return nullptr;
313
0
        }
314
0
    }
315
316
0
    OGRMultiPolygon *poMP = new OGRMultiPolygon();
317
0
    TransferMembersAndDestroy(poMS, poMP);
318
0
    return poMP;
319
0
}