1 /******************************************************************************
2 *
3 * Project: OpenGIS Simple Features Reference Implementation
4 * Purpose: The OGRCompoundCurve geometry 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 <cmath>
33 #include <cstddef>
34
35 #include "cpl_error.h"
36 #include "ogr_core.h"
37 #include "ogr_geometry.h"
38 #include "ogr_p.h"
39 #include "ogr_spatialref.h"
40
41 CPL_CVSID("$Id: ogrcompoundcurve.cpp 172dc4ec9270e5e2e951f843b65d466b085d5693 2021-08-06 11:18:12 +0200 Even Rouault $")
42
43 /************************************************************************/
44 /* OGRCompoundCurve() */
45 /************************************************************************/
46
47 /**
48 * \brief Create an empty compound curve.
49 */
50
51 OGRCompoundCurve::OGRCompoundCurve() = default;
52
53 /************************************************************************/
54 /* OGRCompoundCurve( const OGRCompoundCurve& ) */
55 /************************************************************************/
56
57 /**
58 * \brief Copy constructor.
59 *
60 * Note: before GDAL 2.1, only the default implementation of the constructor
61 * existed, which could be unsafe to use.
62 *
63 * @since GDAL 2.1
64 */
65
66 OGRCompoundCurve::OGRCompoundCurve( const OGRCompoundCurve& ) = default;
67
68 /************************************************************************/
69 /* ~OGRCompoundCurve() */
70 /************************************************************************/
71
72 OGRCompoundCurve::~OGRCompoundCurve() = default;
73
74 /************************************************************************/
75 /* operator=( const OGRCompoundCurve&) */
76 /************************************************************************/
77
78 /**
79 * \brief Assignment operator.
80 *
81 * Note: before GDAL 2.1, only the default implementation of the operator
82 * existed, which could be unsafe to use.
83 *
84 * @since GDAL 2.1
85 */
86
operator =(const OGRCompoundCurve & other)87 OGRCompoundCurve& OGRCompoundCurve::operator=( const OGRCompoundCurve& other )
88 {
89 if( this != &other)
90 {
91 OGRCurve::operator=( other );
92
93 oCC = other.oCC;
94 }
95 return *this;
96 }
97
98 /************************************************************************/
99 /* clone() */
100 /************************************************************************/
101
clone() const102 OGRCompoundCurve *OGRCompoundCurve::clone() const
103
104 {
105 return new (std::nothrow) OGRCompoundCurve(*this);
106 }
107
108 /************************************************************************/
109 /* getGeometryType() */
110 /************************************************************************/
111
getGeometryType() const112 OGRwkbGeometryType OGRCompoundCurve::getGeometryType() const
113
114 {
115 if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
116 return wkbCompoundCurveZM;
117 else if( flags & OGR_G_MEASURED )
118 return wkbCompoundCurveM;
119 else if( flags & OGR_G_3D )
120 return wkbCompoundCurveZ;
121 else
122 return wkbCompoundCurve;
123 }
124
125 /************************************************************************/
126 /* getGeometryName() */
127 /************************************************************************/
128
getGeometryName() const129 const char * OGRCompoundCurve::getGeometryName() const
130
131 {
132 return "COMPOUNDCURVE";
133 }
134
135 /************************************************************************/
136 /* WkbSize() */
137 /************************************************************************/
WkbSize() const138 size_t OGRCompoundCurve::WkbSize() const
139 {
140 return oCC.WkbSize();
141 }
142
143 /************************************************************************/
144 /* addCurveDirectlyFromWkt() */
145 /************************************************************************/
146
addCurveDirectlyFromWkb(OGRGeometry * poSelf,OGRCurve * poCurve)147 OGRErr OGRCompoundCurve::addCurveDirectlyFromWkb( OGRGeometry* poSelf,
148 OGRCurve* poCurve )
149 {
150 OGRCompoundCurve* poCC = poSelf->toCompoundCurve();
151 return poCC->addCurveDirectlyInternal( poCurve, 1e-14, FALSE );
152 }
153
154 /************************************************************************/
155 /* importFromWkb() */
156 /************************************************************************/
157
importFromWkb(const unsigned char * pabyData,size_t nSize,OGRwkbVariant eWkbVariant,size_t & nBytesConsumedOut)158 OGRErr OGRCompoundCurve::importFromWkb( const unsigned char * pabyData,
159 size_t nSize,
160 OGRwkbVariant eWkbVariant,
161 size_t& nBytesConsumedOut )
162 {
163 OGRwkbByteOrder eByteOrder = wkbNDR;
164 size_t nDataOffset = 0;
165 // coverity[tainted_data]
166 OGRErr eErr = oCC.importPreambleFromWkb(this, pabyData, nSize, nDataOffset,
167 eByteOrder, 9, eWkbVariant);
168 if( eErr != OGRERR_NONE )
169 return eErr;
170
171 eErr = oCC.importBodyFromWkb(this, pabyData + nDataOffset, nSize,
172 false, // bAcceptCompoundCurve
173 addCurveDirectlyFromWkb,
174 eWkbVariant,
175 nBytesConsumedOut);
176 if( eErr == OGRERR_NONE )
177 nBytesConsumedOut += nDataOffset;
178 return eErr;
179 }
180
181 /************************************************************************/
182 /* exportToWkb() */
183 /************************************************************************/
exportToWkb(OGRwkbByteOrder eByteOrder,unsigned char * pabyData,OGRwkbVariant eWkbVariant) const184 OGRErr OGRCompoundCurve::exportToWkb( OGRwkbByteOrder eByteOrder,
185 unsigned char * pabyData,
186 OGRwkbVariant eWkbVariant ) const
187 {
188 // Does not make sense for new geometries, so patch it.
189 if( eWkbVariant == wkbVariantOldOgc )
190 eWkbVariant = wkbVariantIso;
191 return oCC.exportToWkb(this, eByteOrder, pabyData, eWkbVariant);
192 }
193
194 /************************************************************************/
195 /* addCurveDirectlyFromWkt() */
196 /************************************************************************/
197
addCurveDirectlyFromWkt(OGRGeometry * poSelf,OGRCurve * poCurve)198 OGRErr OGRCompoundCurve::addCurveDirectlyFromWkt( OGRGeometry* poSelf,
199 OGRCurve* poCurve )
200 {
201 return poSelf->toCompoundCurve()->addCurveDirectly(poCurve);
202 }
203
204 /************************************************************************/
205 /* importFromWkt() */
206 /************************************************************************/
207
importFromWkt(const char ** ppszInput)208 OGRErr OGRCompoundCurve::importFromWkt( const char ** ppszInput )
209 {
210 return importCurveCollectionFromWkt( ppszInput,
211 FALSE, // bAllowEmptyComponent
212 TRUE, // bAllowLineString
213 TRUE, // bAllowCurve
214 FALSE, // bAllowCompoundCurve
215 addCurveDirectlyFromWkt );
216 }
217
218 /************************************************************************/
219 /* exportToWkt() */
220 /************************************************************************/
exportToWkt(const OGRWktOptions & opts,OGRErr * err) const221 std::string OGRCompoundCurve::exportToWkt(const OGRWktOptions& opts,
222 OGRErr *err) const
223 {
224 return oCC.exportToWkt(this, opts, err);
225 }
226
227 /************************************************************************/
228 /* empty() */
229 /************************************************************************/
230
empty()231 void OGRCompoundCurve::empty()
232 {
233 oCC.empty(this);
234 }
235
236 /************************************************************************/
237 /* getEnvelope() */
238 /************************************************************************/
239
getEnvelope(OGREnvelope * psEnvelope) const240 void OGRCompoundCurve::getEnvelope( OGREnvelope * psEnvelope ) const
241 {
242 oCC.getEnvelope(psEnvelope);
243 }
244
245 /************************************************************************/
246 /* getEnvelope() */
247 /************************************************************************/
248
getEnvelope(OGREnvelope3D * psEnvelope) const249 void OGRCompoundCurve::getEnvelope( OGREnvelope3D * psEnvelope ) const
250 {
251 oCC.getEnvelope(psEnvelope);
252 }
253
254 /************************************************************************/
255 /* IsEmpty() */
256 /************************************************************************/
257
IsEmpty() const258 OGRBoolean OGRCompoundCurve::IsEmpty() const
259 {
260 return oCC.IsEmpty();
261 }
262
263 /************************************************************************/
264 /* get_Length() */
265 /* */
266 /* For now we return a simple euclidean 2D distance. */
267 /************************************************************************/
268
get_Length() const269 double OGRCompoundCurve::get_Length() const
270 {
271 double dfLength = 0.0;
272 for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ )
273 dfLength += oCC.papoCurves[iGeom]->get_Length();
274 return dfLength;
275 }
276
277 /************************************************************************/
278 /* StartPoint() */
279 /************************************************************************/
280
StartPoint(OGRPoint * p) const281 void OGRCompoundCurve::StartPoint(OGRPoint *p) const
282 {
283 CPLAssert(oCC.nCurveCount > 0);
284 oCC.papoCurves[0]->StartPoint(p);
285 }
286
287 /************************************************************************/
288 /* EndPoint() */
289 /************************************************************************/
290
EndPoint(OGRPoint * p) const291 void OGRCompoundCurve::EndPoint(OGRPoint *p) const
292 {
293 CPLAssert(oCC.nCurveCount > 0);
294 oCC.papoCurves[oCC.nCurveCount-1]->EndPoint(p);
295 }
296
297 /************************************************************************/
298 /* Value() */
299 /************************************************************************/
300
Value(double dfDistance,OGRPoint * poPoint) const301 void OGRCompoundCurve::Value( double dfDistance, OGRPoint *poPoint ) const
302 {
303
304 if( dfDistance < 0 )
305 {
306 StartPoint( poPoint );
307 return;
308 }
309
310 double dfLength = 0.0;
311 for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ )
312 {
313 const double dfSegLength = oCC.papoCurves[iGeom]->get_Length();
314 if( dfSegLength > 0 )
315 {
316 if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >=
317 dfDistance) )
318 {
319 oCC.papoCurves[iGeom]->Value(dfDistance - dfLength, poPoint);
320
321 return;
322 }
323
324 dfLength += dfSegLength;
325 }
326 }
327
328 EndPoint( poPoint );
329 }
330
331 /************************************************************************/
332 /* CurveToLineInternal() */
333 /************************************************************************/
334
335 OGRLineString *
CurveToLineInternal(double dfMaxAngleStepSizeDegrees,const char * const * papszOptions,int bIsLinearRing) const336 OGRCompoundCurve::CurveToLineInternal( double dfMaxAngleStepSizeDegrees,
337 const char* const* papszOptions,
338 int bIsLinearRing ) const
339 {
340 OGRLineString* const poLine = bIsLinearRing
341 ? new OGRLinearRing()
342 : new OGRLineString();
343 poLine->assignSpatialReference(getSpatialReference());
344 for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ )
345 {
346 OGRLineString* poSubLS =
347 oCC.papoCurves[iGeom]->CurveToLine(dfMaxAngleStepSizeDegrees,
348 papszOptions);
349 poLine->addSubLineString(poSubLS, (iGeom == 0) ? 0 : 1);
350 delete poSubLS;
351 }
352 return poLine;
353 }
354
355 /************************************************************************/
356 /* CurveToLine() */
357 /************************************************************************/
358
359 OGRLineString *
CurveToLine(double dfMaxAngleStepSizeDegrees,const char * const * papszOptions) const360 OGRCompoundCurve::CurveToLine( double dfMaxAngleStepSizeDegrees,
361 const char* const* papszOptions ) const
362 {
363 return CurveToLineInternal(dfMaxAngleStepSizeDegrees, papszOptions, FALSE);
364 }
365
366 /************************************************************************/
367 /* Equals() */
368 /************************************************************************/
369
Equals(const OGRGeometry * poOther) const370 OGRBoolean OGRCompoundCurve::Equals( const OGRGeometry *poOther ) const
371 {
372 if( poOther == this )
373 return TRUE;
374
375 if( poOther->getGeometryType() != getGeometryType() )
376 return FALSE;
377
378 return oCC.Equals(&(poOther->toCompoundCurve()->oCC));
379 }
380
381 /************************************************************************/
382 /* setCoordinateDimension() */
383 /************************************************************************/
384
setCoordinateDimension(int nNewDimension)385 void OGRCompoundCurve::setCoordinateDimension( int nNewDimension )
386 {
387 oCC.setCoordinateDimension( this, nNewDimension );
388 }
389
set3D(OGRBoolean bIs3D)390 void OGRCompoundCurve::set3D( OGRBoolean bIs3D )
391 {
392 oCC.set3D(this, bIs3D);
393 }
394
setMeasured(OGRBoolean bIsMeasured)395 void OGRCompoundCurve::setMeasured( OGRBoolean bIsMeasured )
396 {
397 oCC.setMeasured(this, bIsMeasured);
398 }
399
400 /************************************************************************/
401 /* assignSpatialReference() */
402 /************************************************************************/
403
assignSpatialReference(OGRSpatialReference * poSR)404 void OGRCompoundCurve::assignSpatialReference( OGRSpatialReference * poSR )
405 {
406 oCC.assignSpatialReference(this, poSR);
407 }
408
409 /************************************************************************/
410 /* getNumCurves() */
411 /************************************************************************/
412
413 /**
414 * \brief Return the number of curves.
415 *
416 * Note that the number of curves making this compound curve.
417 *
418 * Relates to the ISO SQL/MM ST_NumCurves() function.
419 *
420 * @return number of curves.
421 */
422
getNumCurves() const423 int OGRCompoundCurve::getNumCurves() const
424 {
425 return oCC.nCurveCount;
426 }
427
428 /************************************************************************/
429 /* getCurve() */
430 /************************************************************************/
431
432 /**
433 * \brief Fetch reference to indicated internal ring.
434 *
435 * Note that the returned curve pointer is to an internal data object of the
436 * OGRCompoundCurve. It should not be modified or deleted by the application,
437 * and the pointer is only valid till the polygon is next modified. Use the
438 * OGRGeometry::clone() method to make a separate copy within the application.
439 *
440 * Relates to the ISO SQL/MM ST_CurveN() function.
441 *
442 * @param iRing curve index from 0 to getNumCurves() - 1.
443 *
444 * @return pointer to curve. May be NULL.
445 */
446
getCurve(int iRing)447 OGRCurve *OGRCompoundCurve::getCurve( int iRing )
448 {
449 return oCC.getCurve(iRing);
450 }
451
452 /************************************************************************/
453 /* getCurve() */
454 /************************************************************************/
455
456 /**
457 * \brief Fetch reference to indicated internal ring.
458 *
459 * Note that the returned curve pointer is to an internal data object of the
460 * OGRCompoundCurve. It should not be modified or deleted by the application,
461 * and the pointer is only valid till the polygon is next modified. Use the
462 * OGRGeometry::clone() method to make a separate copy within the application.
463 *
464 * Relates to the ISO SQL/MM ST_CurveN() function.
465 *
466 * @param iCurve curve index from 0 to getNumCurves() - 1.
467 *
468 * @return pointer to curve. May be NULL.
469 */
470
getCurve(int iCurve) const471 const OGRCurve *OGRCompoundCurve::getCurve( int iCurve ) const
472 {
473 return oCC.getCurve(iCurve);
474 }
475
476 /************************************************************************/
477 /* stealCurve() */
478 /************************************************************************/
479
480 /**
481 * \brief "Steal" reference to curve.
482 *
483 * @param iCurve curve index from 0 to getNumCurves() - 1.
484 *
485 * @return pointer to curve. May be NULL.
486 */
487
stealCurve(int iCurve)488 OGRCurve* OGRCompoundCurve::stealCurve( int iCurve )
489 {
490 return oCC.stealCurve(iCurve);
491 }
492
493 /************************************************************************/
494 /* addCurve() */
495 /************************************************************************/
496
497 /**
498 * \brief Add a curve to the container.
499 *
500 * The passed geometry is cloned to make an internal copy.
501 *
502 * There is no ISO SQL/MM analog to this method.
503 *
504 * This method is the same as the C function OGR_G_AddGeometry().
505 *
506 * @param poCurve geometry to add to the container.
507 * @param dfToleranceEps relative tolerance when checking that the first point of a
508 * segment matches then end point of the previous one.
509 * Default value: 1e-14.
510 *
511 * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error
512 * (for example if curves are not contiguous)
513 */
514
addCurve(OGRCurve * poCurve,double dfToleranceEps)515 OGRErr OGRCompoundCurve::addCurve( OGRCurve* poCurve, double dfToleranceEps )
516 {
517 OGRCurve* poClonedCurve = poCurve->clone();
518 const OGRErr eErr = addCurveDirectly( poClonedCurve, dfToleranceEps );
519 if( eErr != OGRERR_NONE )
520 delete poClonedCurve;
521 return eErr;
522 }
523
524 /************************************************************************/
525 /* addCurveDirectly() */
526 /************************************************************************/
527
528 /**
529 * \brief Add a curve directly to the container.
530 *
531 * Ownership of the passed geometry is taken by the container rather than
532 * cloning as addCurve() does.
533 *
534 * There is no ISO SQL/MM analog to this method.
535 *
536 * This method is the same as the C function OGR_G_AddGeometryDirectly().
537 *
538 * @param poCurve geometry to add to the container.
539 * @param dfToleranceEps relative tolerance when checking that the first point of a
540 * segment matches then end point of the previous one.
541 * Default value: 1e-14.
542 *
543 * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error
544 * (for example if curves are not contiguous)
545 */
addCurveDirectly(OGRCurve * poCurve,double dfToleranceEps)546 OGRErr OGRCompoundCurve::addCurveDirectly( OGRCurve* poCurve,
547 double dfToleranceEps )
548 {
549 return addCurveDirectlyInternal(poCurve, dfToleranceEps, TRUE );
550 }
551
addCurveDirectlyInternal(OGRCurve * poCurve,double dfToleranceEps,int bNeedRealloc)552 OGRErr OGRCompoundCurve::addCurveDirectlyInternal( OGRCurve* poCurve,
553 double dfToleranceEps,
554 int bNeedRealloc )
555 {
556 if( poCurve->getNumPoints() == 1 )
557 {
558 CPLError(CE_Failure, CPLE_AppDefined,
559 "Invalid curve: not enough points");
560 return OGRERR_FAILURE;
561 }
562
563 const OGRwkbGeometryType eCurveType =
564 wkbFlatten(poCurve->getGeometryType());
565 if( EQUAL(poCurve->getGeometryName(), "LINEARRING") )
566 {
567 CPLError(CE_Failure, CPLE_AppDefined, "Linearring not allowed.");
568 return OGRERR_FAILURE;
569 }
570 else if( eCurveType == wkbCompoundCurve )
571 {
572 CPLError(CE_Failure, CPLE_AppDefined,
573 "Cannot add a compound curve inside a compound curve");
574 return OGRERR_FAILURE;
575 }
576
577 if( oCC.nCurveCount > 0 )
578 {
579 if( oCC.papoCurves[oCC.nCurveCount-1]->IsEmpty() ||
580 poCurve->IsEmpty() )
581 {
582 CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves");
583 return OGRERR_FAILURE;
584 }
585
586 OGRPoint oEnd;
587 OGRPoint start;
588 oCC.papoCurves[oCC.nCurveCount-1]->EndPoint(&oEnd);
589 poCurve->StartPoint(&start);
590 if( fabs(oEnd.getX() - start.getX()) > dfToleranceEps * fabs(start.getX()) ||
591 fabs(oEnd.getY() - start.getY()) > dfToleranceEps * fabs(start.getY()) ||
592 fabs(oEnd.getZ() - start.getZ()) > dfToleranceEps * fabs(start.getZ()) )
593 {
594 poCurve->EndPoint(&start);
595 if( fabs(oEnd.getX() - start.getX()) > dfToleranceEps * fabs(start.getX()) ||
596 fabs(oEnd.getY() - start.getY()) > dfToleranceEps * fabs(start.getY()) ||
597 fabs(oEnd.getZ() - start.getZ()) > dfToleranceEps * fabs(start.getZ()) )
598 {
599 CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves");
600 return OGRERR_FAILURE;
601 }
602
603 CPLDebug("GML", "reversing curve");
604 poCurve->toSimpleCurve()->reversePoints();
605 }
606 // Patch so that it matches exactly.
607 poCurve->toSimpleCurve()->setPoint(0, &oEnd);
608 }
609
610 return oCC.addCurveDirectly(this, poCurve, bNeedRealloc);
611 }
612
613 /************************************************************************/
614 /* transform() */
615 /************************************************************************/
616
transform(OGRCoordinateTransformation * poCT)617 OGRErr OGRCompoundCurve::transform( OGRCoordinateTransformation *poCT )
618 {
619 return oCC.transform(this, poCT);
620 }
621
622 /************************************************************************/
623 /* flattenTo2D() */
624 /************************************************************************/
625
flattenTo2D()626 void OGRCompoundCurve::flattenTo2D()
627 {
628 oCC.flattenTo2D(this);
629 }
630
631 /************************************************************************/
632 /* segmentize() */
633 /************************************************************************/
634
segmentize(double dfMaxLength)635 void OGRCompoundCurve::segmentize( double dfMaxLength )
636 {
637 oCC.segmentize(dfMaxLength);
638 }
639
640 /************************************************************************/
641 /* swapXY() */
642 /************************************************************************/
643
swapXY()644 void OGRCompoundCurve::swapXY()
645 {
646 oCC.swapXY();
647 }
648
649 /************************************************************************/
650 /* hasCurveGeometry() */
651 /************************************************************************/
652
hasCurveGeometry(int bLookForNonLinear) const653 OGRBoolean OGRCompoundCurve::hasCurveGeometry( int bLookForNonLinear ) const
654 {
655 if( bLookForNonLinear )
656 {
657 return oCC.hasCurveGeometry(bLookForNonLinear);
658 }
659
660 return TRUE;
661 }
662
663 /************************************************************************/
664 /* getLinearGeometry() */
665 /************************************************************************/
666
667 OGRGeometry *
getLinearGeometry(double dfMaxAngleStepSizeDegrees,const char * const * papszOptions) const668 OGRCompoundCurve::getLinearGeometry( double dfMaxAngleStepSizeDegrees,
669 const char* const* papszOptions ) const
670 {
671 return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions);
672 }
673
674 /************************************************************************/
675 /* getNumPoints() */
676 /************************************************************************/
677
getNumPoints() const678 int OGRCompoundCurve::getNumPoints() const
679 {
680 int nPoints = 0;
681 for( int i = 0; i < oCC.nCurveCount; i++ )
682 {
683 nPoints += oCC.papoCurves[i]->getNumPoints();
684 if( i != 0 )
685 nPoints--;
686 }
687 return nPoints;
688 }
689
690 /************************************************************************/
691 /* OGRCompoundCurvePointIterator */
692 /************************************************************************/
693
694 class OGRCompoundCurvePointIterator final: public OGRPointIterator
695 {
696 CPL_DISALLOW_COPY_ASSIGN(OGRCompoundCurvePointIterator)
697
698 const OGRCompoundCurve *poCC = nullptr;
699 int iCurCurve = 0;
700 OGRPointIterator *poCurveIter = nullptr;
701
702 public:
OGRCompoundCurvePointIterator(const OGRCompoundCurve * poCCIn)703 explicit OGRCompoundCurvePointIterator( const OGRCompoundCurve* poCCIn ) :
704 poCC(poCCIn) {}
~OGRCompoundCurvePointIterator()705 ~OGRCompoundCurvePointIterator() override { delete poCurveIter; }
706
707 OGRBoolean getNextPoint( OGRPoint* p ) override;
708 };
709
710 /************************************************************************/
711 /* getNextPoint() */
712 /************************************************************************/
713
getNextPoint(OGRPoint * p)714 OGRBoolean OGRCompoundCurvePointIterator::getNextPoint( OGRPoint* p )
715 {
716 if( iCurCurve == poCC->getNumCurves() )
717 return FALSE;
718 if( poCurveIter == nullptr )
719 poCurveIter = poCC->getCurve(0)->getPointIterator();
720 if( !poCurveIter->getNextPoint(p) )
721 {
722 iCurCurve++;
723 if( iCurCurve == poCC->getNumCurves() )
724 return FALSE;
725 delete poCurveIter;
726 poCurveIter = poCC->getCurve(iCurCurve)->getPointIterator();
727 // Skip first point.
728 return poCurveIter->getNextPoint(p) &&
729 poCurveIter->getNextPoint(p);
730 }
731 return TRUE;
732 }
733
734 /************************************************************************/
735 /* getPointIterator() */
736 /************************************************************************/
737
getPointIterator() const738 OGRPointIterator* OGRCompoundCurve::getPointIterator() const
739 {
740 return new OGRCompoundCurvePointIterator(this);
741 }
742
743 /************************************************************************/
744 /* CastToLineString() */
745 /************************************************************************/
746
747 //! @cond Doxygen_Suppress
CastToLineString(OGRCompoundCurve * poCC)748 OGRLineString* OGRCompoundCurve::CastToLineString( OGRCompoundCurve* poCC )
749 {
750 for( int i = 0; i < poCC->oCC.nCurveCount; i++ )
751 {
752 poCC->oCC.papoCurves[i] =
753 OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]);
754 if( poCC->oCC.papoCurves[i] == nullptr )
755 {
756 delete poCC;
757 return nullptr;
758 }
759 }
760
761 if( poCC->oCC.nCurveCount == 1 )
762 {
763 OGRLineString* poLS = poCC->oCC.papoCurves[0]->toLineString();
764 poLS->assignSpatialReference(poCC->getSpatialReference());
765 poCC->oCC.papoCurves[0] = nullptr;
766 delete poCC;
767 return poLS;
768 }
769
770 OGRLineString* poLS = poCC->CurveToLineInternal(0, nullptr, FALSE);
771 delete poCC;
772 return poLS;
773 }
774
775 /************************************************************************/
776 /* CastToLinearRing() */
777 /************************************************************************/
778
779 /**
780 * \brief Cast to linear ring.
781 *
782 * The passed in geometry is consumed and a new one returned (or NULL in case
783 * of failure)
784 *
785 * @param poCC the input geometry - ownership is passed to the method.
786 * @return new geometry.
787 */
788
CastToLinearRing(OGRCompoundCurve * poCC)789 OGRLinearRing* OGRCompoundCurve::CastToLinearRing( OGRCompoundCurve* poCC )
790 {
791 for( int i = 0; i < poCC->oCC.nCurveCount; i++ )
792 {
793 poCC->oCC.papoCurves[i] =
794 OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]);
795 if( poCC->oCC.papoCurves[i] == nullptr )
796 {
797 delete poCC;
798 return nullptr;
799 }
800 }
801
802 if( poCC->oCC.nCurveCount == 1 )
803 {
804 OGRLinearRing* poLR =
805 OGRCurve::CastToLinearRing( poCC->oCC.papoCurves[0] );
806 if( poLR != nullptr )
807 {
808 poLR->assignSpatialReference(poCC->getSpatialReference());
809 }
810 poCC->oCC.papoCurves[0] = nullptr;
811 delete poCC;
812 return poLR;
813 }
814
815 OGRLinearRing* poLR =
816 poCC->CurveToLineInternal(0, nullptr, TRUE)->toLinearRing();
817 delete poCC;
818 return poLR;
819 }
820
821 /************************************************************************/
822 /* GetCasterToLineString() */
823 /************************************************************************/
824
CasterToLineString(OGRCurve * poCurve)825 OGRLineString* OGRCompoundCurve::CasterToLineString( OGRCurve* poCurve )
826 {
827 OGRCompoundCurve* poCC = poCurve->toCompoundCurve();
828 return OGRCompoundCurve::CastToLineString(poCC);
829 }
830
GetCasterToLineString() const831 OGRCurveCasterToLineString OGRCompoundCurve::GetCasterToLineString() const {
832 return OGRCompoundCurve::CasterToLineString;
833 }
834
835 /************************************************************************/
836 /* GetCasterToLinearRing() */
837 /************************************************************************/
838
CasterToLinearRing(OGRCurve * poCurve)839 OGRLinearRing* OGRCompoundCurve::CasterToLinearRing( OGRCurve* poCurve )
840 {
841 OGRCompoundCurve* poCC = poCurve->toCompoundCurve();
842 return OGRCompoundCurve::CastToLinearRing(poCC);
843 }
844
GetCasterToLinearRing() const845 OGRCurveCasterToLinearRing OGRCompoundCurve::GetCasterToLinearRing() const {
846 return OGRCompoundCurve::CasterToLinearRing;
847 }
848 //! @endcond
849
850 /************************************************************************/
851 /* get_Area() */
852 /************************************************************************/
853
get_Area() const854 double OGRCompoundCurve::get_Area() const
855 {
856 if( IsEmpty() || !get_IsClosed() )
857 return 0;
858
859 // Optimization for convex rings.
860 if( IsConvex() )
861 {
862 // Compute area of shape without the circular segments.
863 OGRPointIterator* poIter = getPointIterator();
864 OGRLineString oLS;
865 oLS.setNumPoints( getNumPoints() );
866 OGRPoint p;
867 for( int i = 0; poIter->getNextPoint(&p); i++ )
868 {
869 oLS.setPoint( i, p.getX(), p.getY() );
870 }
871 double dfArea = oLS.get_Area();
872 delete poIter;
873
874 // Add the area of the spherical segments.
875 dfArea += get_AreaOfCurveSegments();
876
877 return dfArea;
878 }
879
880 OGRLineString* poLS = CurveToLine();
881 double dfArea = poLS->get_Area();
882 delete poLS;
883
884 return dfArea;
885 }
886
887 /************************************************************************/
888 /* get_AreaOfCurveSegments() */
889 /************************************************************************/
890
891 /** Return area of curve segments
892 * @return area.
893 */
get_AreaOfCurveSegments() const894 double OGRCompoundCurve::get_AreaOfCurveSegments() const
895 {
896 double dfArea = 0;
897 for( int i = 0; i < getNumCurves(); i++ )
898 {
899 const OGRCurve* poPart = getCurve(i);
900 dfArea += poPart->get_AreaOfCurveSegments();
901 }
902 return dfArea;
903 }
904