1 /***************************************************************************
2                          qgslinestring.h
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 #ifndef QGSLINESTRING_H
19 #define QGSLINESTRING_H
20 
21 
22 #include <QPolygonF>
23 
24 #include "qgis_core.h"
25 #include "qgis_sip.h"
26 #include "qgscurve.h"
27 #include "qgscompoundcurve.h"
28 
29 class QgsLineSegment2D;
30 
31 /***************************************************************************
32  * This class is considered CRITICAL and any change MUST be accompanied with
33  * full unit tests in testqgsgeometry.cpp.
34  * See details in QEP #17
35  ****************************************************************************/
36 
37 /**
38  * \ingroup core
39  * \class QgsLineString
40  * \brief Line string geometry type, with support for z-dimension and m-values.
41  * \since QGIS 2.10
42  */
43 class CORE_EXPORT QgsLineString: public QgsCurve
44 {
45 
46   public:
47 
48     /**
49      * Constructor for an empty linestring geometry.
50      */
51     QgsLineString() SIP_HOLDGIL;
52 #ifndef SIP_RUN
53 
54     /**
55      * Construct a linestring from a vector of points.
56      * Z and M type will be set based on the type of the first point
57      * in the vector.
58      * \since QGIS 3.0
59      */
60     QgsLineString( const QVector<QgsPoint> &points );
61 
62     /**
63      * Construct a linestring from list of points.
64      * This constructor is more efficient then calling setPoints()
65      * or repeatedly calling addVertex()
66      * \since QGIS 3.0
67      */
68     QgsLineString( const QVector<QgsPointXY> &points );
69 #else
70 
71     /**
72      * Construct a linestring from a sequence of points (QgsPoint objects, QgsPointXY objects, or sequences of float values).
73      *
74      * The linestring Z and M type will be set based on the type of the first point in the sequence.
75      *
76      * \since QGIS 3.20
77      */
78     QgsLineString( SIP_PYOBJECT points SIP_TYPEHINT( Sequence[Union[QgsPoint, QgsPointXY, Sequence[float]]] ) ) SIP_HOLDGIL [( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z = QVector<double>(), const QVector<double> &m = QVector<double>(), bool is25DType = false )];
79     % MethodCode
80     if ( !PySequence_Check( a0 ) )
81     {
82       PyErr_SetString( PyExc_TypeError, QStringLiteral( "A sequence of QgsPoint, QgsPointXY or array of floats is expected" ).toUtf8().constData() );
83       sipIsErr = 1;
84     }
85     else
86     {
87       int state;
88       const int size = PySequence_Size( a0 );
89       QVector< double > xl;
90       QVector< double > yl;
91       bool hasZ = false;
92       QVector< double > zl;
93       bool hasM = false;
94       QVector< double > ml;
95       xl.reserve( size );
96       yl.reserve( size );
97 
98       bool is25D = false;
99 
100       sipIsErr = 0;
101       for ( int i = 0; i < size; ++i )
102       {
103         PyObject *value = PySequence_GetItem( a0, i );
104         if ( !value )
105         {
106           PyErr_SetString( PyExc_TypeError, QStringLiteral( "Invalid type at index %1." ).arg( i ) .toUtf8().constData() );
107           sipIsErr = 1;
108           break;
109         }
110 
111         if ( PySequence_Check( value ) )
112         {
113           const int elementSize = PySequence_Size( value );
114           if ( elementSize < 2 || elementSize > 4 )
115           {
116             sipIsErr = 1;
117             PyErr_SetString( PyExc_TypeError, QStringLiteral( "Invalid sequence size at index %1. Expected an array of 2-4 float values, got %2." ).arg( i ).arg( elementSize ).toUtf8().constData() );
118             Py_DECREF( value );
119             break;
120           }
121           else
122           {
123             sipIsErr = 0;
124             for ( int j = 0; j < elementSize; ++j )
125             {
126               PyObject *element = PySequence_GetItem( value, j );
127               if ( !element )
128               {
129                 PyErr_SetString( PyExc_TypeError, QStringLiteral( "Invalid type at index %1." ).arg( i ) .toUtf8().constData() );
130                 sipIsErr = 1;
131                 break;
132               }
133 
134               PyErr_Clear();
135               double d = PyFloat_AsDouble( element );
136               if ( PyErr_Occurred() )
137               {
138                 Py_DECREF( value );
139                 sipIsErr = 1;
140                 break;
141               }
142               if ( j == 0 )
143                 xl.append( d );
144               else if ( j == 1 )
145                 yl.append( d );
146 
147               if ( i == 0 && j == 2 )
148               {
149                 hasZ = true;
150                 zl.reserve( size );
151                 zl.append( d );
152               }
153               else if ( i > 0 && j == 2 && hasZ )
154               {
155                 zl.append( d );
156               }
157 
158               if ( i == 0 && j == 3 )
159               {
160                 hasM = true;
161                 ml.reserve( size );
162                 ml.append( d );
163               }
164               else if ( i > 0 && j == 3 && hasM )
165               {
166                 ml.append( d );
167               }
168 
169               Py_DECREF( element );
170             }
171 
172             if ( hasZ && elementSize < 3 )
173               zl.append( std::numeric_limits< double >::quiet_NaN() );
174             if ( hasM && elementSize < 4 )
175               ml.append( std::numeric_limits< double >::quiet_NaN() );
176 
177             Py_DECREF( value );
178             if ( sipIsErr )
179             {
180               break;
181             }
182           }
183         }
184         else
185         {
186           if ( sipCanConvertToType( value, sipType_QgsPointXY, SIP_NOT_NONE ) )
187           {
188             sipIsErr = 0;
189             QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( value, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
190             if ( !sipIsErr )
191             {
192               xl.append( p->x() );
193               yl.append( p->y() );
194             }
195             sipReleaseType( p, sipType_QgsPointXY, state );
196           }
197           else if ( sipCanConvertToType( value, sipType_QgsPoint, SIP_NOT_NONE ) )
198           {
199             sipIsErr = 0;
200             QgsPoint *p = reinterpret_cast<QgsPoint *>( sipConvertToType( value, sipType_QgsPoint, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
201             if ( !sipIsErr )
202             {
203               xl.append( p->x() );
204               yl.append( p->y() );
205 
206               if ( i == 0 && p->is3D() )
207               {
208                 hasZ = true;
209                 zl.reserve( size );
210                 zl.append( p->z() );
211               }
212               else if ( i > 0 && hasZ )
213               {
214                 zl.append( p->z() );
215               }
216 
217               if ( i == 0 && p->isMeasure() )
218               {
219                 hasM = true;
220                 ml.reserve( size );
221                 ml.append( p->m() );
222               }
223               else if ( i > 0 && hasM )
224               {
225                 ml.append( p->m() );
226               }
227 
228               if ( i == 0 && p->wkbType() == QgsWkbTypes::Point25D )
229                 is25D = true;
230             }
231             sipReleaseType( p, sipType_QgsPoint, state );
232           }
233           else
234           {
235             sipIsErr = 1;
236           }
237 
238           Py_DECREF( value );
239 
240           if ( sipIsErr )
241           {
242             // couldn't convert the sequence value to a QgsPoint or QgsPointXY
243             PyErr_SetString( PyExc_TypeError, QStringLiteral( "Invalid type at index %1. Expected QgsPoint, QgsPointXY or array of floats." ).arg( i ) .toUtf8().constData() );
244             break;
245           }
246         }
247       }
248       if ( sipIsErr == 0 )
249         sipCpp = new sipQgsLineString( QgsLineString( xl, yl, zl, ml, is25D ) );
250     }
251     % End
252 #endif
253 
254     /**
255      * Construct a linestring from a single 2d line segment.
256      * \since QGIS 3.2
257      */
258     explicit QgsLineString( const QgsLineSegment2D &segment ) SIP_HOLDGIL;
259 
260     /**
261      * Construct a linestring from arrays of coordinates. If the z or m
262      * arrays are non-empty then the resultant linestring will have
263      * z and m types accordingly.
264      * This constructor is more efficient then calling setPoints()
265      * or repeatedly calling addVertex()
266      *
267      * If the \a z vector is filled, then the geometry type will either
268      * be a LineStringZ(M) or LineString25D depending on the \a is25DType
269      * argument. If \a is25DType is TRUE (and the \a m vector is unfilled) then
270      * the created Linestring will be a LineString25D type. Otherwise, the
271      * LineString will be LineStringZ (or LineStringZM) type.
272      *
273      * If the sizes of \a x and \a y are non-equal then the resultant linestring
274      * will be created using the minimum size of these arrays.
275      *
276      * \since QGIS 3.0
277      */
278     QgsLineString( const QVector<double> &x, const QVector<double> &y,
279                    const QVector<double> &z = QVector<double>(),
280                    const QVector<double> &m = QVector<double>(), bool is25DType = false ) SIP_HOLDGIL;
281 
282     /**
283      * Constructs a linestring with a single segment from \a p1 to \a p2.
284      * \since QGIS 3.2
285      */
286     QgsLineString( const QgsPoint &p1, const QgsPoint &p2 ) SIP_HOLDGIL;
287 
288     /**
289      * Returns a new linestring created by segmentizing the bezier curve between \a start and \a end, with
290      * the specified control points.
291      *
292      * The \a segments parameter controls how many line segments will be present in the returned linestring.
293      *
294      * Any z or m values present in the input coordinates will be interpolated along with the x and y values.
295      *
296      * \since QGIS 3.10
297      */
298     static QgsLineString *fromBezierCurve( const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments = 30 ) SIP_FACTORY;
299 
300     /**
301      * Returns a new linestring from a QPolygonF \a polygon input.
302      *
303      * \since QGIS 3.10
304      */
305     static QgsLineString *fromQPolygonF( const QPolygonF &polygon ) SIP_FACTORY;
306 
307     bool equals( const QgsCurve &other ) const override;
308 
309 #ifndef SIP_RUN
310 
311     /**
312      * Returns the specified point from inside the line string.
313      * \param i index of point, starting at 0 for the first point
314      */
315     QgsPoint pointN( int i ) const;
316 #else
317 
318     /**
319      * Returns the point at the specified index.
320      *
321      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
322      * corresponds to the last point in the line.
323      *
324      * \throws IndexError if no point with the specified index exists.
325      */
326     SIP_PYOBJECT pointN( int i ) const SIP_TYPEHINT( QgsPoint );
327     % MethodCode
328     const int count = sipCpp->numPoints();
329     if ( a0 < -count || a0 >= count )
330     {
331       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
332       sipIsErr = 1;
333     }
334     else
335     {
336       std::unique_ptr< QgsPoint > p;
337       if ( a0 >= 0 )
338         p = std::make_unique< QgsPoint >( sipCpp->pointN( a0 ) );
339       else // negative index, count backwards from end
340         p = std::make_unique< QgsPoint >( sipCpp->pointN( count + a0 ) );
341       sipRes = sipConvertFromType( p.release(), sipType_QgsPoint, Py_None );
342     }
343     % End
344 #endif
345 
346 #ifndef SIP_RUN
347     double xAt( int index ) const override;
348 #else
349 
350     /**
351      * Returns the x-coordinate of the specified node in the line string.
352      *
353      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
354      * corresponds to the last point in the line.
355      *
356      * \throws IndexError if no point with the specified index exists.
357     */
358     double xAt( int index ) const override;
359     % MethodCode
360     const int count = sipCpp->numPoints();
361     if ( a0 < -count || a0 >= count )
362     {
363       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
364       sipIsErr = 1;
365     }
366     else
367     {
368       if ( a0 >= 0 )
369         return PyFloat_FromDouble( sipCpp->xAt( a0 ) );
370       else
371         return PyFloat_FromDouble( sipCpp->xAt( count + a0 ) );
372     }
373     % End
374 #endif
375 
376 #ifndef SIP_RUN
377     double yAt( int index ) const override;
378 #else
379 
380     /**
381      * Returns the y-coordinate of the specified node in the line string.
382      *
383      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
384      * corresponds to the last point in the line.
385      *
386      * \throws IndexError if no point with the specified index exists.
387     */
388     double yAt( int index ) const override;
389     % MethodCode
390     const int count = sipCpp->numPoints();
391     if ( a0 < -count || a0 >= count )
392     {
393       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
394       sipIsErr = 1;
395     }
396     else
397     {
398       if ( a0 >= 0 )
399         return PyFloat_FromDouble( sipCpp->yAt( a0 ) );
400       else
401         return PyFloat_FromDouble( sipCpp->yAt( count + a0 ) );
402     }
403     % End
404 #endif
405 
406     /**
407      * Returns a const pointer to the x vertex data.
408      * \note Not available in Python bindings
409      * \see yData()
410      * \since QGIS 3.2
411      */
412     const double *xData() const SIP_SKIP
413     {
414       return mX.constData();
415     }
416 
417     /**
418      * Returns a const pointer to the y vertex data.
419      * \note Not available in Python bindings
420      * \see xData()
421      * \since QGIS 3.2
422      */
yData()423     const double *yData() const SIP_SKIP
424     {
425       return mY.constData();
426     }
427 
428     /**
429      * Returns a const pointer to the z vertex data, or NULLPTR if the linestring does
430      * not have z values.
431      * \note Not available in Python bindings
432      * \see xData()
433      * \see yData()
434      * \since QGIS 3.2
435      */
zData()436     const double *zData() const SIP_SKIP
437     {
438       if ( mZ.empty() )
439         return nullptr;
440       else
441         return mZ.constData();
442     }
443 
444     /**
445      * Returns a const pointer to the m vertex data, or NULLPTR if the linestring does
446      * not have m values.
447      * \note Not available in Python bindings
448      * \see xData()
449      * \see yData()
450      * \since QGIS 3.2
451      */
mData()452     const double *mData() const SIP_SKIP
453     {
454       if ( mM.empty() )
455         return nullptr;
456       else
457         return mM.constData();
458     }
459 
460 #ifndef SIP_RUN
461 
462     /**
463      * Returns the z-coordinate of the specified node in the line string.
464      * \param index index of node, where the first node in the line is 0
465      * \returns z-coordinate of node, or ``nan`` if index is out of bounds or the line
466      * does not have a z dimension
467      * \see setZAt()
468      */
zAt(int index)469     double zAt( int index ) const
470     {
471       if ( index >= 0 && index < mZ.size() )
472         return mZ.at( index );
473       else
474         return std::numeric_limits<double>::quiet_NaN();
475     }
476 #else
477 
478     /**
479      * Returns the z-coordinate of the specified node in the line string.
480      *
481      * If the LineString does not have a z-dimension then ``nan`` will be returned.
482      *
483      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
484      * corresponds to the last point in the line.
485      *
486      * \throws IndexError if no point with the specified index exists.
487     */
488     double zAt( int index ) const;
489     % MethodCode
490     const int count = sipCpp->numPoints();
491     if ( a0 < -count || a0 >= count )
492     {
493       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
494       sipIsErr = 1;
495     }
496     else
497     {
498       if ( a0 >= 0 )
499         return PyFloat_FromDouble( sipCpp->zAt( a0 ) );
500       else
501         return PyFloat_FromDouble( sipCpp->zAt( count + a0 ) );
502     }
503     % End
504 #endif
505 
506 #ifndef SIP_RUN
507 
508     /**
509      * Returns the m value of the specified node in the line string.
510      * \param index index of node, where the first node in the line is 0
511      * \returns m value of node, or ``nan`` if index is out of bounds or the line
512      * does not have m values
513      * \see setMAt()
514      */
515     double mAt( int index ) const
516     {
517       if ( index >= 0 && index < mM.size() )
518         return mM.at( index );
519       else
520         return std::numeric_limits<double>::quiet_NaN();
521     }
522 #else
523 
524     /**
525      * Returns the m-coordinate of the specified node in the line string.
526      *
527      * If the LineString does not have a m-dimension then ``nan`` will be returned.
528      *
529      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
530      * corresponds to the last point in the line.
531      *
532      * \throws IndexError if no point with the specified index exists.
533     */
534     double mAt( int index ) const;
535     % MethodCode
536     const int count = sipCpp->numPoints();
537     if ( a0 < -count || a0 >= count )
538     {
539       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
540       sipIsErr = 1;
541     }
542     else
543     {
544       if ( a0 >= 0 )
545         return PyFloat_FromDouble( sipCpp->mAt( a0 ) );
546       else
547         return PyFloat_FromDouble( sipCpp->mAt( count + a0 ) );
548     }
549     % End
550 #endif
551 
552 #ifndef SIP_RUN
553 
554     /**
555      * Sets the x-coordinate of the specified node in the line string.
556      * \param index index of node, where the first node in the line is 0. Corresponding
557      * node must already exist in line string.
558      * \param x x-coordinate of node
559      * \see xAt()
560      */
561     void setXAt( int index, double x );
562 #else
563 
564     /**
565      * Sets the x-coordinate of the specified node in the line string.
566      * The corresponding node must already exist in line string.
567      *
568      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
569      * corresponds to the last point in the line.
570      *
571      * \throws IndexError if no point with the specified index exists.
572      *
573      * \see xAt()
574      */
575     void setXAt( int index, double x );
576     % MethodCode
577     const int count = sipCpp->numPoints();
578     if ( a0 < -count || a0 >= count )
579     {
580       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
581       sipIsErr = 1;
582     }
583     else
584     {
585       if ( a0 >= 0 )
586         sipCpp->setXAt( a0, a1 );
587       else
588         sipCpp->setXAt( count + a0, a1 );
589     }
590     % End
591 #endif
592 
593 #ifndef SIP_RUN
594 
595     /**
596      * Sets the y-coordinate of the specified node in the line string.
597      * \param index index of node, where the first node in the line is 0. Corresponding
598      * node must already exist in line string.
599      * \param y y-coordinate of node
600      * \see yAt()
601      */
602     void setYAt( int index, double y );
603 #else
604 
605     /**
606      * Sets the y-coordinate of the specified node in the line string.
607      * The corresponding node must already exist in line string.
608      *
609      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
610      * corresponds to the last point in the line.
611      *
612      * \throws IndexError if no point with the specified index exists.
613      *
614      * \see yAt()
615      */
616     void setYAt( int index, double y );
617     % MethodCode
618     const int count = sipCpp->numPoints();
619     if ( a0 < -count || a0 >= count )
620     {
621       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
622       sipIsErr = 1;
623     }
624     else
625     {
626       if ( a0 >= 0 )
627         sipCpp->setYAt( a0, a1 );
628       else
629         sipCpp->setYAt( count + a0, a1 );
630     }
631     % End
632 #endif
633 
634 #ifndef SIP_RUN
635 
636     /**
637      * Sets the z-coordinate of the specified node in the line string.
638      * \param index index of node, where the first node in the line is 0. Corresponding
639      * node must already exist in line string, and the line string must have z-dimension.
640      * \param z z-coordinate of node
641      * \see zAt()
642      */
setZAt(int index,double z)643     void setZAt( int index, double z )
644     {
645       if ( index >= 0 && index < mZ.size() )
646         mZ[ index ] = z;
647     }
648 #else
649 
650     /**
651      * Sets the z-coordinate of the specified node in the line string.
652      * The corresponding node must already exist in line string and the line string must have z-dimension.
653      *
654      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
655      * corresponds to the last point in the line.
656      *
657      * \throws IndexError if no point with the specified index exists.
658      * \see zAt()
659      */
660     void setZAt( int index, double z );
661     % MethodCode
662     const int count = sipCpp->numPoints();
663     if ( a0 < -count || a0 >= count )
664     {
665       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
666       sipIsErr = 1;
667     }
668     else
669     {
670       if ( a0 >= 0 )
671         sipCpp->setZAt( a0, a1 );
672       else
673         sipCpp->setZAt( count + a0, a1 );
674     }
675     % End
676 #endif
677 
678 #ifndef SIP_RUN
679 
680     /**
681      * Sets the m value of the specified node in the line string.
682      * \param index index of node, where the first node in the line is 0. Corresponding
683      * node must already exist in line string, and the line string must have m values.
684      * \param m m value of node
685      * \see mAt()
686      */
687     void setMAt( int index, double m )
688     {
689       if ( index >= 0 && index < mM.size() )
690         mM[ index ] = m;
691     }
692 #else
693 
694     /**
695      * Sets the m-coordinate of the specified node in the line string.
696      * The corresponding node must already exist in line string and the line string must have m-dimension.
697      *
698      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
699      * corresponds to the last point in the line.
700      *
701      * \throws IndexError if no point with the specified index exists.
702      * \see mAt()
703      */
704     void setMAt( int index, double m );
705     % MethodCode
706     const int count = sipCpp->numPoints();
707     if ( a0 < -count || a0 >= count )
708     {
709       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
710       sipIsErr = 1;
711     }
712     else
713     {
714       if ( a0 >= 0 )
715         sipCpp->setMAt( a0, a1 );
716       else
717         sipCpp->setMAt( count + a0, a1 );
718     }
719     % End
720 #endif
721 
722     /**
723      * Resets the line string to match the specified list of points. The line string will
724      * inherit the dimensionality of the first point in the list.
725      * \param points new points for line string. If empty, line string will be cleared.
726      */
727     void setPoints( const QgsPointSequence &points );
728 
729     /**
730      * Appends the contents of another line string to the end of this line string.
731      * \param line line to append. Ownership is not transferred.
732      */
733     void append( const QgsLineString *line );
734 
735     /**
736      * Adds a new vertex to the end of the line string.
737      * \param pt vertex to add
738      */
739     void addVertex( const QgsPoint &pt );
740 
741     //! Closes the line string by appending the first point to the end of the line, if it is not already closed.
742     void close();
743 
744     /**
745      * Returns the geometry converted to the more generic curve type QgsCompoundCurve
746      * \returns the converted geometry. Caller takes ownership
747     */
748     QgsCompoundCurve *toCurveType() const override SIP_FACTORY;
749 
750     /**
751      * Extends the line geometry by extrapolating out the start or end of the line
752      * by a specified distance. Lines are extended using the bearing of the first or last
753      * segment in the line.
754      * \since QGIS 3.0
755      */
756     void extend( double startDistance, double endDistance );
757 
758 #ifndef SIP_RUN
759 
760     /**
761      * Visits regular points along the linestring, spaced by \a distance.
762      *
763      * The \a visitPoint function should return FALSE to abort further traversal.
764      */
765     void visitPointsByRegularDistance( double distance, const std::function< bool( double x, double y, double z, double m,
766                                        double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM,
767                                        double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM
768                                                                                  ) > &visitPoint ) const;
769 #endif
770 
771     //reimplemented methods
772     QString geometryType() const override SIP_HOLDGIL;
773     int dimension() const override SIP_HOLDGIL;
774     QgsLineString *clone() const override SIP_FACTORY;
775     void clear() override;
776     bool isEmpty() const override SIP_HOLDGIL;
777     int indexOf( const QgsPoint &point ) const final;
778     bool isValid( QString &error SIP_OUT, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const override;
779     QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
780     bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
781     bool isClosed() const override SIP_HOLDGIL;
782     bool isClosed2D() const override SIP_HOLDGIL;
783     bool boundingBoxIntersects( const QgsRectangle &rectangle ) const override SIP_HOLDGIL;
784 
785     /**
786      * Returns a list of any duplicate nodes contained in the geometry, within the specified tolerance.
787      *
788      * If \a useZValues is TRUE then z values will also be considered when testing for duplicates.
789      *
790      * \since QGIS 3.16
791      */
792     QVector< QgsVertexId > collectDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) const;
793 
794     QPolygonF asQPolygonF() const override;
795 
796     bool fromWkb( QgsConstWkbPtr &wkb ) override;
797     bool fromWkt( const QString &wkt ) override;
798 
799     int wkbSize( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
800     QByteArray asWkb( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
801     QString asWkt( int precision = 17 ) const override;
802     QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
803     QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
804     json asJsonObject( int precision = 17 ) const override SIP_SKIP;
805     QString asKml( int precision = 17 ) const override;
806 
807     //curve interface
808     double length() const override SIP_HOLDGIL;
809 
810 #ifndef SIP_RUN
811     std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex( int index ) const final;
812 #endif
813 
814     /**
815      * Returns the length in 3D world of the line string.
816      * If it is not a 3D line string, return its 2D length.
817      * \see length()
818      * \since QGIS 3.10
819      */
820     double length3D() const SIP_HOLDGIL;
821     QgsPoint startPoint() const override SIP_HOLDGIL;
822     QgsPoint endPoint() const override SIP_HOLDGIL;
823 
824     /**
825      * Returns a new line string geometry corresponding to a segmentized approximation
826      * of the curve.
827      * \param tolerance segmentation tolerance
828      * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve
829     */
830     QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override  SIP_FACTORY;
831 
832     int numPoints() const override SIP_HOLDGIL;
833     int nCoordinates() const override SIP_HOLDGIL;
834     void points( QgsPointSequence &pt SIP_OUT ) const override;
835 
836     void draw( QPainter &p ) const override;
837 
838     void transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d = Qgis::TransformDirection::Forward, bool transformZ = false ) override  SIP_THROW( QgsCsException );
839     void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;
840 
841     void addToPainterPath( QPainterPath &path ) const override;
842     void drawAsPolygon( QPainter &p ) const override;
843 
844     bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
845     bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
846     bool deleteVertex( QgsVertexId position ) override;
847 
848     QgsLineString *reversed() const override SIP_FACTORY;
849     QgsPoint *interpolatePoint( double distance ) const override SIP_FACTORY;
850     QgsLineString *curveSubstring( double startDistance, double endDistance ) const override SIP_FACTORY;
851 
852     double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * std::numeric_limits<double>::epsilon() ) const override;
853     bool pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const override;
854 
855     QgsPoint centroid() const override;
856 
857     void sumUpArea( double &sum SIP_OUT ) const override;
858     double vertexAngle( QgsVertexId vertex ) const override;
859     double segmentLength( QgsVertexId startVertex ) const override;
860     bool addZValue( double zValue = 0 ) override;
861     bool addMValue( double mValue = 0 ) override;
862 
863     bool dropZValue() override;
864     bool dropMValue() override;
865     void swapXy() override;
866 
867     bool convertTo( QgsWkbTypes::Type type ) override;
868 
869     bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
870     void scroll( int firstVertexIndex ) final;
871 
872 #ifndef SIP_RUN
873     void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
874     void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
875 
876     /**
877      * Cast the \a geom to a QgsLineString.
878      * Should be used by qgsgeometry_cast<QgsLineString *>( geometry ).
879      *
880      * \note Not available in Python. Objects will be automatically be converted to the appropriate target type.
881      * \since QGIS 3.0
882      */
cast(const QgsAbstractGeometry * geom)883     inline static const QgsLineString *cast( const QgsAbstractGeometry *geom )
884     {
885       if ( geom && QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::LineString )
886         return static_cast<const QgsLineString *>( geom );
887       return nullptr;
888     }
889 #endif
890 
891     QgsLineString *createEmptyWithSameType() const override SIP_FACTORY;
892 
893 #ifdef SIP_RUN
894     SIP_PYOBJECT __repr__();
895     % MethodCode
896     QString wkt = sipCpp->asWkt();
897     if ( wkt.length() > 1000 )
898       wkt = wkt.left( 1000 ) + QStringLiteral( "..." );
899     QString str = QStringLiteral( "<QgsLineString: %1>" ).arg( wkt );
900     sipRes = PyUnicode_FromString( str.toUtf8().constData() );
901     % End
902 
903     /**
904     * Returns the point at the specified ``index``.
905     *
906     * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
907     * corresponds to the last point in the line.
908     *
909     * \throws IndexError if no point with the specified ``index`` exists.
910     * \since QGIS 3.6
911     */
912     SIP_PYOBJECT __getitem__( int index ) SIP_TYPEHINT( QgsPoint );
913     % MethodCode
914     const int count = sipCpp->numPoints();
915     if ( a0 < -count || a0 >= count )
916     {
917       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
918       sipIsErr = 1;
919     }
920     else
921     {
922       std::unique_ptr< QgsPoint > p;
923       if ( a0 >= 0 )
924         p = std::make_unique< QgsPoint >( sipCpp->pointN( a0 ) );
925       else
926         p = std::make_unique< QgsPoint >( sipCpp->pointN( count + a0 ) );
927       sipRes = sipConvertFromType( p.release(), sipType_QgsPoint, Py_None );
928     }
929     % End
930 
931     /**
932     * Sets the point at the specified ``index``.
933     *
934     * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
935     * corresponds to the last point in the line.
936     *
937     * \throws IndexError if no point with the specified ``index`` exists.
938     * \since QGIS 3.6
939     */
940     void __setitem__( int index, const QgsPoint &point );
941     % MethodCode
942     const int count = sipCpp->numPoints();
943     if ( a0 < -count || a0 >= count )
944     {
945       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
946       sipIsErr = 1;
947     }
948     else
949     {
950       if ( a0 < 0 )
951         a0 = count + a0;
952       sipCpp->setXAt( a0, a1->x() );
953       sipCpp->setYAt( a0, a1->y() );
954       if ( sipCpp->isMeasure() )
955         sipCpp->setMAt( a0, a1->m() );
956       if ( sipCpp->is3D() )
957         sipCpp->setZAt( a0, a1->z() );
958     }
959     % End
960 
961 
962     /**
963      * Deletes the vertex at the specified ``index``.
964      *
965      * Indexes can be less than 0, in which case they correspond to positions from the end of the line. E.g. an index of -1
966      * corresponds to the last point in the line.
967      *
968      * \throws IndexError if no point with the specified ``index`` exists.
969      * \since QGIS 3.6
970      */
971     void __delitem__( int index );
972     % MethodCode
973     const int count = sipCpp->numPoints();
974     if ( a0 >= 0 && a0 < count )
975       sipCpp->deleteVertex( QgsVertexId( -1, -1, a0 ) );
976     else if ( a0 < 0 && a0 >= -count )
977       sipCpp->deleteVertex( QgsVertexId( -1, -1, count + a0 ) );
978     else
979     {
980       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
981       sipIsErr = 1;
982     }
983     % End
984 
985 #endif
986 
987   protected:
988 
989     int compareToSameClass( const QgsAbstractGeometry *other ) const final;
990     QgsRectangle calculateBoundingBox() const override;
991 
992   private:
993     QVector<double> mX;
994     QVector<double> mY;
995     QVector<double> mZ;
996     QVector<double> mM;
997 
998     void importVerticesFromWkb( const QgsConstWkbPtr &wkb );
999 
1000     /**
1001      * Resets the line string to match the line string in a WKB geometry.
1002      * \param type WKB type
1003      * \param wkb WKB representation of line geometry
1004      */
fromWkbPoints(QgsWkbTypes::Type type,const QgsConstWkbPtr & wkb)1005     void fromWkbPoints( QgsWkbTypes::Type type, const QgsConstWkbPtr &wkb )
1006     {
1007       mWkbType = type;
1008       importVerticesFromWkb( wkb );
1009     }
1010 
1011     friend class QgsPolygon;
1012     friend class QgsTriangle;
1013     friend class TestQgsGeometry;
1014 
1015 };
1016 
1017 // clazy:excludeall=qstring-allocations
1018 
1019 #endif // QGSLINESTRING_H
1020