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