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