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