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