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