Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGRCurve geometry class.
5 : * Author: Frank Warmerdam, warmerda@home.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "ogr_geometry.h"
30 : #include "ogr_p.h"
31 :
32 : //! @cond Doxygen_Suppress
33 :
34 : /************************************************************************/
35 : /* OGRCurve() */
36 : /************************************************************************/
37 :
38 : OGRCurve::OGRCurve() = default;
39 :
40 : /************************************************************************/
41 : /* ~OGRCurve() */
42 : /************************************************************************/
43 :
44 : OGRCurve::~OGRCurve() = default;
45 :
46 : /************************************************************************/
47 : /* OGRCurve( const OGRCurve& ) */
48 : /************************************************************************/
49 :
50 : OGRCurve::OGRCurve(const OGRCurve &) = default;
51 :
52 : /************************************************************************/
53 : /* operator=( const OGRCurve& ) */
54 : /************************************************************************/
55 :
56 16 : OGRCurve &OGRCurve::operator=(const OGRCurve &other)
57 : {
58 16 : if (this != &other)
59 : {
60 16 : OGRGeometry::operator=(other);
61 : }
62 16 : return *this;
63 : }
64 :
65 : //! @endcond
66 :
67 : /************************************************************************/
68 : /* getDimension() */
69 : /************************************************************************/
70 :
71 86 : int OGRCurve::getDimension() const
72 :
73 : {
74 86 : return 1;
75 : }
76 :
77 : /************************************************************************/
78 : /* get_IsClosed() */
79 : /************************************************************************/
80 :
81 : /**
82 : * \brief Return TRUE if curve is closed.
83 : *
84 : * Tests if a curve is closed. A curve is closed if its start point is
85 : * equal to its end point.
86 : *
87 : * For equality tests, the M dimension is ignored.
88 : *
89 : * This method relates to the SFCOM ICurve::get_IsClosed() method.
90 : *
91 : * @return TRUE if closed, else FALSE.
92 : */
93 :
94 129832 : int OGRCurve::get_IsClosed() const
95 :
96 : {
97 259664 : OGRPoint oStartPoint;
98 129830 : StartPoint(&oStartPoint);
99 :
100 259663 : OGRPoint oEndPoint;
101 129830 : EndPoint(&oEndPoint);
102 :
103 129833 : if (oStartPoint.Is3D() && oEndPoint.Is3D())
104 : {
105 : // XYZ type
106 39892 : if (oStartPoint.getX() == oEndPoint.getX() &&
107 39892 : oStartPoint.getY() == oEndPoint.getY() &&
108 19065 : oStartPoint.getZ() == oEndPoint.getZ())
109 : {
110 18466 : return TRUE;
111 : }
112 : else
113 1787 : return FALSE;
114 : }
115 :
116 : // one of the points is 3D
117 219158 : else if (((oStartPoint.Is3D() & oEndPoint.Is3D()) == 0) &&
118 109578 : ((oStartPoint.Is3D() | oEndPoint.Is3D()) == 1))
119 : {
120 0 : return FALSE;
121 : }
122 :
123 : else
124 : {
125 : // XY type
126 216090 : if (oStartPoint.getX() == oEndPoint.getX() &&
127 106510 : oStartPoint.getY() == oEndPoint.getY())
128 105640 : return TRUE;
129 : else
130 3939 : return FALSE;
131 : }
132 : }
133 :
134 : /**
135 : * \fn double OGRCurve::get_Length() const;
136 : *
137 : * \brief Returns the length of the curve.
138 : *
139 : * This method relates to the SFCOM ICurve::get_Length() method.
140 : *
141 : * @return the length of the curve, zero if the curve hasn't been
142 : * initialized.
143 : */
144 :
145 : /**
146 : * \fn void OGRCurve::StartPoint( OGRPoint * poPoint ) const;
147 : *
148 : * \brief Return the curve start point.
149 : *
150 : * This method relates to the SF COM ICurve::get_StartPoint() method.
151 : *
152 : * @param poPoint the point to be assigned the start location.
153 : */
154 :
155 : /**
156 : * \fn void OGRCurve::EndPoint( OGRPoint * poPoint ) const;
157 : *
158 : * \brief Return the curve end point.
159 : *
160 : * This method relates to the SF COM ICurve::get_EndPoint() method.
161 : *
162 : * @param poPoint the point to be assigned the end location.
163 : */
164 :
165 : /**
166 : * \fn void OGRCurve::Value( double dfDistance, OGRPoint * poPoint ) const;
167 : *
168 : * \brief Fetch point at given distance along curve.
169 : *
170 : * This method relates to the SF COM ICurve::get_Value() method.
171 : *
172 : * This function is the same as the C function OGR_G_Value().
173 : *
174 : * @param dfDistance distance along the curve at which to sample position.
175 : * This distance should be between zero and get_Length()
176 : * for this curve.
177 : * @param poPoint the point to be assigned the curve position.
178 : */
179 :
180 : /**
181 : * \fn OGRLineString* OGRCurve::CurveToLine( double dfMaxAngleStepSizeDegrees,
182 : * const char* const* papszOptions ) const;
183 : *
184 : * \brief Return a linestring from a curve geometry.
185 : *
186 : * The returned geometry is a new instance whose ownership belongs to the
187 : * caller.
188 : *
189 : * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be
190 : * used. This is currently 4 degrees unless the user has overridden the
191 : * value with the OGR_ARC_STEPSIZE configuration variable.
192 : *
193 : * This method relates to the ISO SQL/MM Part 3 ICurve::CurveToLine() method.
194 : *
195 : * This function is the same as C function OGR_G_CurveToLine().
196 : *
197 : * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
198 : * arc, zero to use the default setting.
199 : * @param papszOptions options as a null-terminated list of strings or NULL.
200 : * See OGRGeometryFactory::curveToLineString() for valid
201 : * options.
202 : *
203 : * @return a line string approximating the curve
204 : *
205 : * @since GDAL 2.0
206 : */
207 :
208 : /**
209 : * \fn int OGRCurve::getNumPoints() const;
210 : *
211 : * \brief Return the number of points of a curve geometry.
212 : *
213 : *
214 : * This method, as a method of OGRCurve, does not relate to a standard.
215 : * For circular strings or linestrings, it returns the number of points,
216 : * conforming to SF COM NumPoints().
217 : * For compound curves, it returns the sum of the number of points of each
218 : * of its components (non including intermediate starting/ending points of
219 : * the different parts).
220 : *
221 : * @return the number of points of the curve.
222 : *
223 : * @since GDAL 2.0
224 : */
225 :
226 : /**
227 : * \fn OGRPointIterator* OGRCurve::getPointIterator() const;
228 : *
229 : * \brief Returns a point iterator over the curve.
230 : *
231 : * The curve must not be modified while an iterator exists on it.
232 : *
233 : * The iterator must be destroyed with OGRPointIterator::destroy().
234 : *
235 : * @return a point iterator over the curve.
236 : *
237 : * @since GDAL 2.0
238 : */
239 :
240 : /**
241 : * \fn double OGRCurve::get_Area() const;
242 : *
243 : * \brief Get the area of the (closed) curve.
244 : *
245 : * This method is designed to be used by OGRCurvePolygon::get_Area().
246 : *
247 : * @return the area of the geometry in square units of the spatial reference
248 : * system in use.
249 : *
250 : * @see get_GeodesicArea() for an alternative method returning areas
251 : * computed on the ellipsoid, an in square meters.
252 : *
253 : * @since GDAL 2.0
254 : */
255 :
256 : /**
257 : * \fn double OGRCurve::get_GeodesicArea(const OGRSpatialReference* poSRSOverride = nullptr) const;
258 : *
259 : * \brief Get the area of the (closed) curve, considered as a surface on the
260 : * underlying ellipsoid of the SRS attached to the geometry.
261 : *
262 : * This method is designed to be used by OGRCurvePolygon::get_GeodesicArea().
263 : *
264 : * The returned area will always be in square meters, and assumes that
265 : * polygon edges describe geodesic lines on the ellipsoid.
266 : *
267 : * Note that geometries with circular arcs will be linearized in their original
268 : * coordinate space first, so the resulting geodesic area will be an
269 : * approximation.
270 : *
271 : * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
272 : * @return the area of the geometry in square meters, or a negative value in case
273 : * of error.
274 : *
275 : * @see get_Area() for an alternative method returning areas computed in
276 : * 2D Cartesian space.
277 : *
278 : * @since GDAL 3.9
279 : */
280 :
281 : /**
282 : * \fn double OGRCurve::get_AreaOfCurveSegments() const;
283 : *
284 : * \brief Get the area of the purely curve portions of a (closed) curve.
285 : *
286 : * This method is designed to be used on a closed convex curve.
287 : *
288 : * @return the area of the feature in square units of the spatial reference
289 : * system in use.
290 : *
291 : * @since GDAL 2.0
292 : */
293 :
294 : /************************************************************************/
295 : /* IsConvex() */
296 : /************************************************************************/
297 :
298 : /**
299 : * \brief Returns if a (closed) curve forms a convex shape.
300 : *
301 : * @return TRUE if the curve forms a convex shape.
302 : *
303 : * @since GDAL 2.0
304 : */
305 :
306 42 : OGRBoolean OGRCurve::IsConvex() const
307 : {
308 42 : bool bRet = true;
309 42 : OGRPointIterator *poPointIter = getPointIterator();
310 84 : OGRPoint p1;
311 42 : OGRPoint p2;
312 42 : if (poPointIter->getNextPoint(&p1) && poPointIter->getNextPoint(&p2))
313 : {
314 84 : OGRPoint p3;
315 168 : while (poPointIter->getNextPoint(&p3))
316 : {
317 : const double crossproduct =
318 140 : (p2.getX() - p1.getX()) * (p3.getY() - p2.getY()) -
319 140 : (p2.getY() - p1.getY()) * (p3.getX() - p2.getX());
320 140 : if (crossproduct > 0)
321 : {
322 14 : bRet = false;
323 14 : break;
324 : }
325 126 : p1.setX(p2.getX());
326 126 : p1.setY(p2.getY());
327 126 : p2.setX(p3.getX());
328 126 : p2.setY(p3.getY());
329 : }
330 : }
331 42 : delete poPointIter;
332 84 : return bRet;
333 : }
334 :
335 : /************************************************************************/
336 : /* CastToCompoundCurve() */
337 : /************************************************************************/
338 :
339 : /**
340 : * \brief Cast to compound curve
341 : *
342 : * The passed in geometry is consumed and a new one returned (or NULL in case
343 : * of failure)
344 : *
345 : * @param poCurve the input geometry - ownership is passed to the method.
346 : * @return new geometry
347 : *
348 : * @since GDAL 2.0
349 : */
350 :
351 30 : OGRCompoundCurve *OGRCurve::CastToCompoundCurve(OGRCurve *poCurve)
352 : {
353 30 : OGRCompoundCurve *poCC = new OGRCompoundCurve();
354 30 : if (wkbFlatten(poCurve->getGeometryType()) == wkbLineString)
355 21 : poCurve = CastToLineString(poCurve);
356 30 : if (!poCurve->IsEmpty() && poCC->addCurveDirectly(poCurve) != OGRERR_NONE)
357 : {
358 2 : delete poCC;
359 2 : delete poCurve;
360 2 : return nullptr;
361 : }
362 28 : poCC->assignSpatialReference(poCurve->getSpatialReference());
363 28 : return poCC;
364 : }
365 :
366 : /************************************************************************/
367 : /* CastToLineString() */
368 : /************************************************************************/
369 :
370 : /**
371 : * \brief Cast to linestring
372 : *
373 : * The passed in geometry is consumed and a new one returned (or NULL in case
374 : * of failure)
375 : *
376 : * @param poCurve the input geometry - ownership is passed to the method.
377 : * @return new geometry.
378 : *
379 : * @since GDAL 2.0
380 : */
381 :
382 197 : OGRLineString *OGRCurve::CastToLineString(OGRCurve *poCurve)
383 : {
384 197 : OGRCurveCasterToLineString pfn = poCurve->GetCasterToLineString();
385 197 : return pfn(poCurve);
386 : }
387 :
388 : /************************************************************************/
389 : /* CastToLinearRing() */
390 : /************************************************************************/
391 :
392 : /**
393 : * \brief Cast to linear ring
394 : *
395 : * The passed in geometry is consumed and a new one returned (or NULL in case
396 : * of failure)
397 : *
398 : * @param poCurve the input geometry - ownership is passed to the method.
399 : * @return new geometry.
400 : *
401 : * @since GDAL 2.0
402 : */
403 :
404 550 : OGRLinearRing *OGRCurve::CastToLinearRing(OGRCurve *poCurve)
405 : {
406 550 : OGRCurveCasterToLinearRing pfn = poCurve->GetCasterToLinearRing();
407 550 : return pfn(poCurve);
408 : }
409 :
410 : /************************************************************************/
411 : /* ContainsPoint() */
412 : /************************************************************************/
413 :
414 : /**
415 : * \brief Returns if a point is contained in a (closed) curve.
416 : *
417 : * Final users should use OGRGeometry::Contains() instead.
418 : *
419 : * @param p the point to test
420 : * @return TRUE if it is inside the curve, FALSE otherwise or -1 if unknown.
421 : *
422 : * @since GDAL 2.0
423 : */
424 :
425 9 : int OGRCurve::ContainsPoint(CPL_UNUSED const OGRPoint *p) const
426 : {
427 9 : return -1;
428 : }
429 :
430 : /************************************************************************/
431 : /* IntersectsPoint() */
432 : /************************************************************************/
433 :
434 : /**
435 : * \brief Returns if a point intersects a (closed) curve.
436 : *
437 : * Final users should use OGRGeometry::Intersects() instead.
438 : *
439 : * @param p the point to test
440 : * @return TRUE if it intersects the curve, FALSE otherwise or -1 if unknown.
441 : *
442 : * @since GDAL 2.3
443 : */
444 :
445 1 : int OGRCurve::IntersectsPoint(CPL_UNUSED const OGRPoint *p) const
446 : {
447 1 : return -1;
448 : }
449 :
450 : /************************************************************************/
451 : /* ~OGRPointIterator() */
452 : /************************************************************************/
453 :
454 : OGRPointIterator::~OGRPointIterator() = default;
455 :
456 : /**
457 : * \fn OGRBoolean OGRPointIterator::getNextPoint(OGRPoint* p);
458 : *
459 : * \brief Returns the next point followed by the iterator.
460 : *
461 : * @param p point to fill.
462 : *
463 : * @return TRUE in case of success, or FALSE if the end of the curve is reached.
464 : *
465 : * @since GDAL 2.0
466 : */
467 :
468 : /************************************************************************/
469 : /* destroy() */
470 : /************************************************************************/
471 :
472 : /**
473 : * \brief Destroys a point iterator.
474 : *
475 : * @since GDAL 2.0
476 : */
477 0 : void OGRPointIterator::destroy(OGRPointIterator *poIter)
478 : {
479 0 : delete poIter;
480 0 : }
481 :
482 : /************************************************************************/
483 : /* OGRSimpleCurve::Iterator */
484 : /************************************************************************/
485 :
486 2 : void OGRIteratedPoint::setX(double xIn)
487 : {
488 2 : OGRPoint::setX(xIn);
489 2 : m_poCurve->setPoint(m_nPos, xIn, getY());
490 2 : }
491 :
492 2 : void OGRIteratedPoint::setY(double yIn)
493 : {
494 2 : OGRPoint::setY(yIn);
495 2 : m_poCurve->setPoint(m_nPos, getX(), yIn);
496 2 : }
497 :
498 2 : void OGRIteratedPoint::setZ(double zIn)
499 : {
500 2 : OGRPoint::setZ(zIn);
501 2 : m_poCurve->setZ(m_nPos, zIn);
502 2 : }
503 :
504 2 : void OGRIteratedPoint::setM(double mIn)
505 : {
506 2 : OGRPoint::setM(mIn);
507 2 : m_poCurve->setM(m_nPos, mIn);
508 2 : }
509 :
510 : struct OGRSimpleCurve::Iterator::Private
511 : {
512 : CPL_DISALLOW_COPY_ASSIGN(Private)
513 224 : Private() = default;
514 :
515 : bool m_bUpdateChecked = true;
516 : OGRIteratedPoint m_oPoint{};
517 : };
518 :
519 2426 : void OGRSimpleCurve::Iterator::update()
520 : {
521 2426 : if (!m_poPrivate->m_bUpdateChecked)
522 : {
523 1100 : OGRPoint oPointBefore;
524 1100 : m_poPrivate->m_oPoint.m_poCurve->getPoint(m_poPrivate->m_oPoint.m_nPos,
525 : &oPointBefore);
526 1100 : if (oPointBefore != m_poPrivate->m_oPoint)
527 : {
528 897 : if (m_poPrivate->m_oPoint.Is3D())
529 739 : m_poPrivate->m_oPoint.m_poCurve->set3D(true);
530 897 : if (m_poPrivate->m_oPoint.IsMeasured())
531 4 : m_poPrivate->m_oPoint.m_poCurve->setMeasured(true);
532 1794 : m_poPrivate->m_oPoint.m_poCurve->setPoint(
533 897 : m_poPrivate->m_oPoint.m_nPos, &m_poPrivate->m_oPoint);
534 : }
535 1100 : m_poPrivate->m_bUpdateChecked = true;
536 : }
537 2426 : }
538 :
539 224 : OGRSimpleCurve::Iterator::Iterator(OGRSimpleCurve *poSelf, int nPos)
540 224 : : m_poPrivate(new Private())
541 : {
542 224 : m_poPrivate->m_oPoint.m_poCurve = poSelf;
543 224 : m_poPrivate->m_oPoint.m_nPos = nPos;
544 224 : }
545 :
546 224 : OGRSimpleCurve::Iterator::~Iterator()
547 : {
548 224 : update();
549 224 : }
550 :
551 1100 : OGRIteratedPoint &OGRSimpleCurve::Iterator::operator*()
552 : {
553 1100 : update();
554 2200 : m_poPrivate->m_oPoint.m_poCurve->getPoint(m_poPrivate->m_oPoint.m_nPos,
555 1100 : &m_poPrivate->m_oPoint);
556 1100 : m_poPrivate->m_bUpdateChecked = false;
557 1100 : return m_poPrivate->m_oPoint;
558 : }
559 :
560 1102 : OGRSimpleCurve::Iterator &OGRSimpleCurve::Iterator::operator++()
561 : {
562 1102 : update();
563 1102 : ++m_poPrivate->m_oPoint.m_nPos;
564 1102 : return *this;
565 : }
566 :
567 1215 : bool OGRSimpleCurve::Iterator::operator!=(const Iterator &it) const
568 : {
569 1215 : return m_poPrivate->m_oPoint.m_nPos != it.m_poPrivate->m_oPoint.m_nPos;
570 : }
571 :
572 113 : OGRSimpleCurve::Iterator OGRSimpleCurve::begin()
573 : {
574 113 : return {this, 0};
575 : }
576 :
577 111 : OGRSimpleCurve::Iterator OGRSimpleCurve::end()
578 : {
579 111 : return {this, nPointCount};
580 : }
581 :
582 : /************************************************************************/
583 : /* OGRSimpleCurve::ConstIterator */
584 : /************************************************************************/
585 :
586 : struct OGRSimpleCurve::ConstIterator::Private
587 : {
588 : CPL_DISALLOW_COPY_ASSIGN(Private)
589 46 : Private() = default;
590 :
591 : mutable OGRIteratedPoint m_oPoint{};
592 : const OGRSimpleCurve *m_poSelf = nullptr;
593 : int m_nPos = 0;
594 : };
595 :
596 46 : OGRSimpleCurve::ConstIterator::ConstIterator(const OGRSimpleCurve *poSelf,
597 46 : int nPos)
598 46 : : m_poPrivate(new Private())
599 : {
600 46 : m_poPrivate->m_poSelf = poSelf;
601 46 : m_poPrivate->m_nPos = nPos;
602 46 : }
603 :
604 : OGRSimpleCurve::ConstIterator::~ConstIterator() = default;
605 :
606 64 : const OGRPoint &OGRSimpleCurve::ConstIterator::operator*() const
607 : {
608 128 : m_poPrivate->m_poSelf->getPoint(m_poPrivate->m_nPos,
609 64 : &m_poPrivate->m_oPoint);
610 64 : return m_poPrivate->m_oPoint;
611 : }
612 :
613 64 : OGRSimpleCurve::ConstIterator &OGRSimpleCurve::ConstIterator::operator++()
614 : {
615 64 : ++m_poPrivate->m_nPos;
616 64 : return *this;
617 : }
618 :
619 87 : bool OGRSimpleCurve::ConstIterator::operator!=(const ConstIterator &it) const
620 : {
621 87 : return m_poPrivate->m_nPos != it.m_poPrivate->m_nPos;
622 : }
623 :
624 23 : OGRSimpleCurve::ConstIterator OGRSimpleCurve::begin() const
625 : {
626 23 : return {this, 0};
627 : }
628 :
629 23 : OGRSimpleCurve::ConstIterator OGRSimpleCurve::end() const
630 : {
631 23 : return {this, nPointCount};
632 : }
633 :
634 : /************************************************************************/
635 : /* OGRCurve::ConstIterator */
636 : /************************************************************************/
637 :
638 : struct OGRCurve::ConstIterator::Private
639 : {
640 : CPL_DISALLOW_COPY_ASSIGN(Private)
641 38 : Private() = default;
642 : Private(Private &&) = delete;
643 : Private &operator=(Private &&) = default;
644 :
645 : OGRPoint m_oPoint{};
646 : const OGRCurve *m_poCurve{};
647 : int m_nStep = 0;
648 : std::unique_ptr<OGRPointIterator> m_poIterator{};
649 : };
650 :
651 38 : OGRCurve::ConstIterator::ConstIterator(const OGRCurve *poSelf, bool bStart)
652 38 : : m_poPrivate(new Private())
653 : {
654 38 : m_poPrivate->m_poCurve = poSelf;
655 38 : if (bStart)
656 : {
657 30 : m_poPrivate->m_poIterator.reset(poSelf->getPointIterator());
658 30 : if (!m_poPrivate->m_poIterator->getNextPoint(&m_poPrivate->m_oPoint))
659 : {
660 2 : m_poPrivate->m_nStep = -1;
661 2 : m_poPrivate->m_poIterator.reset();
662 : }
663 : }
664 : else
665 : {
666 8 : m_poPrivate->m_nStep = -1;
667 : }
668 38 : }
669 :
670 0 : OGRCurve::ConstIterator::ConstIterator(ConstIterator &&oOther) noexcept
671 0 : : m_poPrivate(std::move(oOther.m_poPrivate))
672 : {
673 0 : }
674 :
675 : OGRCurve::ConstIterator &
676 4 : OGRCurve::ConstIterator::operator=(ConstIterator &&oOther)
677 : {
678 4 : m_poPrivate = std::move(oOther.m_poPrivate);
679 4 : return *this;
680 : }
681 :
682 : OGRCurve::ConstIterator::~ConstIterator() = default;
683 :
684 119 : const OGRPoint &OGRCurve::ConstIterator::operator*() const
685 : {
686 119 : return m_poPrivate->m_oPoint;
687 : }
688 :
689 83 : OGRCurve::ConstIterator &OGRCurve::ConstIterator::operator++()
690 : {
691 83 : CPLAssert(m_poPrivate->m_nStep >= 0);
692 83 : ++m_poPrivate->m_nStep;
693 83 : if (!m_poPrivate->m_poIterator->getNextPoint(&m_poPrivate->m_oPoint))
694 : {
695 6 : m_poPrivate->m_nStep = -1;
696 6 : m_poPrivate->m_poIterator.reset();
697 : }
698 83 : return *this;
699 : }
700 :
701 24 : bool OGRCurve::ConstIterator::operator!=(const ConstIterator &it) const
702 : {
703 48 : return m_poPrivate->m_poCurve != it.m_poPrivate->m_poCurve ||
704 48 : m_poPrivate->m_nStep != it.m_poPrivate->m_nStep;
705 : }
706 :
707 30 : OGRCurve::ConstIterator OGRCurve::begin() const
708 : {
709 30 : return {this, true};
710 : }
711 :
712 8 : OGRCurve::ConstIterator OGRCurve::end() const
713 : {
714 8 : return {this, false};
715 : }
716 :
717 : /************************************************************************/
718 : /* isClockwise() */
719 : /************************************************************************/
720 :
721 : /**
722 : * \brief Returns TRUE if the ring has clockwise winding (or less than 2 points)
723 : *
724 : * Assumes that the line is closed.
725 : *
726 : * @return TRUE if clockwise otherwise FALSE.
727 : */
728 :
729 18 : int OGRCurve::isClockwise() const
730 :
731 : {
732 : // WARNING: keep in sync OGRLineString::isClockwise(),
733 : // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
734 :
735 18 : const int nPointCount = getNumPoints();
736 18 : if (nPointCount < 3)
737 0 : return TRUE;
738 :
739 18 : bool bUseFallback = false;
740 :
741 : // Find the lowest rightmost vertex.
742 36 : auto oIter = begin();
743 36 : const OGRPoint oStartPoint = *oIter;
744 36 : OGRPoint oPointBefore = oStartPoint;
745 36 : OGRPoint oPointBeforeSel;
746 36 : OGRPoint oPointSel = oStartPoint;
747 36 : OGRPoint oPointNextSel;
748 18 : bool bNextPointIsNextSel = true;
749 18 : int v = 0;
750 :
751 69 : for (int i = 1; i < nPointCount - 1; i++)
752 : {
753 51 : ++oIter;
754 102 : OGRPoint oPointCur = *oIter;
755 51 : if (bNextPointIsNextSel)
756 : {
757 28 : oPointNextSel = oPointCur;
758 28 : bNextPointIsNextSel = false;
759 : }
760 102 : if (oPointCur.getY() < oPointSel.getY() ||
761 51 : (oPointCur.getY() == oPointSel.getY() &&
762 11 : oPointCur.getX() > oPointSel.getX()))
763 : {
764 18 : v = i;
765 18 : oPointBeforeSel = oPointBefore;
766 18 : oPointSel = oPointCur;
767 18 : bUseFallback = false;
768 18 : bNextPointIsNextSel = true;
769 : }
770 37 : else if (oPointCur.getY() == oPointSel.getY() &&
771 4 : oPointCur.getX() == oPointSel.getX())
772 : {
773 : // Two vertex with same coordinates are the lowest rightmost
774 : // vertex. Cannot use that point as the pivot (#5342).
775 4 : bUseFallback = true;
776 : }
777 51 : oPointBefore = oPointCur;
778 : }
779 36 : const OGRPoint oPointN_m2 = *oIter;
780 :
781 18 : if (bNextPointIsNextSel)
782 : {
783 8 : oPointNextSel = oPointN_m2;
784 : }
785 :
786 : // Previous.
787 18 : if (v == 0)
788 : {
789 5 : oPointBeforeSel = oPointN_m2;
790 : }
791 :
792 18 : constexpr double EPSILON = 1.0E-5;
793 40 : const auto epsilonEqual = [](double a, double b, double eps)
794 40 : { return ::fabs(a - b) < eps; };
795 :
796 20 : if (epsilonEqual(oPointBeforeSel.getX(), oPointSel.getX(), EPSILON) &&
797 2 : epsilonEqual(oPointBeforeSel.getY(), oPointSel.getY(), EPSILON))
798 : {
799 : // Don't try to be too clever by retrying with a next point.
800 : // This can lead to false results as in the case of #3356.
801 2 : bUseFallback = true;
802 : }
803 :
804 18 : const double dx0 = oPointBeforeSel.getX() - oPointSel.getX();
805 18 : const double dy0 = oPointBeforeSel.getY() - oPointSel.getY();
806 :
807 : // Following.
808 18 : if (v + 1 >= nPointCount - 1)
809 : {
810 8 : oPointNextSel = oStartPoint;
811 : }
812 :
813 20 : if (epsilonEqual(oPointNextSel.getX(), oPointSel.getX(), EPSILON) &&
814 2 : epsilonEqual(oPointNextSel.getY(), oPointSel.getY(), EPSILON))
815 : {
816 : // Don't try to be too clever by retrying with a next point.
817 : // This can lead to false results as in the case of #3356.
818 2 : bUseFallback = true;
819 : }
820 :
821 18 : const double dx1 = oPointNextSel.getX() - oPointSel.getX();
822 18 : const double dy1 = oPointNextSel.getY() - oPointSel.getY();
823 :
824 18 : const double crossproduct = dx1 * dy0 - dx0 * dy1;
825 :
826 18 : if (!bUseFallback)
827 : {
828 14 : if (crossproduct > 0) // CCW
829 5 : return FALSE;
830 9 : else if (crossproduct < 0) // CW
831 9 : return TRUE;
832 : }
833 :
834 : // This is a degenerate case: the extent of the polygon is less than EPSILON
835 : // or 2 nearly identical points were found.
836 : // Try with Green Formula as a fallback, but this is not a guarantee
837 : // as we'll probably be affected by numerical instabilities.
838 4 : oIter = begin();
839 4 : oPointBefore = oStartPoint;
840 4 : ++oIter;
841 4 : auto oPointCur = *oIter;
842 4 : double dfSum = oStartPoint.getX() * (oPointCur.getY() - oStartPoint.getY());
843 :
844 16 : for (int i = 1; i < nPointCount - 1; i++)
845 : {
846 12 : ++oIter;
847 12 : const auto &oPointNext = *oIter;
848 12 : dfSum += oPointCur.getX() * (oPointNext.getY() - oPointBefore.getY());
849 12 : oPointBefore = oPointCur;
850 12 : oPointCur = oPointNext;
851 : }
852 :
853 4 : dfSum += oPointCur.getX() * (oStartPoint.getY() - oPointBefore.getY());
854 :
855 4 : return dfSum < 0;
856 : }
|