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