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