/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 | } |