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