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