QGIS API Documentation 3.37.0-Master (4ace69f83af)
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"
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 Qgis::WkbType 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
269{
270 return new QgsLineString( *this );
271}
272
274{
275 mX.clear();
276 mY.clear();
277 mZ.clear();
278 mM.clear();
280 clearCache();
281}
282
284{
285 return mX.isEmpty();
286}
287
288int QgsLineString::indexOf( const QgsPoint &point ) const
289{
290 const int size = mX.size();
291 if ( size == 0 )
292 return -1;
293
294 const double *x = mX.constData();
295 const double *y = mY.constData();
296 const bool useZ = is3D();
297 const bool useM = isMeasure();
298 const double *z = useZ ? mZ.constData() : nullptr;
299 const double *m = useM ? mM.constData() : nullptr;
300
301 for ( int i = 0; i < size; ++i )
302 {
303 if ( qgsDoubleNear( *x, point.x() )
304 && qgsDoubleNear( *y, point.y() )
305 && ( !useZ || qgsDoubleNear( *z, point.z() ) )
306 && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
307 return i;
308
309 x++;
310 y++;
311 if ( useZ )
312 z++;
313 if ( useM )
314 m++;
315 }
316 return -1;
317}
318
319bool QgsLineString::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
320{
321 if ( !isEmpty() && ( numPoints() < 2 ) )
322 {
323 error = QObject::tr( "LineString has less than 2 points and is not empty." );
324 return false;
325 }
326 return QgsCurve::isValid( error, flags );
327}
328
329QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
330{
331 // prepare result
332 std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
333
334 bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
335 result->mX, result->mY, result->mZ, result->mM );
336 if ( res )
337 return result.release();
338 else
339 return nullptr;
340}
341
342bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
343{
344 if ( mX.count() <= 2 )
345 return false; // don't create degenerate lines
346 bool result = false;
347 double prevX = mX.at( 0 );
348 double prevY = mY.at( 0 );
349 bool hasZ = is3D();
350 bool useZ = hasZ && useZValues;
351 double prevZ = useZ ? mZ.at( 0 ) : 0;
352 int i = 1;
353 int remaining = mX.count();
354 while ( i < remaining )
355 {
356 double currentX = mX.at( i );
357 double currentY = mY.at( i );
358 double currentZ = useZ ? mZ.at( i ) : 0;
359 if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
360 qgsDoubleNear( currentY, prevY, epsilon ) &&
361 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
362 {
363 result = true;
364 // remove point
365 mX.removeAt( i );
366 mY.removeAt( i );
367 if ( hasZ )
368 mZ.removeAt( i );
369 remaining--;
370 }
371 else
372 {
373 prevX = currentX;
374 prevY = currentY;
375 prevZ = currentZ;
376 i++;
377 }
378 }
379 return result;
380}
381
383{
384 if ( mX.empty() )
385 return false;
386
387 return qgsDoubleNear( mX.first(), mX.last() ) &&
388 qgsDoubleNear( mY.first(), mY.last() );
389}
390
392{
393 bool closed = isClosed2D();
394
395 if ( is3D() && closed )
396 closed &= qgsDoubleNear( mZ.first(), mZ.last() ) || ( std::isnan( mZ.first() ) && std::isnan( mZ.last() ) );
397 return closed;
398}
399
400// As `bool boundingBoxIntersects( const QgsBox3D &box3d )` and `bool boundingBoxIntersects( const QgsRectangle &rectangle )` are nearly
401// the same: if one of these functions is changed then remember to also update the other accordingly
403{
404 if ( mX.empty() )
405 return false;
406
407 if ( !mBoundingBox.isNull() )
408 {
409 return mBoundingBox.intersects( rectangle );
410 }
411 const int nb = mX.size();
412
413 // We are a little fancy here!
414 if ( nb > 40 )
415 {
416 // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
417 // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
418 // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
419 // will fall on approximately these vertex indices)
420 if ( rectangle.contains( mX.at( 0 ), mY.at( 0 ) ) ||
421 rectangle.contains( mX.at( static_cast< int >( nb * 0.2 ) ), mY.at( static_cast< int >( nb * 0.2 ) ) ) ||
422 rectangle.contains( mX.at( static_cast< int >( nb * 0.4 ) ), mY.at( static_cast< int >( nb * 0.4 ) ) ) ||
423 rectangle.contains( mX.at( static_cast< int >( nb * 0.6 ) ), mY.at( static_cast< int >( nb * 0.6 ) ) ) ||
424 rectangle.contains( mX.at( static_cast< int >( nb * 0.8 ) ), mY.at( static_cast< int >( nb * 0.8 ) ) ) ||
425 rectangle.contains( mX.at( nb - 1 ), mY.at( nb - 1 ) ) )
426 return true;
427 }
428
429 // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
430 // already have it, we start performing the bounding box calculation while we are testing whether
431 // each point falls inside the rectangle. That way if we end up testing the majority of the points
432 // anyway, we can update the cached bounding box with the results we've calculated along the way
433 // and save future calls to calculate the bounding box!
434 double xmin = std::numeric_limits<double>::max();
435 double ymin = std::numeric_limits<double>::max();
436 double xmax = -std::numeric_limits<double>::max();
437 double ymax = -std::numeric_limits<double>::max();
438
439 const double *x = mX.constData();
440 const double *y = mY.constData();
441 bool foundPointInRectangle = false;
442 for ( int i = 0; i < nb; ++i )
443 {
444 const double px = *x++;
445 xmin = std::min( xmin, px );
446 xmax = std::max( xmax, px );
447 const double py = *y++;
448 ymin = std::min( ymin, py );
449 ymax = std::max( ymax, py );
450
451 if ( !foundPointInRectangle && rectangle.contains( px, py ) )
452 {
453 foundPointInRectangle = true;
454
455 // now... we have a choice to make. If we've already looped through the majority of the points
456 // in this linestring then let's just continue to iterate through the remainder so that we can
457 // complete the overall bounding box calculation we've already mostly done. If however we're only
458 // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
459 // uncalculated
460 if ( i < nb * 0.5 )
461 return true;
462 }
463 }
464
465 // at this stage we now know the overall bounding box of the linestring, so let's cache
466 // it so we don't ever have to calculate this again. We've done all the hard work anyway!
467 mBoundingBox = QgsRectangle( xmin, ymin, xmax, ymax, false );
468
469 if ( foundPointInRectangle )
470 return true;
471
472 // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
473 // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
474 // So we fall back to the parent class method which compares the overall bounding box against
475 // the rectangle... and this will be very cheap now that we've already calculated and cached
476 // the linestring's bounding box!
477 return QgsCurve::boundingBoxIntersects( rectangle );
478}
479
480// As `bool boundingBoxIntersects( const QgsBox3D &box3d )` and `bool boundingBoxIntersects( const QgsRectangle &rectangle )` are nearly
481// the same: if one of these functions is changed then remember to also update the other accordingly
483{
484 if ( mX.empty() )
485 return false;
486
487 if ( mZ.empty() )
488 return boundingBoxIntersects( box3d.toRectangle() );
489
490 if ( !mBoundingBox.isNull() )
491 {
492 return mBoundingBox.intersects( box3d );
493 }
494 const int nb = mX.size();
495
496 // We are a little fancy here!
497 if ( nb > 40 )
498 {
499 // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
500 // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
501 // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
502 // will fall on approximately these vertex indices)
503 if ( box3d.contains( mX.at( 0 ), mY.at( 0 ), mZ.at( 0 ) ) ||
504 box3d.contains( mX.at( static_cast< int >( nb * 0.2 ) ), mY.at( static_cast< int >( nb * 0.2 ) ), mZ.at( static_cast< int >( nb * 0.2 ) ) ) ||
505 box3d.contains( mX.at( static_cast< int >( nb * 0.4 ) ), mY.at( static_cast< int >( nb * 0.4 ) ), mZ.at( static_cast< int >( nb * 0.4 ) ) ) ||
506 box3d.contains( mX.at( static_cast< int >( nb * 0.6 ) ), mY.at( static_cast< int >( nb * 0.6 ) ), mZ.at( static_cast< int >( nb * 0.6 ) ) ) ||
507 box3d.contains( mX.at( static_cast< int >( nb * 0.8 ) ), mY.at( static_cast< int >( nb * 0.8 ) ), mZ.at( static_cast< int >( nb * 0.8 ) ) ) ||
508 box3d.contains( mX.at( nb - 1 ), mY.at( nb - 1 ), mZ.at( nb - 1 ) ) )
509 return true;
510 }
511
512 // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
513 // already have it, we start performing the bounding box calculation while we are testing whether
514 // each point falls inside the rectangle. That way if we end up testing the majority of the points
515 // anyway, we can update the cached bounding box with the results we've calculated along the way
516 // and save future calls to calculate the bounding box!
517 double xmin = std::numeric_limits<double>::max();
518 double ymin = std::numeric_limits<double>::max();
519 double zmin = std::numeric_limits<double>::max();
520 double xmax = -std::numeric_limits<double>::max();
521 double ymax = -std::numeric_limits<double>::max();
522 double zmax = -std::numeric_limits<double>::max();
523
524 const double *x = mX.constData();
525 const double *y = mY.constData();
526 const double *z = mZ.constData();
527 bool foundPointInBox = false;
528 for ( int i = 0; i < nb; ++i )
529 {
530 const double px = *x++;
531 xmin = std::min( xmin, px );
532 xmax = std::max( xmax, px );
533 const double py = *y++;
534 ymin = std::min( ymin, py );
535 ymax = std::max( ymax, py );
536 const double pz = *z++;
537 zmin = std::min( zmin, pz );
538 zmax = std::max( zmax, pz );
539
540 if ( !foundPointInBox && box3d.contains( px, py, pz ) )
541 {
542 foundPointInBox = true;
543
544 // now... we have a choice to make. If we've already looped through the majority of the points
545 // in this linestring then let's just continue to iterate through the remainder so that we can
546 // complete the overall bounding box calculation we've already mostly done. If however we're only
547 // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
548 // uncalculated
549 if ( i < nb * 0.5 )
550 return true;
551 }
552 }
553
554 // at this stage we now know the overall bounding box of the linestring, so let's cache
555 // it so we don't ever have to calculate this again. We've done all the hard work anyway!
556 mBoundingBox = QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax, false );
557
558 if ( foundPointInBox )
559 return true;
560
561 // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
562 // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
563 // So we fall back to the parent class method which compares the overall bounding box against
564 // the rectangle... and this will be very cheap now that we've already calculated and cached
565 // the linestring's bounding box!
566 return QgsCurve::boundingBoxIntersects( box3d );
567}
568
569QVector< QgsVertexId > QgsLineString::collectDuplicateNodes( double epsilon, bool useZValues ) const
570{
571 QVector< QgsVertexId > res;
572 if ( mX.count() <= 1 )
573 return res;
574
575 const double *x = mX.constData();
576 const double *y = mY.constData();
577 bool hasZ = is3D();
578 bool useZ = hasZ && useZValues;
579 const double *z = useZ ? mZ.constData() : nullptr;
580
581 double prevX = *x++;
582 double prevY = *y++;
583 double prevZ = z ? *z++ : 0;
584
585 QgsVertexId id;
586 for ( int i = 1; i < mX.count(); ++i )
587 {
588 double currentX = *x++;
589 double currentY = *y++;
590 double currentZ = useZ ? *z++ : 0;
591 if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
592 qgsDoubleNear( currentY, prevY, epsilon ) &&
593 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
594 {
595 id.vertex = i;
596 res << id;
597 }
598 else
599 {
600 prevX = currentX;
601 prevY = currentY;
602 prevZ = currentZ;
603 }
604 }
605 return res;
606}
607
609{
610 const int nb = mX.size();
611 QPolygonF points( nb );
612
613 const double *x = mX.constData();
614 const double *y = mY.constData();
615 QPointF *dest = points.data();
616 for ( int i = 0; i < nb; ++i )
617 {
618 *dest++ = QPointF( *x++, *y++ );
619 }
620 return points;
621}
622
624{
625 if ( !wkbPtr )
626 {
627 return false;
628 }
629
630 Qgis::WkbType type = wkbPtr.readHeader();
632 {
633 return false;
634 }
635 mWkbType = type;
636 importVerticesFromWkb( wkbPtr );
637 return true;
638}
639
641{
642 if ( mX.empty() )
643 {
644 return QgsBox3D();
645 }
646
647 auto result2D = std::minmax_element( mX.begin(), mX.end() );
648 const double xmin = *result2D.first;
649 const double xmax = *result2D.second;
650 result2D = std::minmax_element( mY.begin(), mY.end() );
651 const double ymin = *result2D.first;
652 const double ymax = *result2D.second;
653
654 double zmin = std::numeric_limits< double >::quiet_NaN();
655 double zmax = std::numeric_limits< double >::quiet_NaN();
656
657 if ( is3D() )
658 {
659 auto resultZ = std::minmax_element( mZ.begin(), mZ.end() );
660 zmin = *resultZ.first;
661 zmax = *resultZ.second;
662 }
663
664 return QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax );
665}
666
671
672void QgsLineString::scroll( int index )
673{
674 const int size = mX.size();
675 if ( index < 1 || index >= size - 1 )
676 return;
677
678 const bool useZ = is3D();
679 const bool useM = isMeasure();
680
681 QVector<double> newX( size );
682 QVector<double> newY( size );
683 QVector<double> newZ( useZ ? size : 0 );
684 QVector<double> newM( useM ? size : 0 );
685 auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
686 it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
687 *it = *newX.constBegin();
688 mX = std::move( newX );
689
690 it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
691 it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
692 *it = *newY.constBegin();
693 mY = std::move( newY );
694 if ( useZ )
695 {
696 it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
697 it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
698 *it = *newZ.constBegin();
699 mZ = std::move( newZ );
700 }
701 if ( useM )
702 {
703 it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
704 it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
705 *it = *newM.constBegin();
706 mM = std::move( newM );
707 }
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 ****************************************************************************/
715bool QgsLineString::fromWkt( const QString &wkt )
716{
717 clear();
718
719 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
720
722 return false;
723 mWkbType = parts.first;
724
725 QString secondWithoutParentheses = parts.second;
726 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
727 parts.second = parts.second.remove( '(' ).remove( ')' );
728 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
729 secondWithoutParentheses.isEmpty() )
730 return true;
731
733 // There is a non number in the coordinates sequence
734 // LineString ( A b, 1 2)
735 if ( points.isEmpty() )
736 return false;
737
738 setPoints( points );
739 return true;
740}
741
742int QgsLineString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
743{
744 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
745 binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
746 return binarySize;
747}
748
749QByteArray QgsLineString::asWkb( WkbFlags flags ) const
750{
751 QByteArray wkbArray;
752 wkbArray.resize( QgsLineString::wkbSize( flags ) );
753 QgsWkbPtr wkb( wkbArray );
754 wkb << static_cast<char>( QgsApplication::endian() );
755 wkb << static_cast<quint32>( wkbType() );
757 points( pts );
758 QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
759 return wkbArray;
760}
761
762/***************************************************************************
763 * This class is considered CRITICAL and any change MUST be accompanied with
764 * full unit tests.
765 * See details in QEP #17
766 ****************************************************************************/
767
768QString QgsLineString::asWkt( int precision ) const
769{
770 QString wkt = wktTypeStr() + ' ';
771
772 if ( isEmpty() )
773 wkt += QLatin1String( "EMPTY" );
774 else
775 {
777 points( pts );
779 }
780 return wkt;
781}
782
783QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
784{
786 points( pts );
787
788 QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
789
790 if ( isEmpty() )
791 return elemLineString;
792
793 elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
794
795 return elemLineString;
796}
797
798QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
799{
801 points( pts );
802
803 QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
804
805 if ( isEmpty() )
806 return elemLineString;
807
808 elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
809 return elemLineString;
810}
811
813{
815 points( pts );
816 return
817 {
818 { "type", "LineString" },
819 { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
820 };
821}
822
823QString QgsLineString::asKml( int precision ) const
824{
825 QString kml;
826 if ( isRing() )
827 {
828 kml.append( QLatin1String( "<LinearRing>" ) );
829 }
830 else
831 {
832 kml.append( QLatin1String( "<LineString>" ) );
833 }
834 bool z = is3D();
835 kml.append( QLatin1String( "<altitudeMode>" ) );
836 if ( z )
837 {
838 kml.append( QLatin1String( "absolute" ) );
839 }
840 else
841 {
842 kml.append( QLatin1String( "clampToGround" ) );
843 }
844 kml.append( QLatin1String( "</altitudeMode>" ) );
845 kml.append( QLatin1String( "<coordinates>" ) );
846
847 int nPoints = mX.size();
848 for ( int i = 0; i < nPoints; ++i )
849 {
850 if ( i > 0 )
851 {
852 kml.append( QLatin1String( " " ) );
853 }
854 kml.append( qgsDoubleToString( mX[i], precision ) );
855 kml.append( QLatin1String( "," ) );
856 kml.append( qgsDoubleToString( mY[i], precision ) );
857 if ( z )
858 {
859 kml.append( QLatin1String( "," ) );
860 kml.append( qgsDoubleToString( mZ[i], precision ) );
861 }
862 else
863 {
864 kml.append( QLatin1String( ",0" ) );
865 }
866 }
867 kml.append( QLatin1String( "</coordinates>" ) );
868 if ( isRing() )
869 {
870 kml.append( QLatin1String( "</LinearRing>" ) );
871 }
872 else
873 {
874 kml.append( QLatin1String( "</LineString>" ) );
875 }
876 return kml;
877}
878
879/***************************************************************************
880 * This class is considered CRITICAL and any change MUST be accompanied with
881 * full unit tests.
882 * See details in QEP #17
883 ****************************************************************************/
884
886{
887 double total = 0;
888 const int size = mX.size();
889 if ( size < 2 )
890 return 0;
891
892 const double *x = mX.constData();
893 const double *y = mY.constData();
894 double dx, dy;
895
896 double prevX = *x++;
897 double prevY = *y++;
898
899 for ( int i = 1; i < size; ++i )
900 {
901 dx = *x - prevX;
902 dy = *y - prevY;
903 total += std::sqrt( dx * dx + dy * dy );
904
905 prevX = *x++;
906 prevY = *y++;
907 }
908 return total;
909}
910
911std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsLineString::splitCurveAtVertex( int index ) const
912{
913 const bool useZ = is3D();
914 const bool useM = isMeasure();
915
916 const int size = mX.size();
917 if ( size == 0 )
918 return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >() );
919
920 index = std::clamp( index, 0, size - 1 );
921
922 const int part1Size = index + 1;
923 QVector< double > x1( part1Size );
924 QVector< double > y1( part1Size );
925 QVector< double > z1( useZ ? part1Size : 0 );
926 QVector< double > m1( useM ? part1Size : 0 );
927
928 const double *sourceX = mX.constData();
929 const double *sourceY = mY.constData();
930 const double *sourceZ = useZ ? mZ.constData() : nullptr;
931 const double *sourceM = useM ? mM.constData() : nullptr;
932
933 double *destX = x1.data();
934 double *destY = y1.data();
935 double *destZ = useZ ? z1.data() : nullptr;
936 double *destM = useM ? m1.data() : nullptr;
937
938 std::copy( sourceX, sourceX + part1Size, destX );
939 std::copy( sourceY, sourceY + part1Size, destY );
940 if ( useZ )
941 std::copy( sourceZ, sourceZ + part1Size, destZ );
942 if ( useM )
943 std::copy( sourceM, sourceM + part1Size, destM );
944
945 const int part2Size = size - index;
946 if ( part2Size < 2 )
947 return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >() );
948
949 QVector< double > x2( part2Size );
950 QVector< double > y2( part2Size );
951 QVector< double > z2( useZ ? part2Size : 0 );
952 QVector< double > m2( useM ? part2Size : 0 );
953 destX = x2.data();
954 destY = y2.data();
955 destZ = useZ ? z2.data() : nullptr;
956 destM = useM ? m2.data() : nullptr;
957 std::copy( sourceX + index, sourceX + size, destX );
958 std::copy( sourceY + index, sourceY + size, destY );
959 if ( useZ )
960 std::copy( sourceZ + index, sourceZ + size, destZ );
961 if ( useM )
962 std::copy( sourceM + index, sourceM + size, destM );
963
964 if ( part1Size < 2 )
965 return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
966 else
967 return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
968}
969
971{
972 if ( is3D() )
973 {
974 double total = 0;
975 const int size = mX.size();
976 if ( size < 2 )
977 return 0;
978
979 const double *x = mX.constData();
980 const double *y = mY.constData();
981 const double *z = mZ.constData();
982 double dx, dy, dz;
983
984 double prevX = *x++;
985 double prevY = *y++;
986 double prevZ = *z++;
987
988 for ( int i = 1; i < size; ++i )
989 {
990 dx = *x - prevX;
991 dy = *y - prevY;
992 dz = *z - prevZ;
993 total += std::sqrt( dx * dx + dy * dy + dz * dz );
994
995 prevX = *x++;
996 prevY = *y++;
997 prevZ = *z++;
998 }
999 return total;
1000 }
1001 else
1002 {
1003 return length();
1004 }
1005}
1006
1008{
1009 if ( numPoints() < 1 )
1010 {
1011 return QgsPoint();
1012 }
1013 return pointN( 0 );
1014}
1015
1017{
1018 if ( numPoints() < 1 )
1019 {
1020 return QgsPoint();
1021 }
1022 return pointN( numPoints() - 1 );
1023}
1024
1025/***************************************************************************
1026 * This class is considered CRITICAL and any change MUST be accompanied with
1027 * full unit tests.
1028 * See details in QEP #17
1029 ****************************************************************************/
1030
1032{
1033 Q_UNUSED( tolerance )
1034 Q_UNUSED( toleranceType )
1035 return clone();
1036}
1037
1039{
1040 return mX.size();
1041}
1042
1044{
1045 return mX.size();
1046}
1047
1049{
1050 if ( i < 0 || i >= mX.size() )
1051 {
1052 return QgsPoint();
1053 }
1054
1055 double x = mX.at( i );
1056 double y = mY.at( i );
1057 double z = std::numeric_limits<double>::quiet_NaN();
1058 double m = std::numeric_limits<double>::quiet_NaN();
1059
1060 bool hasZ = is3D();
1061 if ( hasZ )
1062 {
1063 z = mZ.at( i );
1064 }
1065 bool hasM = isMeasure();
1066 if ( hasM )
1067 {
1068 m = mM.at( i );
1069 }
1070
1073 {
1075 }
1076 else if ( hasZ && hasM )
1077 {
1079 }
1080 else if ( hasZ )
1081 {
1083 }
1084 else if ( hasM )
1085 {
1087 }
1088 return QgsPoint( t, x, y, z, m );
1089}
1090
1091/***************************************************************************
1092 * This class is considered CRITICAL and any change MUST be accompanied with
1093 * full unit tests.
1094 * See details in QEP #17
1095 ****************************************************************************/
1096
1097double QgsLineString::xAt( int index ) const
1098{
1099 if ( index >= 0 && index < mX.size() )
1100 return mX.at( index );
1101 else
1102 return 0.0;
1103}
1104
1105double QgsLineString::yAt( int index ) const
1106{
1107 if ( index >= 0 && index < mY.size() )
1108 return mY.at( index );
1109 else
1110 return 0.0;
1111}
1112
1113void QgsLineString::setXAt( int index, double x )
1114{
1115 if ( index >= 0 && index < mX.size() )
1116 mX[ index ] = x;
1117 clearCache();
1118}
1119
1120void QgsLineString::setYAt( int index, double y )
1121{
1122 if ( index >= 0 && index < mY.size() )
1123 mY[ index ] = y;
1124 clearCache();
1125}
1126
1127/***************************************************************************
1128 * This class is considered CRITICAL and any change MUST be accompanied with
1129 * full unit tests.
1130 * See details in QEP #17
1131 ****************************************************************************/
1132
1134{
1135 pts.clear();
1136 int nPoints = numPoints();
1137 pts.reserve( nPoints );
1138 for ( int i = 0; i < nPoints; ++i )
1139 {
1140 pts.push_back( pointN( i ) );
1141 }
1142}
1143
1144void QgsLineString::setPoints( size_t size, const double *x, const double *y, const double *z, const double *m )
1145{
1146 clearCache(); //set bounding box invalid
1147
1148 if ( size == 0 )
1149 {
1150 clear();
1151 return;
1152 }
1153
1154 const bool hasZ = static_cast< bool >( z );
1155 const bool hasM = static_cast< bool >( m );
1156
1157 if ( hasZ && hasM )
1158 {
1160 }
1161 else if ( hasZ )
1162 {
1164 }
1165 else if ( hasM )
1166 {
1168 }
1169 else
1170 {
1172 }
1173
1174 mX.resize( size );
1175 mY.resize( size );
1176 double *destX = mX.data();
1177 double *destY = mY.data();
1178 double *destZ = nullptr;
1179 if ( hasZ )
1180 {
1181 mZ.resize( size );
1182 destZ = mZ.data();
1183 }
1184 else
1185 {
1186 mZ.clear();
1187 }
1188 double *destM = nullptr;
1189 if ( hasM )
1190 {
1191 mM.resize( size );
1192 destM = mM.data();
1193 }
1194 else
1195 {
1196 mM.clear();
1197 }
1198
1199 for ( size_t i = 0; i < size; ++i )
1200 {
1201 *destX++ = *x++;
1202 *destY++ = *y++;
1203 if ( hasZ )
1204 {
1205 *destZ++ = *z++;
1206 }
1207 if ( hasM )
1208 {
1209 *destM++ = *m++;
1210 }
1211 }
1212}
1213
1215{
1216 clearCache(); //set bounding box invalid
1217
1218 if ( points.isEmpty() )
1219 {
1220 clear();
1221 return;
1222 }
1223
1224 //get wkb type from first point
1225 const QgsPoint &firstPt = points.at( 0 );
1226 bool hasZ = firstPt.is3D();
1227 bool hasM = firstPt.isMeasure();
1228
1230
1231 mX.resize( points.size() );
1232 mY.resize( points.size() );
1233 if ( hasZ )
1234 {
1235 mZ.resize( points.size() );
1236 }
1237 else
1238 {
1239 mZ.clear();
1240 }
1241 if ( hasM )
1242 {
1243 mM.resize( points.size() );
1244 }
1245 else
1246 {
1247 mM.clear();
1248 }
1249
1250 for ( int i = 0; i < points.size(); ++i )
1251 {
1252 mX[i] = points.at( i ).x();
1253 mY[i] = points.at( i ).y();
1254 if ( hasZ )
1255 {
1256 double z = points.at( i ).z();
1257 mZ[i] = std::isnan( z ) ? 0 : z;
1258 }
1259 if ( hasM )
1260 {
1261 double m = points.at( i ).m();
1262 mM[i] = std::isnan( m ) ? 0 : m;
1263 }
1264 }
1265}
1266
1267/***************************************************************************
1268 * This class is considered CRITICAL and any change MUST be accompanied with
1269 * full unit tests.
1270 * See details in QEP #17
1271 ****************************************************************************/
1272
1274{
1275 if ( !line )
1276 {
1277 return;
1278 }
1279
1280 if ( numPoints() < 1 )
1281 {
1283 }
1284
1285 // do not store duplicate points
1286 if ( numPoints() > 0 &&
1287 line->numPoints() > 0 &&
1288 endPoint() == line->startPoint() )
1289 {
1290 mX.pop_back();
1291 mY.pop_back();
1292
1293 if ( is3D() )
1294 {
1295 mZ.pop_back();
1296 }
1297 if ( isMeasure() )
1298 {
1299 mM.pop_back();
1300 }
1301 }
1302
1303 mX += line->mX;
1304 mY += line->mY;
1305
1306 if ( is3D() )
1307 {
1308 if ( line->is3D() )
1309 {
1310 mZ += line->mZ;
1311 }
1312 else
1313 {
1314 // if append line does not have z coordinates, fill with NaN to match number of points in final line
1315 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1316 }
1317 }
1318
1319 if ( isMeasure() )
1320 {
1321 if ( line->isMeasure() )
1322 {
1323 mM += line->mM;
1324 }
1325 else
1326 {
1327 // if append line does not have m values, fill with NaN to match number of points in final line
1328 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1329 }
1330 }
1331
1332 clearCache(); //set bounding box invalid
1333}
1334
1336{
1337 QgsLineString *copy = clone();
1338 std::reverse( copy->mX.begin(), copy->mX.end() );
1339 std::reverse( copy->mY.begin(), copy->mY.end() );
1340 if ( copy->is3D() )
1341 {
1342 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1343 }
1344 if ( copy->isMeasure() )
1345 {
1346 std::reverse( copy->mM.begin(), copy->mM.end() );
1347 }
1348 return copy;
1349}
1350
1351void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
1352{
1353 if ( distance < 0 )
1354 return;
1355
1356 double distanceTraversed = 0;
1357 const int totalPoints = numPoints();
1358 if ( totalPoints == 0 )
1359 return;
1360
1361 const double *x = mX.constData();
1362 const double *y = mY.constData();
1363 const double *z = is3D() ? mZ.constData() : nullptr;
1364 const double *m = isMeasure() ? mM.constData() : nullptr;
1365
1366 double prevX = *x++;
1367 double prevY = *y++;
1368 double prevZ = z ? *z++ : 0.0;
1369 double prevM = m ? *m++ : 0.0;
1370
1371 if ( qgsDoubleNear( distance, 0.0 ) )
1372 {
1373 visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
1374 return;
1375 }
1376
1377 double pZ = std::numeric_limits<double>::quiet_NaN();
1378 double pM = std::numeric_limits<double>::quiet_NaN();
1379 double nextPointDistance = distance;
1380 for ( int i = 1; i < totalPoints; ++i )
1381 {
1382 double thisX = *x++;
1383 double thisY = *y++;
1384 double thisZ = z ? *z++ : 0.0;
1385 double thisM = m ? *m++ : 0.0;
1386
1387 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1388 while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
1389 {
1390 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1391 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
1392 double pX, pY;
1393 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
1394 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
1395 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
1396
1397 if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1398 return;
1399
1400 nextPointDistance += distance;
1401 }
1402
1403 distanceTraversed += segmentLength;
1404 prevX = thisX;
1405 prevY = thisY;
1406 prevZ = thisZ;
1407 prevM = thisM;
1408 }
1409}
1410
1411QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
1412{
1413 if ( distance < 0 )
1414 return nullptr;
1415
1417 if ( is3D() )
1418 pointType = Qgis::WkbType::PointZ;
1419 if ( isMeasure() )
1420 pointType = QgsWkbTypes::addM( pointType );
1421
1422 std::unique_ptr< QgsPoint > res;
1423 visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
1424 {
1425 res = std::make_unique< QgsPoint >( pointType, x, y, z, m );
1426 return false;
1427 } );
1428 return res.release();
1429}
1430
1431QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
1432{
1433 if ( startDistance < 0 && endDistance < 0 )
1434 return createEmptyWithSameType();
1435
1436 endDistance = std::max( startDistance, endDistance );
1437
1438 const int totalPoints = numPoints();
1439 if ( totalPoints == 0 )
1440 return clone();
1441
1442 QVector< QgsPoint > substringPoints;
1443 substringPoints.reserve( totalPoints );
1444
1446 if ( is3D() )
1447 pointType = Qgis::WkbType::PointZ;
1448 if ( isMeasure() )
1449 pointType = QgsWkbTypes::addM( pointType );
1450
1451 const double *x = mX.constData();
1452 const double *y = mY.constData();
1453 const double *z = is3D() ? mZ.constData() : nullptr;
1454 const double *m = isMeasure() ? mM.constData() : nullptr;
1455
1456 double distanceTraversed = 0;
1457 double prevX = *x++;
1458 double prevY = *y++;
1459 double prevZ = z ? *z++ : 0.0;
1460 double prevM = m ? *m++ : 0.0;
1461 bool foundStart = false;
1462
1463 if ( startDistance < 0 )
1464 startDistance = 0;
1465
1466 for ( int i = 1; i < totalPoints; ++i )
1467 {
1468 double thisX = *x++;
1469 double thisY = *y++;
1470 double thisZ = z ? *z++ : 0.0;
1471 double thisM = m ? *m++ : 0.0;
1472
1473 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1474
1475 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1476 {
1477 // start point falls on this segment
1478 const double distanceToStart = startDistance - distanceTraversed;
1479 double startX, startY;
1480 double startZ = 0;
1481 double startM = 0;
1482 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1483 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1484 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1485 substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1486 foundStart = true;
1487 }
1488 if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1489 {
1490 // end point falls on this segment
1491 const double distanceToEnd = endDistance - distanceTraversed;
1492 double endX, endY;
1493 double endZ = 0;
1494 double endM = 0;
1495 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1496 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1497 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1498 substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1499 }
1500 else if ( foundStart )
1501 {
1502 substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1503 }
1504
1505 prevX = thisX;
1506 prevY = thisY;
1507 prevZ = thisZ;
1508 prevM = thisM;
1509 distanceTraversed += segmentLength;
1510 if ( distanceTraversed >= endDistance )
1511 break;
1512 }
1513
1514 // start point is the last node
1515 if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1516 {
1517 substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1518 << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1519 }
1520
1521 return new QgsLineString( substringPoints );
1522}
1523
1524/***************************************************************************
1525 * This class is considered CRITICAL and any change MUST be accompanied with
1526 * full unit tests.
1527 * See details in QEP #17
1528 ****************************************************************************/
1529
1530void QgsLineString::draw( QPainter &p ) const
1531{
1532 p.drawPolyline( asQPolygonF() );
1533}
1534
1535void QgsLineString::addToPainterPath( QPainterPath &path ) const
1536{
1537 int nPoints = numPoints();
1538 if ( nPoints < 1 )
1539 {
1540 return;
1541 }
1542
1543 if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1544 {
1545 path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1546 }
1547
1548 for ( int i = 1; i < nPoints; ++i )
1549 {
1550 path.lineTo( mX.at( i ), mY.at( i ) );
1551 }
1552}
1553
1554void QgsLineString::drawAsPolygon( QPainter &p ) const
1555{
1556 p.drawPolygon( asQPolygonF() );
1557}
1558
1560{
1561 QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1562 compoundCurve->addCurve( clone() );
1563 return compoundCurve;
1564}
1565
1566void QgsLineString::extend( double startDistance, double endDistance )
1567{
1568 if ( mX.size() < 2 || mY.size() < 2 )
1569 return;
1570
1571 const bool extendStart = startDistance > 0;
1572 const bool extendEnd = endDistance > 0;
1573
1574 // start of line
1575 if ( extendStart )
1576 {
1577 const double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1578 std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1579 const double newLen = currentLen + startDistance;
1580 mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1581 mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1582 }
1583 // end of line
1584 if ( extendEnd )
1585 {
1586 const int last = mX.size() - 1;
1587 const double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1588 std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1589 const double newLen = currentLen + endDistance;
1590 mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1591 mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1592 }
1593
1594 if ( extendStart || extendEnd )
1595 clearCache(); //set bounding box invalid
1596}
1597
1599{
1600 auto result = std::make_unique< QgsLineString >();
1601 result->mWkbType = mWkbType;
1602 return result.release();
1603}
1604
1606{
1607 const QgsLineString *otherLine = qgsgeometry_cast<const QgsLineString *>( other );
1608 if ( !otherLine )
1609 return -1;
1610
1611 const int size = mX.size();
1612 const int otherSize = otherLine->mX.size();
1613 if ( size > otherSize )
1614 {
1615 return 1;
1616 }
1617 else if ( size < otherSize )
1618 {
1619 return -1;
1620 }
1621
1622 if ( is3D() && !otherLine->is3D() )
1623 return 1;
1624 else if ( !is3D() && otherLine->is3D() )
1625 return -1;
1626 const bool considerZ = is3D();
1627
1628 if ( isMeasure() && !otherLine->isMeasure() )
1629 return 1;
1630 else if ( !isMeasure() && otherLine->isMeasure() )
1631 return -1;
1632 const bool considerM = isMeasure();
1633
1634 for ( int i = 0; i < size; i++ )
1635 {
1636 const double x = mX[i];
1637 const double otherX = otherLine->mX[i];
1638 if ( x < otherX )
1639 {
1640 return -1;
1641 }
1642 else if ( x > otherX )
1643 {
1644 return 1;
1645 }
1646
1647 const double y = mY[i];
1648 const double otherY = otherLine->mY[i];
1649 if ( y < otherY )
1650 {
1651 return -1;
1652 }
1653 else if ( y > otherY )
1654 {
1655 return 1;
1656 }
1657
1658 if ( considerZ )
1659 {
1660 const double z = mZ[i];
1661 const double otherZ = otherLine->mZ[i];
1662
1663 if ( z < otherZ )
1664 {
1665 return -1;
1666 }
1667 else if ( z > otherZ )
1668 {
1669 return 1;
1670 }
1671 }
1672
1673 if ( considerM )
1674 {
1675 const double m = mM[i];
1676 const double otherM = otherLine->mM[i];
1677
1678 if ( m < otherM )
1679 {
1680 return -1;
1681 }
1682 else if ( m > otherM )
1683 {
1684 return 1;
1685 }
1686 }
1687 }
1688 return 0;
1689}
1690
1692{
1693 return QStringLiteral( "LineString" );
1694}
1695
1697{
1698 return 1;
1699}
1700
1701/***************************************************************************
1702 * This class is considered CRITICAL and any change MUST be accompanied with
1703 * full unit tests.
1704 * See details in QEP #17
1705 ****************************************************************************/
1706
1708{
1709 double *zArray = nullptr;
1710 bool hasZ = is3D();
1711 int nPoints = numPoints();
1712
1713 // it's possible that transformCoords will throw an exception - so we need to use
1714 // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1715 std::unique_ptr< double[] > dummyZ;
1716 if ( !hasZ || !transformZ )
1717 {
1718 dummyZ.reset( new double[nPoints]() );
1719 zArray = dummyZ.get();
1720 }
1721 else
1722 {
1723 zArray = mZ.data();
1724 }
1725 ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1726 clearCache();
1727}
1728
1729void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1730{
1731 int nPoints = numPoints();
1732 bool hasZ = is3D();
1733 bool hasM = isMeasure();
1734 double *x = mX.data();
1735 double *y = mY.data();
1736 double *z = hasZ ? mZ.data() : nullptr;
1737 double *m = hasM ? mM.data() : nullptr;
1738 for ( int i = 0; i < nPoints; ++i )
1739 {
1740 double xOut, yOut;
1741 t.map( *x, *y, &xOut, &yOut );
1742 *x++ = xOut;
1743 *y++ = yOut;
1744 if ( hasZ )
1745 {
1746 *z = *z * zScale + zTranslate;
1747 z++;
1748 }
1749 if ( hasM )
1750 {
1751 *m = *m * mScale + mTranslate;
1752 m++;
1753 }
1754 }
1755 clearCache();
1756}
1757
1758/***************************************************************************
1759 * This class is considered CRITICAL and any change MUST be accompanied with
1760 * full unit tests.
1761 * See details in QEP #17
1762 ****************************************************************************/
1763
1764bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
1765{
1766 if ( position.vertex < 0 || position.vertex > mX.size() )
1767 {
1768 return false;
1769 }
1770
1771 if ( mWkbType == Qgis::WkbType::Unknown || mX.isEmpty() )
1772 {
1774 }
1775
1776 mX.insert( position.vertex, vertex.x() );
1777 mY.insert( position.vertex, vertex.y() );
1778 if ( is3D() )
1779 {
1780 mZ.insert( position.vertex, vertex.z() );
1781 }
1782 if ( isMeasure() )
1783 {
1784 mM.insert( position.vertex, vertex.m() );
1785 }
1786 clearCache(); //set bounding box invalid
1787 return true;
1788}
1789
1790bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1791{
1792 if ( position.vertex < 0 || position.vertex >= mX.size() )
1793 {
1794 return false;
1795 }
1796 mX[position.vertex] = newPos.x();
1797 mY[position.vertex] = newPos.y();
1798 if ( is3D() && newPos.is3D() )
1799 {
1800 mZ[position.vertex] = newPos.z();
1801 }
1802 if ( isMeasure() && newPos.isMeasure() )
1803 {
1804 mM[position.vertex] = newPos.m();
1805 }
1806 clearCache(); //set bounding box invalid
1807 return true;
1808}
1809
1811{
1812 if ( position.vertex >= mX.size() || position.vertex < 0 )
1813 {
1814 return false;
1815 }
1816
1817 mX.remove( position.vertex );
1818 mY.remove( position.vertex );
1819 if ( is3D() )
1820 {
1821 mZ.remove( position.vertex );
1822 }
1823 if ( isMeasure() )
1824 {
1825 mM.remove( position.vertex );
1826 }
1827
1828 if ( numPoints() == 1 )
1829 {
1830 clear();
1831 }
1832
1833 clearCache(); //set bounding box invalid
1834 return true;
1835}
1836
1837/***************************************************************************
1838 * This class is considered CRITICAL and any change MUST be accompanied with
1839 * full unit tests.
1840 * See details in QEP #17
1841 ****************************************************************************/
1842
1844{
1845 if ( mWkbType == Qgis::WkbType::Unknown || mX.isEmpty() )
1846 {
1848 }
1849
1850 mX.append( pt.x() );
1851 mY.append( pt.y() );
1852 if ( is3D() )
1853 {
1854 mZ.append( pt.z() );
1855 }
1856 if ( isMeasure() )
1857 {
1858 mM.append( pt.m() );
1859 }
1860 clearCache(); //set bounding box invalid
1861}
1862
1863double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1864{
1865 double sqrDist = std::numeric_limits<double>::max();
1866 double leftOfDist = std::numeric_limits<double>::max();
1867 int prevLeftOf = 0;
1868 double prevLeftOfX = 0.0;
1869 double prevLeftOfY = 0.0;
1870 double testDist = 0;
1871 double segmentPtX, segmentPtY;
1872
1873 if ( leftOf )
1874 *leftOf = 0;
1875
1876 int size = mX.size();
1877 if ( size == 0 || size == 1 )
1878 {
1879 vertexAfter = QgsVertexId( 0, 0, 0 );
1880 return -1;
1881 }
1882 for ( int i = 1; i < size; ++i )
1883 {
1884 double prevX = mX.at( i - 1 );
1885 double prevY = mY.at( i - 1 );
1886 double currentX = mX.at( i );
1887 double currentY = mY.at( i );
1888 testDist = QgsGeometryUtilsBase::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
1889 if ( testDist < sqrDist )
1890 {
1891 sqrDist = testDist;
1892 segmentPt.setX( segmentPtX );
1893 segmentPt.setY( segmentPtY );
1894 vertexAfter.part = 0;
1895 vertexAfter.ring = 0;
1896 vertexAfter.vertex = i;
1897 }
1898 if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
1899 {
1900 int left = QgsGeometryUtilsBase::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
1901 // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
1902 // so don't set leftOf in this case, and hope that there's another segment that's the same distance
1903 // where we can perform the check
1904 if ( left != 0 )
1905 {
1906 if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1907 {
1908 // we have two possible segments each with equal distance to point, but they disagree
1909 // on whether or not the point is to the left of them.
1910 // so we test the segments themselves and flip the result.
1911 // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
1912 *leftOf = -QgsGeometryUtilsBase::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
1913 }
1914 else
1915 {
1916 *leftOf = left;
1917 }
1918 prevLeftOf = *leftOf;
1919 leftOfDist = testDist;
1920 prevLeftOfX = prevX;
1921 prevLeftOfY = prevY;
1922 }
1923 else if ( testDist < leftOfDist )
1924 {
1925 *leftOf = left;
1926 leftOfDist = testDist;
1927 prevLeftOf = 0;
1928 }
1929 }
1930 }
1931 return sqrDist;
1932}
1933
1934/***************************************************************************
1935 * This class is considered CRITICAL and any change MUST be accompanied with
1936 * full unit tests.
1937 * See details in QEP #17
1938 ****************************************************************************/
1939
1940bool QgsLineString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1941{
1942 if ( node < 0 || node >= numPoints() )
1943 {
1944 return false;
1945 }
1946 point = pointN( node );
1948 return true;
1949}
1950
1952{
1953 if ( mX.isEmpty() )
1954 return QgsPoint();
1955
1956 int numPoints = mX.count();
1957 if ( numPoints == 1 )
1958 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1959
1960 double totalLineLength = 0.0;
1961 double prevX = mX.at( 0 );
1962 double prevY = mY.at( 0 );
1963 double sumX = 0.0;
1964 double sumY = 0.0;
1965
1966 for ( int i = 1; i < numPoints ; ++i )
1967 {
1968 double currentX = mX.at( i );
1969 double currentY = mY.at( i );
1970 double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
1971 std::pow( currentY - prevY, 2.0 ) );
1972 if ( qgsDoubleNear( segmentLength, 0.0 ) )
1973 continue;
1974
1975 totalLineLength += segmentLength;
1976 sumX += segmentLength * ( currentX + prevX );
1977 sumY += segmentLength * ( currentY + prevY );
1978 prevX = currentX;
1979 prevY = currentY;
1980 }
1981 sumX *= 0.5;
1982 sumY *= 0.5;
1983
1984 if ( qgsDoubleNear( totalLineLength, 0.0 ) )
1985 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
1986 else
1987 return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1988
1989}
1990
1991/***************************************************************************
1992 * This class is considered CRITICAL and any change MUST be accompanied with
1993 * full unit tests.
1994 * See details in QEP #17
1995 ****************************************************************************/
1996
1997void QgsLineString::sumUpArea( double &sum ) const
1998{
2000 {
2001 sum += mSummedUpArea;
2002 return;
2003 }
2004
2005 mSummedUpArea = 0;
2006 const int maxIndex = mX.size();
2007 if ( maxIndex < 2 )
2008 {
2010 return;
2011 }
2012
2013 const double *x = mX.constData();
2014 const double *y = mY.constData();
2015 double prevX = *x++;
2016 double prevY = *y++;
2017 for ( int i = 1; i < maxIndex; ++i )
2018 {
2019 mSummedUpArea += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
2020 prevX = *x++;
2021 prevY = *y++;
2022 }
2023 mSummedUpArea *= 0.5;
2024
2026 sum += mSummedUpArea;
2027}
2028
2029void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
2030{
2031 bool hasZ = is3D();
2032 bool hasM = isMeasure();
2033 int nVertices = 0;
2034 wkb >> nVertices;
2035 mX.resize( nVertices );
2036 mY.resize( nVertices );
2037 hasZ ? mZ.resize( nVertices ) : mZ.clear();
2038 hasM ? mM.resize( nVertices ) : mM.clear();
2039 double *x = mX.data();
2040 double *y = mY.data();
2041 double *m = hasM ? mM.data() : nullptr;
2042 double *z = hasZ ? mZ.data() : nullptr;
2043 for ( int i = 0; i < nVertices; ++i )
2044 {
2045 wkb >> *x++;
2046 wkb >> *y++;
2047 if ( hasZ )
2048 {
2049 wkb >> *z++;
2050 }
2051 if ( hasM )
2052 {
2053 wkb >> *m++;
2054 }
2055 }
2056 clearCache(); //set bounding box invalid
2057}
2058
2059/***************************************************************************
2060 * This class is considered CRITICAL and any change MUST be accompanied with
2061 * full unit tests.
2062 * See details in QEP #17
2063 ****************************************************************************/
2064
2066{
2067 if ( numPoints() < 1 || isClosed() )
2068 {
2069 return;
2070 }
2071 addVertex( startPoint() );
2072}
2073
2075{
2076 if ( mX.count() < 2 )
2077 {
2078 //undefined
2079 return 0.0;
2080 }
2081
2082 if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
2083 {
2084 if ( isClosed() )
2085 {
2086 double previousX = mX.at( numPoints() - 2 );
2087 double previousY = mY.at( numPoints() - 2 );
2088 double currentX = mX.at( 0 );
2089 double currentY = mY.at( 0 );
2090 double afterX = mX.at( 1 );
2091 double afterY = mY.at( 1 );
2092 return QgsGeometryUtilsBase::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2093 }
2094 else if ( vertex.vertex == 0 )
2095 {
2096 return QgsGeometryUtilsBase::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
2097 }
2098 else
2099 {
2100 int a = numPoints() - 2;
2101 int b = numPoints() - 1;
2102 return QgsGeometryUtilsBase::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
2103 }
2104 }
2105 else
2106 {
2107 double previousX = mX.at( vertex.vertex - 1 );
2108 double previousY = mY.at( vertex.vertex - 1 );
2109 double currentX = mX.at( vertex.vertex );
2110 double currentY = mY.at( vertex.vertex );
2111 double afterX = mX.at( vertex.vertex + 1 );
2112 double afterY = mY.at( vertex.vertex + 1 );
2113 return QgsGeometryUtilsBase::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2114 }
2115}
2116
2118{
2119 if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
2120 return 0.0;
2121
2122 double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
2123 double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
2124 return std::sqrt( dx * dx + dy * dy );
2125}
2126
2127/***************************************************************************
2128 * This class is considered CRITICAL and any change MUST be accompanied with
2129 * full unit tests.
2130 * See details in QEP #17
2131 ****************************************************************************/
2132
2133bool QgsLineString::addZValue( double zValue )
2134{
2135 if ( QgsWkbTypes::hasZ( mWkbType ) )
2136 return false;
2137
2138 clearCache();
2140 {
2142 return true;
2143 }
2144
2146
2147 mZ.clear();
2148 int nPoints = numPoints();
2149 mZ.reserve( nPoints );
2150 for ( int i = 0; i < nPoints; ++i )
2151 {
2152 mZ << zValue;
2153 }
2154 return true;
2155}
2156
2157bool QgsLineString::addMValue( double mValue )
2158{
2159 if ( QgsWkbTypes::hasM( mWkbType ) )
2160 return false;
2161
2162 clearCache();
2164 {
2166 return true;
2167 }
2168
2170 {
2172 }
2173 else
2174 {
2176 }
2177
2178 mM.clear();
2179 int nPoints = numPoints();
2180 mM.reserve( nPoints );
2181 for ( int i = 0; i < nPoints; ++i )
2182 {
2183 mM << mValue;
2184 }
2185 return true;
2186}
2187
2189{
2190 if ( !is3D() )
2191 return false;
2192
2193 clearCache();
2195 mZ.clear();
2196 return true;
2197}
2198
2200{
2201 if ( !isMeasure() )
2202 return false;
2203
2204 clearCache();
2206 mM.clear();
2207 return true;
2208}
2209
2211{
2212 std::swap( mX, mY );
2213 clearCache();
2214}
2215
2217{
2218 if ( type == mWkbType )
2219 return true;
2220
2221 clearCache();
2222 if ( type == Qgis::WkbType::LineString25D )
2223 {
2224 //special handling required for conversion to LineString25D
2225 dropMValue();
2226 addZValue( std::numeric_limits<double>::quiet_NaN() );
2228 return true;
2229 }
2230 else
2231 {
2232 return QgsCurve::convertTo( type );
2233 }
2234}
2235
2237{
2238 if ( !transformer )
2239 return false;
2240
2241 bool hasZ = is3D();
2242 bool hasM = isMeasure();
2243 int size = mX.size();
2244
2245 double *srcX = mX.data();
2246 double *srcY = mY.data();
2247 double *srcM = hasM ? mM.data() : nullptr;
2248 double *srcZ = hasZ ? mZ.data() : nullptr;
2249
2250 bool res = true;
2251 for ( int i = 0; i < size; ++i )
2252 {
2253 double x = *srcX;
2254 double y = *srcY;
2255 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2256 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2257 if ( !transformer->transformPoint( x, y, z, m ) )
2258 {
2259 res = false;
2260 break;
2261 }
2262
2263 *srcX++ = x;
2264 *srcY++ = y;
2265 if ( hasM )
2266 *srcM++ = m;
2267 if ( hasZ )
2268 *srcZ++ = z;
2269
2270 if ( feedback && feedback->isCanceled() )
2271 {
2272 res = false;
2273 break;
2274 }
2275 }
2276 clearCache();
2277 return res;
2278}
2279
2280void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
2281{
2282 bool hasZ = is3D();
2283 bool hasM = isMeasure();
2284 int size = mX.size();
2285
2286 double *srcX = mX.data();
2287 double *srcY = mY.data();
2288 double *srcM = hasM ? mM.data() : nullptr;
2289 double *srcZ = hasZ ? mZ.data() : nullptr;
2290
2291 double *destX = srcX;
2292 double *destY = srcY;
2293 double *destM = srcM;
2294 double *destZ = srcZ;
2295
2296 int filteredPoints = 0;
2297 for ( int i = 0; i < size; ++i )
2298 {
2299 double x = *srcX++;
2300 double y = *srcY++;
2301 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
2302 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
2303
2304 if ( filter( QgsPoint( x, y, z, m ) ) )
2305 {
2306 filteredPoints++;
2307 *destX++ = x;
2308 *destY++ = y;
2309 if ( hasM )
2310 *destM++ = m;
2311 if ( hasZ )
2312 *destZ++ = z;
2313 }
2314 }
2315
2316 mX.resize( filteredPoints );
2317 mY.resize( filteredPoints );
2318 if ( hasZ )
2319 mZ.resize( filteredPoints );
2320 if ( hasM )
2321 mM.resize( filteredPoints );
2322
2323 clearCache();
2324}
2325
2326void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
2327{
2328 bool hasZ = is3D();
2329 bool hasM = isMeasure();
2330 int size = mX.size();
2331
2332 double *srcX = mX.data();
2333 double *srcY = mY.data();
2334 double *srcM = hasM ? mM.data() : nullptr;
2335 double *srcZ = hasZ ? mZ.data() : nullptr;
2336
2337 for ( int i = 0; i < size; ++i )
2338 {
2339 double x = *srcX;
2340 double y = *srcY;
2341 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2342 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2343 QgsPoint res = transform( QgsPoint( x, y, z, m ) );
2344 *srcX++ = res.x();
2345 *srcY++ = res.y();
2346 if ( hasM )
2347 *srcM++ = res.m();
2348 if ( hasZ )
2349 *srcZ++ = res.z();
2350 }
2351 clearCache();
2352}
2353
2354
2355QgsLineString *QgsLineString::measuredLine( double start, double end ) const
2356{
2357 const int nbpoints = numPoints();
2358 std::unique_ptr< QgsLineString > cloned( clone() );
2359
2360 if ( !cloned->convertTo( QgsWkbTypes::addM( mWkbType ) ) )
2361 {
2362 return cloned.release();
2363 }
2364
2365 if ( isEmpty() || ( nbpoints < 2 ) )
2366 {
2367 return cloned.release();
2368 }
2369
2370 const double range = end - start;
2371 double lineLength = length();
2372 double lengthSoFar = 0.0;
2373
2374
2375 double *mOut = cloned->mM.data();
2376 *mOut++ = start;
2377 for ( int i = 1; i < nbpoints ; ++i )
2378 {
2379 lengthSoFar += QgsGeometryUtilsBase::distance2D( mX[ i - 1], mY[ i - 1 ], mX[ i ], mY[ i ] );
2380 if ( lineLength > 0.0 )
2381 *mOut++ = start + range * lengthSoFar / lineLength;
2382 else if ( lineLength == 0.0 && nbpoints > 1 )
2383 *mOut++ = start + range * i / ( nbpoints - 1 );
2384 else
2385 *mOut++ = 0.0;
2386 }
2387
2388 return cloned.release();
2389}
VertexType
Types of vertex.
Definition qgis.h:2455
@ Segment
The actual start or end point of a segment.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:182
@ LineString25D
LineString25D.
@ LineStringM
LineStringM.
@ LineString
LineString.
@ LineStringZM
LineStringZM.
@ Unknown
Unknown.
@ PointM
PointM.
@ PointZ
PointZ.
@ Point25D
Point25D.
@ PointZM
PointZM.
@ LineStringZ
LineStringZ.
TransformDirection
Flags for raster layer temporal capabilities.
Definition qgis.h:2169
An abstract base class for classes which transform geometries by transforming input points to output ...
virtual bool transformPoint(double &x, double &y, double &z, double &m)=0
Transforms the point defined by the coordinates (x, y, z) and the specified m value.
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual bool convertTo(Qgis::WkbType type)
Converts the geometry to a specified type.
bool 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.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
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:43
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:132
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
Definition qgsbox3d.cpp:149
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:338
bool isNull() const
Test if the box is null (holding no spatial information).
Definition qgsbox3d.cpp:289
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
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:55
Class for doing transforms between two map coordinate systems.
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform an array of coordinates to the destination CRS.
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:356
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
QgsBox3D mBoundingBox
Cached bounding box.
Definition qgscurve.h:354
double mSummedUpArea
Definition qgscurve.h:357
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
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 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,...
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 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 json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static 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.
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 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 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.
QgsLineString * measuredLine(double start, double end) const
Re-write the measure ordinate (or add one, if it isn't already there) interpolating the measure betwe...
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.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
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.
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.
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.
Q_DECL_DEPRECATED QgsBox3D calculateBoundingBox3d() const
Calculates the minimal 3D bounding box for the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
QgsLineString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
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:60
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:343
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:332
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.
WKB pointer handler.
Definition qgswkbptr.h:44
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType zmType(Qgis::WkbType type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType 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:5061
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5144
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:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
int part
Part number.
Definition qgsvertexid.h:88
int ring
Ring number.
Definition qgsvertexid.h:91