Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGRGeometryCollection class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_geometry.h"
16 :
17 : #include <cstddef>
18 : #include <cstring>
19 : #include <limits>
20 : #include <new>
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_error.h"
24 : #include "cpl_string.h"
25 : #include "cpl_vsi.h"
26 : #include "ogr_api.h"
27 : #include "ogr_core.h"
28 : #include "ogr_p.h"
29 : #include "ogr_spatialref.h"
30 :
31 : /************************************************************************/
32 : /* OGRGeometryCollection( const OGRGeometryCollection& ) */
33 : /************************************************************************/
34 :
35 : /**
36 : * \brief Copy constructor.
37 : */
38 :
39 5544 : OGRGeometryCollection::OGRGeometryCollection(const OGRGeometryCollection &other)
40 5544 : : OGRGeometry(other)
41 : {
42 : // Do not use addGeometry() as it is virtual.
43 5544 : papoGeoms = static_cast<OGRGeometry **>(
44 5544 : VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), other.nGeomCount));
45 5544 : if (papoGeoms)
46 : {
47 5544 : nGeomCount = other.nGeomCount;
48 14723 : for (int i = 0; i < other.nGeomCount; i++)
49 : {
50 9179 : papoGeoms[i] = other.papoGeoms[i]->clone();
51 : }
52 : }
53 5544 : }
54 :
55 : /************************************************************************/
56 : /* OGRGeometryCollection( OGRGeometryCollection&& ) */
57 : /************************************************************************/
58 :
59 : /**
60 : * \brief Move constructor.
61 : *
62 : * @since GDAL 3.11
63 : */
64 :
65 : // cppcheck-suppress-begin accessMoved
66 8 : OGRGeometryCollection::OGRGeometryCollection(OGRGeometryCollection &&other)
67 16 : : OGRGeometry(std::move(other)), nGeomCount(other.nGeomCount),
68 8 : papoGeoms(other.papoGeoms)
69 : {
70 8 : other.nGeomCount = 0;
71 8 : other.papoGeoms = nullptr;
72 8 : }
73 :
74 : // cppcheck-suppress-end accessMoved
75 :
76 : /************************************************************************/
77 : /* ~OGRGeometryCollection() */
78 : /************************************************************************/
79 :
80 147901 : OGRGeometryCollection::~OGRGeometryCollection()
81 :
82 : {
83 138613 : OGRGeometryCollection::empty();
84 147901 : }
85 :
86 : /************************************************************************/
87 : /* operator=( const OGRGeometryCollection&) */
88 : /************************************************************************/
89 :
90 : /**
91 : * \brief Assignment operator.
92 : */
93 :
94 : OGRGeometryCollection &
95 30 : OGRGeometryCollection::operator=(const OGRGeometryCollection &other)
96 : {
97 30 : if (this != &other)
98 : {
99 29 : OGRGeometry::operator=(other);
100 :
101 51 : for (const auto *poOtherSubGeom : other)
102 : {
103 23 : if (!isCompatibleSubType(poOtherSubGeom->getGeometryType()))
104 : {
105 1 : CPLError(CE_Failure, CPLE_AppDefined,
106 : "Illegal use of OGRGeometryCollection::operator=(): "
107 : "trying to assign an incompatible sub-geometry");
108 1 : return *this;
109 : }
110 : }
111 :
112 28 : papoGeoms = static_cast<OGRGeometry **>(
113 28 : VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), other.nGeomCount));
114 28 : if (papoGeoms)
115 : {
116 28 : nGeomCount = other.nGeomCount;
117 50 : for (int i = 0; i < other.nGeomCount; i++)
118 : {
119 22 : papoGeoms[i] = other.papoGeoms[i]->clone();
120 : }
121 : }
122 : }
123 29 : return *this;
124 : }
125 :
126 : /************************************************************************/
127 : /* operator=( OGRGeometryCollection&&) */
128 : /************************************************************************/
129 :
130 : /**
131 : * \brief Move assignment operator.
132 : *
133 : * @since GDAL 3.11
134 : */
135 :
136 : OGRGeometryCollection &
137 16 : OGRGeometryCollection::operator=(OGRGeometryCollection &&other)
138 : {
139 16 : if (this != &other)
140 : {
141 16 : empty();
142 :
143 16 : OGRGeometry::operator=(std::move(other));
144 16 : std::swap(nGeomCount, other.nGeomCount);
145 16 : std::swap(papoGeoms, other.papoGeoms);
146 : }
147 16 : return *this;
148 : }
149 :
150 : /************************************************************************/
151 : /* empty() */
152 : /************************************************************************/
153 :
154 156113 : void OGRGeometryCollection::empty()
155 :
156 : {
157 156113 : if (papoGeoms != nullptr)
158 : {
159 1736090 : for (auto &poSubGeom : *this)
160 : {
161 1607840 : delete poSubGeom;
162 : }
163 128245 : CPLFree(papoGeoms);
164 : }
165 :
166 156113 : nGeomCount = 0;
167 156113 : papoGeoms = nullptr;
168 156113 : }
169 :
170 : /************************************************************************/
171 : /* clone() */
172 : /************************************************************************/
173 :
174 558 : OGRGeometryCollection *OGRGeometryCollection::clone() const
175 :
176 : {
177 558 : auto ret = new (std::nothrow) OGRGeometryCollection(*this);
178 558 : if (ret)
179 : {
180 558 : if (ret->WkbSize() != WkbSize())
181 : {
182 0 : delete ret;
183 0 : ret = nullptr;
184 : }
185 : }
186 558 : return ret;
187 : }
188 :
189 : /************************************************************************/
190 : /* getGeometryType() */
191 : /************************************************************************/
192 :
193 13473 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
194 :
195 : {
196 13473 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
197 2969 : return wkbGeometryCollectionZM;
198 10504 : else if (flags & OGR_G_MEASURED)
199 249 : return wkbGeometryCollectionM;
200 10255 : else if (flags & OGR_G_3D)
201 3791 : return wkbGeometryCollection25D;
202 : else
203 6464 : return wkbGeometryCollection;
204 : }
205 :
206 : /************************************************************************/
207 : /* getDimension() */
208 : /************************************************************************/
209 :
210 6 : int OGRGeometryCollection::getDimension() const
211 :
212 : {
213 6 : int nDimension = 0;
214 : // FIXME? Not sure if it is really appropriate to take the max in case
215 : // of geometries of different dimension.
216 12 : for (const auto &poSubGeom : *this)
217 : {
218 9 : int nSubGeomDimension = poSubGeom->getDimension();
219 9 : if (nSubGeomDimension > nDimension)
220 : {
221 5 : nDimension = nSubGeomDimension;
222 5 : if (nDimension == 2)
223 3 : break;
224 : }
225 : }
226 6 : return nDimension;
227 : }
228 :
229 : /************************************************************************/
230 : /* flattenTo2D() */
231 : /************************************************************************/
232 :
233 101 : void OGRGeometryCollection::flattenTo2D()
234 :
235 : {
236 301 : for (auto &poSubGeom : *this)
237 : {
238 200 : poSubGeom->flattenTo2D();
239 : }
240 :
241 101 : flags &= ~OGR_G_3D;
242 101 : flags &= ~OGR_G_MEASURED;
243 101 : }
244 :
245 : /************************************************************************/
246 : /* getGeometryName() */
247 : /************************************************************************/
248 :
249 1066 : const char *OGRGeometryCollection::getGeometryName() const
250 :
251 : {
252 1066 : return "GEOMETRYCOLLECTION";
253 : }
254 :
255 : /************************************************************************/
256 : /* getNumGeometries() */
257 : /************************************************************************/
258 :
259 : /**
260 : * \brief Fetch number of geometries in container.
261 : *
262 : * This method relates to the SFCOM IGeometryCollect::get_NumGeometries()
263 : * method.
264 : *
265 : * @return count of children geometries. May be zero.
266 : */
267 :
268 129976 : int OGRGeometryCollection::getNumGeometries() const
269 :
270 : {
271 129976 : return nGeomCount;
272 : }
273 :
274 : /************************************************************************/
275 : /* getGeometryRef() */
276 : /************************************************************************/
277 :
278 : /**
279 : * \brief Fetch geometry from container.
280 : *
281 : * This method returns a pointer to a geometry within the container. The
282 : * returned geometry remains owned by the container, and should not be
283 : * modified. The pointer is only valid until the next change to the
284 : * geometry container. Use IGeometry::clone() to make a copy.
285 : *
286 : * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
287 : *
288 : * @param i the index of the geometry to fetch, between 0 and
289 : * getNumGeometries() - 1.
290 : * @return pointer to requested geometry.
291 : */
292 :
293 31640 : OGRGeometry *OGRGeometryCollection::getGeometryRef(int i)
294 :
295 : {
296 31640 : if (i < 0 || i >= nGeomCount)
297 0 : return nullptr;
298 :
299 31640 : return papoGeoms[i];
300 : }
301 :
302 : /**
303 : * \brief Fetch geometry from container.
304 : *
305 : * This method returns a pointer to a geometry within the container. The
306 : * returned geometry remains owned by the container, and should not be
307 : * modified. The pointer is only valid until the next change to the
308 : * geometry container. Use IGeometry::clone() to make a copy.
309 : *
310 : * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
311 : *
312 : * @param i the index of the geometry to fetch, between 0 and
313 : * getNumGeometries() - 1.
314 : * @return pointer to requested geometry.
315 : */
316 :
317 207780 : const OGRGeometry *OGRGeometryCollection::getGeometryRef(int i) const
318 :
319 : {
320 207780 : if (i < 0 || i >= nGeomCount)
321 0 : return nullptr;
322 :
323 207780 : return papoGeoms[i];
324 : }
325 :
326 : /************************************************************************/
327 : /* addGeometry() */
328 : /* */
329 : /* Add a new geometry to a collection. Subclasses should */
330 : /* override this to verify the type of the new geometry, and */
331 : /* then call this method to actually add it. */
332 : /************************************************************************/
333 :
334 : /**
335 : * \brief Add a geometry to the container.
336 : *
337 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
338 : * that can be added, and may return an error. The passed geometry is cloned
339 : * to make an internal copy.
340 : *
341 : * There is no SFCOM analog to this method.
342 : *
343 : * This method is the same as the C function OGR_G_AddGeometry().
344 : *
345 : * @param poNewGeom geometry to add to the container.
346 : *
347 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
348 : * the geometry type is illegal for the type of geometry container.
349 : */
350 :
351 2189 : OGRErr OGRGeometryCollection::addGeometry(const OGRGeometry *poNewGeom)
352 :
353 : {
354 2189 : OGRGeometry *poClone = poNewGeom->clone();
355 2189 : if (poClone == nullptr)
356 0 : return OGRERR_FAILURE;
357 :
358 2189 : const OGRErr eErr = addGeometryDirectly(poClone);
359 2189 : if (eErr != OGRERR_NONE)
360 2 : delete poClone;
361 :
362 2189 : return eErr;
363 : }
364 :
365 : /************************************************************************/
366 : /* addGeometryDirectly() */
367 : /* */
368 : /* Add a new geometry to a collection. Subclasses should */
369 : /* override this to verify the type of the new geometry, and */
370 : /* then call this method to actually add it. */
371 : /************************************************************************/
372 :
373 : /**
374 : * \brief Add a geometry directly to the container.
375 : *
376 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
377 : * that can be added, and may return an error. Ownership of the passed
378 : * geometry is taken by the container rather than cloning as addGeometry()
379 : * does, but only if the method is successful. If the method fails, ownership
380 : * still belongs to the caller.
381 : *
382 : * This method is the same as the C function OGR_G_AddGeometryDirectly().
383 : *
384 : * There is no SFCOM analog to this method.
385 : *
386 : * @param poNewGeom geometry to add to the container.
387 : *
388 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
389 : * the geometry type is illegal for the type of geometry container.
390 : */
391 :
392 239470 : OGRErr OGRGeometryCollection::addGeometryDirectly(OGRGeometry *poNewGeom)
393 :
394 : {
395 239470 : if (!isCompatibleSubType(poNewGeom->getGeometryType()))
396 21195 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
397 :
398 : #if SIZEOF_VOIDP < 8
399 : if (nGeomCount == std::numeric_limits<int>::max() /
400 : static_cast<int>(sizeof(OGRGeometry *)))
401 : {
402 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too many subgeometries");
403 : return OGRERR_FAILURE;
404 : }
405 : #else
406 218275 : if (nGeomCount == std::numeric_limits<int>::max())
407 : {
408 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many subgeometries");
409 0 : return OGRERR_FAILURE;
410 : }
411 : #endif
412 :
413 218275 : HomogenizeDimensionalityWith(poNewGeom);
414 :
415 : OGRGeometry **papoNewGeoms =
416 218275 : static_cast<OGRGeometry **>(VSI_REALLOC_VERBOSE(
417 : papoGeoms, sizeof(OGRGeometry *) * (nGeomCount + 1)));
418 218275 : if (papoNewGeoms == nullptr)
419 0 : return OGRERR_FAILURE;
420 :
421 218275 : papoGeoms = papoNewGeoms;
422 218275 : papoGeoms[nGeomCount] = poNewGeom;
423 :
424 218275 : nGeomCount++;
425 :
426 218275 : return OGRERR_NONE;
427 : }
428 :
429 : /************************************************************************/
430 : /* addGeometry() */
431 : /************************************************************************/
432 :
433 : /**
434 : * \brief Add a geometry directly to the container.
435 : *
436 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
437 : * that can be added, and may return an error.
438 : *
439 : * There is no SFCOM analog to this method.
440 : *
441 : * @param geom geometry to add to the container.
442 : *
443 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
444 : * the geometry type is illegal for the type of geometry container.
445 : */
446 :
447 118586 : OGRErr OGRGeometryCollection::addGeometry(std::unique_ptr<OGRGeometry> geom)
448 : {
449 118586 : OGRGeometry *poGeom = geom.release();
450 118586 : OGRErr eErr = addGeometryDirectly(poGeom);
451 118586 : if (eErr != OGRERR_NONE)
452 21191 : delete poGeom;
453 118586 : return eErr;
454 : }
455 :
456 : /************************************************************************/
457 : /* addGeometryComponents() */
458 : /************************************************************************/
459 :
460 : /**
461 : * \brief Add the components of another OGRGeometryCollection to this one.
462 : *
463 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
464 : * that can be added, and may return an error.
465 : *
466 : * @param geom geometry whose components should be added to add to the container.
467 : *
468 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
469 : * the geometry type is illegal for the type of geometry container.
470 : *
471 : * @since 3.13
472 : */
473 :
474 4 : OGRErr OGRGeometryCollection::addGeometryComponents(
475 : std::unique_ptr<OGRGeometryCollection> geom)
476 : {
477 4 : if (geom->nGeomCount == 0)
478 : {
479 0 : return OGRERR_NONE;
480 : }
481 :
482 10 : for (int i = 0; i < geom->nGeomCount; i++)
483 : {
484 6 : if (!isCompatibleSubType(geom->papoGeoms[i]->getGeometryType()))
485 : {
486 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
487 : }
488 : }
489 :
490 10 : for (int i = 0; i < geom->nGeomCount; i++)
491 : {
492 6 : HomogenizeDimensionalityWith(geom->papoGeoms[i]);
493 : }
494 :
495 : OGRGeometry **papoNewGeoms = static_cast<OGRGeometry **>(
496 4 : VSI_REALLOC_VERBOSE(papoGeoms, sizeof(OGRGeometry *) *
497 : (nGeomCount + geom->nGeomCount)));
498 4 : if (papoNewGeoms == nullptr)
499 0 : return OGRERR_FAILURE;
500 :
501 10 : for (int i = 0; i < geom->nGeomCount; i++)
502 : {
503 6 : papoNewGeoms[nGeomCount + i] = geom->papoGeoms[i];
504 6 : geom->papoGeoms[i] = nullptr;
505 : }
506 :
507 4 : papoGeoms = papoNewGeoms;
508 4 : nGeomCount += geom->nGeomCount;
509 :
510 4 : return OGRERR_NONE;
511 : }
512 :
513 : /************************************************************************/
514 : /* removeGeometry() */
515 : /************************************************************************/
516 :
517 : /**
518 : * \brief Remove a geometry from the container.
519 : *
520 : * Removing a geometry will cause the geometry count to drop by one, and all
521 : * "higher" geometries will shuffle down one in index.
522 : *
523 : * There is no SFCOM analog to this method.
524 : *
525 : * This method is the same as the C function OGR_G_RemoveGeometry().
526 : *
527 : * @param iGeom the index of the geometry to delete. A value of -1 is a
528 : * special flag meaning that all geometries should be removed.
529 : *
530 : * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
531 : * not. The default is TRUE as the container is considered to own the
532 : * geometries in it. Note: using stealGeometry() might be a better alternative
533 : * to using bDelete = false.
534 : *
535 : * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
536 : * out of range.
537 : */
538 :
539 5248 : OGRErr OGRGeometryCollection::removeGeometry(int iGeom, int bDelete)
540 :
541 : {
542 5248 : if (iGeom < -1 || iGeom >= nGeomCount)
543 4 : return OGRERR_FAILURE;
544 :
545 : // Special case.
546 5244 : if (iGeom == -1)
547 : {
548 7 : while (nGeomCount > 0)
549 5 : removeGeometry(nGeomCount - 1, bDelete);
550 2 : return OGRERR_NONE;
551 : }
552 :
553 5242 : if (bDelete)
554 134 : delete papoGeoms[iGeom];
555 :
556 5242 : memmove(papoGeoms + iGeom, papoGeoms + iGeom + 1,
557 5242 : sizeof(OGRGeometry *) * (nGeomCount - iGeom - 1));
558 :
559 5242 : nGeomCount--;
560 :
561 5242 : return OGRERR_NONE;
562 : }
563 :
564 : /************************************************************************/
565 : /* stealGeometry() */
566 : /************************************************************************/
567 :
568 : /**
569 : * \brief Remove a geometry from the container and return it to the caller
570 : *
571 : * Removing a geometry will cause the geometry count to drop by one, and all
572 : * "higher" geometries will shuffle down one in index.
573 : *
574 : * There is no SFCOM analog to this method.
575 : *
576 : * @param iGeom the index of the geometry to delete.
577 : *
578 : * @return the sub-geometry, or nullptr in case of error.
579 : * @since 3.10
580 : */
581 :
582 20 : std::unique_ptr<OGRGeometry> OGRGeometryCollection::stealGeometry(int iGeom)
583 : {
584 20 : if (iGeom < 0 || iGeom >= nGeomCount)
585 0 : return nullptr;
586 :
587 40 : auto poSubGeom = std::unique_ptr<OGRGeometry>(papoGeoms[iGeom]);
588 20 : papoGeoms[iGeom] = nullptr;
589 20 : removeGeometry(iGeom);
590 20 : return poSubGeom;
591 : }
592 :
593 : /************************************************************************/
594 : /* hasEmptyParts() */
595 : /************************************************************************/
596 :
597 9 : bool OGRGeometryCollection::hasEmptyParts() const
598 : {
599 17 : for (const auto &poSubGeom : *this)
600 : {
601 11 : if (poSubGeom->IsEmpty() || poSubGeom->hasEmptyParts())
602 3 : return true;
603 : }
604 6 : return false;
605 : }
606 :
607 : /************************************************************************/
608 : /* removeEmptyParts() */
609 : /************************************************************************/
610 :
611 5 : void OGRGeometryCollection::removeEmptyParts()
612 : {
613 13 : for (int i = nGeomCount - 1; i >= 0; --i)
614 : {
615 8 : papoGeoms[i]->removeEmptyParts();
616 8 : if (papoGeoms[i]->IsEmpty())
617 3 : removeGeometry(i, true);
618 : }
619 5 : }
620 :
621 : /************************************************************************/
622 : /* WkbSize() */
623 : /* */
624 : /* Return the size of this object in well known binary */
625 : /* representation including the byte order, and type information. */
626 : /************************************************************************/
627 :
628 22429 : size_t OGRGeometryCollection::WkbSize() const
629 :
630 : {
631 22429 : size_t nSize = 9;
632 :
633 106584 : for (const auto &poGeom : *this)
634 : {
635 84155 : nSize += poGeom->WkbSize();
636 : }
637 :
638 22429 : return nSize;
639 : }
640 :
641 : /************************************************************************/
642 : /* importFromWkbInternal() */
643 : /************************************************************************/
644 :
645 : //! @cond Doxygen_Suppress
646 14227 : OGRErr OGRGeometryCollection::importFromWkbInternal(
647 : const unsigned char *pabyData, size_t nSize, int nRecLevel,
648 : OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
649 :
650 : {
651 14227 : nBytesConsumedOut = 0;
652 : // Arbitrary value, but certainly large enough for reasonable use cases.
653 14227 : if (nRecLevel == 32)
654 : {
655 1 : CPLError(CE_Failure, CPLE_AppDefined,
656 : "Too many recursion levels (%d) while parsing WKB geometry.",
657 : nRecLevel);
658 1 : return OGRERR_CORRUPT_DATA;
659 : }
660 :
661 14226 : OGRwkbByteOrder eByteOrder = wkbXDR;
662 14226 : size_t nDataOffset = 0;
663 14226 : int nGeomCountNew = 0;
664 14226 : OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
665 : nDataOffset, eByteOrder, 9,
666 : nGeomCountNew, eWkbVariant);
667 :
668 14226 : if (eErr != OGRERR_NONE)
669 324 : return eErr;
670 :
671 13902 : CPLAssert(nGeomCount == 0);
672 13902 : nGeomCount = nGeomCountNew;
673 :
674 : // coverity[tainted_data]
675 13902 : papoGeoms = static_cast<OGRGeometry **>(
676 13902 : VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), nGeomCount));
677 13902 : if (nGeomCount != 0 && papoGeoms == nullptr)
678 : {
679 0 : nGeomCount = 0;
680 0 : return OGRERR_NOT_ENOUGH_MEMORY;
681 : }
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* Get the Geoms. */
685 : /* -------------------------------------------------------------------- */
686 41685 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
687 : {
688 : // Parses sub-geometry.
689 28674 : const unsigned char *pabySubData = pabyData + nDataOffset;
690 28674 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
691 891 : return OGRERR_NOT_ENOUGH_DATA;
692 :
693 28662 : OGRwkbGeometryType eSubGeomType = wkbUnknown;
694 28662 : eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
695 28662 : if (eErr != OGRERR_NONE)
696 372 : return eErr;
697 :
698 28290 : if (!isCompatibleSubType(eSubGeomType))
699 : {
700 1 : nGeomCount = iGeom;
701 1 : CPLDebug(
702 : "OGR",
703 : "Cannot add geometry of type (%d) to geometry of type (%d)",
704 1 : eSubGeomType, getGeometryType());
705 1 : return OGRERR_CORRUPT_DATA;
706 : }
707 :
708 28289 : OGRGeometry *poSubGeom = nullptr;
709 28289 : size_t nSubGeomBytesConsumed = 0;
710 28289 : if (OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection))
711 : {
712 192 : poSubGeom = OGRGeometryFactory::createGeometry(eSubGeomType);
713 192 : if (poSubGeom == nullptr)
714 0 : eErr = OGRERR_FAILURE;
715 : else
716 192 : eErr = poSubGeom->toGeometryCollection()->importFromWkbInternal(
717 : pabySubData, nSize, nRecLevel + 1, eWkbVariant,
718 : nSubGeomBytesConsumed);
719 : }
720 : else
721 : {
722 28097 : eErr = OGRGeometryFactory::createFromWkb(
723 : pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
724 : nSubGeomBytesConsumed);
725 :
726 28097 : if (eErr == OGRERR_NONE)
727 : {
728 : // if this is a Z or M geom make sure the sub geoms are as well
729 27623 : if (Is3D() && !poSubGeom->Is3D())
730 : {
731 0 : CPLDebug("OGR", "Promoting sub-geometry to 3D");
732 0 : poSubGeom->set3D(TRUE);
733 : }
734 :
735 27623 : if (IsMeasured() && !poSubGeom->IsMeasured())
736 : {
737 0 : CPLDebug("OGR", "Promoting sub-geometry to Measured");
738 0 : poSubGeom->setMeasured(TRUE);
739 : }
740 : }
741 : }
742 :
743 28289 : if (eErr != OGRERR_NONE)
744 : {
745 506 : nGeomCount = iGeom;
746 506 : delete poSubGeom;
747 506 : return eErr;
748 : }
749 :
750 27783 : papoGeoms[iGeom] = poSubGeom;
751 :
752 27783 : if (papoGeoms[iGeom]->Is3D())
753 5780 : flags |= OGR_G_3D;
754 27783 : if (papoGeoms[iGeom]->IsMeasured())
755 5678 : flags |= OGR_G_MEASURED;
756 :
757 27783 : CPLAssert(nSubGeomBytesConsumed > 0);
758 27783 : if (nSize != static_cast<size_t>(-1))
759 : {
760 27778 : CPLAssert(nSize >= nSubGeomBytesConsumed);
761 27778 : nSize -= nSubGeomBytesConsumed;
762 : }
763 :
764 27783 : nDataOffset += nSubGeomBytesConsumed;
765 : }
766 13011 : nBytesConsumedOut = nDataOffset;
767 :
768 13011 : return OGRERR_NONE;
769 : }
770 :
771 : //! @endcond
772 :
773 : /************************************************************************/
774 : /* importFromWkb() */
775 : /* */
776 : /* Initialize from serialized stream in well known binary */
777 : /* format. */
778 : /************************************************************************/
779 :
780 5065 : OGRErr OGRGeometryCollection::importFromWkb(const unsigned char *pabyData,
781 : size_t nSize,
782 : OGRwkbVariant eWkbVariant,
783 : size_t &nBytesConsumedOut)
784 :
785 : {
786 5065 : return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant,
787 5065 : nBytesConsumedOut);
788 : }
789 :
790 : /************************************************************************/
791 : /* exportToWkb() */
792 : /* */
793 : /* Build a well known binary representation of this object. */
794 : /************************************************************************/
795 :
796 : OGRErr
797 12452 : OGRGeometryCollection::exportToWkb(unsigned char *pabyData,
798 : const OGRwkbExportOptions *psOptions) const
799 :
800 : {
801 12452 : if (psOptions == nullptr)
802 : {
803 : static const OGRwkbExportOptions defaultOptions;
804 0 : psOptions = &defaultOptions;
805 : }
806 :
807 12452 : OGRwkbExportOptions sOptions(*psOptions);
808 :
809 12591 : if (sOptions.eWkbVariant == wkbVariantOldOgc &&
810 139 : (wkbFlatten(getGeometryType()) == wkbMultiCurve ||
811 129 : wkbFlatten(getGeometryType()) == wkbMultiSurface))
812 : {
813 : // Does not make sense for new geometries, so patch it.
814 19 : sOptions.eWkbVariant = wkbVariantIso;
815 : }
816 :
817 : /* -------------------------------------------------------------------- */
818 : /* Set the byte order. */
819 : /* -------------------------------------------------------------------- */
820 12452 : pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
821 : static_cast<unsigned char>(sOptions.eByteOrder));
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Set the geometry feature type, ensuring that 3D flag is */
825 : /* preserved. */
826 : /* -------------------------------------------------------------------- */
827 12452 : GUInt32 nGType = getGeometryType();
828 :
829 12452 : if (sOptions.eWkbVariant == wkbVariantIso)
830 12328 : nGType = getIsoGeometryType();
831 124 : else if (sOptions.eWkbVariant == wkbVariantPostGIS1)
832 : {
833 4 : const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
834 4 : nGType = wkbFlatten(nGType);
835 4 : if (nGType == wkbMultiCurve)
836 0 : nGType = POSTGIS15_MULTICURVE;
837 4 : else if (nGType == wkbMultiSurface)
838 0 : nGType = POSTGIS15_MULTISURFACE;
839 4 : if (bIs3D)
840 : // Yes, explicitly set wkb25DBit.
841 1 : nGType =
842 1 : static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
843 : }
844 :
845 12452 : if (OGR_SWAP(sOptions.eByteOrder))
846 : {
847 7 : nGType = CPL_SWAP32(nGType);
848 : }
849 :
850 12452 : memcpy(pabyData + 1, &nGType, 4);
851 :
852 : /* -------------------------------------------------------------------- */
853 : /* Copy in the raw data. */
854 : /* -------------------------------------------------------------------- */
855 12452 : if (OGR_SWAP(sOptions.eByteOrder))
856 : {
857 7 : int nCount = CPL_SWAP32(nGeomCount);
858 7 : memcpy(pabyData + 5, &nCount, 4);
859 : }
860 : else
861 : {
862 12445 : memcpy(pabyData + 5, &nGeomCount, 4);
863 : }
864 :
865 12452 : size_t nOffset = 9;
866 :
867 : /* ==================================================================== */
868 : /* Serialize each of the Geoms. */
869 : /* ==================================================================== */
870 12452 : int iGeom = 0;
871 81471 : for (auto &&poSubGeom : *this)
872 : {
873 69019 : poSubGeom->exportToWkb(pabyData + nOffset, &sOptions);
874 : // Should normally not happen if everyone else does its job,
875 : // but has happened sometimes. (#6332)
876 69019 : if (poSubGeom->getCoordinateDimension() != getCoordinateDimension())
877 : {
878 0 : CPLError(CE_Warning, CPLE_AppDefined,
879 : "Sub-geometry %d has coordinate dimension %d, "
880 : "but container has %d",
881 0 : iGeom, poSubGeom->getCoordinateDimension(),
882 0 : getCoordinateDimension());
883 : }
884 :
885 69019 : nOffset += poSubGeom->WkbSize();
886 69019 : iGeom++;
887 : }
888 :
889 12452 : return OGRERR_NONE;
890 : }
891 :
892 : /************************************************************************/
893 : /* importFromWktInternal() */
894 : /************************************************************************/
895 :
896 642 : OGRErr OGRGeometryCollection::importFromWktInternal(const char **ppszInput,
897 : int nRecLevel)
898 :
899 : {
900 : // Arbitrary value, but certainly large enough for reasonable usages.
901 642 : if (nRecLevel == 32)
902 : {
903 1 : CPLError(CE_Failure, CPLE_AppDefined,
904 : "Too many recursion levels (%d) while parsing WKT geometry.",
905 : nRecLevel);
906 1 : return OGRERR_CORRUPT_DATA;
907 : }
908 :
909 641 : int bHasZ = FALSE;
910 641 : int bHasM = FALSE;
911 641 : bool bIsEmpty = false;
912 641 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
913 641 : if (eErr != OGRERR_NONE)
914 4 : return eErr;
915 637 : if (bHasZ)
916 97 : flags |= OGR_G_3D;
917 637 : if (bHasM)
918 61 : flags |= OGR_G_MEASURED;
919 637 : if (bIsEmpty)
920 45 : return OGRERR_NONE;
921 :
922 592 : char szToken[OGR_WKT_TOKEN_MAX] = {};
923 592 : const char *pszInput = *ppszInput;
924 :
925 : // Skip first '('.
926 592 : pszInput = OGRWktReadToken(pszInput, szToken);
927 :
928 : /* ==================================================================== */
929 : /* Read each subgeometry in turn. */
930 : /* ==================================================================== */
931 650 : do
932 : {
933 1242 : OGRGeometry *poGeom = nullptr;
934 :
935 : /* --------------------------------------------------------------------
936 : */
937 : /* Get the first token, which should be the geometry type. */
938 : /* --------------------------------------------------------------------
939 : */
940 1242 : OGRWktReadToken(pszInput, szToken);
941 :
942 : /* --------------------------------------------------------------------
943 : */
944 : /* Do the import. */
945 : /* --------------------------------------------------------------------
946 : */
947 1242 : if (STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION"))
948 : {
949 110 : OGRGeometryCollection *poGC = new OGRGeometryCollection();
950 110 : poGeom = poGC;
951 110 : eErr = poGC->importFromWktInternal(&pszInput, nRecLevel + 1);
952 : }
953 : else
954 : eErr =
955 1132 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
956 :
957 1242 : if (eErr == OGRERR_NONE)
958 : {
959 : // If this has M, but not Z, it is an error if poGeom does
960 : // not have M.
961 1198 : if (!Is3D() && IsMeasured() && !poGeom->IsMeasured())
962 0 : eErr = OGRERR_CORRUPT_DATA;
963 : else
964 1198 : eErr = addGeometryDirectly(poGeom);
965 : }
966 1242 : if (eErr != OGRERR_NONE)
967 : {
968 44 : delete poGeom;
969 44 : return eErr;
970 : }
971 :
972 : /* --------------------------------------------------------------------
973 : */
974 : /* Read the delimiter following the ring. */
975 : /* --------------------------------------------------------------------
976 : */
977 :
978 1198 : pszInput = OGRWktReadToken(pszInput, szToken);
979 1198 : } while (szToken[0] == ',');
980 :
981 : /* -------------------------------------------------------------------- */
982 : /* freak if we don't get a closing bracket. */
983 : /* -------------------------------------------------------------------- */
984 548 : if (szToken[0] != ')')
985 2 : return OGRERR_CORRUPT_DATA;
986 :
987 546 : *ppszInput = pszInput;
988 :
989 546 : return OGRERR_NONE;
990 : }
991 :
992 : /************************************************************************/
993 : /* importFromWkt() */
994 : /************************************************************************/
995 :
996 532 : OGRErr OGRGeometryCollection::importFromWkt(const char **ppszInput)
997 :
998 : {
999 532 : return importFromWktInternal(ppszInput, 0);
1000 : }
1001 :
1002 : /************************************************************************/
1003 : /* exportToWkt() */
1004 : /* */
1005 : /* Translate this structure into its well known text format */
1006 : /* equivalent. */
1007 : /************************************************************************/
1008 :
1009 299 : std::string OGRGeometryCollection::exportToWkt(const OGRWktOptions &opts,
1010 : OGRErr *err) const
1011 : {
1012 299 : return exportToWktInternal(opts, err);
1013 : }
1014 :
1015 : //! @cond Doxygen_Suppress
1016 1245 : std::string OGRGeometryCollection::exportToWktInternal(
1017 : const OGRWktOptions &opts, OGRErr *err, const std::string &exclude) const
1018 : {
1019 1245 : bool first = true;
1020 1245 : const size_t excludeSize = exclude.size();
1021 2490 : std::string wkt(getGeometryName());
1022 1245 : wkt += wktTypeString(opts.variant);
1023 :
1024 : try
1025 : {
1026 2957 : for (const auto &poSubGeom : *this)
1027 : {
1028 1712 : OGRErr subgeomErr = OGRERR_NONE;
1029 1712 : std::string tempWkt = poSubGeom->exportToWkt(opts, &subgeomErr);
1030 1712 : if (subgeomErr != OGRERR_NONE)
1031 : {
1032 0 : if (err)
1033 0 : *err = subgeomErr;
1034 0 : return std::string();
1035 : }
1036 :
1037 : // For some strange reason we exclude the typename leader when using
1038 : // some geometries as part of a collection.
1039 1712 : if (excludeSize && (tempWkt.compare(0, excludeSize, exclude) == 0))
1040 : {
1041 1134 : auto pos = tempWkt.find('(');
1042 : // We won't have an opening paren if the geom is empty.
1043 1134 : if (pos == std::string::npos)
1044 31 : continue;
1045 1103 : tempWkt = tempWkt.substr(pos);
1046 : }
1047 :
1048 : // Also strange, we allow the inclusion of ISO-only geometries (see
1049 : // OGRPolyhedralSurface) in a non-iso geometry collection. In order
1050 : // to facilitate this, we need to rip the ISO bit from the string.
1051 1681 : if (opts.variant != wkbVariantIso)
1052 : {
1053 : std::string::size_type pos;
1054 656 : if ((pos = tempWkt.find(" Z ")) != std::string::npos)
1055 11 : tempWkt.erase(pos + 1, 2);
1056 645 : else if ((pos = tempWkt.find(" M ")) != std::string::npos)
1057 0 : tempWkt.erase(pos + 1, 2);
1058 645 : else if ((pos = tempWkt.find(" ZM ")) != std::string::npos)
1059 0 : tempWkt.erase(pos + 1, 3);
1060 : }
1061 :
1062 1681 : if (first)
1063 1085 : wkt += '(';
1064 : else
1065 596 : wkt += ',';
1066 1681 : first = false;
1067 1681 : wkt += tempWkt;
1068 : }
1069 :
1070 1245 : if (err)
1071 1128 : *err = OGRERR_NONE;
1072 1245 : if (first)
1073 160 : wkt += "EMPTY";
1074 : else
1075 1085 : wkt += ')';
1076 1245 : return wkt;
1077 : }
1078 0 : catch (const std::bad_alloc &e)
1079 : {
1080 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1081 0 : if (err)
1082 0 : *err = OGRERR_FAILURE;
1083 0 : return std::string();
1084 : }
1085 : }
1086 :
1087 : //! @endcond
1088 :
1089 : /************************************************************************/
1090 : /* getEnvelope() */
1091 : /************************************************************************/
1092 :
1093 16167 : void OGRGeometryCollection::getEnvelope(OGREnvelope *psEnvelope) const
1094 :
1095 : {
1096 16167 : OGREnvelope3D oEnv3D;
1097 16167 : getEnvelope(&oEnv3D);
1098 16167 : psEnvelope->MinX = oEnv3D.MinX;
1099 16167 : psEnvelope->MinY = oEnv3D.MinY;
1100 16167 : psEnvelope->MaxX = oEnv3D.MaxX;
1101 16167 : psEnvelope->MaxY = oEnv3D.MaxY;
1102 16167 : }
1103 :
1104 : /************************************************************************/
1105 : /* getEnvelope() */
1106 : /************************************************************************/
1107 :
1108 17003 : void OGRGeometryCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
1109 :
1110 : {
1111 17003 : OGREnvelope3D oGeomEnv;
1112 17003 : bool bExtentSet = false;
1113 :
1114 17003 : *psEnvelope = OGREnvelope3D();
1115 403247 : for (const auto &poSubGeom : *this)
1116 : {
1117 386244 : if (!poSubGeom->IsEmpty())
1118 : {
1119 386236 : bExtentSet = true;
1120 386236 : poSubGeom->getEnvelope(&oGeomEnv);
1121 386236 : psEnvelope->Merge(oGeomEnv);
1122 : }
1123 : }
1124 :
1125 17003 : if (!bExtentSet)
1126 : {
1127 : // To be backward compatible when called on empty geom
1128 13 : psEnvelope->MinX = 0.0;
1129 13 : psEnvelope->MinY = 0.0;
1130 13 : psEnvelope->MinZ = 0.0;
1131 13 : psEnvelope->MaxX = 0.0;
1132 13 : psEnvelope->MaxY = 0.0;
1133 13 : psEnvelope->MaxZ = 0.0;
1134 : }
1135 17003 : }
1136 :
1137 : /************************************************************************/
1138 : /* Equals() */
1139 : /************************************************************************/
1140 :
1141 3892 : OGRBoolean OGRGeometryCollection::Equals(const OGRGeometry *poOther) const
1142 :
1143 : {
1144 3892 : if (poOther == this)
1145 2 : return TRUE;
1146 :
1147 3890 : if (poOther->getGeometryType() != getGeometryType())
1148 0 : return FALSE;
1149 :
1150 3890 : if (IsEmpty() && poOther->IsEmpty())
1151 12 : return TRUE;
1152 :
1153 3878 : auto poOGC = poOther->toGeometryCollection();
1154 3878 : if (getNumGeometries() != poOGC->getNumGeometries())
1155 2 : return FALSE;
1156 :
1157 : // TODO(schwehr): Should test the SRS.
1158 :
1159 8532 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
1160 : {
1161 4657 : if (!getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)))
1162 1 : return FALSE;
1163 : }
1164 :
1165 3875 : return TRUE;
1166 : }
1167 :
1168 : /************************************************************************/
1169 : /* transform() */
1170 : /************************************************************************/
1171 :
1172 182 : OGRErr OGRGeometryCollection::transform(OGRCoordinateTransformation *poCT)
1173 :
1174 : {
1175 182 : int iGeom = 0;
1176 410 : for (auto &poSubGeom : *this)
1177 : {
1178 228 : const OGRErr eErr = poSubGeom->transform(poCT);
1179 228 : if (eErr != OGRERR_NONE)
1180 : {
1181 0 : if (iGeom != 0)
1182 : {
1183 0 : CPLDebug("OGR",
1184 : "OGRGeometryCollection::transform() failed for a "
1185 : "geometry other than the first, meaning some "
1186 : "geometries are transformed and some are not.");
1187 :
1188 0 : return OGRERR_FAILURE;
1189 : }
1190 :
1191 0 : return eErr;
1192 : }
1193 228 : iGeom++;
1194 : }
1195 :
1196 182 : assignSpatialReference(poCT->GetTargetCS());
1197 :
1198 182 : return OGRERR_NONE;
1199 : }
1200 :
1201 : /************************************************************************/
1202 : /* closeRings() */
1203 : /************************************************************************/
1204 :
1205 129 : void OGRGeometryCollection::closeRings()
1206 :
1207 : {
1208 412 : for (auto &poSubGeom : *this)
1209 : {
1210 283 : if (OGR_GT_IsSubClassOf(wkbFlatten(poSubGeom->getGeometryType()),
1211 283 : wkbCurvePolygon))
1212 : {
1213 83 : OGRCurvePolygon *poPoly = poSubGeom->toCurvePolygon();
1214 83 : poPoly->closeRings();
1215 : }
1216 : }
1217 129 : }
1218 :
1219 : /************************************************************************/
1220 : /* setCoordinateDimension() */
1221 : /************************************************************************/
1222 :
1223 494 : bool OGRGeometryCollection::setCoordinateDimension(int nNewDimension)
1224 :
1225 : {
1226 1439 : for (auto &poSubGeom : *this)
1227 : {
1228 945 : if (!poSubGeom->setCoordinateDimension(nNewDimension))
1229 0 : return false;
1230 : }
1231 :
1232 494 : return OGRGeometry::setCoordinateDimension(nNewDimension);
1233 : }
1234 :
1235 88884 : bool OGRGeometryCollection::set3D(OGRBoolean bIs3D)
1236 : {
1237 195589 : for (auto &poSubGeom : *this)
1238 : {
1239 106705 : if (!poSubGeom->set3D(bIs3D))
1240 0 : return false;
1241 : }
1242 :
1243 88884 : return OGRGeometry::set3D(bIs3D);
1244 : }
1245 :
1246 46302 : bool OGRGeometryCollection::setMeasured(OGRBoolean bIsMeasured)
1247 : {
1248 153578 : for (auto &poSubGeom : *this)
1249 : {
1250 107276 : if (!poSubGeom->setMeasured(bIsMeasured))
1251 0 : return false;
1252 : }
1253 :
1254 46302 : return OGRGeometry::setMeasured(bIsMeasured);
1255 : }
1256 :
1257 : /************************************************************************/
1258 : /* get_Length() */
1259 : /************************************************************************/
1260 :
1261 : /**
1262 : * \brief Compute the length of a multicurve.
1263 : *
1264 : * The length is computed as the sum of the length of all members
1265 : * in this collection.
1266 : *
1267 : * @note No warning will be issued if a member of the collection does not
1268 : * support the get_Length method.
1269 : *
1270 : * @return computed length.
1271 : */
1272 :
1273 9 : double OGRGeometryCollection::get_Length() const
1274 : {
1275 9 : double dfLength = 0.0;
1276 26 : for (const auto &poSubGeom : *this)
1277 : {
1278 : const OGRwkbGeometryType eType =
1279 17 : wkbFlatten(poSubGeom->getGeometryType());
1280 17 : if (OGR_GT_IsCurve(eType))
1281 : {
1282 13 : const OGRCurve *poCurve = poSubGeom->toCurve();
1283 13 : dfLength += poCurve->get_Length();
1284 : }
1285 4 : else if (OGR_GT_IsSurface(eType))
1286 : {
1287 1 : const OGRSurface *poSurface = poSubGeom->toSurface();
1288 1 : dfLength += poSurface->get_Length();
1289 : }
1290 3 : else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
1291 : {
1292 : const OGRGeometryCollection *poColl =
1293 2 : poSubGeom->toGeometryCollection();
1294 2 : dfLength += poColl->get_Length();
1295 : }
1296 : }
1297 :
1298 9 : return dfLength;
1299 : }
1300 :
1301 : /************************************************************************/
1302 : /* get_Area() */
1303 : /************************************************************************/
1304 :
1305 : /**
1306 : * \brief Compute area of geometry collection.
1307 : *
1308 : * The area is computed as the sum of the areas of all members
1309 : * in this collection.
1310 : *
1311 : * @note No warning will be issued if a member of the collection does not
1312 : * support the get_Area method.
1313 : *
1314 : * @return computed area.
1315 : */
1316 :
1317 14 : double OGRGeometryCollection::get_Area() const
1318 : {
1319 14 : double dfArea = 0.0;
1320 46 : for (const auto &poSubGeom : *this)
1321 : {
1322 32 : OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
1323 32 : if (OGR_GT_IsSurface(eType))
1324 : {
1325 27 : const OGRSurface *poSurface = poSubGeom->toSurface();
1326 27 : dfArea += poSurface->get_Area();
1327 : }
1328 5 : else if (OGR_GT_IsCurve(eType))
1329 : {
1330 1 : const OGRCurve *poCurve = poSubGeom->toCurve();
1331 1 : dfArea += poCurve->get_Area();
1332 : }
1333 4 : else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
1334 : eType == wkbGeometryCollection)
1335 : {
1336 2 : dfArea += poSubGeom->toGeometryCollection()->get_Area();
1337 : }
1338 : }
1339 :
1340 14 : return dfArea;
1341 : }
1342 :
1343 : /************************************************************************/
1344 : /* get_GeodesicArea() */
1345 : /************************************************************************/
1346 :
1347 : /**
1348 : * \brief Compute area of geometry collection, considered as a surface on
1349 : * the underlying ellipsoid of the SRS attached to the geometry.
1350 : *
1351 : * The returned area will always be in square meters, and assumes that
1352 : * polygon edges describe geodesic lines on the ellipsoid.
1353 : *
1354 : * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
1355 : * follow the shortest route on the surface of the ellipsoid.
1356 : *
1357 : * If the geometry' SRS is not a geographic one, geometries are reprojected to
1358 : * the underlying geographic SRS of the geometry' SRS.
1359 : * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
1360 : *
1361 : * The area is computed as the sum of the areas of all members
1362 : * in this collection.
1363 : *
1364 : * @note No warning will be issued if a member of the collection does not
1365 : * support the get_GeodesicArea method.
1366 : *
1367 : * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
1368 : * @return the area of the geometry in square meters, or a negative value in case
1369 : * of error.
1370 : *
1371 : * @see get_Area() for an alternative method returning areas computed in
1372 : * 2D Cartesian space.
1373 : *
1374 : * @since GDAL 3.9
1375 : */
1376 6 : double OGRGeometryCollection::get_GeodesicArea(
1377 : const OGRSpatialReference *poSRSOverride) const
1378 : {
1379 6 : double dfArea = 0.0;
1380 11 : for (const auto &poSubGeom : *this)
1381 : {
1382 8 : OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
1383 8 : if (OGR_GT_IsSurface(eType))
1384 : {
1385 4 : const OGRSurface *poSurface = poSubGeom->toSurface();
1386 : const double dfLocalArea =
1387 4 : poSurface->get_GeodesicArea(poSRSOverride);
1388 4 : if (dfLocalArea < 0)
1389 1 : return dfLocalArea;
1390 3 : dfArea += dfLocalArea;
1391 : }
1392 4 : else if (OGR_GT_IsCurve(eType))
1393 : {
1394 2 : const OGRCurve *poCurve = poSubGeom->toCurve();
1395 2 : const double dfLocalArea = poCurve->get_GeodesicArea(poSRSOverride);
1396 2 : if (dfLocalArea < 0)
1397 1 : return dfLocalArea;
1398 1 : dfArea += dfLocalArea;
1399 : }
1400 2 : else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
1401 : {
1402 : const double dfLocalArea =
1403 2 : poSubGeom->toGeometryCollection()->get_GeodesicArea(
1404 2 : poSRSOverride);
1405 2 : if (dfLocalArea < 0)
1406 1 : return dfLocalArea;
1407 1 : dfArea += dfLocalArea;
1408 : }
1409 : }
1410 :
1411 3 : return dfArea;
1412 : }
1413 :
1414 : /************************************************************************/
1415 : /* get_GeodesicLength() */
1416 : /************************************************************************/
1417 :
1418 : /**
1419 : * \brief Get the length of the collection,where curve edges are geodesic lines
1420 : * on the underlying ellipsoid of the SRS attached to the geometry.
1421 : *
1422 : * The returned length will always be in meters.
1423 : *
1424 : * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
1425 : * follow the shortest route on the surface of the ellipsoid.
1426 : *
1427 : * If the geometry' SRS is not a geographic one, geometries are reprojected to
1428 : * the underlying geographic SRS of the geometry' SRS.
1429 : * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
1430 : *
1431 : * Note that geometries with circular arcs will be linearized in their original
1432 : * coordinate space first, so the resulting geodesic length will be an
1433 : * approximation.
1434 : *
1435 : * The length is computed as the sum of the lengths of all members
1436 : * in this collection.
1437 : *
1438 : * @note No warning will be issued if a member of the collection does not
1439 : * support the get_GeodesicLength method.
1440 : *
1441 : * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
1442 : * @return the length of the geometry in meters, or a negative value in case
1443 : * of error.
1444 : *
1445 : * @see get_Length() for an alternative method returning areas computed in
1446 : * 2D Cartesian space.
1447 : *
1448 : * @since GDAL 3.10
1449 : */
1450 5 : double OGRGeometryCollection::get_GeodesicLength(
1451 : const OGRSpatialReference *poSRSOverride) const
1452 : {
1453 5 : double dfLength = 0.0;
1454 10 : for (const auto &poSubGeom : *this)
1455 : {
1456 : const OGRwkbGeometryType eType =
1457 7 : wkbFlatten(poSubGeom->getGeometryType());
1458 7 : if (OGR_GT_IsSurface(eType))
1459 : {
1460 4 : const OGRSurface *poSurface = poSubGeom->toSurface();
1461 : const double dfLocalLength =
1462 4 : poSurface->get_GeodesicLength(poSRSOverride);
1463 4 : if (dfLocalLength < 0)
1464 1 : return dfLocalLength;
1465 3 : dfLength += dfLocalLength;
1466 : }
1467 3 : else if (OGR_GT_IsCurve(eType))
1468 : {
1469 1 : const OGRCurve *poCurve = poSubGeom->toCurve();
1470 : const double dfLocalLength =
1471 1 : poCurve->get_GeodesicLength(poSRSOverride);
1472 1 : if (dfLocalLength < 0)
1473 0 : return dfLocalLength;
1474 1 : dfLength += dfLocalLength;
1475 : }
1476 2 : else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
1477 : {
1478 : const double dfLocalLength =
1479 2 : poSubGeom->toGeometryCollection()->get_GeodesicLength(
1480 2 : poSRSOverride);
1481 2 : if (dfLocalLength < 0)
1482 1 : return dfLocalLength;
1483 1 : dfLength += dfLocalLength;
1484 : }
1485 : }
1486 :
1487 3 : return dfLength;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* IsEmpty() */
1492 : /************************************************************************/
1493 :
1494 33613 : OGRBoolean OGRGeometryCollection::IsEmpty() const
1495 : {
1496 33752 : for (const auto &poSubGeom : *this)
1497 : {
1498 32350 : if (poSubGeom->IsEmpty() == FALSE)
1499 32211 : return FALSE;
1500 : }
1501 1402 : return TRUE;
1502 : }
1503 :
1504 : /************************************************************************/
1505 : /* assignSpatialReference() */
1506 : /************************************************************************/
1507 :
1508 120280 : void OGRGeometryCollection::assignSpatialReference(
1509 : const OGRSpatialReference *poSR)
1510 : {
1511 120280 : OGRGeometry::assignSpatialReference(poSR);
1512 1550120 : for (auto &poSubGeom : *this)
1513 : {
1514 1429840 : poSubGeom->assignSpatialReference(poSR);
1515 : }
1516 120280 : }
1517 :
1518 : /************************************************************************/
1519 : /* OGRGeometryCollection::segmentize() */
1520 : /************************************************************************/
1521 :
1522 13 : bool OGRGeometryCollection::segmentize(double dfMaxLength)
1523 : {
1524 26 : for (auto &poSubGeom : *this)
1525 : {
1526 13 : if (!poSubGeom->segmentize(dfMaxLength))
1527 0 : return false;
1528 : }
1529 13 : return true;
1530 : }
1531 :
1532 : /************************************************************************/
1533 : /* swapXY() */
1534 : /************************************************************************/
1535 :
1536 30 : void OGRGeometryCollection::swapXY()
1537 : {
1538 76 : for (auto &poSubGeom : *this)
1539 : {
1540 46 : poSubGeom->swapXY();
1541 : }
1542 30 : }
1543 :
1544 : /************************************************************************/
1545 : /* isCompatibleSubType() */
1546 : /************************************************************************/
1547 :
1548 : /** Returns whether a geometry of the specified geometry type can be a
1549 : * member of this collection.
1550 : *
1551 : * @param eSubType type of the potential member
1552 : * @return TRUE or FALSE
1553 : */
1554 :
1555 39075 : OGRBoolean OGRGeometryCollection::isCompatibleSubType(
1556 : CPL_UNUSED OGRwkbGeometryType eSubType) const
1557 : {
1558 : // Accept all geometries as sub-geometries.
1559 39075 : return TRUE;
1560 : }
1561 :
1562 : /************************************************************************/
1563 : /* hasCurveGeometry() */
1564 : /************************************************************************/
1565 :
1566 2597 : OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const
1567 : {
1568 6626 : for (const auto &poSubGeom : *this)
1569 : {
1570 4112 : if (poSubGeom->hasCurveGeometry(bLookForNonLinear))
1571 83 : return TRUE;
1572 : }
1573 2514 : return FALSE;
1574 : }
1575 :
1576 : /************************************************************************/
1577 : /* getLinearGeometry() */
1578 : /************************************************************************/
1579 :
1580 : OGRGeometry *
1581 332 : OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
1582 : const char *const *papszOptions) const
1583 : {
1584 : auto poGC = std::unique_ptr<OGRGeometryCollection>(
1585 332 : OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType()))
1586 664 : ->toGeometryCollection());
1587 332 : if (!poGC)
1588 0 : return nullptr;
1589 332 : poGC->assignSpatialReference(getSpatialReference());
1590 706 : for (const auto &poSubGeom : *this)
1591 : {
1592 748 : OGRGeometry *poSubGeomNew = poSubGeom->getLinearGeometry(
1593 374 : dfMaxAngleStepSizeDegrees, papszOptions);
1594 374 : if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
1595 0 : return nullptr;
1596 : }
1597 332 : return poGC.release();
1598 : }
1599 :
1600 : /************************************************************************/
1601 : /* getCurveGeometry() */
1602 : /************************************************************************/
1603 :
1604 : OGRGeometry *
1605 15 : OGRGeometryCollection::getCurveGeometry(const char *const *papszOptions) const
1606 : {
1607 : auto poGC = std::unique_ptr<OGRGeometryCollection>(
1608 15 : OGRGeometryFactory::createGeometry(OGR_GT_GetCurve(getGeometryType()))
1609 30 : ->toGeometryCollection());
1610 15 : if (!poGC)
1611 0 : return nullptr;
1612 15 : poGC->assignSpatialReference(getSpatialReference());
1613 15 : bool bHasCurveGeometry = false;
1614 162 : for (const auto &poSubGeom : *this)
1615 : {
1616 147 : OGRGeometry *poSubGeomNew = poSubGeom->getCurveGeometry(papszOptions);
1617 147 : if (poSubGeomNew->hasCurveGeometry())
1618 7 : bHasCurveGeometry = true;
1619 147 : if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
1620 0 : return nullptr;
1621 : }
1622 15 : if (!bHasCurveGeometry)
1623 : {
1624 10 : return clone();
1625 : }
1626 5 : return poGC.release();
1627 : }
1628 :
1629 : /************************************************************************/
1630 : /* TransferMembersAndDestroy() */
1631 : /************************************************************************/
1632 :
1633 : //! @cond Doxygen_Suppress
1634 : OGRGeometryCollection *
1635 1050 : OGRGeometryCollection::TransferMembersAndDestroy(OGRGeometryCollection *poSrc,
1636 : OGRGeometryCollection *poDst)
1637 : {
1638 1050 : poDst->assignSpatialReference(poSrc->getSpatialReference());
1639 1050 : poDst->set3D(poSrc->Is3D());
1640 1050 : poDst->setMeasured(poSrc->IsMeasured());
1641 1050 : poDst->nGeomCount = poSrc->nGeomCount;
1642 1050 : poDst->papoGeoms = poSrc->papoGeoms;
1643 1050 : poSrc->nGeomCount = 0;
1644 1050 : poSrc->papoGeoms = nullptr;
1645 1050 : delete poSrc;
1646 1050 : return poDst;
1647 : }
1648 :
1649 : //! @endcond
1650 :
1651 : /************************************************************************/
1652 : /* CastToGeometryCollection() */
1653 : /************************************************************************/
1654 :
1655 : /**
1656 : * \brief Cast to geometry collection.
1657 : *
1658 : * This methods cast a derived class of geometry collection to a plain
1659 : * geometry collection.
1660 : *
1661 : * The passed in geometry is consumed and a new one returned (or NULL in case
1662 : * of failure).
1663 : *
1664 : * @param poSrc the input geometry - ownership is passed to the method.
1665 : * @return new geometry.
1666 : */
1667 :
1668 : OGRGeometryCollection *
1669 921 : OGRGeometryCollection::CastToGeometryCollection(OGRGeometryCollection *poSrc)
1670 : {
1671 921 : if (wkbFlatten(poSrc->getGeometryType()) == wkbGeometryCollection)
1672 0 : return poSrc;
1673 921 : return TransferMembersAndDestroy(poSrc, new OGRGeometryCollection());
1674 : }
|