QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgslinestring.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslinestring.cpp
3  -------------------
4  begin : September 2014
5  copyright : (C) 2014 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgslinestring.h"
19 #include "qgsapplication.h"
20 #include "qgscompoundcurve.h"
21 #include "qgscoordinatetransform.h"
22 #include "qgsgeometryutils.h"
23 #include "qgsmaptopixel.h"
24 #include "qgswkbptr.h"
25 #include "qgslinesegment.h"
26 
27 #include <nlohmann/json.hpp>
28 #include <cmath>
29 #include <memory>
30 #include <QPainter>
31 #include <limits>
32 #include <QDomDocument>
33 #include <QJsonObject>
34 
35 
36 /***************************************************************************
37  * This class is considered CRITICAL and any change MUST be accompanied with
38  * full unit tests.
39  * See details in QEP #17
40  ****************************************************************************/
41 
43 {
45 }
46 
47 QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
48 {
49  if ( points.isEmpty() )
50  {
52  return;
53  }
54  QgsWkbTypes::Type ptType = points.at( 0 ).wkbType();
56  mX.resize( points.count() );
57  mY.resize( points.count() );
58  double *x = mX.data();
59  double *y = mY.data();
60  double *z = nullptr;
61  double *m = nullptr;
62  if ( QgsWkbTypes::hasZ( mWkbType ) )
63  {
64  mZ.resize( points.count() );
65  z = mZ.data();
66  }
67  if ( QgsWkbTypes::hasM( mWkbType ) )
68  {
69  mM.resize( points.count() );
70  m = mM.data();
71  }
72 
73  for ( const QgsPoint &pt : points )
74  {
75  *x++ = pt.x();
76  *y++ = pt.y();
77  if ( z )
78  *z++ = pt.z();
79  if ( m )
80  *m++ = pt.m();
81  }
82 }
83 
84 QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
85 {
87  int pointCount = std::min( x.size(), y.size() );
88  if ( x.size() == pointCount )
89  {
90  mX = x;
91  }
92  else
93  {
94  mX = x.mid( 0, pointCount );
95  }
96  if ( y.size() == pointCount )
97  {
98  mY = y;
99  }
100  else
101  {
102  mY = y.mid( 0, pointCount );
103  }
104  if ( !z.isEmpty() && z.count() >= pointCount )
105  {
107  if ( z.size() == pointCount )
108  {
109  mZ = z;
110  }
111  else
112  {
113  mZ = z.mid( 0, pointCount );
114  }
115  }
116  if ( !m.isEmpty() && m.count() >= pointCount )
117  {
119  if ( m.size() == pointCount )
120  {
121  mM = m;
122  }
123  else
124  {
125  mM = m.mid( 0, pointCount );
126  }
127  }
128 }
129 
131 {
133  mX.resize( 2 );
134  mX[ 0 ] = p1.x();
135  mX[ 1 ] = p2.x();
136  mY.resize( 2 );
137  mY[ 0 ] = p1.y();
138  mY[ 1 ] = p2.y();
139  if ( p1.is3D() )
140  {
142  mZ.resize( 2 );
143  mZ[ 0 ] = p1.z();
144  mZ[ 1 ] = p2.z();
145  }
146  if ( p1.isMeasure() )
147  {
149  mM.resize( 2 );
150  mM[ 0 ] = p1.m();
151  mM[ 1 ] = p2.m();
152  }
153 }
154 
155 QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
156 {
158  mX.reserve( points.size() );
159  mY.reserve( points.size() );
160  for ( const QgsPointXY &p : points )
161  {
162  mX << p.x();
163  mY << p.y();
164  }
165 }
166 
168 {
170  mX.resize( 2 );
171  mY.resize( 2 );
172  mX[0] = segment.startX();
173  mX[1] = segment.endX();
174  mY[0] = segment.startY();
175  mY[1] = segment.endY();
176 }
177 
178 bool QgsLineString::equals( const QgsCurve &other ) const
179 {
180  const QgsLineString *otherLine = qgsgeometry_cast< const QgsLineString * >( &other );
181  if ( !otherLine )
182  return false;
183 
184  if ( mWkbType != otherLine->mWkbType )
185  return false;
186 
187  if ( mX.count() != otherLine->mX.count() )
188  return false;
189 
190  for ( int i = 0; i < mX.count(); ++i )
191  {
192  if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
193  || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
194  return false;
195 
196  if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
197  return false;
198 
199  if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
200  return false;
201  }
202 
203  return true;
204 }
205 
207 {
208  return new QgsLineString( *this );
209 }
210 
212 {
213  mX.clear();
214  mY.clear();
215  mZ.clear();
216  mM.clear();
218  clearCache();
219 }
220 
222 {
223  return mX.isEmpty();
224 }
225 
226 QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
227 {
228  // prepare result
229  std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
230 
231  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
232  result->mX, result->mY, result->mZ, result->mM );
233  if ( res )
234  return result.release();
235  else
236  return nullptr;
237 }
238 
239 bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
240 {
241  if ( mX.count() <= 2 )
242  return false; // don't create degenerate lines
243  bool result = false;
244  double prevX = mX.at( 0 );
245  double prevY = mY.at( 0 );
246  bool hasZ = is3D();
247  bool useZ = hasZ && useZValues;
248  double prevZ = useZ ? mZ.at( 0 ) : 0;
249  int i = 1;
250  int remaining = mX.count();
251  while ( i < remaining )
252  {
253  double currentX = mX.at( i );
254  double currentY = mY.at( i );
255  double currentZ = useZ ? mZ.at( i ) : 0;
256  if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
257  qgsDoubleNear( currentY, prevY, epsilon ) &&
258  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
259  {
260  result = true;
261  // remove point
262  mX.removeAt( i );
263  mY.removeAt( i );
264  if ( hasZ )
265  mZ.removeAt( i );
266  remaining--;
267  }
268  else
269  {
270  prevX = currentX;
271  prevY = currentY;
272  prevZ = currentZ;
273  i++;
274  }
275  }
276  return result;
277 }
278 
279 QPolygonF QgsLineString::asQPolygonF() const
280 {
281  const int nb = mX.size();
282  QPolygonF points( nb );
283 
284  const double *x = mX.constData();
285  const double *y = mY.constData();
286  QPointF *dest = points.data();
287  for ( int i = 0; i < nb; ++i )
288  {
289  *dest++ = QPointF( *x++, *y++ );
290  }
291  return points;
292 }
293 
295 {
296  if ( !wkbPtr )
297  {
298  return false;
299  }
300 
301  QgsWkbTypes::Type type = wkbPtr.readHeader();
303  {
304  return false;
305  }
306  mWkbType = type;
307  importVerticesFromWkb( wkbPtr );
308  return true;
309 }
310 
312 {
313  double xmin = std::numeric_limits<double>::max();
314  double ymin = std::numeric_limits<double>::max();
315  double xmax = -std::numeric_limits<double>::max();
316  double ymax = -std::numeric_limits<double>::max();
317 
318  for ( double x : mX )
319  {
320  if ( x < xmin )
321  xmin = x;
322  if ( x > xmax )
323  xmax = x;
324  }
325  for ( double y : mY )
326  {
327  if ( y < ymin )
328  ymin = y;
329  if ( y > ymax )
330  ymax = y;
331  }
332  return QgsRectangle( xmin, ymin, xmax, ymax );
333 }
334 
335 /***************************************************************************
336  * This class is considered CRITICAL and any change MUST be accompanied with
337  * full unit tests.
338  * See details in QEP #17
339  ****************************************************************************/
340 
341 bool QgsLineString::fromWkt( const QString &wkt )
342 {
343  clear();
344 
345  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
346 
347  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::LineString )
348  return false;
349  mWkbType = parts.first;
350 
351  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
352  return true;
353 }
354 
355 QByteArray QgsLineString::asWkb() const
356 {
357  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
358  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
359 
360  QByteArray wkbArray;
361  wkbArray.resize( binarySize );
362  QgsWkbPtr wkb( wkbArray );
363  wkb << static_cast<char>( QgsApplication::endian() );
364  wkb << static_cast<quint32>( wkbType() );
365  QgsPointSequence pts;
366  points( pts );
367  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
368  return wkbArray;
369 }
370 
371 /***************************************************************************
372  * This class is considered CRITICAL and any change MUST be accompanied with
373  * full unit tests.
374  * See details in QEP #17
375  ****************************************************************************/
376 
377 QString QgsLineString::asWkt( int precision ) const
378 {
379  QString wkt = wktTypeStr() + ' ';
380  QgsPointSequence pts;
381  points( pts );
382  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
383  return wkt;
384 }
385 
386 QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
387 {
388  QgsPointSequence pts;
389  points( pts );
390 
391  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
392 
393  if ( isEmpty() )
394  return elemLineString;
395 
396  elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
397 
398  return elemLineString;
399 }
400 
401 QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
402 {
403  QgsPointSequence pts;
404  points( pts );
405 
406  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
407 
408  if ( isEmpty() )
409  return elemLineString;
410 
411  elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
412  return elemLineString;
413 }
414 
416 {
417  QgsPointSequence pts;
418  points( pts );
419  return
420  {
421  { "type", "LineString" },
422  { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
423  };
424 }
425 
426 /***************************************************************************
427  * This class is considered CRITICAL and any change MUST be accompanied with
428  * full unit tests.
429  * See details in QEP #17
430  ****************************************************************************/
431 
432 double QgsLineString::length() const
433 {
434  double length = 0;
435  int size = mX.size();
436  double dx, dy;
437  for ( int i = 1; i < size; ++i )
438  {
439  dx = mX.at( i ) - mX.at( i - 1 );
440  dy = mY.at( i ) - mY.at( i - 1 );
441  length += std::sqrt( dx * dx + dy * dy );
442  }
443  return length;
444 }
445 
447 {
448  if ( numPoints() < 1 )
449  {
450  return QgsPoint();
451  }
452  return pointN( 0 );
453 }
454 
456 {
457  if ( numPoints() < 1 )
458  {
459  return QgsPoint();
460  }
461  return pointN( numPoints() - 1 );
462 }
463 
464 /***************************************************************************
465  * This class is considered CRITICAL and any change MUST be accompanied with
466  * full unit tests.
467  * See details in QEP #17
468  ****************************************************************************/
469 
470 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
471 {
472  Q_UNUSED( tolerance )
473  Q_UNUSED( toleranceType )
474  return clone();
475 }
476 
478 {
479  return mX.size();
480 }
481 
483 {
484  return mX.size();
485 }
486 
488 {
489  if ( i < 0 || i >= mX.size() )
490  {
491  return QgsPoint();
492  }
493 
494  double x = mX.at( i );
495  double y = mY.at( i );
496  double z = std::numeric_limits<double>::quiet_NaN();
497  double m = std::numeric_limits<double>::quiet_NaN();
498 
499  bool hasZ = is3D();
500  if ( hasZ )
501  {
502  z = mZ.at( i );
503  }
504  bool hasM = isMeasure();
505  if ( hasM )
506  {
507  m = mM.at( i );
508  }
509 
512  {
514  }
515  else if ( hasZ && hasM )
516  {
518  }
519  else if ( hasZ )
520  {
522  }
523  else if ( hasM )
524  {
526  }
527  return QgsPoint( t, x, y, z, m );
528 }
529 
530 /***************************************************************************
531  * This class is considered CRITICAL and any change MUST be accompanied with
532  * full unit tests.
533  * See details in QEP #17
534  ****************************************************************************/
535 
536 double QgsLineString::xAt( int index ) const
537 {
538  if ( index >= 0 && index < mX.size() )
539  return mX.at( index );
540  else
541  return 0.0;
542 }
543 
544 double QgsLineString::yAt( int index ) const
545 {
546  if ( index >= 0 && index < mY.size() )
547  return mY.at( index );
548  else
549  return 0.0;
550 }
551 
552 void QgsLineString::setXAt( int index, double x )
553 {
554  if ( index >= 0 && index < mX.size() )
555  mX[ index ] = x;
556  clearCache();
557 }
558 
559 void QgsLineString::setYAt( int index, double y )
560 {
561  if ( index >= 0 && index < mY.size() )
562  mY[ index ] = y;
563  clearCache();
564 }
565 
566 /***************************************************************************
567  * This class is considered CRITICAL and any change MUST be accompanied with
568  * full unit tests.
569  * See details in QEP #17
570  ****************************************************************************/
571 
573 {
574  pts.clear();
575  int nPoints = numPoints();
576  for ( int i = 0; i < nPoints; ++i )
577  {
578  pts.push_back( pointN( i ) );
579  }
580 }
581 
583 {
584  clearCache(); //set bounding box invalid
585 
586  if ( points.isEmpty() )
587  {
588  clear();
589  return;
590  }
591 
592  //get wkb type from first point
593  const QgsPoint &firstPt = points.at( 0 );
594  bool hasZ = firstPt.is3D();
595  bool hasM = firstPt.isMeasure();
596 
598 
599  mX.resize( points.size() );
600  mY.resize( points.size() );
601  if ( hasZ )
602  {
603  mZ.resize( points.size() );
604  }
605  else
606  {
607  mZ.clear();
608  }
609  if ( hasM )
610  {
611  mM.resize( points.size() );
612  }
613  else
614  {
615  mM.clear();
616  }
617 
618  for ( int i = 0; i < points.size(); ++i )
619  {
620  mX[i] = points.at( i ).x();
621  mY[i] = points.at( i ).y();
622  if ( hasZ )
623  {
624  double z = points.at( i ).z();
625  mZ[i] = std::isnan( z ) ? 0 : z;
626  }
627  if ( hasM )
628  {
629  double m = points.at( i ).m();
630  mM[i] = std::isnan( m ) ? 0 : m;
631  }
632  }
633 }
634 
635 /***************************************************************************
636  * This class is considered CRITICAL and any change MUST be accompanied with
637  * full unit tests.
638  * See details in QEP #17
639  ****************************************************************************/
640 
642 {
643  if ( !line )
644  {
645  return;
646  }
647 
648  if ( numPoints() < 1 )
649  {
651  }
652 
653  // do not store duplicit points
654  if ( numPoints() > 0 &&
655  line->numPoints() > 0 &&
656  endPoint() == line->startPoint() )
657  {
658  mX.pop_back();
659  mY.pop_back();
660 
661  if ( is3D() )
662  {
663  mZ.pop_back();
664  }
665  if ( isMeasure() )
666  {
667  mM.pop_back();
668  }
669  }
670 
671  mX += line->mX;
672  mY += line->mY;
673 
674  if ( is3D() )
675  {
676  if ( line->is3D() )
677  {
678  mZ += line->mZ;
679  }
680  else
681  {
682  // if append line does not have z coordinates, fill with NaN to match number of points in final line
683  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
684  }
685  }
686 
687  if ( isMeasure() )
688  {
689  if ( line->isMeasure() )
690  {
691  mM += line->mM;
692  }
693  else
694  {
695  // if append line does not have m values, fill with NaN to match number of points in final line
696  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
697  }
698  }
699 
700  clearCache(); //set bounding box invalid
701 }
702 
704 {
705  QgsLineString *copy = clone();
706  std::reverse( copy->mX.begin(), copy->mX.end() );
707  std::reverse( copy->mY.begin(), copy->mY.end() );
708  if ( copy->is3D() )
709  {
710  std::reverse( copy->mZ.begin(), copy->mZ.end() );
711  }
712  if ( copy->isMeasure() )
713  {
714  std::reverse( copy->mM.begin(), copy->mM.end() );
715  }
716  return copy;
717 }
718 
719 QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
720 {
721  if ( distance < 0 )
722  return nullptr;
723 
724  double distanceTraversed = 0;
725  const int totalPoints = numPoints();
726  if ( totalPoints == 0 )
727  return nullptr;
728 
729  const double *x = mX.constData();
730  const double *y = mY.constData();
731  const double *z = is3D() ? mZ.constData() : nullptr;
732  const double *m = isMeasure() ? mM.constData() : nullptr;
733 
735  if ( is3D() )
736  pointType = QgsWkbTypes::PointZ;
737  if ( isMeasure() )
738  pointType = QgsWkbTypes::addM( pointType );
739 
740  double prevX = *x++;
741  double prevY = *y++;
742  double prevZ = z ? *z++ : 0.0;
743  double prevM = m ? *m++ : 0.0;
744 
745  if ( qgsDoubleNear( distance, 0.0 ) )
746  {
747  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
748  }
749 
750  for ( int i = 1; i < totalPoints; ++i )
751  {
752  double thisX = *x++;
753  double thisY = *y++;
754  double thisZ = z ? *z++ : 0.0;
755  double thisM = m ? *m++ : 0.0;
756 
757  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
758  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
759  {
760  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
761  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
762  double pX, pY;
763  double pZ = 0;
764  double pM = 0;
765  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
766  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
767  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
768  return new QgsPoint( pointType, pX, pY, pZ, pM );
769  }
770 
771  distanceTraversed += segmentLength;
772  prevX = thisX;
773  prevY = thisY;
774  prevZ = thisZ;
775  prevM = thisM;
776  }
777 
778  return nullptr;
779 }
780 
781 QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
782 {
783  if ( startDistance < 0 && endDistance < 0 )
784  return createEmptyWithSameType();
785 
786  endDistance = std::max( startDistance, endDistance );
787 
788  double distanceTraversed = 0;
789  const int totalPoints = numPoints();
790  if ( totalPoints == 0 )
791  return clone();
792 
793  QVector< QgsPoint > substringPoints;
794 
796  if ( is3D() )
797  pointType = QgsWkbTypes::PointZ;
798  if ( isMeasure() )
799  pointType = QgsWkbTypes::addM( pointType );
800 
801  const double *x = mX.constData();
802  const double *y = mY.constData();
803  const double *z = is3D() ? mZ.constData() : nullptr;
804  const double *m = isMeasure() ? mM.constData() : nullptr;
805 
806  double prevX = *x++;
807  double prevY = *y++;
808  double prevZ = z ? *z++ : 0.0;
809  double prevM = m ? *m++ : 0.0;
810  bool foundStart = false;
811 
812  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
813  {
814  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
815  foundStart = true;
816  }
817 
818  substringPoints.reserve( totalPoints );
819 
820  for ( int i = 1; i < totalPoints; ++i )
821  {
822  double thisX = *x++;
823  double thisY = *y++;
824  double thisZ = z ? *z++ : 0.0;
825  double thisM = m ? *m++ : 0.0;
826 
827  const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
828  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
829  {
830  // start point falls on this segment
831  const double distanceToStart = startDistance - distanceTraversed;
832  double startX, startY;
833  double startZ = 0;
834  double startM = 0;
835  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
836  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
837  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
838  substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
839  foundStart = true;
840  }
841  if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
842  {
843  // end point falls on this segment
844  const double distanceToEnd = endDistance - distanceTraversed;
845  double endX, endY;
846  double endZ = 0;
847  double endM = 0;
848  QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
849  z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
850  m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
851  substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
852  }
853  else if ( foundStart )
854  {
855  substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
856  }
857 
858  distanceTraversed += segmentLength;
859  if ( distanceTraversed > endDistance )
860  break;
861 
862  prevX = thisX;
863  prevY = thisY;
864  prevZ = thisZ;
865  prevM = thisM;
866  }
867 
868  return new QgsLineString( substringPoints );
869 }
870 
871 /***************************************************************************
872  * This class is considered CRITICAL and any change MUST be accompanied with
873  * full unit tests.
874  * See details in QEP #17
875  ****************************************************************************/
876 
877 void QgsLineString::draw( QPainter &p ) const
878 {
879  p.drawPolyline( asQPolygonF() );
880 }
881 
882 void QgsLineString::addToPainterPath( QPainterPath &path ) const
883 {
884  int nPoints = numPoints();
885  if ( nPoints < 1 )
886  {
887  return;
888  }
889 
890  if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
891  {
892  path.moveTo( mX.at( 0 ), mY.at( 0 ) );
893  }
894 
895  for ( int i = 1; i < nPoints; ++i )
896  {
897  path.lineTo( mX.at( i ), mY.at( i ) );
898  }
899 }
900 
901 void QgsLineString::drawAsPolygon( QPainter &p ) const
902 {
903  p.drawPolygon( asQPolygonF() );
904 }
905 
907 {
908  QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
909  compoundCurve->addCurve( clone() );
910  return compoundCurve;
911 }
912 
913 void QgsLineString::extend( double startDistance, double endDistance )
914 {
915  if ( mX.size() < 2 || mY.size() < 2 )
916  return;
917 
918  // start of line
919  if ( startDistance > 0 )
920  {
921  double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
922  std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
923  double newLen = currentLen + startDistance;
924  mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
925  mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
926  }
927  // end of line
928  if ( endDistance > 0 )
929  {
930  int last = mX.size() - 1;
931  double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
932  std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
933  double newLen = currentLen + endDistance;
934  mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
935  mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
936  }
937 }
938 
940 {
941  auto result = qgis::make_unique< QgsLineString >();
942  result->mWkbType = mWkbType;
943  return result.release();
944 }
945 
947 {
948  return QStringLiteral( "LineString" );
949 }
950 
952 {
953  return 1;
954 }
955 
956 /***************************************************************************
957  * This class is considered CRITICAL and any change MUST be accompanied with
958  * full unit tests.
959  * See details in QEP #17
960  ****************************************************************************/
961 
963 {
964  double *zArray = nullptr;
965  bool hasZ = is3D();
966  int nPoints = numPoints();
967 
968  // it's possible that transformCoords will throw an exception - so we need to use
969  // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
970  std::unique_ptr< double[] > dummyZ;
971  if ( !hasZ || !transformZ )
972  {
973  dummyZ.reset( new double[nPoints]() );
974  zArray = dummyZ.get();
975  }
976  else
977  {
978  zArray = mZ.data();
979  }
980  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
981  clearCache();
982 }
983 
984 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
985 {
986  int nPoints = numPoints();
987  bool hasZ = is3D();
988  bool hasM = isMeasure();
989  for ( int i = 0; i < nPoints; ++i )
990  {
991  qreal x, y;
992  t.map( mX.at( i ), mY.at( i ), &x, &y );
993  mX[i] = x;
994  mY[i] = y;
995  if ( hasZ )
996  {
997  mZ[i] = mZ.at( i ) * zScale + zTranslate;
998  }
999  if ( hasM )
1000  {
1001  mM[i] = mM.at( i ) * mScale + mTranslate;
1002  }
1003  }
1004  clearCache();
1005 }
1006 
1007 /***************************************************************************
1008  * This class is considered CRITICAL and any change MUST be accompanied with
1009  * full unit tests.
1010  * See details in QEP #17
1011  ****************************************************************************/
1012 
1013 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1014 {
1015  if ( position.vertex < 0 || position.vertex > mX.size() )
1016  {
1017  return false;
1018  }
1019 
1020  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1021  {
1023  }
1024 
1025  mX.insert( position.vertex, vertex.x() );
1026  mY.insert( position.vertex, vertex.y() );
1027  if ( is3D() )
1028  {
1029  mZ.insert( position.vertex, vertex.z() );
1030  }
1031  if ( isMeasure() )
1032  {
1033  mM.insert( position.vertex, vertex.m() );
1034  }
1035  clearCache(); //set bounding box invalid
1036  return true;
1037 }
1038 
1039 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1040 {
1041  if ( position.vertex < 0 || position.vertex >= mX.size() )
1042  {
1043  return false;
1044  }
1045  mX[position.vertex] = newPos.x();
1046  mY[position.vertex] = newPos.y();
1047  if ( is3D() && newPos.is3D() )
1048  {
1049  mZ[position.vertex] = newPos.z();
1050  }
1051  if ( isMeasure() && newPos.isMeasure() )
1052  {
1053  mM[position.vertex] = newPos.m();
1054  }
1055  clearCache(); //set bounding box invalid
1056  return true;
1057 }
1058 
1060 {
1061  if ( position.vertex >= mX.size() || position.vertex < 0 )
1062  {
1063  return false;
1064  }
1065 
1066  mX.remove( position.vertex );
1067  mY.remove( position.vertex );
1068  if ( is3D() )
1069  {
1070  mZ.remove( position.vertex );
1071  }
1072  if ( isMeasure() )
1073  {
1074  mM.remove( position.vertex );
1075  }
1076 
1077  if ( numPoints() == 1 )
1078  {
1079  clear();
1080  }
1081 
1082  clearCache(); //set bounding box invalid
1083  return true;
1084 }
1085 
1086 /***************************************************************************
1087  * This class is considered CRITICAL and any change MUST be accompanied with
1088  * full unit tests.
1089  * See details in QEP #17
1090  ****************************************************************************/
1091 
1093 {
1094  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1095  {
1097  }
1098 
1099  mX.append( pt.x() );
1100  mY.append( pt.y() );
1101  if ( is3D() )
1102  {
1103  mZ.append( pt.z() );
1104  }
1105  if ( isMeasure() )
1106  {
1107  mM.append( pt.m() );
1108  }
1109  clearCache(); //set bounding box invalid
1110 }
1111 
1112 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1113 {
1114  double sqrDist = std::numeric_limits<double>::max();
1115  double leftOfDist = std::numeric_limits<double>::max();
1116  int prevLeftOf = 0;
1117  double prevLeftOfX = 0.0;
1118  double prevLeftOfY = 0.0;
1119  double testDist = 0;
1120  double segmentPtX, segmentPtY;
1121 
1122  if ( leftOf )
1123  *leftOf = 0;
1124 
1125  int size = mX.size();
1126  if ( size == 0 || size == 1 )
1127  {
1128  vertexAfter = QgsVertexId( 0, 0, 0 );
1129  return -1;
1130  }
1131  for ( int i = 1; i < size; ++i )
1132  {
1133  double prevX = mX.at( i - 1 );
1134  double prevY = mY.at( i - 1 );
1135  double currentX = mX.at( i );
1136  double currentY = mY.at( i );
1137  testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1138  if ( testDist < sqrDist )
1139  {
1140  sqrDist = testDist;
1141  segmentPt.setX( segmentPtX );
1142  segmentPt.setY( segmentPtY );
1143  vertexAfter.part = 0;
1144  vertexAfter.ring = 0;
1145  vertexAfter.vertex = i;
1146  }
1147  if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1148  {
1149  int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1150  // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1151  // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1152  // where we can perform the check
1153  if ( left != 0 )
1154  {
1155  if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1156  {
1157  // we have two possible segments each with equal distance to point, but they disagree
1158  // on whether or not the point is to the left of them.
1159  // so we test the segments themselves and flip the result.
1160  // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1161  *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1162  }
1163  else
1164  {
1165  *leftOf = left;
1166  }
1167  prevLeftOf = *leftOf;
1168  leftOfDist = testDist;
1169  prevLeftOfX = prevX;
1170  prevLeftOfY = prevY;
1171  }
1172  else if ( testDist < leftOfDist )
1173  {
1174  *leftOf = left;
1175  leftOfDist = testDist;
1176  prevLeftOf = 0;
1177  }
1178  }
1179  }
1180  return sqrDist;
1181 }
1182 
1183 /***************************************************************************
1184  * This class is considered CRITICAL and any change MUST be accompanied with
1185  * full unit tests.
1186  * See details in QEP #17
1187  ****************************************************************************/
1188 
1189 bool QgsLineString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1190 {
1191  if ( node < 0 || node >= numPoints() )
1192  {
1193  return false;
1194  }
1195  point = pointN( node );
1197  return true;
1198 }
1199 
1201 {
1202  if ( mX.isEmpty() )
1203  return QgsPoint();
1204 
1205  int numPoints = mX.count();
1206  if ( numPoints == 1 )
1207  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1208 
1209  double totalLineLength = 0.0;
1210  double prevX = mX.at( 0 );
1211  double prevY = mY.at( 0 );
1212  double sumX = 0.0;
1213  double sumY = 0.0;
1214 
1215  for ( int i = 1; i < numPoints ; ++i )
1216  {
1217  double currentX = mX.at( i );
1218  double currentY = mY.at( i );
1219  double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1220  std::pow( currentY - prevY, 2.0 ) );
1221  if ( qgsDoubleNear( segmentLength, 0.0 ) )
1222  continue;
1223 
1224  totalLineLength += segmentLength;
1225  sumX += segmentLength * 0.5 * ( currentX + prevX );
1226  sumY += segmentLength * 0.5 * ( currentY + prevY );
1227  prevX = currentX;
1228  prevY = currentY;
1229  }
1230 
1231  if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1232  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1233  else
1234  return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1235 
1236 }
1237 
1238 /***************************************************************************
1239  * This class is considered CRITICAL and any change MUST be accompanied with
1240  * full unit tests.
1241  * See details in QEP #17
1242  ****************************************************************************/
1243 
1244 void QgsLineString::sumUpArea( double &sum ) const
1245 {
1246  int maxIndex = numPoints() - 1;
1247 
1248  for ( int i = 0; i < maxIndex; ++i )
1249  {
1250  sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1251  }
1252 }
1253 
1254 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1255 {
1256  bool hasZ = is3D();
1257  bool hasM = isMeasure();
1258  int nVertices = 0;
1259  wkb >> nVertices;
1260  mX.resize( nVertices );
1261  mY.resize( nVertices );
1262  hasZ ? mZ.resize( nVertices ) : mZ.clear();
1263  hasM ? mM.resize( nVertices ) : mM.clear();
1264  double *x = mX.data();
1265  double *y = mY.data();
1266  double *m = hasM ? mM.data() : nullptr;
1267  double *z = hasZ ? mZ.data() : nullptr;
1268  for ( int i = 0; i < nVertices; ++i )
1269  {
1270  wkb >> *x++;
1271  wkb >> *y++;
1272  if ( hasZ )
1273  {
1274  wkb >> *z++;
1275  }
1276  if ( hasM )
1277  {
1278  wkb >> *m++;
1279  }
1280  }
1281  clearCache(); //set bounding box invalid
1282 }
1283 
1284 /***************************************************************************
1285  * This class is considered CRITICAL and any change MUST be accompanied with
1286  * full unit tests.
1287  * See details in QEP #17
1288  ****************************************************************************/
1289 
1291 {
1292  if ( numPoints() < 1 || isClosed() )
1293  {
1294  return;
1295  }
1296  addVertex( startPoint() );
1297 }
1298 
1300 {
1301  if ( mX.count() < 2 )
1302  {
1303  //undefined
1304  return 0.0;
1305  }
1306 
1307  if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1308  {
1309  if ( isClosed() )
1310  {
1311  double previousX = mX.at( numPoints() - 2 );
1312  double previousY = mY.at( numPoints() - 2 );
1313  double currentX = mX.at( 0 );
1314  double currentY = mY.at( 0 );
1315  double afterX = mX.at( 1 );
1316  double afterY = mY.at( 1 );
1317  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1318  }
1319  else if ( vertex.vertex == 0 )
1320  {
1321  return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1322  }
1323  else
1324  {
1325  int a = numPoints() - 2;
1326  int b = numPoints() - 1;
1327  return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1328  }
1329  }
1330  else
1331  {
1332  double previousX = mX.at( vertex.vertex - 1 );
1333  double previousY = mY.at( vertex.vertex - 1 );
1334  double currentX = mX.at( vertex.vertex );
1335  double currentY = mY.at( vertex.vertex );
1336  double afterX = mX.at( vertex.vertex + 1 );
1337  double afterY = mY.at( vertex.vertex + 1 );
1338  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1339  }
1340 }
1341 
1342 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1343 {
1344  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1345  return 0.0;
1346 
1347  double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1348  double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1349  return std::sqrt( dx * dx + dy * dy );
1350 }
1351 
1352 /***************************************************************************
1353  * This class is considered CRITICAL and any change MUST be accompanied with
1354  * full unit tests.
1355  * See details in QEP #17
1356  ****************************************************************************/
1357 
1358 bool QgsLineString::addZValue( double zValue )
1359 {
1360  if ( QgsWkbTypes::hasZ( mWkbType ) )
1361  return false;
1362 
1363  clearCache();
1364  if ( mWkbType == QgsWkbTypes::Unknown )
1365  {
1367  return true;
1368  }
1369 
1371 
1372  mZ.clear();
1373  int nPoints = numPoints();
1374  mZ.reserve( nPoints );
1375  for ( int i = 0; i < nPoints; ++i )
1376  {
1377  mZ << zValue;
1378  }
1379  return true;
1380 }
1381 
1382 bool QgsLineString::addMValue( double mValue )
1383 {
1384  if ( QgsWkbTypes::hasM( mWkbType ) )
1385  return false;
1386 
1387  clearCache();
1388  if ( mWkbType == QgsWkbTypes::Unknown )
1389  {
1391  return true;
1392  }
1393 
1395  {
1397  }
1398  else
1399  {
1401  }
1402 
1403  mM.clear();
1404  int nPoints = numPoints();
1405  mM.reserve( nPoints );
1406  for ( int i = 0; i < nPoints; ++i )
1407  {
1408  mM << mValue;
1409  }
1410  return true;
1411 }
1412 
1414 {
1415  if ( !is3D() )
1416  return false;
1417 
1418  clearCache();
1420  mZ.clear();
1421  return true;
1422 }
1423 
1425 {
1426  if ( !isMeasure() )
1427  return false;
1428 
1429  clearCache();
1431  mM.clear();
1432  return true;
1433 }
1434 
1436 {
1437  std::swap( mX, mY );
1438  clearCache();
1439 }
1440 
1442 {
1443  if ( type == mWkbType )
1444  return true;
1445 
1446  clearCache();
1447  if ( type == QgsWkbTypes::LineString25D )
1448  {
1449  //special handling required for conversion to LineString25D
1450  dropMValue();
1451  addZValue( std::numeric_limits<double>::quiet_NaN() );
1453  return true;
1454  }
1455  else
1456  {
1457  return QgsCurve::convertTo( type );
1458  }
1459 }
1460 
1461 void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1462 {
1463  bool hasZ = is3D();
1464  bool hasM = isMeasure();
1465  int size = mX.size();
1466 
1467  double *srcX = mX.data();
1468  double *srcY = mY.data();
1469  double *srcM = hasM ? mM.data() : nullptr;
1470  double *srcZ = hasZ ? mZ.data() : nullptr;
1471 
1472  double *destX = srcX;
1473  double *destY = srcY;
1474  double *destM = srcM;
1475  double *destZ = srcZ;
1476 
1477  int filteredPoints = 0;
1478  for ( int i = 0; i < size; ++i )
1479  {
1480  double x = *srcX++;
1481  double y = *srcY++;
1482  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
1483  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
1484 
1485  if ( filter( QgsPoint( x, y, z, m ) ) )
1486  {
1487  filteredPoints++;
1488  *destX++ = x;
1489  *destY++ = y;
1490  if ( hasM )
1491  *destM++ = m;
1492  if ( hasZ )
1493  *destZ++ = z;
1494  }
1495  }
1496 
1497  mX.resize( filteredPoints );
1498  mY.resize( filteredPoints );
1499  if ( hasZ )
1500  mZ.resize( filteredPoints );
1501  if ( hasM )
1502  mM.resize( filteredPoints );
1503 
1504  clearCache();
1505 }
1506 
1507 void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1508 {
1509  bool hasZ = is3D();
1510  bool hasM = isMeasure();
1511  int size = mX.size();
1512 
1513  double *srcX = mX.data();
1514  double *srcY = mY.data();
1515  double *srcM = hasM ? mM.data() : nullptr;
1516  double *srcZ = hasZ ? mZ.data() : nullptr;
1517 
1518  for ( int i = 0; i < size; ++i )
1519  {
1520  double x = *srcX;
1521  double y = *srcY;
1522  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
1523  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
1524  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
1525  *srcX++ = res.x();
1526  *srcY++ = res.y();
1527  if ( hasM )
1528  *srcM++ = res.m();
1529  if ( hasZ )
1530  *srcZ++ = res.z();
1531  }
1532  clearCache();
1533 }
bool isMeasure() const
Returns true if the geometry contains m values.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
int precision
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
bool isEmpty() const override
Returns true if the geometry is empty.
double y
Definition: qgspoint.h:42
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
int dimension() const override
Returns the inherent dimension of the geometry.
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction...
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1, y1) to (x2, y2) and (x2, y2) to (x3, y3).
QString geometryType() const override
Returns a unique string representing the geometry type.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsPoint centroid() const override
Returns the centroid of the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
A class to represent a 2D point.
Definition: qgspointxy.h:43
double endY() const
Returns the segment&#39;s end y-coordinate.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void setYAt(int index, double y)
Sets the y-coordinate of the specified node in the line string.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:244
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
static endian_t endian()
Returns whether this machine uses big or little endian.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QgsPoint endPoint() const override
Returns the end point of the curve.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:771
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:941
void setXAt(int index, double x)
Sets the x-coordinate of the specified node in the line string.
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
QgsWkbTypes::Type mWkbType
int numPoints() const override
Returns the number of points in the curve.
bool convertTo(QgsWkbTypes::Type type) override
Converts the geometry to a specified type.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
QString wktTypeStr() const
Returns the WKT type string of the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
double startX() const
Returns the segment&#39;s start x-coordinate.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> ( x2, y2).
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:892
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
QPolygonF asQPolygonF() const override
Returns a QPolygonF representing the points.
Utility class for identifying a unique vertex within a geometry.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex...
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:867
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
void swapXy() override
Swaps the x and y coordinates from the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
AxisOrder
Axis order for GML generation.
QgsPoint startPoint() const override
Returns the starting point of the curve.
nlohmann::json json
Definition: qgsjsonutils.h:27
Represents a single 2D line segment, consisting of a 2D start and end vertex only.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
void setX(double x)
Sets the point&#39;s x-coordinate.
Definition: qgspoint.h:213
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM) const
Helper function for QgsCurve subclasses to snap to grids.
Definition: qgscurve.cpp:267
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
void setY(double y)
Sets the point&#39;s y-coordinate.
Definition: qgspoint.h:224
QVector< QgsPoint > QgsPointSequence
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
double endX() const
Returns the segment&#39;s end x-coordinate.
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:923
QgsLineString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid...
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Class for doing transforms between two map coordinate systems.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool dropMValue() override
Drops any measure values which exist in the geometry.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static Type zmType(Type type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:529
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
virtual bool convertTo(QgsWkbTypes::Type type)
Converts the geometry to a specified type.
double z
Definition: qgspoint.h:43
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:821
Compound curve geometry type.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
QgsLineString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
double length() const override
Returns the length of the geometry.
QgsCompoundCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type QgsCompoundCurve.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
double startY() const
Returns the segment&#39;s start y-coordinate.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:430
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negativ values mean left a...
Definition: MathUtils.cpp:292
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
double m
Definition: qgspoint.h:44
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void extend(double startDistance, double endDistance)
Extends the line geometry by extrapolating out the start or end of the line by a specified distance...
double x
Definition: qgspoint.h:41