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