QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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"
21 #include "qgscoordinatetransform.h"
22 #include "qgsgeometryutils.h"
23 #include "qgsmaptopixel.h"
24 #include "qgswkbptr.h"
25 #include "qgslinesegment.h"
26 
27 #include <nlohmann/json.hpp>
28 #include <cmath>
29 #include <memory>
30 #include <QPainter>
31 #include <limits>
32 #include <QDomDocument>
33 #include <QJsonObject>
34 
35 
36 /***************************************************************************
37  * This class is considered CRITICAL and any change MUST be accompanied with
38  * full unit tests.
39  * See details in QEP #17
40  ****************************************************************************/
41 
43 {
45 }
46 
47 QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
48 {
49  if ( points.isEmpty() )
50  {
52  return;
53  }
54  QgsWkbTypes::Type ptType = points.at( 0 ).wkbType();
56  mX.resize( points.count() );
57  mY.resize( points.count() );
58  double *x = mX.data();
59  double *y = mY.data();
60  double *z = nullptr;
61  double *m = nullptr;
62  if ( QgsWkbTypes::hasZ( mWkbType ) )
63  {
64  mZ.resize( points.count() );
65  z = mZ.data();
66  }
67  if ( QgsWkbTypes::hasM( mWkbType ) )
68  {
69  mM.resize( points.count() );
70  m = mM.data();
71  }
72 
73  for ( const QgsPoint &pt : points )
74  {
75  *x++ = pt.x();
76  *y++ = pt.y();
77  if ( z )
78  *z++ = pt.z();
79  if ( m )
80  *m++ = pt.m();
81  }
82 }
83 
84 QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
85 {
87  int pointCount = std::min( x.size(), y.size() );
88  if ( x.size() == pointCount )
89  {
90  mX = x;
91  }
92  else
93  {
94  mX = x.mid( 0, pointCount );
95  }
96  if ( y.size() == pointCount )
97  {
98  mY = y;
99  }
100  else
101  {
102  mY = y.mid( 0, pointCount );
103  }
104  if ( !z.isEmpty() && z.count() >= pointCount )
105  {
107  if ( z.size() == pointCount )
108  {
109  mZ = z;
110  }
111  else
112  {
113  mZ = z.mid( 0, pointCount );
114  }
115  }
116  if ( !m.isEmpty() && m.count() >= pointCount )
117  {
119  if ( m.size() == pointCount )
120  {
121  mM = m;
122  }
123  else
124  {
125  mM = m.mid( 0, pointCount );
126  }
127  }
128 }
129 
131 {
133  mX.resize( 2 );
134  mX[ 0 ] = p1.x();
135  mX[ 1 ] = p2.x();
136  mY.resize( 2 );
137  mY[ 0 ] = p1.y();
138  mY[ 1 ] = p2.y();
139  if ( p1.is3D() )
140  {
142  mZ.resize( 2 );
143  mZ[ 0 ] = p1.z();
144  mZ[ 1 ] = p2.z();
145  }
146  if ( p1.isMeasure() )
147  {
149  mM.resize( 2 );
150  mM[ 0 ] = p1.m();
151  mM[ 1 ] = p2.m();
152  }
153 }
154 
155 QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
156 {
158  mX.reserve( points.size() );
159  mY.reserve( points.size() );
160  for ( const QgsPointXY &p : points )
161  {
162  mX << p.x();
163  mY << p.y();
164  }
165 }
166 
168 {
170  mX.resize( 2 );
171  mY.resize( 2 );
172  mX[0] = segment.startX();
173  mX[1] = segment.endX();
174  mY[0] = segment.startY();
175  mY[1] = segment.endY();
176 }
177 
178 static double cubicInterpolate( double a, double b,
179  double A, double B, double C, double D )
180 {
181  return A * b * b * b + 3 * B * b * b * a + 3 * C * b * a * a + D * a * a * a;
182 }
183 
184 QgsLineString *QgsLineString::fromBezierCurve( const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments )
185 {
186  if ( segments == 0 )
187  return new QgsLineString();
188 
189  QVector<double> x;
190  x.resize( segments + 1 );
191  QVector<double> y;
192  y.resize( segments + 1 );
193  QVector<double> z;
194  double *zData = nullptr;
195  if ( start.is3D() && end.is3D() && controlPoint1.is3D() && controlPoint2.is3D() )
196  {
197  z.resize( segments + 1 );
198  zData = z.data();
199  }
200  QVector<double> m;
201  double *mData = nullptr;
202  if ( start.isMeasure() && end.isMeasure() && controlPoint1.isMeasure() && controlPoint2.isMeasure() )
203  {
204  m.resize( segments + 1 );
205  mData = m.data();
206  }
207 
208  double *xData = x.data();
209  double *yData = y.data();
210  const double step = 1.0 / segments;
211  double a = 0;
212  double b = 1.0;
213  for ( int i = 0; i < segments; i++, a += step, b -= step )
214  {
215  if ( i == 0 )
216  {
217  *xData++ = start.x();
218  *yData++ = start.y();
219  if ( zData )
220  *zData++ = start.z();
221  if ( mData )
222  *mData++ = start.m();
223  }
224  else
225  {
226  *xData++ = cubicInterpolate( a, b, start.x(), controlPoint1.x(), controlPoint2.x(), end.x() );
227  *yData++ = cubicInterpolate( a, b, start.y(), controlPoint1.y(), controlPoint2.y(), end.y() );
228  if ( zData )
229  *zData++ = cubicInterpolate( a, b, start.z(), controlPoint1.z(), controlPoint2.z(), end.z() );
230  if ( mData )
231  *mData++ = cubicInterpolate( a, b, start.m(), controlPoint1.m(), controlPoint2.m(), end.m() );
232  }
233  }
234 
235  *xData = end.x();
236  *yData = end.y();
237  if ( zData )
238  *zData = end.z();
239  if ( mData )
240  *mData = end.m();
241 
242  return new QgsLineString( x, y, z, m );
243 }
244 
245 QgsLineString *QgsLineString::fromQPolygonF( const QPolygonF &polygon )
246 {
247  QVector< double > x;
248  QVector< double > y;
249  x.resize( polygon.count() );
250  y.resize( polygon.count() );
251  double *xData = x.data();
252  double *yData = y.data();
253 
254  const QPointF *src = polygon.data();
255  for ( int i = 0 ; i < polygon.size(); ++ i )
256  {
257  *xData++ = src->x();
258  *yData++ = src->y();
259  src++;
260  }
261 
262  return new QgsLineString( x, y );
263 }
264 
265 bool QgsLineString::equals( const QgsCurve &other ) const
266 {
267  const QgsLineString *otherLine = qgsgeometry_cast< const QgsLineString * >( &other );
268  if ( !otherLine )
269  return false;
270 
271  if ( mWkbType != otherLine->mWkbType )
272  return false;
273 
274  if ( mX.count() != otherLine->mX.count() )
275  return false;
276 
277  for ( int i = 0; i < mX.count(); ++i )
278  {
279  if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
280  || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
281  return false;
282 
283  if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
284  return false;
285 
286  if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
287  return false;
288  }
289 
290  return true;
291 }
292 
294 {
295  return new QgsLineString( *this );
296 }
297 
299 {
300  mX.clear();
301  mY.clear();
302  mZ.clear();
303  mM.clear();
305  clearCache();
306 }
307 
309 {
310  return mX.isEmpty();
311 }
312 
313 QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
314 {
315  // prepare result
316  std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
317 
318  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
319  result->mX, result->mY, result->mZ, result->mM );
320  if ( res )
321  return result.release();
322  else
323  return nullptr;
324 }
325 
326 bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
327 {
328  if ( mX.count() <= 2 )
329  return false; // don't create degenerate lines
330  bool result = false;
331  double prevX = mX.at( 0 );
332  double prevY = mY.at( 0 );
333  bool hasZ = is3D();
334  bool useZ = hasZ && useZValues;
335  double prevZ = useZ ? mZ.at( 0 ) : 0;
336  int i = 1;
337  int remaining = mX.count();
338  while ( i < remaining )
339  {
340  double currentX = mX.at( i );
341  double currentY = mY.at( i );
342  double currentZ = useZ ? mZ.at( i ) : 0;
343  if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
344  qgsDoubleNear( currentY, prevY, epsilon ) &&
345  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
346  {
347  result = true;
348  // remove point
349  mX.removeAt( i );
350  mY.removeAt( i );
351  if ( hasZ )
352  mZ.removeAt( i );
353  remaining--;
354  }
355  else
356  {
357  prevX = currentX;
358  prevY = currentY;
359  prevZ = currentZ;
360  i++;
361  }
362  }
363  return result;
364 }
365 
366 QPolygonF QgsLineString::asQPolygonF() const
367 {
368  const int nb = mX.size();
369  QPolygonF points( nb );
370 
371  const double *x = mX.constData();
372  const double *y = mY.constData();
373  QPointF *dest = points.data();
374  for ( int i = 0; i < nb; ++i )
375  {
376  *dest++ = QPointF( *x++, *y++ );
377  }
378  return points;
379 }
380 
382 {
383  if ( !wkbPtr )
384  {
385  return false;
386  }
387 
388  QgsWkbTypes::Type type = wkbPtr.readHeader();
390  {
391  return false;
392  }
393  mWkbType = type;
394  importVerticesFromWkb( wkbPtr );
395  return true;
396 }
397 
399 {
400  double xmin = std::numeric_limits<double>::max();
401  double ymin = std::numeric_limits<double>::max();
402  double xmax = -std::numeric_limits<double>::max();
403  double ymax = -std::numeric_limits<double>::max();
404 
405  for ( double x : mX )
406  {
407  if ( x < xmin )
408  xmin = x;
409  if ( x > xmax )
410  xmax = x;
411  }
412  for ( double y : mY )
413  {
414  if ( y < ymin )
415  ymin = y;
416  if ( y > ymax )
417  ymax = y;
418  }
419  return QgsRectangle( xmin, ymin, xmax, ymax );
420 }
421 
422 /***************************************************************************
423  * This class is considered CRITICAL and any change MUST be accompanied with
424  * full unit tests.
425  * See details in QEP #17
426  ****************************************************************************/
427 bool QgsLineString::fromWkt( const QString &wkt )
428 {
429  clear();
430 
431  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
432 
433  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::LineString )
434  return false;
435  mWkbType = parts.first;
436 
437  if ( parts.second == "EMPTY" )
438  return true;
439 
440  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
441  return true;
442 }
443 
444 QByteArray QgsLineString::asWkb() const
445 {
446  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
447  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
448 
449  QByteArray wkbArray;
450  wkbArray.resize( binarySize );
451  QgsWkbPtr wkb( wkbArray );
452  wkb << static_cast<char>( QgsApplication::endian() );
453  wkb << static_cast<quint32>( wkbType() );
454  QgsPointSequence pts;
455  points( pts );
456  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
457  return wkbArray;
458 }
459 
460 /***************************************************************************
461  * This class is considered CRITICAL and any change MUST be accompanied with
462  * full unit tests.
463  * See details in QEP #17
464  ****************************************************************************/
465 
466 QString QgsLineString::asWkt( int precision ) const
467 {
468  QString wkt = wktTypeStr() + ' ';
469 
470  if ( isEmpty() )
471  wkt += QStringLiteral( "EMPTY" );
472  else
473  {
474  QgsPointSequence pts;
475  points( pts );
476  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
477  }
478  return wkt;
479 }
480 
481 QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
482 {
483  QgsPointSequence pts;
484  points( pts );
485 
486  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
487 
488  if ( isEmpty() )
489  return elemLineString;
490 
491  elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
492 
493  return elemLineString;
494 }
495 
496 QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
497 {
498  QgsPointSequence pts;
499  points( pts );
500 
501  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
502 
503  if ( isEmpty() )
504  return elemLineString;
505 
506  elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
507  return elemLineString;
508 }
509 
511 {
512  QgsPointSequence pts;
513  points( pts );
514  return
515  {
516  { "type", "LineString" },
517  { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
518  };
519 }
520 
521 QString QgsLineString::asKml( int precision ) const
522 {
523  QString kml;
524  if ( isRing() )
525  {
526  kml.append( QLatin1String( "<LinearRing>" ) );
527  }
528  else
529  {
530  kml.append( QLatin1String( "<LineString>" ) );
531  }
532  bool z = is3D();
533  kml.append( QLatin1String( "<altitudeMode>" ) );
534  if ( z )
535  {
536  kml.append( QLatin1String( "absolute" ) );
537  }
538  else
539  {
540  kml.append( QLatin1String( "clampToGround" ) );
541  }
542  kml.append( QLatin1String( "</altitudeMode>" ) );
543  kml.append( QLatin1String( "<coordinates>" ) );
544 
545  int nPoints = mX.size();
546  for ( int i = 0; i < nPoints; ++i )
547  {
548  if ( i > 0 )
549  {
550  kml.append( QLatin1String( " " ) );
551  }
552  kml.append( qgsDoubleToString( mX[i], precision ) );
553  kml.append( QLatin1String( "," ) );
554  kml.append( qgsDoubleToString( mY[i], precision ) );
555  if ( z )
556  {
557  kml.append( QLatin1String( "," ) );
558  kml.append( qgsDoubleToString( mZ[i], precision ) );
559  }
560  else
561  {
562  kml.append( QLatin1String( ",0" ) );
563  }
564  }
565  kml.append( QLatin1String( "</coordinates>" ) );
566  if ( isRing() )
567  {
568  kml.append( QLatin1String( "</LinearRing>" ) );
569  }
570  else
571  {
572  kml.append( QLatin1String( "</LineString>" ) );
573  }
574  return kml;
575 }
576 
577 /***************************************************************************
578  * This class is considered CRITICAL and any change MUST be accompanied with
579  * full unit tests.
580  * See details in QEP #17
581  ****************************************************************************/
582 
583 double QgsLineString::length() const
584 {
585  double length = 0;
586  int size = mX.size();
587  double dx, dy;
588  for ( int i = 1; i < size; ++i )
589  {
590  dx = mX.at( i ) - mX.at( i - 1 );
591  dy = mY.at( i ) - mY.at( i - 1 );
592  length += std::sqrt( dx * dx + dy * dy );
593  }
594  return length;
595 }
596 
598 {
599  if ( is3D() )
600  {
601  double length = 0;
602  int size = mX.size();
603  double dx, dy, dz;
604  for ( int i = 1; i < size; ++i )
605  {
606  dx = mX.at( i ) - mX.at( i - 1 );
607  dy = mY.at( i ) - mY.at( i - 1 );
608  dz = mZ.at( i ) - mZ.at( i - 1 );
609  length += std::sqrt( dx * dx + dy * dy + dz * dz );
610  }
611  return length;
612  }
613  else
614  {
615  return length();
616  }
617 }
618 
620 {
621  if ( numPoints() < 1 )
622  {
623  return QgsPoint();
624  }
625  return pointN( 0 );
626 }
627 
629 {
630  if ( numPoints() < 1 )
631  {
632  return QgsPoint();
633  }
634  return pointN( numPoints() - 1 );
635 }
636 
637 /***************************************************************************
638  * This class is considered CRITICAL and any change MUST be accompanied with
639  * full unit tests.
640  * See details in QEP #17
641  ****************************************************************************/
642 
643 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
644 {
645  Q_UNUSED( tolerance )
646  Q_UNUSED( toleranceType )
647  return clone();
648 }
649 
651 {
652  return mX.size();
653 }
654 
656 {
657  return mX.size();
658 }
659 
661 {
662  if ( i < 0 || i >= mX.size() )
663  {
664  return QgsPoint();
665  }
666 
667  double x = mX.at( i );
668  double y = mY.at( i );
669  double z = std::numeric_limits<double>::quiet_NaN();
670  double m = std::numeric_limits<double>::quiet_NaN();
671 
672  bool hasZ = is3D();
673  if ( hasZ )
674  {
675  z = mZ.at( i );
676  }
677  bool hasM = isMeasure();
678  if ( hasM )
679  {
680  m = mM.at( i );
681  }
682 
685  {
687  }
688  else if ( hasZ && hasM )
689  {
691  }
692  else if ( hasZ )
693  {
695  }
696  else if ( hasM )
697  {
699  }
700  return QgsPoint( t, x, y, z, m );
701 }
702 
703 /***************************************************************************
704  * This class is considered CRITICAL and any change MUST be accompanied with
705  * full unit tests.
706  * See details in QEP #17
707  ****************************************************************************/
708 
709 double QgsLineString::xAt( int index ) const
710 {
711  if ( index >= 0 && index < mX.size() )
712  return mX.at( index );
713  else
714  return 0.0;
715 }
716 
717 double QgsLineString::yAt( int index ) const
718 {
719  if ( index >= 0 && index < mY.size() )
720  return mY.at( index );
721  else
722  return 0.0;
723 }
724 
725 void QgsLineString::setXAt( int index, double x )
726 {
727  if ( index >= 0 && index < mX.size() )
728  mX[ index ] = x;
729  clearCache();
730 }
731 
732 void QgsLineString::setYAt( int index, double y )
733 {
734  if ( index >= 0 && index < mY.size() )
735  mY[ index ] = y;
736  clearCache();
737 }
738 
739 /***************************************************************************
740  * This class is considered CRITICAL and any change MUST be accompanied with
741  * full unit tests.
742  * See details in QEP #17
743  ****************************************************************************/
744 
746 {
747  pts.clear();
748  int nPoints = numPoints();
749  pts.reserve( nPoints );
750  for ( int i = 0; i < nPoints; ++i )
751  {
752  pts.push_back( pointN( i ) );
753  }
754 }
755 
757 {
758  clearCache(); //set bounding box invalid
759 
760  if ( points.isEmpty() )
761  {
762  clear();
763  return;
764  }
765 
766  //get wkb type from first point
767  const QgsPoint &firstPt = points.at( 0 );
768  bool hasZ = firstPt.is3D();
769  bool hasM = firstPt.isMeasure();
770 
772 
773  mX.resize( points.size() );
774  mY.resize( points.size() );
775  if ( hasZ )
776  {
777  mZ.resize( points.size() );
778  }
779  else
780  {
781  mZ.clear();
782  }
783  if ( hasM )
784  {
785  mM.resize( points.size() );
786  }
787  else
788  {
789  mM.clear();
790  }
791 
792  for ( int i = 0; i < points.size(); ++i )
793  {
794  mX[i] = points.at( i ).x();
795  mY[i] = points.at( i ).y();
796  if ( hasZ )
797  {
798  double z = points.at( i ).z();
799  mZ[i] = std::isnan( z ) ? 0 : z;
800  }
801  if ( hasM )
802  {
803  double m = points.at( i ).m();
804  mM[i] = std::isnan( m ) ? 0 : m;
805  }
806  }
807 }
808 
809 /***************************************************************************
810  * This class is considered CRITICAL and any change MUST be accompanied with
811  * full unit tests.
812  * See details in QEP #17
813  ****************************************************************************/
814 
816 {
817  if ( !line )
818  {
819  return;
820  }
821 
822  if ( numPoints() < 1 )
823  {
825  }
826 
827  // do not store duplicit points
828  if ( numPoints() > 0 &&
829  line->numPoints() > 0 &&
830  endPoint() == line->startPoint() )
831  {
832  mX.pop_back();
833  mY.pop_back();
834 
835  if ( is3D() )
836  {
837  mZ.pop_back();
838  }
839  if ( isMeasure() )
840  {
841  mM.pop_back();
842  }
843  }
844 
845  mX += line->mX;
846  mY += line->mY;
847 
848  if ( is3D() )
849  {
850  if ( line->is3D() )
851  {
852  mZ += line->mZ;
853  }
854  else
855  {
856  // if append line does not have z coordinates, fill with NaN to match number of points in final line
857  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
858  }
859  }
860 
861  if ( isMeasure() )
862  {
863  if ( line->isMeasure() )
864  {
865  mM += line->mM;
866  }
867  else
868  {
869  // if append line does not have m values, fill with NaN to match number of points in final line
870  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
871  }
872  }
873 
874  clearCache(); //set bounding box invalid
875 }
876 
878 {
879  QgsLineString *copy = clone();
880  std::reverse( copy->mX.begin(), copy->mX.end() );
881  std::reverse( copy->mY.begin(), copy->mY.end() );
882  if ( copy->is3D() )
883  {
884  std::reverse( copy->mZ.begin(), copy->mZ.end() );
885  }
886  if ( copy->isMeasure() )
887  {
888  std::reverse( copy->mM.begin(), copy->mM.end() );
889  }
890  return copy;
891 }
892 
893 QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
894 {
895  if ( distance < 0 )
896  return nullptr;
897 
898  double distanceTraversed = 0;
899  const int totalPoints = numPoints();
900  if ( totalPoints == 0 )
901  return nullptr;
902 
903  const double *x = mX.constData();
904  const double *y = mY.constData();
905  const double *z = is3D() ? mZ.constData() : nullptr;
906  const double *m = isMeasure() ? mM.constData() : nullptr;
907 
909  if ( is3D() )
910  pointType = QgsWkbTypes::PointZ;
911  if ( isMeasure() )
912  pointType = QgsWkbTypes::addM( pointType );
913 
914  double prevX = *x++;
915  double prevY = *y++;
916  double prevZ = z ? *z++ : 0.0;
917  double prevM = m ? *m++ : 0.0;
918 
919  if ( qgsDoubleNear( distance, 0.0 ) )
920  {
921  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
922  }
923 
924  for ( int i = 1; i < totalPoints; ++i )
925  {
926  double thisX = *x++;
927  double thisY = *y++;
928  double thisZ = z ? *z++ : 0.0;
929  double thisM = m ? *m++ : 0.0;
930 
931  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
932  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
933  {
934  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
935  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
936  double pX, pY;
937  double pZ = 0;
938  double pM = 0;
939  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
940  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
941  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
942  return new QgsPoint( pointType, pX, pY, pZ, pM );
943  }
944 
945  distanceTraversed += segmentLength;
946  prevX = thisX;
947  prevY = thisY;
948  prevZ = thisZ;
949  prevM = thisM;
950  }
951 
952  return nullptr;
953 }
954 
955 QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
956 {
957  if ( startDistance < 0 && endDistance < 0 )
958  return createEmptyWithSameType();
959 
960  endDistance = std::max( startDistance, endDistance );
961 
962  double distanceTraversed = 0;
963  const int totalPoints = numPoints();
964  if ( totalPoints == 0 )
965  return clone();
966 
967  QVector< QgsPoint > substringPoints;
968 
970  if ( is3D() )
971  pointType = QgsWkbTypes::PointZ;
972  if ( isMeasure() )
973  pointType = QgsWkbTypes::addM( pointType );
974 
975  const double *x = mX.constData();
976  const double *y = mY.constData();
977  const double *z = is3D() ? mZ.constData() : nullptr;
978  const double *m = isMeasure() ? mM.constData() : nullptr;
979 
980  double prevX = *x++;
981  double prevY = *y++;
982  double prevZ = z ? *z++ : 0.0;
983  double prevM = m ? *m++ : 0.0;
984  bool foundStart = false;
985 
986  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
987  {
988  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
989  foundStart = true;
990  }
991 
992  substringPoints.reserve( totalPoints );
993 
994  for ( int i = 1; i < totalPoints; ++i )
995  {
996  double thisX = *x++;
997  double thisY = *y++;
998  double thisZ = z ? *z++ : 0.0;
999  double thisM = m ? *m++ : 0.0;
1000 
1001  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
1002  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1003  {
1004  // start point falls on this segment
1005  const double distanceToStart = startDistance - distanceTraversed;
1006  double startX, startY;
1007  double startZ = 0;
1008  double startM = 0;
1009  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1010  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1011  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1012  substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1013  foundStart = true;
1014  }
1015  if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1016  {
1017  // end point falls on this segment
1018  const double distanceToEnd = endDistance - distanceTraversed;
1019  double endX, endY;
1020  double endZ = 0;
1021  double endM = 0;
1022  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1023  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1024  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1025  substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1026  }
1027  else if ( foundStart )
1028  {
1029  substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1030  }
1031 
1032  distanceTraversed += segmentLength;
1033  if ( distanceTraversed > endDistance )
1034  break;
1035 
1036  prevX = thisX;
1037  prevY = thisY;
1038  prevZ = thisZ;
1039  prevM = thisM;
1040  }
1041 
1042  return new QgsLineString( substringPoints );
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 
1051 void QgsLineString::draw( QPainter &p ) const
1052 {
1053  p.drawPolyline( asQPolygonF() );
1054 }
1055 
1056 void QgsLineString::addToPainterPath( QPainterPath &path ) const
1057 {
1058  int nPoints = numPoints();
1059  if ( nPoints < 1 )
1060  {
1061  return;
1062  }
1063 
1064  if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1065  {
1066  path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1067  }
1068 
1069  for ( int i = 1; i < nPoints; ++i )
1070  {
1071  path.lineTo( mX.at( i ), mY.at( i ) );
1072  }
1073 }
1074 
1075 void QgsLineString::drawAsPolygon( QPainter &p ) const
1076 {
1077  p.drawPolygon( asQPolygonF() );
1078 }
1079 
1081 {
1082  QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1083  compoundCurve->addCurve( clone() );
1084  return compoundCurve;
1085 }
1086 
1087 void QgsLineString::extend( double startDistance, double endDistance )
1088 {
1089  if ( mX.size() < 2 || mY.size() < 2 )
1090  return;
1091 
1092  // start of line
1093  if ( startDistance > 0 )
1094  {
1095  double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1096  std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1097  double newLen = currentLen + startDistance;
1098  mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1099  mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1100  }
1101  // end of line
1102  if ( endDistance > 0 )
1103  {
1104  int last = mX.size() - 1;
1105  double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1106  std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1107  double newLen = currentLen + endDistance;
1108  mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1109  mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1110  }
1111 }
1112 
1114 {
1115  auto result = qgis::make_unique< QgsLineString >();
1116  result->mWkbType = mWkbType;
1117  return result.release();
1118 }
1119 
1121 {
1122  return QStringLiteral( "LineString" );
1123 }
1124 
1126 {
1127  return 1;
1128 }
1129 
1130 /***************************************************************************
1131  * This class is considered CRITICAL and any change MUST be accompanied with
1132  * full unit tests.
1133  * See details in QEP #17
1134  ****************************************************************************/
1135 
1137 {
1138  double *zArray = nullptr;
1139  bool hasZ = is3D();
1140  int nPoints = numPoints();
1141 
1142  // it's possible that transformCoords will throw an exception - so we need to use
1143  // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1144  std::unique_ptr< double[] > dummyZ;
1145  if ( !hasZ || !transformZ )
1146  {
1147  dummyZ.reset( new double[nPoints]() );
1148  zArray = dummyZ.get();
1149  }
1150  else
1151  {
1152  zArray = mZ.data();
1153  }
1154  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1155  clearCache();
1156 }
1157 
1158 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1159 {
1160  int nPoints = numPoints();
1161  bool hasZ = is3D();
1162  bool hasM = isMeasure();
1163  double *x = mX.data();
1164  double *y = mY.data();
1165  double *z = hasZ ? mZ.data() : nullptr;
1166  double *m = hasM ? mM.data() : nullptr;
1167  for ( int i = 0; i < nPoints; ++i )
1168  {
1169  double xOut, yOut;
1170  t.map( *x, *y, &xOut, &yOut );
1171  *x++ = xOut;
1172  *y++ = yOut;
1173  if ( hasZ )
1174  {
1175  *z = *z * zScale + zTranslate;
1176  z++;
1177  }
1178  if ( hasM )
1179  {
1180  *m = *m * mScale + mTranslate;
1181  m++;
1182  }
1183  }
1184  clearCache();
1185 }
1186 
1187 /***************************************************************************
1188  * This class is considered CRITICAL and any change MUST be accompanied with
1189  * full unit tests.
1190  * See details in QEP #17
1191  ****************************************************************************/
1192 
1193 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1194 {
1195  if ( position.vertex < 0 || position.vertex > mX.size() )
1196  {
1197  return false;
1198  }
1199 
1200  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1201  {
1203  }
1204 
1205  mX.insert( position.vertex, vertex.x() );
1206  mY.insert( position.vertex, vertex.y() );
1207  if ( is3D() )
1208  {
1209  mZ.insert( position.vertex, vertex.z() );
1210  }
1211  if ( isMeasure() )
1212  {
1213  mM.insert( position.vertex, vertex.m() );
1214  }
1215  clearCache(); //set bounding box invalid
1216  return true;
1217 }
1218 
1219 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1220 {
1221  if ( position.vertex < 0 || position.vertex >= mX.size() )
1222  {
1223  return false;
1224  }
1225  mX[position.vertex] = newPos.x();
1226  mY[position.vertex] = newPos.y();
1227  if ( is3D() && newPos.is3D() )
1228  {
1229  mZ[position.vertex] = newPos.z();
1230  }
1231  if ( isMeasure() && newPos.isMeasure() )
1232  {
1233  mM[position.vertex] = newPos.m();
1234  }
1235  clearCache(); //set bounding box invalid
1236  return true;
1237 }
1238 
1240 {
1241  if ( position.vertex >= mX.size() || position.vertex < 0 )
1242  {
1243  return false;
1244  }
1245 
1246  mX.remove( position.vertex );
1247  mY.remove( position.vertex );
1248  if ( is3D() )
1249  {
1250  mZ.remove( position.vertex );
1251  }
1252  if ( isMeasure() )
1253  {
1254  mM.remove( position.vertex );
1255  }
1256 
1257  if ( numPoints() == 1 )
1258  {
1259  clear();
1260  }
1261 
1262  clearCache(); //set bounding box invalid
1263  return true;
1264 }
1265 
1266 /***************************************************************************
1267  * This class is considered CRITICAL and any change MUST be accompanied with
1268  * full unit tests.
1269  * See details in QEP #17
1270  ****************************************************************************/
1271 
1273 {
1274  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1275  {
1277  }
1278 
1279  mX.append( pt.x() );
1280  mY.append( pt.y() );
1281  if ( is3D() )
1282  {
1283  mZ.append( pt.z() );
1284  }
1285  if ( isMeasure() )
1286  {
1287  mM.append( pt.m() );
1288  }
1289  clearCache(); //set bounding box invalid
1290 }
1291 
1292 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1293 {
1294  double sqrDist = std::numeric_limits<double>::max();
1295  double leftOfDist = std::numeric_limits<double>::max();
1296  int prevLeftOf = 0;
1297  double prevLeftOfX = 0.0;
1298  double prevLeftOfY = 0.0;
1299  double testDist = 0;
1300  double segmentPtX, segmentPtY;
1301 
1302  if ( leftOf )
1303  *leftOf = 0;
1304 
1305  int size = mX.size();
1306  if ( size == 0 || size == 1 )
1307  {
1308  vertexAfter = QgsVertexId( 0, 0, 0 );
1309  return -1;
1310  }
1311  for ( int i = 1; i < size; ++i )
1312  {
1313  double prevX = mX.at( i - 1 );
1314  double prevY = mY.at( i - 1 );
1315  double currentX = mX.at( i );
1316  double currentY = mY.at( i );
1317  testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1318  if ( testDist < sqrDist )
1319  {
1320  sqrDist = testDist;
1321  segmentPt.setX( segmentPtX );
1322  segmentPt.setY( segmentPtY );
1323  vertexAfter.part = 0;
1324  vertexAfter.ring = 0;
1325  vertexAfter.vertex = i;
1326  }
1327  if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1328  {
1329  int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1330  // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1331  // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1332  // where we can perform the check
1333  if ( left != 0 )
1334  {
1335  if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1336  {
1337  // we have two possible segments each with equal distance to point, but they disagree
1338  // on whether or not the point is to the left of them.
1339  // so we test the segments themselves and flip the result.
1340  // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1341  *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1342  }
1343  else
1344  {
1345  *leftOf = left;
1346  }
1347  prevLeftOf = *leftOf;
1348  leftOfDist = testDist;
1349  prevLeftOfX = prevX;
1350  prevLeftOfY = prevY;
1351  }
1352  else if ( testDist < leftOfDist )
1353  {
1354  *leftOf = left;
1355  leftOfDist = testDist;
1356  prevLeftOf = 0;
1357  }
1358  }
1359  }
1360  return sqrDist;
1361 }
1362 
1363 /***************************************************************************
1364  * This class is considered CRITICAL and any change MUST be accompanied with
1365  * full unit tests.
1366  * See details in QEP #17
1367  ****************************************************************************/
1368 
1369 bool QgsLineString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1370 {
1371  if ( node < 0 || node >= numPoints() )
1372  {
1373  return false;
1374  }
1375  point = pointN( node );
1377  return true;
1378 }
1379 
1381 {
1382  if ( mX.isEmpty() )
1383  return QgsPoint();
1384 
1385  int numPoints = mX.count();
1386  if ( numPoints == 1 )
1387  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1388 
1389  double totalLineLength = 0.0;
1390  double prevX = mX.at( 0 );
1391  double prevY = mY.at( 0 );
1392  double sumX = 0.0;
1393  double sumY = 0.0;
1394 
1395  for ( int i = 1; i < numPoints ; ++i )
1396  {
1397  double currentX = mX.at( i );
1398  double currentY = mY.at( i );
1399  double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1400  std::pow( currentY - prevY, 2.0 ) );
1401  if ( qgsDoubleNear( segmentLength, 0.0 ) )
1402  continue;
1403 
1404  totalLineLength += segmentLength;
1405  sumX += segmentLength * 0.5 * ( currentX + prevX );
1406  sumY += segmentLength * 0.5 * ( currentY + prevY );
1407  prevX = currentX;
1408  prevY = currentY;
1409  }
1410 
1411  if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1412  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1413  else
1414  return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1415 
1416 }
1417 
1418 /***************************************************************************
1419  * This class is considered CRITICAL and any change MUST be accompanied with
1420  * full unit tests.
1421  * See details in QEP #17
1422  ****************************************************************************/
1423 
1424 void QgsLineString::sumUpArea( double &sum ) const
1425 {
1426  int maxIndex = numPoints() - 1;
1427 
1428  for ( int i = 0; i < maxIndex; ++i )
1429  {
1430  sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1431  }
1432 }
1433 
1434 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1435 {
1436  bool hasZ = is3D();
1437  bool hasM = isMeasure();
1438  int nVertices = 0;
1439  wkb >> nVertices;
1440  mX.resize( nVertices );
1441  mY.resize( nVertices );
1442  hasZ ? mZ.resize( nVertices ) : mZ.clear();
1443  hasM ? mM.resize( nVertices ) : mM.clear();
1444  double *x = mX.data();
1445  double *y = mY.data();
1446  double *m = hasM ? mM.data() : nullptr;
1447  double *z = hasZ ? mZ.data() : nullptr;
1448  for ( int i = 0; i < nVertices; ++i )
1449  {
1450  wkb >> *x++;
1451  wkb >> *y++;
1452  if ( hasZ )
1453  {
1454  wkb >> *z++;
1455  }
1456  if ( hasM )
1457  {
1458  wkb >> *m++;
1459  }
1460  }
1461  clearCache(); //set bounding box invalid
1462 }
1463 
1464 /***************************************************************************
1465  * This class is considered CRITICAL and any change MUST be accompanied with
1466  * full unit tests.
1467  * See details in QEP #17
1468  ****************************************************************************/
1469 
1471 {
1472  if ( numPoints() < 1 || isClosed() )
1473  {
1474  return;
1475  }
1476  addVertex( startPoint() );
1477 }
1478 
1480 {
1481  if ( mX.count() < 2 )
1482  {
1483  //undefined
1484  return 0.0;
1485  }
1486 
1487  if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1488  {
1489  if ( isClosed() )
1490  {
1491  double previousX = mX.at( numPoints() - 2 );
1492  double previousY = mY.at( numPoints() - 2 );
1493  double currentX = mX.at( 0 );
1494  double currentY = mY.at( 0 );
1495  double afterX = mX.at( 1 );
1496  double afterY = mY.at( 1 );
1497  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1498  }
1499  else if ( vertex.vertex == 0 )
1500  {
1501  return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1502  }
1503  else
1504  {
1505  int a = numPoints() - 2;
1506  int b = numPoints() - 1;
1507  return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1508  }
1509  }
1510  else
1511  {
1512  double previousX = mX.at( vertex.vertex - 1 );
1513  double previousY = mY.at( vertex.vertex - 1 );
1514  double currentX = mX.at( vertex.vertex );
1515  double currentY = mY.at( vertex.vertex );
1516  double afterX = mX.at( vertex.vertex + 1 );
1517  double afterY = mY.at( vertex.vertex + 1 );
1518  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1519  }
1520 }
1521 
1522 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1523 {
1524  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1525  return 0.0;
1526 
1527  double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1528  double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1529  return std::sqrt( dx * dx + dy * dy );
1530 }
1531 
1532 /***************************************************************************
1533  * This class is considered CRITICAL and any change MUST be accompanied with
1534  * full unit tests.
1535  * See details in QEP #17
1536  ****************************************************************************/
1537 
1538 bool QgsLineString::addZValue( double zValue )
1539 {
1540  if ( QgsWkbTypes::hasZ( mWkbType ) )
1541  return false;
1542 
1543  clearCache();
1544  if ( mWkbType == QgsWkbTypes::Unknown )
1545  {
1547  return true;
1548  }
1549 
1551 
1552  mZ.clear();
1553  int nPoints = numPoints();
1554  mZ.reserve( nPoints );
1555  for ( int i = 0; i < nPoints; ++i )
1556  {
1557  mZ << zValue;
1558  }
1559  return true;
1560 }
1561 
1562 bool QgsLineString::addMValue( double mValue )
1563 {
1564  if ( QgsWkbTypes::hasM( mWkbType ) )
1565  return false;
1566 
1567  clearCache();
1568  if ( mWkbType == QgsWkbTypes::Unknown )
1569  {
1571  return true;
1572  }
1573 
1575  {
1577  }
1578  else
1579  {
1581  }
1582 
1583  mM.clear();
1584  int nPoints = numPoints();
1585  mM.reserve( nPoints );
1586  for ( int i = 0; i < nPoints; ++i )
1587  {
1588  mM << mValue;
1589  }
1590  return true;
1591 }
1592 
1594 {
1595  if ( !is3D() )
1596  return false;
1597 
1598  clearCache();
1600  mZ.clear();
1601  return true;
1602 }
1603 
1605 {
1606  if ( !isMeasure() )
1607  return false;
1608 
1609  clearCache();
1611  mM.clear();
1612  return true;
1613 }
1614 
1616 {
1617  std::swap( mX, mY );
1618  clearCache();
1619 }
1620 
1622 {
1623  if ( type == mWkbType )
1624  return true;
1625 
1626  clearCache();
1627  if ( type == QgsWkbTypes::LineString25D )
1628  {
1629  //special handling required for conversion to LineString25D
1630  dropMValue();
1631  addZValue( std::numeric_limits<double>::quiet_NaN() );
1633  return true;
1634  }
1635  else
1636  {
1637  return QgsCurve::convertTo( type );
1638  }
1639 }
1640 
1641 void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1642 {
1643  bool hasZ = is3D();
1644  bool hasM = isMeasure();
1645  int size = mX.size();
1646 
1647  double *srcX = mX.data();
1648  double *srcY = mY.data();
1649  double *srcM = hasM ? mM.data() : nullptr;
1650  double *srcZ = hasZ ? mZ.data() : nullptr;
1651 
1652  double *destX = srcX;
1653  double *destY = srcY;
1654  double *destM = srcM;
1655  double *destZ = srcZ;
1656 
1657  int filteredPoints = 0;
1658  for ( int i = 0; i < size; ++i )
1659  {
1660  double x = *srcX++;
1661  double y = *srcY++;
1662  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
1663  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
1664 
1665  if ( filter( QgsPoint( x, y, z, m ) ) )
1666  {
1667  filteredPoints++;
1668  *destX++ = x;
1669  *destY++ = y;
1670  if ( hasM )
1671  *destM++ = m;
1672  if ( hasZ )
1673  *destZ++ = z;
1674  }
1675  }
1676 
1677  mX.resize( filteredPoints );
1678  mY.resize( filteredPoints );
1679  if ( hasZ )
1680  mZ.resize( filteredPoints );
1681  if ( hasM )
1682  mM.resize( filteredPoints );
1683 
1684  clearCache();
1685 }
1686 
1687 void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1688 {
1689  bool hasZ = is3D();
1690  bool hasM = isMeasure();
1691  int size = mX.size();
1692 
1693  double *srcX = mX.data();
1694  double *srcY = mY.data();
1695  double *srcM = hasM ? mM.data() : nullptr;
1696  double *srcZ = hasZ ? mZ.data() : nullptr;
1697 
1698  for ( int i = 0; i < size; ++i )
1699  {
1700  double x = *srcX;
1701  double y = *srcY;
1702  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
1703  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
1704  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
1705  *srcX++ = res.x();
1706  *srcY++ = res.y();
1707  if ( hasM )
1708  *srcM++ = res.m();
1709  if ( hasZ )
1710  *srcZ++ = res.z();
1711  }
1712  clearCache();
1713 }
bool isMeasure() const
Returns true if the geometry contains m values.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
int precision
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
bool isEmpty() const override
Returns true if the geometry is empty.
double y
Definition: qgspoint.h:42
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
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.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
int dimension() const override
Returns the inherent dimension of the geometry.
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...
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values...
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
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, y1) to (x2, y2) and (x2, y2) to (x3, y3).
QString geometryType() const override
Returns a unique string representing the geometry type.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
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.
A class to represent a 2D point.
Definition: qgspointxy.h:43
double endY() const
Returns the segment&#39;s end y-coordinate.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
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.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
virtual bool isRing() const
Returns true if the curve is a ring.
Definition: qgscurve.cpp:56
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void setYAt(int index, double y)
Sets the y-coordinate of the specified node in the line string.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:256
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
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.
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
static endian_t endian()
Returns whether this machine uses big or little endian.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QgsPoint endPoint() const override
Returns the end point of the curve.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:917
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1087
void setXAt(int index, double x)
Sets the x-coordinate of the specified node in the line string.
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
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.
QgsWkbTypes::Type mWkbType
int numPoints() const override
Returns the number of points in the curve.
double length3D() const
Returns the length in 3D world of the line string.
bool convertTo(QgsWkbTypes::Type type) override
Converts the geometry to a specified type.
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...
QString wktTypeStr() const
Returns the WKT type string of the geometry.
const double * xData() const
Returns a const pointer to the x vertex data.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
double startX() const
Returns the segment&#39;s start x-coordinate.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
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 Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1038
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
QPolygonF asQPolygonF() const override
Returns a QPolygonF representing the points.
Utility class for identifying a unique vertex within a 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 * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:275
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
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 setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1013
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
void swapXy() override
Swaps the x and y coordinates from the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
const double * yData() const
Returns a const pointer to the y vertex data.
AxisOrder
Axis order for GML generation.
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...
QgsPoint startPoint() const override
Returns the starting point of the curve.
Represents a single 2D line segment, consisting of a 2D start and end vertex only.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values...
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
void setX(double x)
Sets the point&#39;s x-coordinate.
Definition: qgspoint.h:269
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
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) const
Helper function for QgsCurve subclasses to snap to grids.
Definition: qgscurve.cpp:279
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
void setY(double y)
Sets the point&#39;s y-coordinate.
Definition: qgspoint.h:280
QVector< QgsPoint > QgsPointSequence
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
double endX() const
Returns the segment&#39;s end x-coordinate.
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1069
QgsLineString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid...
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Class for doing transforms between two map coordinate systems.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool dropMValue() override
Drops any measure values which exist in the geometry.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static Type zmType(Type type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:675
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.
virtual bool convertTo(QgsWkbTypes::Type type)
Converts the geometry to a specified type.
double z
Definition: qgspoint.h:43
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:967
Compound curve geometry type.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
QgsLineString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QgsCompoundCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type QgsCompoundCurve.
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...
double startY() const
Returns the segment&#39;s start y-coordinate.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:576
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negative values mean left ...
Definition: MathUtils.cpp:292
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
double m
Definition: qgspoint.h:44
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void extend(double startDistance, double endDistance)
Extends the line geometry by extrapolating out the start or end of the line by a specified distance...
double x
Definition: qgspoint.h:41