QGIS API Documentation  3.17.0-Master (ca637cfeb2)
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 QVector< QgsVertexId > QgsLineString::collectDuplicateNodes( double epsilon, bool useZValues ) const
367 {
368  QVector< QgsVertexId > res;
369  if ( mX.count() <= 1 )
370  return res;
371 
372  const double *x = mX.constData();
373  const double *y = mY.constData();
374  bool hasZ = is3D();
375  bool useZ = hasZ && useZValues;
376  const double *z = useZ ? mZ.constData() : nullptr;
377 
378  double prevX = *x++;
379  double prevY = *y++;
380  double prevZ = z ? *z++ : 0;
381 
382  QgsVertexId id;
383  for ( int i = 1; i < mX.count(); ++i )
384  {
385  double currentX = *x++;
386  double currentY = *y++;
387  double currentZ = useZ ? *z++ : 0;
388  if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
389  qgsDoubleNear( currentY, prevY, epsilon ) &&
390  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
391  {
392  id.vertex = i;
393  res << id;
394  }
395  else
396  {
397  prevX = currentX;
398  prevY = currentY;
399  prevZ = currentZ;
400  }
401  }
402  return res;
403 }
404 
405 QPolygonF QgsLineString::asQPolygonF() const
406 {
407  const int nb = mX.size();
408  QPolygonF points( nb );
409 
410  const double *x = mX.constData();
411  const double *y = mY.constData();
412  QPointF *dest = points.data();
413  for ( int i = 0; i < nb; ++i )
414  {
415  *dest++ = QPointF( *x++, *y++ );
416  }
417  return points;
418 }
419 
421 {
422  if ( !wkbPtr )
423  {
424  return false;
425  }
426 
427  QgsWkbTypes::Type type = wkbPtr.readHeader();
429  {
430  return false;
431  }
432  mWkbType = type;
433  importVerticesFromWkb( wkbPtr );
434  return true;
435 }
436 
438 {
439  double xmin = std::numeric_limits<double>::max();
440  double ymin = std::numeric_limits<double>::max();
441  double xmax = -std::numeric_limits<double>::max();
442  double ymax = -std::numeric_limits<double>::max();
443 
444  for ( double x : mX )
445  {
446  if ( x < xmin )
447  xmin = x;
448  if ( x > xmax )
449  xmax = x;
450  }
451  for ( double y : mY )
452  {
453  if ( y < ymin )
454  ymin = y;
455  if ( y > ymax )
456  ymax = y;
457  }
458  return QgsRectangle( xmin, ymin, xmax, ymax );
459 }
460 
461 /***************************************************************************
462  * This class is considered CRITICAL and any change MUST be accompanied with
463  * full unit tests.
464  * See details in QEP #17
465  ****************************************************************************/
466 bool QgsLineString::fromWkt( const QString &wkt )
467 {
468  clear();
469 
470  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
471 
472  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::LineString )
473  return false;
474  mWkbType = parts.first;
475 
476  QString secondWithoutParentheses = parts.second;
477  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
478  parts.second = parts.second.remove( '(' ).remove( ')' );
479  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
480  secondWithoutParentheses.isEmpty() )
481  return true;
482 
484  // There is a non number in the coordinates sequence
485  // LineString ( A b, 1 2)
486  if ( points.isEmpty() )
487  return false;
488 
489  setPoints( points );
490  return true;
491 }
492 
493 int QgsLineString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
494 {
495  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
496  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
497  return binarySize;
498 }
499 
500 QByteArray QgsLineString::asWkb( WkbFlags flags ) const
501 {
502  QByteArray wkbArray;
503  wkbArray.resize( QgsLineString::wkbSize( flags ) );
504  QgsWkbPtr wkb( wkbArray );
505  wkb << static_cast<char>( QgsApplication::endian() );
506  wkb << static_cast<quint32>( wkbType() );
507  QgsPointSequence pts;
508  points( pts );
509  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
510  return wkbArray;
511 }
512 
513 /***************************************************************************
514  * This class is considered CRITICAL and any change MUST be accompanied with
515  * full unit tests.
516  * See details in QEP #17
517  ****************************************************************************/
518 
519 QString QgsLineString::asWkt( int precision ) const
520 {
521  QString wkt = wktTypeStr() + ' ';
522 
523  if ( isEmpty() )
524  wkt += QLatin1String( "EMPTY" );
525  else
526  {
527  QgsPointSequence pts;
528  points( pts );
529  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
530  }
531  return wkt;
532 }
533 
534 QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
535 {
536  QgsPointSequence pts;
537  points( pts );
538 
539  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
540 
541  if ( isEmpty() )
542  return elemLineString;
543 
544  elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
545 
546  return elemLineString;
547 }
548 
549 QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
550 {
551  QgsPointSequence pts;
552  points( pts );
553 
554  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
555 
556  if ( isEmpty() )
557  return elemLineString;
558 
559  elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
560  return elemLineString;
561 }
562 
564 {
565  QgsPointSequence pts;
566  points( pts );
567  return
568  {
569  { "type", "LineString" },
570  { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
571  };
572 }
573 
574 QString QgsLineString::asKml( int precision ) const
575 {
576  QString kml;
577  if ( isRing() )
578  {
579  kml.append( QLatin1String( "<LinearRing>" ) );
580  }
581  else
582  {
583  kml.append( QLatin1String( "<LineString>" ) );
584  }
585  bool z = is3D();
586  kml.append( QLatin1String( "<altitudeMode>" ) );
587  if ( z )
588  {
589  kml.append( QLatin1String( "absolute" ) );
590  }
591  else
592  {
593  kml.append( QLatin1String( "clampToGround" ) );
594  }
595  kml.append( QLatin1String( "</altitudeMode>" ) );
596  kml.append( QLatin1String( "<coordinates>" ) );
597 
598  int nPoints = mX.size();
599  for ( int i = 0; i < nPoints; ++i )
600  {
601  if ( i > 0 )
602  {
603  kml.append( QLatin1String( " " ) );
604  }
605  kml.append( qgsDoubleToString( mX[i], precision ) );
606  kml.append( QLatin1String( "," ) );
607  kml.append( qgsDoubleToString( mY[i], precision ) );
608  if ( z )
609  {
610  kml.append( QLatin1String( "," ) );
611  kml.append( qgsDoubleToString( mZ[i], precision ) );
612  }
613  else
614  {
615  kml.append( QLatin1String( ",0" ) );
616  }
617  }
618  kml.append( QLatin1String( "</coordinates>" ) );
619  if ( isRing() )
620  {
621  kml.append( QLatin1String( "</LinearRing>" ) );
622  }
623  else
624  {
625  kml.append( QLatin1String( "</LineString>" ) );
626  }
627  return kml;
628 }
629 
630 /***************************************************************************
631  * This class is considered CRITICAL and any change MUST be accompanied with
632  * full unit tests.
633  * See details in QEP #17
634  ****************************************************************************/
635 
636 double QgsLineString::length() const
637 {
638  double length = 0;
639  int size = mX.size();
640  double dx, dy;
641  for ( int i = 1; i < size; ++i )
642  {
643  dx = mX.at( i ) - mX.at( i - 1 );
644  dy = mY.at( i ) - mY.at( i - 1 );
645  length += std::sqrt( dx * dx + dy * dy );
646  }
647  return length;
648 }
649 
651 {
652  if ( is3D() )
653  {
654  double length = 0;
655  int size = mX.size();
656  double dx, dy, dz;
657  for ( int i = 1; i < size; ++i )
658  {
659  dx = mX.at( i ) - mX.at( i - 1 );
660  dy = mY.at( i ) - mY.at( i - 1 );
661  dz = mZ.at( i ) - mZ.at( i - 1 );
662  length += std::sqrt( dx * dx + dy * dy + dz * dz );
663  }
664  return length;
665  }
666  else
667  {
668  return length();
669  }
670 }
671 
673 {
674  if ( numPoints() < 1 )
675  {
676  return QgsPoint();
677  }
678  return pointN( 0 );
679 }
680 
682 {
683  if ( numPoints() < 1 )
684  {
685  return QgsPoint();
686  }
687  return pointN( numPoints() - 1 );
688 }
689 
690 /***************************************************************************
691  * This class is considered CRITICAL and any change MUST be accompanied with
692  * full unit tests.
693  * See details in QEP #17
694  ****************************************************************************/
695 
696 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
697 {
698  Q_UNUSED( tolerance )
699  Q_UNUSED( toleranceType )
700  return clone();
701 }
702 
704 {
705  return mX.size();
706 }
707 
709 {
710  return mX.size();
711 }
712 
714 {
715  if ( i < 0 || i >= mX.size() )
716  {
717  return QgsPoint();
718  }
719 
720  double x = mX.at( i );
721  double y = mY.at( i );
722  double z = std::numeric_limits<double>::quiet_NaN();
723  double m = std::numeric_limits<double>::quiet_NaN();
724 
725  bool hasZ = is3D();
726  if ( hasZ )
727  {
728  z = mZ.at( i );
729  }
730  bool hasM = isMeasure();
731  if ( hasM )
732  {
733  m = mM.at( i );
734  }
735 
738  {
740  }
741  else if ( hasZ && hasM )
742  {
744  }
745  else if ( hasZ )
746  {
748  }
749  else if ( hasM )
750  {
752  }
753  return QgsPoint( t, x, y, z, m );
754 }
755 
756 /***************************************************************************
757  * This class is considered CRITICAL and any change MUST be accompanied with
758  * full unit tests.
759  * See details in QEP #17
760  ****************************************************************************/
761 
762 double QgsLineString::xAt( int index ) const
763 {
764  if ( index >= 0 && index < mX.size() )
765  return mX.at( index );
766  else
767  return 0.0;
768 }
769 
770 double QgsLineString::yAt( int index ) const
771 {
772  if ( index >= 0 && index < mY.size() )
773  return mY.at( index );
774  else
775  return 0.0;
776 }
777 
778 void QgsLineString::setXAt( int index, double x )
779 {
780  if ( index >= 0 && index < mX.size() )
781  mX[ index ] = x;
782  clearCache();
783 }
784 
785 void QgsLineString::setYAt( int index, double y )
786 {
787  if ( index >= 0 && index < mY.size() )
788  mY[ index ] = y;
789  clearCache();
790 }
791 
792 /***************************************************************************
793  * This class is considered CRITICAL and any change MUST be accompanied with
794  * full unit tests.
795  * See details in QEP #17
796  ****************************************************************************/
797 
799 {
800  pts.clear();
801  int nPoints = numPoints();
802  pts.reserve( nPoints );
803  for ( int i = 0; i < nPoints; ++i )
804  {
805  pts.push_back( pointN( i ) );
806  }
807 }
808 
810 {
811  clearCache(); //set bounding box invalid
812 
813  if ( points.isEmpty() )
814  {
815  clear();
816  return;
817  }
818 
819  //get wkb type from first point
820  const QgsPoint &firstPt = points.at( 0 );
821  bool hasZ = firstPt.is3D();
822  bool hasM = firstPt.isMeasure();
823 
825 
826  mX.resize( points.size() );
827  mY.resize( points.size() );
828  if ( hasZ )
829  {
830  mZ.resize( points.size() );
831  }
832  else
833  {
834  mZ.clear();
835  }
836  if ( hasM )
837  {
838  mM.resize( points.size() );
839  }
840  else
841  {
842  mM.clear();
843  }
844 
845  for ( int i = 0; i < points.size(); ++i )
846  {
847  mX[i] = points.at( i ).x();
848  mY[i] = points.at( i ).y();
849  if ( hasZ )
850  {
851  double z = points.at( i ).z();
852  mZ[i] = std::isnan( z ) ? 0 : z;
853  }
854  if ( hasM )
855  {
856  double m = points.at( i ).m();
857  mM[i] = std::isnan( m ) ? 0 : m;
858  }
859  }
860 }
861 
862 /***************************************************************************
863  * This class is considered CRITICAL and any change MUST be accompanied with
864  * full unit tests.
865  * See details in QEP #17
866  ****************************************************************************/
867 
869 {
870  if ( !line )
871  {
872  return;
873  }
874 
875  if ( numPoints() < 1 )
876  {
878  }
879 
880  // do not store duplicit points
881  if ( numPoints() > 0 &&
882  line->numPoints() > 0 &&
883  endPoint() == line->startPoint() )
884  {
885  mX.pop_back();
886  mY.pop_back();
887 
888  if ( is3D() )
889  {
890  mZ.pop_back();
891  }
892  if ( isMeasure() )
893  {
894  mM.pop_back();
895  }
896  }
897 
898  mX += line->mX;
899  mY += line->mY;
900 
901  if ( is3D() )
902  {
903  if ( line->is3D() )
904  {
905  mZ += line->mZ;
906  }
907  else
908  {
909  // if append line does not have z coordinates, fill with NaN to match number of points in final line
910  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
911  }
912  }
913 
914  if ( isMeasure() )
915  {
916  if ( line->isMeasure() )
917  {
918  mM += line->mM;
919  }
920  else
921  {
922  // if append line does not have m values, fill with NaN to match number of points in final line
923  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
924  }
925  }
926 
927  clearCache(); //set bounding box invalid
928 }
929 
931 {
932  QgsLineString *copy = clone();
933  std::reverse( copy->mX.begin(), copy->mX.end() );
934  std::reverse( copy->mY.begin(), copy->mY.end() );
935  if ( copy->is3D() )
936  {
937  std::reverse( copy->mZ.begin(), copy->mZ.end() );
938  }
939  if ( copy->isMeasure() )
940  {
941  std::reverse( copy->mM.begin(), copy->mM.end() );
942  }
943  return copy;
944 }
945 
946 void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
947 {
948  if ( distance < 0 )
949  return;
950 
951  double distanceTraversed = 0;
952  const int totalPoints = numPoints();
953  if ( totalPoints == 0 )
954  return;
955 
956  const double *x = mX.constData();
957  const double *y = mY.constData();
958  const double *z = is3D() ? mZ.constData() : nullptr;
959  const double *m = isMeasure() ? mM.constData() : nullptr;
960 
961  double prevX = *x++;
962  double prevY = *y++;
963  double prevZ = z ? *z++ : 0.0;
964  double prevM = m ? *m++ : 0.0;
965 
966  if ( qgsDoubleNear( distance, 0.0 ) )
967  {
968  visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
969  return;
970  }
971 
972  double pZ = std::numeric_limits<double>::quiet_NaN();
973  double pM = std::numeric_limits<double>::quiet_NaN();
974  double nextPointDistance = distance;
975  for ( int i = 1; i < totalPoints; ++i )
976  {
977  double thisX = *x++;
978  double thisY = *y++;
979  double thisZ = z ? *z++ : 0.0;
980  double thisM = m ? *m++ : 0.0;
981 
982  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
983  while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
984  {
985  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
986  const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
987  double pX, pY;
988  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
989  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
990  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
991 
992  if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
993  return;
994 
995  nextPointDistance += distance;
996  }
997 
998  distanceTraversed += segmentLength;
999  prevX = thisX;
1000  prevY = thisY;
1001  prevZ = thisZ;
1002  prevM = thisM;
1003  }
1004 }
1005 
1006 QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
1007 {
1008  if ( distance < 0 )
1009  return nullptr;
1010 
1012  if ( is3D() )
1013  pointType = QgsWkbTypes::PointZ;
1014  if ( isMeasure() )
1015  pointType = QgsWkbTypes::addM( pointType );
1016 
1017  std::unique_ptr< QgsPoint > res;
1018  visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
1019  {
1020  res = qgis::make_unique< QgsPoint >( pointType, x, y, z, m );
1021  return false;
1022  } );
1023  return res.release();
1024 }
1025 
1026 QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
1027 {
1028  if ( startDistance < 0 && endDistance < 0 )
1029  return createEmptyWithSameType();
1030 
1031  endDistance = std::max( startDistance, endDistance );
1032 
1033  double distanceTraversed = 0;
1034  const int totalPoints = numPoints();
1035  if ( totalPoints == 0 )
1036  return clone();
1037 
1038  QVector< QgsPoint > substringPoints;
1039 
1041  if ( is3D() )
1042  pointType = QgsWkbTypes::PointZ;
1043  if ( isMeasure() )
1044  pointType = QgsWkbTypes::addM( pointType );
1045 
1046  const double *x = mX.constData();
1047  const double *y = mY.constData();
1048  const double *z = is3D() ? mZ.constData() : nullptr;
1049  const double *m = isMeasure() ? mM.constData() : nullptr;
1050 
1051  double prevX = *x++;
1052  double prevY = *y++;
1053  double prevZ = z ? *z++ : 0.0;
1054  double prevM = m ? *m++ : 0.0;
1055  bool foundStart = false;
1056 
1057  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1058  {
1059  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1060  foundStart = true;
1061  }
1062 
1063  substringPoints.reserve( totalPoints );
1064 
1065  for ( int i = 1; i < totalPoints; ++i )
1066  {
1067  double thisX = *x++;
1068  double thisY = *y++;
1069  double thisZ = z ? *z++ : 0.0;
1070  double thisM = m ? *m++ : 0.0;
1071 
1072  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
1073  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1074  {
1075  // start point falls on this segment
1076  const double distanceToStart = startDistance - distanceTraversed;
1077  double startX, startY;
1078  double startZ = 0;
1079  double startM = 0;
1080  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1081  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1082  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1083  substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1084  foundStart = true;
1085  }
1086  if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1087  {
1088  // end point falls on this segment
1089  const double distanceToEnd = endDistance - distanceTraversed;
1090  double endX, endY;
1091  double endZ = 0;
1092  double endM = 0;
1093  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1094  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1095  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1096  substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1097  }
1098  else if ( foundStart )
1099  {
1100  substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1101  }
1102 
1103  distanceTraversed += segmentLength;
1104  if ( distanceTraversed > endDistance )
1105  break;
1106 
1107  prevX = thisX;
1108  prevY = thisY;
1109  prevZ = thisZ;
1110  prevM = thisM;
1111  }
1112 
1113  return new QgsLineString( substringPoints );
1114 }
1115 
1116 /***************************************************************************
1117  * This class is considered CRITICAL and any change MUST be accompanied with
1118  * full unit tests.
1119  * See details in QEP #17
1120  ****************************************************************************/
1121 
1122 void QgsLineString::draw( QPainter &p ) const
1123 {
1124  p.drawPolyline( asQPolygonF() );
1125 }
1126 
1127 void QgsLineString::addToPainterPath( QPainterPath &path ) const
1128 {
1129  int nPoints = numPoints();
1130  if ( nPoints < 1 )
1131  {
1132  return;
1133  }
1134 
1135  if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1136  {
1137  path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1138  }
1139 
1140  for ( int i = 1; i < nPoints; ++i )
1141  {
1142  path.lineTo( mX.at( i ), mY.at( i ) );
1143  }
1144 }
1145 
1146 void QgsLineString::drawAsPolygon( QPainter &p ) const
1147 {
1148  p.drawPolygon( asQPolygonF() );
1149 }
1150 
1152 {
1153  QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1154  compoundCurve->addCurve( clone() );
1155  return compoundCurve;
1156 }
1157 
1158 void QgsLineString::extend( double startDistance, double endDistance )
1159 {
1160  if ( mX.size() < 2 || mY.size() < 2 )
1161  return;
1162 
1163  // start of line
1164  if ( startDistance > 0 )
1165  {
1166  double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1167  std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1168  double newLen = currentLen + startDistance;
1169  mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1170  mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1171  }
1172  // end of line
1173  if ( endDistance > 0 )
1174  {
1175  int last = mX.size() - 1;
1176  double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1177  std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1178  double newLen = currentLen + endDistance;
1179  mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1180  mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1181  }
1182 }
1183 
1185 {
1186  auto result = qgis::make_unique< QgsLineString >();
1187  result->mWkbType = mWkbType;
1188  return result.release();
1189 }
1190 
1192 {
1193  return QStringLiteral( "LineString" );
1194 }
1195 
1197 {
1198  return 1;
1199 }
1200 
1201 /***************************************************************************
1202  * This class is considered CRITICAL and any change MUST be accompanied with
1203  * full unit tests.
1204  * See details in QEP #17
1205  ****************************************************************************/
1206 
1208 {
1209  double *zArray = nullptr;
1210  bool hasZ = is3D();
1211  int nPoints = numPoints();
1212 
1213  // it's possible that transformCoords will throw an exception - so we need to use
1214  // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1215  std::unique_ptr< double[] > dummyZ;
1216  if ( !hasZ || !transformZ )
1217  {
1218  dummyZ.reset( new double[nPoints]() );
1219  zArray = dummyZ.get();
1220  }
1221  else
1222  {
1223  zArray = mZ.data();
1224  }
1225  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1226  clearCache();
1227 }
1228 
1229 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1230 {
1231  int nPoints = numPoints();
1232  bool hasZ = is3D();
1233  bool hasM = isMeasure();
1234  double *x = mX.data();
1235  double *y = mY.data();
1236  double *z = hasZ ? mZ.data() : nullptr;
1237  double *m = hasM ? mM.data() : nullptr;
1238  for ( int i = 0; i < nPoints; ++i )
1239  {
1240  double xOut, yOut;
1241  t.map( *x, *y, &xOut, &yOut );
1242  *x++ = xOut;
1243  *y++ = yOut;
1244  if ( hasZ )
1245  {
1246  *z = *z * zScale + zTranslate;
1247  z++;
1248  }
1249  if ( hasM )
1250  {
1251  *m = *m * mScale + mTranslate;
1252  m++;
1253  }
1254  }
1255  clearCache();
1256 }
1257 
1258 /***************************************************************************
1259  * This class is considered CRITICAL and any change MUST be accompanied with
1260  * full unit tests.
1261  * See details in QEP #17
1262  ****************************************************************************/
1263 
1264 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1265 {
1266  if ( position.vertex < 0 || position.vertex > mX.size() )
1267  {
1268  return false;
1269  }
1270 
1271  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1272  {
1274  }
1275 
1276  mX.insert( position.vertex, vertex.x() );
1277  mY.insert( position.vertex, vertex.y() );
1278  if ( is3D() )
1279  {
1280  mZ.insert( position.vertex, vertex.z() );
1281  }
1282  if ( isMeasure() )
1283  {
1284  mM.insert( position.vertex, vertex.m() );
1285  }
1286  clearCache(); //set bounding box invalid
1287  return true;
1288 }
1289 
1290 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1291 {
1292  if ( position.vertex < 0 || position.vertex >= mX.size() )
1293  {
1294  return false;
1295  }
1296  mX[position.vertex] = newPos.x();
1297  mY[position.vertex] = newPos.y();
1298  if ( is3D() && newPos.is3D() )
1299  {
1300  mZ[position.vertex] = newPos.z();
1301  }
1302  if ( isMeasure() && newPos.isMeasure() )
1303  {
1304  mM[position.vertex] = newPos.m();
1305  }
1306  clearCache(); //set bounding box invalid
1307  return true;
1308 }
1309 
1311 {
1312  if ( position.vertex >= mX.size() || position.vertex < 0 )
1313  {
1314  return false;
1315  }
1316 
1317  mX.remove( position.vertex );
1318  mY.remove( position.vertex );
1319  if ( is3D() )
1320  {
1321  mZ.remove( position.vertex );
1322  }
1323  if ( isMeasure() )
1324  {
1325  mM.remove( position.vertex );
1326  }
1327 
1328  if ( numPoints() == 1 )
1329  {
1330  clear();
1331  }
1332 
1333  clearCache(); //set bounding box invalid
1334  return true;
1335 }
1336 
1337 /***************************************************************************
1338  * This class is considered CRITICAL and any change MUST be accompanied with
1339  * full unit tests.
1340  * See details in QEP #17
1341  ****************************************************************************/
1342 
1344 {
1345  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1346  {
1348  }
1349 
1350  mX.append( pt.x() );
1351  mY.append( pt.y() );
1352  if ( is3D() )
1353  {
1354  mZ.append( pt.z() );
1355  }
1356  if ( isMeasure() )
1357  {
1358  mM.append( pt.m() );
1359  }
1360  clearCache(); //set bounding box invalid
1361 }
1362 
1363 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1364 {
1365  double sqrDist = std::numeric_limits<double>::max();
1366  double leftOfDist = std::numeric_limits<double>::max();
1367  int prevLeftOf = 0;
1368  double prevLeftOfX = 0.0;
1369  double prevLeftOfY = 0.0;
1370  double testDist = 0;
1371  double segmentPtX, segmentPtY;
1372 
1373  if ( leftOf )
1374  *leftOf = 0;
1375 
1376  int size = mX.size();
1377  if ( size == 0 || size == 1 )
1378  {
1379  vertexAfter = QgsVertexId( 0, 0, 0 );
1380  return -1;
1381  }
1382  for ( int i = 1; i < size; ++i )
1383  {
1384  double prevX = mX.at( i - 1 );
1385  double prevY = mY.at( i - 1 );
1386  double currentX = mX.at( i );
1387  double currentY = mY.at( i );
1388  testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1389  if ( testDist < sqrDist )
1390  {
1391  sqrDist = testDist;
1392  segmentPt.setX( segmentPtX );
1393  segmentPt.setY( segmentPtY );
1394  vertexAfter.part = 0;
1395  vertexAfter.ring = 0;
1396  vertexAfter.vertex = i;
1397  }
1398  if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1399  {
1400  int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1401  // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1402  // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1403  // where we can perform the check
1404  if ( left != 0 )
1405  {
1406  if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1407  {
1408  // we have two possible segments each with equal distance to point, but they disagree
1409  // on whether or not the point is to the left of them.
1410  // so we test the segments themselves and flip the result.
1411  // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1412  *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1413  }
1414  else
1415  {
1416  *leftOf = left;
1417  }
1418  prevLeftOf = *leftOf;
1419  leftOfDist = testDist;
1420  prevLeftOfX = prevX;
1421  prevLeftOfY = prevY;
1422  }
1423  else if ( testDist < leftOfDist )
1424  {
1425  *leftOf = left;
1426  leftOfDist = testDist;
1427  prevLeftOf = 0;
1428  }
1429  }
1430  }
1431  return sqrDist;
1432 }
1433 
1434 /***************************************************************************
1435  * This class is considered CRITICAL and any change MUST be accompanied with
1436  * full unit tests.
1437  * See details in QEP #17
1438  ****************************************************************************/
1439 
1440 bool QgsLineString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1441 {
1442  if ( node < 0 || node >= numPoints() )
1443  {
1444  return false;
1445  }
1446  point = pointN( node );
1448  return true;
1449 }
1450 
1452 {
1453  if ( mX.isEmpty() )
1454  return QgsPoint();
1455 
1456  int numPoints = mX.count();
1457  if ( numPoints == 1 )
1458  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1459 
1460  double totalLineLength = 0.0;
1461  double prevX = mX.at( 0 );
1462  double prevY = mY.at( 0 );
1463  double sumX = 0.0;
1464  double sumY = 0.0;
1465 
1466  for ( int i = 1; i < numPoints ; ++i )
1467  {
1468  double currentX = mX.at( i );
1469  double currentY = mY.at( i );
1470  double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1471  std::pow( currentY - prevY, 2.0 ) );
1472  if ( qgsDoubleNear( segmentLength, 0.0 ) )
1473  continue;
1474 
1475  totalLineLength += segmentLength;
1476  sumX += segmentLength * 0.5 * ( currentX + prevX );
1477  sumY += segmentLength * 0.5 * ( currentY + prevY );
1478  prevX = currentX;
1479  prevY = currentY;
1480  }
1481 
1482  if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1483  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1484  else
1485  return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1486 
1487 }
1488 
1489 /***************************************************************************
1490  * This class is considered CRITICAL and any change MUST be accompanied with
1491  * full unit tests.
1492  * See details in QEP #17
1493  ****************************************************************************/
1494 
1495 void QgsLineString::sumUpArea( double &sum ) const
1496 {
1497  int maxIndex = numPoints() - 1;
1498 
1499  for ( int i = 0; i < maxIndex; ++i )
1500  {
1501  sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1502  }
1503 }
1504 
1505 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1506 {
1507  bool hasZ = is3D();
1508  bool hasM = isMeasure();
1509  int nVertices = 0;
1510  wkb >> nVertices;
1511  mX.resize( nVertices );
1512  mY.resize( nVertices );
1513  hasZ ? mZ.resize( nVertices ) : mZ.clear();
1514  hasM ? mM.resize( nVertices ) : mM.clear();
1515  double *x = mX.data();
1516  double *y = mY.data();
1517  double *m = hasM ? mM.data() : nullptr;
1518  double *z = hasZ ? mZ.data() : nullptr;
1519  for ( int i = 0; i < nVertices; ++i )
1520  {
1521  wkb >> *x++;
1522  wkb >> *y++;
1523  if ( hasZ )
1524  {
1525  wkb >> *z++;
1526  }
1527  if ( hasM )
1528  {
1529  wkb >> *m++;
1530  }
1531  }
1532  clearCache(); //set bounding box invalid
1533 }
1534 
1535 /***************************************************************************
1536  * This class is considered CRITICAL and any change MUST be accompanied with
1537  * full unit tests.
1538  * See details in QEP #17
1539  ****************************************************************************/
1540 
1542 {
1543  if ( numPoints() < 1 || isClosed() )
1544  {
1545  return;
1546  }
1547  addVertex( startPoint() );
1548 }
1549 
1551 {
1552  if ( mX.count() < 2 )
1553  {
1554  //undefined
1555  return 0.0;
1556  }
1557 
1558  if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1559  {
1560  if ( isClosed() )
1561  {
1562  double previousX = mX.at( numPoints() - 2 );
1563  double previousY = mY.at( numPoints() - 2 );
1564  double currentX = mX.at( 0 );
1565  double currentY = mY.at( 0 );
1566  double afterX = mX.at( 1 );
1567  double afterY = mY.at( 1 );
1568  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1569  }
1570  else if ( vertex.vertex == 0 )
1571  {
1572  return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1573  }
1574  else
1575  {
1576  int a = numPoints() - 2;
1577  int b = numPoints() - 1;
1578  return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1579  }
1580  }
1581  else
1582  {
1583  double previousX = mX.at( vertex.vertex - 1 );
1584  double previousY = mY.at( vertex.vertex - 1 );
1585  double currentX = mX.at( vertex.vertex );
1586  double currentY = mY.at( vertex.vertex );
1587  double afterX = mX.at( vertex.vertex + 1 );
1588  double afterY = mY.at( vertex.vertex + 1 );
1589  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1590  }
1591 }
1592 
1593 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1594 {
1595  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1596  return 0.0;
1597 
1598  double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1599  double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1600  return std::sqrt( dx * dx + dy * dy );
1601 }
1602 
1603 /***************************************************************************
1604  * This class is considered CRITICAL and any change MUST be accompanied with
1605  * full unit tests.
1606  * See details in QEP #17
1607  ****************************************************************************/
1608 
1609 bool QgsLineString::addZValue( double zValue )
1610 {
1611  if ( QgsWkbTypes::hasZ( mWkbType ) )
1612  return false;
1613 
1614  clearCache();
1615  if ( mWkbType == QgsWkbTypes::Unknown )
1616  {
1618  return true;
1619  }
1620 
1622 
1623  mZ.clear();
1624  int nPoints = numPoints();
1625  mZ.reserve( nPoints );
1626  for ( int i = 0; i < nPoints; ++i )
1627  {
1628  mZ << zValue;
1629  }
1630  return true;
1631 }
1632 
1633 bool QgsLineString::addMValue( double mValue )
1634 {
1635  if ( QgsWkbTypes::hasM( mWkbType ) )
1636  return false;
1637 
1638  clearCache();
1639  if ( mWkbType == QgsWkbTypes::Unknown )
1640  {
1642  return true;
1643  }
1644 
1646  {
1648  }
1649  else
1650  {
1652  }
1653 
1654  mM.clear();
1655  int nPoints = numPoints();
1656  mM.reserve( nPoints );
1657  for ( int i = 0; i < nPoints; ++i )
1658  {
1659  mM << mValue;
1660  }
1661  return true;
1662 }
1663 
1665 {
1666  if ( !is3D() )
1667  return false;
1668 
1669  clearCache();
1671  mZ.clear();
1672  return true;
1673 }
1674 
1676 {
1677  if ( !isMeasure() )
1678  return false;
1679 
1680  clearCache();
1682  mM.clear();
1683  return true;
1684 }
1685 
1687 {
1688  std::swap( mX, mY );
1689  clearCache();
1690 }
1691 
1693 {
1694  if ( type == mWkbType )
1695  return true;
1696 
1697  clearCache();
1698  if ( type == QgsWkbTypes::LineString25D )
1699  {
1700  //special handling required for conversion to LineString25D
1701  dropMValue();
1702  addZValue( std::numeric_limits<double>::quiet_NaN() );
1704  return true;
1705  }
1706  else
1707  {
1708  return QgsCurve::convertTo( type );
1709  }
1710 }
1711 
1712 void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1713 {
1714  bool hasZ = is3D();
1715  bool hasM = isMeasure();
1716  int size = mX.size();
1717 
1718  double *srcX = mX.data();
1719  double *srcY = mY.data();
1720  double *srcM = hasM ? mM.data() : nullptr;
1721  double *srcZ = hasZ ? mZ.data() : nullptr;
1722 
1723  double *destX = srcX;
1724  double *destY = srcY;
1725  double *destM = srcM;
1726  double *destZ = srcZ;
1727 
1728  int filteredPoints = 0;
1729  for ( int i = 0; i < size; ++i )
1730  {
1731  double x = *srcX++;
1732  double y = *srcY++;
1733  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
1734  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
1735 
1736  if ( filter( QgsPoint( x, y, z, m ) ) )
1737  {
1738  filteredPoints++;
1739  *destX++ = x;
1740  *destY++ = y;
1741  if ( hasM )
1742  *destM++ = m;
1743  if ( hasZ )
1744  *destZ++ = z;
1745  }
1746  }
1747 
1748  mX.resize( filteredPoints );
1749  mY.resize( filteredPoints );
1750  if ( hasZ )
1751  mZ.resize( filteredPoints );
1752  if ( hasM )
1753  mM.resize( filteredPoints );
1754 
1755  clearCache();
1756 }
1757 
1758 void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1759 {
1760  bool hasZ = is3D();
1761  bool hasM = isMeasure();
1762  int size = mX.size();
1763 
1764  double *srcX = mX.data();
1765  double *srcY = mY.data();
1766  double *srcM = hasM ? mM.data() : nullptr;
1767  double *srcZ = hasZ ? mZ.data() : nullptr;
1768 
1769  for ( int i = 0; i < size; ++i )
1770  {
1771  double x = *srcX;
1772  double y = *srcY;
1773  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
1774  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
1775  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
1776  *srcX++ = res.x();
1777  *srcY++ = res.y();
1778  if ( hasM )
1779  *srcM++ = res.m();
1780  if ( hasZ )
1781  *srcZ++ = res.z();
1782  }
1783  clearCache();
1784 }
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
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
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.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values...
void setY(double y) SIP_HOLDGIL
Sets the point&#39;s y-coordinate.
Definition: qgspoint.h:280
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
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 (...
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
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
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
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
double startY() const SIP_HOLDGIL
Returns the segment&#39;s start y-coordinate.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
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.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
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:257
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
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.
int vertex
Vertex number.
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.
int part
Part number.
QgsWkbTypes::Type mWkbType
QgsLineString() SIP_HOLDGIL
Constructor for an empty linestring geometry.
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 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, y1) to (x2, y2) and (x2, y2) to (x3, y3).
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...
double length3D() const SIP_HOLDGIL
Returns the length in 3D world of the line string.
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...
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 bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
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:69
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void setX(double x) SIP_HOLDGIL
Sets the point&#39;s x-coordinate.
Definition: qgspoint.h:269
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
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)...
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.
VertexType
Type of vertex.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex...
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
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...
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
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
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
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
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.
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.
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.
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...
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...
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.
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
The actual start or end point of a segment.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
QVector< QgsPoint > QgsPointSequence
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
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...
double endY() const SIP_HOLDGIL
Returns the segment&#39;s end y-coordinate.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
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
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
double startX() const SIP_HOLDGIL
Returns the segment&#39;s start x-coordinate.
Class for doing transforms between two map coordinate systems.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
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.
virtual bool convertTo(QgsWkbTypes::Type type)
Converts the geometry to a specified type.
double z
Definition: qgspoint.h:43
int nCoordinates() const override SIP_HOLDGIL
Returns the number of nodes contained in the geometry.
double endX() const SIP_HOLDGIL
Returns the segment&#39;s end x-coordinate.
Compound curve geometry type.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
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...
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...
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negative values mean left ...
Definition: MathUtils.cpp:292
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
int ring
Ring number.
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
virtual bool isRing() const SIP_HOLDGIL
Returns true if the curve is a ring.
Definition: qgscurve.cpp:56