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 5491 : OGRGeometryCollection::OGRGeometryCollection(const OGRGeometryCollection &other)
40 5491 : : OGRGeometry(other)
41 : {
42 : // Do not use addGeometry() as it is virtual.
43 5491 : papoGeoms = static_cast<OGRGeometry **>(
44 5491 : VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), other.nGeomCount));
45 5491 : if (papoGeoms)
46 : {
47 5491 : nGeomCount = other.nGeomCount;
48 14426 : for (int i = 0; i < other.nGeomCount; i++)
49 : {
50 8935 : papoGeoms[i] = other.papoGeoms[i]->clone();
51 : }
52 : }
53 5491 : }
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 131466 : OGRGeometryCollection::~OGRGeometryCollection()
81 :
82 : {
83 125660 : OGRGeometryCollection::empty();
84 131466 : }
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 137727 : void OGRGeometryCollection::empty()
155 :
156 : {
157 137727 : if (papoGeoms != nullptr)
158 : {
159 1679100 : for (auto &poSubGeom : *this)
160 : {
161 1560790 : delete poSubGeom;
162 : }
163 118302 : CPLFree(papoGeoms);
164 : }
165 :
166 137727 : nGeomCount = 0;
167 137727 : papoGeoms = nullptr;
168 137727 : }
169 :
170 : /************************************************************************/
171 : /* clone() */
172 : /************************************************************************/
173 :
174 555 : OGRGeometryCollection *OGRGeometryCollection::clone() const
175 :
176 : {
177 555 : auto ret = new (std::nothrow) OGRGeometryCollection(*this);
178 555 : if (ret)
179 : {
180 555 : if (ret->WkbSize() != WkbSize())
181 : {
182 0 : delete ret;
183 0 : ret = nullptr;
184 : }
185 : }
186 555 : return ret;
187 : }
188 :
189 : /************************************************************************/
190 : /* getGeometryType() */
191 : /************************************************************************/
192 :
193 10174 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
194 :
195 : {
196 10174 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
197 2969 : return wkbGeometryCollectionZM;
198 7205 : else if (flags & OGR_G_MEASURED)
199 248 : return wkbGeometryCollectionM;
200 6957 : else if (flags & OGR_G_3D)
201 3827 : return wkbGeometryCollection25D;
202 : else
203 3130 : 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 1064 : const char *OGRGeometryCollection::getGeometryName() const
250 :
251 : {
252 1064 : 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 123284 : int OGRGeometryCollection::getNumGeometries() const
269 :
270 : {
271 123284 : 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 31639 : OGRGeometry *OGRGeometryCollection::getGeometryRef(int i)
294 :
295 : {
296 31639 : if (i < 0 || i >= nGeomCount)
297 0 : return nullptr;
298 :
299 31639 : 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 178613 : const OGRGeometry *OGRGeometryCollection::getGeometryRef(int i) const
318 :
319 : {
320 178613 : if (i < 0 || i >= nGeomCount)
321 0 : return nullptr;
322 :
323 178613 : 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 188083 : OGRErr OGRGeometryCollection::addGeometryDirectly(OGRGeometry *poNewGeom)
393 :
394 : {
395 188083 : if (!isCompatibleSubType(poNewGeom->getGeometryType()))
396 6 : 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 188077 : 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 188077 : HomogenizeDimensionalityWith(poNewGeom);
414 :
415 : OGRGeometry **papoNewGeoms =
416 188077 : static_cast<OGRGeometry **>(VSI_REALLOC_VERBOSE(
417 : papoGeoms, sizeof(OGRGeometry *) * (nGeomCount + 1)));
418 188077 : if (papoNewGeoms == nullptr)
419 0 : return OGRERR_FAILURE;
420 :
421 188077 : papoGeoms = papoNewGeoms;
422 188077 : papoGeoms[nGeomCount] = poNewGeom;
423 :
424 188077 : nGeomCount++;
425 :
426 188077 : 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 102806 : OGRErr OGRGeometryCollection::addGeometry(std::unique_ptr<OGRGeometry> geom)
448 : {
449 102806 : OGRGeometry *poGeom = geom.release();
450 102806 : OGRErr eErr = addGeometryDirectly(poGeom);
451 102806 : if (eErr != OGRERR_NONE)
452 2 : delete poGeom;
453 102806 : 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 22352 : size_t OGRGeometryCollection::WkbSize() const
629 :
630 : {
631 22352 : size_t nSize = 9;
632 :
633 106325 : for (const auto &poGeom : *this)
634 : {
635 83973 : nSize += poGeom->WkbSize();
636 : }
637 :
638 22352 : return nSize;
639 : }
640 :
641 : /************************************************************************/
642 : /* importFromWkbInternal() */
643 : /************************************************************************/
644 :
645 : //! @cond Doxygen_Suppress
646 8811 : OGRErr OGRGeometryCollection::importFromWkbInternal(
647 : const unsigned char *pabyData, size_t nSize, int nRecLevel,
648 : OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
649 :
650 : {
651 8811 : nBytesConsumedOut = 0;
652 : // Arbitrary value, but certainly large enough for reasonable use cases.
653 8811 : 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 8810 : OGRwkbByteOrder eByteOrder = wkbXDR;
662 8810 : size_t nDataOffset = 0;
663 8810 : int nGeomCountNew = 0;
664 8810 : OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
665 : nDataOffset, eByteOrder, 9,
666 : nGeomCountNew, eWkbVariant);
667 :
668 8810 : if (eErr != OGRERR_NONE)
669 324 : return eErr;
670 :
671 8486 : CPLAssert(nGeomCount == 0);
672 8486 : nGeomCount = nGeomCountNew;
673 :
674 : // coverity[tainted_data]
675 8486 : papoGeoms = static_cast<OGRGeometry **>(
676 8486 : VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), nGeomCount));
677 8486 : 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 19664 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
687 : {
688 : // Parses sub-geometry.
689 12069 : const unsigned char *pabySubData = pabyData + nDataOffset;
690 12069 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
691 891 : return OGRERR_NOT_ENOUGH_DATA;
692 :
693 12057 : OGRwkbGeometryType eSubGeomType = wkbUnknown;
694 12057 : eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
695 12057 : if (eErr != OGRERR_NONE)
696 372 : return eErr;
697 :
698 11685 : 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 11684 : OGRGeometry *poSubGeom = nullptr;
709 11684 : size_t nSubGeomBytesConsumed = 0;
710 11684 : if (OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection))
711 : {
712 161 : poSubGeom = OGRGeometryFactory::createGeometry(eSubGeomType);
713 161 : if (poSubGeom == nullptr)
714 0 : eErr = OGRERR_FAILURE;
715 : else
716 161 : eErr = poSubGeom->toGeometryCollection()->importFromWkbInternal(
717 : pabySubData, nSize, nRecLevel + 1, eWkbVariant,
718 : nSubGeomBytesConsumed);
719 : }
720 : else
721 : {
722 11523 : eErr = OGRGeometryFactory::createFromWkb(
723 : pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
724 : nSubGeomBytesConsumed);
725 :
726 11523 : if (eErr == OGRERR_NONE)
727 : {
728 : // if this is a Z or M geom make sure the sub geoms are as well
729 11049 : if (Is3D() && !poSubGeom->Is3D())
730 : {
731 0 : CPLDebug("OGR", "Promoting sub-geometry to 3D");
732 0 : poSubGeom->set3D(TRUE);
733 : }
734 :
735 11049 : if (IsMeasured() && !poSubGeom->IsMeasured())
736 : {
737 0 : CPLDebug("OGR", "Promoting sub-geometry to Measured");
738 0 : poSubGeom->setMeasured(TRUE);
739 : }
740 : }
741 : }
742 :
743 11684 : if (eErr != OGRERR_NONE)
744 : {
745 506 : nGeomCount = iGeom;
746 506 : delete poSubGeom;
747 506 : return eErr;
748 : }
749 :
750 11178 : papoGeoms[iGeom] = poSubGeom;
751 :
752 11178 : if (papoGeoms[iGeom]->Is3D())
753 5780 : flags |= OGR_G_3D;
754 11178 : if (papoGeoms[iGeom]->IsMeasured())
755 5678 : flags |= OGR_G_MEASURED;
756 :
757 11178 : CPLAssert(nSubGeomBytesConsumed > 0);
758 11178 : if (nSize != static_cast<size_t>(-1))
759 : {
760 11173 : CPLAssert(nSize >= nSubGeomBytesConsumed);
761 11173 : nSize -= nSubGeomBytesConsumed;
762 : }
763 :
764 11178 : nDataOffset += nSubGeomBytesConsumed;
765 : }
766 7595 : nBytesConsumedOut = nDataOffset;
767 :
768 7595 : 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 5007 : OGRErr OGRGeometryCollection::importFromWkb(const unsigned char *pabyData,
781 : size_t nSize,
782 : OGRwkbVariant eWkbVariant,
783 : size_t &nBytesConsumedOut)
784 :
785 : {
786 5007 : return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant,
787 5007 : nBytesConsumedOut);
788 : }
789 :
790 : /************************************************************************/
791 : /* exportToWkb() */
792 : /* */
793 : /* Build a well known binary representation of this object. */
794 : /************************************************************************/
795 :
796 : OGRErr
797 12419 : OGRGeometryCollection::exportToWkb(unsigned char *pabyData,
798 : const OGRwkbExportOptions *psOptions) const
799 :
800 : {
801 12419 : if (psOptions == nullptr)
802 : {
803 : static const OGRwkbExportOptions defaultOptions;
804 0 : psOptions = &defaultOptions;
805 : }
806 :
807 12419 : OGRwkbExportOptions sOptions(*psOptions);
808 :
809 12558 : 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 12419 : 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 12419 : GUInt32 nGType = getGeometryType();
828 :
829 12419 : if (sOptions.eWkbVariant == wkbVariantIso)
830 12295 : 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 12419 : if (OGR_SWAP(sOptions.eByteOrder))
846 : {
847 7 : nGType = CPL_SWAP32(nGType);
848 : }
849 :
850 12419 : memcpy(pabyData + 1, &nGType, 4);
851 :
852 : /* -------------------------------------------------------------------- */
853 : /* Copy in the raw data. */
854 : /* -------------------------------------------------------------------- */
855 12419 : 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 12412 : memcpy(pabyData + 5, &nGeomCount, 4);
863 : }
864 :
865 12419 : size_t nOffset = 9;
866 :
867 : /* ==================================================================== */
868 : /* Serialize each of the Geoms. */
869 : /* ==================================================================== */
870 12419 : int iGeom = 0;
871 81332 : for (auto &&poSubGeom : *this)
872 : {
873 68913 : poSubGeom->exportToWkb(pabyData + nOffset, &sOptions);
874 : // Should normally not happen if everyone else does its job,
875 : // but has happened sometimes. (#6332)
876 68913 : 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 68913 : nOffset += poSubGeom->WkbSize();
886 68913 : iGeom++;
887 : }
888 :
889 12419 : return OGRERR_NONE;
890 : }
891 :
892 : /************************************************************************/
893 : /* importFromWktInternal() */
894 : /************************************************************************/
895 :
896 640 : OGRErr OGRGeometryCollection::importFromWktInternal(const char **ppszInput,
897 : int nRecLevel)
898 :
899 : {
900 : // Arbitrary value, but certainly large enough for reasonable usages.
901 640 : 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 639 : int bHasZ = FALSE;
910 639 : int bHasM = FALSE;
911 639 : bool bIsEmpty = false;
912 639 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
913 639 : if (eErr != OGRERR_NONE)
914 4 : return eErr;
915 635 : if (bHasZ)
916 97 : flags |= OGR_G_3D;
917 635 : if (bHasM)
918 61 : flags |= OGR_G_MEASURED;
919 635 : if (bIsEmpty)
920 45 : return OGRERR_NONE;
921 :
922 590 : char szToken[OGR_WKT_TOKEN_MAX] = {};
923 590 : const char *pszInput = *ppszInput;
924 :
925 : // Skip first '('.
926 590 : pszInput = OGRWktReadToken(pszInput, szToken);
927 :
928 : /* ==================================================================== */
929 : /* Read each subgeometry in turn. */
930 : /* ==================================================================== */
931 650 : do
932 : {
933 1240 : OGRGeometry *poGeom = nullptr;
934 :
935 : /* --------------------------------------------------------------------
936 : */
937 : /* Get the first token, which should be the geometry type. */
938 : /* --------------------------------------------------------------------
939 : */
940 1240 : OGRWktReadToken(pszInput, szToken);
941 :
942 : /* --------------------------------------------------------------------
943 : */
944 : /* Do the import. */
945 : /* --------------------------------------------------------------------
946 : */
947 1240 : 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 1130 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
956 :
957 1240 : 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 1196 : if (!Is3D() && IsMeasured() && !poGeom->IsMeasured())
962 0 : eErr = OGRERR_CORRUPT_DATA;
963 : else
964 1196 : eErr = addGeometryDirectly(poGeom);
965 : }
966 1240 : 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 1196 : pszInput = OGRWktReadToken(pszInput, szToken);
979 1196 : } while (szToken[0] == ',');
980 :
981 : /* -------------------------------------------------------------------- */
982 : /* freak if we don't get a closing bracket. */
983 : /* -------------------------------------------------------------------- */
984 546 : if (szToken[0] != ')')
985 2 : return OGRERR_CORRUPT_DATA;
986 :
987 544 : *ppszInput = pszInput;
988 :
989 544 : return OGRERR_NONE;
990 : }
991 :
992 : /************************************************************************/
993 : /* importFromWkt() */
994 : /************************************************************************/
995 :
996 530 : OGRErr OGRGeometryCollection::importFromWkt(const char **ppszInput)
997 :
998 : {
999 530 : 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 1246 : std::string OGRGeometryCollection::exportToWktInternal(
1017 : const OGRWktOptions &opts, OGRErr *err, const std::string &exclude) const
1018 : {
1019 1246 : bool first = true;
1020 1246 : const size_t excludeSize = exclude.size();
1021 2492 : std::string wkt(getGeometryName());
1022 1246 : wkt += wktTypeString(opts.variant);
1023 :
1024 : try
1025 : {
1026 2954 : for (const auto &poSubGeom : *this)
1027 : {
1028 1708 : OGRErr subgeomErr = OGRERR_NONE;
1029 1708 : std::string tempWkt = poSubGeom->exportToWkt(opts, &subgeomErr);
1030 1708 : 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 1708 : if (excludeSize && (tempWkt.compare(0, excludeSize, exclude) == 0))
1040 : {
1041 1130 : auto pos = tempWkt.find('(');
1042 : // We won't have an opening paren if the geom is empty.
1043 1130 : if (pos == std::string::npos)
1044 31 : continue;
1045 1099 : 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 1677 : if (opts.variant != wkbVariantIso)
1052 : {
1053 : std::string::size_type pos;
1054 654 : if ((pos = tempWkt.find(" Z ")) != std::string::npos)
1055 11 : tempWkt.erase(pos + 1, 2);
1056 643 : else if ((pos = tempWkt.find(" M ")) != std::string::npos)
1057 0 : tempWkt.erase(pos + 1, 2);
1058 643 : else if ((pos = tempWkt.find(" ZM ")) != std::string::npos)
1059 0 : tempWkt.erase(pos + 1, 3);
1060 : }
1061 :
1062 1677 : if (first)
1063 1086 : wkt += '(';
1064 : else
1065 591 : wkt += ',';
1066 1677 : first = false;
1067 1677 : wkt += tempWkt;
1068 : }
1069 :
1070 1246 : if (err)
1071 1129 : *err = OGRERR_NONE;
1072 1246 : if (first)
1073 160 : wkt += "EMPTY";
1074 : else
1075 1086 : wkt += ')';
1076 1246 : 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 16076 : void OGRGeometryCollection::getEnvelope(OGREnvelope *psEnvelope) const
1094 :
1095 : {
1096 16076 : OGREnvelope3D oEnv3D;
1097 16076 : getEnvelope(&oEnv3D);
1098 16076 : psEnvelope->MinX = oEnv3D.MinX;
1099 16076 : psEnvelope->MinY = oEnv3D.MinY;
1100 16076 : psEnvelope->MaxX = oEnv3D.MaxX;
1101 16076 : psEnvelope->MaxY = oEnv3D.MaxY;
1102 16076 : }
1103 :
1104 : /************************************************************************/
1105 : /* getEnvelope() */
1106 : /************************************************************************/
1107 :
1108 16912 : void OGRGeometryCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
1109 :
1110 : {
1111 16912 : OGREnvelope3D oGeomEnv;
1112 16912 : bool bExtentSet = false;
1113 :
1114 16912 : *psEnvelope = OGREnvelope3D();
1115 403046 : for (const auto &poSubGeom : *this)
1116 : {
1117 386134 : if (!poSubGeom->IsEmpty())
1118 : {
1119 386126 : bExtentSet = true;
1120 386126 : poSubGeom->getEnvelope(&oGeomEnv);
1121 386126 : psEnvelope->Merge(oGeomEnv);
1122 : }
1123 : }
1124 :
1125 16912 : 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 16912 : }
1136 :
1137 : /************************************************************************/
1138 : /* Equals() */
1139 : /************************************************************************/
1140 :
1141 3880 : OGRBoolean OGRGeometryCollection::Equals(const OGRGeometry *poOther) const
1142 :
1143 : {
1144 3880 : if (poOther == this)
1145 2 : return TRUE;
1146 :
1147 3878 : if (poOther->getGeometryType() != getGeometryType())
1148 0 : return FALSE;
1149 :
1150 3878 : if (IsEmpty() && poOther->IsEmpty())
1151 12 : return TRUE;
1152 :
1153 3866 : auto poOGC = poOther->toGeometryCollection();
1154 3866 : if (getNumGeometries() != poOGC->getNumGeometries())
1155 2 : return FALSE;
1156 :
1157 : // TODO(schwehr): Should test the SRS.
1158 :
1159 8504 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
1160 : {
1161 4641 : if (!getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)))
1162 1 : return FALSE;
1163 : }
1164 :
1165 3863 : 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 88874 : bool OGRGeometryCollection::set3D(OGRBoolean bIs3D)
1236 : {
1237 195579 : for (auto &poSubGeom : *this)
1238 : {
1239 106705 : if (!poSubGeom->set3D(bIs3D))
1240 0 : return false;
1241 : }
1242 :
1243 88874 : return OGRGeometry::set3D(bIs3D);
1244 : }
1245 :
1246 46301 : bool OGRGeometryCollection::setMeasured(OGRBoolean bIsMeasured)
1247 : {
1248 153577 : for (auto &poSubGeom : *this)
1249 : {
1250 107276 : if (!poSubGeom->setMeasured(bIsMeasured))
1251 0 : return false;
1252 : }
1253 :
1254 46301 : 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 13 : double OGRGeometryCollection::get_Area() const
1318 : {
1319 13 : double dfArea = 0.0;
1320 44 : for (const auto &poSubGeom : *this)
1321 : {
1322 31 : OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
1323 31 : if (OGR_GT_IsSurface(eType))
1324 : {
1325 26 : const OGRSurface *poSurface = poSubGeom->toSurface();
1326 26 : 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 13 : 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 33505 : OGRBoolean OGRGeometryCollection::IsEmpty() const
1495 : {
1496 33644 : for (const auto &poSubGeom : *this)
1497 : {
1498 32242 : if (poSubGeom->IsEmpty() == FALSE)
1499 32103 : return FALSE;
1500 : }
1501 1402 : return TRUE;
1502 : }
1503 :
1504 : /************************************************************************/
1505 : /* assignSpatialReference() */
1506 : /************************************************************************/
1507 :
1508 113529 : void OGRGeometryCollection::assignSpatialReference(
1509 : const OGRSpatialReference *poSR)
1510 : {
1511 113529 : OGRGeometry::assignSpatialReference(poSR);
1512 1525630 : for (auto &poSubGeom : *this)
1513 : {
1514 1412100 : poSubGeom->assignSpatialReference(poSR);
1515 : }
1516 113529 : }
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 9310 : OGRBoolean OGRGeometryCollection::isCompatibleSubType(
1556 : CPL_UNUSED OGRwkbGeometryType eSubType) const
1557 : {
1558 : // Accept all geometries as sub-geometries.
1559 9310 : return TRUE;
1560 : }
1561 :
1562 : /************************************************************************/
1563 : /* hasCurveGeometry() */
1564 : /************************************************************************/
1565 :
1566 2536 : OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const
1567 : {
1568 6445 : for (const auto &poSubGeom : *this)
1569 : {
1570 3992 : if (poSubGeom->hasCurveGeometry(bLookForNonLinear))
1571 83 : return TRUE;
1572 : }
1573 2453 : return FALSE;
1574 : }
1575 :
1576 : /************************************************************************/
1577 : /* getLinearGeometry() */
1578 : /************************************************************************/
1579 :
1580 : OGRGeometry *
1581 331 : OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
1582 : const char *const *papszOptions) const
1583 : {
1584 : auto poGC = std::unique_ptr<OGRGeometryCollection>(
1585 331 : OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType()))
1586 662 : ->toGeometryCollection());
1587 331 : if (!poGC)
1588 0 : return nullptr;
1589 331 : poGC->assignSpatialReference(getSpatialReference());
1590 704 : for (const auto &poSubGeom : *this)
1591 : {
1592 746 : OGRGeometry *poSubGeomNew = poSubGeom->getLinearGeometry(
1593 373 : dfMaxAngleStepSizeDegrees, papszOptions);
1594 373 : if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
1595 0 : return nullptr;
1596 : }
1597 331 : 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 : }
|