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