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