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