Coverage Report

Created: 2025-06-13 06:29

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