QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
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
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 );
706 QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
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 const bool extendStart = startDistance > 0;
1520 const bool extendEnd = endDistance > 0;
1521
1522 // start of line
1523 if ( extendStart )
1524 {
1525 const double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1526 std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1527 const double newLen = currentLen + startDistance;
1528 mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1529 mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1530 }
1531 // end of line
1532 if ( extendEnd )
1533 {
1534 const int last = mX.size() - 1;
1535 const double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1536 std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1537 const double newLen = currentLen + endDistance;
1538 mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1539 mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1540 }
1541
1542 if ( extendStart || extendEnd )
1543 clearCache(); //set bounding box invalid
1544}
1545
1547{
1548 auto result = std::make_unique< QgsLineString >();
1549 result->mWkbType = mWkbType;
1550 return result.release();
1551}
1552
1554{
1555 const QgsLineString *otherLine = qgsgeometry_cast<const QgsLineString *>( other );
1556 if ( !otherLine )
1557 return -1;
1558
1559 const int size = mX.size();
1560 const int otherSize = otherLine->mX.size();
1561 if ( size > otherSize )
1562 {
1563 return 1;
1564 }
1565 else if ( size < otherSize )
1566 {
1567 return -1;
1568 }
1569
1570 if ( is3D() && !otherLine->is3D() )
1571 return 1;
1572 else if ( !is3D() && otherLine->is3D() )
1573 return -1;
1574 const bool considerZ = is3D();
1575
1576 if ( isMeasure() && !otherLine->isMeasure() )
1577 return 1;
1578 else if ( !isMeasure() && otherLine->isMeasure() )
1579 return -1;
1580 const bool considerM = isMeasure();
1581
1582 for ( int i = 0; i < size; i++ )
1583 {
1584 const double x = mX[i];
1585 const double otherX = otherLine->mX[i];
1586 if ( x < otherX )
1587 {
1588 return -1;
1589 }
1590 else if ( x > otherX )
1591 {
1592 return 1;
1593 }
1594
1595 const double y = mY[i];
1596 const double otherY = otherLine->mY[i];
1597 if ( y < otherY )
1598 {
1599 return -1;
1600 }
1601 else if ( y > otherY )
1602 {
1603 return 1;
1604 }
1605
1606 if ( considerZ )
1607 {
1608 const double z = mZ[i];
1609 const double otherZ = otherLine->mZ[i];
1610
1611 if ( z < otherZ )
1612 {
1613 return -1;
1614 }
1615 else if ( z > otherZ )
1616 {
1617 return 1;
1618 }
1619 }
1620
1621 if ( considerM )
1622 {
1623 const double m = mM[i];
1624 const double otherM = otherLine->mM[i];
1625
1626 if ( m < otherM )
1627 {
1628 return -1;
1629 }
1630 else if ( m > otherM )
1631 {
1632 return 1;
1633 }
1634 }
1635 }
1636 return 0;
1637}
1638
1640{
1641 return QStringLiteral( "LineString" );
1642}
1643
1645{
1646 return 1;
1647}
1648
1649/***************************************************************************
1650 * This class is considered CRITICAL and any change MUST be accompanied with
1651 * full unit tests.
1652 * See details in QEP #17
1653 ****************************************************************************/
1654
1656{
1657 double *zArray = nullptr;
1658 bool hasZ = is3D();
1659 int nPoints = numPoints();
1660
1661 // it's possible that transformCoords will throw an exception - so we need to use
1662 // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1663 std::unique_ptr< double[] > dummyZ;
1664 if ( !hasZ || !transformZ )
1665 {
1666 dummyZ.reset( new double[nPoints]() );
1667 zArray = dummyZ.get();
1668 }
1669 else
1670 {
1671 zArray = mZ.data();
1672 }
1673 ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1674 clearCache();
1675}
1676
1677void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1678{
1679 int nPoints = numPoints();
1680 bool hasZ = is3D();
1681 bool hasM = isMeasure();
1682 double *x = mX.data();
1683 double *y = mY.data();
1684 double *z = hasZ ? mZ.data() : nullptr;
1685 double *m = hasM ? mM.data() : nullptr;
1686 for ( int i = 0; i < nPoints; ++i )
1687 {
1688 double xOut, yOut;
1689 t.map( *x, *y, &xOut, &yOut );
1690 *x++ = xOut;
1691 *y++ = yOut;
1692 if ( hasZ )
1693 {
1694 *z = *z * zScale + zTranslate;
1695 z++;
1696 }
1697 if ( hasM )
1698 {
1699 *m = *m * mScale + mTranslate;
1700 m++;
1701 }
1702 }
1703 clearCache();
1704}
1705
1706/***************************************************************************
1707 * This class is considered CRITICAL and any change MUST be accompanied with
1708 * full unit tests.
1709 * See details in QEP #17
1710 ****************************************************************************/
1711
1712bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1713{
1714 if ( position.vertex < 0 || position.vertex > mX.size() )
1715 {
1716 return false;
1717 }
1718
1719 if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1720 {
1722 }
1723
1724 mX.insert( position.vertex, vertex.x() );
1725 mY.insert( position.vertex, vertex.y() );
1726 if ( is3D() )
1727 {
1728 mZ.insert( position.vertex, vertex.z() );
1729 }
1730 if ( isMeasure() )
1731 {
1732 mM.insert( position.vertex, vertex.m() );
1733 }
1734 clearCache(); //set bounding box invalid
1735 return true;
1736}
1737
1738bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1739{
1740 if ( position.vertex < 0 || position.vertex >= mX.size() )
1741 {
1742 return false;
1743 }
1744 mX[position.vertex] = newPos.x();
1745 mY[position.vertex] = newPos.y();
1746 if ( is3D() && newPos.is3D() )
1747 {
1748 mZ[position.vertex] = newPos.z();
1749 }
1750 if ( isMeasure() && newPos.isMeasure() )
1751 {
1752 mM[position.vertex] = newPos.m();
1753 }
1754 clearCache(); //set bounding box invalid
1755 return true;
1756}
1757
1759{
1760 if ( position.vertex >= mX.size() || position.vertex < 0 )
1761 {
1762 return false;
1763 }
1764
1765 mX.remove( position.vertex );
1766 mY.remove( position.vertex );
1767 if ( is3D() )
1768 {
1769 mZ.remove( position.vertex );
1770 }
1771 if ( isMeasure() )
1772 {
1773 mM.remove( position.vertex );
1774 }
1775
1776 if ( numPoints() == 1 )
1777 {
1778 clear();
1779 }
1780
1781 clearCache(); //set bounding box invalid
1782 return true;
1783}
1784
1785/***************************************************************************
1786 * This class is considered CRITICAL and any change MUST be accompanied with
1787 * full unit tests.
1788 * See details in QEP #17
1789 ****************************************************************************/
1790
1792{
1793 if ( mWkbType == QgsWkbTypes::Unknown || mX.isEmpty() )
1794 {
1796 }
1797
1798 mX.append( pt.x() );
1799 mY.append( pt.y() );
1800 if ( is3D() )
1801 {
1802 mZ.append( pt.z() );
1803 }
1804 if ( isMeasure() )
1805 {
1806 mM.append( pt.m() );
1807 }
1808 clearCache(); //set bounding box invalid
1809}
1810
1811double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1812{
1813 double sqrDist = std::numeric_limits<double>::max();
1814 double leftOfDist = std::numeric_limits<double>::max();
1815 int prevLeftOf = 0;
1816 double prevLeftOfX = 0.0;
1817 double prevLeftOfY = 0.0;
1818 double testDist = 0;
1819 double segmentPtX, segmentPtY;
1820
1821 if ( leftOf )
1822 *leftOf = 0;
1823
1824 int size = mX.size();
1825 if ( size == 0 || size == 1 )
1826 {
1827 vertexAfter = QgsVertexId( 0, 0, 0 );
1828 return -1;
1829 }
1830 for ( int i = 1; i < size; ++i )
1831 {
1832 double prevX = mX.at( i - 1 );
1833 double prevY = mY.at( i - 1 );
1834 double currentX = mX.at( i );
1835 double currentY = mY.at( i );
1836 testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1837 if ( testDist < sqrDist )
1838 {
1839 sqrDist = testDist;
1840 segmentPt.setX( segmentPtX );
1841 segmentPt.setY( segmentPtY );
1842 vertexAfter.part = 0;
1843 vertexAfter.ring = 0;
1844 vertexAfter.vertex = i;
1845 }
1846 if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1847 {
1848 int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1849 // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1850 // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1851 // where we can perform the check
1852 if ( left != 0 )
1853 {
1854 if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1855 {
1856 // we have two possible segments each with equal distance to point, but they disagree
1857 // on whether or not the point is to the left of them.
1858 // so we test the segments themselves and flip the result.
1859 // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1860 *leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1861 }
1862 else
1863 {
1864 *leftOf = left;
1865 }
1866 prevLeftOf = *leftOf;
1867 leftOfDist = testDist;
1868 prevLeftOfX = prevX;
1869 prevLeftOfY = prevY;
1870 }
1871 else if ( testDist < leftOfDist )
1872 {
1873 *leftOf = left;
1874 leftOfDist = testDist;
1875 prevLeftOf = 0;
1876 }
1877 }
1878 }
1879 return sqrDist;
1880}
1881
1882/***************************************************************************
1883 * This class is considered CRITICAL and any change MUST be accompanied with
1884 * full unit tests.
1885 * See details in QEP #17
1886 ****************************************************************************/
1887
1888bool QgsLineString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1889{
1890 if ( node < 0 || node >= numPoints() )
1891 {
1892 return false;
1893 }
1894 point = pointN( node );
1896 return true;
1897}
1898
1900{
1901 if ( mX.isEmpty() )
1902 return QgsPoint();
1903
1904 int numPoints = mX.count();
1905 if ( numPoints == 1 )
1906 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1907
1908 double totalLineLength = 0.0;
1909 double prevX = mX.at( 0 );
1910 double prevY = mY.at( 0 );
1911 double sumX = 0.0;
1912 double sumY = 0.0;
1913
1914 for ( int i = 1; i < numPoints ; ++i )
1915 {
1916 double currentX = mX.at( i );
1917 double currentY = mY.at( i );
1918 double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1919 std::pow( currentY - prevY, 2.0 ) );
1920 if ( qgsDoubleNear( segmentLength, 0.0 ) )
1921 continue;
1922
1923 totalLineLength += segmentLength;
1924 sumX += segmentLength * ( currentX + prevX );
1925 sumY += segmentLength * ( currentY + prevY );
1926 prevX = currentX;
1927 prevY = currentY;
1928 }
1929 sumX *= 0.5;
1930 sumY *= 0.5;
1931
1932 if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1933 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1934 else
1935 return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1936
1937}
1938
1939/***************************************************************************
1940 * This class is considered CRITICAL and any change MUST be accompanied with
1941 * full unit tests.
1942 * See details in QEP #17
1943 ****************************************************************************/
1944
1945void QgsLineString::sumUpArea( double &sum ) const
1946{
1948 {
1949 sum += mSummedUpArea;
1950 return;
1951 }
1952
1953 mSummedUpArea = 0;
1954 const int maxIndex = mX.size();
1955 if ( maxIndex == 0 )
1956 return;
1957
1958 const double *x = mX.constData();
1959 const double *y = mY.constData();
1960 double prevX = *x++;
1961 double prevY = *y++;
1962 for ( int i = 1; i < maxIndex; ++i )
1963 {
1964 mSummedUpArea += prevX * ( *y ) - prevY * ( *x );
1965 prevX = *x++;
1966 prevY = *y++;
1967 }
1968 mSummedUpArea *= 0.5;
1969
1971 sum += mSummedUpArea;
1972}
1973
1974void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
1975{
1976 bool hasZ = is3D();
1977 bool hasM = isMeasure();
1978 int nVertices = 0;
1979 wkb >> nVertices;
1980 mX.resize( nVertices );
1981 mY.resize( nVertices );
1982 hasZ ? mZ.resize( nVertices ) : mZ.clear();
1983 hasM ? mM.resize( nVertices ) : mM.clear();
1984 double *x = mX.data();
1985 double *y = mY.data();
1986 double *m = hasM ? mM.data() : nullptr;
1987 double *z = hasZ ? mZ.data() : nullptr;
1988 for ( int i = 0; i < nVertices; ++i )
1989 {
1990 wkb >> *x++;
1991 wkb >> *y++;
1992 if ( hasZ )
1993 {
1994 wkb >> *z++;
1995 }
1996 if ( hasM )
1997 {
1998 wkb >> *m++;
1999 }
2000 }
2001 clearCache(); //set bounding box invalid
2002}
2003
2004/***************************************************************************
2005 * This class is considered CRITICAL and any change MUST be accompanied with
2006 * full unit tests.
2007 * See details in QEP #17
2008 ****************************************************************************/
2009
2011{
2012 if ( numPoints() < 1 || isClosed() )
2013 {
2014 return;
2015 }
2016 addVertex( startPoint() );
2017}
2018
2020{
2021 if ( mX.count() < 2 )
2022 {
2023 //undefined
2024 return 0.0;
2025 }
2026
2027 if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
2028 {
2029 if ( isClosed() )
2030 {
2031 double previousX = mX.at( numPoints() - 2 );
2032 double previousY = mY.at( numPoints() - 2 );
2033 double currentX = mX.at( 0 );
2034 double currentY = mY.at( 0 );
2035 double afterX = mX.at( 1 );
2036 double afterY = mY.at( 1 );
2037 return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2038 }
2039 else if ( vertex.vertex == 0 )
2040 {
2041 return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
2042 }
2043 else
2044 {
2045 int a = numPoints() - 2;
2046 int b = numPoints() - 1;
2047 return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
2048 }
2049 }
2050 else
2051 {
2052 double previousX = mX.at( vertex.vertex - 1 );
2053 double previousY = mY.at( vertex.vertex - 1 );
2054 double currentX = mX.at( vertex.vertex );
2055 double currentY = mY.at( vertex.vertex );
2056 double afterX = mX.at( vertex.vertex + 1 );
2057 double afterY = mY.at( vertex.vertex + 1 );
2058 return QgsGeometryUtils::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2059 }
2060}
2061
2063{
2064 if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
2065 return 0.0;
2066
2067 double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
2068 double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
2069 return std::sqrt( dx * dx + dy * dy );
2070}
2071
2072/***************************************************************************
2073 * This class is considered CRITICAL and any change MUST be accompanied with
2074 * full unit tests.
2075 * See details in QEP #17
2076 ****************************************************************************/
2077
2078bool QgsLineString::addZValue( double zValue )
2079{
2080 if ( QgsWkbTypes::hasZ( mWkbType ) )
2081 return false;
2082
2083 clearCache();
2085 {
2087 return true;
2088 }
2089
2091
2092 mZ.clear();
2093 int nPoints = numPoints();
2094 mZ.reserve( nPoints );
2095 for ( int i = 0; i < nPoints; ++i )
2096 {
2097 mZ << zValue;
2098 }
2099 return true;
2100}
2101
2102bool QgsLineString::addMValue( double mValue )
2103{
2104 if ( QgsWkbTypes::hasM( mWkbType ) )
2105 return false;
2106
2107 clearCache();
2109 {
2111 return true;
2112 }
2113
2115 {
2117 }
2118 else
2119 {
2121 }
2122
2123 mM.clear();
2124 int nPoints = numPoints();
2125 mM.reserve( nPoints );
2126 for ( int i = 0; i < nPoints; ++i )
2127 {
2128 mM << mValue;
2129 }
2130 return true;
2131}
2132
2134{
2135 if ( !is3D() )
2136 return false;
2137
2138 clearCache();
2140 mZ.clear();
2141 return true;
2142}
2143
2145{
2146 if ( !isMeasure() )
2147 return false;
2148
2149 clearCache();
2151 mM.clear();
2152 return true;
2153}
2154
2156{
2157 std::swap( mX, mY );
2158 clearCache();
2159}
2160
2162{
2163 if ( type == mWkbType )
2164 return true;
2165
2166 clearCache();
2167 if ( type == QgsWkbTypes::LineString25D )
2168 {
2169 //special handling required for conversion to LineString25D
2170 dropMValue();
2171 addZValue( std::numeric_limits<double>::quiet_NaN() );
2173 return true;
2174 }
2175 else
2176 {
2177 return QgsCurve::convertTo( type );
2178 }
2179}
2180
2182{
2183 if ( !transformer )
2184 return false;
2185
2186 bool hasZ = is3D();
2187 bool hasM = isMeasure();
2188 int size = mX.size();
2189
2190 double *srcX = mX.data();
2191 double *srcY = mY.data();
2192 double *srcM = hasM ? mM.data() : nullptr;
2193 double *srcZ = hasZ ? mZ.data() : nullptr;
2194
2195 bool res = true;
2196 for ( int i = 0; i < size; ++i )
2197 {
2198 double x = *srcX;
2199 double y = *srcY;
2200 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2201 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2202 if ( !transformer->transformPoint( x, y, z, m ) )
2203 {
2204 res = false;
2205 break;
2206 }
2207
2208 *srcX++ = x;
2209 *srcY++ = y;
2210 if ( hasM )
2211 *srcM++ = m;
2212 if ( hasZ )
2213 *srcZ++ = z;
2214
2215 if ( feedback && feedback->isCanceled() )
2216 {
2217 res = false;
2218 break;
2219 }
2220 }
2221 clearCache();
2222 return res;
2223}
2224
2225void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
2226{
2227 bool hasZ = is3D();
2228 bool hasM = isMeasure();
2229 int size = mX.size();
2230
2231 double *srcX = mX.data();
2232 double *srcY = mY.data();
2233 double *srcM = hasM ? mM.data() : nullptr;
2234 double *srcZ = hasZ ? mZ.data() : nullptr;
2235
2236 double *destX = srcX;
2237 double *destY = srcY;
2238 double *destM = srcM;
2239 double *destZ = srcZ;
2240
2241 int filteredPoints = 0;
2242 for ( int i = 0; i < size; ++i )
2243 {
2244 double x = *srcX++;
2245 double y = *srcY++;
2246 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
2247 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
2248
2249 if ( filter( QgsPoint( x, y, z, m ) ) )
2250 {
2251 filteredPoints++;
2252 *destX++ = x;
2253 *destY++ = y;
2254 if ( hasM )
2255 *destM++ = m;
2256 if ( hasZ )
2257 *destZ++ = z;
2258 }
2259 }
2260
2261 mX.resize( filteredPoints );
2262 mY.resize( filteredPoints );
2263 if ( hasZ )
2264 mZ.resize( filteredPoints );
2265 if ( hasM )
2266 mM.resize( filteredPoints );
2267
2268 clearCache();
2269}
2270
2271void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
2272{
2273 bool hasZ = is3D();
2274 bool hasM = isMeasure();
2275 int size = mX.size();
2276
2277 double *srcX = mX.data();
2278 double *srcY = mY.data();
2279 double *srcM = hasM ? mM.data() : nullptr;
2280 double *srcZ = hasZ ? mZ.data() : nullptr;
2281
2282 for ( int i = 0; i < size; ++i )
2283 {
2284 double x = *srcX;
2285 double y = *srcY;
2286 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2287 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2288 QgsPoint res = transform( QgsPoint( x, y, z, m ) );
2289 *srcX++ = res.x();
2290 *srcY++ = res.y();
2291 if ( hasM )
2292 *srcM++ = res.m();
2293 if ( hasZ )
2294 *srcZ++ = res.z();
2295 }
2296 clearCache();
2297}
VertexType
Types of vertex.
Definition qgis.h:1518
@ Segment
The actual start or end point of a segment.
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 isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
virtual bool convertTo(QgsWkbTypes::Type type)
Converts the geometry to a specified type.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
QgsWkbTypes::Type mWkbType
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
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:137
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
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
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
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 QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static 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 QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
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.
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.
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.
bool isClosed() const override
Returns true if the curve is closed.
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.
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.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
double length3D() const
Returns the length in 3D world of the line string.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
int dimension() const override
Returns the inherent dimension of the geometry.
void sumUpArea(double &sum) const override
Calculates the shoelace/triangle formula sum for the points in the linestring.
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.
QgsPoint startPoint() const override
Returns the starting point of the curve.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
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 isEmpty() const override
Returns true if the geometry is empty.
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.
int numPoints() const override
Returns the number of points in the curve.
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.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
QgsLineString()
Constructor for an empty linestring geometry.
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.
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override
Returns true if the bounding box of this geometry intersects with a rectangle.
QString geometryType() const override
Returns a unique string representing the geometry type.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
QgsPoint endPoint() const override
Returns the end point of the curve.
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.
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.
void setXAt(int index, double x)
Sets the x-coordinate of the specified node in the line string.
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 isClosed2D() const override
Returns true if the curve is closed.
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 setY(double y)
Sets the point's y-coordinate.
Definition qgspoint.h:291
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:280
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
double y
Definition qgspoint.h:53
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
WKB pointer handler.
Definition qgswkbptr.h:44
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Type
The WKB type describes the number of dimensions a geometry has.
Definition qgswkbtypes.h:70
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
static Type zmType(Type type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
static Type flatType(Type type)
Returns the flat type for a WKB type.
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