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 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "ogr_geometry.h"
32 :
33 : #include <cstddef>
34 : #include <cstring>
35 : #include <new>
36 :
37 : #include "cpl_conv.h"
38 : #include "cpl_error.h"
39 : #include "cpl_string.h"
40 : #include "cpl_vsi.h"
41 : #include "ogr_api.h"
42 : #include "ogr_core.h"
43 : #include "ogr_p.h"
44 : #include "ogr_spatialref.h"
45 :
46 : /************************************************************************/
47 : /* OGRGeometryCollection() */
48 : /************************************************************************/
49 :
50 : /**
51 : * \brief Create an empty geometry collection.
52 : */
53 :
54 : OGRGeometryCollection::OGRGeometryCollection() = default;
55 :
56 : /************************************************************************/
57 : /* OGRGeometryCollection( const OGRGeometryCollection& ) */
58 : /************************************************************************/
59 :
60 : /**
61 : * \brief Copy constructor.
62 : *
63 : * Note: before GDAL 2.1, only the default implementation of the constructor
64 : * existed, which could be unsafe to use.
65 : *
66 : * @since GDAL 2.1
67 : */
68 :
69 5215 : OGRGeometryCollection::OGRGeometryCollection(const OGRGeometryCollection &other)
70 5215 : : OGRGeometry(other)
71 : {
72 : // Do not use addGeometry() as it is virtual.
73 5215 : papoGeoms = static_cast<OGRGeometry **>(
74 5215 : VSI_CALLOC_VERBOSE(sizeof(void *), other.nGeomCount));
75 5215 : if (papoGeoms)
76 : {
77 5215 : nGeomCount = other.nGeomCount;
78 13741 : for (int i = 0; i < other.nGeomCount; i++)
79 : {
80 8526 : papoGeoms[i] = other.papoGeoms[i]->clone();
81 : }
82 : }
83 5215 : }
84 :
85 : /************************************************************************/
86 : /* ~OGRGeometryCollection() */
87 : /************************************************************************/
88 :
89 75388 : OGRGeometryCollection::~OGRGeometryCollection()
90 :
91 : {
92 70509 : OGRGeometryCollection::empty();
93 75388 : }
94 :
95 : /************************************************************************/
96 : /* operator=( const OGRGeometryCollection&) */
97 : /************************************************************************/
98 :
99 : /**
100 : * \brief Assignment operator.
101 : *
102 : * Note: before GDAL 2.1, only the default implementation of the operator
103 : * existed, which could be unsafe to use.
104 : *
105 : * @since GDAL 2.1
106 : */
107 :
108 : OGRGeometryCollection &
109 29 : OGRGeometryCollection::operator=(const OGRGeometryCollection &other)
110 : {
111 29 : if (this != &other)
112 : {
113 28 : empty();
114 :
115 28 : OGRGeometry::operator=(other);
116 :
117 50 : for (int i = 0; i < other.nGeomCount; i++)
118 : {
119 22 : addGeometry(other.papoGeoms[i]);
120 : }
121 : }
122 29 : return *this;
123 : }
124 :
125 : /************************************************************************/
126 : /* empty() */
127 : /************************************************************************/
128 :
129 82209 : void OGRGeometryCollection::empty()
130 :
131 : {
132 82209 : if (papoGeoms != nullptr)
133 : {
134 147034 : for (auto &&poSubGeom : *this)
135 : {
136 79534 : delete poSubGeom;
137 : }
138 67500 : CPLFree(papoGeoms);
139 : }
140 :
141 82209 : nGeomCount = 0;
142 82209 : papoGeoms = nullptr;
143 82209 : }
144 :
145 : /************************************************************************/
146 : /* clone() */
147 : /************************************************************************/
148 :
149 602 : OGRGeometryCollection *OGRGeometryCollection::clone() const
150 :
151 : {
152 602 : return new (std::nothrow) OGRGeometryCollection(*this);
153 : }
154 :
155 : /************************************************************************/
156 : /* getGeometryType() */
157 : /************************************************************************/
158 :
159 8555 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
160 :
161 : {
162 8555 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
163 2967 : return wkbGeometryCollectionZM;
164 5588 : else if (flags & OGR_G_MEASURED)
165 247 : return wkbGeometryCollectionM;
166 5341 : else if (flags & OGR_G_3D)
167 2500 : return wkbGeometryCollection25D;
168 : else
169 2841 : return wkbGeometryCollection;
170 : }
171 :
172 : /************************************************************************/
173 : /* getDimension() */
174 : /************************************************************************/
175 :
176 3 : int OGRGeometryCollection::getDimension() const
177 :
178 : {
179 3 : int nDimension = 0;
180 : // FIXME? Not sure if it is really appropriate to take the max in case
181 : // of geometries of different dimension.
182 7 : for (auto &&poSubGeom : *this)
183 : {
184 5 : int nSubGeomDimension = poSubGeom->getDimension();
185 5 : if (nSubGeomDimension > nDimension)
186 : {
187 3 : nDimension = nSubGeomDimension;
188 3 : if (nDimension == 2)
189 1 : break;
190 : }
191 : }
192 3 : return nDimension;
193 : }
194 :
195 : /************************************************************************/
196 : /* flattenTo2D() */
197 : /************************************************************************/
198 :
199 100 : void OGRGeometryCollection::flattenTo2D()
200 :
201 : {
202 295 : for (auto &&poSubGeom : *this)
203 : {
204 195 : poSubGeom->flattenTo2D();
205 : }
206 :
207 100 : flags &= ~OGR_G_3D;
208 100 : flags &= ~OGR_G_MEASURED;
209 100 : }
210 :
211 : /************************************************************************/
212 : /* getGeometryName() */
213 : /************************************************************************/
214 :
215 1020 : const char *OGRGeometryCollection::getGeometryName() const
216 :
217 : {
218 1020 : return "GEOMETRYCOLLECTION";
219 : }
220 :
221 : /************************************************************************/
222 : /* getNumGeometries() */
223 : /************************************************************************/
224 :
225 : /**
226 : * \brief Fetch number of geometries in container.
227 : *
228 : * This method relates to the SFCOM IGeometryCollect::get_NumGeometries()
229 : * method.
230 : *
231 : * @return count of children geometries. May be zero.
232 : */
233 :
234 119803 : int OGRGeometryCollection::getNumGeometries() const
235 :
236 : {
237 119803 : return nGeomCount;
238 : }
239 :
240 : /************************************************************************/
241 : /* getGeometryRef() */
242 : /************************************************************************/
243 :
244 : /**
245 : * \brief Fetch geometry from container.
246 : *
247 : * This method returns a pointer to a geometry within the container. The
248 : * returned geometry remains owned by the container, and should not be
249 : * modified. The pointer is only valid until the next change to the
250 : * geometry container. Use IGeometry::clone() to make a copy.
251 : *
252 : * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
253 : *
254 : * @param i the index of the geometry to fetch, between 0 and
255 : * getNumGeometries() - 1.
256 : * @return pointer to requested geometry.
257 : */
258 :
259 7496 : OGRGeometry *OGRGeometryCollection::getGeometryRef(int i)
260 :
261 : {
262 7496 : if (i < 0 || i >= nGeomCount)
263 0 : return nullptr;
264 :
265 7496 : return papoGeoms[i];
266 : }
267 :
268 : /**
269 : * \brief Fetch geometry from container.
270 : *
271 : * This method returns a pointer to a geometry within the container. The
272 : * returned geometry remains owned by the container, and should not be
273 : * modified. The pointer is only valid until the next change to the
274 : * geometry container. Use IGeometry::clone() to make a copy.
275 : *
276 : * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
277 : *
278 : * @param i the index of the geometry to fetch, between 0 and
279 : * getNumGeometries() - 1.
280 : * @return pointer to requested geometry.
281 : */
282 :
283 174370 : const OGRGeometry *OGRGeometryCollection::getGeometryRef(int i) const
284 :
285 : {
286 174370 : if (i < 0 || i >= nGeomCount)
287 0 : return nullptr;
288 :
289 174370 : return papoGeoms[i];
290 : }
291 :
292 : /************************************************************************/
293 : /* addGeometry() */
294 : /* */
295 : /* Add a new geometry to a collection. Subclasses should */
296 : /* override this to verify the type of the new geometry, and */
297 : /* then call this method to actually add it. */
298 : /************************************************************************/
299 :
300 : /**
301 : * \brief Add a geometry to the container.
302 : *
303 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
304 : * that can be added, and may return an error. The passed geometry is cloned
305 : * to make an internal copy.
306 : *
307 : * There is no SFCOM analog to this method.
308 : *
309 : * This method is the same as the C function OGR_G_AddGeometry().
310 : *
311 : * @param poNewGeom geometry to add to the container.
312 : *
313 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
314 : * the geometry type is illegal for the type of geometry container.
315 : */
316 :
317 1737 : OGRErr OGRGeometryCollection::addGeometry(const OGRGeometry *poNewGeom)
318 :
319 : {
320 1737 : OGRGeometry *poClone = poNewGeom->clone();
321 1737 : if (poClone == nullptr)
322 0 : return OGRERR_FAILURE;
323 :
324 1737 : const OGRErr eErr = addGeometryDirectly(poClone);
325 1737 : if (eErr != OGRERR_NONE)
326 2 : delete poClone;
327 :
328 1737 : return eErr;
329 : }
330 :
331 : /************************************************************************/
332 : /* addGeometryDirectly() */
333 : /* */
334 : /* Add a new geometry to a collection. Subclasses should */
335 : /* override this to verify the type of the new geometry, and */
336 : /* then call this method to actually add it. */
337 : /************************************************************************/
338 :
339 : /**
340 : * \brief Add a geometry directly to the container.
341 : *
342 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
343 : * that can be added, and may return an error. Ownership of the passed
344 : * geometry is taken by the container rather than cloning as addGeometry()
345 : * does, but only if the method is successful. If the method fails, ownership
346 : * still belongs to the caller.
347 : *
348 : * This method is the same as the C function OGR_G_AddGeometryDirectly().
349 : *
350 : * There is no SFCOM analog to this method.
351 : *
352 : * @param poNewGeom geometry to add to the container.
353 : *
354 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
355 : * the geometry type is illegal for the type of geometry container.
356 : */
357 :
358 51127 : OGRErr OGRGeometryCollection::addGeometryDirectly(OGRGeometry *poNewGeom)
359 :
360 : {
361 51127 : if (!isCompatibleSubType(poNewGeom->getGeometryType()))
362 4 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
363 :
364 51123 : HomogenizeDimensionalityWith(poNewGeom);
365 :
366 : OGRGeometry **papoNewGeoms = static_cast<OGRGeometry **>(
367 51123 : VSI_REALLOC_VERBOSE(papoGeoms, sizeof(void *) * (nGeomCount + 1)));
368 51122 : if (papoNewGeoms == nullptr)
369 0 : return OGRERR_FAILURE;
370 :
371 51122 : papoGeoms = papoNewGeoms;
372 51122 : papoGeoms[nGeomCount] = poNewGeom;
373 :
374 51122 : nGeomCount++;
375 :
376 51122 : return OGRERR_NONE;
377 : }
378 :
379 : /************************************************************************/
380 : /* addGeometry() */
381 : /************************************************************************/
382 :
383 : /**
384 : * \brief Add a geometry directly to the container.
385 : *
386 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
387 : * that can be added, and may return an error.
388 : *
389 : * There is no SFCOM analog to this method.
390 : *
391 : * @param geom geometry to add to the container.
392 : *
393 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
394 : * the geometry type is illegal for the type of geometry container.
395 : */
396 :
397 936 : OGRErr OGRGeometryCollection::addGeometry(std::unique_ptr<OGRGeometry> geom)
398 : {
399 936 : OGRGeometry *poGeom = geom.release();
400 936 : OGRErr eErr = addGeometryDirectly(poGeom);
401 936 : if (eErr != OGRERR_NONE)
402 0 : delete poGeom;
403 936 : return eErr;
404 : }
405 :
406 : /************************************************************************/
407 : /* removeGeometry() */
408 : /************************************************************************/
409 :
410 : /**
411 : * \brief Remove a geometry from the container.
412 : *
413 : * Removing a geometry will cause the geometry count to drop by one, and all
414 : * "higher" geometries will shuffle down one in index.
415 : *
416 : * There is no SFCOM analog to this method.
417 : *
418 : * This method is the same as the C function OGR_G_RemoveGeometry().
419 : *
420 : * @param iGeom the index of the geometry to delete. A value of -1 is a
421 : * special flag meaning that all geometries should be removed.
422 : *
423 : * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
424 : * not. The default is TRUE as the container is considered to own the
425 : * geometries in it.
426 : *
427 : * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
428 : * out of range.
429 : */
430 :
431 19249 : OGRErr OGRGeometryCollection::removeGeometry(int iGeom, int bDelete)
432 :
433 : {
434 19249 : if (iGeom < -1 || iGeom >= nGeomCount)
435 4 : return OGRERR_FAILURE;
436 :
437 : // Special case.
438 19245 : if (iGeom == -1)
439 : {
440 7 : while (nGeomCount > 0)
441 5 : removeGeometry(nGeomCount - 1, bDelete);
442 2 : return OGRERR_NONE;
443 : }
444 :
445 19243 : if (bDelete)
446 63 : delete papoGeoms[iGeom];
447 :
448 19243 : memmove(papoGeoms + iGeom, papoGeoms + iGeom + 1,
449 19243 : sizeof(void *) * (nGeomCount - iGeom - 1));
450 :
451 19243 : nGeomCount--;
452 :
453 19243 : return OGRERR_NONE;
454 : }
455 :
456 : /************************************************************************/
457 : /* WkbSize() */
458 : /* */
459 : /* Return the size of this object in well known binary */
460 : /* representation including the byte order, and type information. */
461 : /************************************************************************/
462 :
463 9368 : size_t OGRGeometryCollection::WkbSize() const
464 :
465 : {
466 9368 : size_t nSize = 9;
467 :
468 22751 : for (const auto &poGeom : *this)
469 : {
470 13383 : nSize += poGeom->WkbSize();
471 : }
472 :
473 9368 : return nSize;
474 : }
475 :
476 : /************************************************************************/
477 : /* importFromWkbInternal() */
478 : /************************************************************************/
479 :
480 : //! @cond Doxygen_Suppress
481 8393 : OGRErr OGRGeometryCollection::importFromWkbInternal(
482 : const unsigned char *pabyData, size_t nSize, int nRecLevel,
483 : OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
484 :
485 : {
486 8393 : nBytesConsumedOut = 0;
487 : // Arbitrary value, but certainly large enough for reasonable use cases.
488 8393 : if (nRecLevel == 32)
489 : {
490 1 : CPLError(CE_Failure, CPLE_AppDefined,
491 : "Too many recursion levels (%d) while parsing WKB geometry.",
492 : nRecLevel);
493 1 : return OGRERR_CORRUPT_DATA;
494 : }
495 :
496 8392 : OGRwkbByteOrder eByteOrder = wkbXDR;
497 8392 : size_t nDataOffset = 0;
498 8392 : int nGeomCountNew = 0;
499 8392 : OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
500 : nDataOffset, eByteOrder, 9,
501 : nGeomCountNew, eWkbVariant);
502 :
503 8392 : if (eErr != OGRERR_NONE)
504 324 : return eErr;
505 :
506 8068 : CPLAssert(nGeomCount == 0);
507 8068 : nGeomCount = nGeomCountNew;
508 :
509 : // coverity[tainted_data]
510 8068 : papoGeoms = static_cast<OGRGeometry **>(
511 8068 : VSI_CALLOC_VERBOSE(sizeof(void *), nGeomCount));
512 8068 : if (nGeomCount != 0 && papoGeoms == nullptr)
513 : {
514 0 : nGeomCount = 0;
515 0 : return OGRERR_NOT_ENOUGH_MEMORY;
516 : }
517 :
518 : /* -------------------------------------------------------------------- */
519 : /* Get the Geoms. */
520 : /* -------------------------------------------------------------------- */
521 17530 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
522 : {
523 : // Parses sub-geometry.
524 10353 : const unsigned char *pabySubData = pabyData + nDataOffset;
525 10353 : if (nSize < 9 && nSize != static_cast<size_t>(-1))
526 891 : return OGRERR_NOT_ENOUGH_DATA;
527 :
528 10341 : OGRwkbGeometryType eSubGeomType = wkbUnknown;
529 10341 : eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
530 10341 : if (eErr != OGRERR_NONE)
531 372 : return eErr;
532 :
533 9969 : if (!isCompatibleSubType(eSubGeomType))
534 : {
535 1 : nGeomCount = iGeom;
536 1 : CPLDebug(
537 : "OGR",
538 : "Cannot add geometry of type (%d) to geometry of type (%d)",
539 1 : eSubGeomType, getGeometryType());
540 1 : return OGRERR_CORRUPT_DATA;
541 : }
542 :
543 9968 : OGRGeometry *poSubGeom = nullptr;
544 9968 : size_t nSubGeomBytesConsumed = 0;
545 9968 : if (OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection))
546 : {
547 161 : poSubGeom = OGRGeometryFactory::createGeometry(eSubGeomType);
548 161 : if (poSubGeom == nullptr)
549 0 : eErr = OGRERR_FAILURE;
550 : else
551 161 : eErr = poSubGeom->toGeometryCollection()->importFromWkbInternal(
552 : pabySubData, nSize, nRecLevel + 1, eWkbVariant,
553 : nSubGeomBytesConsumed);
554 : }
555 : else
556 : {
557 9807 : eErr = OGRGeometryFactory::createFromWkb(
558 : pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
559 : nSubGeomBytesConsumed);
560 :
561 9807 : if (eErr == OGRERR_NONE)
562 : {
563 : // if this is a Z or M geom make sure the sub geoms are as well
564 9333 : if (Is3D() && !poSubGeom->Is3D())
565 : {
566 0 : CPLDebug("OGR", "Promoting sub-geometry to 3D");
567 0 : poSubGeom->set3D(TRUE);
568 : }
569 :
570 9333 : if (IsMeasured() && !poSubGeom->IsMeasured())
571 : {
572 0 : CPLDebug("OGR", "Promoting sub-geometry to Measured");
573 0 : poSubGeom->setMeasured(TRUE);
574 : }
575 : }
576 : }
577 :
578 9968 : if (eErr != OGRERR_NONE)
579 : {
580 506 : nGeomCount = iGeom;
581 506 : delete poSubGeom;
582 506 : return eErr;
583 : }
584 :
585 9462 : papoGeoms[iGeom] = poSubGeom;
586 :
587 9462 : if (papoGeoms[iGeom]->Is3D())
588 5776 : flags |= OGR_G_3D;
589 9462 : if (papoGeoms[iGeom]->IsMeasured())
590 5678 : flags |= OGR_G_MEASURED;
591 :
592 9462 : CPLAssert(nSubGeomBytesConsumed > 0);
593 9462 : if (nSize != static_cast<size_t>(-1))
594 : {
595 9457 : CPLAssert(nSize >= nSubGeomBytesConsumed);
596 9457 : nSize -= nSubGeomBytesConsumed;
597 : }
598 :
599 9462 : nDataOffset += nSubGeomBytesConsumed;
600 : }
601 7177 : nBytesConsumedOut = nDataOffset;
602 :
603 7177 : return OGRERR_NONE;
604 : }
605 :
606 : //! @endcond
607 :
608 : /************************************************************************/
609 : /* importFromWkb() */
610 : /* */
611 : /* Initialize from serialized stream in well known binary */
612 : /* format. */
613 : /************************************************************************/
614 :
615 4894 : OGRErr OGRGeometryCollection::importFromWkb(const unsigned char *pabyData,
616 : size_t nSize,
617 : OGRwkbVariant eWkbVariant,
618 : size_t &nBytesConsumedOut)
619 :
620 : {
621 4894 : return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant,
622 4894 : nBytesConsumedOut);
623 : }
624 :
625 : /************************************************************************/
626 : /* exportToWkb() */
627 : /* */
628 : /* Build a well known binary representation of this object. */
629 : /************************************************************************/
630 :
631 : OGRErr
632 9202 : OGRGeometryCollection::exportToWkb(unsigned char *pabyData,
633 : const OGRwkbExportOptions *psOptions) const
634 :
635 : {
636 9202 : if (psOptions == nullptr)
637 : {
638 : static const OGRwkbExportOptions defaultOptions;
639 0 : psOptions = &defaultOptions;
640 : }
641 :
642 9202 : OGRwkbExportOptions sOptions(*psOptions);
643 :
644 13933 : if (sOptions.eWkbVariant == wkbVariantOldOgc &&
645 4732 : (wkbFlatten(getGeometryType()) == wkbMultiCurve ||
646 4721 : wkbFlatten(getGeometryType()) == wkbMultiSurface))
647 : {
648 : // Does not make sense for new geometries, so patch it.
649 19 : sOptions.eWkbVariant = wkbVariantIso;
650 : }
651 :
652 : /* -------------------------------------------------------------------- */
653 : /* Set the byte order. */
654 : /* -------------------------------------------------------------------- */
655 9201 : pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
656 : static_cast<unsigned char>(sOptions.eByteOrder));
657 :
658 : /* -------------------------------------------------------------------- */
659 : /* Set the geometry feature type, ensuring that 3D flag is */
660 : /* preserved. */
661 : /* -------------------------------------------------------------------- */
662 9201 : GUInt32 nGType = getGeometryType();
663 :
664 9201 : if (sOptions.eWkbVariant == wkbVariantIso)
665 4485 : nGType = getIsoGeometryType();
666 4716 : else if (sOptions.eWkbVariant == wkbVariantPostGIS1)
667 : {
668 4 : const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
669 4 : nGType = wkbFlatten(nGType);
670 4 : if (nGType == wkbMultiCurve)
671 0 : nGType = POSTGIS15_MULTICURVE;
672 4 : else if (nGType == wkbMultiSurface)
673 0 : nGType = POSTGIS15_MULTISURFACE;
674 4 : if (bIs3D)
675 : // Yes, explicitly set wkb25DBit.
676 1 : nGType =
677 1 : static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
678 : }
679 :
680 9201 : if (OGR_SWAP(sOptions.eByteOrder))
681 : {
682 6 : nGType = CPL_SWAP32(nGType);
683 : }
684 :
685 9201 : memcpy(pabyData + 1, &nGType, 4);
686 :
687 : /* -------------------------------------------------------------------- */
688 : /* Copy in the raw data. */
689 : /* -------------------------------------------------------------------- */
690 9201 : if (OGR_SWAP(sOptions.eByteOrder))
691 : {
692 6 : int nCount = CPL_SWAP32(nGeomCount);
693 6 : memcpy(pabyData + 5, &nCount, 4);
694 : }
695 : else
696 : {
697 9195 : memcpy(pabyData + 5, &nGeomCount, 4);
698 : }
699 :
700 9201 : size_t nOffset = 9;
701 :
702 : /* ==================================================================== */
703 : /* Serialize each of the Geoms. */
704 : /* ==================================================================== */
705 9201 : int iGeom = 0;
706 22340 : for (auto &&poSubGeom : *this)
707 : {
708 13139 : poSubGeom->exportToWkb(pabyData + nOffset, &sOptions);
709 : // Should normally not happen if everyone else does its job,
710 : // but has happened sometimes. (#6332)
711 13140 : if (poSubGeom->getCoordinateDimension() != getCoordinateDimension())
712 : {
713 0 : CPLError(CE_Warning, CPLE_AppDefined,
714 : "Sub-geometry %d has coordinate dimension %d, "
715 : "but container has %d",
716 0 : iGeom, poSubGeom->getCoordinateDimension(),
717 0 : getCoordinateDimension());
718 : }
719 :
720 13139 : nOffset += poSubGeom->WkbSize();
721 13139 : iGeom++;
722 : }
723 :
724 9201 : return OGRERR_NONE;
725 : }
726 :
727 : /************************************************************************/
728 : /* importFromWktInternal() */
729 : /************************************************************************/
730 :
731 628 : OGRErr OGRGeometryCollection::importFromWktInternal(const char **ppszInput,
732 : int nRecLevel)
733 :
734 : {
735 : // Arbitrary value, but certainly large enough for reasonable usages.
736 628 : if (nRecLevel == 32)
737 : {
738 1 : CPLError(CE_Failure, CPLE_AppDefined,
739 : "Too many recursion levels (%d) while parsing WKT geometry.",
740 : nRecLevel);
741 1 : return OGRERR_CORRUPT_DATA;
742 : }
743 :
744 627 : int bHasZ = FALSE;
745 627 : int bHasM = FALSE;
746 627 : bool bIsEmpty = false;
747 627 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
748 627 : if (eErr != OGRERR_NONE)
749 4 : return eErr;
750 623 : if (bHasZ)
751 111 : flags |= OGR_G_3D;
752 623 : if (bHasM)
753 57 : flags |= OGR_G_MEASURED;
754 623 : if (bIsEmpty)
755 34 : return OGRERR_NONE;
756 :
757 589 : char szToken[OGR_WKT_TOKEN_MAX] = {};
758 589 : const char *pszInput = *ppszInput;
759 :
760 : // Skip first '('.
761 589 : pszInput = OGRWktReadToken(pszInput, szToken);
762 :
763 : /* ==================================================================== */
764 : /* Read each subgeometry in turn. */
765 : /* ==================================================================== */
766 685 : do
767 : {
768 1274 : OGRGeometry *poGeom = nullptr;
769 :
770 : /* --------------------------------------------------------------------
771 : */
772 : /* Get the first token, which should be the geometry type. */
773 : /* --------------------------------------------------------------------
774 : */
775 1274 : OGRWktReadToken(pszInput, szToken);
776 :
777 : /* --------------------------------------------------------------------
778 : */
779 : /* Do the import. */
780 : /* --------------------------------------------------------------------
781 : */
782 1274 : if (STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION"))
783 : {
784 104 : OGRGeometryCollection *poGC = new OGRGeometryCollection();
785 104 : poGeom = poGC;
786 104 : eErr = poGC->importFromWktInternal(&pszInput, nRecLevel + 1);
787 : }
788 : else
789 : eErr =
790 1170 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
791 :
792 1274 : if (eErr == OGRERR_NONE)
793 : {
794 : // If this has M, but not Z, it is an error if poGeom does
795 : // not have M.
796 1230 : if (!Is3D() && IsMeasured() && !poGeom->IsMeasured())
797 0 : eErr = OGRERR_CORRUPT_DATA;
798 : else
799 1230 : eErr = addGeometryDirectly(poGeom);
800 : }
801 1274 : if (eErr != OGRERR_NONE)
802 : {
803 44 : delete poGeom;
804 44 : return eErr;
805 : }
806 :
807 : /* --------------------------------------------------------------------
808 : */
809 : /* Read the delimiter following the ring. */
810 : /* --------------------------------------------------------------------
811 : */
812 :
813 1230 : pszInput = OGRWktReadToken(pszInput, szToken);
814 1230 : } while (szToken[0] == ',');
815 :
816 : /* -------------------------------------------------------------------- */
817 : /* freak if we don't get a closing bracket. */
818 : /* -------------------------------------------------------------------- */
819 545 : if (szToken[0] != ')')
820 2 : return OGRERR_CORRUPT_DATA;
821 :
822 543 : *ppszInput = pszInput;
823 :
824 543 : return OGRERR_NONE;
825 : }
826 :
827 : /************************************************************************/
828 : /* importFromWkt() */
829 : /************************************************************************/
830 :
831 524 : OGRErr OGRGeometryCollection::importFromWkt(const char **ppszInput)
832 :
833 : {
834 524 : return importFromWktInternal(ppszInput, 0);
835 : }
836 :
837 : /************************************************************************/
838 : /* exportToWkt() */
839 : /* */
840 : /* Translate this structure into its well known text format */
841 : /* equivalent. */
842 : /************************************************************************/
843 :
844 275 : std::string OGRGeometryCollection::exportToWkt(const OGRWktOptions &opts,
845 : OGRErr *err) const
846 : {
847 275 : return exportToWktInternal(opts, err);
848 : }
849 :
850 : //! @cond Doxygen_Suppress
851 1111 : std::string OGRGeometryCollection::exportToWktInternal(
852 : const OGRWktOptions &opts, OGRErr *err, const std::string &exclude) const
853 : {
854 1111 : bool first = true;
855 1111 : const size_t excludeSize = exclude.size();
856 2222 : std::string wkt(getGeometryName());
857 1111 : wkt += wktTypeString(opts.variant);
858 :
859 : try
860 : {
861 2645 : for (int i = 0; i < nGeomCount; ++i)
862 : {
863 1534 : OGRGeometry *geom = papoGeoms[i];
864 1534 : OGRErr subgeomErr = OGRERR_NONE;
865 1534 : std::string tempWkt = geom->exportToWkt(opts, &subgeomErr);
866 1534 : if (subgeomErr != OGRERR_NONE)
867 : {
868 0 : if (err)
869 0 : *err = subgeomErr;
870 0 : return std::string();
871 : }
872 :
873 : // For some strange reason we exclude the typename leader when using
874 : // some geometries as part of a collection.
875 1534 : if (excludeSize && (tempWkt.compare(0, excludeSize, exclude) == 0))
876 : {
877 1023 : auto pos = tempWkt.find('(');
878 : // We won't have an opening paren if the geom is empty.
879 1023 : if (pos == std::string::npos)
880 20 : continue;
881 1003 : tempWkt = tempWkt.substr(pos);
882 : }
883 :
884 : // Also strange, we allow the inclusion of ISO-only geometries (see
885 : // OGRPolyhedralSurface) in a non-iso geometry collection. In order
886 : // to facilitate this, we need to rip the ISO bit from the string.
887 1514 : if (opts.variant != wkbVariantIso)
888 : {
889 : std::string::size_type pos;
890 676 : if ((pos = tempWkt.find(" Z ")) != std::string::npos)
891 11 : tempWkt.erase(pos + 1, 2);
892 665 : else if ((pos = tempWkt.find(" M ")) != std::string::npos)
893 0 : tempWkt.erase(pos + 1, 2);
894 665 : else if ((pos = tempWkt.find(" ZM ")) != std::string::npos)
895 0 : tempWkt.erase(pos + 1, 3);
896 : }
897 :
898 1514 : if (first)
899 984 : wkt += '(';
900 : else
901 530 : wkt += ',';
902 1514 : first = false;
903 1514 : wkt += tempWkt;
904 : }
905 :
906 1111 : if (err)
907 994 : *err = OGRERR_NONE;
908 1111 : if (first)
909 127 : wkt += "EMPTY";
910 : else
911 984 : wkt += ')';
912 1111 : return wkt;
913 : }
914 0 : catch (const std::bad_alloc &e)
915 : {
916 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
917 0 : if (err)
918 0 : *err = OGRERR_FAILURE;
919 0 : return std::string();
920 : }
921 : }
922 :
923 : //! @endcond
924 :
925 : /************************************************************************/
926 : /* getEnvelope() */
927 : /************************************************************************/
928 :
929 6043 : void OGRGeometryCollection::getEnvelope(OGREnvelope *psEnvelope) const
930 :
931 : {
932 6043 : OGREnvelope3D oEnv3D;
933 6043 : getEnvelope(&oEnv3D);
934 6043 : psEnvelope->MinX = oEnv3D.MinX;
935 6043 : psEnvelope->MinY = oEnv3D.MinY;
936 6043 : psEnvelope->MaxX = oEnv3D.MaxX;
937 6043 : psEnvelope->MaxY = oEnv3D.MaxY;
938 6043 : }
939 :
940 : /************************************************************************/
941 : /* getEnvelope() */
942 : /************************************************************************/
943 :
944 7128 : void OGRGeometryCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
945 :
946 : {
947 7128 : OGREnvelope3D oGeomEnv;
948 7128 : bool bExtentSet = false;
949 :
950 7128 : *psEnvelope = OGREnvelope3D();
951 19080 : for (auto &&poSubGeom : *this)
952 : {
953 11952 : if (!poSubGeom->IsEmpty())
954 : {
955 11945 : bExtentSet = true;
956 11945 : poSubGeom->getEnvelope(&oGeomEnv);
957 11945 : psEnvelope->Merge(oGeomEnv);
958 : }
959 : }
960 :
961 7128 : if (!bExtentSet)
962 : {
963 : // To be backward compatible when called on empty geom
964 13 : psEnvelope->MinX = 0.0;
965 13 : psEnvelope->MinY = 0.0;
966 13 : psEnvelope->MinZ = 0.0;
967 13 : psEnvelope->MaxX = 0.0;
968 13 : psEnvelope->MaxY = 0.0;
969 13 : psEnvelope->MaxZ = 0.0;
970 : }
971 7128 : }
972 :
973 : /************************************************************************/
974 : /* Equals() */
975 : /************************************************************************/
976 :
977 3818 : OGRBoolean OGRGeometryCollection::Equals(const OGRGeometry *poOther) const
978 :
979 : {
980 3818 : if (poOther == this)
981 2 : return TRUE;
982 :
983 3816 : if (poOther->getGeometryType() != getGeometryType())
984 0 : return FALSE;
985 :
986 3816 : if (IsEmpty() && poOther->IsEmpty())
987 12 : return TRUE;
988 :
989 3804 : auto poOGC = poOther->toGeometryCollection();
990 3804 : if (getNumGeometries() != poOGC->getNumGeometries())
991 2 : return FALSE;
992 :
993 : // TODO(schwehr): Should test the SRS.
994 :
995 8341 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
996 : {
997 4540 : if (!getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)))
998 1 : return FALSE;
999 : }
1000 :
1001 3801 : return TRUE;
1002 : }
1003 :
1004 : /************************************************************************/
1005 : /* transform() */
1006 : /************************************************************************/
1007 :
1008 148 : OGRErr OGRGeometryCollection::transform(OGRCoordinateTransformation *poCT)
1009 :
1010 : {
1011 148 : int iGeom = 0;
1012 332 : for (auto &&poSubGeom : *this)
1013 : {
1014 184 : const OGRErr eErr = poSubGeom->transform(poCT);
1015 184 : if (eErr != OGRERR_NONE)
1016 : {
1017 0 : if (iGeom != 0)
1018 : {
1019 0 : CPLDebug("OGR",
1020 : "OGRGeometryCollection::transform() failed for a "
1021 : "geometry other than the first, meaning some "
1022 : "geometries are transformed and some are not.");
1023 :
1024 0 : return OGRERR_FAILURE;
1025 : }
1026 :
1027 0 : return eErr;
1028 : }
1029 184 : iGeom++;
1030 : }
1031 :
1032 148 : assignSpatialReference(poCT->GetTargetCS());
1033 :
1034 148 : return OGRERR_NONE;
1035 : }
1036 :
1037 : /************************************************************************/
1038 : /* closeRings() */
1039 : /************************************************************************/
1040 :
1041 255 : void OGRGeometryCollection::closeRings()
1042 :
1043 : {
1044 754 : for (auto &&poSubGeom : *this)
1045 : {
1046 499 : if (OGR_GT_IsSubClassOf(wkbFlatten(poSubGeom->getGeometryType()),
1047 499 : wkbCurvePolygon))
1048 : {
1049 219 : OGRCurvePolygon *poPoly = poSubGeom->toCurvePolygon();
1050 219 : poPoly->closeRings();
1051 : }
1052 : }
1053 255 : }
1054 :
1055 : /************************************************************************/
1056 : /* setCoordinateDimension() */
1057 : /************************************************************************/
1058 :
1059 475 : void OGRGeometryCollection::setCoordinateDimension(int nNewDimension)
1060 :
1061 : {
1062 1357 : for (auto &&poSubGeom : *this)
1063 : {
1064 882 : poSubGeom->setCoordinateDimension(nNewDimension);
1065 : }
1066 :
1067 475 : OGRGeometry::setCoordinateDimension(nNewDimension);
1068 475 : }
1069 :
1070 54771 : void OGRGeometryCollection::set3D(OGRBoolean bIs3D)
1071 : {
1072 57648 : for (auto &&poSubGeom : *this)
1073 : {
1074 2877 : poSubGeom->set3D(bIs3D);
1075 : }
1076 :
1077 54771 : OGRGeometry::set3D(bIs3D);
1078 54771 : }
1079 :
1080 44189 : void OGRGeometryCollection::setMeasured(OGRBoolean bIsMeasured)
1081 : {
1082 47750 : for (auto &&poSubGeom : *this)
1083 : {
1084 3561 : poSubGeom->setMeasured(bIsMeasured);
1085 : }
1086 :
1087 44189 : OGRGeometry::setMeasured(bIsMeasured);
1088 44189 : }
1089 :
1090 : /************************************************************************/
1091 : /* get_Length() */
1092 : /************************************************************************/
1093 :
1094 : /**
1095 : * \brief Compute the length of a multicurve.
1096 : *
1097 : * The length is computed as the sum of the length of all members
1098 : * in this collection.
1099 : *
1100 : * @note No warning will be issued if a member of the collection does not
1101 : * support the get_Length method.
1102 : *
1103 : * @return computed length.
1104 : */
1105 :
1106 9 : double OGRGeometryCollection::get_Length() const
1107 : {
1108 9 : double dfLength = 0.0;
1109 26 : for (auto &&poSubGeom : *this)
1110 : {
1111 : const OGRwkbGeometryType eType =
1112 17 : wkbFlatten(poSubGeom->getGeometryType());
1113 17 : if (OGR_GT_IsCurve(eType))
1114 : {
1115 13 : const OGRCurve *poCurve = poSubGeom->toCurve();
1116 13 : dfLength += poCurve->get_Length();
1117 : }
1118 4 : else if (OGR_GT_IsSubClassOf(eType, wkbMultiCurve) ||
1119 : eType == wkbGeometryCollection)
1120 : {
1121 : const OGRGeometryCollection *poColl =
1122 2 : poSubGeom->toGeometryCollection();
1123 2 : dfLength += poColl->get_Length();
1124 : }
1125 : }
1126 :
1127 9 : return dfLength;
1128 : }
1129 :
1130 : /************************************************************************/
1131 : /* get_Area() */
1132 : /************************************************************************/
1133 :
1134 : /**
1135 : * \brief Compute area of geometry collection.
1136 : *
1137 : * The area is computed as the sum of the areas of all members
1138 : * in this collection.
1139 : *
1140 : * @note No warning will be issued if a member of the collection does not
1141 : * support the get_Area method.
1142 : *
1143 : * @return computed area.
1144 : */
1145 :
1146 11 : double OGRGeometryCollection::get_Area() const
1147 : {
1148 11 : double dfArea = 0.0;
1149 31 : for (auto &&poSubGeom : *this)
1150 : {
1151 20 : OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
1152 20 : if (OGR_GT_IsSurface(eType))
1153 : {
1154 15 : const OGRSurface *poSurface = poSubGeom->toSurface();
1155 15 : dfArea += poSurface->get_Area();
1156 : }
1157 5 : else if (OGR_GT_IsCurve(eType))
1158 : {
1159 1 : const OGRCurve *poCurve = poSubGeom->toCurve();
1160 1 : dfArea += poCurve->get_Area();
1161 : }
1162 4 : else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
1163 : eType == wkbGeometryCollection)
1164 : {
1165 2 : dfArea += poSubGeom->toGeometryCollection()->get_Area();
1166 : }
1167 : }
1168 :
1169 11 : return dfArea;
1170 : }
1171 :
1172 : /************************************************************************/
1173 : /* get_GeodesicArea() */
1174 : /************************************************************************/
1175 :
1176 : /**
1177 : * \brief Compute area of geometry collection, considered as a surface on
1178 : * the underlying ellipsoid of the SRS attached to the geometry.
1179 : *
1180 : * The returned area will always be in square meters, and assumes that
1181 : * polygon edges describe geodesic lines on the ellipsoid.
1182 : *
1183 : * If the geometry' SRS is not a geographic one, geometries are reprojected to
1184 : * the underlying geographic SRS of the geometry' SRS.
1185 : * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
1186 : *
1187 : * The area is computed as the sum of the areas of all members
1188 : * in this collection.
1189 : *
1190 : * @note No warning will be issued if a member of the collection does not
1191 : * support the get_GeodesicArea method.
1192 : *
1193 : * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
1194 : * @return the area of the geometry in square meters, or a negative value in case
1195 : * of error.
1196 : *
1197 : * @see get_Area() for an alternative method returning areas computed in
1198 : * 2D Cartesian space.
1199 : *
1200 : * @since GDAL 3.9
1201 : */
1202 6 : double OGRGeometryCollection::get_GeodesicArea(
1203 : const OGRSpatialReference *poSRSOverride) const
1204 : {
1205 6 : if (!poSRSOverride)
1206 4 : poSRSOverride = getSpatialReference();
1207 :
1208 6 : double dfArea = 0.0;
1209 11 : for (auto &&poSubGeom : *this)
1210 : {
1211 8 : OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
1212 8 : if (OGR_GT_IsSurface(eType))
1213 : {
1214 4 : const OGRSurface *poSurface = poSubGeom->toSurface();
1215 : const double dfLocalArea =
1216 4 : poSurface->get_GeodesicArea(poSRSOverride);
1217 4 : if (dfLocalArea < 0)
1218 1 : return dfLocalArea;
1219 3 : dfArea += dfLocalArea;
1220 : }
1221 4 : else if (OGR_GT_IsCurve(eType))
1222 : {
1223 2 : const OGRCurve *poCurve = poSubGeom->toCurve();
1224 2 : const double dfLocalArea = poCurve->get_GeodesicArea(poSRSOverride);
1225 2 : if (dfLocalArea < 0)
1226 1 : return dfLocalArea;
1227 1 : dfArea += dfLocalArea;
1228 : }
1229 2 : else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
1230 : eType == wkbGeometryCollection)
1231 : {
1232 : const double dfLocalArea =
1233 2 : poSubGeom->toGeometryCollection()->get_GeodesicArea(
1234 2 : poSRSOverride);
1235 2 : if (dfLocalArea < 0)
1236 1 : return dfLocalArea;
1237 1 : dfArea += dfLocalArea;
1238 : }
1239 : }
1240 :
1241 3 : return dfArea;
1242 : }
1243 :
1244 : /************************************************************************/
1245 : /* IsEmpty() */
1246 : /************************************************************************/
1247 :
1248 18592 : OGRBoolean OGRGeometryCollection::IsEmpty() const
1249 : {
1250 18735 : for (auto &&poSubGeom : *this)
1251 : {
1252 17883 : if (poSubGeom->IsEmpty() == FALSE)
1253 17740 : return FALSE;
1254 : }
1255 852 : return TRUE;
1256 : }
1257 :
1258 : /************************************************************************/
1259 : /* assignSpatialReference() */
1260 : /************************************************************************/
1261 :
1262 46176 : void OGRGeometryCollection::assignSpatialReference(
1263 : const OGRSpatialReference *poSR)
1264 : {
1265 46176 : OGRGeometry::assignSpatialReference(poSR);
1266 108731 : for (auto &&poSubGeom : *this)
1267 : {
1268 62555 : poSubGeom->assignSpatialReference(poSR);
1269 : }
1270 46176 : }
1271 :
1272 : /************************************************************************/
1273 : /* OGRGeometryCollection::segmentize() */
1274 : /************************************************************************/
1275 :
1276 12 : void OGRGeometryCollection::segmentize(double dfMaxLength)
1277 : {
1278 24 : for (auto &&poSubGeom : *this)
1279 : {
1280 12 : poSubGeom->segmentize(dfMaxLength);
1281 : }
1282 12 : }
1283 :
1284 : /************************************************************************/
1285 : /* swapXY() */
1286 : /************************************************************************/
1287 :
1288 30 : void OGRGeometryCollection::swapXY()
1289 : {
1290 76 : for (auto &&poSubGeom : *this)
1291 : {
1292 46 : poSubGeom->swapXY();
1293 : }
1294 30 : }
1295 :
1296 : /************************************************************************/
1297 : /* isCompatibleSubType() */
1298 : /************************************************************************/
1299 :
1300 : /** Returns whether a geometry of the specified geometry type can be a
1301 : * member of this collection.
1302 : *
1303 : * @param eSubType type of the potential member
1304 : * @return TRUE or FALSE
1305 : */
1306 :
1307 8903 : OGRBoolean OGRGeometryCollection::isCompatibleSubType(
1308 : CPL_UNUSED OGRwkbGeometryType eSubType) const
1309 : {
1310 : // Accept all geometries as sub-geometries.
1311 8903 : return TRUE;
1312 : }
1313 :
1314 : /************************************************************************/
1315 : /* hasCurveGeometry() */
1316 : /************************************************************************/
1317 :
1318 2352 : OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const
1319 : {
1320 6039 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
1321 : {
1322 3742 : if (papoGeoms[iGeom]->hasCurveGeometry(bLookForNonLinear))
1323 55 : return TRUE;
1324 : }
1325 2297 : return FALSE;
1326 : }
1327 :
1328 : /************************************************************************/
1329 : /* getLinearGeometry() */
1330 : /************************************************************************/
1331 :
1332 : OGRGeometry *
1333 321 : OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
1334 : const char *const *papszOptions) const
1335 : {
1336 : OGRGeometryCollection *poGC =
1337 321 : OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType()))
1338 321 : ->toGeometryCollection();
1339 321 : if (poGC == nullptr)
1340 0 : return nullptr;
1341 321 : poGC->assignSpatialReference(getSpatialReference());
1342 672 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
1343 : {
1344 702 : OGRGeometry *poSubGeom = papoGeoms[iGeom]->getLinearGeometry(
1345 351 : dfMaxAngleStepSizeDegrees, papszOptions);
1346 351 : poGC->addGeometryDirectly(poSubGeom);
1347 : }
1348 321 : return poGC;
1349 : }
1350 :
1351 : /************************************************************************/
1352 : /* getCurveGeometry() */
1353 : /************************************************************************/
1354 :
1355 : OGRGeometry *
1356 15 : OGRGeometryCollection::getCurveGeometry(const char *const *papszOptions) const
1357 : {
1358 : OGRGeometryCollection *poGC =
1359 15 : OGRGeometryFactory::createGeometry(OGR_GT_GetCurve(getGeometryType()))
1360 15 : ->toGeometryCollection();
1361 15 : if (poGC == nullptr)
1362 0 : return nullptr;
1363 15 : poGC->assignSpatialReference(getSpatialReference());
1364 15 : bool bHasCurveGeometry = false;
1365 162 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
1366 : {
1367 : OGRGeometry *poSubGeom =
1368 147 : papoGeoms[iGeom]->getCurveGeometry(papszOptions);
1369 147 : if (poSubGeom->hasCurveGeometry())
1370 7 : bHasCurveGeometry = true;
1371 147 : poGC->addGeometryDirectly(poSubGeom);
1372 : }
1373 15 : if (!bHasCurveGeometry)
1374 : {
1375 10 : delete poGC;
1376 10 : return clone();
1377 : }
1378 5 : return poGC;
1379 : }
1380 :
1381 : /************************************************************************/
1382 : /* TransferMembersAndDestroy() */
1383 : /************************************************************************/
1384 :
1385 : //! @cond Doxygen_Suppress
1386 : OGRGeometryCollection *
1387 230 : OGRGeometryCollection::TransferMembersAndDestroy(OGRGeometryCollection *poSrc,
1388 : OGRGeometryCollection *poDst)
1389 : {
1390 230 : poDst->assignSpatialReference(poSrc->getSpatialReference());
1391 230 : poDst->set3D(poSrc->Is3D());
1392 230 : poDst->setMeasured(poSrc->IsMeasured());
1393 230 : poDst->nGeomCount = poSrc->nGeomCount;
1394 230 : poDst->papoGeoms = poSrc->papoGeoms;
1395 230 : poSrc->nGeomCount = 0;
1396 230 : poSrc->papoGeoms = nullptr;
1397 230 : delete poSrc;
1398 230 : return poDst;
1399 : }
1400 :
1401 : //! @endcond
1402 :
1403 : /************************************************************************/
1404 : /* CastToGeometryCollection() */
1405 : /************************************************************************/
1406 :
1407 : /**
1408 : * \brief Cast to geometry collection.
1409 : *
1410 : * This methods cast a derived class of geometry collection to a plain
1411 : * geometry collection.
1412 : *
1413 : * The passed in geometry is consumed and a new one returned (or NULL in case
1414 : * of failure).
1415 : *
1416 : * @param poSrc the input geometry - ownership is passed to the method.
1417 : * @return new geometry.
1418 : * @since GDAL 2.2
1419 : */
1420 :
1421 : OGRGeometryCollection *
1422 111 : OGRGeometryCollection::CastToGeometryCollection(OGRGeometryCollection *poSrc)
1423 : {
1424 111 : if (wkbFlatten(poSrc->getGeometryType()) == wkbGeometryCollection)
1425 0 : return poSrc;
1426 111 : return TransferMembersAndDestroy(poSrc, new OGRGeometryCollection());
1427 : }
|