QGIS API Documentation 3.99.0-Master (26c88405ac0)
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
20#include <cmath>
21#include <limits>
22#include <memory>
23#include <nlohmann/json.hpp>
24
25#include "qgsapplication.h"
26#include "qgsbox3d.h"
27#include "qgscompoundcurve.h"
29#include "qgsfeedback.h"
31#include "qgsgeometryutils.h"
33#include "qgslinesegment.h"
34#include "qgswkbptr.h"
35
36#include <QDomDocument>
37#include <QJsonObject>
38#include <QPainter>
39
40/***************************************************************************
41 * This class is considered CRITICAL and any change MUST be accompanied with
42 * full unit tests.
43 * See details in QEP #17
44 ****************************************************************************/
45
50
51QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
52{
53 if ( points.isEmpty() )
54 {
56 return;
57 }
58 Qgis::WkbType ptType = points.at( 0 ).wkbType();
60 mX.resize( points.count() );
61 mY.resize( points.count() );
62 double *x = mX.data();
63 double *y = mY.data();
64 double *z = nullptr;
65 double *m = nullptr;
67 {
68 mZ.resize( points.count() );
69 z = mZ.data();
70 }
72 {
73 mM.resize( points.count() );
74 m = mM.data();
75 }
76
77 for ( const QgsPoint &pt : points )
78 {
79 *x++ = pt.x();
80 *y++ = pt.y();
81 if ( z )
82 *z++ = pt.z();
83 if ( m )
84 *m++ = pt.m();
85 }
86}
87
88QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
89{
91 int pointCount = std::min( x.size(), y.size() );
92 if ( x.size() == pointCount )
93 {
94 mX = x;
95 }
96 else
97 {
98 mX = x.mid( 0, pointCount );
99 }
100 if ( y.size() == pointCount )
101 {
102 mY = y;
103 }
104 else
105 {
106 mY = y.mid( 0, pointCount );
107 }
108 if ( !z.isEmpty() && z.count() >= pointCount )
109 {
111 if ( z.size() == pointCount )
112 {
113 mZ = z;
114 }
115 else
116 {
117 mZ = z.mid( 0, pointCount );
118 }
119 }
120 if ( !m.isEmpty() && m.count() >= pointCount )
121 {
123 if ( m.size() == pointCount )
124 {
125 mM = m;
126 }
127 else
128 {
129 mM = m.mid( 0, pointCount );
130 }
131 }
132}
133
135{
137 mX.resize( 2 );
138 mX[ 0 ] = p1.x();
139 mX[ 1 ] = p2.x();
140 mY.resize( 2 );
141 mY[ 0 ] = p1.y();
142 mY[ 1 ] = p2.y();
143 if ( p1.is3D() )
144 {
146 mZ.resize( 2 );
147 mZ[ 0 ] = p1.z();
148 mZ[ 1 ] = p2.z();
149 }
150 if ( p1.isMeasure() )
151 {
153 mM.resize( 2 );
154 mM[ 0 ] = p1.m();
155 mM[ 1 ] = p2.m();
156 }
157}
158
159QgsLineString::QgsLineString( const QVector<QgsPointXY> &points )
160{
162 mX.reserve( points.size() );
163 mY.reserve( points.size() );
164 for ( const QgsPointXY &p : points )
165 {
166 mX << p.x();
167 mY << p.y();
168 }
169}
170
172{
174 mX.resize( 2 );
175 mY.resize( 2 );
176 mX[0] = segment.startX();
177 mX[1] = segment.endX();
178 mY[0] = segment.startY();
179 mY[1] = segment.endY();
180}
181
182static double cubicInterpolate( double a, double b,
183 double A, double B, double C, double D )
184{
185 return A * b * b * b + 3 * B * b * b * a + 3 * C * b * a * a + D * a * a * a;
186}
187
188std::unique_ptr< QgsLineString > QgsLineString::fromBezierCurve( const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments )
189{
190 if ( segments == 0 )
191 return std::make_unique< QgsLineString >();
192
193 QVector<double> x;
194 x.resize( segments + 1 );
195 QVector<double> y;
196 y.resize( segments + 1 );
197 QVector<double> z;
198 double *zData = nullptr;
199 if ( start.is3D() && end.is3D() && controlPoint1.is3D() && controlPoint2.is3D() )
200 {
201 z.resize( segments + 1 );
202 zData = z.data();
203 }
204 QVector<double> m;
205 double *mData = nullptr;
206 if ( start.isMeasure() && end.isMeasure() && controlPoint1.isMeasure() && controlPoint2.isMeasure() )
207 {
208 m.resize( segments + 1 );
209 mData = m.data();
210 }
211
212 double *xData = x.data();
213 double *yData = y.data();
214 const double step = 1.0 / segments;
215 double a = 0;
216 double b = 1.0;
217 for ( int i = 0; i < segments; i++, a += step, b -= step )
218 {
219 if ( i == 0 )
220 {
221 *xData++ = start.x();
222 *yData++ = start.y();
223 if ( zData )
224 *zData++ = start.z();
225 if ( mData )
226 *mData++ = start.m();
227 }
228 else
229 {
230 *xData++ = cubicInterpolate( a, b, start.x(), controlPoint1.x(), controlPoint2.x(), end.x() );
231 *yData++ = cubicInterpolate( a, b, start.y(), controlPoint1.y(), controlPoint2.y(), end.y() );
232 if ( zData )
233 *zData++ = cubicInterpolate( a, b, start.z(), controlPoint1.z(), controlPoint2.z(), end.z() );
234 if ( mData )
235 *mData++ = cubicInterpolate( a, b, start.m(), controlPoint1.m(), controlPoint2.m(), end.m() );
236 }
237 }
238
239 *xData = end.x();
240 *yData = end.y();
241 if ( zData )
242 *zData = end.z();
243 if ( mData )
244 *mData = end.m();
245
246 return std::make_unique< QgsLineString >( x, y, z, m );
247}
248
249std::unique_ptr< QgsLineString > QgsLineString::fromQPolygonF( const QPolygonF &polygon )
250{
251 QVector< double > x;
252 QVector< double > y;
253 x.resize( polygon.count() );
254 y.resize( polygon.count() );
255 double *xData = x.data();
256 double *yData = y.data();
257
258 const QPointF *src = polygon.data();
259 for ( int i = 0 ; i < polygon.size(); ++ i )
260 {
261 *xData++ = src->x();
262 *yData++ = src->y();
263 src++;
264 }
265
266 return std::make_unique< QgsLineString >( x, y );
267}
268
270{
271 return new QgsLineString( *this );
272}
273
275{
276 mX.clear();
277 mY.clear();
278 mZ.clear();
279 mM.clear();
281 clearCache();
282}
283
285{
286 return mX.isEmpty();
287}
288
289int QgsLineString::indexOf( const QgsPoint &point ) const
290{
291 const int size = mX.size();
292 if ( size == 0 )
293 return -1;
294
295 const double *x = mX.constData();
296 const double *y = mY.constData();
297 const bool useZ = is3D();
298 const bool useM = isMeasure();
299 const double *z = useZ ? mZ.constData() : nullptr;
300 const double *m = useM ? mM.constData() : nullptr;
301
302 for ( int i = 0; i < size; ++i )
303 {
304 if ( qgsDoubleNear( *x, point.x() )
305 && qgsDoubleNear( *y, point.y() )
306 && ( !useZ || qgsDoubleNear( *z, point.z() ) )
307 && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
308 return i;
309
310 x++;
311 y++;
312 if ( useZ )
313 z++;
314 if ( useM )
315 m++;
316 }
317 return -1;
318}
319
320bool QgsLineString::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
321{
322 if ( !isEmpty() && ( numPoints() < 2 ) )
323 {
324 error = QObject::tr( "LineString has less than 2 points and is not empty." );
325 return false;
326 }
327 return QgsCurve::isValid( error, flags );
328}
329
330QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
331{
332 // prepare result
333 std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
334
335 bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
336 result->mX, result->mY, result->mZ, result->mM, removeRedundantPoints );
337 if ( res )
338 return result.release();
339 else
340 return nullptr;
341}
342
343bool QgsLineString::removeDuplicateNodes( double epsilon, bool useZValues )
344{
345 if ( mX.count() <= 2 )
346 return false; // don't create degenerate lines
347 bool result = false;
348 double prevX = mX.at( 0 );
349 double prevY = mY.at( 0 );
350 bool hasZ = is3D();
351 bool useZ = hasZ && useZValues;
352 double prevZ = useZ ? mZ.at( 0 ) : 0;
353 int i = 1;
354 int remaining = mX.count();
355 while ( i < remaining )
356 {
357 double currentX = mX.at( i );
358 double currentY = mY.at( i );
359 double currentZ = useZ ? mZ.at( i ) : 0;
360 if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
361 qgsDoubleNear( currentY, prevY, epsilon ) &&
362 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
363 {
364 result = true;
365 // remove point
366 mX.removeAt( i );
367 mY.removeAt( i );
368 if ( hasZ )
369 mZ.removeAt( i );
370 remaining--;
371 }
372 else
373 {
374 prevX = currentX;
375 prevY = currentY;
376 prevZ = currentZ;
377 i++;
378 }
379 }
380 return result;
381}
382
384{
385 if ( mX.empty() )
386 return false;
387
388 return qgsDoubleNear( mX.first(), mX.last() ) &&
389 qgsDoubleNear( mY.first(), mY.last() );
390}
391
393{
394 bool closed = isClosed2D();
395
396 if ( is3D() && closed )
397 closed &= qgsDoubleNear( mZ.first(), mZ.last() ) || ( std::isnan( mZ.first() ) && std::isnan( mZ.last() ) );
398 return closed;
399}
400
401// As `bool boundingBoxIntersects( const QgsBox3D &box3d )` and `bool boundingBoxIntersects( const QgsRectangle &rectangle )` are nearly
402// the same: if one of these functions is changed then remember to also update the other accordingly
404{
405 if ( mX.empty() )
406 return false;
407
408 if ( !mBoundingBox.isNull() )
409 {
410 return mBoundingBox.intersects( rectangle );
411 }
412 const int nb = mX.size();
413
414 // We are a little fancy here!
415 if ( nb > 40 )
416 {
417 // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
418 // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
419 // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
420 // will fall on approximately these vertex indices)
421 if ( rectangle.contains( mX.at( 0 ), mY.at( 0 ) ) ||
422 rectangle.contains( mX.at( static_cast< int >( nb * 0.2 ) ), mY.at( static_cast< int >( nb * 0.2 ) ) ) ||
423 rectangle.contains( mX.at( static_cast< int >( nb * 0.4 ) ), mY.at( static_cast< int >( nb * 0.4 ) ) ) ||
424 rectangle.contains( mX.at( static_cast< int >( nb * 0.6 ) ), mY.at( static_cast< int >( nb * 0.6 ) ) ) ||
425 rectangle.contains( mX.at( static_cast< int >( nb * 0.8 ) ), mY.at( static_cast< int >( nb * 0.8 ) ) ) ||
426 rectangle.contains( mX.at( nb - 1 ), mY.at( nb - 1 ) ) )
427 return true;
428 }
429
430 // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
431 // already have it, we start performing the bounding box calculation while we are testing whether
432 // each point falls inside the rectangle. That way if we end up testing the majority of the points
433 // anyway, we can update the cached bounding box with the results we've calculated along the way
434 // and save future calls to calculate the bounding box!
435 double xmin = std::numeric_limits<double>::max();
436 double ymin = std::numeric_limits<double>::max();
437 double xmax = -std::numeric_limits<double>::max();
438 double ymax = -std::numeric_limits<double>::max();
439
440 const double *x = mX.constData();
441 const double *y = mY.constData();
442 bool foundPointInRectangle = false;
443 for ( int i = 0; i < nb; ++i )
444 {
445 const double px = *x++;
446 xmin = std::min( xmin, px );
447 xmax = std::max( xmax, px );
448 const double py = *y++;
449 ymin = std::min( ymin, py );
450 ymax = std::max( ymax, py );
451
452 if ( !foundPointInRectangle && rectangle.contains( px, py ) )
453 {
454 foundPointInRectangle = true;
455
456 // now... we have a choice to make. If we've already looped through the majority of the points
457 // in this linestring then let's just continue to iterate through the remainder so that we can
458 // complete the overall bounding box calculation we've already mostly done. If however we're only
459 // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
460 // uncalculated
461 if ( i < nb * 0.5 )
462 return true;
463 }
464 }
465
466 // at this stage we now know the overall bounding box of the linestring, so let's cache
467 // it so we don't ever have to calculate this again. We've done all the hard work anyway!
468 mBoundingBox = QgsRectangle( xmin, ymin, xmax, ymax, false );
469
470 if ( foundPointInRectangle )
471 return true;
472
473 // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
474 // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
475 // So we fall back to the parent class method which compares the overall bounding box against
476 // the rectangle... and this will be very cheap now that we've already calculated and cached
477 // the linestring's bounding box!
478 return QgsCurve::boundingBoxIntersects( rectangle );
479}
480
481// As `bool boundingBoxIntersects( const QgsBox3D &box3d )` and `bool boundingBoxIntersects( const QgsRectangle &rectangle )` are nearly
482// the same: if one of these functions is changed then remember to also update the other accordingly
484{
485 if ( mX.empty() )
486 return false;
487
488 if ( mZ.empty() )
489 return boundingBoxIntersects( box3d.toRectangle() );
490
491 if ( !mBoundingBox.isNull() )
492 {
493 return mBoundingBox.intersects( box3d );
494 }
495 const int nb = mX.size();
496
497 // We are a little fancy here!
498 if ( nb > 40 )
499 {
500 // if a large number of vertices, take some sample vertices at 1/5th increments through the linestring
501 // and test whether any are inside the rectangle. Maybe we can shortcut a lot of iterations by doing this!
502 // (why 1/5th? it's picked so that it works nicely for polygon rings which are almost rectangles, so the vertex extremities
503 // will fall on approximately these vertex indices)
504 if ( box3d.contains( mX.at( 0 ), mY.at( 0 ), mZ.at( 0 ) ) ||
505 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 ) ) ) ||
506 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 ) ) ) ||
507 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 ) ) ) ||
508 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 ) ) ) ||
509 box3d.contains( mX.at( nb - 1 ), mY.at( nb - 1 ), mZ.at( nb - 1 ) ) )
510 return true;
511 }
512
513 // Be even MORE fancy! Given that bounding box calculation is non-free, cached, and we don't
514 // already have it, we start performing the bounding box calculation while we are testing whether
515 // each point falls inside the rectangle. That way if we end up testing the majority of the points
516 // anyway, we can update the cached bounding box with the results we've calculated along the way
517 // and save future calls to calculate the bounding box!
518 double xmin = std::numeric_limits<double>::max();
519 double ymin = std::numeric_limits<double>::max();
520 double zmin = std::numeric_limits<double>::max();
521 double xmax = -std::numeric_limits<double>::max();
522 double ymax = -std::numeric_limits<double>::max();
523 double zmax = -std::numeric_limits<double>::max();
524
525 const double *x = mX.constData();
526 const double *y = mY.constData();
527 const double *z = mZ.constData();
528 bool foundPointInBox = false;
529 for ( int i = 0; i < nb; ++i )
530 {
531 const double px = *x++;
532 xmin = std::min( xmin, px );
533 xmax = std::max( xmax, px );
534 const double py = *y++;
535 ymin = std::min( ymin, py );
536 ymax = std::max( ymax, py );
537 const double pz = *z++;
538 zmin = std::min( zmin, pz );
539 zmax = std::max( zmax, pz );
540
541 if ( !foundPointInBox && box3d.contains( px, py, pz ) )
542 {
543 foundPointInBox = true;
544
545 // now... we have a choice to make. If we've already looped through the majority of the points
546 // in this linestring then let's just continue to iterate through the remainder so that we can
547 // complete the overall bounding box calculation we've already mostly done. If however we're only
548 // just at the start of iterating the vertices, we shortcut out early and leave the bounding box
549 // uncalculated
550 if ( i < nb * 0.5 )
551 return true;
552 }
553 }
554
555 // at this stage we now know the overall bounding box of the linestring, so let's cache
556 // it so we don't ever have to calculate this again. We've done all the hard work anyway!
557 mBoundingBox = QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax, false );
558
559 if ( foundPointInBox )
560 return true;
561
562 // NOTE: if none of the points in the line actually fell inside the rectangle, it doesn't
563 // exclude that the OVERALL bounding box of the linestring itself intersects the rectangle!!
564 // So we fall back to the parent class method which compares the overall bounding box against
565 // the rectangle... and this will be very cheap now that we've already calculated and cached
566 // the linestring's bounding box!
567 return QgsCurve::boundingBoxIntersects( box3d );
568}
569
570QVector< QgsVertexId > QgsLineString::collectDuplicateNodes( double epsilon, bool useZValues ) const
571{
572 QVector< QgsVertexId > res;
573 if ( mX.count() <= 1 )
574 return res;
575
576 const double *x = mX.constData();
577 const double *y = mY.constData();
578 bool hasZ = is3D();
579 bool useZ = hasZ && useZValues;
580 const double *z = useZ ? mZ.constData() : nullptr;
581
582 double prevX = *x++;
583 double prevY = *y++;
584 double prevZ = z ? *z++ : 0;
585
586 QgsVertexId id;
587 for ( int i = 1; i < mX.count(); ++i )
588 {
589 double currentX = *x++;
590 double currentY = *y++;
591 double currentZ = useZ ? *z++ : 0;
592 if ( qgsDoubleNear( currentX, prevX, epsilon ) &&
593 qgsDoubleNear( currentY, prevY, epsilon ) &&
594 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
595 {
596 id.vertex = i;
597 res << id;
598 }
599 else
600 {
601 prevX = currentX;
602 prevY = currentY;
603 prevZ = currentZ;
604 }
605 }
606 return res;
607}
608
610{
611 const int nb = mX.size();
612 QPolygonF points( nb );
613
614 const double *x = mX.constData();
615 const double *y = mY.constData();
616 QPointF *dest = points.data();
617 for ( int i = 0; i < nb; ++i )
618 {
619 *dest++ = QPointF( *x++, *y++ );
620 }
621 return points;
622}
623
624
625void simplifySection( int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon )
626{
627 if ( i + 1 == j )
628 {
629 return;
630 }
631
632 double maxDistanceSquared = -1.0;
633
634 int maxIndex = i;
635 double mx, my;
636
637 for ( int k = i + 1; k < j; k++ )
638 {
639 const double distanceSquared = QgsGeometryUtilsBase::sqrDistToLine(
640 x[k], y[k], x[i], y[i], x[j], y[j], mx, my, epsilon );
641
642 if ( distanceSquared > maxDistanceSquared )
643 {
644 maxDistanceSquared = distanceSquared;
645 maxIndex = k;
646 }
647 }
648 if ( maxDistanceSquared <= distanceToleranceSquared )
649 {
650 for ( int k = i + 1; k < j; k++ )
651 {
652 usePoint[k] = false;
653 }
654 }
655 else
656 {
657 simplifySection( i, maxIndex, x, y, usePoint, distanceToleranceSquared, epsilon );
658 simplifySection( maxIndex, j, x, y, usePoint, distanceToleranceSquared, epsilon );
659 }
660};
661
663{
664 if ( mX.empty() )
665 {
666 return new QgsLineString();
667 }
668
669 // ported from GEOS DouglasPeuckerLineSimplifier::simplify
670
671 const double distanceToleranceSquared = tolerance * tolerance;
672 const double *xData = mX.constData();
673 const double *yData = mY.constData();
674 const double *zData = mZ.constData();
675 const double *mData = mM.constData();
676
677 const int size = mX.size();
678
679 std::vector< bool > usePoint( size, true );
680
681 constexpr double epsilon = 4 * std::numeric_limits<double>::epsilon();
682 simplifySection( 0, size - 1, xData, yData, usePoint, distanceToleranceSquared, epsilon );
683
684 QVector< double > newX;
685 newX.reserve( size );
686 QVector< double > newY;
687 newY.reserve( size );
688
689 const bool hasZ = is3D();
690 const bool hasM = isMeasure();
691 QVector< double > newZ;
692 if ( hasZ )
693 newZ.reserve( size );
694 QVector< double > newM;
695 if ( hasM )
696 newM.reserve( size );
697
698 for ( int i = 0, n = size; i < n; ++i )
699 {
700 if ( usePoint[i] || i == n - 1 )
701 {
702 newX.append( xData[i ] );
703 newY.append( yData[i ] );
704 if ( hasZ )
705 newZ.append( zData[i] );
706 if ( hasM )
707 newM.append( mData[i] );
708 }
709 }
710
711 const bool simplifyRing = isRing();
712 const int newSize = newX.size();
713 if ( simplifyRing && newSize > 3 )
714 {
715 double mx, my;
716 const double distanceSquared = QgsGeometryUtilsBase::sqrDistToLine(
717 newX[0], newY[ 0],
718 newX[ newSize - 2], newY[ newSize - 2 ],
719 newX[ 1 ], newY[ 1], mx, my, epsilon );
720
721 if ( distanceSquared <= distanceToleranceSquared )
722 {
723 newX.removeFirst();
724 newX.last() = newX.first();
725 newY.removeFirst();
726 newY.last() = newY.first();
727 if ( hasZ )
728 {
729 newZ.removeFirst();
730 newZ.last() = newZ.first();
731 }
732 if ( hasM )
733 {
734 newM.removeFirst();
735 newM.last() = newM.first();
736 }
737 }
738 }
739
740 return new QgsLineString( newX, newY, newZ, newM );
741}
742
744{
745 if ( !wkbPtr )
746 {
747 return false;
748 }
749
750 Qgis::WkbType type = wkbPtr.readHeader();
752 {
753 return false;
754 }
755 mWkbType = type;
756 importVerticesFromWkb( wkbPtr );
757 return true;
758}
759
761{
762 if ( mX.empty() )
763 {
764 return QgsBox3D();
765 }
766
767 auto result2D = std::minmax_element( mX.begin(), mX.end() );
768 const double xmin = *result2D.first;
769 const double xmax = *result2D.second;
770 result2D = std::minmax_element( mY.begin(), mY.end() );
771 const double ymin = *result2D.first;
772 const double ymax = *result2D.second;
773
774 double zmin = std::numeric_limits< double >::quiet_NaN();
775 double zmax = std::numeric_limits< double >::quiet_NaN();
776
777 if ( is3D() )
778 {
779 auto resultZ = std::minmax_element( mZ.begin(), mZ.end() );
780 zmin = *resultZ.first;
781 zmax = *resultZ.second;
782 }
783
784 return QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax );
785}
786
791
792void QgsLineString::scroll( int index )
793{
794 const int size = mX.size();
795 if ( index < 1 || index >= size - 1 )
796 return;
797
798 const bool useZ = is3D();
799 const bool useM = isMeasure();
800
801 QVector<double> newX( size );
802 QVector<double> newY( size );
803 QVector<double> newZ( useZ ? size : 0 );
804 QVector<double> newM( useM ? size : 0 );
805 auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
806 it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
807 *it = *newX.constBegin();
808 mX = std::move( newX );
809
810 it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
811 it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
812 *it = *newY.constBegin();
813 mY = std::move( newY );
814 if ( useZ )
815 {
816 it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
817 it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
818 *it = *newZ.constBegin();
819 mZ = std::move( newZ );
820 }
821 if ( useM )
822 {
823 it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
824 it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
825 *it = *newM.constBegin();
826 mM = std::move( newM );
827 }
828}
829
830/***************************************************************************
831 * This class is considered CRITICAL and any change MUST be accompanied with
832 * full unit tests.
833 * See details in QEP #17
834 ****************************************************************************/
835bool QgsLineString::fromWkt( const QString &wkt )
836{
837 clear();
838
839 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
840
842 return false;
843 mWkbType = parts.first;
844
845 QString secondWithoutParentheses = parts.second;
846 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
847 parts.second = parts.second.remove( '(' ).remove( ')' );
848 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
849 secondWithoutParentheses.isEmpty() )
850 return true;
851
853 // There is a non number in the coordinates sequence
854 // LineString ( A b, 1 2)
855 if ( points.isEmpty() )
856 return false;
857
858 setPoints( points );
859 return true;
860}
861
863{
864 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
865 binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
866 return binarySize;
867}
868
869QByteArray QgsLineString::asWkb( WkbFlags flags ) const
870{
871 QByteArray wkbArray;
872 wkbArray.resize( QgsLineString::wkbSize( flags ) );
873 QgsWkbPtr wkb( wkbArray );
874 wkb << static_cast<char>( QgsApplication::endian() );
875 wkb << static_cast<quint32>( wkbType() );
877 points( pts );
878 QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
879 return wkbArray;
880}
881
882/***************************************************************************
883 * This class is considered CRITICAL and any change MUST be accompanied with
884 * full unit tests.
885 * See details in QEP #17
886 ****************************************************************************/
887
888QString QgsLineString::asWkt( int precision ) const
889{
890 QString wkt = wktTypeStr() + ' ';
891
892 if ( isEmpty() )
893 wkt += QLatin1String( "EMPTY" );
894 else
895 {
897 points( pts );
898 wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
899 }
900 return wkt;
901}
902
903QDomElement QgsLineString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
904{
906 points( pts );
907
908 QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
909
910 if ( isEmpty() )
911 return elemLineString;
912
913 elemLineString.appendChild( QgsGeometryUtils::pointsToGML2( pts, doc, precision, ns, axisOrder ) );
914
915 return elemLineString;
916}
917
918QDomElement QgsLineString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
919{
921 points( pts );
922
923 QDomElement elemLineString = doc.createElementNS( ns, QStringLiteral( "LineString" ) );
924
925 if ( isEmpty() )
926 return elemLineString;
927
928 elemLineString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
929 return elemLineString;
930}
931
932json QgsLineString::asJsonObject( int precision ) const
933{
935 points( pts );
936 return
937 {
938 { "type", "LineString" },
939 { "coordinates", QgsGeometryUtils::pointsToJson( pts, precision ) }
940 };
941}
942
943QString QgsLineString::asKml( int precision ) const
944{
945 QString kml;
946 if ( isRing() )
947 {
948 kml.append( QLatin1String( "<LinearRing>" ) );
949 }
950 else
951 {
952 kml.append( QLatin1String( "<LineString>" ) );
953 }
954 bool z = is3D();
955 kml.append( QLatin1String( "<altitudeMode>" ) );
956 if ( z )
957 {
958 kml.append( QLatin1String( "absolute" ) );
959 }
960 else
961 {
962 kml.append( QLatin1String( "clampToGround" ) );
963 }
964 kml.append( QLatin1String( "</altitudeMode>" ) );
965 kml.append( QLatin1String( "<coordinates>" ) );
966
967 int nPoints = mX.size();
968 for ( int i = 0; i < nPoints; ++i )
969 {
970 if ( i > 0 )
971 {
972 kml.append( QLatin1String( " " ) );
973 }
974 kml.append( qgsDoubleToString( mX[i], precision ) );
975 kml.append( QLatin1String( "," ) );
976 kml.append( qgsDoubleToString( mY[i], precision ) );
977 if ( z )
978 {
979 kml.append( QLatin1String( "," ) );
980 kml.append( qgsDoubleToString( mZ[i], precision ) );
981 }
982 else
983 {
984 kml.append( QLatin1String( ",0" ) );
985 }
986 }
987 kml.append( QLatin1String( "</coordinates>" ) );
988 if ( isRing() )
989 {
990 kml.append( QLatin1String( "</LinearRing>" ) );
991 }
992 else
993 {
994 kml.append( QLatin1String( "</LineString>" ) );
995 }
996 return kml;
997}
998
999/***************************************************************************
1000 * This class is considered CRITICAL and any change MUST be accompanied with
1001 * full unit tests.
1002 * See details in QEP #17
1003 ****************************************************************************/
1004
1006{
1007 double total = 0;
1008 const int size = mX.size();
1009 if ( size < 2 )
1010 return 0;
1011
1012 const double *x = mX.constData();
1013 const double *y = mY.constData();
1014 double dx, dy;
1015
1016 double prevX = *x++;
1017 double prevY = *y++;
1018
1019 for ( int i = 1; i < size; ++i )
1020 {
1021 dx = *x - prevX;
1022 dy = *y - prevY;
1023 total += std::sqrt( dx * dx + dy * dy );
1024
1025 prevX = *x++;
1026 prevY = *y++;
1027 }
1028 return total;
1029}
1030
1031std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsLineString::splitCurveAtVertex( int index ) const
1032{
1033 const bool useZ = is3D();
1034 const bool useM = isMeasure();
1035
1036 const int size = mX.size();
1037 if ( size == 0 )
1038 return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >() );
1039
1040 index = std::clamp( index, 0, size - 1 );
1041
1042 const int part1Size = index + 1;
1043 QVector< double > x1( part1Size );
1044 QVector< double > y1( part1Size );
1045 QVector< double > z1( useZ ? part1Size : 0 );
1046 QVector< double > m1( useM ? part1Size : 0 );
1047
1048 const double *sourceX = mX.constData();
1049 const double *sourceY = mY.constData();
1050 const double *sourceZ = useZ ? mZ.constData() : nullptr;
1051 const double *sourceM = useM ? mM.constData() : nullptr;
1052
1053 double *destX = x1.data();
1054 double *destY = y1.data();
1055 double *destZ = useZ ? z1.data() : nullptr;
1056 double *destM = useM ? m1.data() : nullptr;
1057
1058 std::copy( sourceX, sourceX + part1Size, destX );
1059 std::copy( sourceY, sourceY + part1Size, destY );
1060 if ( useZ )
1061 std::copy( sourceZ, sourceZ + part1Size, destZ );
1062 if ( useM )
1063 std::copy( sourceM, sourceM + part1Size, destM );
1064
1065 const int part2Size = size - index;
1066 if ( part2Size < 2 )
1067 return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >() );
1068
1069 QVector< double > x2( part2Size );
1070 QVector< double > y2( part2Size );
1071 QVector< double > z2( useZ ? part2Size : 0 );
1072 QVector< double > m2( useM ? part2Size : 0 );
1073 destX = x2.data();
1074 destY = y2.data();
1075 destZ = useZ ? z2.data() : nullptr;
1076 destM = useM ? m2.data() : nullptr;
1077 std::copy( sourceX + index, sourceX + size, destX );
1078 std::copy( sourceY + index, sourceY + size, destY );
1079 if ( useZ )
1080 std::copy( sourceZ + index, sourceZ + size, destZ );
1081 if ( useM )
1082 std::copy( sourceM + index, sourceM + size, destM );
1083
1084 if ( part1Size < 2 )
1085 return std::make_tuple( std::make_unique< QgsLineString >(), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
1086 else
1087 return std::make_tuple( std::make_unique< QgsLineString >( x1, y1, z1, m1 ), std::make_unique< QgsLineString >( x2, y2, z2, m2 ) );
1088}
1089
1090QVector<QgsLineString *> QgsLineString::splitToDisjointXYParts() const
1091{
1092 const double *allPointsX = xData();
1093 const double *allPointsY = yData();
1094 size_t allPointsCount = numPoints();
1095 QVector<double> partX;
1096 QVector<double> partY;
1097 QSet<QgsPointXY> partPointSet;
1098
1099 QVector<QgsLineString *> disjointParts;
1100 for ( size_t i = 0; i < allPointsCount; i++ )
1101 {
1102 const QgsPointXY point( *allPointsX++, *allPointsY++ );
1103 if ( partPointSet.contains( point ) )
1104 {
1105 // This point is used multiple times, cut the curve and add the
1106 // current part
1107 disjointParts.push_back( new QgsLineString( partX, partY ) );
1108 // Now start a new part containing the last line
1109 partX = { partX.last() };
1110 partY = { partY.last() };
1111 partPointSet = { QgsPointXY( partX[0], partY[0] ) };
1112 }
1113 partX.push_back( point.x() );
1114 partY.push_back( point.y() );
1115 partPointSet.insert( point );
1116 }
1117 // Add the last part (if we didn't stop by closing the loop)
1118 if ( partX.size() > 1 || disjointParts.size() == 0 )
1119 disjointParts.push_back( new QgsLineString( partX, partY ) );
1120
1121 return disjointParts;
1122}
1123
1125{
1126 if ( is3D() )
1127 {
1128 double total = 0;
1129 const int size = mX.size();
1130 if ( size < 2 )
1131 return 0;
1132
1133 const double *x = mX.constData();
1134 const double *y = mY.constData();
1135 const double *z = mZ.constData();
1136 double dx, dy, dz;
1137
1138 double prevX = *x++;
1139 double prevY = *y++;
1140 double prevZ = *z++;
1141
1142 for ( int i = 1; i < size; ++i )
1143 {
1144 dx = *x - prevX;
1145 dy = *y - prevY;
1146 dz = *z - prevZ;
1147 total += std::sqrt( dx * dx + dy * dy + dz * dz );
1148
1149 prevX = *x++;
1150 prevY = *y++;
1151 prevZ = *z++;
1152 }
1153 return total;
1154 }
1155 else
1156 {
1157 return length();
1158 }
1159}
1160
1162{
1163 if ( numPoints() < 1 )
1164 {
1165 return QgsPoint();
1166 }
1167 return pointN( 0 );
1168}
1169
1171{
1172 if ( numPoints() < 1 )
1173 {
1174 return QgsPoint();
1175 }
1176 return pointN( numPoints() - 1 );
1177}
1178
1179/***************************************************************************
1180 * This class is considered CRITICAL and any change MUST be accompanied with
1181 * full unit tests.
1182 * See details in QEP #17
1183 ****************************************************************************/
1184
1186{
1187 Q_UNUSED( tolerance )
1188 Q_UNUSED( toleranceType )
1189 return clone();
1190}
1191
1193{
1194 return mX.size();
1195}
1196
1198{
1199 return mX.size();
1200}
1201
1203{
1204 if ( i < 0 || i >= mX.size() )
1205 {
1206 return QgsPoint();
1207 }
1208
1209 double x = mX.at( i );
1210 double y = mY.at( i );
1211 double z = std::numeric_limits<double>::quiet_NaN();
1212 double m = std::numeric_limits<double>::quiet_NaN();
1213
1214 bool hasZ = is3D();
1215 if ( hasZ )
1216 {
1217 z = mZ.at( i );
1218 }
1219 bool hasM = isMeasure();
1220 if ( hasM )
1221 {
1222 m = mM.at( i );
1223 }
1224
1227 {
1229 }
1230 else if ( hasZ && hasM )
1231 {
1233 }
1234 else if ( hasZ )
1235 {
1237 }
1238 else if ( hasM )
1239 {
1241 }
1242 return QgsPoint( t, x, y, z, m );
1243}
1244
1245/***************************************************************************
1246 * This class is considered CRITICAL and any change MUST be accompanied with
1247 * full unit tests.
1248 * See details in QEP #17
1249 ****************************************************************************/
1250
1251double QgsLineString::xAt( int index ) const
1252{
1253 if ( index >= 0 && index < mX.size() )
1254 return mX.at( index );
1255 else
1256 return 0.0;
1257}
1258
1259double QgsLineString::yAt( int index ) const
1260{
1261 if ( index >= 0 && index < mY.size() )
1262 return mY.at( index );
1263 else
1264 return 0.0;
1265}
1266
1267void QgsLineString::setXAt( int index, double x )
1268{
1269 if ( index >= 0 && index < mX.size() )
1270 mX[ index ] = x;
1271 clearCache();
1272}
1273
1274void QgsLineString::setYAt( int index, double y )
1275{
1276 if ( index >= 0 && index < mY.size() )
1277 mY[ index ] = y;
1278 clearCache();
1279}
1280
1281/***************************************************************************
1282 * This class is considered CRITICAL and any change MUST be accompanied with
1283 * full unit tests.
1284 * See details in QEP #17
1285 ****************************************************************************/
1286
1288{
1289 pts.clear();
1290 int nPoints = numPoints();
1291 pts.reserve( nPoints );
1292 for ( int i = 0; i < nPoints; ++i )
1293 {
1294 pts.push_back( pointN( i ) );
1295 }
1296}
1297
1298void QgsLineString::setPoints( size_t size, const double *x, const double *y, const double *z, const double *m )
1299{
1300 clearCache(); //set bounding box invalid
1301
1302 if ( size == 0 )
1303 {
1304 clear();
1305 return;
1306 }
1307
1308 const bool hasZ = static_cast< bool >( z );
1309 const bool hasM = static_cast< bool >( m );
1310
1311 if ( hasZ && hasM )
1312 {
1314 }
1315 else if ( hasZ )
1316 {
1318 }
1319 else if ( hasM )
1320 {
1322 }
1323 else
1324 {
1326 }
1327
1328 mX.resize( size );
1329 mY.resize( size );
1330 double *destX = mX.data();
1331 double *destY = mY.data();
1332 double *destZ = nullptr;
1333 if ( hasZ )
1334 {
1335 mZ.resize( size );
1336 destZ = mZ.data();
1337 }
1338 else
1339 {
1340 mZ.clear();
1341 }
1342 double *destM = nullptr;
1343 if ( hasM )
1344 {
1345 mM.resize( size );
1346 destM = mM.data();
1347 }
1348 else
1349 {
1350 mM.clear();
1351 }
1352
1353 for ( size_t i = 0; i < size; ++i )
1354 {
1355 *destX++ = *x++;
1356 *destY++ = *y++;
1357 if ( hasZ )
1358 {
1359 *destZ++ = *z++;
1360 }
1361 if ( hasM )
1362 {
1363 *destM++ = *m++;
1364 }
1365 }
1366}
1367
1369{
1370 clearCache(); //set bounding box invalid
1371
1372 if ( points.isEmpty() )
1373 {
1374 clear();
1375 return;
1376 }
1377
1378 //get wkb type from first point
1379 const QgsPoint &firstPt = points.at( 0 );
1380 bool hasZ = firstPt.is3D();
1381 bool hasM = firstPt.isMeasure();
1382
1384
1385 mX.resize( points.size() );
1386 mY.resize( points.size() );
1387 if ( hasZ )
1388 {
1389 mZ.resize( points.size() );
1390 }
1391 else
1392 {
1393 mZ.clear();
1394 }
1395 if ( hasM )
1396 {
1397 mM.resize( points.size() );
1398 }
1399 else
1400 {
1401 mM.clear();
1402 }
1403
1404 for ( int i = 0; i < points.size(); ++i )
1405 {
1406 mX[i] = points.at( i ).x();
1407 mY[i] = points.at( i ).y();
1408 if ( hasZ )
1409 {
1410 double z = points.at( i ).z();
1411 mZ[i] = std::isnan( z ) ? 0 : z;
1412 }
1413 if ( hasM )
1414 {
1415 double m = points.at( i ).m();
1416 mM[i] = std::isnan( m ) ? 0 : m;
1417 }
1418 }
1419}
1420
1421/***************************************************************************
1422 * This class is considered CRITICAL and any change MUST be accompanied with
1423 * full unit tests.
1424 * See details in QEP #17
1425 ****************************************************************************/
1426
1428{
1429 if ( !line )
1430 {
1431 return;
1432 }
1433
1434 if ( numPoints() < 1 )
1435 {
1437 }
1438
1439 // do not store duplicate points
1440 if ( numPoints() > 0 &&
1441 line->numPoints() > 0 &&
1442 endPoint() == line->startPoint() )
1443 {
1444 mX.pop_back();
1445 mY.pop_back();
1446
1447 if ( is3D() )
1448 {
1449 mZ.pop_back();
1450 }
1451 if ( isMeasure() )
1452 {
1453 mM.pop_back();
1454 }
1455 }
1456
1457 mX += line->mX;
1458 mY += line->mY;
1459
1460 if ( is3D() )
1461 {
1462 if ( line->is3D() )
1463 {
1464 mZ += line->mZ;
1465 }
1466 else
1467 {
1468 // if append line does not have z coordinates, fill with NaN to match number of points in final line
1469 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1470 }
1471 }
1472
1473 if ( isMeasure() )
1474 {
1475 if ( line->isMeasure() )
1476 {
1477 mM += line->mM;
1478 }
1479 else
1480 {
1481 // if append line does not have m values, fill with NaN to match number of points in final line
1482 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1483 }
1484 }
1485
1486 clearCache(); //set bounding box invalid
1487}
1488
1490{
1491 QgsLineString *copy = clone();
1492 std::reverse( copy->mX.begin(), copy->mX.end() );
1493 std::reverse( copy->mY.begin(), copy->mY.end() );
1494 if ( copy->is3D() )
1495 {
1496 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1497 }
1498 if ( copy->isMeasure() )
1499 {
1500 std::reverse( copy->mM.begin(), copy->mM.end() );
1501 }
1502
1504 return copy;
1505}
1506
1507void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
1508{
1509 if ( distance < 0 )
1510 return;
1511
1512 double distanceTraversed = 0;
1513 const int totalPoints = numPoints();
1514 if ( totalPoints == 0 )
1515 return;
1516
1517 const double *x = mX.constData();
1518 const double *y = mY.constData();
1519 const double *z = is3D() ? mZ.constData() : nullptr;
1520 const double *m = isMeasure() ? mM.constData() : nullptr;
1521
1522 double prevX = *x++;
1523 double prevY = *y++;
1524 double prevZ = z ? *z++ : 0.0;
1525 double prevM = m ? *m++ : 0.0;
1526
1527 if ( qgsDoubleNear( distance, 0.0 ) )
1528 {
1529 visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
1530 return;
1531 }
1532
1533 double pZ = std::numeric_limits<double>::quiet_NaN();
1534 double pM = std::numeric_limits<double>::quiet_NaN();
1535 double nextPointDistance = distance;
1536 const double eps = 4 * nextPointDistance * std::numeric_limits<double>::epsilon();
1537 for ( int i = 1; i < totalPoints; ++i )
1538 {
1539 double thisX = *x++;
1540 double thisY = *y++;
1541 double thisZ = z ? *z++ : 0.0;
1542 double thisM = m ? *m++ : 0.0;
1543
1544 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1545 while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength, eps ) )
1546 {
1547 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1548 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
1549 double pX, pY;
1550 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
1551 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
1552 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
1553
1554 if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1555 return;
1556
1557 nextPointDistance += distance;
1558 }
1559
1560 distanceTraversed += segmentLength;
1561 prevX = thisX;
1562 prevY = thisY;
1563 prevZ = thisZ;
1564 prevM = thisM;
1565 }
1566}
1567
1568QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
1569{
1570 if ( distance < 0 )
1571 return nullptr;
1572
1574 if ( is3D() )
1575 pointType = Qgis::WkbType::PointZ;
1576 if ( isMeasure() )
1577 pointType = QgsWkbTypes::addM( pointType );
1578
1579 std::unique_ptr< QgsPoint > res;
1580 visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
1581 {
1582 res = std::make_unique< QgsPoint >( pointType, x, y, z, m );
1583 return false;
1584 } );
1585 return res.release();
1586}
1587
1588bool QgsLineString::lineLocatePointByM( double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance ) const
1589{
1590 return lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance, false );
1591}
1592
1593bool QgsLineString::lineLocatePointByMPrivate( double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance, bool haveInterpolatedM ) const
1594{
1595 if ( !isMeasure() )
1596 return false;
1597
1598 distanceFromStart = 0;
1599 const int totalPoints = numPoints();
1600 if ( totalPoints == 0 )
1601 return false;
1602
1603 const double *xData = mX.constData();
1604 const double *yData = mY.constData();
1605 const double *mData = mM.constData();
1606
1607 const double *zData = is3D() ? mZ.constData() : nullptr;
1608 use3DDistance &= static_cast< bool >( zData );
1609
1610 double prevX = *xData++;
1611 double prevY = *yData++;
1612 double prevZ = zData ? *zData++ : 0;
1613 double prevM = *mData++;
1614
1615 int i = 1;
1616 while ( i < totalPoints )
1617 {
1618 double thisX = *xData++;
1619 double thisY = *yData++;
1620 double thisZ = zData ? *zData++ : 0;
1621 double thisM = *mData++;
1622 const double segmentLength = use3DDistance ? QgsGeometryUtilsBase::distance3D( thisX, thisY, thisZ, prevX, prevY, prevZ )
1623 : QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1624
1625 if ( std::isnan( thisM ) )
1626 {
1627 if ( haveInterpolatedM )
1628 return false;
1629
1630 // if we hit a NaN m value, interpolate m to fill the blanks and then re-try
1631 std::unique_ptr< QgsLineString > interpolatedM( interpolateM( use3DDistance ) );
1632 return interpolatedM->lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance, true );
1633 }
1634 else
1635 {
1636 // check if target m value falls within this segment's range
1637 if ( ( prevM < m && thisM > m ) || ( prevM > m && thisM < m ) || qgsDoubleNear( prevM, m ) || qgsDoubleNear( thisM, m ) )
1638 {
1639 // use centroid for constant value m segments
1640 if ( qgsDoubleNear( thisM, m ) && ( i < totalPoints - 1 ) && qgsDoubleNear( *mData, m ) )
1641 {
1642 distanceFromStart += segmentLength;
1643 // scan ahead till we find a vertex with a different m
1644 double totalLengthOfSegmentsWithConstantM = 0;
1645 for ( int j = 0; j < ( totalPoints - i ); ++j )
1646 {
1647 if ( !qgsDoubleNear( *( mData + j ), m ) )
1648 break;
1649
1650 const double segmentLength = use3DDistance ? QgsGeometryUtilsBase::distance3D( *( xData + j - 1 ), *( yData + j - 1 ), *( zData + j - 1 ), *( xData + j ), *( yData + j ), *( zData + j ) )
1651 : QgsGeometryUtilsBase::distance2D( *( xData + j - 1 ), *( yData + j - 1 ), *( xData + j ), *( yData + j ) );
1652 totalLengthOfSegmentsWithConstantM += segmentLength;
1653 }
1654
1655 distanceFromStart += totalLengthOfSegmentsWithConstantM / 2;
1656 std::unique_ptr< QgsPoint> point( interpolatePoint( distanceFromStart ) );
1657 if ( !point )
1658 return false;
1659 x = point->x();
1660 y = point->y();
1661 z = point->z();
1662 return true;
1663 }
1664
1665 const double delta = ( m - prevM ) / ( thisM - prevM );
1666
1667 const double distanceToPoint = delta * segmentLength;
1668
1669 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, x, y );
1670 z = prevZ + ( thisZ - prevZ ) * delta;
1671 distanceFromStart += distanceToPoint;
1672 return true;
1673 }
1674 }
1675
1676 distanceFromStart += segmentLength;
1677 prevX = thisX;
1678 prevY = thisY;
1679 prevZ = thisZ;
1680 prevM = thisM;
1681 ++i;
1682 }
1683 return false;
1684}
1685
1686QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
1687{
1688 if ( startDistance < 0 && endDistance < 0 )
1689 return createEmptyWithSameType();
1690
1691 endDistance = std::max( startDistance, endDistance );
1692
1693 const int totalPoints = numPoints();
1694 if ( totalPoints == 0 )
1695 return clone();
1696
1697 QVector< QgsPoint > substringPoints;
1698 substringPoints.reserve( totalPoints );
1699
1701 if ( is3D() )
1702 pointType = Qgis::WkbType::PointZ;
1703 if ( isMeasure() )
1704 pointType = QgsWkbTypes::addM( pointType );
1705
1706 const double *x = mX.constData();
1707 const double *y = mY.constData();
1708 const double *z = is3D() ? mZ.constData() : nullptr;
1709 const double *m = isMeasure() ? mM.constData() : nullptr;
1710
1711 double distanceTraversed = 0;
1712 double prevX = *x++;
1713 double prevY = *y++;
1714 double prevZ = z ? *z++ : 0.0;
1715 double prevM = m ? *m++ : 0.0;
1716 bool foundStart = false;
1717
1718 if ( startDistance < 0 )
1719 startDistance = 0;
1720
1721 for ( int i = 1; i < totalPoints; ++i )
1722 {
1723 double thisX = *x++;
1724 double thisY = *y++;
1725 double thisZ = z ? *z++ : 0.0;
1726 double thisM = m ? *m++ : 0.0;
1727
1728 const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
1729
1730 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1731 {
1732 // start point falls on this segment
1733 const double distanceToStart = startDistance - distanceTraversed;
1734 double startX, startY;
1735 double startZ = 0;
1736 double startM = 0;
1737 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY,
1738 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &startZ : nullptr,
1739 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &startM : nullptr );
1740 substringPoints << QgsPoint( pointType, startX, startY, startZ, startM );
1741 foundStart = true;
1742 }
1743 if ( foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1744 {
1745 // end point falls on this segment
1746 const double distanceToEnd = endDistance - distanceTraversed;
1747 double endX, endY;
1748 double endZ = 0;
1749 double endM = 0;
1750 QgsGeometryUtilsBase::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY,
1751 z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &endZ : nullptr,
1752 m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &endM : nullptr );
1753 substringPoints << QgsPoint( pointType, endX, endY, endZ, endM );
1754 }
1755 else if ( foundStart )
1756 {
1757 substringPoints << QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1758 }
1759
1760 prevX = thisX;
1761 prevY = thisY;
1762 prevZ = thisZ;
1763 prevM = thisM;
1764 distanceTraversed += segmentLength;
1765 if ( distanceTraversed >= endDistance )
1766 break;
1767 }
1768
1769 // start point is the last node
1770 if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1771 {
1772 substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1773 << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1774 }
1775
1776 return new QgsLineString( substringPoints );
1777}
1778
1779/***************************************************************************
1780 * This class is considered CRITICAL and any change MUST be accompanied with
1781 * full unit tests.
1782 * See details in QEP #17
1783 ****************************************************************************/
1784
1785void QgsLineString::draw( QPainter &p ) const
1786{
1787 p.drawPolyline( asQPolygonF() );
1788}
1789
1790void QgsLineString::addToPainterPath( QPainterPath &path ) const
1791{
1792 int nPoints = numPoints();
1793 if ( nPoints < 1 )
1794 {
1795 return;
1796 }
1797
1798 if ( path.isEmpty() || path.currentPosition() != QPointF( mX.at( 0 ), mY.at( 0 ) ) )
1799 {
1800 path.moveTo( mX.at( 0 ), mY.at( 0 ) );
1801 }
1802
1803 for ( int i = 1; i < nPoints; ++i )
1804 {
1805 path.lineTo( mX.at( i ), mY.at( i ) );
1806 }
1807}
1808
1809void QgsLineString::drawAsPolygon( QPainter &p ) const
1810{
1811 p.drawPolygon( asQPolygonF() );
1812}
1813
1815{
1816 QgsCompoundCurve *compoundCurve = new QgsCompoundCurve();
1817 compoundCurve->addCurve( clone() );
1818 return compoundCurve;
1819}
1820
1821void QgsLineString::extend( double startDistance, double endDistance )
1822{
1823 if ( mX.size() < 2 || mY.size() < 2 )
1824 return;
1825
1826 const bool extendStart = startDistance > 0;
1827 const bool extendEnd = endDistance > 0;
1828
1829 // start of line
1830 if ( extendStart )
1831 {
1832 const double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) +
1833 std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) );
1834 const double newLen = currentLen + startDistance;
1835 mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen;
1836 mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen;
1837 }
1838 // end of line
1839 if ( extendEnd )
1840 {
1841 const int last = mX.size() - 1;
1842 const double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) +
1843 std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) );
1844 const double newLen = currentLen + endDistance;
1845 mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen;
1846 mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen;
1847 }
1848
1849 if ( extendStart || extendEnd )
1850 clearCache(); //set bounding box invalid
1851}
1852
1854{
1855 auto result = std::make_unique< QgsLineString >();
1856 result->mWkbType = mWkbType;
1857 return result.release();
1858}
1859
1861{
1862 const QgsLineString *otherLine = qgsgeometry_cast<const QgsLineString *>( other );
1863 if ( !otherLine )
1864 return -1;
1865
1866 const int size = mX.size();
1867 const int otherSize = otherLine->mX.size();
1868 if ( size > otherSize )
1869 {
1870 return 1;
1871 }
1872 else if ( size < otherSize )
1873 {
1874 return -1;
1875 }
1876
1877 if ( is3D() && !otherLine->is3D() )
1878 return 1;
1879 else if ( !is3D() && otherLine->is3D() )
1880 return -1;
1881 const bool considerZ = is3D();
1882
1883 if ( isMeasure() && !otherLine->isMeasure() )
1884 return 1;
1885 else if ( !isMeasure() && otherLine->isMeasure() )
1886 return -1;
1887 const bool considerM = isMeasure();
1888
1889 for ( int i = 0; i < size; i++ )
1890 {
1891 const double x = mX[i];
1892 const double otherX = otherLine->mX[i];
1893 if ( x < otherX )
1894 {
1895 return -1;
1896 }
1897 else if ( x > otherX )
1898 {
1899 return 1;
1900 }
1901
1902 const double y = mY[i];
1903 const double otherY = otherLine->mY[i];
1904 if ( y < otherY )
1905 {
1906 return -1;
1907 }
1908 else if ( y > otherY )
1909 {
1910 return 1;
1911 }
1912
1913 if ( considerZ )
1914 {
1915 const double z = mZ[i];
1916 const double otherZ = otherLine->mZ[i];
1917
1918 if ( z < otherZ )
1919 {
1920 return -1;
1921 }
1922 else if ( z > otherZ )
1923 {
1924 return 1;
1925 }
1926 }
1927
1928 if ( considerM )
1929 {
1930 const double m = mM[i];
1931 const double otherM = otherLine->mM[i];
1932
1933 if ( m < otherM )
1934 {
1935 return -1;
1936 }
1937 else if ( m > otherM )
1938 {
1939 return 1;
1940 }
1941 }
1942 }
1943 return 0;
1944}
1945
1947{
1948 return QStringLiteral( "LineString" );
1949}
1950
1952{
1953 return 1;
1954}
1955
1956/***************************************************************************
1957 * This class is considered CRITICAL and any change MUST be accompanied with
1958 * full unit tests.
1959 * See details in QEP #17
1960 ****************************************************************************/
1961
1963{
1964 double *zArray = nullptr;
1965 bool hasZ = is3D();
1966 int nPoints = numPoints();
1967
1968 // it's possible that transformCoords will throw an exception - so we need to use
1969 // a smart pointer for the dummy z values in order to ensure that they always get cleaned up
1970 std::unique_ptr< double[] > dummyZ;
1971 if ( !hasZ || !transformZ )
1972 {
1973 dummyZ = std::make_unique<double[]>( nPoints );
1974 zArray = dummyZ.get();
1975 }
1976 else
1977 {
1978 zArray = mZ.data();
1979 }
1980 ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1981 clearCache();
1982}
1983
1984void QgsLineString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1985{
1986 int nPoints = numPoints();
1987 bool hasZ = is3D();
1988 bool hasM = isMeasure();
1989 double *x = mX.data();
1990 double *y = mY.data();
1991 double *z = hasZ ? mZ.data() : nullptr;
1992 double *m = hasM ? mM.data() : nullptr;
1993 for ( int i = 0; i < nPoints; ++i )
1994 {
1995 double xOut, yOut;
1996 t.map( *x, *y, &xOut, &yOut );
1997 *x++ = xOut;
1998 *y++ = yOut;
1999 if ( hasZ )
2000 {
2001 *z = *z * zScale + zTranslate;
2002 z++;
2003 }
2004 if ( hasM )
2005 {
2006 *m = *m * mScale + mTranslate;
2007 m++;
2008 }
2009 }
2010 clearCache();
2011}
2012
2013/***************************************************************************
2014 * This class is considered CRITICAL and any change MUST be accompanied with
2015 * full unit tests.
2016 * See details in QEP #17
2017 ****************************************************************************/
2018
2019bool QgsLineString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
2020{
2021 if ( position.vertex < 0 || position.vertex > mX.size() )
2022 {
2023 return false;
2024 }
2025
2026 if ( mWkbType == Qgis::WkbType::Unknown || mX.isEmpty() )
2027 {
2029 }
2030
2031 mX.insert( position.vertex, vertex.x() );
2032 mY.insert( position.vertex, vertex.y() );
2033 if ( is3D() )
2034 {
2035 mZ.insert( position.vertex, vertex.z() );
2036 }
2037 if ( isMeasure() )
2038 {
2039 mM.insert( position.vertex, vertex.m() );
2040 }
2041 clearCache(); //set bounding box invalid
2042 return true;
2043}
2044
2045bool QgsLineString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
2046{
2047 if ( position.vertex < 0 || position.vertex >= mX.size() )
2048 {
2049 return false;
2050 }
2051 mX[position.vertex] = newPos.x();
2052 mY[position.vertex] = newPos.y();
2053 if ( is3D() && newPos.is3D() )
2054 {
2055 mZ[position.vertex] = newPos.z();
2056 }
2057 if ( isMeasure() && newPos.isMeasure() )
2058 {
2059 mM[position.vertex] = newPos.m();
2060 }
2061 clearCache(); //set bounding box invalid
2062 return true;
2063}
2064
2066{
2067 if ( position.vertex >= mX.size() || position.vertex < 0 )
2068 {
2069 return false;
2070 }
2071
2072 mX.remove( position.vertex );
2073 mY.remove( position.vertex );
2074 if ( is3D() )
2075 {
2076 mZ.remove( position.vertex );
2077 }
2078 if ( isMeasure() )
2079 {
2080 mM.remove( position.vertex );
2081 }
2082
2083 if ( numPoints() == 1 )
2084 {
2085 clear();
2086 }
2087
2088 clearCache(); //set bounding box invalid
2089 return true;
2090}
2091
2092/***************************************************************************
2093 * This class is considered CRITICAL and any change MUST be accompanied with
2094 * full unit tests.
2095 * See details in QEP #17
2096 ****************************************************************************/
2097
2099{
2100 if ( mWkbType == Qgis::WkbType::Unknown || mX.isEmpty() )
2101 {
2103 }
2104
2105 mX.append( pt.x() );
2106 mY.append( pt.y() );
2107 if ( is3D() )
2108 {
2109 mZ.append( pt.z() );
2110 }
2111 if ( isMeasure() )
2112 {
2113 mM.append( pt.m() );
2114 }
2115 clearCache(); //set bounding box invalid
2116}
2117
2118double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
2119{
2120 double sqrDist = std::numeric_limits<double>::max();
2121 double leftOfDist = std::numeric_limits<double>::max();
2122 int prevLeftOf = 0;
2123 double prevLeftOfX = 0.0;
2124 double prevLeftOfY = 0.0;
2125 double testDist = 0;
2126 double segmentPtX, segmentPtY;
2127
2128 if ( leftOf )
2129 *leftOf = 0;
2130
2131 const int size = mX.size();
2132 if ( size == 0 || size == 1 )
2133 {
2134 vertexAfter = QgsVertexId( 0, 0, 0 );
2135 return -1;
2136 }
2137
2138 const double *xData = mX.constData();
2139 const double *yData = mY.constData();
2140 for ( int i = 1; i < size; ++i )
2141 {
2142 double prevX = xData[ i - 1 ];
2143 double prevY = yData[ i - 1 ];
2144 double currentX = xData[ i ];
2145 double currentY = yData[ i ];
2146 testDist = QgsGeometryUtilsBase::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
2147 if ( testDist < sqrDist )
2148 {
2149 sqrDist = testDist;
2150 segmentPt.setX( segmentPtX );
2151 segmentPt.setY( segmentPtY );
2152 vertexAfter.part = 0;
2153 vertexAfter.ring = 0;
2154 vertexAfter.vertex = i;
2155 }
2156 if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
2157 {
2158 int left = QgsGeometryUtilsBase::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
2159 // if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
2160 // so don't set leftOf in this case, and hope that there's another segment that's the same distance
2161 // where we can perform the check
2162 if ( left != 0 )
2163 {
2164 if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
2165 {
2166 // we have two possible segments each with equal distance to point, but they disagree
2167 // on whether or not the point is to the left of them.
2168 // so we test the segments themselves and flip the result.
2169 // see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
2170 *leftOf = -QgsGeometryUtilsBase::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
2171 }
2172 else
2173 {
2174 *leftOf = left;
2175 }
2176 prevLeftOf = *leftOf;
2177 leftOfDist = testDist;
2178 prevLeftOfX = prevX;
2179 prevLeftOfY = prevY;
2180 }
2181 else if ( testDist < leftOfDist )
2182 {
2183 *leftOf = left;
2184 leftOfDist = testDist;
2185 prevLeftOf = 0;
2186 }
2187 }
2188 }
2189 return sqrDist;
2190}
2191
2192/***************************************************************************
2193 * This class is considered CRITICAL and any change MUST be accompanied with
2194 * full unit tests.
2195 * See details in QEP #17
2196 ****************************************************************************/
2197
2198bool QgsLineString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
2199{
2200 if ( node < 0 || node >= numPoints() )
2201 {
2202 return false;
2203 }
2204 point = pointN( node );
2206 return true;
2207}
2208
2210{
2211 if ( mX.isEmpty() )
2212 return QgsPoint();
2213
2214 int numPoints = mX.count();
2215 if ( numPoints == 1 )
2216 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
2217
2218 double totalLineLength = 0.0;
2219 double prevX = mX.at( 0 );
2220 double prevY = mY.at( 0 );
2221 double sumX = 0.0;
2222 double sumY = 0.0;
2223
2224 for ( int i = 1; i < numPoints ; ++i )
2225 {
2226 double currentX = mX.at( i );
2227 double currentY = mY.at( i );
2228 double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) +
2229 std::pow( currentY - prevY, 2.0 ) );
2230 if ( qgsDoubleNear( segmentLength, 0.0 ) )
2231 continue;
2232
2233 totalLineLength += segmentLength;
2234 sumX += segmentLength * ( currentX + prevX );
2235 sumY += segmentLength * ( currentY + prevY );
2236 prevX = currentX;
2237 prevY = currentY;
2238 }
2239 sumX *= 0.5;
2240 sumY *= 0.5;
2241
2242 if ( qgsDoubleNear( totalLineLength, 0.0 ) )
2243 return QgsPoint( mX.at( 0 ), mY.at( 0 ) );
2244 else
2245 return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
2246
2247}
2248
2249/***************************************************************************
2250 * This class is considered CRITICAL and any change MUST be accompanied with
2251 * full unit tests.
2252 * See details in QEP #17
2253 ****************************************************************************/
2254
2255void QgsLineString::sumUpArea( double &sum ) const
2256{
2258 {
2259 sum += mSummedUpArea;
2260 return;
2261 }
2262
2263 mSummedUpArea = 0;
2264 const int maxIndex = mX.size();
2265 if ( maxIndex < 2 )
2266 {
2268 return;
2269 }
2270
2271 const double *x = mX.constData();
2272 const double *y = mY.constData();
2273 double prevX = *x++;
2274 double prevY = *y++;
2275 for ( int i = 1; i < maxIndex; ++i )
2276 {
2277 mSummedUpArea += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
2278 prevX = *x++;
2279 prevY = *y++;
2280 }
2281 mSummedUpArea *= 0.5;
2282
2284 sum += mSummedUpArea;
2285}
2286
2287void QgsLineString::importVerticesFromWkb( const QgsConstWkbPtr &wkb )
2288{
2289 bool hasZ = is3D();
2290 bool hasM = isMeasure();
2291 int nVertices = 0;
2292 wkb >> nVertices;
2293 mX.resize( nVertices );
2294 mY.resize( nVertices );
2295 hasZ ? mZ.resize( nVertices ) : mZ.clear();
2296 hasM ? mM.resize( nVertices ) : mM.clear();
2297 double *x = mX.data();
2298 double *y = mY.data();
2299 double *m = hasM ? mM.data() : nullptr;
2300 double *z = hasZ ? mZ.data() : nullptr;
2301 for ( int i = 0; i < nVertices; ++i )
2302 {
2303 wkb >> *x++;
2304 wkb >> *y++;
2305 if ( hasZ )
2306 {
2307 wkb >> *z++;
2308 }
2309 if ( hasM )
2310 {
2311 wkb >> *m++;
2312 }
2313 }
2314 clearCache(); //set bounding box invalid
2315}
2316
2317/***************************************************************************
2318 * This class is considered CRITICAL and any change MUST be accompanied with
2319 * full unit tests.
2320 * See details in QEP #17
2321 ****************************************************************************/
2322
2324{
2325 if ( numPoints() < 1 || isClosed() )
2326 {
2327 return;
2328 }
2329 addVertex( startPoint() );
2330}
2331
2333{
2334 if ( mX.count() < 2 )
2335 {
2336 //undefined
2337 return 0.0;
2338 }
2339
2340 if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
2341 {
2342 if ( isClosed() )
2343 {
2344 double previousX = mX.at( numPoints() - 2 );
2345 double previousY = mY.at( numPoints() - 2 );
2346 double currentX = mX.at( 0 );
2347 double currentY = mY.at( 0 );
2348 double afterX = mX.at( 1 );
2349 double afterY = mY.at( 1 );
2350 return QgsGeometryUtilsBase::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2351 }
2352 else if ( vertex.vertex == 0 )
2353 {
2354 return QgsGeometryUtilsBase::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
2355 }
2356 else
2357 {
2358 int a = numPoints() - 2;
2359 int b = numPoints() - 1;
2360 return QgsGeometryUtilsBase::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
2361 }
2362 }
2363 else
2364 {
2365 double previousX = mX.at( vertex.vertex - 1 );
2366 double previousY = mY.at( vertex.vertex - 1 );
2367 double currentX = mX.at( vertex.vertex );
2368 double currentY = mY.at( vertex.vertex );
2369 double afterX = mX.at( vertex.vertex + 1 );
2370 double afterY = mY.at( vertex.vertex + 1 );
2371 return QgsGeometryUtilsBase::averageAngle( previousX, previousY, currentX, currentY, afterX, afterY );
2372 }
2373}
2374
2376{
2377 if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 1 )
2378 return 0.0;
2379
2380 double dx = mX.at( startVertex.vertex + 1 ) - mX.at( startVertex.vertex );
2381 double dy = mY.at( startVertex.vertex + 1 ) - mY.at( startVertex.vertex );
2382 return std::sqrt( dx * dx + dy * dy );
2383}
2384
2385/***************************************************************************
2386 * This class is considered CRITICAL and any change MUST be accompanied with
2387 * full unit tests.
2388 * See details in QEP #17
2389 ****************************************************************************/
2390
2391bool QgsLineString::addZValue( double zValue )
2392{
2393 if ( QgsWkbTypes::hasZ( mWkbType ) )
2394 return false;
2395
2396 clearCache();
2398 {
2400 return true;
2401 }
2402
2404
2405 mZ.clear();
2406 int nPoints = numPoints();
2407 mZ.reserve( nPoints );
2408 for ( int i = 0; i < nPoints; ++i )
2409 {
2410 mZ << zValue;
2411 }
2412 return true;
2413}
2414
2415bool QgsLineString::addMValue( double mValue )
2416{
2417 if ( QgsWkbTypes::hasM( mWkbType ) )
2418 return false;
2419
2420 clearCache();
2422 {
2424 return true;
2425 }
2426
2428 {
2430 }
2431 else
2432 {
2434 }
2435
2436 mM.clear();
2437 int nPoints = numPoints();
2438 mM.reserve( nPoints );
2439 for ( int i = 0; i < nPoints; ++i )
2440 {
2441 mM << mValue;
2442 }
2443 return true;
2444}
2445
2447{
2448 if ( !is3D() )
2449 return false;
2450
2451 clearCache();
2453 mZ.clear();
2454 return true;
2455}
2456
2458{
2459 if ( !isMeasure() )
2460 return false;
2461
2462 clearCache();
2464 mM.clear();
2465 return true;
2466}
2467
2469{
2470 std::swap( mX, mY );
2471 clearCache();
2472}
2473
2475{
2476 if ( type == mWkbType )
2477 return true;
2478
2479 clearCache();
2480 if ( type == Qgis::WkbType::LineString25D )
2481 {
2482 //special handling required for conversion to LineString25D
2483 dropMValue();
2484 addZValue( std::numeric_limits<double>::quiet_NaN() );
2486 return true;
2487 }
2488 else
2489 {
2490 return QgsCurve::convertTo( type );
2491 }
2492}
2493
2495{
2496 if ( !transformer )
2497 return false;
2498
2499 bool hasZ = is3D();
2500 bool hasM = isMeasure();
2501 int size = mX.size();
2502
2503 double *srcX = mX.data();
2504 double *srcY = mY.data();
2505 double *srcM = hasM ? mM.data() : nullptr;
2506 double *srcZ = hasZ ? mZ.data() : nullptr;
2507
2508 bool res = true;
2509 for ( int i = 0; i < size; ++i )
2510 {
2511 double x = *srcX;
2512 double y = *srcY;
2513 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2514 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2515 if ( !transformer->transformPoint( x, y, z, m ) )
2516 {
2517 res = false;
2518 break;
2519 }
2520
2521 *srcX++ = x;
2522 *srcY++ = y;
2523 if ( hasM )
2524 *srcM++ = m;
2525 if ( hasZ )
2526 *srcZ++ = z;
2527
2528 if ( feedback && feedback->isCanceled() )
2529 {
2530 res = false;
2531 break;
2532 }
2533 }
2534 clearCache();
2535 return res;
2536}
2537
2538void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
2539{
2540 bool hasZ = is3D();
2541 bool hasM = isMeasure();
2542 int size = mX.size();
2543
2544 double *srcX = mX.data();
2545 double *srcY = mY.data();
2546 double *srcM = hasM ? mM.data() : nullptr;
2547 double *srcZ = hasZ ? mZ.data() : nullptr;
2548
2549 double *destX = srcX;
2550 double *destY = srcY;
2551 double *destM = srcM;
2552 double *destZ = srcZ;
2553
2554 int filteredPoints = 0;
2555 for ( int i = 0; i < size; ++i )
2556 {
2557 double x = *srcX++;
2558 double y = *srcY++;
2559 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
2560 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
2561
2562 if ( filter( QgsPoint( x, y, z, m ) ) )
2563 {
2564 filteredPoints++;
2565 *destX++ = x;
2566 *destY++ = y;
2567 if ( hasM )
2568 *destM++ = m;
2569 if ( hasZ )
2570 *destZ++ = z;
2571 }
2572 }
2573
2574 mX.resize( filteredPoints );
2575 mY.resize( filteredPoints );
2576 if ( hasZ )
2577 mZ.resize( filteredPoints );
2578 if ( hasM )
2579 mM.resize( filteredPoints );
2580
2581 clearCache();
2582}
2583
2584void QgsLineString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
2585{
2586 bool hasZ = is3D();
2587 bool hasM = isMeasure();
2588 int size = mX.size();
2589
2590 double *srcX = mX.data();
2591 double *srcY = mY.data();
2592 double *srcM = hasM ? mM.data() : nullptr;
2593 double *srcZ = hasZ ? mZ.data() : nullptr;
2594
2595 for ( int i = 0; i < size; ++i )
2596 {
2597 double x = *srcX;
2598 double y = *srcY;
2599 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
2600 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
2601 QgsPoint res = transform( QgsPoint( x, y, z, m ) );
2602 *srcX++ = res.x();
2603 *srcY++ = res.y();
2604 if ( hasM )
2605 *srcM++ = res.m();
2606 if ( hasZ )
2607 *srcZ++ = res.z();
2608 }
2609 clearCache();
2610}
2611
2612
2613std::unique_ptr< QgsLineString > QgsLineString::measuredLine( double start, double end ) const
2614{
2615 const int nbpoints = numPoints();
2616 std::unique_ptr< QgsLineString > cloned( clone() );
2617
2618 if ( !cloned->convertTo( QgsWkbTypes::addM( mWkbType ) ) )
2619 {
2620 return cloned;
2621 }
2622
2623 if ( isEmpty() || ( nbpoints < 2 ) )
2624 {
2625 return cloned;
2626 }
2627
2628 const double range = end - start;
2629 double lineLength = length();
2630 double lengthSoFar = 0.0;
2631
2632
2633 double *mOut = cloned->mM.data();
2634 *mOut++ = start;
2635 for ( int i = 1; i < nbpoints ; ++i )
2636 {
2637 lengthSoFar += QgsGeometryUtilsBase::distance2D( mX[ i - 1], mY[ i - 1 ], mX[ i ], mY[ i ] );
2638 if ( lineLength > 0.0 )
2639 *mOut++ = start + range * lengthSoFar / lineLength;
2640 else if ( lineLength == 0.0 && nbpoints > 1 )
2641 *mOut++ = start + range * i / ( nbpoints - 1 );
2642 else
2643 *mOut++ = 0.0;
2644 }
2645
2646 return cloned;
2647}
2648
2649std::unique_ptr< QgsLineString > QgsLineString::interpolateM( bool use3DDistance ) const
2650{
2651 if ( !isMeasure() )
2652 return nullptr;
2653
2654 const int totalPoints = numPoints();
2655 if ( totalPoints < 2 )
2656 return std::unique_ptr< QgsLineString >( clone() );
2657
2658 const double *xData = mX.constData();
2659 const double *yData = mY.constData();
2660 const double *mData = mM.constData();
2661 const double *zData = is3D() ? mZ.constData() : nullptr;
2662 use3DDistance &= static_cast< bool >( zData );
2663
2664 QVector< double > xOut( totalPoints );
2665 QVector< double > yOut( totalPoints );
2666 QVector< double > mOut( totalPoints );
2667 QVector< double > zOut( static_cast< bool >( zData ) ? totalPoints : 0 );
2668
2669 double *xOutData = xOut.data();
2670 double *yOutData = yOut.data();
2671 double *mOutData = mOut.data();
2672 double *zOutData = static_cast< bool >( zData ) ? zOut.data() : nullptr;
2673
2674 int i = 0;
2675 double currentSegmentLength = 0;
2676 double lastValidM = std::numeric_limits< double >::quiet_NaN();
2677 double prevX = *xData;
2678 double prevY = *yData;
2679 double prevZ = zData ? *zData : 0;
2680 while ( i < totalPoints )
2681 {
2682 double thisX = *xData++;
2683 double thisY = *yData++;
2684 double thisZ = zData ? *zData++ : 0;
2685 double thisM = *mData++;
2686
2687 currentSegmentLength = use3DDistance
2688 ? QgsGeometryUtilsBase::distance3D( prevX, prevY, prevZ, thisX, thisY, thisZ )
2689 : QgsGeometryUtilsBase::distance2D( prevX, prevY, thisX, thisY );
2690
2691 if ( !std::isnan( thisM ) )
2692 {
2693 *xOutData++ = thisX;
2694 *yOutData++ = thisY;
2695 *mOutData++ = thisM;
2696 if ( zOutData )
2697 *zOutData++ = thisZ;
2698 lastValidM = thisM;
2699 }
2700 else if ( i == 0 )
2701 {
2702 // nan m value at start of line, read ahead to find first non-nan value and backfill
2703 int j = 0;
2704 double scanAheadM = thisM;
2705 while ( i + j + 1 < totalPoints && std::isnan( scanAheadM ) )
2706 {
2707 scanAheadM = mData[ j ];
2708 ++j;
2709 }
2710 if ( std::isnan( scanAheadM ) )
2711 {
2712 // no valid m values in line
2713 return nullptr;
2714 }
2715 *xOutData++ = thisX;
2716 *yOutData++ = thisY;
2717 *mOutData++ = scanAheadM;
2718 if ( zOutData )
2719 *zOutData++ = thisZ;
2720 for ( ; i < j; ++i )
2721 {
2722 thisX = *xData++;
2723 thisY = *yData++;
2724 *xOutData++ = thisX;
2725 *yOutData++ = thisY;
2726 *mOutData++ = scanAheadM;
2727 mData++;
2728 if ( zOutData )
2729 *zOutData++ = *zData++;
2730 }
2731 lastValidM = scanAheadM;
2732 }
2733 else
2734 {
2735 // nan m value in middle of line, read ahead till next non-nan value and interpolate
2736 int j = 0;
2737 double scanAheadX = thisX;
2738 double scanAheadY = thisY;
2739 double scanAheadZ = thisZ;
2740 double distanceToNextValidM = currentSegmentLength;
2741 std::vector< double > scanAheadSegmentLengths;
2742 scanAheadSegmentLengths.emplace_back( currentSegmentLength );
2743
2744 double nextValidM = std::numeric_limits< double >::quiet_NaN();
2745 while ( i + j < totalPoints - 1 )
2746 {
2747 double nextScanAheadX = xData[j];
2748 double nextScanAheadY = yData[j];
2749 double nextScanAheadZ = zData ? zData[j] : 0;
2750 double nextScanAheadM = mData[ j ];
2751 const double scanAheadSegmentLength = use3DDistance
2752 ? QgsGeometryUtilsBase::distance3D( scanAheadX, scanAheadY, scanAheadZ, nextScanAheadX, nextScanAheadY, nextScanAheadZ )
2753 : QgsGeometryUtilsBase::distance2D( scanAheadX, scanAheadY, nextScanAheadX, nextScanAheadY );
2754 scanAheadSegmentLengths.emplace_back( scanAheadSegmentLength );
2755 distanceToNextValidM += scanAheadSegmentLength;
2756
2757 if ( !std::isnan( nextScanAheadM ) )
2758 {
2759 nextValidM = nextScanAheadM;
2760 break;
2761 }
2762
2763 scanAheadX = nextScanAheadX;
2764 scanAheadY = nextScanAheadY;
2765 scanAheadZ = nextScanAheadZ;
2766 ++j;
2767 }
2768
2769 if ( std::isnan( nextValidM ) )
2770 {
2771 // no more valid m values, so just fill remainder of vertices with previous valid m value
2772 *xOutData++ = thisX;
2773 *yOutData++ = thisY;
2774 *mOutData++ = lastValidM;
2775 if ( zOutData )
2776 *zOutData++ = thisZ;
2777 ++i;
2778 for ( ; i < totalPoints; ++i )
2779 {
2780 *xOutData++ = *xData++;
2781 *yOutData++ = *yData++;
2782 *mOutData++ = lastValidM;
2783 if ( zOutData )
2784 *zOutData++ = *zData++;
2785 }
2786 break;
2787 }
2788 else
2789 {
2790 // interpolate along segments
2791 const double delta = ( nextValidM - lastValidM ) / distanceToNextValidM;
2792 *xOutData++ = thisX;
2793 *yOutData++ = thisY;
2794 *mOutData++ = lastValidM + delta * scanAheadSegmentLengths[0];
2795 double totalScanAheadLength = scanAheadSegmentLengths[0];
2796 if ( zOutData )
2797 *zOutData++ = thisZ;
2798 for ( int k = 1; k <= j; ++i, ++k )
2799 {
2800 thisX = *xData++;
2801 thisY = *yData++;
2802 *xOutData++ = thisX;
2803 *yOutData++ = thisY;
2804 totalScanAheadLength += scanAheadSegmentLengths[k];
2805 *mOutData++ = lastValidM + delta * totalScanAheadLength;
2806 mData++;
2807 if ( zOutData )
2808 *zOutData++ = *zData++;
2809 }
2810 lastValidM = nextValidM;
2811 }
2812 }
2813
2814 prevX = thisX;
2815 prevY = thisY;
2816 prevZ = thisZ;
2817 ++i;
2818 }
2819 return std::make_unique< QgsLineString >( xOut, yOut, zOut, mOut );
2820}
2821
2823{
2824 // Convert QgsVertexId to simple vertex numbers for linestrings (single ring, single part)
2825 if ( fromVertex.part != 0 || fromVertex.ring != 0 || toVertex.part != 0 || toVertex.ring != 0 )
2826 return -1.0;
2827
2828 const int fromVertexNumber = fromVertex.vertex;
2829 const int toVertexNumber = toVertex.vertex;
2830
2831 // Ensure fromVertex < toVertex for simplicity
2832 if ( fromVertexNumber > toVertexNumber )
2833 {
2834 return distanceBetweenVertices( QgsVertexId( 0, 0, toVertexNumber ), QgsVertexId( 0, 0, fromVertexNumber ) );
2835 }
2836
2837 const int nPoints = numPoints();
2838 if ( fromVertexNumber < 0 || fromVertexNumber >= nPoints || toVertexNumber < 0 || toVertexNumber >= nPoints )
2839 return -1.0;
2840
2841 if ( fromVertexNumber == toVertexNumber )
2842 return 0.0;
2843
2844 const bool is3DGeometry = is3D();
2845 const double *xData = mX.constData();
2846 const double *yData = mY.constData();
2847 const double *zData = is3DGeometry ? mZ.constData() : nullptr;
2848 double totalDistance = 0.0;
2849
2850 // For linestring, just accumulate Euclidean distances between consecutive points
2851 for ( int i = fromVertexNumber; i < toVertexNumber; ++i )
2852 {
2853 double dx = xData[i + 1] - xData[i];
2854 double dy = yData[i + 1] - yData[i];
2855 double dz = 0.0;
2856
2857 if ( is3DGeometry )
2858 {
2859 dz = zData[i + 1] - zData[i];
2860 }
2861
2862 totalDistance += std::sqrt( dx * dx + dy * dy + dz * dz );
2863 }
2864
2865 return totalDistance;
2866}
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2075
VertexType
Types of vertex.
Definition qgis.h:3066
@ Segment
The actual start or end point of a segment.
Definition qgis.h:3067
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
@ LineString25D
LineString25D.
Definition qgis.h:341
@ LineStringM
LineStringM.
Definition qgis.h:311
@ Point
Point.
Definition qgis.h:279
@ LineString
LineString.
Definition qgis.h:280
@ LineStringZM
LineStringZM.
Definition qgis.h:326
@ Unknown
Unknown.
Definition qgis.h:278
@ PointM
PointM.
Definition qgis.h:310
@ PointZ
PointZ.
Definition qgis.h:295
@ Point25D
Point25D.
Definition qgis.h:340
@ PointZM
PointZM.
Definition qgis.h:325
@ LineStringZ
LineStringZ.
Definition qgis.h:296
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2671
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.
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.
QFlags< WkbFlag > WkbFlags
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.
QgsAbstractGeometry()=default
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:42
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
Definition qgsbox3d.cpp:163
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:378
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:139
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:56
Handles coordinate transforms between two 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:294
bool mHasCachedSummedUpArea
Definition qgscurve.h:394
virtual bool isRing() const
Returns true if the curve is a ring.
Definition qgscurve.cpp:66
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:248
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, bool removeRedundantPoints) const
Helper function for QgsCurve subclasses to snap to grids.
Definition qgscurve.cpp:318
QgsBox3D mBoundingBox
Cached bounding box.
Definition qgscurve.h:392
double mSummedUpArea
Definition qgscurve.h:395
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
Convenience functions for geometry utils.
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 distance3D(double x1, double y1, double z1, double x2, double y2, double z2)
Returns the 3D distance between (x1, y1, z1) and (x2, y2, z2).
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.
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.
static std::unique_ptr< 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 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.
QVector< QgsLineString * > splitToDisjointXYParts() const
Divides the linestring into parts that don't share any points or lines.
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.
static std::unique_ptr< QgsLineString > fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
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.
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.
std::unique_ptr< QgsLineString > interpolateM(bool use3DDistance=true) const
Returns a copy of this line with all missing (NaN) m values interpolated from m values of surrounding...
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.
std::unique_ptr< 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.
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.
double distanceBetweenVertices(QgsVertexId fromVertex, QgsVertexId toVertex) const override
Returns the distance along the curve between two vertices.
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.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
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
Returns a WKB representation of the geometry.
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.
QgsLineString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
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:337
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:326
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:45
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE 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:6524
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
void simplifySection(int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon)
QLineF segment(int index, QRectF rect, double radius)
double distance2D(const QgsPolylineXY &coords)
Definition qgstracer.cpp:50
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