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"
21 #include "qgscoordinatetransform.h"
22 #include "qgsgeometryutils.h"
23 #include "qgsmaptopixel.h"
24 #include "qgswkbptr.h"
25 #include "qgslinesegment.h"
26 #include "qgsgeometrytransformer.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 
38 /***************************************************************************
39  * This class is considered CRITICAL and any change MUST be accompanied with
40  * full unit tests.
41  * See details in QEP #17
42  ****************************************************************************/
43 
QgsLineString()44 QgsLineString::QgsLineString()
45 {
46   mWkbType = QgsWkbTypes::LineString;
47 }
48 
QgsLineString(const QVector<QgsPoint> & points)49 QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
50 {
51   if ( points.isEmpty() )
52   {
53     mWkbType = QgsWkbTypes::LineString;
54     return;
55   }
56   QgsWkbTypes::Type ptType = points.at( 0 ).wkbType();
57   mWkbType = QgsWkbTypes::zmType( QgsWkbTypes::LineString, QgsWkbTypes::hasZ( ptType ), QgsWkbTypes::hasM( ptType ) );
58   mX.resize( points.count() );
59   mY.resize( points.count() );
60   double *x = mX.data();
61   double *y = mY.data();
62   double *z = nullptr;
63   double *m = nullptr;
64   if ( QgsWkbTypes::hasZ( mWkbType ) )
65   {
66     mZ.resize( points.count() );
67     z = mZ.data();
68   }
69   if ( QgsWkbTypes::hasM( mWkbType ) )
70   {
71     mM.resize( points.count() );
72     m = mM.data();
73   }
74 
75   for ( const QgsPoint &pt : points )
76   {
77     *x++ = pt.x();
78     *y++ = pt.y();
79     if ( z )
80       *z++ = pt.z();
81     if ( m )
82       *m++ = pt.m();
83   }
84 }
85 
QgsLineString(const QVector<double> & x,const QVector<double> & y,const QVector<double> & z,const QVector<double> & m,bool is25DType)86 QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
87 {
88   mWkbType = QgsWkbTypes::LineString;
89   int pointCount = std::min( x.size(), y.size() );
90   if ( x.size() == pointCount )
91   {
92     mX = x;
93   }
94   else
95   {
96     mX = x.mid( 0, pointCount );
97   }
98   if ( y.size() == pointCount )
99   {
100     mY = y;
101   }
102   else
103   {
104     mY = y.mid( 0, pointCount );
105   }
106   if ( !z.isEmpty() && z.count() >= pointCount )
107   {
108     mWkbType = is25DType ? QgsWkbTypes::LineString25D : QgsWkbTypes::LineStringZ;
109     if ( z.size() == pointCount )
110     {
111       mZ = z;
112     }
113     else
114     {
115       mZ = z.mid( 0, pointCount );
116     }
117   }
118   if ( !m.isEmpty() && m.count() >= pointCount )
119   {
120     mWkbType = QgsWkbTypes::addM( mWkbType );
121     if ( m.size() == pointCount )
122     {
123       mM = m;
124     }
125     else
126     {
127       mM = m.mid( 0, pointCount );
128     }
129   }
130 }
131 
QgsLineString(const QgsPoint & p1,const QgsPoint & p2)132 QgsLineString::QgsLineString( const QgsPoint &p1, const QgsPoint &p2 )
133 {
134   mWkbType = QgsWkbTypes::LineString;
135   mX.resize( 2 );
136   mX[ 0 ] = p1.x();
137   mX[ 1 ] = p2.x();
138   mY.resize( 2 );
139   mY[ 0 ] = p1.y();
140   mY[ 1 ] = p2.y();
141   if ( p1.is3D() )
142   {
143     mWkbType = QgsWkbTypes::addZ( mWkbType );
144     mZ.resize( 2 );
145     mZ[ 0 ] = p1.z();
146     mZ[ 1 ] = p2.z();
147   }
148   if ( p1.isMeasure() )
149   {
150     mWkbType = QgsWkbTypes::addM( mWkbType );
151     mM.resize( 2 );
152     mM[ 0 ] = p1.m();
153     mM[ 1 ] = p2.m();
154   }
155 }
156 
QgsLineString(const QVector<QgsPointXY> & points)157 QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
158 {
159   mWkbType = QgsWkbTypes::LineString;
160   mX.reserve( points.size() );
161   mY.reserve( points.size() );
162   for ( const QgsPointXY &p : points )
163   {
164     mX << p.x();
165     mY << p.y();
166   }
167 }
168 
QgsLineString(const QgsLineSegment2D & segment)169 QgsLineString::QgsLineString( const QgsLineSegment2D &segment )
170 {
171   mWkbType = QgsWkbTypes::LineString;
172   mX.resize( 2 );
173   mY.resize( 2 );
174   mX[0] = segment.startX();
175   mX[1] = segment.endX();
176   mY[0] = segment.startY();
177   mY[1] = segment.endY();
178 }
179 
cubicInterpolate(double a,double b,double A,double B,double C,double D)180 static double cubicInterpolate( double a, double b,
181                                 double A, double B, double C, double D )
182 {
183   return A * b * b * b + 3 * B * b * b * a + 3 * C * b * a * a + D * a * a * a;
184 }
185 
fromBezierCurve(const QgsPoint & start,const QgsPoint & controlPoint1,const QgsPoint & controlPoint2,const QgsPoint & end,int segments)186 QgsLineString *QgsLineString::fromBezierCurve( const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments )
187 {
188   if ( segments == 0 )
189     return new QgsLineString();
190 
191   QVector<double> x;
192   x.resize( segments + 1 );
193   QVector<double> y;
194   y.resize( segments + 1 );
195   QVector<double> z;
196   double *zData = nullptr;
197   if ( start.is3D() && end.is3D() && controlPoint1.is3D() && controlPoint2.is3D() )
198   {
199     z.resize( segments + 1 );
200     zData = z.data();
201   }
202   QVector<double> m;
203   double *mData = nullptr;
204   if ( start.isMeasure() && end.isMeasure() && controlPoint1.isMeasure() && controlPoint2.isMeasure() )
205   {
206     m.resize( segments + 1 );
207     mData = m.data();
208   }
209 
210   double *xData = x.data();
211   double *yData = y.data();
212   const double step = 1.0 / segments;
213   double a = 0;
214   double b = 1.0;
215   for ( int i = 0; i < segments; i++, a += step, b -= step )
216   {
217     if ( i == 0 )
218     {
219       *xData++ = start.x();
220       *yData++ = start.y();
221       if ( zData )
222         *zData++ = start.z();
223       if ( mData )
224         *mData++ = start.m();
225     }
226     else
227     {
228       *xData++ = cubicInterpolate( a, b, start.x(), controlPoint1.x(), controlPoint2.x(), end.x() );
229       *yData++ = cubicInterpolate( a, b, start.y(), controlPoint1.y(), controlPoint2.y(), end.y() );
230       if ( zData )
231         *zData++ = cubicInterpolate( a, b, start.z(), controlPoint1.z(), controlPoint2.z(), end.z() );
232       if ( mData )
233         *mData++ = cubicInterpolate( a, b, start.m(), controlPoint1.m(), controlPoint2.m(), end.m() );
234     }
235   }
236 
237   *xData = end.x();
238   *yData = end.y();
239   if ( zData )
240     *zData = end.z();
241   if ( mData )
242     *mData = end.m();
243 
244   return new QgsLineString( x, y, z, m );
245 }
246 
fromQPolygonF(const QPolygonF & polygon)247 QgsLineString *QgsLineString::fromQPolygonF( const QPolygonF &polygon )
248 {
249   QVector< double > x;
250   QVector< double > y;
251   x.resize( polygon.count() );
252   y.resize( polygon.count() );
253   double *xData = x.data();
254   double *yData = y.data();
255 
256   const QPointF *src = polygon.data();
257   for ( int i  = 0 ; i < polygon.size(); ++ i )
258   {
259     *xData++ = src->x();
260     *yData++ = src->y();
261     src++;
262   }
263 
264   return new QgsLineString( x, y );
265 }
266 
equals(const QgsCurve & other) const267 bool QgsLineString::equals( const QgsCurve &other ) const
268 {
269   const QgsLineString *otherLine = qgsgeometry_cast< const QgsLineString * >( &other );
270   if ( !otherLine )
271     return false;
272 
273   if ( mWkbType != otherLine->mWkbType )
274     return false;
275 
276   if ( mX.count() != otherLine->mX.count() )
277     return false;
278 
279   for ( int i = 0; i < mX.count(); ++i )
280   {
281     if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
282          || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
283       return false;
284 
285     if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
286       return false;
287 
288     if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
289       return false;
290   }
291 
292   return true;
293 }
294 
clone() const295 QgsLineString *QgsLineString::clone() const
296 {
297   return new QgsLineString( *this );
298 }
299 
clear()300 void QgsLineString::clear()
301 {
302   mX.clear();
303   mY.clear();
304   mZ.clear();
305   mM.clear();
306   mWkbType = QgsWkbTypes::LineString;
307   clearCache();
308 }
309 
isEmpty() const310 bool QgsLineString::isEmpty() const
311 {
312   return mX.isEmpty();
313 }
314 
indexOf(const QgsPoint & point) const315 int QgsLineString::indexOf( const QgsPoint &point ) const
316 {
317   const int size = mX.size();
318   if ( size == 0 )
319     return -1;
320 
321   const double *x = mX.constData();
322   const double *y = mY.constData();
323   const bool useZ = is3D();
324   const bool useM = isMeasure();
325   const double *z = useZ ? mZ.constData() : nullptr;
326   const double *m = useM ? mM.constData() : nullptr;
327 
328   for ( int i = 0; i < size; ++i )
329   {
330     if ( qgsDoubleNear( *x, point.x() )
331          && qgsDoubleNear( *y, point.y() )
332          && ( !useZ || qgsDoubleNear( *z, point.z() ) )
333          && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
334       return i;
335 
336     x++;
337     y++;
338     if ( useZ )
339       z++;
340     if ( useM )
341       m++;
342   }
343   return -1;
344 }
345 
isValid(QString & error,Qgis::GeometryValidityFlags flags) const346 bool QgsLineString::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
347 {
348   if ( !isEmpty() && ( numPoints() < 2 ) )
349   {
350     error = QObject::tr( "LineString has less than 2 points and is not empty." );
351     return false;
352   }
353   return QgsCurve::isValid( error, flags );
354 }
355 
snappedToGrid(double hSpacing,double vSpacing,double dSpacing,double mSpacing) const356 QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
357 {
358   // prepare result
359   std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
360 
361   bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
362                                 result->mX, result->mY, result->mZ, result->mM );
363   if ( res )
364     return result.release();
365   else
366     return nullptr;
367 }
368 
removeDuplicateNodes(double epsilon,bool useZValues)369 bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
370 {
371   if ( mX.count() <= 2 )
372     return false; // don't create degenerate lines
373   bool result = false;
374   double prevX = mX.at( 0 );
375   double prevY = mY.at( 0 );
376   bool hasZ = is3D();
377   bool useZ = hasZ && useZValues;
378   double prevZ = useZ ? mZ.at( 0 ) : 0;
379   int i = 1;
380   int remaining = mX.count();
381   while ( i < remaining )
382   {
383     double currentX = mX.at( i );
384     double currentY = mY.at( i );
385     double currentZ = useZ ? mZ.at( i ) : 0;
386     if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
387          qgsDoubleNear( currentY, prevY, epsilon ) &&
388          ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
389     {
390       result = true;
391       // remove point
392       mX.removeAt( i );
393       mY.removeAt( i );
394       if ( hasZ )
395         mZ.removeAt( i );
396       remaining--;
397     }
398     else
399     {
400       prevX = currentX;
401       prevY = currentY;
402       prevZ = currentZ;
403       i++;
404     }
405   }
406   return result;
407 }
408 
isClosed2D() const409 bool QgsLineString::isClosed2D() const
410 {
411   if ( mX.empty() )
412     return false;
413 
414   return qgsDoubleNear( mX.first(), mX.last() ) &&
415          qgsDoubleNear( mY.first(), mY.last() );
416 }
417 
isClosed() const418 bool QgsLineString::isClosed() const
419 {
420   bool closed = isClosed2D();
421 
422   if ( is3D() && closed )
423     closed &= qgsDoubleNear( mZ.first(), mZ.last() ) || ( std::isnan( mZ.first() ) && std::isnan( mZ.last() ) );
424   return closed;
425 }
426 
boundingBoxIntersects(const QgsRectangle & rectangle) const427 bool QgsLineString::boundingBoxIntersects( const QgsRectangle &rectangle ) const
428 {
429   if ( mX.empty() )
430     return false;
431 
432   if ( !mBoundingBox.isNull() )
433   {
434     return mBoundingBox.intersects( rectangle );
435   }
436   const int nb = mX.size();
437 
438   // We are a little fancy here!
439   if ( nb > 40 )
440   {
441     // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
442     // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
443     // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
444     // will fall on approximately these vertex indices)
445     if ( rectangle.contains( mX.at( 0 ), mY.at( 0 ) ) ||
446          rectangle.contains( mX.at( static_cast< int >( nb * 0.2 ) ), mY.at( static_cast< int >( nb * 0.2 ) ) ) ||
447          rectangle.contains( mX.at( static_cast< int >( nb * 0.4 ) ), mY.at( static_cast< int >( nb * 0.4 ) ) ) ||
448          rectangle.contains( mX.at( static_cast< int >( nb * 0.6 ) ), mY.at( static_cast< int >( nb * 0.6 ) ) ) ||
449          rectangle.contains( mX.at( static_cast< int >( nb * 0.8 ) ), mY.at( static_cast< int >( nb * 0.8 ) ) ) ||
450          rectangle.contains( mX.at( nb - 1 ), mY.at( nb - 1 ) ) )
451       return true;
452   }
453 
454   // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
455   // already have it, we start performing the bounding box calculation while we are testing whether
456   // each point falls inside the rectangle. That way if we end up testing the majority of the points
457   // anyway, we can update the cached bounding box with the results we've calculated along the way
458   // and save future calls to calculate the bounding box!
459   double xmin = std::numeric_limits<double>::max();
460   double ymin = std::numeric_limits<double>::max();
461   double xmax = -std::numeric_limits<double>::max();
462   double ymax = -std::numeric_limits<double>::max();
463 
464   const double *x = mX.constData();
465   const double *y = mY.constData();
466   bool foundPointInRectangle = false;
467   for ( int i = 0; i < nb; ++i )
468   {
469     const double px = *x++;
470     xmin = std::min( xmin, px );
471     xmax = std::max( xmax, px );
472     const double py = *y++;
473     ymin = std::min( ymin, py );
474     ymax = std::max( ymax, py );
475 
476     if ( !foundPointInRectangle && rectangle.contains( px, py ) )
477     {
478       foundPointInRectangle = true;
479 
480       // now... we have a choice to make. If we've already looped through the majority of the points
481       // in this linestring then let's just continue to iterate through the remainder so that we can
482       // complete the overall bounding box calculation we've already mostly done. If however we're only
483       // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
484       // uncalculated
485       if ( i < nb * 0.5 )
486         return true;
487     }
488   }
489 
490   // at this stage we now know the overall bounding box of the linestring, so let's cache
491   // it so we don't ever have to calculate this again. We've done all the hard work anyway!
492   mBoundingBox = QgsRectangle( xmin, ymin, xmax, ymax, false );
493 
494   if ( foundPointInRectangle )
495     return true;
496 
497   // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
498   // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
499   // So we fall back to the parent class method which compares the overall bounding box against
500   // the rectangle... and this will be very cheap now that we've already calculated and cached
501   // the linestring's bounding box!
502   return QgsCurve::boundingBoxIntersects( rectangle );
503 }
504 
collectDuplicateNodes(double epsilon,bool useZValues) const505 QVector< QgsVertexId > QgsLineString::collectDuplicateNodes( double epsilon, bool useZValues ) const
506 {
507   QVector< QgsVertexId > res;
508   if ( mX.count() <= 1 )
509     return res;
510 
511   const double *x = mX.constData();
512   const double *y = mY.constData();
513   bool hasZ = is3D();
514   bool useZ = hasZ && useZValues;
515   const double *z = useZ ? mZ.constData() : nullptr;
516 
517   double prevX = *x++;
518   double prevY = *y++;
519   double prevZ = z ? *z++ : 0;
520 
521   QgsVertexId id;
522   for ( int i = 1; i < mX.count(); ++i )
523   {
524     double currentX = *x++;
525     double currentY = *y++;
526     double currentZ = useZ ? *z++ : 0;
527     if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
528          qgsDoubleNear( currentY, prevY, epsilon ) &&
529          ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
530     {
531       id.vertex = i;
532       res << id;
533     }
534     else
535     {
536       prevX = currentX;
537       prevY = currentY;
538       prevZ = currentZ;
539     }
540   }
541   return res;
542 }
543 
asQPolygonF() const544 QPolygonF QgsLineString::asQPolygonF() const
545 {
546   const int nb = mX.size();
547   QPolygonF points( nb );
548 
549   const double *x = mX.constData();
550   const double *y = mY.constData();
551   QPointF *dest = points.data();
552   for ( int i = 0; i < nb; ++i )
553   {
554     *dest++ = QPointF( *x++, *y++ );
555   }
556   return points;
557 }
558 
fromWkb(QgsConstWkbPtr & wkbPtr)559 bool QgsLineString::fromWkb( QgsConstWkbPtr &wkbPtr )
560 {
561   if ( !wkbPtr )
562   {
563     return false;
564   }
565 
566   QgsWkbTypes::Type type = wkbPtr.readHeader();
567   if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::LineString )
568   {
569     return false;
570   }
571   mWkbType = type;
572   importVerticesFromWkb( wkbPtr );
573   return true;
574 }
575 
calculateBoundingBox() const576 QgsRectangle QgsLineString::calculateBoundingBox() const
577 {
578   if ( mX.empty() )
579     return QgsRectangle();
580 
581   auto result = std::minmax_element( mX.begin(), mX.end() );
582   const double xmin = *result.first;
583   const double xmax = *result.second;
584   result = std::minmax_element( mY.begin(), mY.end() );
585   const double ymin = *result.first;
586   const double ymax = *result.second;
587   return QgsRectangle( xmin, ymin, xmax, ymax, false );
588 }
589 
scroll(int index)590 void QgsLineString::scroll( int index )
591 {
592   const int size = mX.size();
593   if ( index < 1 || index >= size - 1 )
594     return;
595 
596   const bool useZ = is3D();
597   const bool useM = isMeasure();
598 
599   QVector<double> newX( size );
600   QVector<double> newY( size );
601   QVector<double> newZ( useZ ? size : 0 );
602   QVector<double> newM( useM ? size : 0 );
603   auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
604   it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
605   *it = *newX.constBegin();
606   mX = std::move( newX );
607 
608   it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
609   it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
610   *it = *newY.constBegin();
611   mY = std::move( newY );
612   if ( useZ )
613   {
614     it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
615     it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
616     *it = *newZ.constBegin();
617     mZ = std::move( newZ );
618   }
619   if ( useM )
620   {
621     it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
622     it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
623     *it = *newM.constBegin();
624     mM = std::move( newM );
625   }
626 }
627 
628 /***************************************************************************
629  * This class is considered CRITICAL and any change MUST be accompanied with
630  * full unit tests.
631  * See details in QEP #17
632  ****************************************************************************/
fromWkt(const QString & wkt)633 bool QgsLineString::fromWkt( const QString &wkt )
634 {
635   clear();
636 
637   QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
638 
639   if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::LineString )
640     return false;
641   mWkbType = parts.first;
642 
643   QString secondWithoutParentheses = parts.second;
644   secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
645   parts.second = parts.second.remove( '(' ).remove( ')' );
646   if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
647        secondWithoutParentheses.isEmpty() )
648     return true;
649 
650   QgsPointSequence points = QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() );
651   // There is a non number in the coordinates sequence
652   // LineString ( A b, 1 2)
653   if ( points.isEmpty() )
654     return false;
655 
656   setPoints( points );
657   return true;
658 }
659 
wkbSize(QgsAbstractGeometry::WkbFlags) const660 int QgsLineString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
661 {
662   int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
663   binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
664   return binarySize;
665 }
666 
asWkb(WkbFlags flags) const667 QByteArray QgsLineString::asWkb( WkbFlags flags ) const
668 {
669   QByteArray wkbArray;
670   wkbArray.resize( QgsLineString::wkbSize( flags ) );
671   QgsWkbPtr wkb( wkbArray );
672   wkb << static_cast<char>( QgsApplication::endian() );
673   wkb << static_cast<quint32>( wkbType() );
674   QgsPointSequence pts;
675   points( pts );
676   QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
677   return wkbArray;
678 }
679 
680 /***************************************************************************
681  * This class is considered CRITICAL and any change MUST be accompanied with
682  * full unit tests.
683  * See details in QEP #17
684  ****************************************************************************/
685 
asWkt(int precision) const686 QString QgsLineString::asWkt( int precision ) const
687 {
688   QString wkt = wktTypeStr() + ' ';
689 
690   if ( isEmpty() )
691     wkt += QLatin1String( "EMPTY" );
692   else
693   {
694     QgsPointSequence pts;
695     points( pts );
696     wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
697   }
698   return wkt;
699 }
700 
asGml2(QDomDocument & doc,int precision,const QString & ns,const AxisOrder axisOrder) const701 QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
702 {
703   QgsPointSequence pts;
704   points( pts );
705 
706   QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
707 
708   if ( isEmpty() )
709     return elemLineString;
710 
711   elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
712 
713   return elemLineString;
714 }
715 
asGml3(QDomDocument & doc,int precision,const QString & ns,const QgsAbstractGeometry::AxisOrder axisOrder) const716 QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
717 {
718   QgsPointSequence pts;
719   points( pts );
720 
721   QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
722 
723   if ( isEmpty() )
724     return elemLineString;
725 
726   elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
727   return elemLineString;
728 }
729 
asJsonObject(int precision) const730 json QgsLineString::asJsonObject( int precision ) const
731 {
732   QgsPointSequence pts;
733   points( pts );
734   return
735   {
736     {  "type",  "LineString" },
737     {  "coordinates",  QgsGeometryUtils::pointsToJson( pts, precision ) }
738   };
739 }
740 
asKml(int precision) const741 QString QgsLineString::asKml( int precision ) const
742 {
743   QString kml;
744   if ( isRing() )
745   {
746     kml.append( QLatin1String( "<LinearRing>" ) );
747   }
748   else
749   {
750     kml.append( QLatin1String( "<LineString>" ) );
751   }
752   bool z = is3D();
753   kml.append( QLatin1String( "<altitudeMode>" ) );
754   if ( z )
755   {
756     kml.append( QLatin1String( "absolute" ) );
757   }
758   else
759   {
760     kml.append( QLatin1String( "clampToGround" ) );
761   }
762   kml.append( QLatin1String( "</altitudeMode>" ) );
763   kml.append( QLatin1String( "<coordinates>" ) );
764 
765   int nPoints = mX.size();
766   for ( int i = 0; i < nPoints; ++i )
767   {
768     if ( i > 0 )
769     {
770       kml.append( QLatin1String( " " ) );
771     }
772     kml.append( qgsDoubleToString( mX[i], precision ) );
773     kml.append( QLatin1String( "," ) );
774     kml.append( qgsDoubleToString( mY[i], precision ) );
775     if ( z )
776     {
777       kml.append( QLatin1String( "," ) );
778       kml.append( qgsDoubleToString( mZ[i], precision ) );
779     }
780     else
781     {
782       kml.append( QLatin1String( ",0" ) );
783     }
784   }
785   kml.append( QLatin1String( "</coordinates>" ) );
786   if ( isRing() )
787   {
788     kml.append( QLatin1String( "</LinearRing>" ) );
789   }
790   else
791   {
792     kml.append( QLatin1String( "</LineString>" ) );
793   }
794   return kml;
795 }
796 
797 /***************************************************************************
798  * This class is considered CRITICAL and any change MUST be accompanied with
799  * full unit tests.
800  * See details in QEP #17
801  ****************************************************************************/
802 
length() const803 double QgsLineString::length() const
804 {
805   double total = 0;
806   const int size = mX.size();
807   if ( size < 2 )
808     return 0;
809 
810   const double *x = mX.constData();
811   const double *y = mY.constData();
812   double dx, dy;
813 
814   double prevX = *x++;
815   double prevY = *y++;
816 
817   for ( int i = 1; i < size; ++i )
818   {
819     dx = *x - prevX;
820     dy = *y - prevY;
821     total += std::sqrt( dx * dx + dy * dy );
822 
823     prevX = *x++;
824     prevY = *y++;
825   }
826   return total;
827 }
828 
splitCurveAtVertex(int index) const829 std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsLineString::splitCurveAtVertex( int index ) const
830 {
831   const bool useZ = is3D();
832   const bool useM = isMeasure();
833 
834   const int size = mX.size();
835   if ( size == 0 )
836     return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >() );
837 
838   index = std::clamp( index, 0, size - 1 );
839 
840   const int part1Size = index + 1;
841   QVector< double > x1( part1Size );
842   QVector< double > y1( part1Size );
843   QVector< double > z1( useZ ? part1Size : 0 );
844   QVector< double > m1( useM ? part1Size : 0 );
845 
846   const double *sourceX = mX.constData();
847   const double *sourceY = mY.constData();
848   const double *sourceZ = useZ ? mZ.constData() : nullptr;
849   const double *sourceM = useM ? mM.constData() : nullptr;
850 
851   double *destX = x1.data();
852   double *destY = y1.data();
853   double *destZ = useZ ? z1.data() : nullptr;
854   double *destM = useM ? m1.data() : nullptr;
855 
856   std::copy( sourceX, sourceX + part1Size, destX );
857   std::copy( sourceY, sourceY + part1Size, destY );
858   if ( useZ )
859     std::copy( sourceZ, sourceZ + part1Size, destZ );
860   if ( useM )
861     std::copy( sourceM, sourceM + part1Size, destM );
862 
863   const int part2Size = size - index;
864   if ( part2Size < 2 )
865     return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >() );
866 
867   QVector< double > x2( part2Size );
868   QVector< double > y2( part2Size );
869   QVector< double > z2( useZ ? part2Size : 0 );
870   QVector< double > m2( useM ? part2Size : 0 );
871   destX = x2.data();
872   destY = y2.data();
873   destZ = useZ ? z2.data() : nullptr;
874   destM = useM ? m2.data() : nullptr;
875   std::copy( sourceX + index, sourceX + size, destX );
876   std::copy( sourceY + index, sourceY + size, destY );
877   if ( useZ )
878     std::copy( sourceZ + index, sourceZ + size, destZ );
879   if ( useM )
880     std::copy( sourceM + index, sourceM + size, destM );
881 
882   if ( part1Size < 2 )
883     return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
884   else
885     return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
886 }
887 
length3D() const888 double QgsLineString::length3D() const
889 {
890   if ( is3D() )
891   {
892     double total = 0;
893     const int size = mX.size();
894     if ( size < 2 )
895       return 0;
896 
897     const double *x = mX.constData();
898     const double *y = mY.constData();
899     const double *z = mZ.constData();
900     double dx, dy, dz;
901 
902     double prevX = *x++;
903     double prevY = *y++;
904     double prevZ = *z++;
905 
906     for ( int i = 1; i < size; ++i )
907     {
908       dx = *x - prevX;
909       dy = *y - prevY;
910       dz = *z - prevZ;
911       total += std::sqrt( dx * dx + dy * dy + dz * dz );
912 
913       prevX = *x++;
914       prevY = *y++;
915       prevZ = *z++;
916     }
917     return total;
918   }
919   else
920   {
921     return length();
922   }
923 }
924 
startPoint() const925 QgsPoint QgsLineString::startPoint() const
926 {
927   if ( numPoints() < 1 )
928   {
929     return QgsPoint();
930   }
931   return pointN( 0 );
932 }
933 
endPoint() const934 QgsPoint QgsLineString::endPoint() const
935 {
936   if ( numPoints() < 1 )
937   {
938     return QgsPoint();
939   }
940   return pointN( numPoints() - 1 );
941 }
942 
943 /***************************************************************************
944  * This class is considered CRITICAL and any change MUST be accompanied with
945  * full unit tests.
946  * See details in QEP #17
947  ****************************************************************************/
948 
curveToLine(double tolerance,SegmentationToleranceType toleranceType) const949 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
950 {
951   Q_UNUSED( tolerance )
952   Q_UNUSED( toleranceType )
953   return clone();
954 }
955 
numPoints() const956 int QgsLineString::numPoints() const
957 {
958   return mX.size();
959 }
960 
nCoordinates() const961 int QgsLineString::nCoordinates() const
962 {
963   return mX.size();
964 }
965 
pointN(int i) const966 QgsPoint QgsLineString::pointN( int i ) const
967 {
968   if ( i < 0 || i >= mX.size() )
969   {
970     return QgsPoint();
971   }
972 
973   double x = mX.at( i );
974   double y = mY.at( i );
975   double z = std::numeric_limits<double>::quiet_NaN();
976   double m = std::numeric_limits<double>::quiet_NaN();
977 
978   bool hasZ = is3D();
979   if ( hasZ )
980   {
981     z = mZ.at( i );
982   }
983   bool hasM = isMeasure();
984   if ( hasM )
985   {
986     m = mM.at( i );
987   }
988 
989   QgsWkbTypes::Type t = QgsWkbTypes::Point;
990   if ( mWkbType == QgsWkbTypes::LineString25D )
991   {
992     t = QgsWkbTypes::Point25D;
993   }
994   else if ( hasZ && hasM )
995   {
996     t = QgsWkbTypes::PointZM;
997   }
998   else if ( hasZ )
999   {
1000     t = QgsWkbTypes::PointZ;
1001   }
1002   else if ( hasM )
1003   {
1004     t = QgsWkbTypes::PointM;
1005   }
1006   return QgsPoint( t, x, y, z, m );
1007 }
1008 
1009 /***************************************************************************
1010  * This class is considered CRITICAL and any change MUST be accompanied with
1011  * full unit tests.
1012  * See details in QEP #17
1013  ****************************************************************************/
1014 
xAt(int index) const1015 double QgsLineString::xAt( int index ) const
1016 {
1017   if ( index >= 0 && index < mX.size() )
1018     return mX.at( index );
1019   else
1020     return 0.0;
1021 }
1022 
yAt(int index) const1023 double QgsLineString::yAt( int index ) const
1024 {
1025   if ( index >= 0 && index < mY.size() )
1026     return mY.at( index );
1027   else
1028     return 0.0;
1029 }
1030 
setXAt(int index,double x)1031 void QgsLineString::setXAt( int index, double x )
1032 {
1033   if ( index >= 0 && index < mX.size() )
1034     mX[ index ] = x;
1035   clearCache();
1036 }
1037 
setYAt(int index,double y)1038 void QgsLineString::setYAt( int index, double y )
1039 {
1040   if ( index >= 0 && index < mY.size() )
1041     mY[ index ] = y;
1042   clearCache();
1043 }
1044 
1045 /***************************************************************************
1046  * This class is considered CRITICAL and any change MUST be accompanied with
1047  * full unit tests.
1048  * See details in QEP #17
1049  ****************************************************************************/
1050 
points(QgsPointSequence & pts) const1051 void QgsLineString::points( QgsPointSequence &pts ) const
1052 {
1053   pts.clear();
1054   int nPoints = numPoints();
1055   pts.reserve( nPoints );
1056   for ( int i = 0; i < nPoints; ++i )
1057   {
1058     pts.push_back( pointN( i ) );
1059   }
1060 }
1061 
setPoints(const QgsPointSequence & points)1062 void QgsLineString::setPoints( const QgsPointSequence &points )
1063 {
1064   clearCache(); //set bounding box invalid
1065 
1066   if ( points.isEmpty() )
1067   {
1068     clear();
1069     return;
1070   }
1071 
1072   //get wkb type from first point
1073   const QgsPoint &firstPt = points.at( 0 );
1074   bool hasZ = firstPt.is3D();
1075   bool hasM = firstPt.isMeasure();
1076 
1077   setZMTypeFromSubGeometry( &firstPt, QgsWkbTypes::LineString );
1078 
1079   mX.resize( points.size() );
1080   mY.resize( points.size() );
1081   if ( hasZ )
1082   {
1083     mZ.resize( points.size() );
1084   }
1085   else
1086   {
1087     mZ.clear();
1088   }
1089   if ( hasM )
1090   {
1091     mM.resize( points.size() );
1092   }
1093   else
1094   {
1095     mM.clear();
1096   }
1097 
1098   for ( int i = 0; i < points.size(); ++i )
1099   {
1100     mX[i] = points.at( i ).x();
1101     mY[i] = points.at( i ).y();
1102     if ( hasZ )
1103     {
1104       double z = points.at( i ).z();
1105       mZ[i] = std::isnan( z ) ? 0 : z;
1106     }
1107     if ( hasM )
1108     {
1109       double m = points.at( i ).m();
1110       mM[i] = std::isnan( m ) ? 0 : m;
1111     }
1112   }
1113 }
1114 
1115 /***************************************************************************
1116  * This class is considered CRITICAL and any change MUST be accompanied with
1117  * full unit tests.
1118  * See details in QEP #17
1119  ****************************************************************************/
1120 
append(const QgsLineString * line)1121 void QgsLineString::append( const QgsLineString *line )
1122 {
1123   if ( !line )
1124   {
1125     return;
1126   }
1127 
1128   if ( numPoints() < 1 )
1129   {
1130     setZMTypeFromSubGeometry( line, QgsWkbTypes::LineString );
1131   }
1132 
1133   // do not store duplicate points
1134   if ( numPoints() > 0 &&
1135        line->numPoints() > 0 &&
1136        endPoint() == line->startPoint() )
1137   {
1138     mX.pop_back();
1139     mY.pop_back();
1140 
1141     if ( is3D() )
1142     {
1143       mZ.pop_back();
1144     }
1145     if ( isMeasure() )
1146     {
1147       mM.pop_back();
1148     }
1149   }
1150 
1151   mX += line->mX;
1152   mY += line->mY;
1153 
1154   if ( is3D() )
1155   {
1156     if ( line->is3D() )
1157     {
1158       mZ += line->mZ;
1159     }
1160     else
1161     {
1162       // if append line does not have z coordinates, fill with NaN to match number of points in final line
1163       mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1164     }
1165   }
1166 
1167   if ( isMeasure() )
1168   {
1169     if ( line->isMeasure() )
1170     {
1171       mM += line->mM;
1172     }
1173     else
1174     {
1175       // if append line does not have m values, fill with NaN to match number of points in final line
1176       mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1177     }
1178   }
1179 
1180   clearCache(); //set bounding box invalid
1181 }
1182 
reversed() const1183 QgsLineString *QgsLineString::reversed() const
1184 {
1185   QgsLineString *copy = clone();
1186   std::reverse( copy->mX.begin(), copy->mX.end() );
1187   std::reverse( copy->mY.begin(), copy->mY.end() );
1188   if ( copy->is3D() )
1189   {
1190     std::reverse( copy->mZ.begin(), copy->mZ.end() );
1191   }
1192   if ( copy->isMeasure() )
1193   {
1194     std::reverse( copy->mM.begin(), copy->mM.end() );
1195   }
1196   return copy;
1197 }
1198 
visitPointsByRegularDistance(const double distance,const std::function<bool (double,double,double,double,double,double,double,double,double,double,double,double)> & visitPoint) const1199 void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
1200 {
1201   if ( distance < 0 )
1202     return;
1203 
1204   double distanceTraversed = 0;
1205   const int totalPoints = numPoints();
1206   if ( totalPoints == 0 )
1207     return;
1208 
1209   const double *x = mX.constData();
1210   const double *y = mY.constData();
1211   const double *z = is3D() ? mZ.constData() : nullptr;
1212   const double *m = isMeasure() ? mM.constData() : nullptr;
1213 
1214   double prevX = *x++;
1215   double prevY = *y++;
1216   double prevZ = z ? *z++ : 0.0;
1217   double prevM = m ? *m++ : 0.0;
1218 
1219   if ( qgsDoubleNear( distance, 0.0 ) )
1220   {
1221     visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
1222     return;
1223   }
1224 
1225   double pZ = std::numeric_limits<double>::quiet_NaN();
1226   double pM = std::numeric_limits<double>::quiet_NaN();
1227   double nextPointDistance = distance;
1228   for ( int i = 1; i < totalPoints; ++i )
1229   {
1230     double thisX = *x++;
1231     double thisY = *y++;
1232     double thisZ = z ? *z++ : 0.0;
1233     double thisM = m ? *m++ : 0.0;
1234 
1235     const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
1236     while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
1237     {
1238       // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1239       const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
1240       double pX, pY;
1241       QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
1242           z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
1243           m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
1244 
1245       if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1246         return;
1247 
1248       nextPointDistance += distance;
1249     }
1250 
1251     distanceTraversed += segmentLength;
1252     prevX = thisX;
1253     prevY = thisY;
1254     prevZ = thisZ;
1255     prevM = thisM;
1256   }
1257 }
1258 
interpolatePoint(const double distance) const1259 QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
1260 {
1261   if ( distance < 0 )
1262     return nullptr;
1263 
1264   QgsWkbTypes::Type pointType = QgsWkbTypes::Point;
1265   if ( is3D() )
1266     pointType = QgsWkbTypes::PointZ;
1267   if ( isMeasure() )
1268     pointType = QgsWkbTypes::addM( pointType );
1269 
1270   std::unique_ptr< QgsPoint > res;
1271   visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
1272   {
1273     res = std::make_unique< QgsPoint >( pointType, x, y, z, m );
1274     return false;
1275   } );
1276   return res.release();
1277 }
1278 
curveSubstring(double startDistance,double endDistance) const1279 QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
1280 {
1281   if ( startDistance < 0 && endDistance < 0 )
1282     return createEmptyWithSameType();
1283 
1284   endDistance = std::max( startDistance, endDistance );
1285 
1286   const int totalPoints = numPoints();
1287   if ( totalPoints == 0 )
1288     return clone();
1289 
1290   QVector< QgsPoint > substringPoints;
1291   substringPoints.reserve( totalPoints );
1292 
1293   QgsWkbTypes::Type pointType = QgsWkbTypes::Point;
1294   if ( is3D() )
1295     pointType = QgsWkbTypes::PointZ;
1296   if ( isMeasure() )
1297     pointType = QgsWkbTypes::addM( pointType );
1298 
1299   const double *x = mX.constData();
1300   const double *y = mY.constData();
1301   const double *z = is3D() ? mZ.constData() : nullptr;
1302   const double *m = isMeasure() ? mM.constData() : nullptr;
1303 
1304   double distanceTraversed = 0;
1305   double prevX = *x++;
1306   double prevY = *y++;
1307   double prevZ = z ? *z++ : 0.0;
1308   double prevM = m ? *m++ : 0.0;
1309   bool foundStart = false;
1310 
1311   if ( startDistance < 0 )
1312     startDistance = 0;
1313 
1314   for ( int i = 1; i < totalPoints; ++i )
1315   {
1316     double thisX = *x++;
1317     double thisY = *y++;
1318     double thisZ = z ? *z++ : 0.0;
1319     double thisM = m ? *m++ : 0.0;
1320 
1321     const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
1322 
1323     if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1324     {
1325       // start point falls on this segment
1326       const double distanceToStart = startDistance - distanceTraversed;
1327       double startX, startY;
1328       double startZ = 0;
1329       double startM = 0;
1330       QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1331           z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1332           m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1333       substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1334       foundStart = true;
1335     }
1336     if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1337     {
1338       // end point falls on this segment
1339       const double distanceToEnd = endDistance - distanceTraversed;
1340       double endX, endY;
1341       double endZ = 0;
1342       double endM = 0;
1343       QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1344           z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1345           m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1346       substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1347     }
1348     else if ( foundStart )
1349     {
1350       substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1351     }
1352 
1353     prevX = thisX;
1354     prevY = thisY;
1355     prevZ = thisZ;
1356     prevM = thisM;
1357     distanceTraversed += segmentLength;
1358     if ( distanceTraversed >= endDistance )
1359       break;
1360   }
1361 
1362   // start point is the last node
1363   if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1364   {
1365     substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1366                     << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1367   }
1368 
1369   return new QgsLineString( substringPoints );
1370 }
1371 
1372 /***************************************************************************
1373  * This class is considered CRITICAL and any change MUST be accompanied with
1374  * full unit tests.
1375  * See details in QEP #17
1376  ****************************************************************************/
1377 
draw(QPainter & p) const1378 void QgsLineString::draw( QPainter &p ) const
1379 {
1380   p.drawPolyline( asQPolygonF() );
1381 }
1382 
addToPainterPath(QPainterPath & path) const1383 void QgsLineString::addToPainterPath( QPainterPath &path ) const
1384 {
1385   int nPoints = numPoints();
1386   if ( nPoints < 1 )
1387   {
1388     return;
1389   }
1390 
1391   if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1392   {
1393     path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1394   }
1395 
1396   for ( int i = 1; i < nPoints; ++i )
1397   {
1398     path.lineTo( mX.at( i ), mY.at( i ) );
1399   }
1400 }
1401 
drawAsPolygon(QPainter & p) const1402 void QgsLineString::drawAsPolygon( QPainter &p ) const
1403 {
1404   p.drawPolygon( asQPolygonF() );
1405 }
1406 
toCurveType() const1407 QgsCompoundCurve *QgsLineString::toCurveType() const
1408 {
1409   QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1410   compoundCurve->addCurve( clone() );
1411   return compoundCurve;
1412 }
1413 
extend(double startDistance,double endDistance)1414 void QgsLineString::extend( double startDistance, double endDistance )
1415 {
1416   if ( mX.size() < 2 || mY.size() < 2 )
1417     return;
1418 
1419   // start of line
1420   if ( startDistance > 0 )
1421   {
1422     double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1423                                    std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1424     double newLen = currentLen + startDistance;
1425     mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1426     mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1427   }
1428   // end of line
1429   if ( endDistance > 0 )
1430   {
1431     int last = mX.size() - 1;
1432     double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1433                                    std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1434     double newLen = currentLen + endDistance;
1435     mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1436     mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1437   }
1438 }
1439 
createEmptyWithSameType() const1440 QgsLineString *QgsLineString::createEmptyWithSameType() const
1441 {
1442   auto result = std::make_unique< QgsLineString >();
1443   result->mWkbType = mWkbType;
1444   return result.release();
1445 }
1446 
compareToSameClass(const QgsAbstractGeometry * other) const1447 int QgsLineString::compareToSameClass( const QgsAbstractGeometry *other ) const
1448 {
1449   const QgsLineString *otherLine = qgsgeometry_cast<const QgsLineString *>( other );
1450   if ( !otherLine )
1451     return -1;
1452 
1453   const int size = mX.size();
1454   const int otherSize = otherLine->mX.size();
1455   if ( size > otherSize )
1456   {
1457     return 1;
1458   }
1459   else if ( size < otherSize )
1460   {
1461     return -1;
1462   }
1463 
1464   if ( is3D() && !otherLine->is3D() )
1465     return 1;
1466   else if ( !is3D() && otherLine->is3D() )
1467     return -1;
1468   const bool considerZ = is3D();
1469 
1470   if ( isMeasure() && !otherLine->isMeasure() )
1471     return 1;
1472   else if ( !isMeasure() && otherLine->isMeasure() )
1473     return -1;
1474   const bool considerM = isMeasure();
1475 
1476   for ( int i = 0; i < size; i++ )
1477   {
1478     const double x = mX[i];
1479     const double otherX = otherLine->mX[i];
1480     if ( x < otherX )
1481     {
1482       return -1;
1483     }
1484     else if ( x > otherX )
1485     {
1486       return 1;
1487     }
1488 
1489     const double y = mY[i];
1490     const double otherY = otherLine->mY[i];
1491     if ( y < otherY )
1492     {
1493       return -1;
1494     }
1495     else if ( y > otherY )
1496     {
1497       return 1;
1498     }
1499 
1500     if ( considerZ )
1501     {
1502       const double z = mZ[i];
1503       const double otherZ = otherLine->mZ[i];
1504 
1505       if ( z < otherZ )
1506       {
1507         return -1;
1508       }
1509       else if ( z > otherZ )
1510       {
1511         return 1;
1512       }
1513     }
1514 
1515     if ( considerM )
1516     {
1517       const double m = mM[i];
1518       const double otherM = otherLine->mM[i];
1519 
1520       if ( m < otherM )
1521       {
1522         return -1;
1523       }
1524       else if ( m > otherM )
1525       {
1526         return 1;
1527       }
1528     }
1529   }
1530   return 0;
1531 }
1532 
geometryType() const1533 QString QgsLineString::geometryType() const
1534 {
1535   return QStringLiteral( "LineString" );
1536 }
1537 
dimension() const1538 int QgsLineString::dimension() const
1539 {
1540   return 1;
1541 }
1542 
1543 /***************************************************************************
1544  * This class is considered CRITICAL and any change MUST be accompanied with
1545  * full unit tests.
1546  * See details in QEP #17
1547  ****************************************************************************/
1548 
transform(const QgsCoordinateTransform & ct,Qgis::TransformDirection d,bool transformZ)1549 void QgsLineString::transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d, bool transformZ )
1550 {
1551   double *zArray = nullptr;
1552   bool hasZ = is3D();
1553   int nPoints = numPoints();
1554 
1555   // it's possible that transformCoords will throw an exception - so we need to use
1556   // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1557   std::unique_ptr< double[] > dummyZ;
1558   if ( !hasZ || !transformZ )
1559   {
1560     dummyZ.reset( new double[nPoints]() );
1561     zArray = dummyZ.get();
1562   }
1563   else
1564   {
1565     zArray = mZ.data();
1566   }
1567   ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1568   clearCache();
1569 }
1570 
transform(const QTransform & t,double zTranslate,double zScale,double mTranslate,double mScale)1571 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1572 {
1573   int nPoints = numPoints();
1574   bool hasZ = is3D();
1575   bool hasM = isMeasure();
1576   double *x = mX.data();
1577   double *y = mY.data();
1578   double *z = hasZ ? mZ.data() : nullptr;
1579   double *m = hasM ? mM.data() : nullptr;
1580   for ( int i = 0; i < nPoints; ++i )
1581   {
1582     double xOut, yOut;
1583     t.map( *x, *y, &xOut, &yOut );
1584     *x++ = xOut;
1585     *y++ = yOut;
1586     if ( hasZ )
1587     {
1588       *z = *z * zScale + zTranslate;
1589       z++;
1590     }
1591     if ( hasM )
1592     {
1593       *m = *m * mScale + mTranslate;
1594       m++;
1595     }
1596   }
1597   clearCache();
1598 }
1599 
1600 /***************************************************************************
1601  * This class is considered CRITICAL and any change MUST be accompanied with
1602  * full unit tests.
1603  * See details in QEP #17
1604  ****************************************************************************/
1605 
insertVertex(QgsVertexId position,const QgsPoint & vertex)1606 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1607 {
1608   if ( position.vertex < 0 || position.vertex > mX.size() )
1609   {
1610     return false;
1611   }
1612 
1613   if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1614   {
1615     setZMTypeFromSubGeometry( &vertex, QgsWkbTypes::LineString );
1616   }
1617 
1618   mX.insert( position.vertex, vertex.x() );
1619   mY.insert( position.vertex, vertex.y() );
1620   if ( is3D() )
1621   {
1622     mZ.insert( position.vertex, vertex.z() );
1623   }
1624   if ( isMeasure() )
1625   {
1626     mM.insert( position.vertex, vertex.m() );
1627   }
1628   clearCache(); //set bounding box invalid
1629   return true;
1630 }
1631 
moveVertex(QgsVertexId position,const QgsPoint & newPos)1632 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1633 {
1634   if ( position.vertex < 0 || position.vertex >= mX.size() )
1635   {
1636     return false;
1637   }
1638   mX[position.vertex] = newPos.x();
1639   mY[position.vertex] = newPos.y();
1640   if ( is3D() && newPos.is3D() )
1641   {
1642     mZ[position.vertex] = newPos.z();
1643   }
1644   if ( isMeasure() && newPos.isMeasure() )
1645   {
1646     mM[position.vertex] = newPos.m();
1647   }
1648   clearCache(); //set bounding box invalid
1649   return true;
1650 }
1651 
deleteVertex(QgsVertexId position)1652 bool QgsLineString::deleteVertex( QgsVertexId position )
1653 {
1654   if ( position.vertex >= mX.size() || position.vertex < 0 )
1655   {
1656     return false;
1657   }
1658 
1659   mX.remove( position.vertex );
1660   mY.remove( position.vertex );
1661   if ( is3D() )
1662   {
1663     mZ.remove( position.vertex );
1664   }
1665   if ( isMeasure() )
1666   {
1667     mM.remove( position.vertex );
1668   }
1669 
1670   if ( numPoints() == 1 )
1671   {
1672     clear();
1673   }
1674 
1675   clearCache(); //set bounding box invalid
1676   return true;
1677 }
1678 
1679 /***************************************************************************
1680  * This class is considered CRITICAL and any change MUST be accompanied with
1681  * full unit tests.
1682  * See details in QEP #17
1683  ****************************************************************************/
1684 
addVertex(const QgsPoint & pt)1685 void QgsLineString::addVertex( const QgsPoint &pt )
1686 {
1687   if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1688   {
1689     setZMTypeFromSubGeometry( &pt, QgsWkbTypes::LineString );
1690   }
1691 
1692   mX.append( pt.x() );
1693   mY.append( pt.y() );
1694   if ( is3D() )
1695   {
1696     mZ.append( pt.z() );
1697   }
1698   if ( isMeasure() )
1699   {
1700     mM.append( pt.m() );
1701   }
1702   clearCache(); //set bounding box invalid
1703 }
1704 
closestSegment(const QgsPoint & pt,QgsPoint & segmentPt,QgsVertexId & vertexAfter,int * leftOf,double epsilon) const1705 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt,  QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1706 {
1707   double sqrDist = std::numeric_limits<double>::max();
1708   double leftOfDist = std::numeric_limits<double>::max();
1709   int prevLeftOf = 0;
1710   double prevLeftOfX = 0.0;
1711   double prevLeftOfY = 0.0;
1712   double testDist = 0;
1713   double segmentPtX, segmentPtY;
1714 
1715   if ( leftOf )
1716     *leftOf = 0;
1717 
1718   int size = mX.size();
1719   if ( size == 0 || size == 1 )
1720   {
1721     vertexAfter = QgsVertexId( 0, 0, 0 );
1722     return -1;
1723   }
1724   for ( int i = 1; i < size; ++i )
1725   {
1726     double prevX = mX.at( i - 1 );
1727     double prevY = mY.at( i - 1 );
1728     double currentX = mX.at( i );
1729     double currentY = mY.at( i );
1730     testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1731     if ( testDist < sqrDist )
1732     {
1733       sqrDist = testDist;
1734       segmentPt.setX( segmentPtX );
1735       segmentPt.setY( segmentPtY );
1736       vertexAfter.part = 0;
1737       vertexAfter.ring = 0;
1738       vertexAfter.vertex = i;
1739     }
1740     if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1741     {
1742       int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1743       // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1744       // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1745       // where we can perform the check
1746       if ( left != 0 )
1747       {
1748         if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1749         {
1750           // we have two possible segments each with equal distance to point, but they disagree
1751           // on whether or not the point is to the left of them.
1752           // so we test the segments themselves and flip the result.
1753           // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1754           *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1755         }
1756         else
1757         {
1758           *leftOf = left;
1759         }
1760         prevLeftOf = *leftOf;
1761         leftOfDist = testDist;
1762         prevLeftOfX = prevX;
1763         prevLeftOfY = prevY;
1764       }
1765       else if ( testDist < leftOfDist )
1766       {
1767         *leftOf = left;
1768         leftOfDist = testDist;
1769         prevLeftOf = 0;
1770       }
1771     }
1772   }
1773   return sqrDist;
1774 }
1775 
1776 /***************************************************************************
1777  * This class is considered CRITICAL and any change MUST be accompanied with
1778  * full unit tests.
1779  * See details in QEP #17
1780  ****************************************************************************/
1781 
pointAt(int node,QgsPoint & point,Qgis::VertexType & type) const1782 bool QgsLineString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1783 {
1784   if ( node < 0 || node >= numPoints() )
1785   {
1786     return false;
1787   }
1788   point = pointN( node );
1789   type = Qgis::VertexType::Segment;
1790   return true;
1791 }
1792 
centroid() const1793 QgsPoint QgsLineString::centroid() const
1794 {
1795   if ( mX.isEmpty() )
1796     return QgsPoint();
1797 
1798   int numPoints = mX.count();
1799   if ( numPoints == 1 )
1800     return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1801 
1802   double totalLineLength = 0.0;
1803   double prevX = mX.at( 0 );
1804   double prevY = mY.at( 0 );
1805   double sumX = 0.0;
1806   double sumY = 0.0;
1807 
1808   for ( int i = 1; i < numPoints ; ++i )
1809   {
1810     double currentX = mX.at( i );
1811     double currentY = mY.at( i );
1812     double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1813                                       std::pow( currentY - prevY, 2.0 ) );
1814     if ( qgsDoubleNear( segmentLength, 0.0 ) )
1815       continue;
1816 
1817     totalLineLength += segmentLength;
1818     sumX += segmentLength * 0.5 * ( currentX + prevX );
1819     sumY += segmentLength * 0.5 * ( currentY + prevY );
1820     prevX = currentX;
1821     prevY = currentY;
1822   }
1823 
1824   if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1825     return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1826   else
1827     return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1828 
1829 }
1830 
1831 /***************************************************************************
1832  * This class is considered CRITICAL and any change MUST be accompanied with
1833  * full unit tests.
1834  * See details in QEP #17
1835  ****************************************************************************/
1836 
sumUpArea(double & sum) const1837 void QgsLineString::sumUpArea( double &sum ) const
1838 {
1839   int maxIndex = numPoints() - 1;
1840 
1841   for ( int i = 0; i < maxIndex; ++i )
1842   {
1843     sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1844   }
1845 }
1846 
importVerticesFromWkb(const QgsConstWkbPtr & wkb)1847 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1848 {
1849   bool hasZ = is3D();
1850   bool hasM = isMeasure();
1851   int nVertices = 0;
1852   wkb >> nVertices;
1853   mX.resize( nVertices );
1854   mY.resize( nVertices );
1855   hasZ ? mZ.resize( nVertices ) : mZ.clear();
1856   hasM ? mM.resize( nVertices ) : mM.clear();
1857   double *x = mX.data();
1858   double *y = mY.data();
1859   double *m = hasM ? mM.data() : nullptr;
1860   double *z = hasZ ? mZ.data() : nullptr;
1861   for ( int i = 0; i < nVertices; ++i )
1862   {
1863     wkb >> *x++;
1864     wkb >> *y++;
1865     if ( hasZ )
1866     {
1867       wkb >> *z++;
1868     }
1869     if ( hasM )
1870     {
1871       wkb >> *m++;
1872     }
1873   }
1874   clearCache(); //set bounding box invalid
1875 }
1876 
1877 /***************************************************************************
1878  * This class is considered CRITICAL and any change MUST be accompanied with
1879  * full unit tests.
1880  * See details in QEP #17
1881  ****************************************************************************/
1882 
close()1883 void QgsLineString::close()
1884 {
1885   if ( numPoints() < 1 || isClosed() )
1886   {
1887     return;
1888   }
1889   addVertex( startPoint() );
1890 }
1891 
vertexAngle(QgsVertexId vertex) const1892 double QgsLineString::vertexAngle( QgsVertexId vertex ) const
1893 {
1894   if ( mX.count() < 2 )
1895   {
1896     //undefined
1897     return 0.0;
1898   }
1899 
1900   if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1901   {
1902     if ( isClosed() )
1903     {
1904       double previousX = mX.at( numPoints() - 2 );
1905       double previousY = mY.at( numPoints() - 2 );
1906       double currentX = mX.at( 0 );
1907       double currentY = mY.at( 0 );
1908       double afterX = mX.at( 1 );
1909       double afterY = mY.at( 1 );
1910       return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1911     }
1912     else if ( vertex.vertex == 0 )
1913     {
1914       return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1915     }
1916     else
1917     {
1918       int a = numPoints() - 2;
1919       int b = numPoints() - 1;
1920       return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1921     }
1922   }
1923   else
1924   {
1925     double previousX = mX.at( vertex.vertex - 1 );
1926     double previousY = mY.at( vertex.vertex - 1 );
1927     double currentX = mX.at( vertex.vertex );
1928     double currentY = mY.at( vertex.vertex );
1929     double afterX = mX.at( vertex.vertex + 1 );
1930     double afterY = mY.at( vertex.vertex + 1 );
1931     return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1932   }
1933 }
1934 
segmentLength(QgsVertexId startVertex) const1935 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1936 {
1937   if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1938     return 0.0;
1939 
1940   double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1941   double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1942   return std::sqrt( dx * dx + dy * dy );
1943 }
1944 
1945 /***************************************************************************
1946  * This class is considered CRITICAL and any change MUST be accompanied with
1947  * full unit tests.
1948  * See details in QEP #17
1949  ****************************************************************************/
1950 
addZValue(double zValue)1951 bool QgsLineString::addZValue( double zValue )
1952 {
1953   if ( QgsWkbTypes::hasZ( mWkbType ) )
1954     return false;
1955 
1956   clearCache();
1957   if ( mWkbType == QgsWkbTypes::Unknown )
1958   {
1959     mWkbType = QgsWkbTypes::LineStringZ;
1960     return true;
1961   }
1962 
1963   mWkbType = QgsWkbTypes::addZ( mWkbType );
1964 
1965   mZ.clear();
1966   int nPoints = numPoints();
1967   mZ.reserve( nPoints );
1968   for ( int i = 0; i < nPoints; ++i )
1969   {
1970     mZ << zValue;
1971   }
1972   return true;
1973 }
1974 
addMValue(double mValue)1975 bool QgsLineString::addMValue( double mValue )
1976 {
1977   if ( QgsWkbTypes::hasM( mWkbType ) )
1978     return false;
1979 
1980   clearCache();
1981   if ( mWkbType == QgsWkbTypes::Unknown )
1982   {
1983     mWkbType = QgsWkbTypes::LineStringM;
1984     return true;
1985   }
1986 
1987   if ( mWkbType == QgsWkbTypes::LineString25D )
1988   {
1989     mWkbType = QgsWkbTypes::LineStringZM;
1990   }
1991   else
1992   {
1993     mWkbType = QgsWkbTypes::addM( mWkbType );
1994   }
1995 
1996   mM.clear();
1997   int nPoints = numPoints();
1998   mM.reserve( nPoints );
1999   for ( int i = 0; i < nPoints; ++i )
2000   {
2001     mM << mValue;
2002   }
2003   return true;
2004 }
2005 
dropZValue()2006 bool QgsLineString::dropZValue()
2007 {
2008   if ( !is3D() )
2009     return false;
2010 
2011   clearCache();
2012   mWkbType = QgsWkbTypes::dropZ( mWkbType );
2013   mZ.clear();
2014   return true;
2015 }
2016 
dropMValue()2017 bool QgsLineString::dropMValue()
2018 {
2019   if ( !isMeasure() )
2020     return false;
2021 
2022   clearCache();
2023   mWkbType = QgsWkbTypes::dropM( mWkbType );
2024   mM.clear();
2025   return true;
2026 }
2027 
swapXy()2028 void QgsLineString::swapXy()
2029 {
2030   std::swap( mX, mY );
2031   clearCache();
2032 }
2033 
convertTo(QgsWkbTypes::Type type)2034 bool QgsLineString::convertTo( QgsWkbTypes::Type type )
2035 {
2036   if ( type == mWkbType )
2037     return true;
2038 
2039   clearCache();
2040   if ( type == QgsWkbTypes::LineString25D )
2041   {
2042     //special handling required for conversion to LineString25D
2043     dropMValue();
2044     addZValue( std::numeric_limits<double>::quiet_NaN() );
2045     mWkbType = QgsWkbTypes::LineString25D;
2046     return true;
2047   }
2048   else
2049   {
2050     return QgsCurve::convertTo( type );
2051   }
2052 }
2053 
transform(QgsAbstractGeometryTransformer * transformer,QgsFeedback * feedback)2054 bool QgsLineString::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
2055 {
2056   if ( !transformer )
2057     return false;
2058 
2059   bool hasZ = is3D();
2060   bool hasM = isMeasure();
2061   int size = mX.size();
2062 
2063   double *srcX = mX.data();
2064   double *srcY = mY.data();
2065   double *srcM = hasM ? mM.data() : nullptr;
2066   double *srcZ = hasZ ? mZ.data() : nullptr;
2067 
2068   bool res = true;
2069   for ( int i = 0; i < size; ++i )
2070   {
2071     double x = *srcX;
2072     double y = *srcY;
2073     double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2074     double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2075     if ( !transformer->transformPoint( x, y, z, m ) )
2076     {
2077       res = false;
2078       break;
2079     }
2080 
2081     *srcX++ = x;
2082     *srcY++ = y;
2083     if ( hasM )
2084       *srcM++ = m;
2085     if ( hasZ )
2086       *srcZ++ = z;
2087 
2088     if ( feedback && feedback->isCanceled() )
2089     {
2090       res = false;
2091       break;
2092     }
2093   }
2094   clearCache();
2095   return res;
2096 }
2097 
filterVertices(const std::function<bool (const QgsPoint &)> & filter)2098 void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
2099 {
2100   bool hasZ = is3D();
2101   bool hasM = isMeasure();
2102   int size = mX.size();
2103 
2104   double *srcX = mX.data();
2105   double *srcY = mY.data();
2106   double *srcM = hasM ? mM.data() : nullptr;
2107   double *srcZ = hasZ ? mZ.data() : nullptr;
2108 
2109   double *destX = srcX;
2110   double *destY = srcY;
2111   double *destM = srcM;
2112   double *destZ = srcZ;
2113 
2114   int filteredPoints = 0;
2115   for ( int i = 0; i < size; ++i )
2116   {
2117     double x = *srcX++;
2118     double y = *srcY++;
2119     double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
2120     double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
2121 
2122     if ( filter( QgsPoint( x, y, z, m ) ) )
2123     {
2124       filteredPoints++;
2125       *destX++ = x;
2126       *destY++ = y;
2127       if ( hasM )
2128         *destM++ = m;
2129       if ( hasZ )
2130         *destZ++ = z;
2131     }
2132   }
2133 
2134   mX.resize( filteredPoints );
2135   mY.resize( filteredPoints );
2136   if ( hasZ )
2137     mZ.resize( filteredPoints );
2138   if ( hasM )
2139     mM.resize( filteredPoints );
2140 
2141   clearCache();
2142 }
2143 
transformVertices(const std::function<QgsPoint (const QgsPoint &)> & transform)2144 void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
2145 {
2146   bool hasZ = is3D();
2147   bool hasM = isMeasure();
2148   int size = mX.size();
2149 
2150   double *srcX = mX.data();
2151   double *srcY = mY.data();
2152   double *srcM = hasM ? mM.data() : nullptr;
2153   double *srcZ = hasZ ? mZ.data() : nullptr;
2154 
2155   for ( int i = 0; i < size; ++i )
2156   {
2157     double x = *srcX;
2158     double y = *srcY;
2159     double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2160     double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2161     QgsPoint res = transform( QgsPoint( x, y, z, m ) );
2162     *srcX++ = res.x();
2163     *srcY++ = res.y();
2164     if ( hasM )
2165       *srcM++ = res.m();
2166     if ( hasZ )
2167       *srcZ++ = res.z();
2168   }
2169   clearCache();
2170 }
2171