QGIS API Documentation  3.0.2-Girona (307d082)
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 
26 #include <cmath>
27 #include <memory>
28 #include <QPainter>
29 #include <limits>
30 #include <QDomDocument>
31 
32 
33 /***************************************************************************
34  * This class is considered CRITICAL and any change MUST be accompanied with
35  * full unit tests.
36  * See details in QEP #17
37  ****************************************************************************/
38 
40 {
42 }
43 
44 QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
45 {
46  if ( points.isEmpty() )
47  {
49  return;
50  }
51  QgsWkbTypes::Type ptType = points.at( 0 ).wkbType();
53  mX.resize( points.count() );
54  mY.resize( points.count() );
55  double *x = mX.data();
56  double *y = mY.data();
57  double *z = nullptr;
58  double *m = nullptr;
59  if ( QgsWkbTypes::hasZ( mWkbType ) )
60  {
61  mZ.resize( points.count() );
62  z = mZ.data();
63  }
64  if ( QgsWkbTypes::hasM( mWkbType ) )
65  {
66  mM.resize( points.count() );
67  m = mM.data();
68  }
69 
70  for ( const QgsPoint &pt : points )
71  {
72  *x++ = pt.x();
73  *y++ = pt.y();
74  if ( z )
75  *z++ = pt.z();
76  if ( m )
77  *m++ = pt.m();
78  }
79 }
80 
81 QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m )
82 {
84  int pointCount = std::min( x.size(), y.size() );
85  if ( x.size() == pointCount )
86  {
87  mX = x;
88  }
89  else
90  {
91  mX = x.mid( 0, pointCount );
92  }
93  if ( y.size() == pointCount )
94  {
95  mY = y;
96  }
97  else
98  {
99  mY = y.mid( 0, pointCount );
100  }
101  if ( !z.isEmpty() && z.count() >= pointCount )
102  {
104  if ( z.size() == pointCount )
105  {
106  mZ = z;
107  }
108  else
109  {
110  mZ = z.mid( 0, pointCount );
111  }
112  }
113  if ( !m.isEmpty() && m.count() >= pointCount )
114  {
116  if ( m.size() == pointCount )
117  {
118  mM = m;
119  }
120  else
121  {
122  mM = m.mid( 0, pointCount );
123  }
124  }
125 }
126 
127 QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
128 {
130  mX.reserve( points.size() );
131  mY.reserve( points.size() );
132  for ( const QgsPointXY &p : points )
133  {
134  mX << p.x();
135  mY << p.y();
136  }
137 }
138 
139 bool QgsLineString::equals( const QgsCurve &other ) const
140 {
141  const QgsLineString *otherLine = qgsgeometry_cast< const QgsLineString * >( &other );
142  if ( !otherLine )
143  return false;
144 
145  if ( mWkbType != otherLine->mWkbType )
146  return false;
147 
148  if ( mX.count() != otherLine->mX.count() )
149  return false;
150 
151  for ( int i = 0; i < mX.count(); ++i )
152  {
153  if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
154  || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
155  return false;
156 
157  if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
158  return false;
159 
160  if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
161  return false;
162  }
163 
164  return true;
165 }
166 
168 {
169  return new QgsLineString( *this );
170 }
171 
173 {
174  mX.clear();
175  mY.clear();
176  mZ.clear();
177  mM.clear();
179  clearCache();
180 }
181 
183 {
184  return mX.isEmpty();
185 }
186 
187 QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
188 {
189  // prepare result
190  std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
191 
192  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
193  result->mX, result->mY, result->mZ, result->mM );
194  if ( res )
195  return result.release();
196  else
197  return nullptr;
198 }
199 
200 bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
201 {
202  if ( mX.count() <= 2 )
203  return false; // don't create degenerate lines
204  bool result = false;
205  double prevX = mX.at( 0 );
206  double prevY = mY.at( 0 );
207  bool hasZ = is3D();
208  bool useZ = hasZ && useZValues;
209  double prevZ = useZ ? mZ.at( 0 ) : 0;
210  int i = 1;
211  int remaining = mX.count();
212  while ( i < remaining )
213  {
214  double currentX = mX.at( i );
215  double currentY = mY.at( i );
216  double currentZ = useZ ? mZ.at( i ) : 0;
217  if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
218  qgsDoubleNear( currentY, prevY, epsilon ) &&
219  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
220  {
221  result = true;
222  // remove point
223  mX.removeAt( i );
224  mY.removeAt( i );
225  if ( hasZ )
226  mZ.removeAt( i );
227  remaining--;
228  }
229  else
230  {
231  prevX = currentX;
232  prevY = currentY;
233  prevZ = currentZ;
234  i++;
235  }
236  }
237  return result;
238 }
239 
241 {
242  if ( !wkbPtr )
243  {
244  return false;
245  }
246 
247  QgsWkbTypes::Type type = wkbPtr.readHeader();
249  {
250  return false;
251  }
252  mWkbType = type;
253  importVerticesFromWkb( wkbPtr );
254  return true;
255 }
256 
257 void QgsLineString::fromWkbPoints( QgsWkbTypes::Type type, const QgsConstWkbPtr &wkb )
258 {
259  mWkbType = type;
260  importVerticesFromWkb( wkb );
261 }
262 
264 {
265  double xmin = std::numeric_limits<double>::max();
266  double ymin = std::numeric_limits<double>::max();
267  double xmax = -std::numeric_limits<double>::max();
268  double ymax = -std::numeric_limits<double>::max();
269 
270  for ( double x : mX )
271  {
272  if ( x < xmin )
273  xmin = x;
274  if ( x > xmax )
275  xmax = x;
276  }
277  for ( double y : mY )
278  {
279  if ( y < ymin )
280  ymin = y;
281  if ( y > ymax )
282  ymax = y;
283  }
284  return QgsRectangle( xmin, ymin, xmax, ymax );
285 }
286 
287 /***************************************************************************
288  * This class is considered CRITICAL and any change MUST be accompanied with
289  * full unit tests.
290  * See details in QEP #17
291  ****************************************************************************/
292 
293 bool QgsLineString::fromWkt( const QString &wkt )
294 {
295  clear();
296 
297  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
298 
299  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::LineString )
300  return false;
301  mWkbType = parts.first;
302 
303  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
304  return true;
305 }
306 
307 QByteArray QgsLineString::asWkb() const
308 {
309  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
310  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
311 
312  QByteArray wkbArray;
313  wkbArray.resize( binarySize );
314  QgsWkbPtr wkb( wkbArray );
315  wkb << static_cast<char>( QgsApplication::endian() );
316  wkb << static_cast<quint32>( wkbType() );
317  QgsPointSequence pts;
318  points( pts );
319  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
320  return wkbArray;
321 }
322 
323 /***************************************************************************
324  * This class is considered CRITICAL and any change MUST be accompanied with
325  * full unit tests.
326  * See details in QEP #17
327  ****************************************************************************/
328 
329 QString QgsLineString::asWkt( int precision ) const
330 {
331  QString wkt = wktTypeStr() + ' ';
332  QgsPointSequence pts;
333  points( pts );
334  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
335  return wkt;
336 }
337 
338 QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns ) const
339 {
340  QgsPointSequence pts;
341  points( pts );
342 
343  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
344 
345  if ( isEmpty() )
346  return elemLineString;
347 
348  elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns ) );
349 
350  return elemLineString;
351 }
352 
353 QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns ) const
354 {
355  QgsPointSequence pts;
356  points( pts );
357 
358  QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
359 
360  if ( isEmpty() )
361  return elemLineString;
362 
363  elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D() ) );
364  return elemLineString;
365 }
366 
367 QString QgsLineString::asJson( int precision ) const
368 {
369  QgsPointSequence pts;
370  points( pts );
371 
372  return "{\"type\": \"LineString\", \"coordinates\": " + QgsGeometryUtils::pointsToJSON( pts, precision ) + '}';
373 }
374 
375 /***************************************************************************
376  * This class is considered CRITICAL and any change MUST be accompanied with
377  * full unit tests.
378  * See details in QEP #17
379  ****************************************************************************/
380 
381 double QgsLineString::length() const
382 {
383  double length = 0;
384  int size = mX.size();
385  double dx, dy;
386  for ( int i = 1; i < size; ++i )
387  {
388  dx = mX.at( i ) - mX.at( i - 1 );
389  dy = mY.at( i ) - mY.at( i - 1 );
390  length += std::sqrt( dx * dx + dy * dy );
391  }
392  return length;
393 }
394 
396 {
397  if ( numPoints() < 1 )
398  {
399  return QgsPoint();
400  }
401  return pointN( 0 );
402 }
403 
405 {
406  if ( numPoints() < 1 )
407  {
408  return QgsPoint();
409  }
410  return pointN( numPoints() - 1 );
411 }
412 
413 /***************************************************************************
414  * This class is considered CRITICAL and any change MUST be accompanied with
415  * full unit tests.
416  * See details in QEP #17
417  ****************************************************************************/
418 
419 QgsLineString *QgsLineString::curveToLine( double tolerance, SegmentationToleranceType toleranceType ) const
420 {
421  Q_UNUSED( tolerance );
422  Q_UNUSED( toleranceType );
423  return static_cast<QgsLineString *>( clone() );
424 }
425 
427 {
428  return mX.size();
429 }
430 
432 {
433  return mX.size();
434 }
435 
437 {
438  if ( i < 0 || i >= mX.size() )
439  {
440  return QgsPoint();
441  }
442 
443  double x = mX.at( i );
444  double y = mY.at( i );
445  double z = std::numeric_limits<double>::quiet_NaN();
446  double m = std::numeric_limits<double>::quiet_NaN();
447 
448  bool hasZ = is3D();
449  if ( hasZ )
450  {
451  z = mZ.at( i );
452  }
453  bool hasM = isMeasure();
454  if ( hasM )
455  {
456  m = mM.at( i );
457  }
458 
461  {
463  }
464  else if ( hasZ && hasM )
465  {
467  }
468  else if ( hasZ )
469  {
471  }
472  else if ( hasM )
473  {
475  }
476  return QgsPoint( t, x, y, z, m );
477 }
478 
479 /***************************************************************************
480  * This class is considered CRITICAL and any change MUST be accompanied with
481  * full unit tests.
482  * See details in QEP #17
483  ****************************************************************************/
484 
485 double QgsLineString::xAt( int index ) const
486 {
487  if ( index >= 0 && index < mX.size() )
488  return mX.at( index );
489  else
490  return 0.0;
491 }
492 
493 double QgsLineString::yAt( int index ) const
494 {
495  if ( index >= 0 && index < mY.size() )
496  return mY.at( index );
497  else
498  return 0.0;
499 }
500 
501 double QgsLineString::zAt( int index ) const
502 {
503  if ( index >= 0 && index < mZ.size() )
504  return mZ.at( index );
505  else
506  return std::numeric_limits<double>::quiet_NaN();
507 }
508 
509 double QgsLineString::mAt( int index ) const
510 {
511  if ( index >= 0 && index < mM.size() )
512  return mM.at( index );
513  else
514  return std::numeric_limits<double>::quiet_NaN();
515 }
516 
517 void QgsLineString::setXAt( int index, double x )
518 {
519  if ( index >= 0 && index < mX.size() )
520  mX[ index ] = x;
521  clearCache();
522 }
523 
524 void QgsLineString::setYAt( int index, double y )
525 {
526  if ( index >= 0 && index < mY.size() )
527  mY[ index ] = y;
528  clearCache();
529 }
530 
531 void QgsLineString::setZAt( int index, double z )
532 {
533  if ( index >= 0 && index < mZ.size() )
534  mZ[ index ] = z;
535 }
536 
537 void QgsLineString::setMAt( int index, double m )
538 {
539  if ( index >= 0 && index < mM.size() )
540  mM[ index ] = m;
541 }
542 
543 /***************************************************************************
544  * This class is considered CRITICAL and any change MUST be accompanied with
545  * full unit tests.
546  * See details in QEP #17
547  ****************************************************************************/
548 
550 {
551  pts.clear();
552  int nPoints = numPoints();
553  for ( int i = 0; i < nPoints; ++i )
554  {
555  pts.push_back( pointN( i ) );
556  }
557 }
558 
560 {
561  clearCache(); //set bounding box invalid
562 
563  if ( points.isEmpty() )
564  {
565  clear();
566  return;
567  }
568 
569  //get wkb type from first point
570  const QgsPoint &firstPt = points.at( 0 );
571  bool hasZ = firstPt.is3D();
572  bool hasM = firstPt.isMeasure();
573 
575 
576  mX.resize( points.size() );
577  mY.resize( points.size() );
578  if ( hasZ )
579  {
580  mZ.resize( points.size() );
581  }
582  else
583  {
584  mZ.clear();
585  }
586  if ( hasM )
587  {
588  mM.resize( points.size() );
589  }
590  else
591  {
592  mM.clear();
593  }
594 
595  for ( int i = 0; i < points.size(); ++i )
596  {
597  mX[i] = points.at( i ).x();
598  mY[i] = points.at( i ).y();
599  if ( hasZ )
600  {
601  double z = points.at( i ).z();
602  mZ[i] = std::isnan( z ) ? 0 : z;
603  }
604  if ( hasM )
605  {
606  double m = points.at( i ).m();
607  mM[i] = std::isnan( m ) ? 0 : m;
608  }
609  }
610 }
611 
612 /***************************************************************************
613  * This class is considered CRITICAL and any change MUST be accompanied with
614  * full unit tests.
615  * See details in QEP #17
616  ****************************************************************************/
617 
619 {
620  if ( !line )
621  {
622  return;
623  }
624 
625  if ( numPoints() < 1 )
626  {
628  }
629 
630  // do not store duplicit points
631  if ( numPoints() > 0 &&
632  line->numPoints() > 0 &&
633  endPoint() == line->startPoint() )
634  {
635  mX.pop_back();
636  mY.pop_back();
637 
638  if ( is3D() )
639  {
640  mZ.pop_back();
641  }
642  if ( isMeasure() )
643  {
644  mM.pop_back();
645  }
646  }
647 
648  mX += line->mX;
649  mY += line->mY;
650 
651  if ( is3D() )
652  {
653  if ( line->is3D() )
654  {
655  mZ += line->mZ;
656  }
657  else
658  {
659  // if append line does not have z coordinates, fill with NaN to match number of points in final line
660  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
661  }
662  }
663 
664  if ( isMeasure() )
665  {
666  if ( line->isMeasure() )
667  {
668  mM += line->mM;
669  }
670  else
671  {
672  // if append line does not have m values, fill with NaN to match number of points in final line
673  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
674  }
675  }
676 
677  clearCache(); //set bounding box invalid
678 }
679 
681 {
682  QgsLineString *copy = clone();
683  std::reverse( copy->mX.begin(), copy->mX.end() );
684  std::reverse( copy->mY.begin(), copy->mY.end() );
685  if ( copy->is3D() )
686  {
687  std::reverse( copy->mZ.begin(), copy->mZ.end() );
688  }
689  if ( copy->isMeasure() )
690  {
691  std::reverse( copy->mM.begin(), copy->mM.end() );
692  }
693  return copy;
694 }
695 
696 /***************************************************************************
697  * This class is considered CRITICAL and any change MUST be accompanied with
698  * full unit tests.
699  * See details in QEP #17
700  ****************************************************************************/
701 
702 void QgsLineString::draw( QPainter &p ) const
703 {
704  p.drawPolyline( asQPolygonF() );
705 }
706 
707 void QgsLineString::addToPainterPath( QPainterPath &path ) const
708 {
709  int nPoints = numPoints();
710  if ( nPoints < 1 )
711  {
712  return;
713  }
714 
715  if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
716  {
717  path.moveTo( mX.at( 0 ), mY.at( 0 ) );
718  }
719 
720  for ( int i = 1; i < nPoints; ++i )
721  {
722  path.lineTo( mX.at( i ), mY.at( i ) );
723  }
724 }
725 
726 void QgsLineString::drawAsPolygon( QPainter &p ) const
727 {
728  p.drawPolygon( asQPolygonF() );
729 }
730 
732 {
733  QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
734  compoundCurve->addCurve( clone() );
735  return compoundCurve;
736 }
737 
738 void QgsLineString::extend( double startDistance, double endDistance )
739 {
740  if ( mX.size() < 2 || mY.size() < 2 )
741  return;
742 
743  // start of line
744  if ( startDistance > 0 )
745  {
746  double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
747  std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
748  double newLen = currentLen + startDistance;
749  mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
750  mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
751  }
752  // end of line
753  if ( endDistance > 0 )
754  {
755  int last = mX.size() - 1;
756  double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
757  std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
758  double newLen = currentLen + endDistance;
759  mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
760  mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
761  }
762 }
763 
765 {
766  auto result = qgis::make_unique< QgsLineString >();
767  result->mWkbType = mWkbType;
768  return result.release();
769 }
770 
772 {
773  return QStringLiteral( "LineString" );
774 }
775 
777 {
778  return 1;
779 }
780 
781 /***************************************************************************
782  * This class is considered CRITICAL and any change MUST be accompanied with
783  * full unit tests.
784  * See details in QEP #17
785  ****************************************************************************/
786 
788 {
789  double *zArray = nullptr;
790  bool hasZ = is3D();
791  int nPoints = numPoints();
792 
793  // it's possible that transformCoords will throw an exception - so we need to use
794  // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
795  std::unique_ptr< double[] > dummyZ;
796  if ( !hasZ || !transformZ )
797  {
798  dummyZ.reset( new double[nPoints]() );
799  zArray = dummyZ.get();
800  }
801  else
802  {
803  zArray = mZ.data();
804  }
805  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
806  clearCache();
807 }
808 
809 void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
810 {
811  int nPoints = numPoints();
812  bool hasZ = is3D();
813  bool hasM = isMeasure();
814  for ( int i = 0; i < nPoints; ++i )
815  {
816  qreal x, y;
817  t.map( mX.at( i ), mY.at( i ), &x, &y );
818  mX[i] = x;
819  mY[i] = y;
820  if ( hasZ )
821  {
822  mZ[i] = mZ.at( i ) * zScale + zTranslate;
823  }
824  if ( hasM )
825  {
826  mM[i] = mM.at( i ) * mScale + mTranslate;
827  }
828  }
829  clearCache();
830 }
831 
832 /***************************************************************************
833  * This class is considered CRITICAL and any change MUST be accompanied with
834  * full unit tests.
835  * See details in QEP #17
836  ****************************************************************************/
837 
838 bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
839 {
840  if ( position.vertex < 0 || position.vertex > mX.size() )
841  {
842  return false;
843  }
844 
845  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
846  {
848  }
849 
850  mX.insert( position.vertex, vertex.x() );
851  mY.insert( position.vertex, vertex.y() );
852  if ( is3D() )
853  {
854  mZ.insert( position.vertex, vertex.z() );
855  }
856  if ( isMeasure() )
857  {
858  mM.insert( position.vertex, vertex.m() );
859  }
860  clearCache(); //set bounding box invalid
861  return true;
862 }
863 
864 bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
865 {
866  if ( position.vertex < 0 || position.vertex >= mX.size() )
867  {
868  return false;
869  }
870  mX[position.vertex] = newPos.x();
871  mY[position.vertex] = newPos.y();
872  if ( is3D() && newPos.is3D() )
873  {
874  mZ[position.vertex] = newPos.z();
875  }
876  if ( isMeasure() && newPos.isMeasure() )
877  {
878  mM[position.vertex] = newPos.m();
879  }
880  clearCache(); //set bounding box invalid
881  return true;
882 }
883 
885 {
886  if ( position.vertex >= mX.size() || position.vertex < 0 )
887  {
888  return false;
889  }
890 
891  mX.remove( position.vertex );
892  mY.remove( position.vertex );
893  if ( is3D() )
894  {
895  mZ.remove( position.vertex );
896  }
897  if ( isMeasure() )
898  {
899  mM.remove( position.vertex );
900  }
901 
902  if ( numPoints() == 1 )
903  {
904  clear();
905  }
906 
907  clearCache(); //set bounding box invalid
908  return true;
909 }
910 
911 /***************************************************************************
912  * This class is considered CRITICAL and any change MUST be accompanied with
913  * full unit tests.
914  * See details in QEP #17
915  ****************************************************************************/
916 
918 {
919  if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
920  {
922  }
923 
924  mX.append( pt.x() );
925  mY.append( pt.y() );
926  if ( is3D() )
927  {
928  mZ.append( pt.z() );
929  }
930  if ( isMeasure() )
931  {
932  mM.append( pt.m() );
933  }
934  clearCache(); //set bounding box invalid
935 }
936 
937 double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
938 {
939  double sqrDist = std::numeric_limits<double>::max();
940  double leftOfDist = std::numeric_limits<double>::max();
941  int prevLeftOf = 0;
942  double prevLeftOfX;
943  double prevLeftOfY;
944  double testDist = 0;
945  double segmentPtX, segmentPtY;
946 
947  if ( leftOf )
948  *leftOf = 0;
949 
950  int size = mX.size();
951  if ( size == 0 || size == 1 )
952  {
953  vertexAfter = QgsVertexId( 0, 0, 0 );
954  return -1;
955  }
956  for ( int i = 1; i < size; ++i )
957  {
958  double prevX = mX.at( i - 1 );
959  double prevY = mY.at( i - 1 );
960  double currentX = mX.at( i );
961  double currentY = mY.at( i );
962  testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
963  if ( testDist < sqrDist )
964  {
965  sqrDist = testDist;
966  segmentPt.setX( segmentPtX );
967  segmentPt.setY( segmentPtY );
968  vertexAfter.part = 0;
969  vertexAfter.ring = 0;
970  vertexAfter.vertex = i;
971  }
972  if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
973  {
974  int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
975  // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
976  // so don't set leftOf in this case, and hope that there's another segment that's the same distance
977  // where we can perform the check
978  if ( left != 0 )
979  {
980  if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
981  {
982  // we have two possible segments each with equal distance to point, but they disagree
983  // on whether or not the point is to the left of them.
984  // so we test the segments themselves and flip the result.
985  // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
986  *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
987  }
988  else
989  {
990  *leftOf = left;
991  }
992  prevLeftOf = *leftOf;
993  leftOfDist = testDist;
994  prevLeftOfX = prevX;
995  prevLeftOfY = prevY;
996  }
997  else if ( testDist < leftOfDist )
998  {
999  *leftOf = left;
1000  leftOfDist = testDist;
1001  prevLeftOf = 0;
1002  }
1003  }
1004  }
1005  return sqrDist;
1006 }
1007 
1008 /***************************************************************************
1009  * This class is considered CRITICAL and any change MUST be accompanied with
1010  * full unit tests.
1011  * See details in QEP #17
1012  ****************************************************************************/
1013 
1014 bool QgsLineString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1015 {
1016  if ( node < 0 || node >= numPoints() )
1017  {
1018  return false;
1019  }
1020  point = pointN( node );
1022  return true;
1023 }
1024 
1026 {
1027  if ( mX.isEmpty() )
1028  return QgsPoint();
1029 
1030  int numPoints = mX.count();
1031  if ( numPoints == 1 )
1032  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1033 
1034  double totalLineLength = 0.0;
1035  double prevX = mX.at( 0 );
1036  double prevY = mY.at( 0 );
1037  double sumX = 0.0;
1038  double sumY = 0.0;
1039 
1040  for ( int i = 1; i < numPoints ; ++i )
1041  {
1042  double currentX = mX.at( i );
1043  double currentY = mY.at( i );
1044  double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1045  std::pow( currentY - prevY, 2.0 ) );
1046  if ( qgsDoubleNear( segmentLength, 0.0 ) )
1047  continue;
1048 
1049  totalLineLength += segmentLength;
1050  sumX += segmentLength * 0.5 * ( currentX + prevX );
1051  sumY += segmentLength * 0.5 * ( currentY + prevY );
1052  prevX = currentX;
1053  prevY = currentY;
1054  }
1055 
1056  if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1057  return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1058  else
1059  return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1060 
1061 }
1062 
1063 /***************************************************************************
1064  * This class is considered CRITICAL and any change MUST be accompanied with
1065  * full unit tests.
1066  * See details in QEP #17
1067  ****************************************************************************/
1068 
1069 void QgsLineString::sumUpArea( double &sum ) const
1070 {
1071  int maxIndex = numPoints() - 1;
1072 
1073  for ( int i = 0; i < maxIndex; ++i )
1074  {
1075  sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
1076  }
1077 }
1078 
1079 void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1080 {
1081  bool hasZ = is3D();
1082  bool hasM = isMeasure();
1083  int nVertices = 0;
1084  wkb >> nVertices;
1085  mX.resize( nVertices );
1086  mY.resize( nVertices );
1087  hasZ ? mZ.resize( nVertices ) : mZ.clear();
1088  hasM ? mM.resize( nVertices ) : mM.clear();
1089  for ( int i = 0; i < nVertices; ++i )
1090  {
1091  wkb >> mX[i];
1092  wkb >> mY[i];
1093  if ( hasZ )
1094  {
1095  wkb >> mZ[i];
1096  }
1097  if ( hasM )
1098  {
1099  wkb >> mM[i];
1100  }
1101  }
1102  clearCache(); //set bounding box invalid
1103 }
1104 
1105 /***************************************************************************
1106  * This class is considered CRITICAL and any change MUST be accompanied with
1107  * full unit tests.
1108  * See details in QEP #17
1109  ****************************************************************************/
1110 
1112 {
1113  if ( numPoints() < 1 || isClosed() )
1114  {
1115  return;
1116  }
1117  addVertex( startPoint() );
1118 }
1119 
1121 {
1122  if ( mX.count() < 2 )
1123  {
1124  //undefined
1125  return 0.0;
1126  }
1127 
1128  if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
1129  {
1130  if ( isClosed() )
1131  {
1132  double previousX = mX.at( numPoints() - 2 );
1133  double previousY = mY.at( numPoints() - 2 );
1134  double currentX = mX.at( 0 );
1135  double currentY = mY.at( 0 );
1136  double afterX = mX.at( 1 );
1137  double afterY = mY.at( 1 );
1138  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1139  }
1140  else if ( vertex.vertex == 0 )
1141  {
1142  return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
1143  }
1144  else
1145  {
1146  int a = numPoints() - 2;
1147  int b = numPoints() - 1;
1148  return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
1149  }
1150  }
1151  else
1152  {
1153  double previousX = mX.at( vertex.vertex - 1 );
1154  double previousY = mY.at( vertex.vertex - 1 );
1155  double currentX = mX.at( vertex.vertex );
1156  double currentY = mY.at( vertex.vertex );
1157  double afterX = mX.at( vertex.vertex + 1 );
1158  double afterY = mY.at( vertex.vertex + 1 );
1159  return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
1160  }
1161 }
1162 
1163 double QgsLineString::segmentLength( QgsVertexId startVertex ) const
1164 {
1165  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
1166  return 0.0;
1167 
1168  double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
1169  double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
1170  return std::sqrt( dx * dx + dy * dy );
1171 }
1172 
1173 /***************************************************************************
1174  * This class is considered CRITICAL and any change MUST be accompanied with
1175  * full unit tests.
1176  * See details in QEP #17
1177  ****************************************************************************/
1178 
1179 bool QgsLineString::addZValue( double zValue )
1180 {
1181  if ( QgsWkbTypes::hasZ( mWkbType ) )
1182  return false;
1183 
1184  clearCache();
1185  if ( mWkbType == QgsWkbTypes::Unknown )
1186  {
1188  return true;
1189  }
1190 
1192 
1193  mZ.clear();
1194  int nPoints = numPoints();
1195  mZ.reserve( nPoints );
1196  for ( int i = 0; i < nPoints; ++i )
1197  {
1198  mZ << zValue;
1199  }
1200  return true;
1201 }
1202 
1203 bool QgsLineString::addMValue( double mValue )
1204 {
1205  if ( QgsWkbTypes::hasM( mWkbType ) )
1206  return false;
1207 
1208  clearCache();
1209  if ( mWkbType == QgsWkbTypes::Unknown )
1210  {
1212  return true;
1213  }
1214 
1216  {
1218  }
1219  else
1220  {
1222  }
1223 
1224  mM.clear();
1225  int nPoints = numPoints();
1226  mM.reserve( nPoints );
1227  for ( int i = 0; i < nPoints; ++i )
1228  {
1229  mM << mValue;
1230  }
1231  return true;
1232 }
1233 
1235 {
1236  if ( !is3D() )
1237  return false;
1238 
1239  clearCache();
1241  mZ.clear();
1242  return true;
1243 }
1244 
1246 {
1247  if ( !isMeasure() )
1248  return false;
1249 
1250  clearCache();
1252  mM.clear();
1253  return true;
1254 }
1255 
1257 {
1258  if ( type == mWkbType )
1259  return true;
1260 
1261  clearCache();
1262  if ( type == QgsWkbTypes::LineString25D )
1263  {
1264  //special handling required for conversion to LineString25D
1265  dropMValue();
1266  addZValue( std::numeric_limits<double>::quiet_NaN() );
1268  return true;
1269  }
1270  else
1271  {
1272  return QgsCurve::convertTo( type );
1273  }
1274 }
bool isMeasure() const
Returns true if the geometry contains m values.
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.
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const
Transform an array of coordinates to the destination CRS.
A rectangle specified with double values.
Definition: qgsrectangle.h:39
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.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string.
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)
Angle between two linear segments.
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.
A class to represent a 2D point.
Definition: qgspointxy.h:43
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
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:203
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
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:768
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:938
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)
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
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.
QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition: qgscurve.cpp:191
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:67
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:889
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns)
Returns a gml::coordinates DOM element.
Utility class for identifying a unique vertex within a geometry.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D)
Returns a gml::posList DOM element.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
double mAt(int index) const
Returns the m value of the specified node in the line string.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
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:864
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...
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void setZAt(int index, double z)
Sets the z-coordinate of the specified node in the line string.
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.
QgsPoint startPoint() const override
Returns the starting point of the curve.
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:192
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:39
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:224
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:203
void setMAt(int index, double m)
Sets the m value of the specified node in the line string.
QVector< QgsPoint > QgsPointSequence
bool removeDuplicateNodes(double epsilon=4 *DBL_EPSILON, bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:920
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.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML3 representation of the geometry.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:41
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.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML2 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:526
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:818
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.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *DBL_EPSILON) const override
Searches for the closest segment of the geometry to a given point.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:427
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
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