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