QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 #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 
45 {
47 }
48 
49 QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
50 {
51  if ( points.isEmpty() )
52  {
54  return;
55  }
56  QgsWkbTypes::Type ptType = points.at( 0 ).wkbType();
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 
86 QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
87 {
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  {
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  {
121  if ( m.size() == pointCount )
122  {
123  mM = m;
124  }
125  else
126  {
127  mM = m.mid( 0, pointCount );
128  }
129  }
130 }
131 
133 {
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  {
144  mZ.resize( 2 );
145  mZ[ 0 ] = p1.z();
146  mZ[ 1 ] = p2.z();
147  }
148  if ( p1.isMeasure() )
149  {
151  mM.resize( 2 );
152  mM[ 0 ] = p1.m();
153  mM[ 1 ] = p2.m();
154  }
155 }
156 
157 QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
158 {
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 
170 {
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 
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 
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 
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 
267 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 
296 {
297  return new QgsLineString( *this );
298 }
299 
301 {
302  mX.clear();
303  mY.clear();
304  mZ.clear();
305  mM.clear();
307  clearCache();
308 }
309 
311 {
312  return mX.isEmpty();
313 }
314 
315 bool QgsLineString::isValid( QString &error, int flags ) const
316 {
317  if ( !isEmpty() && ( numPoints() < 2 ) )
318  {
319  error = QObject::tr( "LineString has less than 2 points and is not empty." );
320  return false;
321  }
322  return QgsCurve::isValid( error, flags );
323 }
324 
325 QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
326 {
327  // prepare result
328  std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
329 
330  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
331  result->mX, result->mY, result->mZ, result->mM );
332  if ( res )
333  return result.release();
334  else
335  return nullptr;
336 }
337 
338 bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
339 {
340  if ( mX.count() <= 2 )
341  return false; // don't create degenerate lines
342  bool result = false;
343  double prevX = mX.at( 0 );
344  double prevY = mY.at( 0 );
345  bool hasZ = is3D();
346  bool useZ = hasZ && useZValues;
347  double prevZ = useZ ? mZ.at( 0 ) : 0;
348  int i = 1;
349  int remaining = mX.count();
350  while ( i < remaining )
351  {
352  double currentX = mX.at( i );
353  double currentY = mY.at( i );
354  double currentZ = useZ ? mZ.at( i ) : 0;
355  if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
356  qgsDoubleNear( currentY, prevY, epsilon ) &&
357  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
358  {
359  result = true;
360  // remove point
361  mX.removeAt( i );
362  mY.removeAt( i );
363  if ( hasZ )
364  mZ.removeAt( i );
365  remaining--;
366  }
367  else
368  {
369  prevX = currentX;
370  prevY = currentY;
371  prevZ = currentZ;
372  i++;
373  }
374  }
375  return result;
376 }
377 
378 QVector< QgsVertexId > QgsLineString::collectDuplicateNodes( double epsilon, bool useZValues ) const
379 {
380  QVector< QgsVertexId > res;
381  if ( mX.count() <= 1 )
382  return res;
383 
384  const double *x = mX.constData();
385  const double *y = mY.constData();
386  bool hasZ = is3D();
387  bool useZ = hasZ && useZValues;
388  const double *z = useZ ? mZ.constData() : nullptr;
389 
390  double prevX = *x++;
391  double prevY = *y++;
392  double prevZ = z ? *z++ : 0;
393 
394  QgsVertexId id;
395  for ( int i = 1; i < mX.count(); ++i )
396  {
397  double currentX = *x++;
398  double currentY = *y++;
399  double currentZ = useZ ? *z++ : 0;
400  if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
401  qgsDoubleNear( currentY, prevY, epsilon ) &&
402  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
403  {
404  id.vertex = i;
405  res << id;
406  }
407  else
408  {
409  prevX = currentX;
410  prevY = currentY;
411  prevZ = currentZ;
412  }
413  }
414  return res;
415 }
416 
417 QPolygonF QgsLineString::asQPolygonF() const
418 {
419  const int nb = mX.size();
420  QPolygonF points( nb );
421 
422  const double *x = mX.constData();
423  const double *y = mY.constData();
424  QPointF *dest = points.data();
425  for ( int i = 0; i < nb; ++i )
426  {
427  *dest++ = QPointF( *x++, *y++ );
428  }
429  return points;
430 }
431 
433 {
434  if ( !wkbPtr )
435  {
436  return false;
437  }
438 
439  QgsWkbTypes::Type type = wkbPtr.readHeader();
441  {
442  return false;
443  }
444  mWkbType = type;
445  importVerticesFromWkb( wkbPtr );
446  return true;
447 }
448 
450 {
451  double xmin = std::numeric_limits<double>::max();
452  double ymin = std::numeric_limits<double>::max();
453  double xmax = -std::numeric_limits<double>::max();
454  double ymax = -std::numeric_limits<double>::max();
455 
456  for ( double x : mX )
457  {
458  if ( x < xmin )
459  xmin = x;
460  if ( x > xmax )
461  xmax = x;
462  }
463  for ( double y : mY )
464  {
465  if ( y < ymin )
466  ymin = y;
467  if ( y > ymax )
468  ymax = y;
469  }
470  return QgsRectangle( xmin, ymin, xmax, ymax );
471 }
472 
473 /***************************************************************************
474  * This class is considered CRITICAL and any change MUST be accompanied with
475  * full unit tests.
476  * See details in QEP #17
477  ****************************************************************************/
478 bool QgsLineString::fromWkt( const QString &wkt )
479 {
480  clear();
481 
482  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
483 
485  return false;
486  mWkbType = parts.first;
487 
488  QString secondWithoutParentheses = parts.second;
489  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
490  parts.second = parts.second.remove( '(' ).remove( ')' );
491  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
492  secondWithoutParentheses.isEmpty() )
493  return true;
494 
496  // There is a non number in the coordinates sequence
497  // LineString ( A b, 1 2)
498  if ( points.isEmpty() )
499  return false;
500 
501  setPoints( points );
502  return true;
503 }
504 
505 int QgsLineString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
506 {
507  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
508  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
509  return binarySize;
510 }
511 
512 QByteArray QgsLineString::asWkb( WkbFlags flags ) const
513 {
514  QByteArray wkbArray;
515  wkbArray.resize( QgsLineString::wkbSize( flags ) );
516  QgsWkbPtr wkb( wkbArray );
517  wkb << static_cast<char>( QgsApplication::endian() );
518  wkb << static_cast<quint32>( wkbType() );
519  QgsPointSequence pts;
520  points( pts );
521  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
522  return wkbArray;
523 }
524 
525 /***************************************************************************
526  * This class is considered CRITICAL and any change MUST be accompanied with
527  * full unit tests.
528  * See details in QEP #17
529  ****************************************************************************/
530 
531 QString QgsLineString::asWkt( int precision ) const
532 {
533  QString wkt = wktTypeStr() + ' ';
534 
535  if ( isEmpty() )
536  wkt += QLatin1String( "EMPTY" );
537  else
538  {
539  QgsPointSequence pts;
540  points( pts );
542  }
543  return wkt;
544 }
545 
546 QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
547 {
548  QgsPointSequence pts;
549  points( pts );
550 
551  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
552 
553  if ( isEmpty() )
554  return elemLineString;
555 
556  elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
557 
558  return elemLineString;
559 }
560 
561 QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
562 {
563  QgsPointSequence pts;
564  points( pts );
565 
566  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
567 
568  if ( isEmpty() )
569  return elemLineString;
570 
571  elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
572  return elemLineString;
573 }
574 
576 {
577  QgsPointSequence pts;
578  points( pts );
579  return
580  {
581  { "type", "LineString" },
582  { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
583  };
584 }
585 
586 QString QgsLineString::asKml( int precision ) const
587 {
588  QString kml;
589  if ( isRing() )
590  {
591  kml.append( QLatin1String( "<LinearRing>" ) );
592  }
593  else
594  {
595  kml.append( QLatin1String( "<LineString>" ) );
596  }
597  bool z = is3D();
598  kml.append( QLatin1String( "<altitudeMode>" ) );
599  if ( z )
600  {
601  kml.append( QLatin1String( "absolute" ) );
602  }
603  else
604  {
605  kml.append( QLatin1String( "clampToGround" ) );
606  }
607  kml.append( QLatin1String( "</altitudeMode>" ) );
608  kml.append( QLatin1String( "<coordinates>" ) );
609 
610  int nPoints = mX.size();
611  for ( int i = 0; i < nPoints; ++i )
612  {
613  if ( i > 0 )
614  {
615  kml.append( QLatin1String( " " ) );
616  }
617  kml.append( qgsDoubleToString( mX[i], precision ) );
618  kml.append( QLatin1String( "," ) );
619  kml.append( qgsDoubleToString( mY[i], precision ) );
620  if ( z )
621  {
622  kml.append( QLatin1String( "," ) );
623  kml.append( qgsDoubleToString( mZ[i], precision ) );
624  }
625  else
626  {
627  kml.append( QLatin1String( ",0" ) );
628  }
629  }
630  kml.append( QLatin1String( "</coordinates>" ) );
631  if ( isRing() )
632  {
633  kml.append( QLatin1String( "</LinearRing>" ) );
634  }
635  else
636  {
637  kml.append( QLatin1String( "</LineString>" ) );
638  }
639  return kml;
640 }
641 
642 /***************************************************************************
643  * This class is considered CRITICAL and any change MUST be accompanied with
644  * full unit tests.
645  * See details in QEP #17
646  ****************************************************************************/
647 
648 double QgsLineString::length() const
649 {
650  double length = 0;
651  int size = mX.size();
652  double dx, dy;
653  for ( int i = 1; i < size; ++i )
654  {
655  dx = mX.at( i ) - mX.at( i - 1 );
656  dy = mY.at( i ) - mY.at( i - 1 );
657  length += std::sqrt( dx * dx + dy * dy );
658  }
659  return length;
660 }
661 
663 {
664  if ( is3D() )
665  {
666  double length = 0;
667  int size = mX.size();
668  double dx, dy, dz;
669  for ( int i = 1; i < size; ++i )
670  {
671  dx = mX.at( i ) - mX.at( i - 1 );
672  dy = mY.at( i ) - mY.at( i - 1 );
673  dz = mZ.at( i ) - mZ.at( i - 1 );
674  length += std::sqrt( dx * dx + dy * dy + dz * dz );
675  }
676  return length;
677  }
678  else
679  {
680  return length();
681  }
682 }
683 
685 {
686  if ( numPoints() < 1 )
687  {
688  return QgsPoint();
689  }
690  return pointN( 0 );
691 }
692 
694 {
695  if ( numPoints() < 1 )
696  {
697  return QgsPoint();
698  }
699  return pointN( numPoints() - 1 );
700 }
701 
702 /***************************************************************************
703  * This class is considered CRITICAL and any change MUST be accompanied with
704  * full unit tests.
705  * See details in QEP #17
706  ****************************************************************************/
707 
708 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
709 {
710  Q_UNUSED( tolerance )
711  Q_UNUSED( toleranceType )
712  return clone();
713 }
714 
716 {
717  return mX.size();
718 }
719 
721 {
722  return mX.size();
723 }
724 
726 {
727  if ( i < 0 || i >= mX.size() )
728  {
729  return QgsPoint();
730  }
731 
732  double x = mX.at( i );
733  double y = mY.at( i );
734  double z = std::numeric_limits<double>::quiet_NaN();
735  double m = std::numeric_limits<double>::quiet_NaN();
736 
737  bool hasZ = is3D();
738  if ( hasZ )
739  {
740  z = mZ.at( i );
741  }
742  bool hasM = isMeasure();
743  if ( hasM )
744  {
745  m = mM.at( i );
746  }
747 
750  {
752  }
753  else if ( hasZ && hasM )
754  {
756  }
757  else if ( hasZ )
758  {
760  }
761  else if ( hasM )
762  {
764  }
765  return QgsPoint( t, x, y, z, m );
766 }
767 
768 /***************************************************************************
769  * This class is considered CRITICAL and any change MUST be accompanied with
770  * full unit tests.
771  * See details in QEP #17
772  ****************************************************************************/
773 
774 double QgsLineString::xAt( int index ) const
775 {
776  if ( index >= 0 && index < mX.size() )
777  return mX.at( index );
778  else
779  return 0.0;
780 }
781 
782 double QgsLineString::yAt( int index ) const
783 {
784  if ( index >= 0 && index < mY.size() )
785  return mY.at( index );
786  else
787  return 0.0;
788 }
789 
790 void QgsLineString::setXAt( int index, double x )
791 {
792  if ( index >= 0 && index < mX.size() )
793  mX[ index ] = x;
794  clearCache();
795 }
796 
797 void QgsLineString::setYAt( int index, double y )
798 {
799  if ( index >= 0 && index < mY.size() )
800  mY[ index ] = y;
801  clearCache();
802 }
803 
804 /***************************************************************************
805  * This class is considered CRITICAL and any change MUST be accompanied with
806  * full unit tests.
807  * See details in QEP #17
808  ****************************************************************************/
809 
811 {
812  pts.clear();
813  int nPoints = numPoints();
814  pts.reserve( nPoints );
815  for ( int i = 0; i < nPoints; ++i )
816  {
817  pts.push_back( pointN( i ) );
818  }
819 }
820 
822 {
823  clearCache(); //set bounding box invalid
824 
825  if ( points.isEmpty() )
826  {
827  clear();
828  return;
829  }
830 
831  //get wkb type from first point
832  const QgsPoint &firstPt = points.at( 0 );
833  bool hasZ = firstPt.is3D();
834  bool hasM = firstPt.isMeasure();
835 
837 
838  mX.resize( points.size() );
839  mY.resize( points.size() );
840  if ( hasZ )
841  {
842  mZ.resize( points.size() );
843  }
844  else
845  {
846  mZ.clear();
847  }
848  if ( hasM )
849  {
850  mM.resize( points.size() );
851  }
852  else
853  {
854  mM.clear();
855  }
856 
857  for ( int i = 0; i < points.size(); ++i )
858  {
859  mX[i] = points.at( i ).x();
860  mY[i] = points.at( i ).y();
861  if ( hasZ )
862  {
863  double z = points.at( i ).z();
864  mZ[i] = std::isnan( z ) ? 0 : z;
865  }
866  if ( hasM )
867  {
868  double m = points.at( i ).m();
869  mM[i] = std::isnan( m ) ? 0 : m;
870  }
871  }
872 }
873 
874 /***************************************************************************
875  * This class is considered CRITICAL and any change MUST be accompanied with
876  * full unit tests.
877  * See details in QEP #17
878  ****************************************************************************/
879 
881 {
882  if ( !line )
883  {
884  return;
885  }
886 
887  if ( numPoints() < 1 )
888  {
890  }
891 
892  // do not store duplicit points
893  if ( numPoints() > 0 &&
894  line->numPoints() > 0 &&
895  endPoint() == line->startPoint() )
896  {
897  mX.pop_back();
898  mY.pop_back();
899 
900  if ( is3D() )
901  {
902  mZ.pop_back();
903  }
904  if ( isMeasure() )
905  {
906  mM.pop_back();
907  }
908  }
909 
910  mX += line->mX;
911  mY += line->mY;
912 
913  if ( is3D() )
914  {
915  if ( line->is3D() )
916  {
917  mZ += line->mZ;
918  }
919  else
920  {
921  // if append line does not have z coordinates, fill with NaN to match number of points in final line
922  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
923  }
924  }
925 
926  if ( isMeasure() )
927  {
928  if ( line->isMeasure() )
929  {
930  mM += line->mM;
931  }
932  else
933  {
934  // if append line does not have m values, fill with NaN to match number of points in final line
935  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
936  }
937  }
938 
939  clearCache(); //set bounding box invalid
940 }
941 
943 {
944  QgsLineString *copy = clone();
945  std::reverse( copy->mX.begin(), copy->mX.end() );
946  std::reverse( copy->mY.begin(), copy->mY.end() );
947  if ( copy->is3D() )
948  {
949  std::reverse( copy->mZ.begin(), copy->mZ.end() );
950  }
951  if ( copy->isMeasure() )
952  {
953  std::reverse( copy->mM.begin(), copy->mM.end() );
954  }
955  return copy;
956 }
957 
958 void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
959 {
960  if ( distance < 0 )
961  return;
962 
963  double distanceTraversed = 0;
964  const int totalPoints = numPoints();
965  if ( totalPoints == 0 )
966  return;
967 
968  const double *x = mX.constData();
969  const double *y = mY.constData();
970  const double *z = is3D() ? mZ.constData() : nullptr;
971  const double *m = isMeasure() ? mM.constData() : nullptr;
972 
973  double prevX = *x++;
974  double prevY = *y++;
975  double prevZ = z ? *z++ : 0.0;
976  double prevM = m ? *m++ : 0.0;
977 
978  if ( qgsDoubleNear( distance, 0.0 ) )
979  {
980  visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
981  return;
982  }
983 
984  double pZ = std::numeric_limits<double>::quiet_NaN();
985  double pM = std::numeric_limits<double>::quiet_NaN();
986  double nextPointDistance = distance;
987  for ( int i = 1; i < totalPoints; ++i )
988  {
989  double thisX = *x++;
990  double thisY = *y++;
991  double thisZ = z ? *z++ : 0.0;
992  double thisM = m ? *m++ : 0.0;
993 
994  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
995  while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
996  {
997  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
998  const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
999  double pX, pY;
1000  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
1001  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
1002  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
1003 
1004  if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1005  return;
1006 
1007  nextPointDistance += distance;
1008  }
1009 
1010  distanceTraversed += segmentLength;
1011  prevX = thisX;
1012  prevY = thisY;
1013  prevZ = thisZ;
1014  prevM = thisM;
1015  }
1016 }
1017 
1018 QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
1019 {
1020  if ( distance < 0 )
1021  return nullptr;
1022 
1024  if ( is3D() )
1025  pointType = QgsWkbTypes::PointZ;
1026  if ( isMeasure() )
1027  pointType = QgsWkbTypes::addM( pointType );
1028 
1029  std::unique_ptr< QgsPoint > res;
1030  visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
1031  {
1032  res = qgis::make_unique< QgsPoint >( pointType, x, y, z, m );
1033  return false;
1034  } );
1035  return res.release();
1036 }
1037 
1038 QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
1039 {
1040  if ( startDistance < 0 && endDistance < 0 )
1041  return createEmptyWithSameType();
1042 
1043  endDistance = std::max( startDistance, endDistance );
1044 
1045  const int totalPoints = numPoints();
1046  if ( totalPoints == 0 )
1047  return clone();
1048 
1049  QVector< QgsPoint > substringPoints;
1050  substringPoints.reserve( totalPoints );
1051 
1053  if ( is3D() )
1054  pointType = QgsWkbTypes::PointZ;
1055  if ( isMeasure() )
1056  pointType = QgsWkbTypes::addM( pointType );
1057 
1058  const double *x = mX.constData();
1059  const double *y = mY.constData();
1060  const double *z = is3D() ? mZ.constData() : nullptr;
1061  const double *m = isMeasure() ? mM.constData() : nullptr;
1062 
1063  double distanceTraversed = 0;
1064  double prevX = *x++;
1065  double prevY = *y++;
1066  double prevZ = z ? *z++ : 0.0;
1067  double prevM = m ? *m++ : 0.0;
1068  bool foundStart = false;
1069 
1070  if ( startDistance < 0 )
1071  startDistance = 0;
1072 
1073  for ( int i = 1; i < totalPoints; ++i )
1074  {
1075  double thisX = *x++;
1076  double thisY = *y++;
1077  double thisZ = z ? *z++ : 0.0;
1078  double thisM = m ? *m++ : 0.0;
1079 
1080  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
1081 
1082  if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1083  {
1084  // start point falls on this segment
1085  const double distanceToStart = startDistance - distanceTraversed;
1086  double startX, startY;
1087  double startZ = 0;
1088  double startM = 0;
1089  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1090  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1091  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1092  substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1093  foundStart = true;
1094  }
1095  if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1096  {
1097  // end point falls on this segment
1098  const double distanceToEnd = endDistance - distanceTraversed;
1099  double endX, endY;
1100  double endZ = 0;
1101  double endM = 0;
1102  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1103  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1104  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1105  substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1106  }
1107  else if ( foundStart )
1108  {
1109  substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1110  }
1111 
1112  prevX = thisX;
1113  prevY = thisY;
1114  prevZ = thisZ;
1115  prevM = thisM;
1116  distanceTraversed += segmentLength;
1117  if ( distanceTraversed >= endDistance )
1118  break;
1119  }
1120 
1121  // start point is the last node
1122  if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1123  {
1124  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1125  << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1126  }
1127 
1128  return new QgsLineString( substringPoints );
1129 }
1130 
1131 /***************************************************************************
1132  * This class is considered CRITICAL and any change MUST be accompanied with
1133  * full unit tests.
1134  * See details in QEP #17
1135  ****************************************************************************/
1136 
1137 void QgsLineString::draw( QPainter &p ) const
1138 {
1139  p.drawPolyline( asQPolygonF() );
1140 }
1141 
1142 void QgsLineString::addToPainterPath( QPainterPath &path ) const
1143 {
1144  int nPoints = numPoints();
1145  if ( nPoints < 1 )
1146  {
1147  return;
1148  }
1149 
1150  if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1151  {
1152  path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1153  }
1154 
1155  for ( int i = 1; i < nPoints; ++i )
1156  {
1157  path.lineTo( mX.at( i ), mY.at( i ) );
1158  }
1159 }
1160 
1161 void QgsLineString::drawAsPolygon( QPainter &p ) const
1162 {
1163  p.drawPolygon( asQPolygonF() );
1164 }
1165 
1167 {
1168  QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1169  compoundCurve->addCurve( clone() );
1170  return compoundCurve;
1171 }
1172 
1173 void QgsLineString::extend( double startDistance, double endDistance )
1174 {
1175  if ( mX.size() < 2 || mY.size() < 2 )
1176  return;
1177 
1178  // start of line
1179  if ( startDistance > 0 )
1180  {
1181  double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1182  std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1183  double newLen = currentLen + startDistance;
1184  mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1185  mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1186  }
1187  // end of line
1188  if ( endDistance > 0 )
1189  {
1190  int last = mX.size() - 1;
1191  double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1192  std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1193  double newLen = currentLen + endDistance;
1194  mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1195  mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1196  }
1197 }
1198 
1200 {
1201  auto result = qgis::make_unique< QgsLineString >();
1202  result->mWkbType = mWkbType;
1203  return result.release();
1204 }
1205 
1207 {
1208  return QStringLiteral( "LineString" );
1209 }
1210 
1212 {
1213  return 1;
1214 }
1215 
1216 /***************************************************************************
1217  * This class is considered CRITICAL and any change MUST be accompanied with
1218  * full unit tests.
1219  * See details in QEP #17
1220  ****************************************************************************/
1221 
1223 {
1224  double *zArray = nullptr;
1225  bool hasZ = is3D();
1226  int nPoints = numPoints();
1227 
1228  // it's possible that transformCoords will throw an exception - so we need to use
1229  // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1230  std::unique_ptr< double[] > dummyZ;
1231  if ( !hasZ || !transformZ )
1232  {
1233  dummyZ.reset( new double[nPoints]() );
1234  zArray = dummyZ.get();
1235  }
1236  else
1237  {
1238  zArray = mZ.data();
1239  }
1240  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1241  clearCache();
1242 }
1243 
1244 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1245 {
1246  int nPoints = numPoints();
1247  bool hasZ = is3D();
1248  bool hasM = isMeasure();
1249  double *x = mX.data();
1250  double *y = mY.data();
1251  double *z = hasZ ? mZ.data() : nullptr;
1252  double *m = hasM ? mM.data() : nullptr;
1253  for ( int i = 0; i < nPoints; ++i )
1254  {
1255  double xOut, yOut;
1256  t.map( *x, *y, &xOut, &yOut );
1257  *x++ = xOut;
1258  *y++ = yOut;
1259  if ( hasZ )
1260  {
1261  *z = *z * zScale + zTranslate;
1262  z++;
1263  }
1264  if ( hasM )
1265  {
1266  *m = *m * mScale + mTranslate;
1267  m++;
1268  }
1269  }
1270  clearCache();
1271 }
1272 
1273 /***************************************************************************
1274  * This class is considered CRITICAL and any change MUST be accompanied with
1275  * full unit tests.
1276  * See details in QEP #17
1277  ****************************************************************************/
1278 
1279 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1280 {
1281  if ( position.vertex < 0 || position.vertex > mX.size() )
1282  {
1283  return false;
1284  }
1285 
1286  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1287  {
1289  }
1290 
1291  mX.insert( position.vertex, vertex.x() );
1292  mY.insert( position.vertex, vertex.y() );
1293  if ( is3D() )
1294  {
1295  mZ.insert( position.vertex, vertex.z() );
1296  }
1297  if ( isMeasure() )
1298  {
1299  mM.insert( position.vertex, vertex.m() );
1300  }
1301  clearCache(); //set bounding box invalid
1302  return true;
1303 }
1304 
1305 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1306 {
1307  if ( position.vertex < 0 || position.vertex >= mX.size() )
1308  {
1309  return false;
1310  }
1311  mX[position.vertex] = newPos.x();
1312  mY[position.vertex] = newPos.y();
1313  if ( is3D() && newPos.is3D() )
1314  {
1315  mZ[position.vertex] = newPos.z();
1316  }
1317  if ( isMeasure() && newPos.isMeasure() )
1318  {
1319  mM[position.vertex] = newPos.m();
1320  }
1321  clearCache(); //set bounding box invalid
1322  return true;
1323 }
1324 
1326 {
1327  if ( position.vertex >= mX.size() || position.vertex < 0 )
1328  {
1329  return false;
1330  }
1331 
1332  mX.remove( position.vertex );
1333  mY.remove( position.vertex );
1334  if ( is3D() )
1335  {
1336  mZ.remove( position.vertex );
1337  }
1338  if ( isMeasure() )
1339  {
1340  mM.remove( position.vertex );
1341  }
1342 
1343  if ( numPoints() == 1 )
1344  {
1345  clear();
1346  }
1347 
1348  clearCache(); //set bounding box invalid
1349  return true;
1350 }
1351 
1352 /***************************************************************************
1353  * This class is considered CRITICAL and any change MUST be accompanied with
1354  * full unit tests.
1355  * See details in QEP #17
1356  ****************************************************************************/
1357 
1359 {
1360  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1361  {
1363  }
1364 
1365  mX.append( pt.x() );
1366  mY.append( pt.y() );
1367  if ( is3D() )
1368  {
1369  mZ.append( pt.z() );
1370  }
1371  if ( isMeasure() )
1372  {
1373  mM.append( pt.m() );
1374  }
1375  clearCache(); //set bounding box invalid
1376 }
1377 
1378 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1379 {
1380  double sqrDist = std::numeric_limits<double>::max();
1381  double leftOfDist = std::numeric_limits<double>::max();
1382  int prevLeftOf = 0;
1383  double prevLeftOfX = 0.0;
1384  double prevLeftOfY = 0.0;
1385  double testDist = 0;
1386  double segmentPtX, segmentPtY;
1387 
1388  if ( leftOf )
1389  *leftOf = 0;
1390 
1391  int size = mX.size();
1392  if ( size == 0 || size == 1 )
1393  {
1394  vertexAfter = QgsVertexId( 0, 0, 0 );
1395  return -1;
1396  }
1397  for ( int i = 1; i < size; ++i )
1398  {
1399  double prevX = mX.at( i - 1 );
1400  double prevY = mY.at( i - 1 );
1401  double currentX = mX.at( i );
1402  double currentY = mY.at( i );
1403  testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1404  if ( testDist < sqrDist )
1405  {
1406  sqrDist = testDist;
1407  segmentPt.setX( segmentPtX );
1408  segmentPt.setY( segmentPtY );
1409  vertexAfter.part = 0;
1410  vertexAfter.ring = 0;
1411  vertexAfter.vertex = i;
1412  }
1413  if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1414  {
1415  int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1416  // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1417  // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1418  // where we can perform the check
1419  if ( left != 0 )
1420  {
1421  if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1422  {
1423  // we have two possible segments each with equal distance to point, but they disagree
1424  // on whether or not the point is to the left of them.
1425  // so we test the segments themselves and flip the result.
1426  // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1427  *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1428  }
1429  else
1430  {
1431  *leftOf = left;
1432  }
1433  prevLeftOf = *leftOf;
1434  leftOfDist = testDist;
1435  prevLeftOfX = prevX;
1436  prevLeftOfY = prevY;
1437  }
1438  else if ( testDist < leftOfDist )
1439  {
1440  *leftOf = left;
1441  leftOfDist = testDist;
1442  prevLeftOf = 0;
1443  }
1444  }
1445  }
1446  return sqrDist;
1447 }
1448 
1449 /***************************************************************************
1450  * This class is considered CRITICAL and any change MUST be accompanied with
1451  * full unit tests.
1452  * See details in QEP #17
1453  ****************************************************************************/
1454 
1455 bool QgsLineString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1456 {
1457  if ( node < 0 || node >= numPoints() )
1458  {
1459  return false;
1460  }
1461  point = pointN( node );
1463  return true;
1464 }
1465 
1467 {
1468  if ( mX.isEmpty() )
1469  return QgsPoint();
1470 
1471  int numPoints = mX.count();
1472  if ( numPoints == 1 )
1473  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1474 
1475  double totalLineLength = 0.0;
1476  double prevX = mX.at( 0 );
1477  double prevY = mY.at( 0 );
1478  double sumX = 0.0;
1479  double sumY = 0.0;
1480 
1481  for ( int i = 1; i < numPoints ; ++i )
1482  {
1483  double currentX = mX.at( i );
1484  double currentY = mY.at( i );
1485  double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1486  std::pow( currentY - prevY, 2.0 ) );
1487  if ( qgsDoubleNear( segmentLength, 0.0 ) )
1488  continue;
1489 
1490  totalLineLength += segmentLength;
1491  sumX += segmentLength * 0.5 * ( currentX + prevX );
1492  sumY += segmentLength * 0.5 * ( currentY + prevY );
1493  prevX = currentX;
1494  prevY = currentY;
1495  }
1496 
1497  if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1498  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1499  else
1500  return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1501 
1502 }
1503 
1504 /***************************************************************************
1505  * This class is considered CRITICAL and any change MUST be accompanied with
1506  * full unit tests.
1507  * See details in QEP #17
1508  ****************************************************************************/
1509 
1510 void QgsLineString::sumUpArea( double &sum ) const
1511 {
1512  int maxIndex = numPoints() - 1;
1513 
1514  for ( int i = 0; i < maxIndex; ++i )
1515  {
1516  sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1517  }
1518 }
1519 
1520 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1521 {
1522  bool hasZ = is3D();
1523  bool hasM = isMeasure();
1524  int nVertices = 0;
1525  wkb >> nVertices;
1526  mX.resize( nVertices );
1527  mY.resize( nVertices );
1528  hasZ ? mZ.resize( nVertices ) : mZ.clear();
1529  hasM ? mM.resize( nVertices ) : mM.clear();
1530  double *x = mX.data();
1531  double *y = mY.data();
1532  double *m = hasM ? mM.data() : nullptr;
1533  double *z = hasZ ? mZ.data() : nullptr;
1534  for ( int i = 0; i < nVertices; ++i )
1535  {
1536  wkb >> *x++;
1537  wkb >> *y++;
1538  if ( hasZ )
1539  {
1540  wkb >> *z++;
1541  }
1542  if ( hasM )
1543  {
1544  wkb >> *m++;
1545  }
1546  }
1547  clearCache(); //set bounding box invalid
1548 }
1549 
1550 /***************************************************************************
1551  * This class is considered CRITICAL and any change MUST be accompanied with
1552  * full unit tests.
1553  * See details in QEP #17
1554  ****************************************************************************/
1555 
1557 {
1558  if ( numPoints() < 1 || isClosed() )
1559  {
1560  return;
1561  }
1562  addVertex( startPoint() );
1563 }
1564 
1566 {
1567  if ( mX.count() < 2 )
1568  {
1569  //undefined
1570  return 0.0;
1571  }
1572 
1573  if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1574  {
1575  if ( isClosed() )
1576  {
1577  double previousX = mX.at( numPoints() - 2 );
1578  double previousY = mY.at( numPoints() - 2 );
1579  double currentX = mX.at( 0 );
1580  double currentY = mY.at( 0 );
1581  double afterX = mX.at( 1 );
1582  double afterY = mY.at( 1 );
1583  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1584  }
1585  else if ( vertex.vertex == 0 )
1586  {
1587  return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1588  }
1589  else
1590  {
1591  int a = numPoints() - 2;
1592  int b = numPoints() - 1;
1593  return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1594  }
1595  }
1596  else
1597  {
1598  double previousX = mX.at( vertex.vertex - 1 );
1599  double previousY = mY.at( vertex.vertex - 1 );
1600  double currentX = mX.at( vertex.vertex );
1601  double currentY = mY.at( vertex.vertex );
1602  double afterX = mX.at( vertex.vertex + 1 );
1603  double afterY = mY.at( vertex.vertex + 1 );
1604  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1605  }
1606 }
1607 
1608 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1609 {
1610  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1611  return 0.0;
1612 
1613  double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1614  double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1615  return std::sqrt( dx * dx + dy * dy );
1616 }
1617 
1618 /***************************************************************************
1619  * This class is considered CRITICAL and any change MUST be accompanied with
1620  * full unit tests.
1621  * See details in QEP #17
1622  ****************************************************************************/
1623 
1624 bool QgsLineString::addZValue( double zValue )
1625 {
1626  if ( QgsWkbTypes::hasZ( mWkbType ) )
1627  return false;
1628 
1629  clearCache();
1630  if ( mWkbType == QgsWkbTypes::Unknown )
1631  {
1633  return true;
1634  }
1635 
1637 
1638  mZ.clear();
1639  int nPoints = numPoints();
1640  mZ.reserve( nPoints );
1641  for ( int i = 0; i < nPoints; ++i )
1642  {
1643  mZ << zValue;
1644  }
1645  return true;
1646 }
1647 
1648 bool QgsLineString::addMValue( double mValue )
1649 {
1650  if ( QgsWkbTypes::hasM( mWkbType ) )
1651  return false;
1652 
1653  clearCache();
1654  if ( mWkbType == QgsWkbTypes::Unknown )
1655  {
1657  return true;
1658  }
1659 
1661  {
1663  }
1664  else
1665  {
1667  }
1668 
1669  mM.clear();
1670  int nPoints = numPoints();
1671  mM.reserve( nPoints );
1672  for ( int i = 0; i < nPoints; ++i )
1673  {
1674  mM << mValue;
1675  }
1676  return true;
1677 }
1678 
1680 {
1681  if ( !is3D() )
1682  return false;
1683 
1684  clearCache();
1686  mZ.clear();
1687  return true;
1688 }
1689 
1691 {
1692  if ( !isMeasure() )
1693  return false;
1694 
1695  clearCache();
1697  mM.clear();
1698  return true;
1699 }
1700 
1702 {
1703  std::swap( mX, mY );
1704  clearCache();
1705 }
1706 
1708 {
1709  if ( type == mWkbType )
1710  return true;
1711 
1712  clearCache();
1713  if ( type == QgsWkbTypes::LineString25D )
1714  {
1715  //special handling required for conversion to LineString25D
1716  dropMValue();
1717  addZValue( std::numeric_limits<double>::quiet_NaN() );
1719  return true;
1720  }
1721  else
1722  {
1723  return QgsCurve::convertTo( type );
1724  }
1725 }
1726 
1728 {
1729  if ( !transformer )
1730  return false;
1731 
1732  bool hasZ = is3D();
1733  bool hasM = isMeasure();
1734  int size = mX.size();
1735 
1736  double *srcX = mX.data();
1737  double *srcY = mY.data();
1738  double *srcM = hasM ? mM.data() : nullptr;
1739  double *srcZ = hasZ ? mZ.data() : nullptr;
1740 
1741  bool res = true;
1742  for ( int i = 0; i < size; ++i )
1743  {
1744  double x = *srcX;
1745  double y = *srcY;
1746  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
1747  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
1748  if ( !transformer->transformPoint( x, y, z, m ) )
1749  {
1750  res = false;
1751  break;
1752  }
1753 
1754  *srcX++ = x;
1755  *srcY++ = y;
1756  if ( hasM )
1757  *srcM++ = m;
1758  if ( hasZ )
1759  *srcZ++ = z;
1760 
1761  if ( feedback && feedback->isCanceled() )
1762  {
1763  res = false;
1764  break;
1765  }
1766  }
1767  clearCache();
1768  return res;
1769 }
1770 
1771 void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1772 {
1773  bool hasZ = is3D();
1774  bool hasM = isMeasure();
1775  int size = mX.size();
1776 
1777  double *srcX = mX.data();
1778  double *srcY = mY.data();
1779  double *srcM = hasM ? mM.data() : nullptr;
1780  double *srcZ = hasZ ? mZ.data() : nullptr;
1781 
1782  double *destX = srcX;
1783  double *destY = srcY;
1784  double *destM = srcM;
1785  double *destZ = srcZ;
1786 
1787  int filteredPoints = 0;
1788  for ( int i = 0; i < size; ++i )
1789  {
1790  double x = *srcX++;
1791  double y = *srcY++;
1792  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
1793  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
1794 
1795  if ( filter( QgsPoint( x, y, z, m ) ) )
1796  {
1797  filteredPoints++;
1798  *destX++ = x;
1799  *destY++ = y;
1800  if ( hasM )
1801  *destM++ = m;
1802  if ( hasZ )
1803  *destZ++ = z;
1804  }
1805  }
1806 
1807  mX.resize( filteredPoints );
1808  mY.resize( filteredPoints );
1809  if ( hasZ )
1810  mZ.resize( filteredPoints );
1811  if ( hasM )
1812  mM.resize( filteredPoints );
1813 
1814  clearCache();
1815 }
1816 
1817 void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1818 {
1819  bool hasZ = is3D();
1820  bool hasM = isMeasure();
1821  int size = mX.size();
1822 
1823  double *srcX = mX.data();
1824  double *srcY = mY.data();
1825  double *srcM = hasM ? mM.data() : nullptr;
1826  double *srcZ = hasZ ? mZ.data() : nullptr;
1827 
1828  for ( int i = 0; i < size; ++i )
1829  {
1830  double x = *srcX;
1831  double y = *srcY;
1832  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
1833  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
1834  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
1835  *srcX++ = res.x();
1836  *srcY++ = res.y();
1837  if ( hasM )
1838  *srcM++ = res.m();
1839  if ( hasZ )
1840  *srcZ++ = res.z();
1841  }
1842  clearCache();
1843 }
An abstract base class for classes which transform geometries by transforming input points to output ...
virtual bool transformPoint(double &x, double &y, double &z, double &m)=0
Transforms the point defined by the coordinates (x, y, z) and the specified m value.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
virtual bool convertTo(QgsWkbTypes::Type type)
Converts the geometry to a specified type.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
QgsWkbTypes::Type mWkbType
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static endian_t endian()
Returns whether this machine uses big or little endian.
Compound curve geometry type.
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
A const WKB pointer.
Definition: qgswkbptr.h:130
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
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.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:257
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
virtual bool isRing() const SIP_HOLDGIL
Returns true if the curve is a ring.
Definition: qgscurve.cpp:56
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:280
bool isValid(QString &error, int flags=0) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgscurve.cpp:211
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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 json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double lineAngle(double x1, double y1, double x2, double y2) SIP_HOLDGIL
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon) SIP_HOLDGIL
Returns the squared distance between a point and a line.
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2) SIP_HOLDGIL
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Represents a single 2D line segment, consisting of a 2D start and end vertex only.
double endX() const SIP_HOLDGIL
Returns the segment's end x-coordinate.
double endY() const SIP_HOLDGIL
Returns the segment's end y-coordinate.
double startX() const SIP_HOLDGIL
Returns the segment's start x-coordinate.
double startY() const SIP_HOLDGIL
Returns the segment's start y-coordinate.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
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 swapXy() override
Swaps the x and y coordinates from the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
const double * yData() const
Returns a const pointer to the y vertex data.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
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.
static QgsLineString * fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
QgsPoint centroid() const override
Returns the centroid of the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
QPolygonF asQPolygonF() const override
Returns a QPolygonF representing the points.
bool convertTo(QgsWkbTypes::Type type) override
Converts the geometry to a specified type.
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 setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
void setYAt(int index, double y)
Sets the y-coordinate of the specified node in the line string.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
double length3D() const SIP_HOLDGIL
Returns the length in 3D world of the line string.
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
void extend(double startDistance, double endDistance)
Extends the line geometry by extrapolating out the start or end of the line by a specified distance.
QgsCompoundCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type QgsCompoundCurve.
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
QgsLineString() SIP_HOLDGIL
Constructor for an empty linestring geometry.
void setXAt(int index, double x)
Sets the x-coordinate of the specified node in the line string.
int nCoordinates() const override SIP_HOLDGIL
Returns the number of nodes contained in the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
QVector< QgsVertexId > collectDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) const
Returns a list of any duplicate nodes contained in the geometry, within the specified tolerance.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
bool dropMValue() override
Drops any measure values which exist in the geometry.
bool isValid(QString &error, int flags=0) const override
Checks validity of the geometry, and returns true if the geometry is valid.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
QgsLineString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
const double * xData() const
Returns a const pointer to the x vertex data.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
A class to represent a 2D point.
Definition: qgspointxy.h:44
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
Definition: qgspoint.h:269
Q_GADGET double x
Definition: qgspoint.h:41
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
Definition: qgspoint.h:280
double z
Definition: qgspoint.h:43
double m
Definition: qgspoint.h:44
double y
Definition: qgspoint.h:42
A rectangle specified with double values.
Definition: qgsrectangle.h:42
WKB pointer handler.
Definition: qgswkbptr.h:44
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type zmType(Type type, bool hasZ, bool hasM) SIP_HOLDGIL
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:801
static Type dropZ(Type type) SIP_HOLDGIL
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1207
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
static Type dropM(Type type) SIP_HOLDGIL
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1225
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:292
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
QVector< QgsPoint > QgsPointSequence
int precision
Utility class for identifying a unique vertex within a geometry.
int vertex
Vertex number.
int part
Part number.
int ring
Ring number.
VertexType
Type of vertex.
@ SegmentVertex
The actual start or end point of a segment.