1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  The OGRCurveCollection class.
5  * Author:   Even Rouault, even dot rouault at spatialys dot com
6  *
7  ******************************************************************************
8  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "cpl_port.h"
30 #include "ogr_geometry.h"
31 
32 #include <cstddef>
33 #include <cstring>
34 #include <new>
35 
36 #include "ogr_core.h"
37 #include "ogr_p.h"
38 #include "ogr_spatialref.h"
39 #include "cpl_conv.h"
40 #include "cpl_error.h"
41 #include "cpl_string.h"
42 #include "cpl_vsi.h"
43 
44 CPL_CVSID("$Id: ogrcurvecollection.cpp 3798cbe48457b7127606931896549f26507469db 2021-04-09 15:04:16 +0200 Even Rouault $")
45 
46 //! @cond Doxygen_Suppress
47 
48 /************************************************************************/
49 /*                         OGRCurveCollection()                         */
50 /************************************************************************/
51 
52 OGRCurveCollection::OGRCurveCollection() = default;
53 
54 /************************************************************************/
55 /*             OGRCurveCollection( const OGRCurveCollection& )          */
56 /************************************************************************/
57 
58 /**
59  * \brief Copy constructor.
60  *
61  * Note: before GDAL 2.1, only the default implementation of the constructor
62  * existed, which could be unsafe to use.
63  *
64  * @since GDAL 2.1
65  */
66 
OGRCurveCollection(const OGRCurveCollection & other)67 OGRCurveCollection::OGRCurveCollection( const OGRCurveCollection& other )
68 {
69     if( other.nCurveCount > 0 )
70     {
71         nCurveCount = other.nCurveCount;
72         papoCurves = static_cast<OGRCurve **>(
73             VSI_CALLOC_VERBOSE(sizeof(void*), nCurveCount));
74 
75         if( papoCurves )
76         {
77             for( int i = 0; i < nCurveCount; i++ )
78             {
79                 papoCurves[i] = other.papoCurves[i]->clone();
80             }
81         }
82     }
83 }
84 
85 /************************************************************************/
86 /*                         ~OGRCurveCollection()                        */
87 /************************************************************************/
88 
~OGRCurveCollection()89 OGRCurveCollection::~OGRCurveCollection()
90 
91 {
92     empty(nullptr);
93 }
94 
95 /************************************************************************/
96 /*                 operator=( const OGRCurveCollection& )               */
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 OGRCurveCollection&
operator =(const OGRCurveCollection & other)109 OGRCurveCollection::operator=( const OGRCurveCollection& other )
110 {
111     if( this != &other)
112     {
113         empty(nullptr);
114 
115         if( other.nCurveCount > 0 )
116         {
117             nCurveCount = other.nCurveCount;
118             papoCurves = static_cast<OGRCurve **>(
119                 VSI_MALLOC2_VERBOSE(sizeof(void*), nCurveCount));
120 
121             if( papoCurves )
122             {
123                 for( int i = 0; i < nCurveCount; i++ )
124                 {
125                     papoCurves[i] = other.papoCurves[i]->clone();
126                 }
127             }
128         }
129     }
130     return *this;
131 }
132 
133 /************************************************************************/
134 /*                              WkbSize()                               */
135 /************************************************************************/
136 
WkbSize() const137 size_t OGRCurveCollection::WkbSize() const
138 {
139     size_t nSize = 9;
140 
141     for( auto&& poSubGeom: *this )
142     {
143         nSize += poSubGeom->WkbSize();
144     }
145 
146     return nSize;
147 }
148 
149 /************************************************************************/
150 /*                          addCurveDirectly()                          */
151 /************************************************************************/
152 
addCurveDirectly(OGRGeometry * poGeom,OGRCurve * poCurve,int bNeedRealloc)153 OGRErr OGRCurveCollection::addCurveDirectly( OGRGeometry* poGeom,
154                                              OGRCurve* poCurve,
155                                              int bNeedRealloc )
156 {
157     poGeom->HomogenizeDimensionalityWith(poCurve);
158 
159     if( bNeedRealloc )
160     {
161         OGRCurve** papoNewCurves = static_cast<OGRCurve **>(
162             VSI_REALLOC_VERBOSE(papoCurves,
163                                 sizeof(OGRCurve*) * (nCurveCount + 1)));
164         if( papoNewCurves == nullptr )
165             return OGRERR_FAILURE;
166         papoCurves = papoNewCurves;
167     }
168 
169     papoCurves[nCurveCount] = poCurve;
170 
171     nCurveCount++;
172 
173     return OGRERR_NONE;
174 }
175 
176 /************************************************************************/
177 /*                        importPreambleFromWkb()                      */
178 /************************************************************************/
179 
importPreambleFromWkb(OGRGeometry * poGeom,const unsigned char * pabyData,size_t & nSize,size_t & nDataOffset,OGRwkbByteOrder & eByteOrder,size_t nMinSubGeomSize,OGRwkbVariant eWkbVariant)180 OGRErr OGRCurveCollection::importPreambleFromWkb( OGRGeometry* poGeom,
181                                                    const unsigned char * pabyData,
182                                                    size_t& nSize,
183                                                    size_t& nDataOffset,
184                                                    OGRwkbByteOrder& eByteOrder,
185                                                    size_t nMinSubGeomSize,
186                                                    OGRwkbVariant eWkbVariant )
187 {
188     OGRErr eErr = poGeom->importPreambleOfCollectionFromWkb(
189                                                         pabyData,
190                                                         nSize,
191                                                         nDataOffset,
192                                                         eByteOrder,
193                                                         nMinSubGeomSize,
194                                                         nCurveCount,
195                                                         eWkbVariant );
196     if( eErr != OGRERR_NONE )
197         return eErr;
198 
199     // coverity[tainted_data]
200     papoCurves = static_cast<OGRCurve **>(
201         VSI_CALLOC_VERBOSE(sizeof(void*), nCurveCount));
202     if( nCurveCount != 0 && papoCurves == nullptr )
203     {
204         nCurveCount = 0;
205         return OGRERR_NOT_ENOUGH_MEMORY;
206     }
207 
208     return OGRERR_NONE;
209 }
210 
211 /************************************************************************/
212 /*                       importBodyFromWkb()                            */
213 /************************************************************************/
214 
importBodyFromWkb(OGRGeometry * poGeom,const unsigned char * pabyData,size_t nSize,bool bAcceptCompoundCurve,OGRErr (* pfnAddCurveDirectlyFromWkb)(OGRGeometry * poGeom,OGRCurve * poCurve),OGRwkbVariant eWkbVariant,size_t & nBytesConsumedOut)215 OGRErr OGRCurveCollection::importBodyFromWkb(
216     OGRGeometry* poGeom,
217     const unsigned char * pabyData,
218     size_t nSize,
219     bool bAcceptCompoundCurve,
220     OGRErr (*pfnAddCurveDirectlyFromWkb)(OGRGeometry* poGeom,
221                                          OGRCurve* poCurve),
222     OGRwkbVariant eWkbVariant,
223     size_t& nBytesConsumedOut )
224 {
225     nBytesConsumedOut = 0;
226 /* -------------------------------------------------------------------- */
227 /*      Get the Geoms.                                                  */
228 /* -------------------------------------------------------------------- */
229     const int nIter = nCurveCount;
230     nCurveCount = 0;
231     size_t nDataOffset = 0;
232     for( int iGeom = 0; iGeom < nIter; iGeom++ )
233     {
234         OGRGeometry* poSubGeom = nullptr;
235 
236         // Parses sub-geometry.
237         const unsigned char* pabySubData = pabyData + nDataOffset;
238         if( nSize < 9 && nSize != static_cast<size_t>(-1) )
239             return OGRERR_NOT_ENOUGH_DATA;
240 
241         OGRwkbGeometryType eFlattenSubGeomType = wkbUnknown;
242         if( OGRReadWKBGeometryType( pabySubData, eWkbVariant,
243                                     &eFlattenSubGeomType ) != OGRERR_NONE )
244             return OGRERR_FAILURE;
245         eFlattenSubGeomType = wkbFlatten(eFlattenSubGeomType);
246 
247         OGRErr eErr = OGRERR_NONE;
248         size_t nSubGeomBytesConsumedOut = 0;
249         if( (eFlattenSubGeomType != wkbCompoundCurve &&
250              OGR_GT_IsCurve(eFlattenSubGeomType)) ||
251             (bAcceptCompoundCurve && eFlattenSubGeomType == wkbCompoundCurve) )
252         {
253             eErr = OGRGeometryFactory::
254                 createFromWkb( pabySubData, nullptr,
255                                &poSubGeom, nSize, eWkbVariant,
256                                nSubGeomBytesConsumedOut );
257         }
258         else
259         {
260             CPLDebug(
261                 "OGR",
262                 "Cannot add geometry of type (%d) to geometry of type (%d)",
263                 eFlattenSubGeomType, poGeom->getGeometryType());
264             return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
265         }
266 
267         if( eErr == OGRERR_NONE )
268         {
269             CPLAssert( nSubGeomBytesConsumedOut > 0 );
270             if( nSize != static_cast<size_t>(-1) )
271             {
272                 CPLAssert( nSize >= nSubGeomBytesConsumedOut );
273                 nSize -= nSubGeomBytesConsumedOut;
274             }
275 
276             nDataOffset += nSubGeomBytesConsumedOut;
277 
278             OGRCurve *poCurve = poSubGeom->toCurve();
279             eErr = pfnAddCurveDirectlyFromWkb(poGeom, poCurve);
280         }
281         if( eErr != OGRERR_NONE )
282         {
283             delete poSubGeom;
284             return eErr;
285         }
286 
287     }
288     nBytesConsumedOut = nDataOffset;
289 
290     return OGRERR_NONE;
291 }
292 
293 /************************************************************************/
294 /*                            exportToWkt()                             */
295 /************************************************************************/
296 
exportToWkt(const OGRGeometry * baseGeom,const OGRWktOptions & opts,OGRErr * err) const297 std::string OGRCurveCollection::exportToWkt(const OGRGeometry *baseGeom,
298     const OGRWktOptions& opts, OGRErr *err) const
299 {
300     try
301     {
302         bool first = true;
303         std::string wkt(baseGeom->getGeometryName());
304 
305         OGRWktOptions optsModified(opts);
306         optsModified.variant = wkbVariantIso;
307         wkt += baseGeom->wktTypeString(optsModified.variant);
308 
309         for (int i = 0; i < nCurveCount; ++i)
310         {
311             OGRGeometry *geom = papoCurves[i];
312 
313             OGRErr subgeomErr = OGRERR_NONE;
314             std::string tempWkt = geom->exportToWkt(optsModified, &subgeomErr);
315             if (subgeomErr != OGRERR_NONE)
316             {
317                 if( err )
318                     *err = subgeomErr;
319                 return std::string();
320             }
321 
322             // A curve collection has a list of linestrings (OGRCompoundCurve),
323             // which should have their leader removed, or a OGRCurvePolygon,
324             // which has leaders for each of its sub-geometries that aren't
325             // linestrings.
326             if (tempWkt.compare(0, strlen("LINESTRING"), "LINESTRING") == 0)
327             {
328                 auto pos = tempWkt.find('(');
329                 if (pos != std::string::npos)
330                     tempWkt = tempWkt.substr(pos);
331             }
332 
333             if (tempWkt.find("EMPTY") != std::string::npos)
334                 continue;
335 
336             if (first)
337                 wkt += '(';
338             else
339                 wkt += ',';
340             first = false;
341             wkt += tempWkt;
342         }
343 
344         if (err)
345             *err = OGRERR_NONE;
346         if( first )
347             wkt += "EMPTY";
348         else
349             wkt += ')';
350         return wkt;
351     }
352     catch( const std::bad_alloc& e )
353     {
354         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
355         if (err)
356             *err = OGRERR_FAILURE;
357         return std::string();
358     }
359 }
360 
361 /************************************************************************/
362 /*                            exportToWkb()                             */
363 /************************************************************************/
364 
exportToWkb(const OGRGeometry * poGeom,OGRwkbByteOrder eByteOrder,unsigned char * pabyData,OGRwkbVariant eWkbVariant) const365 OGRErr OGRCurveCollection::exportToWkb( const OGRGeometry* poGeom,
366                                         OGRwkbByteOrder eByteOrder,
367                                         unsigned char * pabyData,
368                                         OGRwkbVariant eWkbVariant ) const
369 {
370 /* -------------------------------------------------------------------- */
371 /*      Set the byte order.                                             */
372 /* -------------------------------------------------------------------- */
373     pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(static_cast<unsigned char>(eByteOrder));
374 
375 /* -------------------------------------------------------------------- */
376 /*      Set the geometry feature type, ensuring that 3D flag is         */
377 /*      preserved.                                                      */
378 /* -------------------------------------------------------------------- */
379     GUInt32 nGType = poGeom->getIsoGeometryType();
380     if( eWkbVariant == wkbVariantPostGIS1 )
381     {
382         const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
383         nGType = wkbFlatten(nGType);
384         if( nGType == wkbCurvePolygon )
385             nGType = POSTGIS15_CURVEPOLYGON;
386         if( bIs3D )
387             // Explicitly set wkb25DBit.
388             nGType = static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
389     }
390 
391     if( OGR_SWAP( eByteOrder ) )
392     {
393         nGType = CPL_SWAP32(nGType);
394     }
395 
396     memcpy( pabyData + 1, &nGType, 4 );
397 
398 /* -------------------------------------------------------------------- */
399 /*      Copy in the raw data.                                           */
400 /* -------------------------------------------------------------------- */
401     if( OGR_SWAP( eByteOrder ) )
402     {
403         const int nCount = CPL_SWAP32( nCurveCount );
404         memcpy( pabyData+5, &nCount, 4 );
405     }
406     else
407     {
408         memcpy( pabyData+5, &nCurveCount, 4 );
409     }
410 
411     // TODO(schwehr): Where do these 9 values come from?
412     size_t nOffset = 9;
413 
414 /* ==================================================================== */
415 /*      Serialize each of the Geoms.                                    */
416 /* ==================================================================== */
417     for( auto&& poSubGeom: *this )
418     {
419         poSubGeom->exportToWkb( eByteOrder, pabyData + nOffset,
420                                         eWkbVariant );
421 
422         nOffset += poSubGeom->WkbSize();
423     }
424 
425     return OGRERR_NONE;
426 }
427 
428 /************************************************************************/
429 /*                               empty()                                */
430 /************************************************************************/
431 
empty(OGRGeometry * poGeom)432 void OGRCurveCollection::empty( OGRGeometry* poGeom )
433 {
434     if( papoCurves != nullptr )
435     {
436         for( auto&& poSubGeom: *this )
437         {
438             delete poSubGeom;
439         }
440         CPLFree( papoCurves );
441     }
442 
443     nCurveCount = 0;
444     papoCurves = nullptr;
445     if( poGeom )
446         poGeom->setCoordinateDimension(2);
447 }
448 
449 /************************************************************************/
450 /*                            getEnvelope()                             */
451 /************************************************************************/
452 
getEnvelope(OGREnvelope * psEnvelope) const453 void OGRCurveCollection::getEnvelope( OGREnvelope * psEnvelope ) const
454 {
455     OGREnvelope3D oEnv3D;
456     getEnvelope(&oEnv3D);
457     psEnvelope->MinX = oEnv3D.MinX;
458     psEnvelope->MinY = oEnv3D.MinY;
459     psEnvelope->MaxX = oEnv3D.MaxX;
460     psEnvelope->MaxY = oEnv3D.MaxY;
461 }
462 
463 /************************************************************************/
464 /*                            getEnvelope()                             */
465 /************************************************************************/
466 
getEnvelope(OGREnvelope3D * psEnvelope) const467 void OGRCurveCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const
468 {
469     OGREnvelope3D oGeomEnv;
470     bool bExtentSet = false;
471 
472     *psEnvelope = OGREnvelope3D();
473     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
474     {
475         if( !papoCurves[iGeom]->IsEmpty() )
476         {
477             bExtentSet = true;
478             papoCurves[iGeom]->getEnvelope( &oGeomEnv );
479             psEnvelope->Merge( oGeomEnv );
480         }
481     }
482 
483     if( !bExtentSet )
484     {
485         // To be backward compatible when called on empty geom
486         psEnvelope->MinX = 0.0;
487         psEnvelope->MinY = 0.0;
488         psEnvelope->MinZ = 0.0;
489         psEnvelope->MaxX = 0.0;
490         psEnvelope->MaxY = 0.0;
491         psEnvelope->MaxZ = 0.0;
492     }
493 }
494 
495 /************************************************************************/
496 /*                               IsEmpty()                              */
497 /************************************************************************/
498 
IsEmpty() const499 OGRBoolean OGRCurveCollection::IsEmpty() const
500 {
501     for( auto&& poSubGeom: *this )
502     {
503         if( !poSubGeom->IsEmpty() )
504             return FALSE;
505     }
506     return TRUE;
507 }
508 
509 /************************************************************************/
510 /*                               Equals()                                */
511 /************************************************************************/
512 
Equals(const OGRCurveCollection * poOCC) const513 OGRBoolean OGRCurveCollection::Equals( const OGRCurveCollection *poOCC ) const
514 {
515     if( getNumCurves() != poOCC->getNumCurves() )
516         return FALSE;
517 
518     // Should eventually test the SRS.
519 
520     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
521     {
522         if( !getCurve(iGeom)->Equals(poOCC->getCurve(iGeom)) )
523             return FALSE;
524     }
525 
526     return TRUE;
527 }
528 
529 /************************************************************************/
530 /*                       setCoordinateDimension()                       */
531 /************************************************************************/
532 
setCoordinateDimension(OGRGeometry * poGeom,int nNewDimension)533 void OGRCurveCollection::setCoordinateDimension( OGRGeometry* poGeom,
534                                                  int nNewDimension )
535 {
536     for( auto&& poSubGeom: *this )
537     {
538         poSubGeom->setCoordinateDimension( nNewDimension );
539     }
540 
541     poGeom->OGRGeometry::setCoordinateDimension( nNewDimension );
542 }
543 
set3D(OGRGeometry * poGeom,OGRBoolean bIs3D)544 void OGRCurveCollection::set3D( OGRGeometry* poGeom, OGRBoolean bIs3D )
545 {
546     for( auto&& poSubGeom: *this )
547     {
548         poSubGeom->set3D( bIs3D );
549     }
550 
551     poGeom->OGRGeometry::set3D( bIs3D );
552 }
553 
setMeasured(OGRGeometry * poGeom,OGRBoolean bIsMeasured)554 void OGRCurveCollection::setMeasured( OGRGeometry* poGeom,
555                                       OGRBoolean bIsMeasured )
556 {
557     for( auto&& poSubGeom: *this )
558     {
559         poSubGeom->setMeasured( bIsMeasured );
560     }
561 
562     poGeom->OGRGeometry::setMeasured( bIsMeasured );
563 }
564 
565 /************************************************************************/
566 /*                       assignSpatialReference()                       */
567 /************************************************************************/
568 
assignSpatialReference(OGRGeometry * poGeom,OGRSpatialReference * poSR)569 void OGRCurveCollection::assignSpatialReference( OGRGeometry* poGeom,
570                                                  OGRSpatialReference * poSR )
571 {
572     for( auto&& poSubGeom: *this )
573     {
574         poSubGeom->assignSpatialReference( poSR );
575     }
576     poGeom->OGRGeometry::assignSpatialReference( poSR );
577 }
578 
579 /************************************************************************/
580 /*                          getNumCurves()                              */
581 /************************************************************************/
582 
getNumCurves() const583 int OGRCurveCollection::getNumCurves() const
584 {
585     return nCurveCount;
586 }
587 
588 /************************************************************************/
589 /*                           getCurve()                                 */
590 /************************************************************************/
591 
getCurve(int i)592 OGRCurve *OGRCurveCollection::getCurve( int i )
593 {
594     if( i < 0 || i >= nCurveCount )
595         return nullptr;
596     return papoCurves[i];
597 }
598 
599 /************************************************************************/
600 /*                           getCurve()                                 */
601 /************************************************************************/
602 
getCurve(int i) const603 const OGRCurve *OGRCurveCollection::getCurve( int i ) const
604 {
605     if( i < 0 || i >= nCurveCount )
606         return nullptr;
607     return papoCurves[i];
608 }
609 
610 /************************************************************************/
611 /*                           stealCurve()                               */
612 /************************************************************************/
613 
stealCurve(int i)614 OGRCurve* OGRCurveCollection::stealCurve( int i )
615 {
616     if( i < 0 || i >= nCurveCount )
617         return nullptr;
618     OGRCurve* poRet = papoCurves[i];
619     if( i < nCurveCount - 1 )
620     {
621         memmove(papoCurves + i,
622                 papoCurves + i + 1,
623                 (nCurveCount - i - 1) * sizeof(OGRCurve*));
624     }
625     nCurveCount--;
626     return poRet;
627 }
628 
629 /************************************************************************/
630 /*                             transform()                              */
631 /************************************************************************/
632 
transform(OGRGeometry * poGeom,OGRCoordinateTransformation * poCT)633 OGRErr OGRCurveCollection::transform( OGRGeometry* poGeom,
634                                       OGRCoordinateTransformation *poCT )
635 {
636     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
637     {
638         const OGRErr eErr = papoCurves[iGeom]->transform( poCT );
639         if( eErr != OGRERR_NONE )
640         {
641             if( iGeom != 0 )
642             {
643                 CPLDebug("OGR",
644                          "OGRCurveCollection::transform() failed for a "
645                          "geometry other than the first, meaning some "
646                          "geometries are transformed and some are not!" );
647 
648                 return OGRERR_FAILURE;
649             }
650 
651             return eErr;
652         }
653     }
654 
655     poGeom->assignSpatialReference( poCT->GetTargetCS() );
656 
657     return OGRERR_NONE;
658 }
659 
660 /************************************************************************/
661 /*                            flattenTo2D()                             */
662 /************************************************************************/
663 
flattenTo2D(OGRGeometry * poGeom)664 void OGRCurveCollection::flattenTo2D(OGRGeometry* poGeom)
665 {
666     for( auto&& poSubGeom: *this )
667     {
668         poSubGeom->flattenTo2D();
669     }
670 
671     poGeom->setCoordinateDimension(2);
672 }
673 
674 /************************************************************************/
675 /*                              segmentize()                            */
676 /************************************************************************/
677 
segmentize(double dfMaxLength)678 void OGRCurveCollection::segmentize(double dfMaxLength)
679 {
680     for( auto&& poSubGeom: *this )
681     {
682         poSubGeom->segmentize(dfMaxLength);
683     }
684 }
685 
686 /************************************************************************/
687 /*                               swapXY()                               */
688 /************************************************************************/
689 
swapXY()690 void OGRCurveCollection::swapXY()
691 {
692     for( auto&& poSubGeom: *this )
693     {
694         poSubGeom->swapXY();
695     }
696 }
697 
698 /************************************************************************/
699 /*                         hasCurveGeometry()                           */
700 /************************************************************************/
701 
hasCurveGeometry(int bLookForNonLinear) const702 OGRBoolean OGRCurveCollection::hasCurveGeometry(int bLookForNonLinear) const
703 {
704     for( auto&& poSubGeom: *this )
705     {
706         if( poSubGeom->hasCurveGeometry(bLookForNonLinear) )
707             return TRUE;
708     }
709     return FALSE;
710 }
711 
712 /************************************************************************/
713 /*                           removeCurve()                              */
714 /************************************************************************/
715 
716 /**
717  * \brief Remove a geometry from the container.
718  *
719  * Removing a geometry will cause the geometry count to drop by one, and all
720  * "higher" geometries will shuffle down one in index.
721  *
722  * @param iIndex the index of the geometry to delete.  A value of -1 is a
723  * special flag meaning that all geometries should be removed.
724  *
725  * @param bDelete if true the geometry will be deallocated, otherwise it will
726  * not.  The default is true as the container is considered to own the
727  * geometries in it.
728  *
729  * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
730  * out of range.
731  */
732 
removeCurve(int iIndex,bool bDelete)733 OGRErr OGRCurveCollection::removeCurve( int iIndex, bool bDelete )
734 
735 {
736     if( iIndex < -1 || iIndex >= nCurveCount )
737         return OGRERR_FAILURE;
738 
739     // Special case.
740     if( iIndex == -1 )
741     {
742         while( nCurveCount > 0 )
743             removeCurve( nCurveCount-1, bDelete );
744         return OGRERR_NONE;
745     }
746 
747     if( bDelete )
748         delete papoCurves[iIndex];
749 
750     memmove( papoCurves + iIndex, papoCurves + iIndex + 1,
751              sizeof(void*) * (nCurveCount-iIndex-1) );
752 
753     nCurveCount--;
754 
755     return OGRERR_NONE;
756 }
757 
758 //! @endcond
759