QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
qgslinestring.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslinestring.cpp
3 -------------------
4 begin : September 2014
5 copyright : (C) 2014 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgslinestring.h"
19#include "qgsapplication.h"
20#include "qgscompoundcurve.h"
22#include "qgsgeometryutils.h"
24#include "qgswkbptr.h"
25#include "qgslinesegment.h"
27#include "qgsfeedback.h"
28
29#include <nlohmann/json.hpp>
30#include <cmath>
31#include <memory>
32#include <QPainter>
33#include <limits>
34#include <QDomDocument>
35#include <QJsonObject>
36
37#include "qgsbox3d.h"
38
39/***************************************************************************
40 * This class is considered CRITICAL and any change MUST be accompanied with
41 * full unit tests.
42 * See details in QEP #17
43 ****************************************************************************/
44
49
50QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
51{
52 if ( points.isEmpty() )
53 {
55 return;
56 }
57 Qgis::WkbType ptType = points.at( 0 ).wkbType();
59 mX.resize( points.count() );
60 mY.resize( points.count() );
61 double *x = mX.data();
62 double *y = mY.data();
63 double *z = nullptr;
64 double *m = nullptr;
66 {
67 mZ.resize( points.count() );
68 z = mZ.data();
69 }
71 {
72 mM.resize( points.count() );
73 m = mM.data();
74 }
75
76 for ( const QgsPoint &pt : points )
77 {
78 *x++ = pt.x();
79 *y++ = pt.y();
80 if ( z )
81 *z++ = pt.z();
82 if ( m )
83 *m++ = pt.m();
84 }
85}
86
87QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
88{
90 int pointCount = std::min( x.size(), y.size() );
91 if ( x.size() == pointCount )
92 {
93 mX = x;
94 }
95 else
96 {
97 mX = x.mid( 0, pointCount );
98 }
99 if ( y.size() == pointCount )
100 {
101 mY = y;
102 }
103 else
104 {
105 mY = y.mid( 0, pointCount );
106 }
107 if ( !z.isEmpty() && z.count() >= pointCount )
108 {
110 if ( z.size() == pointCount )
111 {
112 mZ = z;
113 }
114 else
115 {
116 mZ = z.mid( 0, pointCount );
117 }
118 }
119 if ( !m.isEmpty() && m.count() >= pointCount )
120 {
122 if ( m.size() == pointCount )
123 {
124 mM = m;
125 }
126 else
127 {
128 mM = m.mid( 0, pointCount );
129 }
130 }
131}
132
134{
136 mX.resize( 2 );
137 mX[ 0 ] = p1.x();
138 mX[ 1 ] = p2.x();
139 mY.resize( 2 );
140 mY[ 0 ] = p1.y();
141 mY[ 1 ] = p2.y();
142 if ( p1.is3D() )
143 {
145 mZ.resize( 2 );
146 mZ[ 0 ] = p1.z();
147 mZ[ 1 ] = p2.z();
148 }
149 if ( p1.isMeasure() )
150 {
152 mM.resize( 2 );
153 mM[ 0 ] = p1.m();
154 mM[ 1 ] = p2.m();
155 }
156}
157
158QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
159{
161 mX.reserve( points.size() );
162 mY.reserve( points.size() );
163 for ( const QgsPointXY &p : points )
164 {
165 mX << p.x();
166 mY << p.y();
167 }
168}
169
171{
173 mX.resize( 2 );
174 mY.resize( 2 );
175 mX[0] = segment.startX();
176 mX[1] = segment.endX();
177 mY[0] = segment.startY();
178 mY[1] = segment.endY();
179}
180
181static double cubicInterpolate( double a, double b,
182 double A, double B, double C, double D )
183{
184 return A * b * b * b + 3 * B * b * b * a + 3 * C * b * a * a + D * a * a * a;
185}
186
187QgsLineString *QgsLineString::fromBezierCurve( const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments )
188{
189 if ( segments == 0 )
190 return new QgsLineString();
191
192 QVector<double> x;
193 x.resize( segments + 1 );
194 QVector<double> y;
195 y.resize( segments + 1 );
196 QVector<double> z;
197 double *zData = nullptr;
198 if ( start.is3D() && end.is3D() && controlPoint1.is3D() && controlPoint2.is3D() )
199 {
200 z.resize( segments + 1 );
201 zData = z.data();
202 }
203 QVector<double> m;
204 double *mData = nullptr;
205 if ( start.isMeasure() && end.isMeasure() && controlPoint1.isMeasure() && controlPoint2.isMeasure() )
206 {
207 m.resize( segments + 1 );
208 mData = m.data();
209 }
210
211 double *xData = x.data();
212 double *yData = y.data();
213 const double step = 1.0 / segments;
214 double a = 0;
215 double b = 1.0;
216 for ( int i = 0; i < segments; i++, a += step, b -= step )
217 {
218 if ( i == 0 )
219 {
220 *xData++ = start.x();
221 *yData++ = start.y();
222 if ( zData )
223 *zData++ = start.z();
224 if ( mData )
225 *mData++ = start.m();
226 }
227 else
228 {
229 *xData++ = cubicInterpolate( a, b, start.x(), controlPoint1.x(), controlPoint2.x(), end.x() );
230 *yData++ = cubicInterpolate( a, b, start.y(), controlPoint1.y(), controlPoint2.y(), end.y() );
231 if ( zData )
232 *zData++ = cubicInterpolate( a, b, start.z(), controlPoint1.z(), controlPoint2.z(), end.z() );
233 if ( mData )
234 *mData++ = cubicInterpolate( a, b, start.m(), controlPoint1.m(), controlPoint2.m(), end.m() );
235 }
236 }
237
238 *xData = end.x();
239 *yData = end.y();
240 if ( zData )
241 *zData = end.z();
242 if ( mData )
243 *mData = end.m();
244
245 return new QgsLineString( x, y, z, m );
246}
247
248QgsLineString *QgsLineString::fromQPolygonF( const QPolygonF &polygon )
249{
250 QVector< double > x;
251 QVector< double > y;
252 x.resize( polygon.count() );
253 y.resize( polygon.count() );
254 double *xData = x.data();
255 double *yData = y.data();
256
257 const QPointF *src = polygon.data();
258 for ( int i = 0 ; i < polygon.size(); ++ i )
259 {
260 *xData++ = src->x();
261 *yData++ = src->y();
262 src++;
263 }
264
265 return new QgsLineString( x, y );
266}
267
269{
270 return new QgsLineString( *this );
271}
272
274{
275 mX.clear();
276 mY.clear();
277 mZ.clear();
278 mM.clear();
280 clearCache();
281}
282
284{
285 return mX.isEmpty();
286}
287
288int QgsLineString::indexOf( const QgsPoint &point ) const
289{
290 const int size = mX.size();
291 if ( size == 0 )
292 return -1;
293
294 const double *x = mX.constData();
295 const double *y = mY.constData();
296 const bool useZ = is3D();
297 const bool useM = isMeasure();
298 const double *z = useZ ? mZ.constData() : nullptr;
299 const double *m = useM ? mM.constData() : nullptr;
300
301 for ( int i = 0; i < size; ++i )
302 {
303 if ( qgsDoubleNear( *x, point.x() )
304 && qgsDoubleNear( *y, point.y() )
305 && ( !useZ || qgsDoubleNear( *z, point.z() ) )
306 && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
307 return i;
308
309 x++;
310 y++;
311 if ( useZ )
312 z++;
313 if ( useM )
314 m++;
315 }
316 return -1;
317}
318
319bool QgsLineString::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
320{
321 if ( !isEmpty() && ( numPoints() < 2 ) )
322 {
323 error = QObject::tr( "LineString has less than 2 points and is not empty." );
324 return false;
325 }
326 return QgsCurve::isValid( error, flags );
327}
328
329QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
330{
331 // prepare result
332 std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
333
334 bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
335 result->mX, result->mY, result->mZ, result->mM, removeRedundantPoints );
336 if ( res )
337 return result.release();
338 else
339 return nullptr;
340}
341
342bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
343{
344 if ( mX.count() <= 2 )
345 return false; // don't create degenerate lines
346 bool result = false;
347 double prevX = mX.at( 0 );
348 double prevY = mY.at( 0 );
349 bool hasZ = is3D();
350 bool useZ = hasZ && useZValues;
351 double prevZ = useZ ? mZ.at( 0 ) : 0;
352 int i = 1;
353 int remaining = mX.count();
354 while ( i < remaining )
355 {
356 double currentX = mX.at( i );
357 double currentY = mY.at( i );
358 double currentZ = useZ ? mZ.at( i ) : 0;
359 if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
360 qgsDoubleNear( currentY, prevY, epsilon ) &&
361 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
362 {
363 result = true;
364 // remove point
365 mX.removeAt( i );
366 mY.removeAt( i );
367 if ( hasZ )
368 mZ.removeAt( i );
369 remaining--;
370 }
371 else
372 {
373 prevX = currentX;
374 prevY = currentY;
375 prevZ = currentZ;
376 i++;
377 }
378 }
379 return result;
380}
381
383{
384 if ( mX.empty() )
385 return false;
386
387 return qgsDoubleNear( mX.first(), mX.last() ) &&
388 qgsDoubleNear( mY.first(), mY.last() );
389}
390
392{
393 bool closed = isClosed2D();
394
395 if ( is3D() && closed )
396 closed &= qgsDoubleNear( mZ.first(), mZ.last() ) || ( std::isnan( mZ.first() ) && std::isnan( mZ.last() ) );
397 return closed;
398}
399
400// As `bool boundingBoxIntersects( const QgsBox3D &box3d )` and `bool boundingBoxIntersects( const QgsRectangle &rectangle )` are nearly
401// the same: if one of these functions is changed then remember to also update the other accordingly
403{
404 if ( mX.empty() )
405 return false;
406
407 if ( !mBoundingBox.isNull() )
408 {
409 return mBoundingBox.intersects( rectangle );
410 }
411 const int nb = mX.size();
412
413 // We are a little fancy here!
414 if ( nb > 40 )
415 {
416 // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
417 // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
418 // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
419 // will fall on approximately these vertex indices)
420 if ( rectangle.contains( mX.at( 0 ), mY.at( 0 ) ) ||
421 rectangle.contains( mX.at( static_cast< int >( nb * 0.2 ) ), mY.at( static_cast< int >( nb * 0.2 ) ) ) ||
422 rectangle.contains( mX.at( static_cast< int >( nb * 0.4 ) ), mY.at( static_cast< int >( nb * 0.4 ) ) ) ||
423 rectangle.contains( mX.at( static_cast< int >( nb * 0.6 ) ), mY.at( static_cast< int >( nb * 0.6 ) ) ) ||
424 rectangle.contains( mX.at( static_cast< int >( nb * 0.8 ) ), mY.at( static_cast< int >( nb * 0.8 ) ) ) ||
425 rectangle.contains( mX.at( nb - 1 ), mY.at( nb - 1 ) ) )
426 return true;
427 }
428
429 // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
430 // already have it, we start performing the bounding box calculation while we are testing whether
431 // each point falls inside the rectangle. That way if we end up testing the majority of the points
432 // anyway, we can update the cached bounding box with the results we've calculated along the way
433 // and save future calls to calculate the bounding box!
434 double xmin = std::numeric_limits<double>::max();
435 double ymin = std::numeric_limits<double>::max();
436 double xmax = -std::numeric_limits<double>::max();
437 double ymax = -std::numeric_limits<double>::max();
438
439 const double *x = mX.constData();
440 const double *y = mY.constData();
441 bool foundPointInRectangle = false;
442 for ( int i = 0; i < nb; ++i )
443 {
444 const double px = *x++;
445 xmin = std::min( xmin, px );
446 xmax = std::max( xmax, px );
447 const double py = *y++;
448 ymin = std::min( ymin, py );
449 ymax = std::max( ymax, py );
450
451 if ( !foundPointInRectangle && rectangle.contains( px, py ) )
452 {
453 foundPointInRectangle = true;
454
455 // now... we have a choice to make. If we've already looped through the majority of the points
456 // in this linestring then let's just continue to iterate through the remainder so that we can
457 // complete the overall bounding box calculation we've already mostly done. If however we're only
458 // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
459 // uncalculated
460 if ( i < nb * 0.5 )
461 return true;
462 }
463 }
464
465 // at this stage we now know the overall bounding box of the linestring, so let's cache
466 // it so we don't ever have to calculate this again. We've done all the hard work anyway!
467 mBoundingBox = QgsRectangle( xmin, ymin, xmax, ymax, false );
468
469 if ( foundPointInRectangle )
470 return true;
471
472 // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
473 // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
474 // So we fall back to the parent class method which compares the overall bounding box against
475 // the rectangle... and this will be very cheap now that we've already calculated and cached
476 // the linestring's bounding box!
477 return QgsCurve::boundingBoxIntersects( rectangle );
478}
479
480// As `bool boundingBoxIntersects( const QgsBox3D &box3d )` and `bool boundingBoxIntersects( const QgsRectangle &rectangle )` are nearly
481// the same: if one of these functions is changed then remember to also update the other accordingly
483{
484 if ( mX.empty() )
485 return false;
486
487 if ( mZ.empty() )
488 return boundingBoxIntersects( box3d.toRectangle() );
489
490 if ( !mBoundingBox.isNull() )
491 {
492 return mBoundingBox.intersects( box3d );
493 }
494 const int nb = mX.size();
495
496 // We are a little fancy here!
497 if ( nb > 40 )
498 {
499 // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
500 // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
501 // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
502 // will fall on approximately these vertex indices)
503 if ( box3d.contains( mX.at( 0 ), mY.at( 0 ), mZ.at( 0 ) ) ||
504 box3d.contains( mX.at( static_cast< int >( nb * 0.2 ) ), mY.at( static_cast< int >( nb * 0.2 ) ), mZ.at( static_cast< int >( nb * 0.2 ) ) ) ||
505 box3d.contains( mX.at( static_cast< int >( nb * 0.4 ) ), mY.at( static_cast< int >( nb * 0.4 ) ), mZ.at( static_cast< int >( nb * 0.4 ) ) ) ||
506 box3d.contains( mX.at( static_cast< int >( nb * 0.6 ) ), mY.at( static_cast< int >( nb * 0.6 ) ), mZ.at( static_cast< int >( nb * 0.6 ) ) ) ||
507 box3d.contains( mX.at( static_cast< int >( nb * 0.8 ) ), mY.at( static_cast< int >( nb * 0.8 ) ), mZ.at( static_cast< int >( nb * 0.8 ) ) ) ||
508 box3d.contains( mX.at( nb - 1 ), mY.at( nb - 1 ), mZ.at( nb - 1 ) ) )
509 return true;
510 }
511
512 // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
513 // already have it, we start performing the bounding box calculation while we are testing whether
514 // each point falls inside the rectangle. That way if we end up testing the majority of the points
515 // anyway, we can update the cached bounding box with the results we've calculated along the way
516 // and save future calls to calculate the bounding box!
517 double xmin = std::numeric_limits<double>::max();
518 double ymin = std::numeric_limits<double>::max();
519 double zmin = std::numeric_limits<double>::max();
520 double xmax = -std::numeric_limits<double>::max();
521 double ymax = -std::numeric_limits<double>::max();
522 double zmax = -std::numeric_limits<double>::max();
523
524 const double *x = mX.constData();
525 const double *y = mY.constData();
526 const double *z = mZ.constData();
527 bool foundPointInBox = false;
528 for ( int i = 0; i < nb; ++i )
529 {
530 const double px = *x++;
531 xmin = std::min( xmin, px );
532 xmax = std::max( xmax, px );
533 const double py = *y++;
534 ymin = std::min( ymin, py );
535 ymax = std::max( ymax, py );
536 const double pz = *z++;
537 zmin = std::min( zmin, pz );
538 zmax = std::max( zmax, pz );
539
540 if ( !foundPointInBox && box3d.contains( px, py, pz ) )
541 {
542 foundPointInBox = true;
543
544 // now... we have a choice to make. If we've already looped through the majority of the points
545 // in this linestring then let's just continue to iterate through the remainder so that we can
546 // complete the overall bounding box calculation we've already mostly done. If however we're only
547 // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
548 // uncalculated
549 if ( i < nb * 0.5 )
550 return true;
551 }
552 }
553
554 // at this stage we now know the overall bounding box of the linestring, so let's cache
555 // it so we don't ever have to calculate this again. We've done all the hard work anyway!
556 mBoundingBox = QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax, false );
557
558 if ( foundPointInBox )
559 return true;
560
561 // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
562 // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
563 // So we fall back to the parent class method which compares the overall bounding box against
564 // the rectangle... and this will be very cheap now that we've already calculated and cached
565 // the linestring's bounding box!
566 return QgsCurve::boundingBoxIntersects( box3d );
567}
568
569QVector< QgsVertexId > QgsLineString::collectDuplicateNodes( double epsilon, bool useZValues ) const
570{
571 QVector< QgsVertexId > res;
572 if ( mX.count() <= 1 )
573 return res;
574
575 const double *x = mX.constData();
576 const double *y = mY.constData();
577 bool hasZ = is3D();
578 bool useZ = hasZ && useZValues;
579 const double *z = useZ ? mZ.constData() : nullptr;
580
581 double prevX = *x++;
582 double prevY = *y++;
583 double prevZ = z ? *z++ : 0;
584
585 QgsVertexId id;
586 for ( int i = 1; i < mX.count(); ++i )
587 {
588 double currentX = *x++;
589 double currentY = *y++;
590 double currentZ = useZ ? *z++ : 0;
591 if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
592 qgsDoubleNear( currentY, prevY, epsilon ) &&
593 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
594 {
595 id.vertex = i;
596 res << id;
597 }
598 else
599 {
600 prevX = currentX;
601 prevY = currentY;
602 prevZ = currentZ;
603 }
604 }
605 return res;
606}
607
609{
610 const int nb = mX.size();
611 QPolygonF points( nb );
612
613 const double *x = mX.constData();
614 const double *y = mY.constData();
615 QPointF *dest = points.data();
616 for ( int i = 0; i < nb; ++i )
617 {
618 *dest++ = QPointF( *x++, *y++ );
619 }
620 return points;
621}
622
623
624void simplifySection( int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon )
625{
626 if ( i + 1 == j )
627 {
628 return;
629 }
630
631 double maxDistanceSquared = -1.0;
632
633 int maxIndex = i;
634 double mx, my;
635
636 for ( int k = i + 1; k < j; k++ )
637 {
638 const double distanceSquared = QgsGeometryUtilsBase::sqrDistToLine(
639 x[k], y[k], x[i], y[i], x[j], y[j], mx, my, epsilon );
640
641 if ( distanceSquared > maxDistanceSquared )
642 {
643 maxDistanceSquared = distanceSquared;
644 maxIndex = k;
645 }
646 }
647 if ( maxDistanceSquared <= distanceToleranceSquared )
648 {
649 for ( int k = i + 1; k < j; k++ )
650 {
651 usePoint[k] = false;
652 }
653 }
654 else
655 {
656 simplifySection( i, maxIndex, x, y, usePoint, distanceToleranceSquared, epsilon );
657 simplifySection( maxIndex, j, x, y, usePoint, distanceToleranceSquared, epsilon );
658 }
659};
660
662{
663 if ( mX.empty() )
664 {
665 return new QgsLineString();
666 }
667
668 // ported from GEOS DouglasPeuckerLineSimplifier::simplify
669
670 const double distanceToleranceSquared = tolerance * tolerance;
671 const double *xData = mX.constData();
672 const double *yData = mY.constData();
673 const double *zData = mZ.constData();
674 const double *mData = mM.constData();
675
676 const int size = mX.size();
677
678 std::vector< bool > usePoint( size, true );
679
680 constexpr double epsilon = 4 * std::numeric_limits<double>::epsilon();
681 simplifySection( 0, size - 1, xData, yData, usePoint, distanceToleranceSquared, epsilon );
682
683 QVector< double > newX;
684 newX.reserve( size );
685 QVector< double > newY;
686 newY.reserve( size );
687
688 const bool hasZ = is3D();
689 const bool hasM = isMeasure();
690 QVector< double > newZ;
691 if ( hasZ )
692 newZ.reserve( size );
693 QVector< double > newM;
694 if ( hasM )
695 newM.reserve( size );
696
697 for ( int i = 0, n = size; i < n; ++i )
698 {
699 if ( usePoint[i] || i == n - 1 )
700 {
701 newX.append( xData[i ] );
702 newY.append( yData[i ] );
703 if ( hasZ )
704 newZ.append( zData[i] );
705 if ( hasM )
706 newM.append( mData[i] );
707 }
708 }
709
710 const bool simplifyRing = isRing();
711 const int newSize = newX.size();
712 if ( simplifyRing && newSize > 3 )
713 {
714 double mx, my;
715 const double distanceSquared = QgsGeometryUtilsBase::sqrDistToLine(
716 newX[0], newY[ 0],
717 newX[ newSize - 2], newY[ newSize - 2 ],
718 newX[ 1 ], newY[ 1], mx, my, epsilon );
719
720 if ( distanceSquared <= distanceToleranceSquared )
721 {
722 newX.removeFirst();
723 newX.last() = newX.first();
724 newY.removeFirst();
725 newY.last() = newY.first();
726 if ( hasZ )
727 {
728 newZ.removeFirst();
729 newZ.last() = newZ.first();
730 }
731 if ( hasM )
732 {
733 newM.removeFirst();
734 newM.last() = newM.first();
735 }
736 }
737 }
738
739 return new QgsLineString( newX, newY, newZ, newM );
740}
741
743{
744 if ( !wkbPtr )
745 {
746 return false;
747 }
748
749 Qgis::WkbType type = wkbPtr.readHeader();
751 {
752 return false;
753 }
754 mWkbType = type;
755 importVerticesFromWkb( wkbPtr );
756 return true;
757}
758
760{
761 if ( mX.empty() )
762 {
763 return QgsBox3D();
764 }
765
766 auto result2D = std::minmax_element( mX.begin(), mX.end() );
767 const double xmin = *result2D.first;
768 const double xmax = *result2D.second;
769 result2D = std::minmax_element( mY.begin(), mY.end() );
770 const double ymin = *result2D.first;
771 const double ymax = *result2D.second;
772
773 double zmin = std::numeric_limits< double >::quiet_NaN();
774 double zmax = std::numeric_limits< double >::quiet_NaN();
775
776 if ( is3D() )
777 {
778 auto resultZ = std::minmax_element( mZ.begin(), mZ.end() );
779 zmin = *resultZ.first;
780 zmax = *resultZ.second;
781 }
782
783 return QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax );
784}
785
790
791void QgsLineString::scroll( int index )
792{
793 const int size = mX.size();
794 if ( index < 1 || index >= size - 1 )
795 return;
796
797 const bool useZ = is3D();
798 const bool useM = isMeasure();
799
800 QVector<double> newX( size );
801 QVector<double> newY( size );
802 QVector<double> newZ( useZ ? size : 0 );
803 QVector<double> newM( useM ? size : 0 );
804 auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
805 it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
806 *it = *newX.constBegin();
807 mX = std::move( newX );
808
809 it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
810 it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
811 *it = *newY.constBegin();
812 mY = std::move( newY );
813 if ( useZ )
814 {
815 it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
816 it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
817 *it = *newZ.constBegin();
818 mZ = std::move( newZ );
819 }
820 if ( useM )
821 {
822 it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
823 it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
824 *it = *newM.constBegin();
825 mM = std::move( newM );
826 }
827}
828
829/***************************************************************************
830 * This class is considered CRITICAL and any change MUST be accompanied with
831 * full unit tests.
832 * See details in QEP #17
833 ****************************************************************************/
834bool QgsLineString::fromWkt( const QString &wkt )
835{
836 clear();
837
838 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
839
841 return false;
842 mWkbType = parts.first;
843
844 QString secondWithoutParentheses = parts.second;
845 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
846 parts.second = parts.second.remove( '(' ).remove( ')' );
847 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
848 secondWithoutParentheses.isEmpty() )
849 return true;
850
852 // There is a non number in the coordinates sequence
853 // LineString ( A b, 1 2)
854 if ( points.isEmpty() )
855 return false;
856
857 setPoints( points );
858 return true;
859}
860
862{
863 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
864 binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
865 return binarySize;
866}
867
868QByteArray QgsLineString::asWkb( WkbFlags flags ) const
869{
870 QByteArray wkbArray;
871 wkbArray.resize( QgsLineString::wkbSize( flags ) );
872 QgsWkbPtr wkb( wkbArray );
873 wkb << static_cast<char>( QgsApplication::endian() );
874 wkb << static_cast<quint32>( wkbType() );
876 points( pts );
877 QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
878 return wkbArray;
879}
880
881/***************************************************************************
882 * This class is considered CRITICAL and any change MUST be accompanied with
883 * full unit tests.
884 * See details in QEP #17
885 ****************************************************************************/
886
887QString QgsLineString::asWkt( int precision ) const
888{
889 QString wkt = wktTypeStr() + ' ';
890
891 if ( isEmpty() )
892 wkt += QLatin1String( "EMPTY" );
893 else
894 {
896 points( pts );
898 }
899 return wkt;
900}
901
902QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
903{
905 points( pts );
906
907 QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
908
909 if ( isEmpty() )
910 return elemLineString;
911
912 elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
913
914 return elemLineString;
915}
916
917QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
918{
920 points( pts );
921
922 QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
923
924 if ( isEmpty() )
925 return elemLineString;
926
927 elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
928 return elemLineString;
929}
930
932{
934 points( pts );
935 return
936 {
937 { "type", "LineString" },
938 { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
939 };
940}
941
942QString QgsLineString::asKml( int precision ) const
943{
944 QString kml;
945 if ( isRing() )
946 {
947 kml.append( QLatin1String( "<LinearRing>" ) );
948 }
949 else
950 {
951 kml.append( QLatin1String( "<LineString>" ) );
952 }
953 bool z = is3D();
954 kml.append( QLatin1String( "<altitudeMode>" ) );
955 if ( z )
956 {
957 kml.append( QLatin1String( "absolute" ) );
958 }
959 else
960 {
961 kml.append( QLatin1String( "clampToGround" ) );
962 }
963 kml.append( QLatin1String( "</altitudeMode>" ) );
964 kml.append( QLatin1String( "<coordinates>" ) );
965
966 int nPoints = mX.size();
967 for ( int i = 0; i < nPoints; ++i )
968 {
969 if ( i > 0 )
970 {
971 kml.append( QLatin1String( " " ) );
972 }
973 kml.append( qgsDoubleToString( mX[i], precision ) );
974 kml.append( QLatin1String( "," ) );
975 kml.append( qgsDoubleToString( mY[i], precision ) );
976 if ( z )
977 {
978 kml.append( QLatin1String( "," ) );
979 kml.append( qgsDoubleToString( mZ[i], precision ) );
980 }
981 else
982 {
983 kml.append( QLatin1String( ",0" ) );
984 }
985 }
986 kml.append( QLatin1String( "</coordinates>" ) );
987 if ( isRing() )
988 {
989 kml.append( QLatin1String( "</LinearRing>" ) );
990 }
991 else
992 {
993 kml.append( QLatin1String( "</LineString>" ) );
994 }
995 return kml;
996}
997
998/***************************************************************************
999 * This class is considered CRITICAL and any change MUST be accompanied with
1000 * full unit tests.
1001 * See details in QEP #17
1002 ****************************************************************************/
1003
1005{
1006 double total = 0;
1007 const int size = mX.size();
1008 if ( size < 2 )
1009 return 0;
1010
1011 const double *x = mX.constData();
1012 const double *y = mY.constData();
1013 double dx, dy;
1014
1015 double prevX = *x++;
1016 double prevY = *y++;
1017
1018 for ( int i = 1; i < size; ++i )
1019 {
1020 dx = *x - prevX;
1021 dy = *y - prevY;
1022 total += std::sqrt( dx * dx + dy * dy );
1023
1024 prevX = *x++;
1025 prevY = *y++;
1026 }
1027 return total;
1028}
1029
1030std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsLineString::splitCurveAtVertex( int index ) const
1031{
1032 const bool useZ = is3D();
1033 const bool useM = isMeasure();
1034
1035 const int size = mX.size();
1036 if ( size == 0 )
1037 return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >() );
1038
1039 index = std::clamp( index, 0, size - 1 );
1040
1041 const int part1Size = index + 1;
1042 QVector< double > x1( part1Size );
1043 QVector< double > y1( part1Size );
1044 QVector< double > z1( useZ ? part1Size : 0 );
1045 QVector< double > m1( useM ? part1Size : 0 );
1046
1047 const double *sourceX = mX.constData();
1048 const double *sourceY = mY.constData();
1049 const double *sourceZ = useZ ? mZ.constData() : nullptr;
1050 const double *sourceM = useM ? mM.constData() : nullptr;
1051
1052 double *destX = x1.data();
1053 double *destY = y1.data();
1054 double *destZ = useZ ? z1.data() : nullptr;
1055 double *destM = useM ? m1.data() : nullptr;
1056
1057 std::copy( sourceX, sourceX + part1Size, destX );
1058 std::copy( sourceY, sourceY + part1Size, destY );
1059 if ( useZ )
1060 std::copy( sourceZ, sourceZ + part1Size, destZ );
1061 if ( useM )
1062 std::copy( sourceM, sourceM + part1Size, destM );
1063
1064 const int part2Size = size - index;
1065 if ( part2Size < 2 )
1066 return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >() );
1067
1068 QVector< double > x2( part2Size );
1069 QVector< double > y2( part2Size );
1070 QVector< double > z2( useZ ? part2Size : 0 );
1071 QVector< double > m2( useM ? part2Size : 0 );
1072 destX = x2.data();
1073 destY = y2.data();
1074 destZ = useZ ? z2.data() : nullptr;
1075 destM = useM ? m2.data() : nullptr;
1076 std::copy( sourceX + index, sourceX + size, destX );
1077 std::copy( sourceY + index, sourceY + size, destY );
1078 if ( useZ )
1079 std::copy( sourceZ + index, sourceZ + size, destZ );
1080 if ( useM )
1081 std::copy( sourceM + index, sourceM + size, destM );
1082
1083 if ( part1Size < 2 )
1084 return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
1085 else
1086 return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
1087}
1088
1090{
1091 if ( is3D() )
1092 {
1093 double total = 0;
1094 const int size = mX.size();
1095 if ( size < 2 )
1096 return 0;
1097
1098 const double *x = mX.constData();
1099 const double *y = mY.constData();
1100 const double *z = mZ.constData();
1101 double dx, dy, dz;
1102
1103 double prevX = *x++;
1104 double prevY = *y++;
1105 double prevZ = *z++;
1106
1107 for ( int i = 1; i < size; ++i )
1108 {
1109 dx = *x - prevX;
1110 dy = *y - prevY;
1111 dz = *z - prevZ;
1112 total += std::sqrt( dx * dx + dy * dy + dz * dz );
1113
1114 prevX = *x++;
1115 prevY = *y++;
1116 prevZ = *z++;
1117 }
1118 return total;
1119 }
1120 else
1121 {
1122 return length();
1123 }
1124}
1125
1127{
1128 if ( numPoints() < 1 )
1129 {
1130 return QgsPoint();
1131 }
1132 return pointN( 0 );
1133}
1134
1136{
1137 if ( numPoints() < 1 )
1138 {
1139 return QgsPoint();
1140 }
1141 return pointN( numPoints() - 1 );
1142}
1143
1144/***************************************************************************
1145 * This class is considered CRITICAL and any change MUST be accompanied with
1146 * full unit tests.
1147 * See details in QEP #17
1148 ****************************************************************************/
1149
1151{
1152 Q_UNUSED( tolerance )
1153 Q_UNUSED( toleranceType )
1154 return clone();
1155}
1156
1158{
1159 return mX.size();
1160}
1161
1163{
1164 return mX.size();
1165}
1166
1168{
1169 if ( i < 0 || i >= mX.size() )
1170 {
1171 return QgsPoint();
1172 }
1173
1174 double x = mX.at( i );
1175 double y = mY.at( i );
1176 double z = std::numeric_limits<double>::quiet_NaN();
1177 double m = std::numeric_limits<double>::quiet_NaN();
1178
1179 bool hasZ = is3D();
1180 if ( hasZ )
1181 {
1182 z = mZ.at( i );
1183 }
1184 bool hasM = isMeasure();
1185 if ( hasM )
1186 {
1187 m = mM.at( i );
1188 }
1189
1192 {
1194 }
1195 else if ( hasZ && hasM )
1196 {
1198 }
1199 else if ( hasZ )
1200 {
1202 }
1203 else if ( hasM )
1204 {
1206 }
1207 return QgsPoint( t, x, y, z, m );
1208}
1209
1210/***************************************************************************
1211 * This class is considered CRITICAL and any change MUST be accompanied with
1212 * full unit tests.
1213 * See details in QEP #17
1214 ****************************************************************************/
1215
1216double QgsLineString::xAt( int index ) const
1217{
1218 if ( index >= 0 && index < mX.size() )
1219 return mX.at( index );
1220 else
1221 return 0.0;
1222}
1223
1224double QgsLineString::yAt( int index ) const
1225{
1226 if ( index >= 0 && index < mY.size() )
1227 return mY.at( index );
1228 else
1229 return 0.0;
1230}
1231
1232void QgsLineString::setXAt( int index, double x )
1233{
1234 if ( index >= 0 && index < mX.size() )
1235 mX[ index ] = x;
1236 clearCache();
1237}
1238
1239void QgsLineString::setYAt( int index, double y )
1240{
1241 if ( index >= 0 && index < mY.size() )
1242 mY[ index ] = y;
1243 clearCache();
1244}
1245
1246/***************************************************************************
1247 * This class is considered CRITICAL and any change MUST be accompanied with
1248 * full unit tests.
1249 * See details in QEP #17
1250 ****************************************************************************/
1251
1253{
1254 pts.clear();
1255 int nPoints = numPoints();
1256 pts.reserve( nPoints );
1257 for ( int i = 0; i < nPoints; ++i )
1258 {
1259 pts.push_back( pointN( i ) );
1260 }
1261}
1262
1263void QgsLineString::setPoints( size_t size, const double *x, const double *y, const double *z, const double *m )
1264{
1265 clearCache(); //set bounding box invalid
1266
1267 if ( size == 0 )
1268 {
1269 clear();
1270 return;
1271 }
1272
1273 const bool hasZ = static_cast< bool >( z );
1274 const bool hasM = static_cast< bool >( m );
1275
1276 if ( hasZ && hasM )
1277 {
1279 }
1280 else if ( hasZ )
1281 {
1283 }
1284 else if ( hasM )
1285 {
1287 }
1288 else
1289 {
1291 }
1292
1293 mX.resize( size );
1294 mY.resize( size );
1295 double *destX = mX.data();
1296 double *destY = mY.data();
1297 double *destZ = nullptr;
1298 if ( hasZ )
1299 {
1300 mZ.resize( size );
1301 destZ = mZ.data();
1302 }
1303 else
1304 {
1305 mZ.clear();
1306 }
1307 double *destM = nullptr;
1308 if ( hasM )
1309 {
1310 mM.resize( size );
1311 destM = mM.data();
1312 }
1313 else
1314 {
1315 mM.clear();
1316 }
1317
1318 for ( size_t i = 0; i < size; ++i )
1319 {
1320 *destX++ = *x++;
1321 *destY++ = *y++;
1322 if ( hasZ )
1323 {
1324 *destZ++ = *z++;
1325 }
1326 if ( hasM )
1327 {
1328 *destM++ = *m++;
1329 }
1330 }
1331}
1332
1334{
1335 clearCache(); //set bounding box invalid
1336
1337 if ( points.isEmpty() )
1338 {
1339 clear();
1340 return;
1341 }
1342
1343 //get wkb type from first point
1344 const QgsPoint &firstPt = points.at( 0 );
1345 bool hasZ = firstPt.is3D();
1346 bool hasM = firstPt.isMeasure();
1347
1349
1350 mX.resize( points.size() );
1351 mY.resize( points.size() );
1352 if ( hasZ )
1353 {
1354 mZ.resize( points.size() );
1355 }
1356 else
1357 {
1358 mZ.clear();
1359 }
1360 if ( hasM )
1361 {
1362 mM.resize( points.size() );
1363 }
1364 else
1365 {
1366 mM.clear();
1367 }
1368
1369 for ( int i = 0; i < points.size(); ++i )
1370 {
1371 mX[i] = points.at( i ).x();
1372 mY[i] = points.at( i ).y();
1373 if ( hasZ )
1374 {
1375 double z = points.at( i ).z();
1376 mZ[i] = std::isnan( z ) ? 0 : z;
1377 }
1378 if ( hasM )
1379 {
1380 double m = points.at( i ).m();
1381 mM[i] = std::isnan( m ) ? 0 : m;
1382 }
1383 }
1384}
1385
1386/***************************************************************************
1387 * This class is considered CRITICAL and any change MUST be accompanied with
1388 * full unit tests.
1389 * See details in QEP #17
1390 ****************************************************************************/
1391
1393{
1394 if ( !line )
1395 {
1396 return;
1397 }
1398
1399 if ( numPoints() < 1 )
1400 {
1402 }
1403
1404 // do not store duplicate points
1405 if ( numPoints() > 0 &&
1406 line->numPoints() > 0 &&
1407 endPoint() == line->startPoint() )
1408 {
1409 mX.pop_back();
1410 mY.pop_back();
1411
1412 if ( is3D() )
1413 {
1414 mZ.pop_back();
1415 }
1416 if ( isMeasure() )
1417 {
1418 mM.pop_back();
1419 }
1420 }
1421
1422 mX += line->mX;
1423 mY += line->mY;
1424
1425 if ( is3D() )
1426 {
1427 if ( line->is3D() )
1428 {
1429 mZ += line->mZ;
1430 }
1431 else
1432 {
1433 // if append line does not have z coordinates, fill with NaN to match number of points in final line
1434 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1435 }
1436 }
1437
1438 if ( isMeasure() )
1439 {
1440 if ( line->isMeasure() )
1441 {
1442 mM += line->mM;
1443 }
1444 else
1445 {
1446 // if append line does not have m values, fill with NaN to match number of points in final line
1447 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1448 }
1449 }
1450
1451 clearCache(); //set bounding box invalid
1452}
1453
1455{
1456 QgsLineString *copy = clone();
1457 std::reverse( copy->mX.begin(), copy->mX.end() );
1458 std::reverse( copy->mY.begin(), copy->mY.end() );
1459 if ( copy->is3D() )
1460 {
1461 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1462 }
1463 if ( copy->isMeasure() )
1464 {
1465 std::reverse( copy->mM.begin(), copy->mM.end() );
1466 }
1467 return copy;
1468}
1469
1470void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
1471{
1472 if ( distance < 0 )
1473 return;
1474
1475 double distanceTraversed = 0;
1476 const int totalPoints = numPoints();
1477 if ( totalPoints == 0 )
1478 return;
1479
1480 const double *x = mX.constData();
1481 const double *y = mY.constData();
1482 const double *z = is3D() ? mZ.constData() : nullptr;
1483 const double *m = isMeasure() ? mM.constData() : nullptr;
1484
1485 double prevX = *x++;
1486 double prevY = *y++;
1487 double prevZ = z ? *z++ : 0.0;
1488 double prevM = m ? *m++ : 0.0;
1489
1490 if ( qgsDoubleNear( distance, 0.0 ) )
1491 {
1492 visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
1493 return;
1494 }
1495
1496 double pZ = std::numeric_limits<double>::quiet_NaN();
1497 double pM = std::numeric_limits<double>::quiet_NaN();
1498 double nextPointDistance = distance;
1499 for ( int i = 1; i < totalPoints; ++i )
1500 {
1501 double thisX = *x++;
1502 double thisY = *y++;
1503 double thisZ = z ? *z++ : 0.0;
1504 double thisM = m ? *m++ : 0.0;
1505
1506 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1507 while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
1508 {
1509 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1510 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
1511 double pX, pY;
1512 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
1513 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
1514 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
1515
1516 if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1517 return;
1518
1519 nextPointDistance += distance;
1520 }
1521
1522 distanceTraversed += segmentLength;
1523 prevX = thisX;
1524 prevY = thisY;
1525 prevZ = thisZ;
1526 prevM = thisM;
1527 }
1528}
1529
1530QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
1531{
1532 if ( distance < 0 )
1533 return nullptr;
1534
1536 if ( is3D() )
1537 pointType = Qgis::WkbType::PointZ;
1538 if ( isMeasure() )
1539 pointType = QgsWkbTypes::addM( pointType );
1540
1541 std::unique_ptr< QgsPoint > res;
1542 visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
1543 {
1544 res = std::make_unique< QgsPoint >( pointType, x, y, z, m );
1545 return false;
1546 } );
1547 return res.release();
1548}
1549
1550bool QgsLineString::lineLocatePointByM( double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance ) const
1551{
1552 return lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance, false );
1553}
1554
1555bool QgsLineString::lineLocatePointByMPrivate( double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance, bool haveInterpolatedM ) const
1556{
1557 if ( !isMeasure() )
1558 return false;
1559
1560 distanceFromStart = 0;
1561 const int totalPoints = numPoints();
1562 if ( totalPoints == 0 )
1563 return false;
1564
1565 const double *xData = mX.constData();
1566 const double *yData = mY.constData();
1567 const double *mData = mM.constData();
1568
1569 const double *zData = is3D() ? mZ.constData() : nullptr;
1570 use3DDistance &= static_cast< bool >( zData );
1571
1572 double prevX = *xData++;
1573 double prevY = *yData++;
1574 double prevZ = zData ? *zData++ : 0;
1575 double prevM = *mData++;
1576
1577 int i = 1;
1578 while ( i < totalPoints )
1579 {
1580 double thisX = *xData++;
1581 double thisY = *yData++;
1582 double thisZ = zData ? *zData++ : 0;
1583 double thisM = *mData++;
1584 const double segmentLength = use3DDistance ? QgsGeometryUtilsBase::distance3D( thisX, thisY, thisZ, prevX, prevY, prevZ )
1585 : QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1586
1587 if ( std::isnan( thisM ) )
1588 {
1589 if ( haveInterpolatedM )
1590 return false;
1591
1592 // if we hit a NaN m value, interpolate m to fill the blanks and then re-try
1593 std::unique_ptr< QgsLineString > interpolatedM( interpolateM( use3DDistance ) );
1594 return interpolatedM->lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance, true );
1595 }
1596 else
1597 {
1598 // check if target m value falls within this segment's range
1599 if ( ( prevM < m && thisM > m ) || ( prevM > m && thisM < m ) || qgsDoubleNear( prevM, m ) || qgsDoubleNear( thisM, m ) )
1600 {
1601 // use centroid for constant value m segments
1602 if ( qgsDoubleNear( thisM, m ) && ( i < totalPoints - 1 ) && qgsDoubleNear( *mData, m ) )
1603 {
1604 distanceFromStart += segmentLength;
1605 // scan ahead till we find a vertex with a different m
1606 double totalLengthOfSegmentsWithConstantM = 0;
1607 for ( int j = 0; j < ( totalPoints - i ); ++j )
1608 {
1609 if ( !qgsDoubleNear( *( mData + j ), m ) )
1610 break;
1611
1612 const double segmentLength = use3DDistance ? QgsGeometryUtilsBase::distance3D( *( xData + j - 1 ), *( yData + j - 1 ), *( zData + j - 1 ), *( xData + j ), *( yData + j ), *( zData + j ) )
1613 : QgsGeometryUtilsBase::distance2D( *( xData + j - 1 ), *( yData + j - 1 ), *( xData + j ), *( yData + j ) );
1614 totalLengthOfSegmentsWithConstantM += segmentLength;
1615 }
1616
1617 distanceFromStart += totalLengthOfSegmentsWithConstantM / 2;
1618 std::unique_ptr< QgsPoint> point( interpolatePoint( distanceFromStart ) );
1619 if ( !point )
1620 return false;
1621 x = point->x();
1622 y = point->y();
1623 z = point->z();
1624 return true;
1625 }
1626
1627 const double delta = ( m - prevM ) / ( thisM - prevM );
1628
1629 const double distanceToPoint = delta * segmentLength;
1630
1631 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, x, y );
1632 z = prevZ + ( thisZ - prevZ ) * delta;
1633 distanceFromStart += distanceToPoint;
1634 return true;
1635 }
1636 }
1637
1638 distanceFromStart += segmentLength;
1639 prevX = thisX;
1640 prevY = thisY;
1641 prevZ = thisZ;
1642 prevM = thisM;
1643 ++i;
1644 }
1645 return false;
1646}
1647
1648QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
1649{
1650 if ( startDistance < 0 && endDistance < 0 )
1651 return createEmptyWithSameType();
1652
1653 endDistance = std::max( startDistance, endDistance );
1654
1655 const int totalPoints = numPoints();
1656 if ( totalPoints == 0 )
1657 return clone();
1658
1659 QVector< QgsPoint > substringPoints;
1660 substringPoints.reserve( totalPoints );
1661
1663 if ( is3D() )
1664 pointType = Qgis::WkbType::PointZ;
1665 if ( isMeasure() )
1666 pointType = QgsWkbTypes::addM( pointType );
1667
1668 const double *x = mX.constData();
1669 const double *y = mY.constData();
1670 const double *z = is3D() ? mZ.constData() : nullptr;
1671 const double *m = isMeasure() ? mM.constData() : nullptr;
1672
1673 double distanceTraversed = 0;
1674 double prevX = *x++;
1675 double prevY = *y++;
1676 double prevZ = z ? *z++ : 0.0;
1677 double prevM = m ? *m++ : 0.0;
1678 bool foundStart = false;
1679
1680 if ( startDistance < 0 )
1681 startDistance = 0;
1682
1683 for ( int i = 1; i < totalPoints; ++i )
1684 {
1685 double thisX = *x++;
1686 double thisY = *y++;
1687 double thisZ = z ? *z++ : 0.0;
1688 double thisM = m ? *m++ : 0.0;
1689
1690 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1691
1692 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1693 {
1694 // start point falls on this segment
1695 const double distanceToStart = startDistance - distanceTraversed;
1696 double startX, startY;
1697 double startZ = 0;
1698 double startM = 0;
1699 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1700 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1701 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1702 substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1703 foundStart = true;
1704 }
1705 if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1706 {
1707 // end point falls on this segment
1708 const double distanceToEnd = endDistance - distanceTraversed;
1709 double endX, endY;
1710 double endZ = 0;
1711 double endM = 0;
1712 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1713 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1714 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1715 substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1716 }
1717 else if ( foundStart )
1718 {
1719 substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1720 }
1721
1722 prevX = thisX;
1723 prevY = thisY;
1724 prevZ = thisZ;
1725 prevM = thisM;
1726 distanceTraversed += segmentLength;
1727 if ( distanceTraversed >= endDistance )
1728 break;
1729 }
1730
1731 // start point is the last node
1732 if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1733 {
1734 substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1735 << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1736 }
1737
1738 return new QgsLineString( substringPoints );
1739}
1740
1741/***************************************************************************
1742 * This class is considered CRITICAL and any change MUST be accompanied with
1743 * full unit tests.
1744 * See details in QEP #17
1745 ****************************************************************************/
1746
1747void QgsLineString::draw( QPainter &p ) const
1748{
1749 p.drawPolyline( asQPolygonF() );
1750}
1751
1752void QgsLineString::addToPainterPath( QPainterPath &path ) const
1753{
1754 int nPoints = numPoints();
1755 if ( nPoints < 1 )
1756 {
1757 return;
1758 }
1759
1760 if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1761 {
1762 path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1763 }
1764
1765 for ( int i = 1; i < nPoints; ++i )
1766 {
1767 path.lineTo( mX.at( i ), mY.at( i ) );
1768 }
1769}
1770
1771void QgsLineString::drawAsPolygon( QPainter &p ) const
1772{
1773 p.drawPolygon( asQPolygonF() );
1774}
1775
1777{
1778 QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1779 compoundCurve->addCurve( clone() );
1780 return compoundCurve;
1781}
1782
1783void QgsLineString::extend( double startDistance, double endDistance )
1784{
1785 if ( mX.size() < 2 || mY.size() < 2 )
1786 return;
1787
1788 const bool extendStart = startDistance > 0;
1789 const bool extendEnd = endDistance > 0;
1790
1791 // start of line
1792 if ( extendStart )
1793 {
1794 const double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1795 std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1796 const double newLen = currentLen + startDistance;
1797 mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1798 mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1799 }
1800 // end of line
1801 if ( extendEnd )
1802 {
1803 const int last = mX.size() - 1;
1804 const double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1805 std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1806 const double newLen = currentLen + endDistance;
1807 mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1808 mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1809 }
1810
1811 if ( extendStart || extendEnd )
1812 clearCache(); //set bounding box invalid
1813}
1814
1816{
1817 auto result = std::make_unique< QgsLineString >();
1818 result->mWkbType = mWkbType;
1819 return result.release();
1820}
1821
1823{
1824 const QgsLineString *otherLine = qgsgeometry_cast<const QgsLineString *>( other );
1825 if ( !otherLine )
1826 return -1;
1827
1828 const int size = mX.size();
1829 const int otherSize = otherLine->mX.size();
1830 if ( size > otherSize )
1831 {
1832 return 1;
1833 }
1834 else if ( size < otherSize )
1835 {
1836 return -1;
1837 }
1838
1839 if ( is3D() && !otherLine->is3D() )
1840 return 1;
1841 else if ( !is3D() && otherLine->is3D() )
1842 return -1;
1843 const bool considerZ = is3D();
1844
1845 if ( isMeasure() && !otherLine->isMeasure() )
1846 return 1;
1847 else if ( !isMeasure() && otherLine->isMeasure() )
1848 return -1;
1849 const bool considerM = isMeasure();
1850
1851 for ( int i = 0; i < size; i++ )
1852 {
1853 const double x = mX[i];
1854 const double otherX = otherLine->mX[i];
1855 if ( x < otherX )
1856 {
1857 return -1;
1858 }
1859 else if ( x > otherX )
1860 {
1861 return 1;
1862 }
1863
1864 const double y = mY[i];
1865 const double otherY = otherLine->mY[i];
1866 if ( y < otherY )
1867 {
1868 return -1;
1869 }
1870 else if ( y > otherY )
1871 {
1872 return 1;
1873 }
1874
1875 if ( considerZ )
1876 {
1877 const double z = mZ[i];
1878 const double otherZ = otherLine->mZ[i];
1879
1880 if ( z < otherZ )
1881 {
1882 return -1;
1883 }
1884 else if ( z > otherZ )
1885 {
1886 return 1;
1887 }
1888 }
1889
1890 if ( considerM )
1891 {
1892 const double m = mM[i];
1893 const double otherM = otherLine->mM[i];
1894
1895 if ( m < otherM )
1896 {
1897 return -1;
1898 }
1899 else if ( m > otherM )
1900 {
1901 return 1;
1902 }
1903 }
1904 }
1905 return 0;
1906}
1907
1909{
1910 return QStringLiteral( "LineString" );
1911}
1912
1914{
1915 return 1;
1916}
1917
1918/***************************************************************************
1919 * This class is considered CRITICAL and any change MUST be accompanied with
1920 * full unit tests.
1921 * See details in QEP #17
1922 ****************************************************************************/
1923
1925{
1926 double *zArray = nullptr;
1927 bool hasZ = is3D();
1928 int nPoints = numPoints();
1929
1930 // it's possible that transformCoords will throw an exception - so we need to use
1931 // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1932 std::unique_ptr< double[] > dummyZ;
1933 if ( !hasZ || !transformZ )
1934 {
1935 dummyZ.reset( new double[nPoints]() );
1936 zArray = dummyZ.get();
1937 }
1938 else
1939 {
1940 zArray = mZ.data();
1941 }
1942 ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1943 clearCache();
1944}
1945
1946void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1947{
1948 int nPoints = numPoints();
1949 bool hasZ = is3D();
1950 bool hasM = isMeasure();
1951 double *x = mX.data();
1952 double *y = mY.data();
1953 double *z = hasZ ? mZ.data() : nullptr;
1954 double *m = hasM ? mM.data() : nullptr;
1955 for ( int i = 0; i < nPoints; ++i )
1956 {
1957 double xOut, yOut;
1958 t.map( *x, *y, &xOut, &yOut );
1959 *x++ = xOut;
1960 *y++ = yOut;
1961 if ( hasZ )
1962 {
1963 *z = *z * zScale + zTranslate;
1964 z++;
1965 }
1966 if ( hasM )
1967 {
1968 *m = *m * mScale + mTranslate;
1969 m++;
1970 }
1971 }
1972 clearCache();
1973}
1974
1975/***************************************************************************
1976 * This class is considered CRITICAL and any change MUST be accompanied with
1977 * full unit tests.
1978 * See details in QEP #17
1979 ****************************************************************************/
1980
1981bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1982{
1983 if ( position.vertex < 0 || position.vertex > mX.size() )
1984 {
1985 return false;
1986 }
1987
1988 if ( mWkbType == Qgis::WkbType::Unknown || mX.isEmpty() )
1989 {
1991 }
1992
1993 mX.insert( position.vertex, vertex.x() );
1994 mY.insert( position.vertex, vertex.y() );
1995 if ( is3D() )
1996 {
1997 mZ.insert( position.vertex, vertex.z() );
1998 }
1999 if ( isMeasure() )
2000 {
2001 mM.insert( position.vertex, vertex.m() );
2002 }
2003 clearCache(); //set bounding box invalid
2004 return true;
2005}
2006
2007bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
2008{
2009 if ( position.vertex < 0 || position.vertex >= mX.size() )
2010 {
2011 return false;
2012 }
2013 mX[position.vertex] = newPos.x();
2014 mY[position.vertex] = newPos.y();
2015 if ( is3D() && newPos.is3D() )
2016 {
2017 mZ[position.vertex] = newPos.z();
2018 }
2019 if ( isMeasure() && newPos.isMeasure() )
2020 {
2021 mM[position.vertex] = newPos.m();
2022 }
2023 clearCache(); //set bounding box invalid
2024 return true;
2025}
2026
2028{
2029 if ( position.vertex >= mX.size() || position.vertex < 0 )
2030 {
2031 return false;
2032 }
2033
2034 mX.remove( position.vertex );
2035 mY.remove( position.vertex );
2036 if ( is3D() )
2037 {
2038 mZ.remove( position.vertex );
2039 }
2040 if ( isMeasure() )
2041 {
2042 mM.remove( position.vertex );
2043 }
2044
2045 if ( numPoints() == 1 )
2046 {
2047 clear();
2048 }
2049
2050 clearCache(); //set bounding box invalid
2051 return true;
2052}
2053
2054/***************************************************************************
2055 * This class is considered CRITICAL and any change MUST be accompanied with
2056 * full unit tests.
2057 * See details in QEP #17
2058 ****************************************************************************/
2059
2061{
2062 if ( mWkbType == Qgis::WkbType::Unknown || mX.isEmpty() )
2063 {
2065 }
2066
2067 mX.append( pt.x() );
2068 mY.append( pt.y() );
2069 if ( is3D() )
2070 {
2071 mZ.append( pt.z() );
2072 }
2073 if ( isMeasure() )
2074 {
2075 mM.append( pt.m() );
2076 }
2077 clearCache(); //set bounding box invalid
2078}
2079
2080double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
2081{
2082 double sqrDist = std::numeric_limits<double>::max();
2083 double leftOfDist = std::numeric_limits<double>::max();
2084 int prevLeftOf = 0;
2085 double prevLeftOfX = 0.0;
2086 double prevLeftOfY = 0.0;
2087 double testDist = 0;
2088 double segmentPtX, segmentPtY;
2089
2090 if ( leftOf )
2091 *leftOf = 0;
2092
2093 const int size = mX.size();
2094 if ( size == 0 || size == 1 )
2095 {
2096 vertexAfter = QgsVertexId( 0, 0, 0 );
2097 return -1;
2098 }
2099
2100 const double *xData = mX.constData();
2101 const double *yData = mY.constData();
2102 for ( int i = 1; i < size; ++i )
2103 {
2104 double prevX = xData[ i - 1 ];
2105 double prevY = yData[ i - 1 ];
2106 double currentX = xData[ i ];
2107 double currentY = yData[ i ];
2108 testDist = QgsGeometryUtilsBase::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
2109 if ( testDist < sqrDist )
2110 {
2111 sqrDist = testDist;
2112 segmentPt.setX( segmentPtX );
2113 segmentPt.setY( segmentPtY );
2114 vertexAfter.part = 0;
2115 vertexAfter.ring = 0;
2116 vertexAfter.vertex = i;
2117 }
2118 if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
2119 {
2120 int left = QgsGeometryUtilsBase::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
2121 // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
2122 // so don't set leftOf in this case, and hope that there's another segment that's the same distance
2123 // where we can perform the check
2124 if ( left != 0 )
2125 {
2126 if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
2127 {
2128 // we have two possible segments each with equal distance to point, but they disagree
2129 // on whether or not the point is to the left of them.
2130 // so we test the segments themselves and flip the result.
2131 // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
2132 *leftOf = -QgsGeometryUtilsBase::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
2133 }
2134 else
2135 {
2136 *leftOf = left;
2137 }
2138 prevLeftOf = *leftOf;
2139 leftOfDist = testDist;
2140 prevLeftOfX = prevX;
2141 prevLeftOfY = prevY;
2142 }
2143 else if ( testDist < leftOfDist )
2144 {
2145 *leftOf = left;
2146 leftOfDist = testDist;
2147 prevLeftOf = 0;
2148 }
2149 }
2150 }
2151 return sqrDist;
2152}
2153
2154/***************************************************************************
2155 * This class is considered CRITICAL and any change MUST be accompanied with
2156 * full unit tests.
2157 * See details in QEP #17
2158 ****************************************************************************/
2159
2160bool QgsLineString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
2161{
2162 if ( node < 0 || node >= numPoints() )
2163 {
2164 return false;
2165 }
2166 point = pointN( node );
2168 return true;
2169}
2170
2172{
2173 if ( mX.isEmpty() )
2174 return QgsPoint();
2175
2176 int numPoints = mX.count();
2177 if ( numPoints == 1 )
2178 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
2179
2180 double totalLineLength = 0.0;
2181 double prevX = mX.at( 0 );
2182 double prevY = mY.at( 0 );
2183 double sumX = 0.0;
2184 double sumY = 0.0;
2185
2186 for ( int i = 1; i < numPoints ; ++i )
2187 {
2188 double currentX = mX.at( i );
2189 double currentY = mY.at( i );
2190 double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
2191 std::pow( currentY - prevY, 2.0 ) );
2192 if ( qgsDoubleNear( segmentLength, 0.0 ) )
2193 continue;
2194
2195 totalLineLength += segmentLength;
2196 sumX += segmentLength * ( currentX + prevX );
2197 sumY += segmentLength * ( currentY + prevY );
2198 prevX = currentX;
2199 prevY = currentY;
2200 }
2201 sumX *= 0.5;
2202 sumY *= 0.5;
2203
2204 if ( qgsDoubleNear( totalLineLength, 0.0 ) )
2205 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
2206 else
2207 return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
2208
2209}
2210
2211/***************************************************************************
2212 * This class is considered CRITICAL and any change MUST be accompanied with
2213 * full unit tests.
2214 * See details in QEP #17
2215 ****************************************************************************/
2216
2217void QgsLineString::sumUpArea( double &sum ) const
2218{
2220 {
2221 sum += mSummedUpArea;
2222 return;
2223 }
2224
2225 mSummedUpArea = 0;
2226 const int maxIndex = mX.size();
2227 if ( maxIndex < 2 )
2228 {
2230 return;
2231 }
2232
2233 const double *x = mX.constData();
2234 const double *y = mY.constData();
2235 double prevX = *x++;
2236 double prevY = *y++;
2237 for ( int i = 1; i < maxIndex; ++i )
2238 {
2239 mSummedUpArea += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
2240 prevX = *x++;
2241 prevY = *y++;
2242 }
2243 mSummedUpArea *= 0.5;
2244
2246 sum += mSummedUpArea;
2247}
2248
2249void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
2250{
2251 bool hasZ = is3D();
2252 bool hasM = isMeasure();
2253 int nVertices = 0;
2254 wkb >> nVertices;
2255 mX.resize( nVertices );
2256 mY.resize( nVertices );
2257 hasZ ? mZ.resize( nVertices ) : mZ.clear();
2258 hasM ? mM.resize( nVertices ) : mM.clear();
2259 double *x = mX.data();
2260 double *y = mY.data();
2261 double *m = hasM ? mM.data() : nullptr;
2262 double *z = hasZ ? mZ.data() : nullptr;
2263 for ( int i = 0; i < nVertices; ++i )
2264 {
2265 wkb >> *x++;
2266 wkb >> *y++;
2267 if ( hasZ )
2268 {
2269 wkb >> *z++;
2270 }
2271 if ( hasM )
2272 {
2273 wkb >> *m++;
2274 }
2275 }
2276 clearCache(); //set bounding box invalid
2277}
2278
2279/***************************************************************************
2280 * This class is considered CRITICAL and any change MUST be accompanied with
2281 * full unit tests.
2282 * See details in QEP #17
2283 ****************************************************************************/
2284
2286{
2287 if ( numPoints() < 1 || isClosed() )
2288 {
2289 return;
2290 }
2291 addVertex( startPoint() );
2292}
2293
2295{
2296 if ( mX.count() < 2 )
2297 {
2298 //undefined
2299 return 0.0;
2300 }
2301
2302 if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
2303 {
2304 if ( isClosed() )
2305 {
2306 double previousX = mX.at( numPoints() - 2 );
2307 double previousY = mY.at( numPoints() - 2 );
2308 double currentX = mX.at( 0 );
2309 double currentY = mY.at( 0 );
2310 double afterX = mX.at( 1 );
2311 double afterY = mY.at( 1 );
2312 return QgsGeometryUtilsBase::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2313 }
2314 else if ( vertex.vertex == 0 )
2315 {
2316 return QgsGeometryUtilsBase::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
2317 }
2318 else
2319 {
2320 int a = numPoints() - 2;
2321 int b = numPoints() - 1;
2322 return QgsGeometryUtilsBase::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
2323 }
2324 }
2325 else
2326 {
2327 double previousX = mX.at( vertex.vertex - 1 );
2328 double previousY = mY.at( vertex.vertex - 1 );
2329 double currentX = mX.at( vertex.vertex );
2330 double currentY = mY.at( vertex.vertex );
2331 double afterX = mX.at( vertex.vertex + 1 );
2332 double afterY = mY.at( vertex.vertex + 1 );
2333 return QgsGeometryUtilsBase::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2334 }
2335}
2336
2338{
2339 if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
2340 return 0.0;
2341
2342 double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
2343 double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
2344 return std::sqrt( dx * dx + dy * dy );
2345}
2346
2347/***************************************************************************
2348 * This class is considered CRITICAL and any change MUST be accompanied with
2349 * full unit tests.
2350 * See details in QEP #17
2351 ****************************************************************************/
2352
2353bool QgsLineString::addZValue( double zValue )
2354{
2355 if ( QgsWkbTypes::hasZ( mWkbType ) )
2356 return false;
2357
2358 clearCache();
2360 {
2362 return true;
2363 }
2364
2366
2367 mZ.clear();
2368 int nPoints = numPoints();
2369 mZ.reserve( nPoints );
2370 for ( int i = 0; i < nPoints; ++i )
2371 {
2372 mZ << zValue;
2373 }
2374 return true;
2375}
2376
2377bool QgsLineString::addMValue( double mValue )
2378{
2379 if ( QgsWkbTypes::hasM( mWkbType ) )
2380 return false;
2381
2382 clearCache();
2384 {
2386 return true;
2387 }
2388
2390 {
2392 }
2393 else
2394 {
2396 }
2397
2398 mM.clear();
2399 int nPoints = numPoints();
2400 mM.reserve( nPoints );
2401 for ( int i = 0; i < nPoints; ++i )
2402 {
2403 mM << mValue;
2404 }
2405 return true;
2406}
2407
2409{
2410 if ( !is3D() )
2411 return false;
2412
2413 clearCache();
2415 mZ.clear();
2416 return true;
2417}
2418
2420{
2421 if ( !isMeasure() )
2422 return false;
2423
2424 clearCache();
2426 mM.clear();
2427 return true;
2428}
2429
2431{
2432 std::swap( mX, mY );
2433 clearCache();
2434}
2435
2437{
2438 if ( type == mWkbType )
2439 return true;
2440
2441 clearCache();
2442 if ( type == Qgis::WkbType::LineString25D )
2443 {
2444 //special handling required for conversion to LineString25D
2445 dropMValue();
2446 addZValue( std::numeric_limits<double>::quiet_NaN() );
2448 return true;
2449 }
2450 else
2451 {
2452 return QgsCurve::convertTo( type );
2453 }
2454}
2455
2457{
2458 if ( !transformer )
2459 return false;
2460
2461 bool hasZ = is3D();
2462 bool hasM = isMeasure();
2463 int size = mX.size();
2464
2465 double *srcX = mX.data();
2466 double *srcY = mY.data();
2467 double *srcM = hasM ? mM.data() : nullptr;
2468 double *srcZ = hasZ ? mZ.data() : nullptr;
2469
2470 bool res = true;
2471 for ( int i = 0; i < size; ++i )
2472 {
2473 double x = *srcX;
2474 double y = *srcY;
2475 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2476 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2477 if ( !transformer->transformPoint( x, y, z, m ) )
2478 {
2479 res = false;
2480 break;
2481 }
2482
2483 *srcX++ = x;
2484 *srcY++ = y;
2485 if ( hasM )
2486 *srcM++ = m;
2487 if ( hasZ )
2488 *srcZ++ = z;
2489
2490 if ( feedback && feedback->isCanceled() )
2491 {
2492 res = false;
2493 break;
2494 }
2495 }
2496 clearCache();
2497 return res;
2498}
2499
2500void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
2501{
2502 bool hasZ = is3D();
2503 bool hasM = isMeasure();
2504 int size = mX.size();
2505
2506 double *srcX = mX.data();
2507 double *srcY = mY.data();
2508 double *srcM = hasM ? mM.data() : nullptr;
2509 double *srcZ = hasZ ? mZ.data() : nullptr;
2510
2511 double *destX = srcX;
2512 double *destY = srcY;
2513 double *destM = srcM;
2514 double *destZ = srcZ;
2515
2516 int filteredPoints = 0;
2517 for ( int i = 0; i < size; ++i )
2518 {
2519 double x = *srcX++;
2520 double y = *srcY++;
2521 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
2522 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
2523
2524 if ( filter( QgsPoint( x, y, z, m ) ) )
2525 {
2526 filteredPoints++;
2527 *destX++ = x;
2528 *destY++ = y;
2529 if ( hasM )
2530 *destM++ = m;
2531 if ( hasZ )
2532 *destZ++ = z;
2533 }
2534 }
2535
2536 mX.resize( filteredPoints );
2537 mY.resize( filteredPoints );
2538 if ( hasZ )
2539 mZ.resize( filteredPoints );
2540 if ( hasM )
2541 mM.resize( filteredPoints );
2542
2543 clearCache();
2544}
2545
2546void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
2547{
2548 bool hasZ = is3D();
2549 bool hasM = isMeasure();
2550 int size = mX.size();
2551
2552 double *srcX = mX.data();
2553 double *srcY = mY.data();
2554 double *srcM = hasM ? mM.data() : nullptr;
2555 double *srcZ = hasZ ? mZ.data() : nullptr;
2556
2557 for ( int i = 0; i < size; ++i )
2558 {
2559 double x = *srcX;
2560 double y = *srcY;
2561 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2562 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2563 QgsPoint res = transform( QgsPoint( x, y, z, m ) );
2564 *srcX++ = res.x();
2565 *srcY++ = res.y();
2566 if ( hasM )
2567 *srcM++ = res.m();
2568 if ( hasZ )
2569 *srcZ++ = res.z();
2570 }
2571 clearCache();
2572}
2573
2574
2575QgsLineString *QgsLineString::measuredLine( double start, double end ) const
2576{
2577 const int nbpoints = numPoints();
2578 std::unique_ptr< QgsLineString > cloned( clone() );
2579
2580 if ( !cloned->convertTo( QgsWkbTypes::addM( mWkbType ) ) )
2581 {
2582 return cloned.release();
2583 }
2584
2585 if ( isEmpty() || ( nbpoints < 2 ) )
2586 {
2587 return cloned.release();
2588 }
2589
2590 const double range = end - start;
2591 double lineLength = length();
2592 double lengthSoFar = 0.0;
2593
2594
2595 double *mOut = cloned->mM.data();
2596 *mOut++ = start;
2597 for ( int i = 1; i < nbpoints ; ++i )
2598 {
2599 lengthSoFar += QgsGeometryUtilsBase::distance2D( mX[ i - 1], mY[ i - 1 ], mX[ i ], mY[ i ] );
2600 if ( lineLength > 0.0 )
2601 *mOut++ = start + range * lengthSoFar / lineLength;
2602 else if ( lineLength == 0.0 && nbpoints > 1 )
2603 *mOut++ = start + range * i / ( nbpoints - 1 );
2604 else
2605 *mOut++ = 0.0;
2606 }
2607
2608 return cloned.release();
2609}
2610
2611QgsLineString *QgsLineString::interpolateM( bool use3DDistance ) const
2612{
2613 if ( !isMeasure() )
2614 return nullptr;
2615
2616 const int totalPoints = numPoints();
2617 if ( totalPoints < 2 )
2618 return clone();
2619
2620 const double *xData = mX.constData();
2621 const double *yData = mY.constData();
2622 const double *mData = mM.constData();
2623 const double *zData = is3D() ? mZ.constData() : nullptr;
2624 use3DDistance &= static_cast< bool >( zData );
2625
2626 QVector< double > xOut( totalPoints );
2627 QVector< double > yOut( totalPoints );
2628 QVector< double > mOut( totalPoints );
2629 QVector< double > zOut( static_cast< bool >( zData ) ? totalPoints : 0 );
2630
2631 double *xOutData = xOut.data();
2632 double *yOutData = yOut.data();
2633 double *mOutData = mOut.data();
2634 double *zOutData = static_cast< bool >( zData ) ? zOut.data() : nullptr;
2635
2636 int i = 0;
2637 double currentSegmentLength = 0;
2638 double lastValidM = std::numeric_limits< double >::quiet_NaN();
2639 double prevX = *xData;
2640 double prevY = *yData;
2641 double prevZ = zData ? *zData : 0;
2642 while ( i < totalPoints )
2643 {
2644 double thisX = *xData++;
2645 double thisY = *yData++;
2646 double thisZ = zData ? *zData++ : 0;
2647 double thisM = *mData++;
2648
2649 currentSegmentLength = use3DDistance
2650 ? QgsGeometryUtilsBase::distance3D( prevX, prevY, prevZ, thisX, thisY, thisZ )
2651 : QgsGeometryUtilsBase::distance2D( prevX, prevY, thisX, thisY );
2652
2653 if ( !std::isnan( thisM ) )
2654 {
2655 *xOutData++ = thisX;
2656 *yOutData++ = thisY;
2657 *mOutData++ = thisM;
2658 if ( zOutData )
2659 *zOutData++ = thisZ;
2660 lastValidM = thisM;
2661 }
2662 else if ( i == 0 )
2663 {
2664 // nan m value at start of line, read ahead to find first non-nan value and backfill
2665 int j = 0;
2666 double scanAheadM = thisM;
2667 while ( i + j + 1 < totalPoints && std::isnan( scanAheadM ) )
2668 {
2669 scanAheadM = mData[ j ];
2670 ++j;
2671 }
2672 if ( std::isnan( scanAheadM ) )
2673 {
2674 // no valid m values in line
2675 return nullptr;
2676 }
2677 *xOutData++ = thisX;
2678 *yOutData++ = thisY;
2679 *mOutData++ = scanAheadM;
2680 if ( zOutData )
2681 *zOutData++ = thisZ;
2682 for ( ; i < j; ++i )
2683 {
2684 thisX = *xData++;
2685 thisY = *yData++;
2686 *xOutData++ = thisX;
2687 *yOutData++ = thisY;
2688 *mOutData++ = scanAheadM;
2689 mData++;
2690 if ( zOutData )
2691 *zOutData++ = *zData++;
2692 }
2693 lastValidM = scanAheadM;
2694 }
2695 else
2696 {
2697 // nan m value in middle of line, read ahead till next non-nan value and interpolate
2698 int j = 0;
2699 double scanAheadX = thisX;
2700 double scanAheadY = thisY;
2701 double scanAheadZ = thisZ;
2702 double distanceToNextValidM = currentSegmentLength;
2703 std::vector< double > scanAheadSegmentLengths;
2704 scanAheadSegmentLengths.emplace_back( currentSegmentLength );
2705
2706 double nextValidM = std::numeric_limits< double >::quiet_NaN();
2707 while ( i + j < totalPoints - 1 )
2708 {
2709 double nextScanAheadX = xData[j];
2710 double nextScanAheadY = yData[j];
2711 double nextScanAheadZ = zData ? zData[j] : 0;
2712 double nextScanAheadM = mData[ j ];
2713 const double scanAheadSegmentLength = use3DDistance
2714 ? QgsGeometryUtilsBase::distance3D( scanAheadX, scanAheadY, scanAheadZ, nextScanAheadX, nextScanAheadY, nextScanAheadZ )
2715 : QgsGeometryUtilsBase::distance2D( scanAheadX, scanAheadY, nextScanAheadX, nextScanAheadY );
2716 scanAheadSegmentLengths.emplace_back( scanAheadSegmentLength );
2717 distanceToNextValidM += scanAheadSegmentLength;
2718
2719 if ( !std::isnan( nextScanAheadM ) )
2720 {
2721 nextValidM = nextScanAheadM;
2722 break;
2723 }
2724
2725 scanAheadX = nextScanAheadX;
2726 scanAheadY = nextScanAheadY;
2727 scanAheadZ = nextScanAheadZ;
2728 ++j;
2729 }
2730
2731 if ( std::isnan( nextValidM ) )
2732 {
2733 // no more valid m values, so just fill remainder of vertices with previous valid m value
2734 *xOutData++ = thisX;
2735 *yOutData++ = thisY;
2736 *mOutData++ = lastValidM;
2737 if ( zOutData )
2738 *zOutData++ = thisZ;
2739 ++i;
2740 for ( ; i < totalPoints; ++i )
2741 {
2742 *xOutData++ = *xData++;
2743 *yOutData++ = *yData++;
2744 *mOutData++ = lastValidM;
2745 if ( zOutData )
2746 *zOutData++ = *zData++;
2747 }
2748 break;
2749 }
2750 else
2751 {
2752 // interpolate along segments
2753 const double delta = ( nextValidM - lastValidM ) / distanceToNextValidM;
2754 *xOutData++ = thisX;
2755 *yOutData++ = thisY;
2756 *mOutData++ = lastValidM + delta * scanAheadSegmentLengths[0];
2757 double totalScanAheadLength = scanAheadSegmentLengths[0];
2758 if ( zOutData )
2759 *zOutData++ = thisZ;
2760 for ( int k = 1; k <= j; ++i, ++k )
2761 {
2762 thisX = *xData++;
2763 thisY = *yData++;
2764 *xOutData++ = thisX;
2765 *yOutData++ = thisY;
2766 totalScanAheadLength += scanAheadSegmentLengths[k];
2767 *mOutData++ = lastValidM + delta * totalScanAheadLength;
2768 mData++;
2769 if ( zOutData )
2770 *zOutData++ = *zData++;
2771 }
2772 lastValidM = nextValidM;
2773 }
2774 }
2775
2776 prevX = thisX;
2777 prevY = thisY;
2778 prevZ = thisZ;
2779 ++i;
2780 }
2781 return new QgsLineString( xOut, yOut, zOut, mOut );
2782}
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:1922
VertexType
Types of vertex.
Definition qgis.h:2833
@ Segment
The actual start or end point of a segment.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ LineString25D
LineString25D.
@ LineStringM
LineStringM.
@ LineString
LineString.
@ LineStringZM
LineStringZM.
@ Unknown
Unknown.
@ PointM
PointM.
@ PointZ
PointZ.
@ Point25D
Point25D.
@ PointZM
PointZM.
@ LineStringZ
LineStringZ.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2502
An abstract base class for classes which transform geometries by transforming input points to output ...
virtual bool transformPoint(double &x, double &y, double &z, double &m)=0
Transforms the point defined by the coordinates (x, y, z) and the specified m value.
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual bool convertTo(Qgis::WkbType type)
Converts the geometry to a specified type.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:132
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
Definition qgsbox3d.cpp:149
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:361
bool isNull() const
Test if the box is null (holding no spatial information).
Definition qgsbox3d.cpp:289
Compound curve geometry type.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
A const WKB pointer.
Definition qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:55
Class for doing transforms between two map coordinate systems.
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform an array of coordinates to the destination CRS.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition qgscurve.cpp:293
bool mHasCachedSummedUpArea
Definition qgscurve.h:354
virtual bool isRing() const
Returns true if the curve is a ring.
Definition qgscurve.cpp:65
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgscurve.cpp:247
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM, bool removeRedundantPoints) const
Helper function for QgsCurve subclasses to snap to grids.
Definition qgscurve.cpp:317
QgsBox3D mBoundingBox
Cached bounding box.
Definition qgscurve.h:352
double mSummedUpArea
Definition qgscurve.h:355
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
Convenience functions for geometry utils.
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double distance3D(double x1, double y1, double z1, double x2, double y2, double z2)
Returns the 3D distance between (x1, y1, z1) and (x2, y2, z2).
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Represents a single 2D line segment, consisting of a 2D start and end vertex only.
Line string geometry type, with support for z-dimension and m-values.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool isClosed() const override
Returns true if the curve is closed.
void swapXy() override
Swaps the x and y coordinates from the geometry.
const double * yData() const
Returns a const pointer to the y vertex data.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
const double * xData() const
Returns a const pointer to the x vertex data.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
double length3D() const
Returns the length in 3D world of the line string.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
int dimension() const override
Returns the inherent dimension of the geometry.
void sumUpArea(double &sum) const override
Calculates the shoelace/triangle formula sum for the points in the linestring.
void clear() override
Clears the geometry, ie reset it to a null geometry.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QgsPoint startPoint() const override
Returns the starting point of the curve.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
QgsLineString * interpolateM(bool use3DDistance=true) const
Returns a copy of this line with all missing (NaN) m values interpolated from m values of surrounding...
bool isEmpty() const override
Returns true if the geometry is empty.
static QgsLineString * fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
int numPoints() const override
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
QgsLineString()
Constructor for an empty linestring geometry.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
QgsPoint centroid() const override
Returns the centroid of the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
QPolygonF asQPolygonF() const override
Returns a QPolygonF representing the points.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override
Returns true if the bounding box of this geometry intersects with a rectangle.
QString geometryType() const override
Returns a unique string representing the geometry type.
QgsLineString * measuredLine(double start, double end) const
Re-write the measure ordinate (or add one, if it isn't already there) interpolating the measure betwe...
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
QgsPoint endPoint() const override
Returns the end point of the curve.
void setYAt(int index, double y)
Sets the y-coordinate of the specified node in the line string.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
void extend(double startDistance, double endDistance)
Extends the line geometry by extrapolating out the start or end of the line by a specified distance.
QgsCompoundCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type QgsCompoundCurve.
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
void setXAt(int index, double x)
Sets the x-coordinate of the specified node in the line string.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
QVector< QgsVertexId > collectDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) const
Returns a list of any duplicate nodes contained in the geometry, within the specified tolerance.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
bool dropMValue() override
Drops any measure values which exist in the geometry.
Q_DECL_DEPRECATED QgsBox3D calculateBoundingBox3d() const
Calculates the minimal 3D bounding box for the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns a WKB representation of the geometry.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
QgsLineString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
bool isClosed2D() const override
Returns true if the curve is closed.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsLineString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
A class to represent a 2D point.
Definition qgspointxy.h:60
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
void setY(double y)
Sets the point's y-coordinate.
Definition qgspoint.h:343
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:332
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
double y
Definition qgspoint.h:53
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
WKB pointer handler.
Definition qgswkbptr.h:44
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType zmType(Qgis::WkbType type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:5774
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5857
QVector< QgsPoint > QgsPointSequence
void simplifySection(int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon)
QLineF segment(int index, QRectF rect, double radius)
double distance2D(const QgsPolylineXY &coords)
Definition qgstracer.cpp:47
int precision
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
int part
Part number.
Definition qgsvertexid.h:88
int ring
Ring number.
Definition qgsvertexid.h:91