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