QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 /***************************************************************************
522  * This class is considered CRITICAL and any change MUST be accompanied with
523  * full unit tests.
524  * See details in QEP #17
525  ****************************************************************************/
526 
527 double QgsLineString::length() const
528 {
529  double length = 0;
530  int size = mX.size();
531  double dx, dy;
532  for ( int i = 1; i < size; ++i )
533  {
534  dx = mX.at( i ) - mX.at( i - 1 );
535  dy = mY.at( i ) - mY.at( i - 1 );
536  length += std::sqrt( dx * dx + dy * dy );
537  }
538  return length;
539 }
540 
542 {
543  if ( is3D() )
544  {
545  double length = 0;
546  int size = mX.size();
547  double dx, dy, dz;
548  for ( int i = 1; i < size; ++i )
549  {
550  dx = mX.at( i ) - mX.at( i - 1 );
551  dy = mY.at( i ) - mY.at( i - 1 );
552  dz = mZ.at( i ) - mZ.at( i - 1 );
553  length += std::sqrt( dx * dx + dy * dy + dz * dz );
554  }
555  return length;
556  }
557  else
558  {
559  return length();
560  }
561 }
562 
564 {
565  if ( numPoints() < 1 )
566  {
567  return QgsPoint();
568  }
569  return pointN( 0 );
570 }
571 
573 {
574  if ( numPoints() < 1 )
575  {
576  return QgsPoint();
577  }
578  return pointN( numPoints() - 1 );
579 }
580 
581 /***************************************************************************
582  * This class is considered CRITICAL and any change MUST be accompanied with
583  * full unit tests.
584  * See details in QEP #17
585  ****************************************************************************/
586 
587 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
588 {
589  Q_UNUSED( tolerance )
590  Q_UNUSED( toleranceType )
591  return clone();
592 }
593 
595 {
596  return mX.size();
597 }
598 
600 {
601  return mX.size();
602 }
603 
605 {
606  if ( i < 0 || i >= mX.size() )
607  {
608  return QgsPoint();
609  }
610 
611  double x = mX.at( i );
612  double y = mY.at( i );
613  double z = std::numeric_limits<double>::quiet_NaN();
614  double m = std::numeric_limits<double>::quiet_NaN();
615 
616  bool hasZ = is3D();
617  if ( hasZ )
618  {
619  z = mZ.at( i );
620  }
621  bool hasM = isMeasure();
622  if ( hasM )
623  {
624  m = mM.at( i );
625  }
626 
629  {
631  }
632  else if ( hasZ && hasM )
633  {
635  }
636  else if ( hasZ )
637  {
639  }
640  else if ( hasM )
641  {
643  }
644  return QgsPoint( t, x, y, z, m );
645 }
646 
647 /***************************************************************************
648  * This class is considered CRITICAL and any change MUST be accompanied with
649  * full unit tests.
650  * See details in QEP #17
651  ****************************************************************************/
652 
653 double QgsLineString::xAt( int index ) const
654 {
655  if ( index >= 0 && index < mX.size() )
656  return mX.at( index );
657  else
658  return 0.0;
659 }
660 
661 double QgsLineString::yAt( int index ) const
662 {
663  if ( index >= 0 && index < mY.size() )
664  return mY.at( index );
665  else
666  return 0.0;
667 }
668 
669 void QgsLineString::setXAt( int index, double x )
670 {
671  if ( index >= 0 && index < mX.size() )
672  mX[ index ] = x;
673  clearCache();
674 }
675 
676 void QgsLineString::setYAt( int index, double y )
677 {
678  if ( index >= 0 && index < mY.size() )
679  mY[ index ] = y;
680  clearCache();
681 }
682 
683 /***************************************************************************
684  * This class is considered CRITICAL and any change MUST be accompanied with
685  * full unit tests.
686  * See details in QEP #17
687  ****************************************************************************/
688 
690 {
691  pts.clear();
692  int nPoints = numPoints();
693  pts.reserve( nPoints );
694  for ( int i = 0; i < nPoints; ++i )
695  {
696  pts.push_back( pointN( i ) );
697  }
698 }
699 
701 {
702  clearCache(); //set bounding box invalid
703 
704  if ( points.isEmpty() )
705  {
706  clear();
707  return;
708  }
709 
710  //get wkb type from first point
711  const QgsPoint &firstPt = points.at( 0 );
712  bool hasZ = firstPt.is3D();
713  bool hasM = firstPt.isMeasure();
714 
716 
717  mX.resize( points.size() );
718  mY.resize( points.size() );
719  if ( hasZ )
720  {
721  mZ.resize( points.size() );
722  }
723  else
724  {
725  mZ.clear();
726  }
727  if ( hasM )
728  {
729  mM.resize( points.size() );
730  }
731  else
732  {
733  mM.clear();
734  }
735 
736  for ( int i = 0; i < points.size(); ++i )
737  {
738  mX[i] = points.at( i ).x();
739  mY[i] = points.at( i ).y();
740  if ( hasZ )
741  {
742  double z = points.at( i ).z();
743  mZ[i] = std::isnan( z ) ? 0 : z;
744  }
745  if ( hasM )
746  {
747  double m = points.at( i ).m();
748  mM[i] = std::isnan( m ) ? 0 : m;
749  }
750  }
751 }
752 
753 /***************************************************************************
754  * This class is considered CRITICAL and any change MUST be accompanied with
755  * full unit tests.
756  * See details in QEP #17
757  ****************************************************************************/
758 
760 {
761  if ( !line )
762  {
763  return;
764  }
765 
766  if ( numPoints() < 1 )
767  {
769  }
770 
771  // do not store duplicit points
772  if ( numPoints() > 0 &&
773  line->numPoints() > 0 &&
774  endPoint() == line->startPoint() )
775  {
776  mX.pop_back();
777  mY.pop_back();
778 
779  if ( is3D() )
780  {
781  mZ.pop_back();
782  }
783  if ( isMeasure() )
784  {
785  mM.pop_back();
786  }
787  }
788 
789  mX += line->mX;
790  mY += line->mY;
791 
792  if ( is3D() )
793  {
794  if ( line->is3D() )
795  {
796  mZ += line->mZ;
797  }
798  else
799  {
800  // if append line does not have z coordinates, fill with NaN to match number of points in final line
801  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
802  }
803  }
804 
805  if ( isMeasure() )
806  {
807  if ( line->isMeasure() )
808  {
809  mM += line->mM;
810  }
811  else
812  {
813  // if append line does not have m values, fill with NaN to match number of points in final line
814  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
815  }
816  }
817 
818  clearCache(); //set bounding box invalid
819 }
820 
822 {
823  QgsLineString *copy = clone();
824  std::reverse( copy->mX.begin(), copy->mX.end() );
825  std::reverse( copy->mY.begin(), copy->mY.end() );
826  if ( copy->is3D() )
827  {
828  std::reverse( copy->mZ.begin(), copy->mZ.end() );
829  }
830  if ( copy->isMeasure() )
831  {
832  std::reverse( copy->mM.begin(), copy->mM.end() );
833  }
834  return copy;
835 }
836 
837 QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
838 {
839  if ( distance < 0 )
840  return nullptr;
841 
842  double distanceTraversed = 0;
843  const int totalPoints = numPoints();
844  if ( totalPoints == 0 )
845  return nullptr;
846 
847  const double *x = mX.constData();
848  const double *y = mY.constData();
849  const double *z = is3D() ? mZ.constData() : nullptr;
850  const double *m = isMeasure() ? mM.constData() : nullptr;
851 
853  if ( is3D() )
854  pointType = QgsWkbTypes::PointZ;
855  if ( isMeasure() )
856  pointType = QgsWkbTypes::addM( pointType );
857 
858  double prevX = *x++;
859  double prevY = *y++;
860  double prevZ = z ? *z++ : 0.0;
861  double prevM = m ? *m++ : 0.0;
862 
863  if ( qgsDoubleNear( distance, 0.0 ) )
864  {
865  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
866  }
867 
868  for ( int i = 1; i < totalPoints; ++i )
869  {
870  double thisX = *x++;
871  double thisY = *y++;
872  double thisZ = z ? *z++ : 0.0;
873  double thisM = m ? *m++ : 0.0;
874 
875  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
876  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
877  {
878  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
879  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
880  double pX, pY;
881  double pZ = 0;
882  double pM = 0;
883  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
884  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
885  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
886  return new QgsPoint( pointType, pX, pY, pZ, pM );
887  }
888 
889  distanceTraversed += segmentLength;
890  prevX = thisX;
891  prevY = thisY;
892  prevZ = thisZ;
893  prevM = thisM;
894  }
895 
896  return nullptr;
897 }
898 
899 QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
900 {
901  if ( startDistance < 0 && endDistance < 0 )
902  return createEmptyWithSameType();
903 
904  endDistance = std::max( startDistance, endDistance );
905 
906  double distanceTraversed = 0;
907  const int totalPoints = numPoints();
908  if ( totalPoints == 0 )
909  return clone();
910 
911  QVector< QgsPoint > substringPoints;
912 
914  if ( is3D() )
915  pointType = QgsWkbTypes::PointZ;
916  if ( isMeasure() )
917  pointType = QgsWkbTypes::addM( pointType );
918 
919  const double *x = mX.constData();
920  const double *y = mY.constData();
921  const double *z = is3D() ? mZ.constData() : nullptr;
922  const double *m = isMeasure() ? mM.constData() : nullptr;
923 
924  double prevX = *x++;
925  double prevY = *y++;
926  double prevZ = z ? *z++ : 0.0;
927  double prevM = m ? *m++ : 0.0;
928  bool foundStart = false;
929 
930  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
931  {
932  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
933  foundStart = true;
934  }
935 
936  substringPoints.reserve( totalPoints );
937 
938  for ( int i = 1; i < totalPoints; ++i )
939  {
940  double thisX = *x++;
941  double thisY = *y++;
942  double thisZ = z ? *z++ : 0.0;
943  double thisM = m ? *m++ : 0.0;
944 
945  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
946  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
947  {
948  // start point falls on this segment
949  const double distanceToStart = startDistance - distanceTraversed;
950  double startX, startY;
951  double startZ = 0;
952  double startM = 0;
953  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
954  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
955  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
956  substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
957  foundStart = true;
958  }
959  if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
960  {
961  // end point falls on this segment
962  const double distanceToEnd = endDistance - distanceTraversed;
963  double endX, endY;
964  double endZ = 0;
965  double endM = 0;
966  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
967  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
968  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
969  substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
970  }
971  else if ( foundStart )
972  {
973  substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
974  }
975 
976  distanceTraversed += segmentLength;
977  if ( distanceTraversed > endDistance )
978  break;
979 
980  prevX = thisX;
981  prevY = thisY;
982  prevZ = thisZ;
983  prevM = thisM;
984  }
985 
986  return new QgsLineString( substringPoints );
987 }
988 
989 /***************************************************************************
990  * This class is considered CRITICAL and any change MUST be accompanied with
991  * full unit tests.
992  * See details in QEP #17
993  ****************************************************************************/
994 
995 void QgsLineString::draw( QPainter &p ) const
996 {
997  p.drawPolyline( asQPolygonF() );
998 }
999 
1000 void QgsLineString::addToPainterPath( QPainterPath &path ) const
1001 {
1002  int nPoints = numPoints();
1003  if ( nPoints < 1 )
1004  {
1005  return;
1006  }
1007 
1008  if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1009  {
1010  path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1011  }
1012 
1013  for ( int i = 1; i < nPoints; ++i )
1014  {
1015  path.lineTo( mX.at( i ), mY.at( i ) );
1016  }
1017 }
1018 
1019 void QgsLineString::drawAsPolygon( QPainter &p ) const
1020 {
1021  p.drawPolygon( asQPolygonF() );
1022 }
1023 
1025 {
1026  QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1027  compoundCurve->addCurve( clone() );
1028  return compoundCurve;
1029 }
1030 
1031 void QgsLineString::extend( double startDistance, double endDistance )
1032 {
1033  if ( mX.size() < 2 || mY.size() < 2 )
1034  return;
1035 
1036  // start of line
1037  if ( startDistance > 0 )
1038  {
1039  double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1040  std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1041  double newLen = currentLen + startDistance;
1042  mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1043  mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1044  }
1045  // end of line
1046  if ( endDistance > 0 )
1047  {
1048  int last = mX.size() - 1;
1049  double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1050  std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1051  double newLen = currentLen + endDistance;
1052  mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1053  mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1054  }
1055 }
1056 
1058 {
1059  auto result = qgis::make_unique< QgsLineString >();
1060  result->mWkbType = mWkbType;
1061  return result.release();
1062 }
1063 
1065 {
1066  return QStringLiteral( "LineString" );
1067 }
1068 
1070 {
1071  return 1;
1072 }
1073 
1074 /***************************************************************************
1075  * This class is considered CRITICAL and any change MUST be accompanied with
1076  * full unit tests.
1077  * See details in QEP #17
1078  ****************************************************************************/
1079 
1081 {
1082  double *zArray = nullptr;
1083  bool hasZ = is3D();
1084  int nPoints = numPoints();
1085 
1086  // it's possible that transformCoords will throw an exception - so we need to use
1087  // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1088  std::unique_ptr< double[] > dummyZ;
1089  if ( !hasZ || !transformZ )
1090  {
1091  dummyZ.reset( new double[nPoints]() );
1092  zArray = dummyZ.get();
1093  }
1094  else
1095  {
1096  zArray = mZ.data();
1097  }
1098  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1099  clearCache();
1100 }
1101 
1102 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1103 {
1104  int nPoints = numPoints();
1105  bool hasZ = is3D();
1106  bool hasM = isMeasure();
1107  double *x = mX.data();
1108  double *y = mY.data();
1109  double *z = hasZ ? mZ.data() : nullptr;
1110  double *m = hasM ? mM.data() : nullptr;
1111  for ( int i = 0; i < nPoints; ++i )
1112  {
1113  double xOut, yOut;
1114  t.map( *x, *y, &xOut, &yOut );
1115  *x++ = xOut;
1116  *y++ = yOut;
1117  if ( hasZ )
1118  {
1119  *z = *z * zScale + zTranslate;
1120  z++;
1121  }
1122  if ( hasM )
1123  {
1124  *m = *m * mScale + mTranslate;
1125  m++;
1126  }
1127  }
1128  clearCache();
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 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1138 {
1139  if ( position.vertex < 0 || position.vertex > mX.size() )
1140  {
1141  return false;
1142  }
1143 
1144  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1145  {
1147  }
1148 
1149  mX.insert( position.vertex, vertex.x() );
1150  mY.insert( position.vertex, vertex.y() );
1151  if ( is3D() )
1152  {
1153  mZ.insert( position.vertex, vertex.z() );
1154  }
1155  if ( isMeasure() )
1156  {
1157  mM.insert( position.vertex, vertex.m() );
1158  }
1159  clearCache(); //set bounding box invalid
1160  return true;
1161 }
1162 
1163 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1164 {
1165  if ( position.vertex < 0 || position.vertex >= mX.size() )
1166  {
1167  return false;
1168  }
1169  mX[position.vertex] = newPos.x();
1170  mY[position.vertex] = newPos.y();
1171  if ( is3D() && newPos.is3D() )
1172  {
1173  mZ[position.vertex] = newPos.z();
1174  }
1175  if ( isMeasure() && newPos.isMeasure() )
1176  {
1177  mM[position.vertex] = newPos.m();
1178  }
1179  clearCache(); //set bounding box invalid
1180  return true;
1181 }
1182 
1184 {
1185  if ( position.vertex >= mX.size() || position.vertex < 0 )
1186  {
1187  return false;
1188  }
1189 
1190  mX.remove( position.vertex );
1191  mY.remove( position.vertex );
1192  if ( is3D() )
1193  {
1194  mZ.remove( position.vertex );
1195  }
1196  if ( isMeasure() )
1197  {
1198  mM.remove( position.vertex );
1199  }
1200 
1201  if ( numPoints() == 1 )
1202  {
1203  clear();
1204  }
1205 
1206  clearCache(); //set bounding box invalid
1207  return true;
1208 }
1209 
1210 /***************************************************************************
1211  * This class is considered CRITICAL and any change MUST be accompanied with
1212  * full unit tests.
1213  * See details in QEP #17
1214  ****************************************************************************/
1215 
1217 {
1218  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1219  {
1221  }
1222 
1223  mX.append( pt.x() );
1224  mY.append( pt.y() );
1225  if ( is3D() )
1226  {
1227  mZ.append( pt.z() );
1228  }
1229  if ( isMeasure() )
1230  {
1231  mM.append( pt.m() );
1232  }
1233  clearCache(); //set bounding box invalid
1234 }
1235 
1236 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1237 {
1238  double sqrDist = std::numeric_limits<double>::max();
1239  double leftOfDist = std::numeric_limits<double>::max();
1240  int prevLeftOf = 0;
1241  double prevLeftOfX = 0.0;
1242  double prevLeftOfY = 0.0;
1243  double testDist = 0;
1244  double segmentPtX, segmentPtY;
1245 
1246  if ( leftOf )
1247  *leftOf = 0;
1248 
1249  int size = mX.size();
1250  if ( size == 0 || size == 1 )
1251  {
1252  vertexAfter = QgsVertexId( 0, 0, 0 );
1253  return -1;
1254  }
1255  for ( int i = 1; i < size; ++i )
1256  {
1257  double prevX = mX.at( i - 1 );
1258  double prevY = mY.at( i - 1 );
1259  double currentX = mX.at( i );
1260  double currentY = mY.at( i );
1261  testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1262  if ( testDist < sqrDist )
1263  {
1264  sqrDist = testDist;
1265  segmentPt.setX( segmentPtX );
1266  segmentPt.setY( segmentPtY );
1267  vertexAfter.part = 0;
1268  vertexAfter.ring = 0;
1269  vertexAfter.vertex = i;
1270  }
1271  if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1272  {
1273  int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1274  // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1275  // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1276  // where we can perform the check
1277  if ( left != 0 )
1278  {
1279  if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1280  {
1281  // we have two possible segments each with equal distance to point, but they disagree
1282  // on whether or not the point is to the left of them.
1283  // so we test the segments themselves and flip the result.
1284  // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1285  *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1286  }
1287  else
1288  {
1289  *leftOf = left;
1290  }
1291  prevLeftOf = *leftOf;
1292  leftOfDist = testDist;
1293  prevLeftOfX = prevX;
1294  prevLeftOfY = prevY;
1295  }
1296  else if ( testDist < leftOfDist )
1297  {
1298  *leftOf = left;
1299  leftOfDist = testDist;
1300  prevLeftOf = 0;
1301  }
1302  }
1303  }
1304  return sqrDist;
1305 }
1306 
1307 /***************************************************************************
1308  * This class is considered CRITICAL and any change MUST be accompanied with
1309  * full unit tests.
1310  * See details in QEP #17
1311  ****************************************************************************/
1312 
1313 bool QgsLineString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1314 {
1315  if ( node < 0 || node >= numPoints() )
1316  {
1317  return false;
1318  }
1319  point = pointN( node );
1321  return true;
1322 }
1323 
1325 {
1326  if ( mX.isEmpty() )
1327  return QgsPoint();
1328 
1329  int numPoints = mX.count();
1330  if ( numPoints == 1 )
1331  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1332 
1333  double totalLineLength = 0.0;
1334  double prevX = mX.at( 0 );
1335  double prevY = mY.at( 0 );
1336  double sumX = 0.0;
1337  double sumY = 0.0;
1338 
1339  for ( int i = 1; i < numPoints ; ++i )
1340  {
1341  double currentX = mX.at( i );
1342  double currentY = mY.at( i );
1343  double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1344  std::pow( currentY - prevY, 2.0 ) );
1345  if ( qgsDoubleNear( segmentLength, 0.0 ) )
1346  continue;
1347 
1348  totalLineLength += segmentLength;
1349  sumX += segmentLength * 0.5 * ( currentX + prevX );
1350  sumY += segmentLength * 0.5 * ( currentY + prevY );
1351  prevX = currentX;
1352  prevY = currentY;
1353  }
1354 
1355  if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1356  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1357  else
1358  return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1359 
1360 }
1361 
1362 /***************************************************************************
1363  * This class is considered CRITICAL and any change MUST be accompanied with
1364  * full unit tests.
1365  * See details in QEP #17
1366  ****************************************************************************/
1367 
1368 void QgsLineString::sumUpArea( double &sum ) const
1369 {
1370  int maxIndex = numPoints() - 1;
1371 
1372  for ( int i = 0; i < maxIndex; ++i )
1373  {
1374  sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1375  }
1376 }
1377 
1378 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1379 {
1380  bool hasZ = is3D();
1381  bool hasM = isMeasure();
1382  int nVertices = 0;
1383  wkb >> nVertices;
1384  mX.resize( nVertices );
1385  mY.resize( nVertices );
1386  hasZ ? mZ.resize( nVertices ) : mZ.clear();
1387  hasM ? mM.resize( nVertices ) : mM.clear();
1388  double *x = mX.data();
1389  double *y = mY.data();
1390  double *m = hasM ? mM.data() : nullptr;
1391  double *z = hasZ ? mZ.data() : nullptr;
1392  for ( int i = 0; i < nVertices; ++i )
1393  {
1394  wkb >> *x++;
1395  wkb >> *y++;
1396  if ( hasZ )
1397  {
1398  wkb >> *z++;
1399  }
1400  if ( hasM )
1401  {
1402  wkb >> *m++;
1403  }
1404  }
1405  clearCache(); //set bounding box invalid
1406 }
1407 
1408 /***************************************************************************
1409  * This class is considered CRITICAL and any change MUST be accompanied with
1410  * full unit tests.
1411  * See details in QEP #17
1412  ****************************************************************************/
1413 
1415 {
1416  if ( numPoints() < 1 || isClosed() )
1417  {
1418  return;
1419  }
1420  addVertex( startPoint() );
1421 }
1422 
1424 {
1425  if ( mX.count() < 2 )
1426  {
1427  //undefined
1428  return 0.0;
1429  }
1430 
1431  if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1432  {
1433  if ( isClosed() )
1434  {
1435  double previousX = mX.at( numPoints() - 2 );
1436  double previousY = mY.at( numPoints() - 2 );
1437  double currentX = mX.at( 0 );
1438  double currentY = mY.at( 0 );
1439  double afterX = mX.at( 1 );
1440  double afterY = mY.at( 1 );
1441  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1442  }
1443  else if ( vertex.vertex == 0 )
1444  {
1445  return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1446  }
1447  else
1448  {
1449  int a = numPoints() - 2;
1450  int b = numPoints() - 1;
1451  return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1452  }
1453  }
1454  else
1455  {
1456  double previousX = mX.at( vertex.vertex - 1 );
1457  double previousY = mY.at( vertex.vertex - 1 );
1458  double currentX = mX.at( vertex.vertex );
1459  double currentY = mY.at( vertex.vertex );
1460  double afterX = mX.at( vertex.vertex + 1 );
1461  double afterY = mY.at( vertex.vertex + 1 );
1462  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1463  }
1464 }
1465 
1466 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1467 {
1468  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1469  return 0.0;
1470 
1471  double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1472  double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1473  return std::sqrt( dx * dx + dy * dy );
1474 }
1475 
1476 /***************************************************************************
1477  * This class is considered CRITICAL and any change MUST be accompanied with
1478  * full unit tests.
1479  * See details in QEP #17
1480  ****************************************************************************/
1481 
1482 bool QgsLineString::addZValue( double zValue )
1483 {
1484  if ( QgsWkbTypes::hasZ( mWkbType ) )
1485  return false;
1486 
1487  clearCache();
1488  if ( mWkbType == QgsWkbTypes::Unknown )
1489  {
1491  return true;
1492  }
1493 
1495 
1496  mZ.clear();
1497  int nPoints = numPoints();
1498  mZ.reserve( nPoints );
1499  for ( int i = 0; i < nPoints; ++i )
1500  {
1501  mZ << zValue;
1502  }
1503  return true;
1504 }
1505 
1506 bool QgsLineString::addMValue( double mValue )
1507 {
1508  if ( QgsWkbTypes::hasM( mWkbType ) )
1509  return false;
1510 
1511  clearCache();
1512  if ( mWkbType == QgsWkbTypes::Unknown )
1513  {
1515  return true;
1516  }
1517 
1519  {
1521  }
1522  else
1523  {
1525  }
1526 
1527  mM.clear();
1528  int nPoints = numPoints();
1529  mM.reserve( nPoints );
1530  for ( int i = 0; i < nPoints; ++i )
1531  {
1532  mM << mValue;
1533  }
1534  return true;
1535 }
1536 
1538 {
1539  if ( !is3D() )
1540  return false;
1541 
1542  clearCache();
1544  mZ.clear();
1545  return true;
1546 }
1547 
1549 {
1550  if ( !isMeasure() )
1551  return false;
1552 
1553  clearCache();
1555  mM.clear();
1556  return true;
1557 }
1558 
1560 {
1561  std::swap( mX, mY );
1562  clearCache();
1563 }
1564 
1566 {
1567  if ( type == mWkbType )
1568  return true;
1569 
1570  clearCache();
1571  if ( type == QgsWkbTypes::LineString25D )
1572  {
1573  //special handling required for conversion to LineString25D
1574  dropMValue();
1575  addZValue( std::numeric_limits<double>::quiet_NaN() );
1577  return true;
1578  }
1579  else
1580  {
1581  return QgsCurve::convertTo( type );
1582  }
1583 }
1584 
1585 void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1586 {
1587  bool hasZ = is3D();
1588  bool hasM = isMeasure();
1589  int size = mX.size();
1590 
1591  double *srcX = mX.data();
1592  double *srcY = mY.data();
1593  double *srcM = hasM ? mM.data() : nullptr;
1594  double *srcZ = hasZ ? mZ.data() : nullptr;
1595 
1596  double *destX = srcX;
1597  double *destY = srcY;
1598  double *destM = srcM;
1599  double *destZ = srcZ;
1600 
1601  int filteredPoints = 0;
1602  for ( int i = 0; i < size; ++i )
1603  {
1604  double x = *srcX++;
1605  double y = *srcY++;
1606  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
1607  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
1608 
1609  if ( filter( QgsPoint( x, y, z, m ) ) )
1610  {
1611  filteredPoints++;
1612  *destX++ = x;
1613  *destY++ = y;
1614  if ( hasM )
1615  *destM++ = m;
1616  if ( hasZ )
1617  *destZ++ = z;
1618  }
1619  }
1620 
1621  mX.resize( filteredPoints );
1622  mY.resize( filteredPoints );
1623  if ( hasZ )
1624  mZ.resize( filteredPoints );
1625  if ( hasM )
1626  mM.resize( filteredPoints );
1627 
1628  clearCache();
1629 }
1630 
1631 void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1632 {
1633  bool hasZ = is3D();
1634  bool hasM = isMeasure();
1635  int size = mX.size();
1636 
1637  double *srcX = mX.data();
1638  double *srcY = mY.data();
1639  double *srcM = hasM ? mM.data() : nullptr;
1640  double *srcZ = hasZ ? mZ.data() : nullptr;
1641 
1642  for ( int i = 0; i < size; ++i )
1643  {
1644  double x = *srcX;
1645  double y = *srcY;
1646  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
1647  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
1648  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
1649  *srcX++ = res.x();
1650  *srcY++ = res.y();
1651  if ( hasM )
1652  *srcM++ = res.m();
1653  if ( hasZ )
1654  *srcZ++ = res.z();
1655  }
1656  clearCache();
1657 }
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:280
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.
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:245
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.
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:262
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:268
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:273
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;. Negativ values mean left a...
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