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