/src/gdal/ogr/ogrcurvecollection.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: The OGRCurveCollection 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 | | #include <cstring> |
18 | | #include <limits> |
19 | | #include <new> |
20 | | |
21 | | #include "ogr_core.h" |
22 | | #include "ogr_p.h" |
23 | | #include "ogr_spatialref.h" |
24 | | #include "cpl_conv.h" |
25 | | #include "cpl_error.h" |
26 | | #include "cpl_string.h" |
27 | | #include "cpl_vsi.h" |
28 | | |
29 | | //! @cond Doxygen_Suppress |
30 | | |
31 | | /************************************************************************/ |
32 | | /* OGRCurveCollection( const OGRCurveCollection& ) */ |
33 | | /************************************************************************/ |
34 | | |
35 | | /** |
36 | | * \brief Copy constructor. |
37 | | */ |
38 | | |
39 | | OGRCurveCollection::OGRCurveCollection(const OGRCurveCollection &other) |
40 | 0 | { |
41 | 0 | if (other.nCurveCount > 0) |
42 | 0 | { |
43 | 0 | nCurveCount = other.nCurveCount; |
44 | 0 | papoCurves = static_cast<OGRCurve **>( |
45 | 0 | VSI_CALLOC_VERBOSE(sizeof(void *), nCurveCount)); |
46 | |
|
47 | 0 | if (papoCurves) |
48 | 0 | { |
49 | 0 | for (int i = 0; i < nCurveCount; i++) |
50 | 0 | { |
51 | 0 | papoCurves[i] = other.papoCurves[i]->clone(); |
52 | 0 | } |
53 | 0 | } |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | | /************************************************************************/ |
58 | | /* OGRCurveCollection( OGRCurveCollection&& ) */ |
59 | | /************************************************************************/ |
60 | | |
61 | | /** |
62 | | * \brief Move constructor. |
63 | | * |
64 | | * @since GDAL 3.11 |
65 | | */ |
66 | | |
67 | | OGRCurveCollection::OGRCurveCollection(OGRCurveCollection &&other) |
68 | 0 | : nCurveCount(other.nCurveCount), papoCurves(other.papoCurves) |
69 | 0 | { |
70 | 0 | other.nCurveCount = 0; |
71 | 0 | other.papoCurves = nullptr; |
72 | 0 | } |
73 | | |
74 | | /************************************************************************/ |
75 | | /* ~OGRCurveCollection() */ |
76 | | /************************************************************************/ |
77 | | |
78 | | OGRCurveCollection::~OGRCurveCollection() |
79 | | |
80 | 2.38k | { |
81 | 2.38k | empty(nullptr); |
82 | 2.38k | } |
83 | | |
84 | | /************************************************************************/ |
85 | | /* operator=( const OGRCurveCollection& ) */ |
86 | | /************************************************************************/ |
87 | | |
88 | | /** |
89 | | * \brief Assignment operator. |
90 | | */ |
91 | | |
92 | | OGRCurveCollection & |
93 | | OGRCurveCollection::operator=(const OGRCurveCollection &other) |
94 | 0 | { |
95 | 0 | if (this != &other) |
96 | 0 | { |
97 | 0 | empty(nullptr); |
98 | |
|
99 | 0 | if (other.nCurveCount > 0) |
100 | 0 | { |
101 | 0 | nCurveCount = other.nCurveCount; |
102 | 0 | papoCurves = static_cast<OGRCurve **>( |
103 | 0 | VSI_MALLOC2_VERBOSE(sizeof(void *), nCurveCount)); |
104 | |
|
105 | 0 | if (papoCurves) |
106 | 0 | { |
107 | 0 | for (int i = 0; i < nCurveCount; i++) |
108 | 0 | { |
109 | 0 | papoCurves[i] = other.papoCurves[i]->clone(); |
110 | 0 | } |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | 0 | return *this; |
115 | 0 | } |
116 | | |
117 | | /************************************************************************/ |
118 | | /* operator=( OGRCurveCollection&& ) */ |
119 | | /************************************************************************/ |
120 | | |
121 | | /** |
122 | | * \brief Move assignment operator. |
123 | | * |
124 | | * @since GDAL 3.11 |
125 | | */ |
126 | | |
127 | | OGRCurveCollection &OGRCurveCollection::operator=(OGRCurveCollection &&other) |
128 | 0 | { |
129 | 0 | if (this != &other) |
130 | 0 | { |
131 | 0 | empty(nullptr); |
132 | 0 | std::swap(nCurveCount, other.nCurveCount); |
133 | 0 | std::swap(papoCurves, other.papoCurves); |
134 | 0 | } |
135 | 0 | return *this; |
136 | 0 | } |
137 | | |
138 | | /************************************************************************/ |
139 | | /* WkbSize() */ |
140 | | /************************************************************************/ |
141 | | |
142 | | size_t OGRCurveCollection::WkbSize() const |
143 | 0 | { |
144 | 0 | size_t nSize = 9; |
145 | |
|
146 | 0 | for (auto &&poSubGeom : *this) |
147 | 0 | { |
148 | 0 | nSize += poSubGeom->WkbSize(); |
149 | 0 | } |
150 | |
|
151 | 0 | return nSize; |
152 | 0 | } |
153 | | |
154 | | /************************************************************************/ |
155 | | /* addCurveDirectly() */ |
156 | | /************************************************************************/ |
157 | | |
158 | | OGRErr OGRCurveCollection::addCurveDirectly(OGRGeometry *poGeom, |
159 | | OGRCurve *poCurve, int bNeedRealloc) |
160 | 1.30k | { |
161 | 1.30k | poGeom->HomogenizeDimensionalityWith(poCurve); |
162 | | |
163 | 1.30k | if (bNeedRealloc) |
164 | 1.30k | { |
165 | | #if SIZEOF_VOIDP < 8 |
166 | | if (nCurveCount == std::numeric_limits<int>::max() / |
167 | | static_cast<int>(sizeof(OGRCurve *))) |
168 | | { |
169 | | CPLError(CE_Failure, CPLE_OutOfMemory, "Too many subgeometries"); |
170 | | return OGRERR_FAILURE; |
171 | | } |
172 | | #else |
173 | 1.30k | if (nCurveCount == std::numeric_limits<int>::max()) |
174 | 0 | { |
175 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Too many subgeometries"); |
176 | 0 | return OGRERR_FAILURE; |
177 | 0 | } |
178 | 1.30k | #endif |
179 | | |
180 | 1.30k | OGRCurve **papoNewCurves = static_cast<OGRCurve **>(VSI_REALLOC_VERBOSE( |
181 | 1.30k | papoCurves, sizeof(OGRCurve *) * (nCurveCount + 1))); |
182 | 1.30k | if (papoNewCurves == nullptr) |
183 | 0 | return OGRERR_FAILURE; |
184 | 1.30k | papoCurves = papoNewCurves; |
185 | 1.30k | } |
186 | | |
187 | 1.30k | papoCurves[nCurveCount] = poCurve; |
188 | | |
189 | 1.30k | nCurveCount++; |
190 | | |
191 | 1.30k | return OGRERR_NONE; |
192 | 1.30k | } |
193 | | |
194 | | /************************************************************************/ |
195 | | /* importPreambleFromWkb() */ |
196 | | /************************************************************************/ |
197 | | |
198 | | OGRErr OGRCurveCollection::importPreambleFromWkb( |
199 | | OGRGeometry *poGeom, const unsigned char *pabyData, size_t &nSize, |
200 | | size_t &nDataOffset, OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, |
201 | | OGRwkbVariant eWkbVariant) |
202 | 0 | { |
203 | 0 | int nCurveCountNew = 0; |
204 | |
|
205 | 0 | OGRErr eErr = poGeom->importPreambleOfCollectionFromWkb( |
206 | 0 | pabyData, nSize, nDataOffset, eByteOrder, nMinSubGeomSize, |
207 | 0 | nCurveCountNew, eWkbVariant); |
208 | 0 | if (eErr != OGRERR_NONE) |
209 | 0 | return eErr; |
210 | | |
211 | 0 | CPLAssert(nCurveCount == 0); |
212 | 0 | nCurveCount = nCurveCountNew; |
213 | | |
214 | | // coverity[tainted_data] |
215 | 0 | papoCurves = static_cast<OGRCurve **>( |
216 | 0 | VSI_CALLOC_VERBOSE(sizeof(void *), nCurveCount)); |
217 | 0 | if (nCurveCount != 0 && papoCurves == nullptr) |
218 | 0 | { |
219 | 0 | nCurveCount = 0; |
220 | 0 | return OGRERR_NOT_ENOUGH_MEMORY; |
221 | 0 | } |
222 | | |
223 | 0 | return OGRERR_NONE; |
224 | 0 | } |
225 | | |
226 | | /************************************************************************/ |
227 | | /* importBodyFromWkb() */ |
228 | | /************************************************************************/ |
229 | | |
230 | | OGRErr OGRCurveCollection::importBodyFromWkb( |
231 | | OGRGeometry *poGeom, const unsigned char *pabyData, size_t nSize, |
232 | | bool bAcceptCompoundCurve, |
233 | | OGRErr (*pfnAddCurveDirectlyFromWkb)(OGRGeometry *poGeom, |
234 | | OGRCurve *poCurve), |
235 | | OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut) |
236 | 0 | { |
237 | 0 | nBytesConsumedOut = 0; |
238 | | /* -------------------------------------------------------------------- */ |
239 | | /* Get the Geoms. */ |
240 | | /* -------------------------------------------------------------------- */ |
241 | 0 | const int nIter = nCurveCount; |
242 | 0 | nCurveCount = 0; |
243 | 0 | size_t nDataOffset = 0; |
244 | 0 | for (int iGeom = 0; iGeom < nIter; iGeom++) |
245 | 0 | { |
246 | 0 | OGRGeometry *poSubGeom = nullptr; |
247 | | |
248 | | // Parses sub-geometry. |
249 | 0 | const unsigned char *pabySubData = pabyData + nDataOffset; |
250 | 0 | if (nSize < 9 && nSize != static_cast<size_t>(-1)) |
251 | 0 | return OGRERR_NOT_ENOUGH_DATA; |
252 | | |
253 | 0 | OGRwkbGeometryType eFlattenSubGeomType = wkbUnknown; |
254 | 0 | if (OGRReadWKBGeometryType(pabySubData, eWkbVariant, |
255 | 0 | &eFlattenSubGeomType) != OGRERR_NONE) |
256 | 0 | return OGRERR_FAILURE; |
257 | 0 | eFlattenSubGeomType = wkbFlatten(eFlattenSubGeomType); |
258 | |
|
259 | 0 | OGRErr eErr = OGRERR_NONE; |
260 | 0 | size_t nSubGeomBytesConsumedOut = 0; |
261 | 0 | if ((eFlattenSubGeomType != wkbCompoundCurve && |
262 | 0 | OGR_GT_IsCurve(eFlattenSubGeomType)) || |
263 | 0 | (bAcceptCompoundCurve && eFlattenSubGeomType == wkbCompoundCurve)) |
264 | 0 | { |
265 | 0 | eErr = OGRGeometryFactory::createFromWkb( |
266 | 0 | pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant, |
267 | 0 | nSubGeomBytesConsumedOut); |
268 | 0 | } |
269 | 0 | else |
270 | 0 | { |
271 | 0 | CPLDebug( |
272 | 0 | "OGR", |
273 | 0 | "Cannot add geometry of type (%d) to geometry of type (%d)", |
274 | 0 | eFlattenSubGeomType, poGeom->getGeometryType()); |
275 | 0 | return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; |
276 | 0 | } |
277 | | |
278 | 0 | if (eErr == OGRERR_NONE) |
279 | 0 | { |
280 | 0 | CPLAssert(nSubGeomBytesConsumedOut > 0); |
281 | 0 | if (nSize != static_cast<size_t>(-1)) |
282 | 0 | { |
283 | 0 | CPLAssert(nSize >= nSubGeomBytesConsumedOut); |
284 | 0 | nSize -= nSubGeomBytesConsumedOut; |
285 | 0 | } |
286 | | |
287 | 0 | nDataOffset += nSubGeomBytesConsumedOut; |
288 | |
|
289 | 0 | OGRCurve *poCurve = poSubGeom->toCurve(); |
290 | 0 | eErr = pfnAddCurveDirectlyFromWkb(poGeom, poCurve); |
291 | 0 | } |
292 | 0 | if (eErr != OGRERR_NONE) |
293 | 0 | { |
294 | 0 | delete poSubGeom; |
295 | 0 | return eErr; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | nBytesConsumedOut = nDataOffset; |
299 | |
|
300 | 0 | return OGRERR_NONE; |
301 | 0 | } |
302 | | |
303 | | /************************************************************************/ |
304 | | /* exportToWkt() */ |
305 | | /************************************************************************/ |
306 | | |
307 | | std::string OGRCurveCollection::exportToWkt(const OGRGeometry *baseGeom, |
308 | | const OGRWktOptions &opts, |
309 | | OGRErr *err) const |
310 | 0 | { |
311 | 0 | try |
312 | 0 | { |
313 | 0 | bool first = true; |
314 | 0 | std::string wkt(baseGeom->getGeometryName()); |
315 | |
|
316 | 0 | OGRWktOptions optsModified(opts); |
317 | 0 | optsModified.variant = wkbVariantIso; |
318 | 0 | wkt += baseGeom->wktTypeString(optsModified.variant); |
319 | |
|
320 | 0 | for (int i = 0; i < nCurveCount; ++i) |
321 | 0 | { |
322 | 0 | OGRGeometry *geom = papoCurves[i]; |
323 | |
|
324 | 0 | OGRErr subgeomErr = OGRERR_NONE; |
325 | 0 | std::string tempWkt = geom->exportToWkt(optsModified, &subgeomErr); |
326 | 0 | if (subgeomErr != OGRERR_NONE) |
327 | 0 | { |
328 | 0 | if (err) |
329 | 0 | *err = subgeomErr; |
330 | 0 | return std::string(); |
331 | 0 | } |
332 | | |
333 | | // A curve collection has a list of linestrings (OGRCompoundCurve), |
334 | | // which should have their leader removed, or a OGRCurvePolygon, |
335 | | // which has leaders for each of its sub-geometries that aren't |
336 | | // linestrings. |
337 | 0 | if (tempWkt.compare(0, strlen("LINESTRING"), "LINESTRING") == 0) |
338 | 0 | { |
339 | 0 | auto pos = tempWkt.find('('); |
340 | 0 | if (pos != std::string::npos) |
341 | 0 | tempWkt = tempWkt.substr(pos); |
342 | 0 | } |
343 | |
|
344 | 0 | if (tempWkt.find("EMPTY") != std::string::npos) |
345 | 0 | continue; |
346 | | |
347 | 0 | if (first) |
348 | 0 | wkt += '('; |
349 | 0 | else |
350 | 0 | wkt += ','; |
351 | 0 | first = false; |
352 | 0 | wkt += tempWkt; |
353 | 0 | } |
354 | | |
355 | 0 | if (err) |
356 | 0 | *err = OGRERR_NONE; |
357 | 0 | if (first) |
358 | 0 | wkt += "EMPTY"; |
359 | 0 | else |
360 | 0 | wkt += ')'; |
361 | 0 | return wkt; |
362 | 0 | } |
363 | 0 | catch (const std::bad_alloc &e) |
364 | 0 | { |
365 | 0 | CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what()); |
366 | 0 | if (err) |
367 | 0 | *err = OGRERR_FAILURE; |
368 | 0 | return std::string(); |
369 | 0 | } |
370 | 0 | } |
371 | | |
372 | | /************************************************************************/ |
373 | | /* exportToWkb() */ |
374 | | /************************************************************************/ |
375 | | |
376 | | OGRErr |
377 | | OGRCurveCollection::exportToWkb(const OGRGeometry *poGeom, |
378 | | unsigned char *pabyData, |
379 | | const OGRwkbExportOptions *psOptions) const |
380 | 0 | { |
381 | 0 | if (psOptions == nullptr) |
382 | 0 | { |
383 | 0 | static const OGRwkbExportOptions defaultOptions; |
384 | 0 | psOptions = &defaultOptions; |
385 | 0 | } |
386 | | |
387 | | /* -------------------------------------------------------------------- */ |
388 | | /* Set the byte order. */ |
389 | | /* -------------------------------------------------------------------- */ |
390 | 0 | pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER( |
391 | 0 | static_cast<unsigned char>(psOptions->eByteOrder)); |
392 | | |
393 | | /* -------------------------------------------------------------------- */ |
394 | | /* Set the geometry feature type, ensuring that 3D flag is */ |
395 | | /* preserved. */ |
396 | | /* -------------------------------------------------------------------- */ |
397 | 0 | GUInt32 nGType = poGeom->getIsoGeometryType(); |
398 | 0 | if (psOptions->eWkbVariant == wkbVariantPostGIS1) |
399 | 0 | { |
400 | 0 | const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType)); |
401 | 0 | nGType = wkbFlatten(nGType); |
402 | 0 | if (nGType == wkbCurvePolygon) |
403 | 0 | nGType = POSTGIS15_CURVEPOLYGON; |
404 | 0 | if (bIs3D) |
405 | | // Explicitly set wkb25DBit. |
406 | 0 | nGType = |
407 | 0 | static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse); |
408 | 0 | } |
409 | |
|
410 | 0 | if (OGR_SWAP(psOptions->eByteOrder)) |
411 | 0 | { |
412 | 0 | nGType = CPL_SWAP32(nGType); |
413 | 0 | } |
414 | |
|
415 | 0 | memcpy(pabyData + 1, &nGType, 4); |
416 | | |
417 | | /* -------------------------------------------------------------------- */ |
418 | | /* Copy in the raw data. */ |
419 | | /* -------------------------------------------------------------------- */ |
420 | 0 | if (OGR_SWAP(psOptions->eByteOrder)) |
421 | 0 | { |
422 | 0 | const int nCount = CPL_SWAP32(nCurveCount); |
423 | 0 | memcpy(pabyData + 5, &nCount, 4); |
424 | 0 | } |
425 | 0 | else |
426 | 0 | { |
427 | 0 | memcpy(pabyData + 5, &nCurveCount, 4); |
428 | 0 | } |
429 | | |
430 | | // TODO(schwehr): Where do these 9 values come from? |
431 | 0 | size_t nOffset = 9; |
432 | | |
433 | | /* ==================================================================== */ |
434 | | /* Serialize each of the Geoms. */ |
435 | | /* ==================================================================== */ |
436 | 0 | for (auto &&poSubGeom : *this) |
437 | 0 | { |
438 | 0 | poSubGeom->exportToWkb(pabyData + nOffset, psOptions); |
439 | |
|
440 | 0 | nOffset += poSubGeom->WkbSize(); |
441 | 0 | } |
442 | |
|
443 | 0 | return OGRERR_NONE; |
444 | 0 | } |
445 | | |
446 | | /************************************************************************/ |
447 | | /* empty() */ |
448 | | /************************************************************************/ |
449 | | |
450 | | void OGRCurveCollection::empty(OGRGeometry *poGeom) |
451 | 3.18k | { |
452 | 3.18k | if (papoCurves != nullptr) |
453 | 2.18k | { |
454 | 2.18k | for (auto &&poSubGeom : *this) |
455 | 3.92k | { |
456 | 3.92k | delete poSubGeom; |
457 | 3.92k | } |
458 | 2.18k | CPLFree(papoCurves); |
459 | 2.18k | } |
460 | | |
461 | 3.18k | nCurveCount = 0; |
462 | 3.18k | papoCurves = nullptr; |
463 | 3.18k | if (poGeom) |
464 | 793 | poGeom->setCoordinateDimension(2); |
465 | 3.18k | } |
466 | | |
467 | | /************************************************************************/ |
468 | | /* getEnvelope() */ |
469 | | /************************************************************************/ |
470 | | |
471 | | void OGRCurveCollection::getEnvelope(OGREnvelope *psEnvelope) const |
472 | 0 | { |
473 | 0 | OGREnvelope3D oEnv3D; |
474 | 0 | getEnvelope(&oEnv3D); |
475 | 0 | psEnvelope->MinX = oEnv3D.MinX; |
476 | 0 | psEnvelope->MinY = oEnv3D.MinY; |
477 | 0 | psEnvelope->MaxX = oEnv3D.MaxX; |
478 | 0 | psEnvelope->MaxY = oEnv3D.MaxY; |
479 | 0 | } |
480 | | |
481 | | /************************************************************************/ |
482 | | /* getEnvelope() */ |
483 | | /************************************************************************/ |
484 | | |
485 | | void OGRCurveCollection::getEnvelope(OGREnvelope3D *psEnvelope) const |
486 | 0 | { |
487 | 0 | OGREnvelope3D oGeomEnv; |
488 | 0 | bool bExtentSet = false; |
489 | |
|
490 | 0 | *psEnvelope = OGREnvelope3D(); |
491 | 0 | for (int iGeom = 0; iGeom < nCurveCount; iGeom++) |
492 | 0 | { |
493 | 0 | if (!papoCurves[iGeom]->IsEmpty()) |
494 | 0 | { |
495 | 0 | bExtentSet = true; |
496 | 0 | papoCurves[iGeom]->getEnvelope(&oGeomEnv); |
497 | 0 | psEnvelope->Merge(oGeomEnv); |
498 | 0 | } |
499 | 0 | } |
500 | |
|
501 | 0 | if (!bExtentSet) |
502 | 0 | { |
503 | | // To be backward compatible when called on empty geom |
504 | 0 | psEnvelope->MinX = 0.0; |
505 | 0 | psEnvelope->MinY = 0.0; |
506 | 0 | psEnvelope->MinZ = 0.0; |
507 | 0 | psEnvelope->MaxX = 0.0; |
508 | 0 | psEnvelope->MaxY = 0.0; |
509 | 0 | psEnvelope->MaxZ = 0.0; |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | | /************************************************************************/ |
514 | | /* IsEmpty() */ |
515 | | /************************************************************************/ |
516 | | |
517 | | OGRBoolean OGRCurveCollection::IsEmpty() const |
518 | 0 | { |
519 | 0 | for (auto &&poSubGeom : *this) |
520 | 0 | { |
521 | 0 | if (!poSubGeom->IsEmpty()) |
522 | 0 | return FALSE; |
523 | 0 | } |
524 | 0 | return TRUE; |
525 | 0 | } |
526 | | |
527 | | /************************************************************************/ |
528 | | /* Equals() */ |
529 | | /************************************************************************/ |
530 | | |
531 | | OGRBoolean OGRCurveCollection::Equals(const OGRCurveCollection *poOCC) const |
532 | 0 | { |
533 | 0 | if (getNumCurves() != poOCC->getNumCurves()) |
534 | 0 | return FALSE; |
535 | | |
536 | | // Should eventually test the SRS. |
537 | | |
538 | 0 | for (int iGeom = 0; iGeom < nCurveCount; iGeom++) |
539 | 0 | { |
540 | 0 | if (!getCurve(iGeom)->Equals(poOCC->getCurve(iGeom))) |
541 | 0 | return FALSE; |
542 | 0 | } |
543 | | |
544 | 0 | return TRUE; |
545 | 0 | } |
546 | | |
547 | | /************************************************************************/ |
548 | | /* setCoordinateDimension() */ |
549 | | /************************************************************************/ |
550 | | |
551 | | bool OGRCurveCollection::setCoordinateDimension(OGRGeometry *poGeom, |
552 | | int nNewDimension) |
553 | 793 | { |
554 | 793 | for (auto &&poSubGeom : *this) |
555 | 0 | { |
556 | 0 | if (!poSubGeom->setCoordinateDimension(nNewDimension)) |
557 | 0 | return false; |
558 | 0 | } |
559 | | |
560 | 793 | return poGeom->OGRGeometry::setCoordinateDimension(nNewDimension); |
561 | 793 | } |
562 | | |
563 | | bool OGRCurveCollection::set3D(OGRGeometry *poGeom, OGRBoolean bIs3D) |
564 | 849 | { |
565 | 849 | for (auto &&poSubGeom : *this) |
566 | 941 | { |
567 | 941 | if (!poSubGeom->set3D(bIs3D)) |
568 | 0 | return false; |
569 | 941 | } |
570 | | |
571 | 849 | return poGeom->OGRGeometry::set3D(bIs3D); |
572 | 849 | } |
573 | | |
574 | | bool OGRCurveCollection::setMeasured(OGRGeometry *poGeom, |
575 | | OGRBoolean bIsMeasured) |
576 | 1.77k | { |
577 | 1.77k | for (auto &&poSubGeom : *this) |
578 | 1.14k | { |
579 | 1.14k | if (!poSubGeom->setMeasured(bIsMeasured)) |
580 | 0 | return false; |
581 | 1.14k | } |
582 | | |
583 | 1.77k | return poGeom->OGRGeometry::setMeasured(bIsMeasured); |
584 | 1.77k | } |
585 | | |
586 | | /************************************************************************/ |
587 | | /* assignSpatialReference() */ |
588 | | /************************************************************************/ |
589 | | |
590 | | void OGRCurveCollection::assignSpatialReference(OGRGeometry *poGeom, |
591 | | const OGRSpatialReference *poSR) |
592 | 2.52k | { |
593 | 2.52k | for (auto &&poSubGeom : *this) |
594 | 2.82k | { |
595 | 2.82k | poSubGeom->assignSpatialReference(poSR); |
596 | 2.82k | } |
597 | 2.52k | poGeom->OGRGeometry::assignSpatialReference(poSR); |
598 | 2.52k | } |
599 | | |
600 | | /************************************************************************/ |
601 | | /* getNumCurves() */ |
602 | | /************************************************************************/ |
603 | | |
604 | | int OGRCurveCollection::getNumCurves() const |
605 | 0 | { |
606 | 0 | return nCurveCount; |
607 | 0 | } |
608 | | |
609 | | /************************************************************************/ |
610 | | /* getCurve() */ |
611 | | /************************************************************************/ |
612 | | |
613 | | OGRCurve *OGRCurveCollection::getCurve(int i) |
614 | 6.53k | { |
615 | 6.53k | if (i < 0 || i >= nCurveCount) |
616 | 18 | return nullptr; |
617 | 6.51k | return papoCurves[i]; |
618 | 6.53k | } |
619 | | |
620 | | /************************************************************************/ |
621 | | /* getCurve() */ |
622 | | /************************************************************************/ |
623 | | |
624 | | const OGRCurve *OGRCurveCollection::getCurve(int i) const |
625 | 0 | { |
626 | 0 | if (i < 0 || i >= nCurveCount) |
627 | 0 | return nullptr; |
628 | 0 | return papoCurves[i]; |
629 | 0 | } |
630 | | |
631 | | /************************************************************************/ |
632 | | /* stealCurve() */ |
633 | | /************************************************************************/ |
634 | | |
635 | | OGRCurve *OGRCurveCollection::stealCurve(int i) |
636 | 0 | { |
637 | 0 | if (i < 0 || i >= nCurveCount) |
638 | 0 | return nullptr; |
639 | 0 | OGRCurve *poRet = papoCurves[i]; |
640 | 0 | if (i < nCurveCount - 1) |
641 | 0 | { |
642 | 0 | memmove(papoCurves + i, papoCurves + i + 1, |
643 | 0 | (nCurveCount - i - 1) * sizeof(OGRCurve *)); |
644 | 0 | } |
645 | 0 | nCurveCount--; |
646 | 0 | return poRet; |
647 | 0 | } |
648 | | |
649 | | /************************************************************************/ |
650 | | /* transform() */ |
651 | | /************************************************************************/ |
652 | | |
653 | | OGRErr OGRCurveCollection::transform(OGRGeometry *poGeom, |
654 | | OGRCoordinateTransformation *poCT) |
655 | 0 | { |
656 | 0 | for (int iGeom = 0; iGeom < nCurveCount; iGeom++) |
657 | 0 | { |
658 | 0 | const OGRErr eErr = papoCurves[iGeom]->transform(poCT); |
659 | 0 | if (eErr != OGRERR_NONE) |
660 | 0 | { |
661 | 0 | if (iGeom != 0) |
662 | 0 | { |
663 | 0 | CPLDebug("OGR", "OGRCurveCollection::transform() failed for a " |
664 | 0 | "geometry other than the first, meaning some " |
665 | 0 | "geometries are transformed and some are not!"); |
666 | |
|
667 | 0 | return OGRERR_FAILURE; |
668 | 0 | } |
669 | | |
670 | 0 | return eErr; |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | 0 | poGeom->assignSpatialReference(poCT->GetTargetCS()); |
675 | |
|
676 | 0 | return OGRERR_NONE; |
677 | 0 | } |
678 | | |
679 | | /************************************************************************/ |
680 | | /* flattenTo2D() */ |
681 | | /************************************************************************/ |
682 | | |
683 | | void OGRCurveCollection::flattenTo2D(OGRGeometry *poGeom) |
684 | 0 | { |
685 | 0 | for (auto &&poSubGeom : *this) |
686 | 0 | { |
687 | 0 | poSubGeom->flattenTo2D(); |
688 | 0 | } |
689 | |
|
690 | 0 | poGeom->setCoordinateDimension(2); |
691 | 0 | } |
692 | | |
693 | | /************************************************************************/ |
694 | | /* segmentize() */ |
695 | | /************************************************************************/ |
696 | | |
697 | | bool OGRCurveCollection::segmentize(double dfMaxLength) |
698 | 0 | { |
699 | 0 | for (auto &&poSubGeom : *this) |
700 | 0 | { |
701 | 0 | if (!poSubGeom->segmentize(dfMaxLength)) |
702 | 0 | return false; |
703 | 0 | } |
704 | 0 | return true; |
705 | 0 | } |
706 | | |
707 | | /************************************************************************/ |
708 | | /* swapXY() */ |
709 | | /************************************************************************/ |
710 | | |
711 | | void OGRCurveCollection::swapXY() |
712 | 0 | { |
713 | 0 | for (auto &&poSubGeom : *this) |
714 | 0 | { |
715 | 0 | poSubGeom->swapXY(); |
716 | 0 | } |
717 | 0 | } |
718 | | |
719 | | /************************************************************************/ |
720 | | /* hasCurveGeometry() */ |
721 | | /************************************************************************/ |
722 | | |
723 | | OGRBoolean OGRCurveCollection::hasCurveGeometry(int bLookForNonLinear) const |
724 | 0 | { |
725 | 0 | for (auto &&poSubGeom : *this) |
726 | 0 | { |
727 | 0 | if (poSubGeom->hasCurveGeometry(bLookForNonLinear)) |
728 | 0 | return TRUE; |
729 | 0 | } |
730 | 0 | return FALSE; |
731 | 0 | } |
732 | | |
733 | | /************************************************************************/ |
734 | | /* removeCurve() */ |
735 | | /************************************************************************/ |
736 | | |
737 | | /** |
738 | | * \brief Remove a geometry from the container. |
739 | | * |
740 | | * Removing a geometry will cause the geometry count to drop by one, and all |
741 | | * "higher" geometries will shuffle down one in index. |
742 | | * |
743 | | * @param iIndex the index of the geometry to delete. A value of -1 is a |
744 | | * special flag meaning that all geometries should be removed. |
745 | | * |
746 | | * @param bDelete if true the geometry will be deallocated, otherwise it will |
747 | | * not. The default is true as the container is considered to own the |
748 | | * geometries in it. |
749 | | * |
750 | | * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is |
751 | | * out of range. |
752 | | */ |
753 | | |
754 | | OGRErr OGRCurveCollection::removeCurve(int iIndex, bool bDelete) |
755 | | |
756 | 0 | { |
757 | 0 | if (iIndex < -1 || iIndex >= nCurveCount) |
758 | 0 | return OGRERR_FAILURE; |
759 | | |
760 | | // Special case. |
761 | 0 | if (iIndex == -1) |
762 | 0 | { |
763 | 0 | while (nCurveCount > 0) |
764 | 0 | removeCurve(nCurveCount - 1, bDelete); |
765 | 0 | return OGRERR_NONE; |
766 | 0 | } |
767 | | |
768 | 0 | if (bDelete) |
769 | 0 | delete papoCurves[iIndex]; |
770 | |
|
771 | 0 | memmove(papoCurves + iIndex, papoCurves + iIndex + 1, |
772 | 0 | sizeof(void *) * (nCurveCount - iIndex - 1)); |
773 | |
|
774 | 0 | nCurveCount--; |
775 | |
|
776 | 0 | return OGRERR_NONE; |
777 | 0 | } |
778 | | |
779 | | /************************************************************************/ |
780 | | /* hasEmptyParts() */ |
781 | | /************************************************************************/ |
782 | | |
783 | | /** |
784 | | * \brief Returns whether a geometry has empty parts/rings. |
785 | | * |
786 | | * Returns true if removeEmptyParts() will modify the geometry. |
787 | | * |
788 | | * This is different from IsEmpty(). |
789 | | * |
790 | | * @since GDAL 3.10 |
791 | | */ |
792 | | bool OGRCurveCollection::hasEmptyParts() const |
793 | 0 | { |
794 | 0 | for (int i = 0; i < nCurveCount; ++i) |
795 | 0 | { |
796 | 0 | if (papoCurves[i]->IsEmpty() || papoCurves[i]->hasEmptyParts()) |
797 | 0 | return true; |
798 | 0 | } |
799 | 0 | return false; |
800 | 0 | } |
801 | | |
802 | | /************************************************************************/ |
803 | | /* removeEmptyParts() */ |
804 | | /************************************************************************/ |
805 | | |
806 | | /** |
807 | | * \brief Remove empty parts/rings from this geometry. |
808 | | * |
809 | | * @since GDAL 3.10 |
810 | | */ |
811 | | void OGRCurveCollection::removeEmptyParts() |
812 | 0 | { |
813 | 0 | for (int i = nCurveCount - 1; i >= 0; --i) |
814 | 0 | { |
815 | 0 | papoCurves[i]->removeEmptyParts(); |
816 | 0 | if (papoCurves[i]->IsEmpty()) |
817 | 0 | removeCurve(i, true); |
818 | 0 | } |
819 | 0 | } |
820 | | |
821 | | /************************************************************************/ |
822 | | /* reversePoints() */ |
823 | | /************************************************************************/ |
824 | | |
825 | | /** |
826 | | * \brief Reverse point order. |
827 | | * |
828 | | * This method updates the points in this curve in place |
829 | | * reversing the point ordering (first for last, etc) and component ordering. |
830 | | * |
831 | | * @since 3.10 |
832 | | */ |
833 | | void OGRCurveCollection::reversePoints() |
834 | | |
835 | 0 | { |
836 | 0 | for (int i = 0; i < nCurveCount / 2; ++i) |
837 | 0 | { |
838 | 0 | std::swap(papoCurves[i], papoCurves[nCurveCount - 1 - i]); |
839 | 0 | } |
840 | 0 | for (int i = 0; i < nCurveCount; ++i) |
841 | 0 | { |
842 | 0 | papoCurves[i]->reversePoints(); |
843 | 0 | } |
844 | 0 | } |
845 | | |
846 | | //! @endcond |