Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGRSimpleCurve and OGRLineString geometry classes.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_geometry.h"
15 : #include "ogr_geos.h"
16 : #include "ogr_p.h"
17 :
18 : #include "geodesic.h" // from PROJ
19 :
20 : #include <cmath>
21 : #include <cstdlib>
22 : #include <algorithm>
23 : #include <limits>
24 : #include <new>
25 :
26 : namespace
27 : {
28 :
29 611 : int DoubleToIntClamp(double dfValue)
30 : {
31 611 : if (std::isnan(dfValue))
32 0 : return 0;
33 611 : if (dfValue >= std::numeric_limits<int>::max())
34 1 : return std::numeric_limits<int>::max();
35 610 : if (dfValue <= std::numeric_limits<int>::min())
36 0 : return std::numeric_limits<int>::min();
37 610 : return static_cast<int>(dfValue);
38 : }
39 :
40 : } // namespace
41 :
42 : /************************************************************************/
43 : /* OGRSimpleCurve( const OGRSimpleCurve& ) */
44 : /************************************************************************/
45 :
46 : /**
47 : * \brief Copy constructor.
48 : *
49 : * Note: before GDAL 2.1, only the default implementation of the constructor
50 : * existed, which could be unsafe to use.
51 : *
52 : * @since GDAL 2.1
53 : */
54 :
55 302376 : OGRSimpleCurve::OGRSimpleCurve(const OGRSimpleCurve &other)
56 : : OGRCurve(other), nPointCount(0), paoPoints(nullptr), padfZ(nullptr),
57 302376 : padfM(nullptr)
58 : {
59 302376 : if (other.nPointCount > 0)
60 302168 : setPoints(other.nPointCount, other.paoPoints, other.padfZ, other.padfM);
61 302376 : }
62 :
63 : /************************************************************************/
64 : /* OGRSimpleCurve( OGRSimpleCurve&& ) */
65 : /************************************************************************/
66 :
67 : /**
68 : * \brief Move constructor.
69 : *
70 : * @since GDAL 3.11
71 : */
72 :
73 : // cppcheck-suppress-begin accessMoved
74 3 : OGRSimpleCurve::OGRSimpleCurve(OGRSimpleCurve &&other)
75 6 : : OGRCurve(std::move(other)), nPointCount(other.nPointCount),
76 3 : m_nPointCapacity(other.m_nPointCapacity), paoPoints(other.paoPoints),
77 3 : padfZ(other.padfZ), padfM(other.padfM)
78 : {
79 3 : other.nPointCount = 0;
80 3 : other.m_nPointCapacity = 0;
81 3 : other.paoPoints = nullptr;
82 3 : other.padfZ = nullptr;
83 3 : other.padfM = nullptr;
84 3 : }
85 :
86 : // cppcheck-suppress-end accessMoved
87 :
88 : /************************************************************************/
89 : /* ~OGRSimpleCurve() */
90 : /************************************************************************/
91 :
92 3734650 : OGRSimpleCurve::~OGRSimpleCurve()
93 :
94 : {
95 3734680 : CPLFree(paoPoints);
96 3734680 : CPLFree(padfZ);
97 3734660 : CPLFree(padfM);
98 3734670 : }
99 :
100 : /************************************************************************/
101 : /* operator=(const OGRSimpleCurve &other) */
102 : /************************************************************************/
103 :
104 : /**
105 : * \brief Assignment operator.
106 : *
107 : * Note: before GDAL 2.1, only the default implementation of the operator
108 : * existed, which could be unsafe to use.
109 : *
110 : * @since GDAL 2.1
111 : */
112 :
113 12 : OGRSimpleCurve &OGRSimpleCurve::operator=(const OGRSimpleCurve &other)
114 : {
115 12 : if (this == &other)
116 0 : return *this;
117 :
118 12 : OGRCurve::operator=(other);
119 :
120 12 : setPoints(other.nPointCount, other.paoPoints, other.padfZ, other.padfM);
121 12 : flags = other.flags;
122 :
123 12 : return *this;
124 : }
125 :
126 : /************************************************************************/
127 : /* operator=(OGRSimpleCurve &&other) */
128 : /************************************************************************/
129 :
130 : /**
131 : * \brief Move assignment operator.
132 : *
133 : * @since GDAL 3.11
134 : */
135 :
136 6 : OGRSimpleCurve &OGRSimpleCurve::operator=(OGRSimpleCurve &&other)
137 : {
138 6 : if (this != &other)
139 : {
140 : // cppcheck-suppress-begin accessMoved
141 6 : OGRCurve::operator=(std::move(other));
142 :
143 6 : nPointCount = other.nPointCount;
144 6 : m_nPointCapacity = other.m_nPointCapacity;
145 6 : CPLFree(paoPoints);
146 6 : paoPoints = other.paoPoints;
147 6 : CPLFree(padfZ);
148 6 : padfZ = other.padfZ;
149 6 : CPLFree(padfM);
150 6 : padfM = other.padfM;
151 6 : flags = other.flags;
152 6 : other.nPointCount = 0;
153 6 : other.m_nPointCapacity = 0;
154 6 : other.paoPoints = nullptr;
155 6 : other.padfZ = nullptr;
156 6 : other.padfM = nullptr;
157 : // cppcheck-suppress-end accessMoved
158 : }
159 :
160 6 : return *this;
161 : }
162 :
163 : /************************************************************************/
164 : /* flattenTo2D() */
165 : /************************************************************************/
166 :
167 1069 : void OGRSimpleCurve::flattenTo2D()
168 :
169 : {
170 1069 : Make2D();
171 1069 : setMeasured(FALSE);
172 1069 : }
173 :
174 : /************************************************************************/
175 : /* empty() */
176 : /************************************************************************/
177 :
178 18764 : void OGRSimpleCurve::empty()
179 :
180 : {
181 18764 : setNumPoints(0);
182 18763 : }
183 :
184 : /************************************************************************/
185 : /* setCoordinateDimension() */
186 : /************************************************************************/
187 :
188 2179 : bool OGRSimpleCurve::setCoordinateDimension(int nNewDimension)
189 :
190 : {
191 2179 : setMeasured(FALSE);
192 2179 : if (nNewDimension == 2)
193 1150 : Make2D();
194 1029 : else if (nNewDimension == 3)
195 1029 : return Make3D();
196 1150 : return true;
197 : }
198 :
199 119692 : bool OGRSimpleCurve::set3D(OGRBoolean bIs3D)
200 :
201 : {
202 119692 : if (bIs3D)
203 117097 : return Make3D();
204 : else
205 2595 : Make2D();
206 2595 : return true;
207 : }
208 :
209 119340 : bool OGRSimpleCurve::setMeasured(OGRBoolean bIsMeasured)
210 :
211 : {
212 119340 : if (bIsMeasured)
213 8193 : return AddM();
214 : else
215 111147 : RemoveM();
216 111147 : return true;
217 : }
218 :
219 : /************************************************************************/
220 : /* WkbSize() */
221 : /* */
222 : /* Return the size of this object in well known binary */
223 : /* representation including the byte order, and type information. */
224 : /************************************************************************/
225 :
226 92896 : size_t OGRSimpleCurve::WkbSize() const
227 :
228 : {
229 92896 : return 5 + 4 + 8 * static_cast<size_t>(nPointCount) * CoordinateDimension();
230 : }
231 :
232 : //! @cond Doxygen_Suppress
233 :
234 : /************************************************************************/
235 : /* Make2D() */
236 : /************************************************************************/
237 :
238 54783 : void OGRSimpleCurve::Make2D()
239 :
240 : {
241 54783 : if (padfZ != nullptr)
242 : {
243 1652 : CPLFree(padfZ);
244 1652 : padfZ = nullptr;
245 : }
246 54783 : flags &= ~OGR_G_3D;
247 54783 : }
248 :
249 : /************************************************************************/
250 : /* Make3D() */
251 : /************************************************************************/
252 :
253 2780800 : bool OGRSimpleCurve::Make3D()
254 :
255 : {
256 2780800 : if (padfZ == nullptr)
257 : {
258 2660180 : padfZ = static_cast<double *>(
259 2660180 : VSI_CALLOC_VERBOSE(sizeof(double), std::max(1, m_nPointCapacity)));
260 2660180 : if (padfZ == nullptr)
261 : {
262 0 : flags &= ~OGR_G_3D;
263 0 : CPLError(CE_Failure, CPLE_AppDefined,
264 : "OGRSimpleCurve::Make3D() failed");
265 0 : return false;
266 : }
267 : }
268 2780800 : flags |= OGR_G_3D;
269 2780800 : return true;
270 : }
271 :
272 : /************************************************************************/
273 : /* RemoveM() */
274 : /************************************************************************/
275 :
276 126583 : void OGRSimpleCurve::RemoveM()
277 :
278 : {
279 126583 : if (padfM != nullptr)
280 : {
281 250 : CPLFree(padfM);
282 250 : padfM = nullptr;
283 : }
284 126583 : flags &= ~OGR_G_MEASURED;
285 126583 : }
286 :
287 : /************************************************************************/
288 : /* AddM() */
289 : /************************************************************************/
290 :
291 39013 : bool OGRSimpleCurve::AddM()
292 :
293 : {
294 39013 : if (padfM == nullptr)
295 : {
296 34603 : padfM = static_cast<double *>(
297 34603 : VSI_CALLOC_VERBOSE(sizeof(double), std::max(1, m_nPointCapacity)));
298 34603 : if (padfM == nullptr)
299 : {
300 0 : flags &= ~OGR_G_MEASURED;
301 0 : CPLError(CE_Failure, CPLE_AppDefined,
302 : "OGRSimpleCurve::AddM() failed");
303 0 : return false;
304 : }
305 : }
306 39013 : flags |= OGR_G_MEASURED;
307 39013 : return true;
308 : }
309 :
310 : //! @endcond
311 :
312 : /************************************************************************/
313 : /* getPoint() */
314 : /************************************************************************/
315 :
316 : /**
317 : * \brief Fetch a point in line string.
318 : *
319 : * This method relates to the SFCOM ILineString::get_Point() method.
320 : *
321 : * @param i the vertex to fetch, from 0 to getNumPoints()-1.
322 : * @param poPoint a point to initialize with the fetched point.
323 : */
324 :
325 1180760 : void OGRSimpleCurve::getPoint(int i, OGRPoint *poPoint) const
326 :
327 : {
328 1180760 : CPLAssert(i >= 0);
329 1180760 : CPLAssert(i < nPointCount);
330 1180760 : CPLAssert(poPoint != nullptr);
331 :
332 1180760 : poPoint->setX(paoPoints[i].x);
333 1180760 : poPoint->setY(paoPoints[i].y);
334 :
335 1180760 : if ((flags & OGR_G_3D) && padfZ != nullptr)
336 45190 : poPoint->setZ(padfZ[i]);
337 1180760 : if ((flags & OGR_G_MEASURED) && padfM != nullptr)
338 28415 : poPoint->setM(padfM[i]);
339 1180760 : }
340 :
341 : /**
342 : * \fn int OGRSimpleCurve::getNumPoints() const;
343 : *
344 : * \brief Fetch vertex count.
345 : *
346 : * Returns the number of vertices in the line string.
347 : *
348 : * @return vertex count.
349 : */
350 :
351 : /**
352 : * \fn double OGRSimpleCurve::getX( int iVertex ) const;
353 : *
354 : * \brief Get X at vertex.
355 : *
356 : * Returns the X value at the indicated vertex. If iVertex is out of range a
357 : * crash may occur, no internal range checking is performed.
358 : *
359 : * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
360 : *
361 : * @return X value.
362 : */
363 :
364 : /**
365 : * \fn double OGRSimpleCurve::getY( int iVertex ) const;
366 : *
367 : * \brief Get Y at vertex.
368 : *
369 : * Returns the Y value at the indicated vertex. If iVertex is out of range a
370 : * crash may occur, no internal range checking is performed.
371 : *
372 : * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
373 : *
374 : * @return X value.
375 : */
376 :
377 : /************************************************************************/
378 : /* getZ() */
379 : /************************************************************************/
380 :
381 : /**
382 : * \brief Get Z at vertex.
383 : *
384 : * Returns the Z (elevation) value at the indicated vertex. If no Z
385 : * value is available, 0.0 is returned. If iVertex is out of range a
386 : * crash may occur, no internal range checking is performed.
387 : *
388 : * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
389 : *
390 : * @return Z value.
391 : */
392 :
393 688314 : double OGRSimpleCurve::getZ(int iVertex) const
394 :
395 : {
396 688314 : if (padfZ != nullptr && iVertex >= 0 && iVertex < nPointCount &&
397 252729 : (flags & OGR_G_3D))
398 252729 : return (padfZ[iVertex]);
399 : else
400 435585 : return 0.0;
401 : }
402 :
403 : /************************************************************************/
404 : /* getM() */
405 : /************************************************************************/
406 :
407 : /**
408 : * \brief Get measure at vertex.
409 : *
410 : * Returns the M (measure) value at the indicated vertex. If no M
411 : * value is available, 0.0 is returned.
412 : *
413 : * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
414 : *
415 : * @return M value.
416 : */
417 :
418 2660 : double OGRSimpleCurve::getM(int iVertex) const
419 :
420 : {
421 2660 : if (padfM != nullptr && iVertex >= 0 && iVertex < nPointCount &&
422 2660 : (flags & OGR_G_MEASURED))
423 2660 : return (padfM[iVertex]);
424 : else
425 0 : return 0.0;
426 : }
427 :
428 : /************************************************************************/
429 : /* setNumPoints() */
430 : /************************************************************************/
431 :
432 : /**
433 : * \brief Set number of points in geometry.
434 : *
435 : * This method primary exists to preset the number of points in a linestring
436 : * geometry before setPoint() is used to assign them to avoid reallocating
437 : * the array larger with each call to addPoint().
438 : *
439 : * This method has no SFCOM analog.
440 : *
441 : * @param nNewPointCount the new number of points for geometry.
442 : * @param bZeroizeNewContent whether to set to zero the new elements of arrays
443 : * that are extended.
444 : * @return (since 3.10) true in case of success, false in case of memory allocation error
445 : */
446 :
447 21579600 : bool OGRSimpleCurve::setNumPoints(int nNewPointCount, int bZeroizeNewContent)
448 :
449 : {
450 21579600 : CPLAssert(nNewPointCount >= 0);
451 :
452 21579600 : if (nNewPointCount > m_nPointCapacity)
453 : {
454 : // Overflow of sizeof(OGRRawPoint) * nNewPointCount can only occur on
455 : // 32 bit, but we don't really want to allocate 2 billion points even on
456 : // 64 bit...
457 8637790 : if (nNewPointCount > std::numeric_limits<int>::max() /
458 : static_cast<int>(sizeof(OGRRawPoint)))
459 : {
460 2 : CPLError(CE_Failure, CPLE_IllegalArg,
461 : "Too many points on line/curve (%d points exceeds the "
462 : "limit of %d points)",
463 : nNewPointCount,
464 12 : std::numeric_limits<int>::max() /
465 : static_cast<int>(sizeof(OGRRawPoint)));
466 2 : return false;
467 : }
468 :
469 : // If first allocation, just aim for nNewPointCount
470 : // Otherwise aim for nNewPointCount + nNewPointCount / 3 to have
471 : // exponential growth.
472 : const int nNewCapacity =
473 13548900 : (nPointCount == 0 ||
474 4911110 : nNewPointCount > std::numeric_limits<int>::max() /
475 4911110 : static_cast<int>(sizeof(OGRRawPoint)) -
476 4911110 : nNewPointCount / 3)
477 8637770 : ? nNewPointCount
478 4911110 : : nNewPointCount + nNewPointCount / 3;
479 :
480 8637770 : if (nPointCount == 0 && paoPoints)
481 : {
482 : // If there was an allocated array, but the old number of points is
483 : // 0, then free the arrays before allocating them, to avoid
484 : // potential costly recopy of useless data.
485 19 : VSIFree(paoPoints);
486 19 : paoPoints = nullptr;
487 19 : VSIFree(padfZ);
488 19 : padfZ = nullptr;
489 19 : VSIFree(padfM);
490 19 : padfM = nullptr;
491 19 : m_nPointCapacity = 0;
492 : }
493 :
494 : OGRRawPoint *paoNewPoints = static_cast<OGRRawPoint *>(
495 8637770 : VSI_REALLOC_VERBOSE(paoPoints, sizeof(OGRRawPoint) * nNewCapacity));
496 8637770 : if (paoNewPoints == nullptr)
497 : {
498 0 : return false;
499 : }
500 8637770 : paoPoints = paoNewPoints;
501 :
502 8637770 : if (flags & OGR_G_3D)
503 : {
504 : double *padfNewZ = static_cast<double *>(
505 3865920 : VSI_REALLOC_VERBOSE(padfZ, sizeof(double) * nNewCapacity));
506 3865920 : if (padfNewZ == nullptr)
507 : {
508 0 : return false;
509 : }
510 3865920 : padfZ = padfNewZ;
511 : }
512 :
513 8637770 : if (flags & OGR_G_MEASURED)
514 : {
515 : double *padfNewM = static_cast<double *>(
516 4419 : VSI_REALLOC_VERBOSE(padfM, sizeof(double) * nNewCapacity));
517 4419 : if (padfNewM == nullptr)
518 : {
519 0 : return false;
520 : }
521 4419 : padfM = padfNewM;
522 : }
523 :
524 8637770 : m_nPointCapacity = nNewCapacity;
525 : }
526 :
527 21579600 : if (nNewPointCount > nPointCount && bZeroizeNewContent)
528 : {
529 : // gcc 8.0 (dev) complains about -Wclass-memaccess since
530 : // OGRRawPoint() has a constructor. So use a void* pointer. Doing
531 : // the memset() here is correct since the constructor sets to 0. We
532 : // could instead use a std::fill(), but at every other place, we
533 : // treat this class as a regular POD (see above use of realloc())
534 19526600 : void *dest = static_cast<void *>(paoPoints + nPointCount);
535 19526600 : memset(dest, 0, sizeof(OGRRawPoint) * (nNewPointCount - nPointCount));
536 :
537 19526600 : if ((flags & OGR_G_3D) && padfZ)
538 5503670 : memset(padfZ + nPointCount, 0,
539 5503670 : sizeof(double) * (nNewPointCount - nPointCount));
540 :
541 19526600 : if ((flags & OGR_G_MEASURED) && padfM)
542 409 : memset(padfM + nPointCount, 0,
543 409 : sizeof(double) * (nNewPointCount - nPointCount));
544 : }
545 :
546 21579600 : nPointCount = nNewPointCount;
547 21579600 : return true;
548 : }
549 :
550 : /************************************************************************/
551 : /* setPoint() */
552 : /************************************************************************/
553 :
554 : /**
555 : * \brief Set the location of a vertex in line string.
556 : *
557 : * If iPoint is larger than the number of necessary the number of existing
558 : * points in the line string, the point count will be increased to
559 : * accommodate the request.
560 : *
561 : * There is no SFCOM analog to this method.
562 : *
563 : * @param iPoint the index of the vertex to assign (zero based).
564 : * @param poPoint the value to assign to the vertex.
565 : * @return (since 3.10) true in case of success, false in case of memory allocation error
566 : */
567 :
568 1602 : bool OGRSimpleCurve::setPoint(int iPoint, OGRPoint *poPoint)
569 :
570 : {
571 1602 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
572 59 : return setPoint(iPoint, poPoint->getX(), poPoint->getY(),
573 59 : poPoint->getZ(), poPoint->getM());
574 1543 : else if (flags & OGR_G_3D)
575 864 : return setPoint(iPoint, poPoint->getX(), poPoint->getY(),
576 864 : poPoint->getZ());
577 679 : else if (flags & OGR_G_MEASURED)
578 3 : return setPointM(iPoint, poPoint->getX(), poPoint->getY(),
579 3 : poPoint->getM());
580 : else
581 676 : return setPoint(iPoint, poPoint->getX(), poPoint->getY());
582 : }
583 :
584 : /************************************************************************/
585 : /* CheckPointCount() */
586 : /************************************************************************/
587 :
588 19507400 : static inline bool CheckPointCount(int iPoint)
589 : {
590 19507400 : if (iPoint == std::numeric_limits<int>::max())
591 : {
592 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too big point count.");
593 1 : return false;
594 : }
595 19507400 : return true;
596 : }
597 :
598 : /************************************************************************/
599 : /* setPoint() */
600 : /************************************************************************/
601 :
602 : /**
603 : * \brief Set the location of a vertex in line string.
604 : *
605 : * If iPoint is larger than the number of necessary the number of existing
606 : * points in the line string, the point count will be increased to
607 : * accommodate the request.
608 : *
609 : * There is no SFCOM analog to this method.
610 : *
611 : * @param iPoint the index of the vertex to assign (zero based).
612 : * @param xIn input X coordinate to assign.
613 : * @param yIn input Y coordinate to assign.
614 : * @param zIn input Z coordinate to assign (defaults to zero).
615 : * @return (since 3.10) true in case of success, false in case of memory allocation error
616 : */
617 :
618 5563110 : bool OGRSimpleCurve::setPoint(int iPoint, double xIn, double yIn, double zIn)
619 :
620 : {
621 5563110 : if (!(flags & OGR_G_3D))
622 : {
623 1263540 : if (!Make3D())
624 0 : return false;
625 : }
626 :
627 5563110 : if (iPoint >= nPointCount)
628 : {
629 5503410 : if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
630 1 : return false;
631 : }
632 : #ifdef DEBUG
633 5563100 : if (paoPoints == nullptr)
634 0 : return false;
635 : #endif
636 :
637 5563100 : paoPoints[iPoint].x = xIn;
638 5563100 : paoPoints[iPoint].y = yIn;
639 :
640 5563100 : if (padfZ != nullptr)
641 : {
642 5563100 : padfZ[iPoint] = zIn;
643 : }
644 5563100 : return true;
645 : }
646 :
647 : /**
648 : * \brief Set the location of a vertex in line string.
649 : *
650 : * If iPoint is larger than the number of necessary the number of existing
651 : * points in the line string, the point count will be increased to
652 : * accommodate the request.
653 : *
654 : * There is no SFCOM analog to this method.
655 : *
656 : * @param iPoint the index of the vertex to assign (zero based).
657 : * @param xIn input X coordinate to assign.
658 : * @param yIn input Y coordinate to assign.
659 : * @param mIn input M coordinate to assign (defaults to zero).
660 : * @return (since 3.10) true in case of success, false in case of memory allocation error
661 : */
662 :
663 443 : bool OGRSimpleCurve::setPointM(int iPoint, double xIn, double yIn, double mIn)
664 :
665 : {
666 443 : if (!(flags & OGR_G_MEASURED))
667 : {
668 108 : if (!AddM())
669 0 : return false;
670 : }
671 :
672 443 : if (iPoint >= nPointCount)
673 : {
674 6 : if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
675 0 : return false;
676 : }
677 : #ifdef DEBUG
678 443 : if (paoPoints == nullptr)
679 0 : return false;
680 : #endif
681 :
682 443 : paoPoints[iPoint].x = xIn;
683 443 : paoPoints[iPoint].y = yIn;
684 :
685 443 : if (padfM != nullptr)
686 : {
687 443 : padfM[iPoint] = mIn;
688 : }
689 443 : return true;
690 : }
691 :
692 : /**
693 : * \brief Set the location of a vertex in line string.
694 : *
695 : * If iPoint is larger than the number of necessary the number of existing
696 : * points in the line string, the point count will be increased to
697 : * accommodate the request.
698 : *
699 : * There is no SFCOM analog to this method.
700 : *
701 : * @param iPoint the index of the vertex to assign (zero based).
702 : * @param xIn input X coordinate to assign.
703 : * @param yIn input Y coordinate to assign.
704 : * @param zIn input Z coordinate to assign (defaults to zero).
705 : * @param mIn input M coordinate to assign (defaults to zero).
706 : * @return (since 3.10) true in case of success, false in case of memory allocation error
707 : */
708 :
709 852 : bool OGRSimpleCurve::setPoint(int iPoint, double xIn, double yIn, double zIn,
710 : double mIn)
711 :
712 : {
713 852 : if (!(flags & OGR_G_3D))
714 : {
715 271 : if (!Make3D())
716 0 : return false;
717 : }
718 852 : if (!(flags & OGR_G_MEASURED))
719 : {
720 284 : if (!AddM())
721 0 : return false;
722 : }
723 :
724 852 : if (iPoint >= nPointCount)
725 : {
726 261 : if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
727 0 : return false;
728 : }
729 : #ifdef DEBUG
730 852 : if (paoPoints == nullptr)
731 0 : return false;
732 : #endif
733 :
734 852 : paoPoints[iPoint].x = xIn;
735 852 : paoPoints[iPoint].y = yIn;
736 :
737 852 : if (padfZ != nullptr)
738 : {
739 852 : padfZ[iPoint] = zIn;
740 : }
741 852 : if (padfM != nullptr)
742 : {
743 852 : padfM[iPoint] = mIn;
744 : }
745 852 : return true;
746 : }
747 :
748 : /**
749 : * \brief Set the location of a vertex in line string.
750 : *
751 : * If iPoint is larger than the number of necessary the number of existing
752 : * points in the line string, the point count will be increased to
753 : * accommodate the request.
754 : *
755 : * There is no SFCOM analog to this method.
756 : *
757 : * @param iPoint the index of the vertex to assign (zero based).
758 : * @param xIn input X coordinate to assign.
759 : * @param yIn input Y coordinate to assign.
760 : * @return (since 3.10) true in case of success, false in case of memory allocation error
761 : */
762 :
763 27708700 : bool OGRSimpleCurve::setPoint(int iPoint, double xIn, double yIn)
764 :
765 : {
766 27708700 : if (iPoint >= nPointCount)
767 : {
768 14003700 : if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1) || !paoPoints)
769 2 : return false;
770 : }
771 :
772 27708700 : paoPoints[iPoint].x = xIn;
773 27708700 : paoPoints[iPoint].y = yIn;
774 27708700 : return true;
775 : }
776 :
777 : /************************************************************************/
778 : /* setZ() */
779 : /************************************************************************/
780 :
781 : /**
782 : * \brief Set the Z of a vertex in line string.
783 : *
784 : * If iPoint is larger than the number of necessary the number of existing
785 : * points in the line string, the point count will be increased to
786 : * accommodate the request.
787 : *
788 : * There is no SFCOM analog to this method.
789 : *
790 : * @param iPoint the index of the vertex to assign (zero based).
791 : * @param zIn input Z coordinate to assign.
792 : * @return (since 3.10) true in case of success, false in case of memory allocation error
793 : */
794 :
795 7157 : bool OGRSimpleCurve::setZ(int iPoint, double zIn)
796 : {
797 7157 : if (getCoordinateDimension() == 2)
798 : {
799 860 : if (!Make3D())
800 0 : return false;
801 : }
802 :
803 7157 : if (iPoint >= nPointCount)
804 : {
805 0 : if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
806 0 : return false;
807 : }
808 :
809 7157 : if (padfZ != nullptr)
810 7157 : padfZ[iPoint] = zIn;
811 7157 : return true;
812 : }
813 :
814 : /************************************************************************/
815 : /* setM() */
816 : /************************************************************************/
817 :
818 : /**
819 : * \brief Set the M of a vertex in line string.
820 : *
821 : * If iPoint is larger than the number of necessary the number of existing
822 : * points in the line string, the point count will be increased to
823 : * accommodate the request.
824 : *
825 : * There is no SFCOM analog to this method.
826 : *
827 : * @param iPoint the index of the vertex to assign (zero based).
828 : * @param mIn input M coordinate to assign.
829 : * @return (since 3.10) true in case of success, false in case of memory allocation error
830 : */
831 :
832 842 : bool OGRSimpleCurve::setM(int iPoint, double mIn)
833 : {
834 842 : if (!(flags & OGR_G_MEASURED))
835 : {
836 103 : if (!AddM())
837 0 : return false;
838 : }
839 :
840 842 : if (iPoint >= nPointCount)
841 : {
842 0 : if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
843 0 : return false;
844 : }
845 :
846 842 : if (padfM != nullptr)
847 842 : padfM[iPoint] = mIn;
848 842 : return true;
849 : }
850 :
851 : /************************************************************************/
852 : /* addPoint() */
853 : /************************************************************************/
854 :
855 : /**
856 : * \brief Add a point to a line string.
857 : *
858 : * The vertex count of the line string is increased by one, and assigned from
859 : * the passed location value.
860 : *
861 : * There is no SFCOM analog to this method.
862 : *
863 : * @param poPoint the point to assign to the new vertex.
864 : * @return (since 3.10) true in case of success, false in case of memory allocation error
865 : */
866 :
867 5023370 : bool OGRSimpleCurve::addPoint(const OGRPoint *poPoint)
868 :
869 : {
870 5023370 : if (poPoint->Is3D() && poPoint->IsMeasured())
871 250 : return setPoint(nPointCount, poPoint->getX(), poPoint->getY(),
872 250 : poPoint->getZ(), poPoint->getM());
873 5023120 : else if (poPoint->Is3D())
874 4986960 : return setPoint(nPointCount, poPoint->getX(), poPoint->getY(),
875 4986960 : poPoint->getZ());
876 36157 : else if (poPoint->IsMeasured())
877 0 : return setPointM(nPointCount, poPoint->getX(), poPoint->getY(),
878 0 : poPoint->getM());
879 : else
880 36157 : return setPoint(nPointCount, poPoint->getX(), poPoint->getY());
881 : }
882 :
883 : /************************************************************************/
884 : /* addPoint() */
885 : /************************************************************************/
886 :
887 : /**
888 : * \brief Add a point to a line string.
889 : *
890 : * The vertex count of the line string is increased by one, and assigned from
891 : * the passed location value.
892 : *
893 : * There is no SFCOM analog to this method.
894 : *
895 : * @param x the X coordinate to assign to the new point.
896 : * @param y the Y coordinate to assign to the new point.
897 : * @param z the Z coordinate to assign to the new point (defaults to zero).
898 : * @param m the M coordinate to assign to the new point (defaults to zero).
899 : * @return (since 3.10) true in case of success, false in case of memory allocation error
900 : */
901 :
902 11 : bool OGRSimpleCurve::addPoint(double x, double y, double z, double m)
903 :
904 : {
905 11 : return setPoint(nPointCount, x, y, z, m);
906 : }
907 :
908 : /**
909 : * \brief Add a point to a line string.
910 : *
911 : * The vertex count of the line string is increased by one, and assigned from
912 : * the passed location value.
913 : *
914 : * There is no SFCOM analog to this method.
915 : *
916 : * @param x the X coordinate to assign to the new point.
917 : * @param y the Y coordinate to assign to the new point.
918 : * @param z the Z coordinate to assign to the new point (defaults to zero).
919 : * @return (since 3.10) true in case of success, false in case of memory allocation error
920 : */
921 :
922 507279 : bool OGRSimpleCurve::addPoint(double x, double y, double z)
923 :
924 : {
925 507279 : return setPoint(nPointCount, x, y, z);
926 : }
927 :
928 : /**
929 : * \brief Add a point to a line string.
930 : *
931 : * The vertex count of the line string is increased by one, and assigned from
932 : * the passed location value.
933 : *
934 : * There is no SFCOM analog to this method.
935 : *
936 : * @param x the X coordinate to assign to the new point.
937 : * @param y the Y coordinate to assign to the new point.
938 : * @return (since 3.10) true in case of success, false in case of memory allocation error
939 : */
940 :
941 13905600 : bool OGRSimpleCurve::addPoint(double x, double y)
942 :
943 : {
944 13905600 : return setPoint(nPointCount, x, y);
945 : }
946 :
947 : /**
948 : * \brief Add a point to a line string.
949 : *
950 : * The vertex count of the line string is increased by one, and assigned from
951 : * the passed location value.
952 : *
953 : * There is no SFCOM analog to this method.
954 : *
955 : * @param x the X coordinate to assign to the new point.
956 : * @param y the Y coordinate to assign to the new point.
957 : * @param m the M coordinate to assign to the new point.
958 : * @return (since 3.10) true in case of success, false in case of memory allocation error
959 : */
960 :
961 6 : bool OGRSimpleCurve::addPointM(double x, double y, double m)
962 :
963 : {
964 6 : return setPointM(nPointCount, x, y, m);
965 : }
966 :
967 : /************************************************************************/
968 : /* removePoint() */
969 : /************************************************************************/
970 :
971 : /**
972 : * \brief Remove a point from a line string.
973 : *
974 : * There is no SFCOM analog to this method.
975 : *
976 : * @param nIndex Point index
977 : * @since GDAL 3.3
978 : */
979 :
980 15 : bool OGRSimpleCurve::removePoint(int nIndex)
981 : {
982 15 : if (nIndex < 0 || nIndex >= nPointCount)
983 4 : return false;
984 11 : if (nIndex < nPointCount - 1)
985 : {
986 7 : memmove(paoPoints + nIndex, paoPoints + nIndex + 1,
987 7 : sizeof(OGRRawPoint) * (nPointCount - 1 - nIndex));
988 7 : if (padfZ)
989 : {
990 1 : memmove(padfZ + nIndex, padfZ + nIndex + 1,
991 1 : sizeof(double) * (nPointCount - 1 - nIndex));
992 : }
993 7 : if (padfM)
994 : {
995 1 : memmove(padfM + nIndex, padfM + nIndex + 1,
996 1 : sizeof(double) * (nPointCount - 1 - nIndex));
997 : }
998 : }
999 11 : nPointCount--;
1000 11 : return true;
1001 : }
1002 :
1003 : /************************************************************************/
1004 : /* setPointsM() */
1005 : /************************************************************************/
1006 :
1007 : /**
1008 : * \brief Assign all points in a line string.
1009 : *
1010 : * This method clears any existing points assigned to this line string,
1011 : * and assigns a whole new set. It is the most efficient way of assigning
1012 : * the value of a line string.
1013 : *
1014 : * There is no SFCOM analog to this method.
1015 : *
1016 : * @param nPointsIn number of points being passed in paoPointsIn
1017 : * @param paoPointsIn list of points being assigned.
1018 : * @param padfMIn the M values that go with the points.
1019 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1020 : */
1021 :
1022 190 : bool OGRSimpleCurve::setPointsM(int nPointsIn, const OGRRawPoint *paoPointsIn,
1023 : const double *padfMIn)
1024 :
1025 : {
1026 190 : if (!setNumPoints(nPointsIn, FALSE)
1027 : #ifdef DEBUG
1028 190 : || paoPoints == nullptr
1029 : #endif
1030 : )
1031 0 : return false;
1032 :
1033 190 : if (nPointsIn)
1034 : {
1035 190 : const void *pUnaligned = paoPointsIn;
1036 190 : memcpy(paoPoints, pUnaligned, sizeof(OGRRawPoint) * nPointsIn);
1037 : }
1038 :
1039 : /* -------------------------------------------------------------------- */
1040 : /* Check measures. */
1041 : /* -------------------------------------------------------------------- */
1042 190 : if (padfMIn == nullptr && (flags & OGR_G_MEASURED))
1043 : {
1044 0 : RemoveM();
1045 : }
1046 190 : else if (padfMIn)
1047 : {
1048 190 : if (!AddM())
1049 0 : return false;
1050 190 : if (padfM && nPointsIn)
1051 : {
1052 190 : const void *pUnaligned = padfMIn;
1053 190 : memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1054 : }
1055 : }
1056 190 : return true;
1057 : }
1058 :
1059 : /************************************************************************/
1060 : /* setPoints() */
1061 : /************************************************************************/
1062 :
1063 : /**
1064 : * \brief Assign all points in a line string.
1065 : *
1066 : * This method clears any existing points assigned to this line string,
1067 : * and assigns a whole new set. It is the most efficient way of assigning
1068 : * the value of a line string.
1069 : *
1070 : * There is no SFCOM analog to this method.
1071 : *
1072 : * @param nPointsIn number of points being passed in paoPointsIn
1073 : * @param paoPointsIn list of points being assigned.
1074 : * @param padfZIn the Z values that go with the points.
1075 : * @param padfMIn the M values that go with the points.
1076 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1077 : */
1078 :
1079 1855230 : bool OGRSimpleCurve::setPoints(int nPointsIn, const OGRRawPoint *paoPointsIn,
1080 : const double *padfZIn, const double *padfMIn)
1081 :
1082 : {
1083 1855230 : if (!setNumPoints(nPointsIn, FALSE)
1084 : #ifdef DEBUG
1085 1855230 : || paoPoints == nullptr
1086 : #endif
1087 : )
1088 89 : return false;
1089 :
1090 1855140 : if (nPointsIn)
1091 : {
1092 1855140 : const void *pUnaligned = paoPointsIn;
1093 1855140 : memcpy(paoPoints, pUnaligned, sizeof(OGRRawPoint) * nPointsIn);
1094 : }
1095 :
1096 : /* -------------------------------------------------------------------- */
1097 : /* Check 2D/3D. */
1098 : /* -------------------------------------------------------------------- */
1099 1855140 : if (padfZIn == nullptr && getCoordinateDimension() > 2)
1100 : {
1101 0 : Make2D();
1102 : }
1103 1855140 : else if (padfZIn)
1104 : {
1105 1352330 : if (!Make3D())
1106 0 : return false;
1107 1352330 : if (padfZ && nPointsIn)
1108 : {
1109 1352330 : const void *pUnaligned = padfZIn;
1110 1352330 : memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1111 : }
1112 : }
1113 :
1114 : /* -------------------------------------------------------------------- */
1115 : /* Check measures. */
1116 : /* -------------------------------------------------------------------- */
1117 1855140 : if (padfMIn == nullptr && (flags & OGR_G_MEASURED))
1118 : {
1119 0 : RemoveM();
1120 : }
1121 1855140 : else if (padfMIn)
1122 : {
1123 598 : if (!AddM())
1124 0 : return false;
1125 598 : if (padfM && nPointsIn)
1126 : {
1127 598 : const void *pUnaligned = padfMIn;
1128 598 : memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1129 : }
1130 : }
1131 1855140 : return true;
1132 : }
1133 :
1134 : /************************************************************************/
1135 : /* setPoints() */
1136 : /************************************************************************/
1137 :
1138 : /**
1139 : * \brief Assign all points in a line string.
1140 : *
1141 : * This method clears any existing points assigned to this line string,
1142 : * and assigns a whole new set. It is the most efficient way of assigning
1143 : * the value of a line string.
1144 : *
1145 : * There is no SFCOM analog to this method.
1146 : *
1147 : * @param nPointsIn number of points being passed in paoPointsIn
1148 : * @param paoPointsIn list of points being assigned.
1149 : * @param padfZIn the Z values that go with the points (optional, may be NULL).
1150 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1151 : */
1152 :
1153 20624 : bool OGRSimpleCurve::setPoints(int nPointsIn, const OGRRawPoint *paoPointsIn,
1154 : const double *padfZIn)
1155 :
1156 : {
1157 20624 : if (!setNumPoints(nPointsIn, FALSE)
1158 : #ifdef DEBUG
1159 20624 : || paoPoints == nullptr
1160 : #endif
1161 : )
1162 7 : return false;
1163 :
1164 20617 : if (nPointsIn)
1165 : {
1166 20617 : const void *pUnaligned = paoPointsIn;
1167 20617 : memcpy(paoPoints, pUnaligned, sizeof(OGRRawPoint) * nPointsIn);
1168 : }
1169 :
1170 : /* -------------------------------------------------------------------- */
1171 : /* Check 2D/3D. */
1172 : /* -------------------------------------------------------------------- */
1173 20617 : if (padfZIn == nullptr && getCoordinateDimension() > 2)
1174 : {
1175 0 : Make2D();
1176 : }
1177 20617 : else if (padfZIn)
1178 : {
1179 1237 : if (!Make3D())
1180 0 : return false;
1181 1237 : if (padfZ && nPointsIn)
1182 : {
1183 1237 : const void *pUnaligned = padfZIn;
1184 1237 : memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1185 : }
1186 : }
1187 20617 : return true;
1188 : }
1189 :
1190 : /************************************************************************/
1191 : /* setPoints() */
1192 : /************************************************************************/
1193 :
1194 : /**
1195 : * \brief Assign all points in a line string.
1196 : *
1197 : * This method clear any existing points assigned to this line string,
1198 : * and assigns a whole new set.
1199 : *
1200 : * There is no SFCOM analog to this method.
1201 : *
1202 : * @param nPointsIn number of points being passed in padfX and padfY.
1203 : * @param padfX list of X coordinates of points being assigned.
1204 : * @param padfY list of Y coordinates of points being assigned.
1205 : * @param padfZIn list of Z coordinates of points being assigned (defaults to
1206 : * NULL for 2D objects).
1207 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1208 : */
1209 :
1210 48369 : bool OGRSimpleCurve::setPoints(int nPointsIn, const double *padfX,
1211 : const double *padfY, const double *padfZIn)
1212 :
1213 : {
1214 : /* -------------------------------------------------------------------- */
1215 : /* Check 2D/3D. */
1216 : /* -------------------------------------------------------------------- */
1217 48369 : if (padfZIn == nullptr)
1218 40070 : Make2D();
1219 : else
1220 : {
1221 8299 : if (!Make3D())
1222 0 : return false;
1223 : }
1224 :
1225 : /* -------------------------------------------------------------------- */
1226 : /* Assign values. */
1227 : /* -------------------------------------------------------------------- */
1228 48369 : if (!setNumPoints(nPointsIn, FALSE))
1229 0 : return false;
1230 :
1231 567342 : for (int i = 0; i < nPointsIn; i++)
1232 : {
1233 518973 : paoPoints[i].x = padfX[i];
1234 518973 : paoPoints[i].y = padfY[i];
1235 : }
1236 :
1237 48369 : if (padfZ && padfZIn && nPointsIn)
1238 : {
1239 8299 : const void *pUnaligned = padfZIn;
1240 8299 : memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1241 : }
1242 48369 : return true;
1243 : }
1244 :
1245 : /************************************************************************/
1246 : /* setPointsM() */
1247 : /************************************************************************/
1248 :
1249 : /**
1250 : * \brief Assign all points in a line string.
1251 : *
1252 : * This method clear any existing points assigned to this line string,
1253 : * and assigns a whole new set.
1254 : *
1255 : * There is no SFCOM analog to this method.
1256 : *
1257 : * @param nPointsIn number of points being passed in padfX and padfY.
1258 : * @param padfX list of X coordinates of points being assigned.
1259 : * @param padfY list of Y coordinates of points being assigned.
1260 : * @param padfMIn list of M coordinates of points being assigned.
1261 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1262 : */
1263 :
1264 22 : bool OGRSimpleCurve::setPointsM(int nPointsIn, const double *padfX,
1265 : const double *padfY, const double *padfMIn)
1266 :
1267 : {
1268 : /* -------------------------------------------------------------------- */
1269 : /* Check 2D/3D. */
1270 : /* -------------------------------------------------------------------- */
1271 22 : if (padfMIn == nullptr)
1272 4 : RemoveM();
1273 : else
1274 : {
1275 18 : if (!AddM())
1276 0 : return false;
1277 : }
1278 :
1279 : /* -------------------------------------------------------------------- */
1280 : /* Assign values. */
1281 : /* -------------------------------------------------------------------- */
1282 22 : if (!setNumPoints(nPointsIn, FALSE))
1283 0 : return false;
1284 :
1285 90 : for (int i = 0; i < nPointsIn; i++)
1286 : {
1287 68 : paoPoints[i].x = padfX[i];
1288 68 : paoPoints[i].y = padfY[i];
1289 : }
1290 :
1291 22 : if (padfMIn && padfM && nPointsIn)
1292 : {
1293 18 : const void *pUnaligned = padfMIn;
1294 18 : memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1295 : }
1296 22 : return true;
1297 : }
1298 :
1299 : /************************************************************************/
1300 : /* setPoints() */
1301 : /************************************************************************/
1302 :
1303 : /**
1304 : * \brief Assign all points in a line string.
1305 : *
1306 : * This method clear any existing points assigned to this line string,
1307 : * and assigns a whole new set.
1308 : *
1309 : * There is no SFCOM analog to this method.
1310 : *
1311 : * @param nPointsIn number of points being passed in padfX and padfY.
1312 : * @param padfX list of X coordinates of points being assigned.
1313 : * @param padfY list of Y coordinates of points being assigned.
1314 : * @param padfZIn list of Z coordinates of points being assigned.
1315 : * @param padfMIn list of M coordinates of points being assigned.
1316 : * @return (since 3.10) true in case of success, false in case of memory allocation error
1317 : */
1318 :
1319 3281 : bool OGRSimpleCurve::setPoints(int nPointsIn, const double *padfX,
1320 : const double *padfY, const double *padfZIn,
1321 : const double *padfMIn)
1322 :
1323 : {
1324 : /* -------------------------------------------------------------------- */
1325 : /* Check 2D/3D. */
1326 : /* -------------------------------------------------------------------- */
1327 3281 : if (padfZIn == nullptr)
1328 66 : Make2D();
1329 : else
1330 : {
1331 3215 : if (!Make3D())
1332 0 : return false;
1333 : }
1334 :
1335 : /* -------------------------------------------------------------------- */
1336 : /* Check measures. */
1337 : /* -------------------------------------------------------------------- */
1338 3281 : if (padfMIn == nullptr)
1339 3253 : RemoveM();
1340 : else
1341 : {
1342 28 : if (!AddM())
1343 0 : return false;
1344 : }
1345 :
1346 : /* -------------------------------------------------------------------- */
1347 : /* Assign values. */
1348 : /* -------------------------------------------------------------------- */
1349 3281 : if (!setNumPoints(nPointsIn, FALSE))
1350 0 : return false;
1351 :
1352 60728 : for (int i = 0; i < nPointsIn; i++)
1353 : {
1354 57447 : paoPoints[i].x = padfX[i];
1355 57447 : paoPoints[i].y = padfY[i];
1356 : }
1357 :
1358 3281 : if (padfZ != nullptr && padfZIn && nPointsIn)
1359 : {
1360 3215 : const void *pUnaligned = padfZIn;
1361 3215 : memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1362 : }
1363 3281 : if (padfM != nullptr && padfMIn && nPointsIn)
1364 : {
1365 28 : const void *pUnaligned = padfMIn;
1366 28 : memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1367 : }
1368 3281 : return true;
1369 : }
1370 :
1371 : /************************************************************************/
1372 : /* getPoints() */
1373 : /************************************************************************/
1374 :
1375 : /**
1376 : * \brief Returns all points of line string.
1377 : *
1378 : * This method copies all points into user list. This list must be at
1379 : * least sizeof(OGRRawPoint) * OGRGeometry::getNumPoints() byte in size.
1380 : * It also copies all Z coordinates.
1381 : *
1382 : * There is no SFCOM analog to this method.
1383 : *
1384 : * @param paoPointsOut a buffer into which the points is written.
1385 : * @param padfZOut the Z values that go with the points (optional, may be NULL).
1386 : */
1387 :
1388 329 : void OGRSimpleCurve::getPoints(OGRRawPoint *paoPointsOut,
1389 : double *padfZOut) const
1390 : {
1391 329 : if (!paoPointsOut || nPointCount == 0)
1392 3 : return;
1393 :
1394 : {
1395 326 : void *pUnaligned = paoPointsOut;
1396 326 : memcpy(pUnaligned, paoPoints, sizeof(OGRRawPoint) * nPointCount);
1397 : }
1398 :
1399 : /* -------------------------------------------------------------------- */
1400 : /* Check 2D/3D. */
1401 : /* -------------------------------------------------------------------- */
1402 326 : if (padfZOut)
1403 : {
1404 77 : void *pUnaligned = padfZOut;
1405 77 : if (padfZ)
1406 75 : memcpy(pUnaligned, padfZ, sizeof(double) * nPointCount);
1407 : else
1408 2 : memset(pUnaligned, 0, sizeof(double) * nPointCount);
1409 : }
1410 : }
1411 :
1412 : /**
1413 : * \brief Returns all points of line string.
1414 : *
1415 : * This method copies all points into user arrays. The user provides the
1416 : * stride between 2 consecutive elements of the array.
1417 : *
1418 : * On some CPU architectures, care must be taken so that the arrays are properly
1419 : * aligned.
1420 : *
1421 : * There is no SFCOM analog to this method.
1422 : *
1423 : * @param pabyX a buffer of at least (nXStride * nPointCount) bytes, may be
1424 : * NULL.
1425 : * @param nXStride the number of bytes between 2 elements of pabyX.
1426 : * @param pabyY a buffer of at least (nYStride * nPointCount) bytes, may be
1427 : * NULL.
1428 : * @param nYStride the number of bytes between 2 elements of pabyY.
1429 : * @param pabyZ a buffer of at last size (nZStride * nPointCount) bytes, may be
1430 : * NULL.
1431 : * @param nZStride the number of bytes between 2 elements of pabyZ.
1432 : * @param pabyM a buffer of at last size (nMStride * nPointCount) bytes, may be
1433 : * NULL.
1434 : * @param nMStride the number of bytes between 2 elements of pabyM.
1435 : *
1436 : * @since OGR 2.1.0
1437 : */
1438 :
1439 186 : void OGRSimpleCurve::getPoints(void *pabyX, int nXStride, void *pabyY,
1440 : int nYStride, void *pabyZ, int nZStride,
1441 : void *pabyM, int nMStride) const
1442 : {
1443 186 : if (pabyX != nullptr && nXStride == 0)
1444 0 : return;
1445 186 : if (pabyY != nullptr && nYStride == 0)
1446 0 : return;
1447 186 : if (pabyZ != nullptr && nZStride == 0)
1448 0 : return;
1449 186 : if (pabyM != nullptr && nMStride == 0)
1450 0 : return;
1451 186 : if (nXStride == sizeof(OGRRawPoint) && nYStride == sizeof(OGRRawPoint) &&
1452 : static_cast<char *>(pabyY) ==
1453 186 : static_cast<char *>(pabyX) + sizeof(double) &&
1454 77 : (pabyZ == nullptr || nZStride == sizeof(double)))
1455 : {
1456 186 : getPoints(static_cast<OGRRawPoint *>(pabyX),
1457 : static_cast<double *>(pabyZ));
1458 : }
1459 : else
1460 : {
1461 0 : for (int i = 0; i < nPointCount; i++)
1462 : {
1463 0 : if (pabyX)
1464 0 : *reinterpret_cast<double *>(static_cast<char *>(pabyX) +
1465 0 : i * nXStride) = paoPoints[i].x;
1466 0 : if (pabyY)
1467 0 : *reinterpret_cast<double *>(static_cast<char *>(pabyY) +
1468 0 : i * nYStride) = paoPoints[i].y;
1469 : }
1470 :
1471 0 : if (pabyZ)
1472 : {
1473 0 : if (nZStride == sizeof(double))
1474 : {
1475 0 : if (padfZ)
1476 0 : memcpy(pabyZ, padfZ, sizeof(double) * nPointCount);
1477 : else
1478 0 : memset(pabyZ, 0, sizeof(double) * nPointCount);
1479 : }
1480 : else
1481 : {
1482 0 : for (int i = 0; i < nPointCount; i++)
1483 : {
1484 0 : *reinterpret_cast<double *>(static_cast<char *>(pabyZ) +
1485 0 : i * nZStride) =
1486 0 : (padfZ) ? padfZ[i] : 0.0;
1487 : }
1488 : }
1489 : }
1490 : }
1491 186 : if (pabyM)
1492 : {
1493 58 : if (nMStride == sizeof(double))
1494 : {
1495 58 : if (padfM)
1496 58 : memcpy(pabyM, padfM, sizeof(double) * nPointCount);
1497 : else
1498 0 : memset(pabyM, 0, sizeof(double) * nPointCount);
1499 : }
1500 : else
1501 : {
1502 0 : for (int i = 0; i < nPointCount; i++)
1503 : {
1504 0 : *reinterpret_cast<double *>(static_cast<char *>(pabyM) +
1505 0 : i * nMStride) =
1506 0 : (padfM) ? padfM[i] : 0.0;
1507 : }
1508 : }
1509 : }
1510 : }
1511 :
1512 : /************************************************************************/
1513 : /* reversePoints() */
1514 : /************************************************************************/
1515 :
1516 : /**
1517 : * \brief Reverse point order.
1518 : *
1519 : * This method updates the points in this line string in place
1520 : * reversing the point ordering (first for last, etc).
1521 : */
1522 :
1523 2533 : void OGRSimpleCurve::reversePoints()
1524 :
1525 : {
1526 62843 : for (int i = 0; i < nPointCount / 2; i++)
1527 : {
1528 60310 : std::swap(paoPoints[i], paoPoints[nPointCount - i - 1]);
1529 60310 : if (padfZ)
1530 : {
1531 2401 : std::swap(padfZ[i], padfZ[nPointCount - i - 1]);
1532 : }
1533 :
1534 60310 : if (padfM)
1535 : {
1536 6 : std::swap(padfM[i], padfM[nPointCount - i - 1]);
1537 : }
1538 : }
1539 2533 : }
1540 :
1541 : /************************************************************************/
1542 : /* addSubLineString() */
1543 : /************************************************************************/
1544 :
1545 : /**
1546 : * \brief Add a segment of another linestring to this one.
1547 : *
1548 : * Adds the request range of vertices to the end of this line string
1549 : * in an efficient manner. If the nStartVertex is larger than the
1550 : * nEndVertex then the vertices will be reversed as they are copied.
1551 : *
1552 : * @param poOtherLine the other OGRLineString.
1553 : * @param nStartVertex the first vertex to copy, defaults to 0 to start
1554 : * with the first vertex in the other linestring.
1555 : * @param nEndVertex the last vertex to copy, defaults to -1 indicating
1556 : * the last vertex of the other line string.
1557 : */
1558 :
1559 9233 : void OGRSimpleCurve::addSubLineString(const OGRLineString *poOtherLine,
1560 : int nStartVertex, int nEndVertex)
1561 :
1562 : {
1563 9233 : int nOtherLineNumPoints = poOtherLine->getNumPoints();
1564 9233 : if (nOtherLineNumPoints == 0)
1565 0 : return;
1566 :
1567 : /* -------------------------------------------------------------------- */
1568 : /* Do a bit of argument defaulting and validation. */
1569 : /* -------------------------------------------------------------------- */
1570 9233 : if (nEndVertex == -1)
1571 6453 : nEndVertex = nOtherLineNumPoints - 1;
1572 :
1573 9233 : if (nStartVertex < 0 || nEndVertex < 0 ||
1574 9233 : nStartVertex >= nOtherLineNumPoints ||
1575 : nEndVertex >= nOtherLineNumPoints)
1576 : {
1577 0 : CPLAssert(false);
1578 : return;
1579 : }
1580 :
1581 : /* -------------------------------------------------------------------- */
1582 : /* Grow this linestring to hold the additional points. */
1583 : /* -------------------------------------------------------------------- */
1584 9233 : int nOldPoints = nPointCount;
1585 9233 : int nPointsToAdd = std::abs(nEndVertex - nStartVertex) + 1;
1586 :
1587 9233 : if (!setNumPoints(nPointsToAdd + nOldPoints, FALSE)
1588 : #ifdef DEBUG
1589 9233 : || paoPoints == nullptr
1590 : #endif
1591 : )
1592 0 : return;
1593 :
1594 : /* -------------------------------------------------------------------- */
1595 : /* Copy the x/y points - forward copies use memcpy. */
1596 : /* -------------------------------------------------------------------- */
1597 9233 : if (nEndVertex >= nStartVertex)
1598 : {
1599 9042 : memcpy(paoPoints + nOldPoints, poOtherLine->paoPoints + nStartVertex,
1600 9042 : sizeof(OGRRawPoint) * nPointsToAdd);
1601 9042 : if (poOtherLine->padfZ != nullptr)
1602 : {
1603 1171 : Make3D();
1604 1171 : if (padfZ != nullptr)
1605 : {
1606 1171 : memcpy(padfZ + nOldPoints, poOtherLine->padfZ + nStartVertex,
1607 1171 : sizeof(double) * nPointsToAdd);
1608 : }
1609 : }
1610 9042 : if (poOtherLine->padfM != nullptr)
1611 : {
1612 92 : AddM();
1613 92 : if (padfM != nullptr)
1614 : {
1615 92 : memcpy(padfM + nOldPoints, poOtherLine->padfM + nStartVertex,
1616 92 : sizeof(double) * nPointsToAdd);
1617 : }
1618 : }
1619 : }
1620 :
1621 : /* -------------------------------------------------------------------- */
1622 : /* Copy the x/y points - reverse copies done double by double. */
1623 : /* -------------------------------------------------------------------- */
1624 : else
1625 : {
1626 1480 : for (int i = 0; i < nPointsToAdd; i++)
1627 : {
1628 1289 : paoPoints[i + nOldPoints].x =
1629 1289 : poOtherLine->paoPoints[nStartVertex - i].x;
1630 1289 : paoPoints[i + nOldPoints].y =
1631 1289 : poOtherLine->paoPoints[nStartVertex - i].y;
1632 : }
1633 :
1634 191 : if (poOtherLine->padfZ != nullptr)
1635 : {
1636 0 : Make3D();
1637 0 : if (padfZ != nullptr)
1638 : {
1639 0 : for (int i = 0; i < nPointsToAdd; i++)
1640 : {
1641 0 : padfZ[i + nOldPoints] =
1642 0 : poOtherLine->padfZ[nStartVertex - i];
1643 : }
1644 : }
1645 : }
1646 191 : if (poOtherLine->padfM != nullptr)
1647 : {
1648 0 : AddM();
1649 0 : if (padfM != nullptr)
1650 : {
1651 0 : for (int i = 0; i < nPointsToAdd; i++)
1652 : {
1653 0 : padfM[i + nOldPoints] =
1654 0 : poOtherLine->padfM[nStartVertex - i];
1655 : }
1656 : }
1657 : }
1658 : }
1659 : }
1660 :
1661 : /************************************************************************/
1662 : /* importFromWkb() */
1663 : /* */
1664 : /* Initialize from serialized stream in well known binary */
1665 : /* format. */
1666 : /************************************************************************/
1667 :
1668 7053 : OGRErr OGRSimpleCurve::importFromWkb(const unsigned char *pabyData,
1669 : size_t nSize, OGRwkbVariant eWkbVariant,
1670 : size_t &nBytesConsumedOut)
1671 :
1672 : {
1673 : OGRwkbByteOrder eByteOrder;
1674 7053 : size_t nDataOffset = 0;
1675 7053 : int nNewNumPoints = 0;
1676 :
1677 7053 : nBytesConsumedOut = 0;
1678 7053 : OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
1679 : nDataOffset, eByteOrder, 16,
1680 : nNewNumPoints, eWkbVariant);
1681 7054 : if (eErr != OGRERR_NONE)
1682 203 : return eErr;
1683 :
1684 : // Check if the wkb stream buffer is big enough to store
1685 : // fetched number of points.
1686 6851 : const int dim = CoordinateDimension();
1687 6852 : const size_t nPointSize = dim * sizeof(double);
1688 13704 : if (nNewNumPoints < 0 ||
1689 6851 : static_cast<size_t>(nNewNumPoints) >
1690 6851 : std::numeric_limits<size_t>::max() / nPointSize)
1691 : {
1692 0 : return OGRERR_CORRUPT_DATA;
1693 : }
1694 6853 : const size_t nBufferMinSize = nPointSize * nNewNumPoints;
1695 :
1696 6853 : if (nSize != static_cast<size_t>(-1) && nBufferMinSize > nSize)
1697 : {
1698 67 : CPLError(CE_Failure, CPLE_AppDefined,
1699 : "Length of input WKB is too small");
1700 67 : return OGRERR_NOT_ENOUGH_DATA;
1701 : }
1702 :
1703 6786 : if (!setNumPoints(nNewNumPoints, FALSE))
1704 0 : return OGRERR_NOT_ENOUGH_MEMORY;
1705 :
1706 6784 : nBytesConsumedOut = 9 + 8 * static_cast<size_t>(nPointCount) *
1707 6784 : (2 + ((flags & OGR_G_3D) ? 1 : 0) +
1708 6784 : ((flags & OGR_G_MEASURED) ? 1 : 0));
1709 :
1710 : /* -------------------------------------------------------------------- */
1711 : /* Get the vertex. */
1712 : /* -------------------------------------------------------------------- */
1713 6784 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
1714 : {
1715 8832 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1716 : {
1717 5328 : memcpy(paoPoints + i, pabyData + 9 + i * 32, 16);
1718 5328 : memcpy(padfZ + i, pabyData + 9 + 16 + i * 32, 8);
1719 5328 : memcpy(padfM + i, pabyData + 9 + 24 + i * 32, 8);
1720 3504 : }
1721 : }
1722 3280 : else if (flags & OGR_G_MEASURED)
1723 : {
1724 252 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1725 : {
1726 185 : memcpy(paoPoints + i, pabyData + 9 + i * 24, 16);
1727 185 : memcpy(padfM + i, pabyData + 9 + 16 + i * 24, 8);
1728 : }
1729 : }
1730 3213 : else if (flags & OGR_G_3D)
1731 : {
1732 574 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1733 : {
1734 413 : memcpy(paoPoints + i, pabyData + 9 + i * 24, 16);
1735 413 : memcpy(padfZ + i, pabyData + 9 + 16 + i * 24, 8);
1736 : }
1737 : }
1738 3052 : else if (nPointCount)
1739 : {
1740 3018 : memcpy(paoPoints, pabyData + 9, 16 * static_cast<size_t>(nPointCount));
1741 : }
1742 :
1743 : /* -------------------------------------------------------------------- */
1744 : /* Byte swap if needed. */
1745 : /* -------------------------------------------------------------------- */
1746 6784 : if (OGR_SWAP(eByteOrder))
1747 : {
1748 440 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1749 : {
1750 310 : CPL_SWAPDOUBLE(&(paoPoints[i].x));
1751 310 : CPL_SWAPDOUBLE(&(paoPoints[i].y));
1752 : }
1753 :
1754 130 : if (flags & OGR_G_3D)
1755 : {
1756 14 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1757 : {
1758 10 : CPL_SWAPDOUBLE(padfZ + i);
1759 : }
1760 : }
1761 :
1762 130 : if (flags & OGR_G_MEASURED)
1763 : {
1764 6 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1765 : {
1766 4 : CPL_SWAPDOUBLE(padfM + i);
1767 : }
1768 : }
1769 : }
1770 :
1771 6784 : return OGRERR_NONE;
1772 : }
1773 :
1774 : /************************************************************************/
1775 : /* exportToWkb() */
1776 : /* */
1777 : /* Build a well known binary representation of this object. */
1778 : /************************************************************************/
1779 :
1780 10697 : OGRErr OGRSimpleCurve::exportToWkb(unsigned char *pabyData,
1781 : const OGRwkbExportOptions *psOptions) const
1782 :
1783 : {
1784 10697 : if (psOptions == nullptr)
1785 : {
1786 : static const OGRwkbExportOptions defaultOptions;
1787 0 : psOptions = &defaultOptions;
1788 : }
1789 :
1790 : /* -------------------------------------------------------------------- */
1791 : /* Set the byte order. */
1792 : /* -------------------------------------------------------------------- */
1793 10697 : pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
1794 : static_cast<unsigned char>(psOptions->eByteOrder));
1795 :
1796 : /* -------------------------------------------------------------------- */
1797 : /* Set the geometry feature type. */
1798 : /* -------------------------------------------------------------------- */
1799 10697 : GUInt32 nGType = getGeometryType();
1800 :
1801 10697 : if (psOptions->eWkbVariant == wkbVariantPostGIS1)
1802 : {
1803 6 : nGType = wkbFlatten(nGType);
1804 6 : if (Is3D())
1805 : // Explicitly set wkb25DBit.
1806 1 : nGType =
1807 1 : static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
1808 6 : if (IsMeasured())
1809 0 : nGType = static_cast<OGRwkbGeometryType>(nGType | 0x40000000);
1810 : }
1811 10691 : else if (psOptions->eWkbVariant == wkbVariantIso)
1812 10229 : nGType = getIsoGeometryType();
1813 :
1814 10697 : if (psOptions->eByteOrder == wkbNDR)
1815 : {
1816 10684 : CPL_LSBPTR32(&nGType);
1817 : }
1818 : else
1819 : {
1820 13 : CPL_MSBPTR32(&nGType);
1821 : }
1822 :
1823 10697 : memcpy(pabyData + 1, &nGType, 4);
1824 :
1825 : /* -------------------------------------------------------------------- */
1826 : /* Copy in the data count. */
1827 : /* -------------------------------------------------------------------- */
1828 10697 : memcpy(pabyData + 5, &nPointCount, 4);
1829 :
1830 : /* -------------------------------------------------------------------- */
1831 : /* Copy in the raw data. */
1832 : /* -------------------------------------------------------------------- */
1833 10697 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
1834 : {
1835 6367 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1836 : {
1837 4532 : memcpy(pabyData + 9 + 32 * i, paoPoints + i, 16);
1838 4532 : memcpy(pabyData + 9 + 16 + 32 * i, padfZ + i, 8);
1839 4532 : memcpy(pabyData + 9 + 24 + 32 * i, padfM + i, 8);
1840 : }
1841 1835 : OGRRoundCoordinatesIEEE754XYValues<32>(
1842 1835 : psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1843 1835 : OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nZBitPrecision,
1844 : pabyData + 9 + 2 * sizeof(uint64_t),
1845 1835 : nPointCount);
1846 1835 : OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nMBitPrecision,
1847 : pabyData + 9 + 3 * sizeof(uint64_t),
1848 1835 : nPointCount);
1849 : }
1850 8862 : else if (flags & OGR_G_MEASURED)
1851 : {
1852 234 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1853 : {
1854 164 : memcpy(pabyData + 9 + 24 * i, paoPoints + i, 16);
1855 164 : memcpy(pabyData + 9 + 16 + 24 * i, padfM + i, 8);
1856 : }
1857 70 : OGRRoundCoordinatesIEEE754XYValues<24>(
1858 70 : psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1859 70 : OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nMBitPrecision,
1860 : pabyData + 9 + 2 * sizeof(uint64_t),
1861 70 : nPointCount);
1862 : }
1863 8792 : else if (flags & OGR_G_3D)
1864 : {
1865 61147 : for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1866 : {
1867 57610 : memcpy(pabyData + 9 + 24 * i, paoPoints + i, 16);
1868 57610 : memcpy(pabyData + 9 + 16 + 24 * i, padfZ + i, 8);
1869 : }
1870 3537 : OGRRoundCoordinatesIEEE754XYValues<24>(
1871 3537 : psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1872 3537 : OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nZBitPrecision,
1873 : pabyData + 9 + 2 * sizeof(uint64_t),
1874 3537 : nPointCount);
1875 : }
1876 5255 : else if (nPointCount)
1877 : {
1878 5182 : memcpy(pabyData + 9, paoPoints, 16 * static_cast<size_t>(nPointCount));
1879 5182 : OGRRoundCoordinatesIEEE754XYValues<16>(
1880 5182 : psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1881 : }
1882 :
1883 : /* -------------------------------------------------------------------- */
1884 : /* Swap if needed. */
1885 : /* -------------------------------------------------------------------- */
1886 10697 : if (OGR_SWAP(psOptions->eByteOrder))
1887 : {
1888 13 : const int nCount = CPL_SWAP32(nPointCount);
1889 13 : memcpy(pabyData + 5, &nCount, 4);
1890 :
1891 : const size_t nCoords =
1892 13 : CoordinateDimension() * static_cast<size_t>(nPointCount);
1893 175 : for (size_t i = 0; i < nCoords; i++)
1894 : {
1895 162 : CPL_SWAP64PTR(pabyData + 9 + 8 * i);
1896 : }
1897 : }
1898 :
1899 10697 : return OGRERR_NONE;
1900 : }
1901 :
1902 : /************************************************************************/
1903 : /* importFromWkt() */
1904 : /* */
1905 : /* Instantiate from well known text format. Currently this is */
1906 : /* `LINESTRING ( x y, x y, ...)', */
1907 : /************************************************************************/
1908 :
1909 5177 : OGRErr OGRSimpleCurve::importFromWkt(const char **ppszInput)
1910 :
1911 : {
1912 5177 : int bHasZ = FALSE;
1913 5177 : int bHasM = FALSE;
1914 5177 : bool bIsEmpty = false;
1915 : const OGRErr eErr =
1916 5177 : importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
1917 5177 : flags = 0;
1918 5177 : if (eErr != OGRERR_NONE)
1919 5 : return eErr;
1920 5172 : if (bHasZ)
1921 303 : flags |= OGR_G_3D;
1922 5172 : if (bHasM)
1923 135 : flags |= OGR_G_MEASURED;
1924 5172 : if (bIsEmpty)
1925 : {
1926 86 : return OGRERR_NONE;
1927 : }
1928 :
1929 5086 : const char *pszInput = *ppszInput;
1930 :
1931 : /* -------------------------------------------------------------------- */
1932 : /* Read the point list. */
1933 : /* -------------------------------------------------------------------- */
1934 5086 : int flagsFromInput = flags;
1935 5086 : nPointCount = 0;
1936 :
1937 : pszInput =
1938 5086 : OGRWktReadPointsM(pszInput, &paoPoints, &padfZ, &padfM, &flagsFromInput,
1939 : &m_nPointCapacity, &nPointCount);
1940 5086 : if (pszInput == nullptr)
1941 15 : return OGRERR_CORRUPT_DATA;
1942 :
1943 5071 : if ((flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D))
1944 : {
1945 246 : if (!set3D(TRUE))
1946 0 : return OGRERR_NOT_ENOUGH_MEMORY;
1947 : }
1948 5071 : if ((flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED))
1949 : {
1950 0 : if (!setMeasured(TRUE))
1951 0 : return OGRERR_NOT_ENOUGH_MEMORY;
1952 : }
1953 :
1954 5071 : *ppszInput = pszInput;
1955 :
1956 5071 : return OGRERR_NONE;
1957 : }
1958 :
1959 : //! @cond Doxygen_Suppress
1960 : /************************************************************************/
1961 : /* importFromWKTListOnly() */
1962 : /* */
1963 : /* Instantiate from "(x y, x y, ...)" */
1964 : /************************************************************************/
1965 :
1966 1362 : OGRErr OGRSimpleCurve::importFromWKTListOnly(const char **ppszInput, int bHasZ,
1967 : int bHasM,
1968 : OGRRawPoint *&paoPointsIn,
1969 : int &nMaxPointsIn,
1970 : double *&padfZIn)
1971 :
1972 : {
1973 1362 : const char *pszInput = *ppszInput;
1974 :
1975 : /* -------------------------------------------------------------------- */
1976 : /* Read the point list. */
1977 : /* -------------------------------------------------------------------- */
1978 1362 : int flagsFromInput = flags;
1979 1362 : int nPointCountRead = 0;
1980 1362 : double *padfMIn = nullptr;
1981 1362 : if (flagsFromInput == 0) // Flags was not set, this is not called by us.
1982 : {
1983 1362 : if (bHasM)
1984 124 : flagsFromInput |= OGR_G_MEASURED;
1985 1362 : if (bHasZ)
1986 197 : flagsFromInput |= OGR_G_3D;
1987 : }
1988 :
1989 : pszInput =
1990 1362 : OGRWktReadPointsM(pszInput, &paoPointsIn, &padfZIn, &padfMIn,
1991 : &flagsFromInput, &nMaxPointsIn, &nPointCountRead);
1992 :
1993 1362 : if (pszInput == nullptr)
1994 : {
1995 6 : CPLFree(padfMIn);
1996 6 : return OGRERR_CORRUPT_DATA;
1997 : }
1998 1356 : if ((flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D))
1999 : {
2000 341 : flags |= OGR_G_3D;
2001 341 : bHasZ = TRUE;
2002 : }
2003 1356 : if ((flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED))
2004 : {
2005 124 : flags |= OGR_G_MEASURED;
2006 124 : bHasM = TRUE;
2007 : }
2008 :
2009 1356 : *ppszInput = pszInput;
2010 :
2011 1356 : if (bHasM && bHasZ)
2012 72 : setPoints(nPointCountRead, paoPointsIn, padfZIn, padfMIn);
2013 1284 : else if (bHasM && !bHasZ)
2014 52 : setPointsM(nPointCountRead, paoPointsIn, padfMIn);
2015 : else
2016 1232 : setPoints(nPointCountRead, paoPointsIn, padfZIn);
2017 :
2018 1356 : CPLFree(padfMIn);
2019 :
2020 1356 : return OGRERR_NONE;
2021 : }
2022 :
2023 : //! @endcond
2024 :
2025 : /************************************************************************/
2026 : /* exportToWkt() */
2027 : /* */
2028 : /* Translate this structure into its well known text format */
2029 : /* equivalent. This could be made a lot more CPU efficient. */
2030 : /************************************************************************/
2031 :
2032 3731 : std::string OGRSimpleCurve::exportToWkt(const OGRWktOptions &opts,
2033 : OGRErr *err) const
2034 : {
2035 : // LINEARRING or LINESTRING or CIRCULARSTRING
2036 7462 : std::string wkt = getGeometryName();
2037 3731 : wkt += wktTypeString(opts.variant);
2038 3731 : if (IsEmpty())
2039 : {
2040 90 : wkt += "EMPTY";
2041 : }
2042 : else
2043 : {
2044 3641 : wkt += '(';
2045 :
2046 3641 : OGRBoolean hasZ = Is3D();
2047 : OGRBoolean hasM =
2048 3641 : (opts.variant != wkbVariantIso ? FALSE : IsMeasured());
2049 :
2050 : try
2051 : {
2052 3641 : const int nOrdinatesPerVertex =
2053 3641 : 2 + ((hasZ) ? 1 : 0) + ((hasM) ? 1 : 0);
2054 : // At least 2 bytes per ordinate: one for the value,
2055 : // and one for the separator...
2056 3641 : wkt.reserve(wkt.size() + 2 * static_cast<size_t>(nPointCount) *
2057 3641 : nOrdinatesPerVertex);
2058 :
2059 35809 : for (int i = 0; i < nPointCount; i++)
2060 : {
2061 32168 : if (i > 0)
2062 28527 : wkt += ',';
2063 :
2064 91508 : wkt += OGRMakeWktCoordinateM(
2065 60918 : paoPoints[i].x, paoPoints[i].y, padfZ ? padfZ[i] : 0.0,
2066 64336 : padfM ? padfM[i] : 0.0, hasZ, hasM, opts);
2067 : }
2068 3641 : wkt += ')';
2069 : }
2070 0 : catch (const std::bad_alloc &e)
2071 : {
2072 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2073 0 : if (err)
2074 0 : *err = OGRERR_FAILURE;
2075 0 : return std::string();
2076 : }
2077 : }
2078 3731 : if (err)
2079 3722 : *err = OGRERR_NONE;
2080 3731 : return wkt;
2081 : }
2082 :
2083 : /************************************************************************/
2084 : /* get_Length() */
2085 : /* */
2086 : /* For now we return a simple euclidean 2D distance. */
2087 : /************************************************************************/
2088 :
2089 1143 : double OGRSimpleCurve::get_Length() const
2090 :
2091 : {
2092 1143 : double dfLength = 0.0;
2093 :
2094 3285 : for (int i = 0; i < nPointCount - 1; i++)
2095 : {
2096 :
2097 2142 : const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2098 2142 : const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2099 2142 : dfLength += sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2100 : }
2101 :
2102 1143 : return dfLength;
2103 : }
2104 :
2105 : /************************************************************************/
2106 : /* StartPoint() */
2107 : /************************************************************************/
2108 :
2109 288055 : void OGRSimpleCurve::StartPoint(OGRPoint *poPoint) const
2110 :
2111 : {
2112 288055 : getPoint(0, poPoint);
2113 288055 : }
2114 :
2115 : /************************************************************************/
2116 : /* EndPoint() */
2117 : /************************************************************************/
2118 :
2119 227815 : void OGRSimpleCurve::EndPoint(OGRPoint *poPoint) const
2120 :
2121 : {
2122 227815 : getPoint(nPointCount - 1, poPoint);
2123 227815 : }
2124 :
2125 : /************************************************************************/
2126 : /* Value() */
2127 : /* */
2128 : /* Get an interpolated point at some distance along the curve. */
2129 : /************************************************************************/
2130 :
2131 52 : void OGRSimpleCurve::Value(double dfDistance, OGRPoint *poPoint) const
2132 :
2133 : {
2134 52 : if (dfDistance < 0)
2135 : {
2136 1 : StartPoint(poPoint);
2137 1 : return;
2138 : }
2139 :
2140 51 : double dfLength = 0.0;
2141 :
2142 192 : for (int i = 0; i < nPointCount - 1; i++)
2143 : {
2144 191 : const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2145 191 : const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2146 : const double dfSegLength =
2147 191 : sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2148 :
2149 191 : if (dfSegLength > 0)
2150 : {
2151 171 : if ((dfLength <= dfDistance) &&
2152 171 : ((dfLength + dfSegLength) >= dfDistance))
2153 : {
2154 50 : double dfRatio = (dfDistance - dfLength) / dfSegLength;
2155 :
2156 50 : poPoint->setX(paoPoints[i].x * (1 - dfRatio) +
2157 50 : paoPoints[i + 1].x * dfRatio);
2158 50 : poPoint->setY(paoPoints[i].y * (1 - dfRatio) +
2159 50 : paoPoints[i + 1].y * dfRatio);
2160 :
2161 50 : if (getCoordinateDimension() == 3)
2162 1 : poPoint->setZ(padfZ[i] * (1 - dfRatio) +
2163 1 : padfZ[i + 1] * dfRatio);
2164 :
2165 50 : return;
2166 : }
2167 :
2168 121 : dfLength += dfSegLength;
2169 : }
2170 : }
2171 :
2172 1 : EndPoint(poPoint);
2173 : }
2174 :
2175 : /************************************************************************/
2176 : /* Project() */
2177 : /* */
2178 : /* Return distance of point projected on line from origin of this line. */
2179 : /************************************************************************/
2180 :
2181 : /**
2182 : * \brief Project point on linestring.
2183 : *
2184 : * The input point projected on linestring. This is the shortest distance
2185 : * from point to the linestring. The distance from begin of linestring to
2186 : * the point projection returned.
2187 : *
2188 : * This method is built on the GEOS library. Check it for the
2189 : * definition of the geometry operation.
2190 : * If OGR is built without the GEOS library, this method will always return -1,
2191 : * issuing a CPLE_NotSupported error.
2192 : *
2193 : * @return a distance from the begin of the linestring to the projected point.
2194 : */
2195 :
2196 62 : double OGRSimpleCurve::Project(const OGRPoint *poPoint) const
2197 :
2198 : {
2199 62 : double dfResult = -1;
2200 : #ifndef HAVE_GEOS
2201 : CPL_IGNORE_RET_VAL(poPoint);
2202 : CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2203 : return dfResult;
2204 : #else
2205 62 : GEOSGeom hThisGeosGeom = nullptr;
2206 62 : GEOSGeom hPointGeosGeom = nullptr;
2207 :
2208 62 : GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2209 62 : hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2210 62 : hPointGeosGeom = poPoint->exportToGEOS(hGEOSCtxt);
2211 62 : if (hThisGeosGeom != nullptr && hPointGeosGeom != nullptr)
2212 : {
2213 62 : dfResult = GEOSProject_r(hGEOSCtxt, hThisGeosGeom, hPointGeosGeom);
2214 : }
2215 62 : GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2216 62 : GEOSGeom_destroy_r(hGEOSCtxt, hPointGeosGeom);
2217 62 : freeGEOSContext(hGEOSCtxt);
2218 :
2219 62 : return dfResult;
2220 :
2221 : #endif // HAVE_GEOS
2222 : }
2223 :
2224 : /************************************************************************/
2225 : /* getSubLine() */
2226 : /* */
2227 : /* Extracts a portion of this OGRLineString into a new OGRLineString. */
2228 : /************************************************************************/
2229 :
2230 : /**
2231 : * \brief Get the portion of linestring.
2232 : *
2233 : * The portion of the linestring extracted to new one. The input distances
2234 : * (maybe present as ratio of length of linestring) set begin and end of
2235 : * extracted portion.
2236 : *
2237 : * @param dfDistanceFrom The distance from the origin of linestring, where the
2238 : * subline should begins
2239 : * @param dfDistanceTo The distance from the origin of linestring, where the
2240 : * subline should ends
2241 : * @param bAsRatio The flag indicating that distances are the ratio of the
2242 : * linestring length.
2243 : *
2244 : * @return a newly allocated linestring now owned by the caller, or NULL on
2245 : * failure.
2246 : *
2247 : * @since OGR 1.11.0
2248 : */
2249 :
2250 53 : OGRLineString *OGRSimpleCurve::getSubLine(double dfDistanceFrom,
2251 : double dfDistanceTo,
2252 : int bAsRatio) const
2253 :
2254 : {
2255 106 : auto poNewLineString = std::make_unique<OGRLineString>();
2256 :
2257 53 : poNewLineString->assignSpatialReference(getSpatialReference());
2258 53 : poNewLineString->setCoordinateDimension(getCoordinateDimension());
2259 :
2260 53 : const double dfLen = get_Length();
2261 53 : if (bAsRatio == TRUE)
2262 : {
2263 : // Convert to real distance.
2264 0 : dfDistanceFrom *= dfLen;
2265 0 : dfDistanceTo *= dfLen;
2266 : }
2267 :
2268 53 : if (dfDistanceFrom < 0)
2269 0 : dfDistanceFrom = 0;
2270 53 : if (dfDistanceTo > dfLen)
2271 0 : dfDistanceTo = dfLen;
2272 :
2273 53 : if (dfDistanceFrom > dfDistanceTo || dfDistanceFrom >= dfLen)
2274 : {
2275 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Input distances are invalid.");
2276 :
2277 0 : return nullptr;
2278 : }
2279 :
2280 53 : double dfLength = 0.0;
2281 :
2282 : // Get first point.
2283 :
2284 53 : int i = 0; // Used after if blocks.
2285 53 : if (dfDistanceFrom == 0)
2286 : {
2287 : bool bRet;
2288 5 : if (getCoordinateDimension() == 3)
2289 0 : bRet = poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y,
2290 0 : padfZ[0]);
2291 : else
2292 5 : bRet = poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y);
2293 5 : if (!bRet)
2294 0 : return nullptr;
2295 : }
2296 : else
2297 : {
2298 367 : for (i = 0; i < nPointCount - 1; i++)
2299 : {
2300 367 : const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2301 367 : const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2302 : const double dfSegLength =
2303 367 : sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2304 :
2305 367 : if (dfSegLength > 0)
2306 : {
2307 367 : if ((dfLength <= dfDistanceFrom) &&
2308 367 : ((dfLength + dfSegLength) >= dfDistanceFrom))
2309 : {
2310 48 : double dfRatio = (dfDistanceFrom - dfLength) / dfSegLength;
2311 :
2312 48 : double dfX = paoPoints[i].x * (1 - dfRatio) +
2313 48 : paoPoints[i + 1].x * dfRatio;
2314 48 : double dfY = paoPoints[i].y * (1 - dfRatio) +
2315 48 : paoPoints[i + 1].y * dfRatio;
2316 :
2317 : bool bRet;
2318 48 : if (getCoordinateDimension() == 3)
2319 : {
2320 0 : bRet = poNewLineString->addPoint(
2321 : dfX, dfY,
2322 0 : padfZ[i] * (1 - dfRatio) + padfZ[i + 1] * dfRatio);
2323 : }
2324 : else
2325 : {
2326 48 : bRet = poNewLineString->addPoint(dfX, dfY);
2327 : }
2328 48 : if (!bRet)
2329 0 : return nullptr;
2330 :
2331 : // Check if dfDistanceTo is in same segment.
2332 48 : if (dfLength <= dfDistanceTo &&
2333 48 : (dfLength + dfSegLength) >= dfDistanceTo)
2334 : {
2335 27 : dfRatio = (dfDistanceTo - dfLength) / dfSegLength;
2336 :
2337 27 : dfX = paoPoints[i].x * (1 - dfRatio) +
2338 27 : paoPoints[i + 1].x * dfRatio;
2339 27 : dfY = paoPoints[i].y * (1 - dfRatio) +
2340 27 : paoPoints[i + 1].y * dfRatio;
2341 :
2342 27 : if (getCoordinateDimension() == 3)
2343 : {
2344 0 : bRet = poNewLineString->addPoint(
2345 : dfX, dfY,
2346 0 : padfZ[i] * (1 - dfRatio) +
2347 0 : padfZ[i + 1] * dfRatio);
2348 : }
2349 : else
2350 : {
2351 27 : bRet = poNewLineString->addPoint(dfX, dfY);
2352 : }
2353 :
2354 27 : if (!bRet || poNewLineString->getNumPoints() < 2)
2355 : {
2356 0 : return nullptr;
2357 : }
2358 :
2359 27 : return poNewLineString.release();
2360 : }
2361 21 : i++;
2362 21 : dfLength += dfSegLength;
2363 21 : break;
2364 : }
2365 :
2366 319 : dfLength += dfSegLength;
2367 : }
2368 : }
2369 : }
2370 :
2371 : // Add points.
2372 55 : for (; i < nPointCount - 1; i++)
2373 : {
2374 : bool bRet;
2375 55 : if (getCoordinateDimension() == 3)
2376 0 : bRet = poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y,
2377 0 : padfZ[i]);
2378 : else
2379 55 : bRet = poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y);
2380 55 : if (!bRet)
2381 0 : return nullptr;
2382 :
2383 55 : const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2384 55 : const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2385 : const double dfSegLength =
2386 55 : sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2387 :
2388 55 : if (dfSegLength > 0)
2389 : {
2390 55 : if ((dfLength <= dfDistanceTo) &&
2391 55 : ((dfLength + dfSegLength) >= dfDistanceTo))
2392 : {
2393 26 : const double dfRatio = (dfDistanceTo - dfLength) / dfSegLength;
2394 :
2395 26 : const double dfX = paoPoints[i].x * (1 - dfRatio) +
2396 26 : paoPoints[i + 1].x * dfRatio;
2397 26 : const double dfY = paoPoints[i].y * (1 - dfRatio) +
2398 26 : paoPoints[i + 1].y * dfRatio;
2399 :
2400 26 : if (getCoordinateDimension() == 3)
2401 0 : bRet = poNewLineString->addPoint(
2402 : dfX, dfY,
2403 0 : padfZ[i] * (1 - dfRatio) + padfZ[i + 1] * dfRatio);
2404 : else
2405 26 : bRet = poNewLineString->addPoint(dfX, dfY);
2406 26 : if (!bRet)
2407 0 : return nullptr;
2408 :
2409 26 : return poNewLineString.release();
2410 : }
2411 :
2412 29 : dfLength += dfSegLength;
2413 : }
2414 : }
2415 :
2416 : bool bRet;
2417 0 : if (getCoordinateDimension() == 3)
2418 0 : bRet = poNewLineString->addPoint(paoPoints[nPointCount - 1].x,
2419 0 : paoPoints[nPointCount - 1].y,
2420 0 : padfZ[nPointCount - 1]);
2421 : else
2422 0 : bRet = poNewLineString->addPoint(paoPoints[nPointCount - 1].x,
2423 0 : paoPoints[nPointCount - 1].y);
2424 :
2425 0 : if (!bRet || poNewLineString->getNumPoints() < 2)
2426 : {
2427 0 : return nullptr;
2428 : }
2429 :
2430 0 : return poNewLineString.release();
2431 : }
2432 :
2433 : /************************************************************************/
2434 : /* getEnvelope() */
2435 : /************************************************************************/
2436 :
2437 5670440 : void OGRSimpleCurve::getEnvelope(OGREnvelope *psEnvelope) const
2438 :
2439 : {
2440 5670440 : if (IsEmpty())
2441 : {
2442 3 : psEnvelope->MinX = 0.0;
2443 3 : psEnvelope->MaxX = 0.0;
2444 3 : psEnvelope->MinY = 0.0;
2445 3 : psEnvelope->MaxY = 0.0;
2446 3 : return;
2447 : }
2448 :
2449 5670430 : double dfMinX = paoPoints[0].x;
2450 5670430 : double dfMaxX = paoPoints[0].x;
2451 5670430 : double dfMinY = paoPoints[0].y;
2452 5670430 : double dfMaxY = paoPoints[0].y;
2453 :
2454 32115300 : for (int iPoint = 1; iPoint < nPointCount; iPoint++)
2455 : {
2456 26444900 : if (dfMaxX < paoPoints[iPoint].x)
2457 7541960 : dfMaxX = paoPoints[iPoint].x;
2458 26444900 : if (dfMaxY < paoPoints[iPoint].y)
2459 7492680 : dfMaxY = paoPoints[iPoint].y;
2460 26444900 : if (dfMinX > paoPoints[iPoint].x)
2461 4783170 : dfMinX = paoPoints[iPoint].x;
2462 26444900 : if (dfMinY > paoPoints[iPoint].y)
2463 5603920 : dfMinY = paoPoints[iPoint].y;
2464 : }
2465 :
2466 5670430 : psEnvelope->MinX = dfMinX;
2467 5670430 : psEnvelope->MaxX = dfMaxX;
2468 5670430 : psEnvelope->MinY = dfMinY;
2469 5670430 : psEnvelope->MaxY = dfMaxY;
2470 : }
2471 :
2472 : /************************************************************************/
2473 : /* getEnvelope() */
2474 : /************************************************************************/
2475 :
2476 641143 : void OGRSimpleCurve::getEnvelope(OGREnvelope3D *psEnvelope) const
2477 :
2478 : {
2479 641143 : getEnvelope(static_cast<OGREnvelope *>(psEnvelope));
2480 :
2481 641143 : if (IsEmpty() || padfZ == nullptr)
2482 : {
2483 264078 : psEnvelope->MinZ = 0.0;
2484 264078 : psEnvelope->MaxZ = 0.0;
2485 264078 : return;
2486 : }
2487 :
2488 377064 : double dfMinZ = padfZ[0];
2489 377064 : double dfMaxZ = padfZ[0];
2490 :
2491 1548030 : for (int iPoint = 1; iPoint < nPointCount; iPoint++)
2492 : {
2493 1170970 : if (dfMinZ > padfZ[iPoint])
2494 369959 : dfMinZ = padfZ[iPoint];
2495 1170970 : if (dfMaxZ < padfZ[iPoint])
2496 230791 : dfMaxZ = padfZ[iPoint];
2497 : }
2498 :
2499 377064 : psEnvelope->MinZ = dfMinZ;
2500 377064 : psEnvelope->MaxZ = dfMaxZ;
2501 : }
2502 :
2503 : /************************************************************************/
2504 : /* Equals() */
2505 : /************************************************************************/
2506 :
2507 48056 : OGRBoolean OGRSimpleCurve::Equals(const OGRGeometry *poOther) const
2508 :
2509 : {
2510 48056 : if (poOther == this)
2511 1 : return TRUE;
2512 :
2513 48055 : if (poOther->getGeometryType() != getGeometryType())
2514 1 : return FALSE;
2515 :
2516 48054 : if (IsEmpty() && poOther->IsEmpty())
2517 1 : return TRUE;
2518 :
2519 : // TODO(schwehr): Test the SRS.
2520 :
2521 48053 : auto poOLine = poOther->toSimpleCurve();
2522 48053 : if (getNumPoints() != poOLine->getNumPoints())
2523 8 : return FALSE;
2524 :
2525 250190 : for (int iPoint = 0; iPoint < getNumPoints(); iPoint++)
2526 : {
2527 428589 : if (getX(iPoint) != poOLine->getX(iPoint) ||
2528 428589 : getY(iPoint) != poOLine->getY(iPoint) ||
2529 202145 : getZ(iPoint) != poOLine->getZ(iPoint))
2530 13993 : return FALSE;
2531 : }
2532 :
2533 34052 : return TRUE;
2534 : }
2535 :
2536 : /************************************************************************/
2537 : /* transform() */
2538 : /************************************************************************/
2539 :
2540 7658 : OGRErr OGRSimpleCurve::transform(OGRCoordinateTransformation *poCT)
2541 :
2542 : {
2543 : /* -------------------------------------------------------------------- */
2544 : /* Make a copy of the points to operate on, so as to be able to */
2545 : /* keep only valid reprojected points if partial reprojection enabled */
2546 : /* or keeping intact the original geometry if only full reprojection */
2547 : /* allowed. */
2548 : /* -------------------------------------------------------------------- */
2549 : double *xyz = static_cast<double *>(
2550 7658 : VSI_MALLOC_VERBOSE(sizeof(double) * nPointCount * 3));
2551 : int *pabSuccess =
2552 7658 : static_cast<int *>(VSI_CALLOC_VERBOSE(sizeof(int), nPointCount));
2553 7658 : if (xyz == nullptr || pabSuccess == nullptr)
2554 : {
2555 0 : VSIFree(xyz);
2556 0 : VSIFree(pabSuccess);
2557 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2558 : }
2559 :
2560 70197 : for (int i = 0; i < nPointCount; i++)
2561 : {
2562 62539 : xyz[i] = paoPoints[i].x;
2563 62539 : xyz[i + nPointCount] = paoPoints[i].y;
2564 62539 : if (padfZ)
2565 34517 : xyz[i + nPointCount * 2] = padfZ[i];
2566 : else
2567 28022 : xyz[i + nPointCount * 2] = 0.0;
2568 : }
2569 :
2570 : /* -------------------------------------------------------------------- */
2571 : /* Transform and reapply. */
2572 : /* -------------------------------------------------------------------- */
2573 7658 : poCT->Transform(nPointCount, xyz, xyz + nPointCount, xyz + nPointCount * 2,
2574 7658 : nullptr, pabSuccess);
2575 :
2576 7658 : const char *pszEnablePartialReprojection = nullptr;
2577 :
2578 7658 : int j = 0; // Used after for.
2579 70197 : for (int i = 0; i < nPointCount; i++)
2580 : {
2581 62539 : if (pabSuccess[i])
2582 : {
2583 62539 : xyz[j] = xyz[i];
2584 62539 : xyz[j + nPointCount] = xyz[i + nPointCount];
2585 62539 : xyz[j + 2 * nPointCount] = xyz[i + 2 * nPointCount];
2586 62539 : j++;
2587 : }
2588 : else
2589 : {
2590 0 : if (pszEnablePartialReprojection == nullptr)
2591 0 : pszEnablePartialReprojection = CPLGetConfigOption(
2592 : "OGR_ENABLE_PARTIAL_REPROJECTION", nullptr);
2593 0 : if (pszEnablePartialReprojection == nullptr)
2594 : {
2595 : static bool bHasWarned = false;
2596 0 : if (!bHasWarned)
2597 : {
2598 : // Check that there is at least one valid reprojected point
2599 : // and issue an error giving an hint to use
2600 : // OGR_ENABLE_PARTIAL_REPROJECTION.
2601 0 : bool bHasOneValidPoint = j != 0;
2602 0 : for (; i < nPointCount && !bHasOneValidPoint; i++)
2603 : {
2604 0 : if (pabSuccess[i])
2605 0 : bHasOneValidPoint = true;
2606 : }
2607 0 : if (bHasOneValidPoint)
2608 : {
2609 0 : bHasWarned = true;
2610 0 : CPLError(CE_Failure, CPLE_AppDefined,
2611 : "Full reprojection failed, but partial is "
2612 : "possible if you define "
2613 : "OGR_ENABLE_PARTIAL_REPROJECTION "
2614 : "configuration option to TRUE");
2615 : }
2616 : }
2617 :
2618 0 : CPLFree(xyz);
2619 0 : CPLFree(pabSuccess);
2620 0 : return OGRERR_FAILURE;
2621 : }
2622 0 : else if (!CPLTestBool(pszEnablePartialReprojection))
2623 : {
2624 0 : CPLFree(xyz);
2625 0 : CPLFree(pabSuccess);
2626 0 : return OGRERR_FAILURE;
2627 : }
2628 : }
2629 : }
2630 :
2631 7658 : if (j == 0 && nPointCount != 0)
2632 : {
2633 0 : CPLFree(xyz);
2634 0 : CPLFree(pabSuccess);
2635 0 : return OGRERR_FAILURE;
2636 : }
2637 :
2638 7658 : setPoints(j, xyz, xyz + nPointCount,
2639 7658 : (padfZ) ? xyz + nPointCount * 2 : nullptr);
2640 7658 : CPLFree(xyz);
2641 7658 : CPLFree(pabSuccess);
2642 :
2643 7658 : assignSpatialReference(poCT->GetTargetCS());
2644 :
2645 7658 : return OGRERR_NONE;
2646 : }
2647 :
2648 : /************************************************************************/
2649 : /* IsEmpty() */
2650 : /************************************************************************/
2651 :
2652 7819830 : OGRBoolean OGRSimpleCurve::IsEmpty() const
2653 : {
2654 7819830 : return (nPointCount == 0);
2655 : }
2656 :
2657 : /************************************************************************/
2658 : /* OGRSimpleCurve::segmentize() */
2659 : /************************************************************************/
2660 :
2661 75 : bool OGRSimpleCurve::segmentize(double dfMaxLength)
2662 : {
2663 75 : if (dfMaxLength <= 0)
2664 : {
2665 0 : CPLError(CE_Failure, CPLE_AppDefined,
2666 : "dfMaxLength must be strictly positive");
2667 0 : return false;
2668 : }
2669 75 : if (nPointCount < 2)
2670 0 : return true;
2671 :
2672 : // So as to make sure that the same line followed in both directions
2673 : // result in the same segmentized line.
2674 75 : if (paoPoints[0].x < paoPoints[nPointCount - 1].x ||
2675 68 : (paoPoints[0].x == paoPoints[nPointCount - 1].x &&
2676 60 : paoPoints[0].y < paoPoints[nPointCount - 1].y))
2677 : {
2678 16 : reversePoints();
2679 16 : bool bRet = segmentize(dfMaxLength);
2680 16 : reversePoints();
2681 16 : return bRet;
2682 : }
2683 :
2684 59 : int nNewPointCount = 0;
2685 59 : const double dfSquareMaxLength = dfMaxLength * dfMaxLength;
2686 :
2687 : // First pass to compute new number of points
2688 59 : constexpr double REL_EPSILON_LENGTH_SQUARE = 1e-5;
2689 59 : constexpr double REL_EPSILON_ROUND = 1e-2;
2690 740 : for (int i = 0; i < nPointCount; i++)
2691 : {
2692 740 : nNewPointCount++;
2693 :
2694 740 : if (i == nPointCount - 1)
2695 58 : break;
2696 :
2697 : // Must be kept in sync with the second pass loop
2698 682 : const double dfX = paoPoints[i + 1].x - paoPoints[i].x;
2699 682 : const double dfY = paoPoints[i + 1].y - paoPoints[i].y;
2700 682 : const double dfSquareDist = dfX * dfX + dfY * dfY;
2701 682 : if (dfSquareDist - dfSquareMaxLength >
2702 682 : REL_EPSILON_LENGTH_SQUARE * dfSquareMaxLength)
2703 : {
2704 307 : const double dfIntermediatePoints = floor(
2705 307 : sqrt(dfSquareDist / dfSquareMaxLength) - REL_EPSILON_ROUND);
2706 : const int nIntermediatePoints =
2707 307 : DoubleToIntClamp(dfIntermediatePoints);
2708 :
2709 : // TODO(schwehr): Can these be tighter?
2710 : // Limit allocation of paoNewPoints to a few GB of memory.
2711 : // An OGRRawPoint is 2 doubles.
2712 : // kMax is a guess of what a reasonable max might be.
2713 307 : constexpr int kMax = 2 << 26;
2714 307 : if (nNewPointCount > kMax || nIntermediatePoints > kMax)
2715 : {
2716 1 : CPLError(CE_Failure, CPLE_AppDefined,
2717 : "Too many points in a segment: %d or %d",
2718 : nNewPointCount, nIntermediatePoints);
2719 1 : return false;
2720 : }
2721 :
2722 306 : nNewPointCount += nIntermediatePoints;
2723 : }
2724 : }
2725 :
2726 58 : if (nPointCount == nNewPointCount)
2727 8 : return true;
2728 :
2729 : // Allocate new arrays
2730 : OGRRawPoint *paoNewPoints = static_cast<OGRRawPoint *>(
2731 50 : VSI_MALLOC_VERBOSE(sizeof(OGRRawPoint) * nNewPointCount));
2732 50 : if (paoNewPoints == nullptr)
2733 0 : return false;
2734 50 : double *padfNewZ = nullptr;
2735 50 : double *padfNewM = nullptr;
2736 50 : if (padfZ != nullptr)
2737 : {
2738 : padfNewZ = static_cast<double *>(
2739 2 : VSI_MALLOC_VERBOSE(sizeof(double) * nNewPointCount));
2740 2 : if (padfNewZ == nullptr)
2741 : {
2742 0 : VSIFree(paoNewPoints);
2743 0 : return false;
2744 : }
2745 : }
2746 50 : if (padfM != nullptr)
2747 : {
2748 : padfNewM = static_cast<double *>(
2749 2 : VSI_MALLOC_VERBOSE(sizeof(double) * nNewPointCount));
2750 2 : if (padfNewM == nullptr)
2751 : {
2752 0 : VSIFree(paoNewPoints);
2753 0 : VSIFree(padfNewZ);
2754 0 : return false;
2755 : }
2756 : }
2757 :
2758 : // Second pass to fill new arrays
2759 : // Must be kept in sync with the first pass loop
2760 50 : nNewPointCount = 0;
2761 639 : for (int i = 0; i < nPointCount; i++)
2762 : {
2763 639 : paoNewPoints[nNewPointCount] = paoPoints[i];
2764 :
2765 639 : if (padfZ != nullptr)
2766 : {
2767 4 : padfNewZ[nNewPointCount] = padfZ[i];
2768 : }
2769 :
2770 639 : if (padfM != nullptr)
2771 : {
2772 4 : padfNewM[nNewPointCount] = padfM[i];
2773 : }
2774 :
2775 639 : nNewPointCount++;
2776 :
2777 639 : if (i == nPointCount - 1)
2778 50 : break;
2779 :
2780 589 : const double dfX = paoPoints[i + 1].x - paoPoints[i].x;
2781 589 : const double dfY = paoPoints[i + 1].y - paoPoints[i].y;
2782 589 : const double dfSquareDist = dfX * dfX + dfY * dfY;
2783 :
2784 : // Must be kept in sync with the initial pass loop
2785 589 : if (dfSquareDist - dfSquareMaxLength >
2786 589 : REL_EPSILON_LENGTH_SQUARE * dfSquareMaxLength)
2787 : {
2788 304 : const double dfIntermediatePoints = floor(
2789 304 : sqrt(dfSquareDist / dfSquareMaxLength) - REL_EPSILON_ROUND);
2790 : const int nIntermediatePoints =
2791 304 : DoubleToIntClamp(dfIntermediatePoints);
2792 304 : const double dfRatioX =
2793 304 : dfX / (static_cast<double>(nIntermediatePoints) + 1);
2794 304 : const double dfRatioY =
2795 304 : dfY / (static_cast<double>(nIntermediatePoints) + 1);
2796 :
2797 21606 : for (int j = 1; j <= nIntermediatePoints; j++)
2798 : {
2799 : // coverity[overflow_const]
2800 21302 : const int newI = nNewPointCount + j - 1;
2801 21302 : paoNewPoints[newI].x = paoPoints[i].x + j * dfRatioX;
2802 21302 : paoNewPoints[newI].y = paoPoints[i].y + j * dfRatioY;
2803 21302 : if (padfZ != nullptr)
2804 : {
2805 : // No interpolation.
2806 10 : padfNewZ[newI] = padfZ[i];
2807 : }
2808 21302 : if (padfM != nullptr)
2809 : {
2810 : // No interpolation.
2811 2 : padfNewM[newI] = padfM[i];
2812 : }
2813 : }
2814 :
2815 304 : nNewPointCount += nIntermediatePoints;
2816 : }
2817 : }
2818 :
2819 50 : CPLFree(paoPoints);
2820 50 : paoPoints = paoNewPoints;
2821 50 : nPointCount = nNewPointCount;
2822 50 : m_nPointCapacity = nNewPointCount;
2823 :
2824 50 : if (padfZ != nullptr)
2825 : {
2826 2 : CPLFree(padfZ);
2827 2 : padfZ = padfNewZ;
2828 : }
2829 50 : if (padfM != nullptr)
2830 : {
2831 2 : CPLFree(padfM);
2832 2 : padfM = padfNewM;
2833 : }
2834 50 : return true;
2835 : }
2836 :
2837 : /************************************************************************/
2838 : /* swapXY() */
2839 : /************************************************************************/
2840 :
2841 102 : void OGRSimpleCurve::swapXY()
2842 : {
2843 656 : for (int i = 0; i < nPointCount; i++)
2844 : {
2845 554 : std::swap(paoPoints[i].x, paoPoints[i].y);
2846 : }
2847 102 : }
2848 :
2849 : /************************************************************************/
2850 : /* OGRSimpleCurvePointIterator */
2851 : /************************************************************************/
2852 :
2853 : class OGRSimpleCurvePointIterator final : public OGRPointIterator
2854 : {
2855 : CPL_DISALLOW_COPY_ASSIGN(OGRSimpleCurvePointIterator)
2856 :
2857 : const OGRSimpleCurve *poSC = nullptr;
2858 : int iCurPoint = 0;
2859 :
2860 : public:
2861 169 : explicit OGRSimpleCurvePointIterator(const OGRSimpleCurve *poSCIn)
2862 169 : : poSC(poSCIn)
2863 : {
2864 169 : }
2865 :
2866 : OGRBoolean getNextPoint(OGRPoint *p) override;
2867 : };
2868 :
2869 : /************************************************************************/
2870 : /* getNextPoint() */
2871 : /************************************************************************/
2872 :
2873 697 : OGRBoolean OGRSimpleCurvePointIterator::getNextPoint(OGRPoint *p)
2874 : {
2875 697 : if (iCurPoint >= poSC->getNumPoints())
2876 133 : return FALSE;
2877 564 : poSC->getPoint(iCurPoint, p);
2878 564 : iCurPoint++;
2879 564 : return TRUE;
2880 : }
2881 :
2882 : /************************************************************************/
2883 : /* getPointIterator() */
2884 : /************************************************************************/
2885 :
2886 169 : OGRPointIterator *OGRSimpleCurve::getPointIterator() const
2887 : {
2888 169 : return new OGRSimpleCurvePointIterator(this);
2889 : }
2890 :
2891 : /************************************************************************/
2892 : /* OGRLineString( const OGRLineString& ) */
2893 : /************************************************************************/
2894 :
2895 : /**
2896 : * \brief Copy constructor.
2897 : *
2898 : * Note: before GDAL 2.1, only the default implementation of the constructor
2899 : * existed, which could be unsafe to use.
2900 : *
2901 : * @since GDAL 2.1
2902 : */
2903 :
2904 : OGRLineString::OGRLineString(const OGRLineString &) = default;
2905 :
2906 : /************************************************************************/
2907 : /* OGRLineString( OGRLineString&& ) */
2908 : /************************************************************************/
2909 :
2910 : /**
2911 : * \brief Move constructor.
2912 : *
2913 : * @since GDAL 3.11
2914 : */
2915 :
2916 : OGRLineString::OGRLineString(OGRLineString &&) = default;
2917 :
2918 : /************************************************************************/
2919 : /* operator=( const OGRLineString& ) */
2920 : /************************************************************************/
2921 :
2922 : /**
2923 : * \brief Assignment operator.
2924 : *
2925 : * Note: before GDAL 2.1, only the default implementation of the operator
2926 : * existed, which could be unsafe to use.
2927 : *
2928 : * @since GDAL 2.1
2929 : */
2930 :
2931 9 : OGRLineString &OGRLineString::operator=(const OGRLineString &other)
2932 : {
2933 9 : if (this != &other)
2934 : {
2935 8 : OGRSimpleCurve::operator=(other);
2936 : }
2937 9 : return *this;
2938 : }
2939 :
2940 : /************************************************************************/
2941 : /* operator=( OGRLineString&& ) */
2942 : /************************************************************************/
2943 :
2944 : /**
2945 : * \brief Move assignment operator.
2946 : *
2947 : * @since GDAL 3.11
2948 : */
2949 :
2950 4 : OGRLineString &OGRLineString::operator=(OGRLineString &&other)
2951 : {
2952 4 : if (this != &other)
2953 : {
2954 4 : OGRSimpleCurve::operator=(std::move(other));
2955 : }
2956 4 : return *this;
2957 : }
2958 :
2959 : /************************************************************************/
2960 : /* getGeometryType() */
2961 : /************************************************************************/
2962 :
2963 891786 : OGRwkbGeometryType OGRLineString::getGeometryType() const
2964 :
2965 : {
2966 891786 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
2967 44253 : return wkbLineStringZM;
2968 847533 : else if (flags & OGR_G_MEASURED)
2969 1262 : return wkbLineStringM;
2970 846271 : else if (flags & OGR_G_3D)
2971 149443 : return wkbLineString25D;
2972 : else
2973 696828 : return wkbLineString;
2974 : }
2975 :
2976 : /************************************************************************/
2977 : /* getGeometryName() */
2978 : /************************************************************************/
2979 :
2980 10524 : const char *OGRLineString::getGeometryName() const
2981 :
2982 : {
2983 10524 : return "LINESTRING";
2984 : }
2985 :
2986 : /************************************************************************/
2987 : /* curveToLine() */
2988 : /************************************************************************/
2989 :
2990 932 : OGRLineString *OGRLineString::CurveToLine(
2991 : CPL_UNUSED double /* dfMaxAngleStepSizeDegrees */,
2992 : CPL_UNUSED const char *const * /* papszOptions */) const
2993 : {
2994 932 : return clone();
2995 : }
2996 :
2997 : /************************************************************************/
2998 : /* get_LinearArea() */
2999 : /************************************************************************/
3000 :
3001 : /**
3002 : * \brief Compute area of ring / closed linestring.
3003 : *
3004 : * The area is computed according to Green's Theorem:
3005 : *
3006 : * Area is "Sum(x(i)*(y(i+1) - y(i-1)))/2" for i = 0 to pointCount-1,
3007 : * assuming the last point is a duplicate of the first.
3008 : *
3009 : * @return computed area.
3010 : */
3011 :
3012 61898 : double OGRSimpleCurve::get_LinearArea() const
3013 :
3014 : {
3015 123795 : if (nPointCount < 2 ||
3016 61897 : (WkbSize() != 0 && /* if not a linearring, check it is closed */
3017 60 : (paoPoints[0].x != paoPoints[nPointCount - 1].x ||
3018 59 : paoPoints[0].y != paoPoints[nPointCount - 1].y)))
3019 : {
3020 2 : return 0;
3021 : }
3022 :
3023 61896 : double dfAreaSum =
3024 61896 : paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount - 1].y);
3025 :
3026 3373180 : for (int i = 1; i < nPointCount - 1; i++)
3027 : {
3028 3311280 : dfAreaSum += paoPoints[i].x * (paoPoints[i + 1].y - paoPoints[i - 1].y);
3029 : }
3030 :
3031 61896 : dfAreaSum += paoPoints[nPointCount - 1].x *
3032 61896 : (paoPoints[0].y - paoPoints[nPointCount - 2].y);
3033 :
3034 61896 : return 0.5 * fabs(dfAreaSum);
3035 : }
3036 :
3037 : /************************************************************************/
3038 : /* getCurveGeometry() */
3039 : /************************************************************************/
3040 :
3041 : OGRGeometry *
3042 3197 : OGRLineString::getCurveGeometry(const char *const *papszOptions) const
3043 : {
3044 3197 : return OGRGeometryFactory::curveFromLineString(this, papszOptions);
3045 : }
3046 :
3047 : /************************************************************************/
3048 : /* TransferMembersAndDestroy() */
3049 : /************************************************************************/
3050 : //! @cond Doxygen_Suppress
3051 570 : OGRLineString *OGRLineString::TransferMembersAndDestroy(OGRLineString *poSrc,
3052 : OGRLineString *poDst)
3053 : {
3054 570 : if (poSrc->Is3D())
3055 101 : poDst->flags |= OGR_G_3D;
3056 570 : if (poSrc->IsMeasured())
3057 47 : poDst->flags |= OGR_G_MEASURED;
3058 570 : poDst->assignSpatialReference(poSrc->getSpatialReference());
3059 570 : poDst->nPointCount = poSrc->nPointCount;
3060 570 : poDst->m_nPointCapacity = poSrc->m_nPointCapacity;
3061 570 : poDst->paoPoints = poSrc->paoPoints;
3062 570 : poDst->padfZ = poSrc->padfZ;
3063 570 : poDst->padfM = poSrc->padfM;
3064 570 : poSrc->nPointCount = 0;
3065 570 : poSrc->m_nPointCapacity = 0;
3066 570 : poSrc->paoPoints = nullptr;
3067 570 : poSrc->padfZ = nullptr;
3068 570 : poSrc->padfM = nullptr;
3069 570 : delete poSrc;
3070 570 : return poDst;
3071 : }
3072 :
3073 : //! @endcond
3074 : /************************************************************************/
3075 : /* CastToLinearRing() */
3076 : /************************************************************************/
3077 :
3078 : /**
3079 : * \brief Cast to linear ring.
3080 : *
3081 : * The passed in geometry is consumed and a new one returned (or NULL in case
3082 : * of failure)
3083 : *
3084 : * @param poLS the input geometry - ownership is passed to the method.
3085 : * @return new geometry.
3086 : */
3087 :
3088 505 : OGRLinearRing *OGRLineString::CastToLinearRing(OGRLineString *poLS)
3089 : {
3090 505 : if (poLS->nPointCount < 2 || !poLS->get_IsClosed())
3091 : {
3092 2 : CPLError(CE_Failure, CPLE_AppDefined,
3093 : "Cannot convert non-closed linestring to linearring");
3094 2 : delete poLS;
3095 2 : return nullptr;
3096 : }
3097 503 : OGRLinearRing *poLR = new OGRLinearRing();
3098 503 : TransferMembersAndDestroy(poLS, poLR);
3099 503 : return poLR;
3100 : }
3101 :
3102 : /************************************************************************/
3103 : /* clone() */
3104 : /************************************************************************/
3105 :
3106 301927 : OGRLineString *OGRLineString::clone() const
3107 : {
3108 301927 : auto ret = new (std::nothrow) OGRLineString(*this);
3109 301927 : if (ret)
3110 : {
3111 301927 : if (ret->getNumPoints() != getNumPoints())
3112 : {
3113 0 : delete ret;
3114 0 : ret = nullptr;
3115 : }
3116 : }
3117 301927 : return ret;
3118 : }
3119 :
3120 : //! @cond Doxygen_Suppress
3121 :
3122 : /************************************************************************/
3123 : /* GetCasterToLineString() */
3124 : /************************************************************************/
3125 :
3126 159 : static OGRLineString *CasterToLineString(OGRCurve *poCurve)
3127 : {
3128 159 : return poCurve->toLineString();
3129 : }
3130 :
3131 159 : OGRCurveCasterToLineString OGRLineString::GetCasterToLineString() const
3132 : {
3133 159 : return ::CasterToLineString;
3134 : }
3135 :
3136 : /************************************************************************/
3137 : /* GetCasterToLinearRing() */
3138 : /************************************************************************/
3139 :
3140 505 : OGRLinearRing *OGRLineString::CasterToLinearRing(OGRCurve *poCurve)
3141 : {
3142 505 : return OGRLineString::CastToLinearRing(poCurve->toLineString());
3143 : }
3144 :
3145 505 : OGRCurveCasterToLinearRing OGRLineString::GetCasterToLinearRing() const
3146 : {
3147 505 : return OGRLineString::CasterToLinearRing;
3148 : }
3149 :
3150 : /************************************************************************/
3151 : /* get_Area() */
3152 : /************************************************************************/
3153 :
3154 61896 : double OGRLineString::get_Area() const
3155 : {
3156 61896 : return get_LinearArea();
3157 : }
3158 :
3159 : /************************************************************************/
3160 : /* GetGeodesicInputs() */
3161 : /************************************************************************/
3162 :
3163 47 : static bool GetGeodesicInputs(const OGRLineString *poLS,
3164 : const OGRSpatialReference *poSRSOverride,
3165 : const char *pszComputationType, geod_geodesic &g,
3166 : std::vector<double> &adfLat,
3167 : std::vector<double> &adfLon)
3168 : {
3169 47 : if (!poSRSOverride)
3170 14 : poSRSOverride = poLS->getSpatialReference();
3171 :
3172 47 : if (!poSRSOverride)
3173 : {
3174 2 : CPLError(CE_Failure, CPLE_AppDefined,
3175 : "Cannot compute %s on ellipsoid due to missing SRS",
3176 : pszComputationType);
3177 2 : return false;
3178 : }
3179 :
3180 45 : OGRErr eErr = OGRERR_NONE;
3181 45 : double dfSemiMajor = poSRSOverride->GetSemiMajor(&eErr);
3182 45 : if (eErr != OGRERR_NONE)
3183 2 : return false;
3184 43 : const double dfInvFlattening = poSRSOverride->GetInvFlattening(&eErr);
3185 43 : if (eErr != OGRERR_NONE)
3186 0 : return false;
3187 :
3188 43 : geod_init(&g, dfSemiMajor,
3189 : dfInvFlattening != 0 ? 1.0 / dfInvFlattening : 0.0);
3190 :
3191 43 : const int nPointCount = poLS->getNumPoints();
3192 43 : adfLat.reserve(nPointCount);
3193 43 : adfLon.reserve(nPointCount);
3194 :
3195 86 : OGRSpatialReference oGeogCRS;
3196 43 : if (oGeogCRS.CopyGeogCSFrom(poSRSOverride) != OGRERR_NONE)
3197 : {
3198 0 : CPLError(CE_Failure, CPLE_AppDefined,
3199 : "Cannot reproject geometry to geographic CRS");
3200 0 : return false;
3201 : }
3202 43 : oGeogCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3203 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
3204 86 : OGRCreateCoordinateTransformation(poSRSOverride, &oGeogCRS));
3205 43 : if (!poCT)
3206 : {
3207 0 : CPLError(CE_Failure, CPLE_AppDefined,
3208 : "Cannot reproject geometry to geographic CRS");
3209 0 : return false;
3210 : }
3211 905 : for (int i = 0; i < nPointCount; ++i)
3212 : {
3213 862 : adfLon.push_back(poLS->getX(i));
3214 862 : adfLat.push_back(poLS->getY(i));
3215 : }
3216 : #ifdef __GNUC__
3217 : #pragma GCC diagnostic push
3218 : #pragma GCC diagnostic ignored "-Wnull-dereference"
3219 : #endif
3220 86 : std::vector<int> anSuccess;
3221 43 : anSuccess.resize(adfLon.size());
3222 : #ifdef __GNUC__
3223 : #pragma GCC diagnostic pop
3224 : #endif
3225 43 : poCT->Transform(adfLon.size(), adfLon.data(), adfLat.data(), nullptr,
3226 : anSuccess.data());
3227 : double dfToDegrees =
3228 43 : oGeogCRS.GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
3229 43 : if (std::fabs(dfToDegrees - 1) <= 1e-10)
3230 43 : dfToDegrees = 1.0;
3231 905 : for (int i = 0; i < nPointCount; ++i)
3232 : {
3233 862 : if (!anSuccess[i])
3234 : {
3235 0 : CPLError(CE_Failure, CPLE_AppDefined,
3236 : "Cannot reproject geometry to geographic CRS");
3237 0 : return false;
3238 : }
3239 862 : adfLon[i] *= dfToDegrees;
3240 862 : adfLat[i] *= dfToDegrees;
3241 : }
3242 :
3243 43 : return true;
3244 : }
3245 :
3246 : /************************************************************************/
3247 : /* get_GeodesicArea() */
3248 : /************************************************************************/
3249 :
3250 : double
3251 22 : OGRLineString::get_GeodesicArea(const OGRSpatialReference *poSRSOverride) const
3252 : {
3253 : geod_geodesic g;
3254 44 : std::vector<double> adfLat;
3255 44 : std::vector<double> adfLon;
3256 22 : if (!GetGeodesicInputs(this, poSRSOverride, "area", g, adfLat, adfLon))
3257 2 : return -1.0;
3258 20 : double dfArea = -1.0;
3259 20 : geod_polygonarea(&g, adfLat.data(), adfLon.data(),
3260 20 : static_cast<int>(adfLat.size()), &dfArea, nullptr);
3261 20 : return std::fabs(dfArea);
3262 : }
3263 :
3264 : /************************************************************************/
3265 : /* get_GeodesicLength() */
3266 : /************************************************************************/
3267 :
3268 25 : double OGRLineString::get_GeodesicLength(
3269 : const OGRSpatialReference *poSRSOverride) const
3270 : {
3271 : geod_geodesic g;
3272 50 : std::vector<double> adfLat;
3273 50 : std::vector<double> adfLon;
3274 25 : if (!GetGeodesicInputs(this, poSRSOverride, "length", g, adfLat, adfLon))
3275 2 : return -1.0;
3276 23 : double dfLength = 0;
3277 434 : for (size_t i = 0; i + 1 < adfLon.size(); ++i)
3278 : {
3279 411 : double dfSegmentLength = 0;
3280 411 : geod_inverse(&g, adfLat[i], adfLon[i], adfLat[i + 1], adfLon[i + 1],
3281 : &dfSegmentLength, nullptr, nullptr);
3282 411 : dfLength += dfSegmentLength;
3283 : }
3284 23 : return dfLength;
3285 : }
3286 :
3287 : /************************************************************************/
3288 : /* get_AreaOfCurveSegments() */
3289 : /************************************************************************/
3290 :
3291 31 : double OGRLineString::get_AreaOfCurveSegments() const
3292 : {
3293 31 : return 0;
3294 : }
3295 :
3296 : /************************************************************************/
3297 : /* isClockwise() */
3298 : /************************************************************************/
3299 :
3300 : /**
3301 : * \brief Returns TRUE if the ring has clockwise winding (or less than 2 points)
3302 : *
3303 : * Assumes that the line is closed.
3304 : *
3305 : * @return TRUE if clockwise otherwise FALSE.
3306 : */
3307 :
3308 80959 : int OGRLineString::isClockwise() const
3309 :
3310 : {
3311 : // WARNING: keep in sync OGRLineString::isClockwise(),
3312 : // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
3313 :
3314 80959 : if (nPointCount < 2)
3315 1 : return TRUE;
3316 :
3317 80958 : bool bUseFallback = false;
3318 :
3319 : // Find the lowest rightmost vertex.
3320 80958 : int v = 0; // Used after for.
3321 3817720 : for (int i = 1; i < nPointCount - 1; i++)
3322 : {
3323 : // => v < end.
3324 3736770 : if (paoPoints[i].y < paoPoints[v].y ||
3325 3247540 : (paoPoints[i].y == paoPoints[v].y &&
3326 56976 : paoPoints[i].x > paoPoints[v].x))
3327 : {
3328 519327 : v = i;
3329 519327 : bUseFallback = false;
3330 : }
3331 3217440 : else if (paoPoints[i].y == paoPoints[v].y &&
3332 26860 : paoPoints[i].x == paoPoints[v].x)
3333 : {
3334 : // Two vertex with same coordinates are the lowest rightmost
3335 : // vertex. Cannot use that point as the pivot (#5342).
3336 96 : bUseFallback = true;
3337 : }
3338 : }
3339 :
3340 : // Previous.
3341 80958 : int next = v - 1;
3342 80958 : if (next < 0)
3343 : {
3344 28243 : next = nPointCount - 1 - 1;
3345 : }
3346 :
3347 80958 : constexpr double EPSILON = 1.0E-5;
3348 180865 : const auto epsilonEqual = [](double a, double b, double eps)
3349 180865 : { return ::fabs(a - b) < eps; };
3350 :
3351 97241 : if (epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) &&
3352 16281 : epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON))
3353 : {
3354 : // Don't try to be too clever by retrying with a next point.
3355 : // This can lead to false results as in the case of #3356.
3356 247 : bUseFallback = true;
3357 : }
3358 :
3359 80940 : const double dx0 = paoPoints[next].x - paoPoints[v].x;
3360 80940 : const double dy0 = paoPoints[next].y - paoPoints[v].y;
3361 :
3362 : // Following.
3363 80940 : next = v + 1;
3364 80940 : if (next >= nPointCount - 1)
3365 : {
3366 17972 : next = 0;
3367 : }
3368 :
3369 83632 : if (epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) &&
3370 2678 : epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON))
3371 : {
3372 : // Don't try to be too clever by retrying with a next point.
3373 : // This can lead to false results as in the case of #3356.
3374 288 : bUseFallback = true;
3375 : }
3376 :
3377 80959 : const double dx1 = paoPoints[next].x - paoPoints[v].x;
3378 80959 : const double dy1 = paoPoints[next].y - paoPoints[v].y;
3379 :
3380 80959 : const double crossproduct = dx1 * dy0 - dx0 * dy1;
3381 :
3382 80959 : if (!bUseFallback)
3383 : {
3384 80545 : if (crossproduct > 0) // CCW
3385 45710 : return FALSE;
3386 34835 : else if (crossproduct < 0) // CW
3387 34807 : return TRUE;
3388 : }
3389 :
3390 : // This is a degenerate case: the extent of the polygon is less than EPSILON
3391 : // or 2 nearly identical points were found.
3392 : // Try with Green Formula as a fallback, but this is not a guarantee
3393 : // as we'll probably be affected by numerical instabilities.
3394 :
3395 442 : double dfSum =
3396 442 : paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount - 1].y);
3397 :
3398 46396 : for (int i = 1; i < nPointCount - 1; i++)
3399 : {
3400 45954 : dfSum += paoPoints[i].x * (paoPoints[i + 1].y - paoPoints[i - 1].y);
3401 : }
3402 :
3403 442 : dfSum += paoPoints[nPointCount - 1].x *
3404 442 : (paoPoints[0].y - paoPoints[nPointCount - 2].y);
3405 :
3406 442 : return dfSum < 0;
3407 : }
3408 :
3409 : //! @endcond
|