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