23#include <nlohmann/json.hpp>
33#include <QDomDocument>
38using namespace Qt::StringLiterals;
62 double *x =
mX.data();
63 double *y =
mY.data();
88QgsLineString::QgsLineString(
const QVector<double> &x,
const QVector<double> &y,
const QVector<double> &z,
const QVector<double> &m,
bool is25DType )
91 int pointCount = std::min( x.size(), y.size() );
92 if ( x.size() == pointCount )
98 mX = x.mid( 0, pointCount );
100 if ( y.size() == pointCount )
106 mY = y.mid( 0, pointCount );
108 if ( !z.isEmpty() && z.count() >= pointCount )
111 if ( z.size() == pointCount )
117 mZ = z.mid( 0, pointCount );
120 if ( !m.isEmpty() && m.count() >= pointCount )
123 if ( m.size() == pointCount )
129 mM = m.mid( 0, pointCount );
185 return std::make_unique< QgsLineString >();
187 QVector<double> x( segments + 1 );
188 QVector<double> y( segments + 1 );
192 const bool hasZ = start.
is3D() && controlPoint1.
is3D() && controlPoint2.
is3D() && end.
is3D();
195 z.resize( segments + 1 );
201 m.resize( segments + 1 );
204 double *
xData = x.data();
205 double *
yData = y.data();
206 double *
zData = z.data();
207 double *
mData = m.data();
209 const double step = 1.0 / segments;
211 for (
int i = 0; i <= segments; ++i )
213 const double t = i * step;
216 double iz = std::numeric_limits<double>::quiet_NaN();
217 double im = std::numeric_limits<double>::quiet_NaN();
253 return std::make_unique< QgsLineString >( x, y, z, m );
260 x.resize( polygon.count() );
261 y.resize( polygon.count() );
262 double *
xData = x.data();
263 double *
yData = y.data();
265 const QPointF *src = polygon.data();
266 for (
int i = 0; i < polygon.size(); ++i )
273 return std::make_unique< QgsLineString >( x, y );
289 const int size =
mX.size();
293 const double *x =
mX.constData();
294 const double *y =
mY.constData();
295 const bool useZ =
is3D();
297 const double *z = useZ ?
mZ.constData() :
nullptr;
298 const double *m = useM ?
mM.constData() :
nullptr;
300 for (
int i = 0; i < size; ++i )
319 error = QObject::tr(
"LineString has less than 2 points and is not empty." );
330 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing,
mX,
mY,
mZ,
mM, result->mX, result->mY, result->mZ, result->mM, removeRedundantPoints );
332 return result.release();
339 if (
mX.count() <= 2 )
342 double prevX =
mX.at( 0 );
343 double prevY =
mY.at( 0 );
345 bool useZ = hasZ && useZValues;
346 double prevZ = useZ ?
mZ.at( 0 ) : 0;
348 int remaining =
mX.count();
349 while ( i < remaining )
351 double currentX =
mX.at( i );
352 double currentY =
mY.at( i );
353 double currentZ = useZ ?
mZ.at( i ) : 0;
387 if (
is3D() && closed )
388 closed &=
qgsDoubleNear(
mZ.first(),
mZ.last() ) || ( std::isnan(
mZ.first() ) && std::isnan(
mZ.last() ) );
401 return mBoundingBox.toRectangle().intersects( rectangle );
403 const int nb =
mX.size();
413 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.2 ) ),
mY.at(
static_cast< int >( nb * 0.2 ) ) )
414 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.4 ) ),
mY.at(
static_cast< int >( nb * 0.4 ) ) )
415 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.6 ) ),
mY.at(
static_cast< int >( nb * 0.6 ) ) )
416 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.8 ) ),
mY.at(
static_cast< int >( nb * 0.8 ) ) )
417 || rectangle.
contains(
mX.at( nb - 1 ),
mY.at( nb - 1 ) ) )
426 double xmin = std::numeric_limits<double>::max();
427 double ymin = std::numeric_limits<double>::max();
428 double zmin = -std::numeric_limits<double>::max();
429 double xmax = -std::numeric_limits<double>::max();
430 double ymax = -std::numeric_limits<double>::max();
431 double zmax = -std::numeric_limits<double>::max();
433 const double *x =
mX.constData();
434 const double *y =
mY.constData();
435 const double *z =
is3D() ?
mZ.constData() :
nullptr;
436 bool foundPointInRectangle =
false;
437 for (
int i = 0; i < nb; ++i )
439 const double px = *x++;
440 xmin = std::min( xmin, px );
441 xmax = std::max( xmax, px );
442 const double py = *y++;
443 ymin = std::min( ymin, py );
444 ymax = std::max( ymax, py );
447 const double pz = *z++;
448 zmin = std::min( zmin, pz );
449 zmax = std::max( zmax, pz );
452 if ( !foundPointInRectangle && rectangle.
contains( px, py ) )
454 foundPointInRectangle =
true;
470 if ( foundPointInRectangle )
495 const int nb =
mX.size();
505 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.2 ) ),
mY.at(
static_cast< int >( nb * 0.2 ) ),
mZ.at(
static_cast< int >( nb * 0.2 ) ) )
506 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.4 ) ),
mY.at(
static_cast< int >( nb * 0.4 ) ),
mZ.at(
static_cast< int >( nb * 0.4 ) ) )
507 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.6 ) ),
mY.at(
static_cast< int >( nb * 0.6 ) ),
mZ.at(
static_cast< int >( nb * 0.6 ) ) )
508 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.8 ) ),
mY.at(
static_cast< int >( nb * 0.8 ) ),
mZ.at(
static_cast< int >( nb * 0.8 ) ) )
509 || box3d.
contains(
mX.at( nb - 1 ),
mY.at( nb - 1 ),
mZ.at( nb - 1 ) ) )
518 double xmin = std::numeric_limits<double>::max();
519 double ymin = std::numeric_limits<double>::max();
520 double zmin = std::numeric_limits<double>::max();
521 double xmax = -std::numeric_limits<double>::max();
522 double ymax = -std::numeric_limits<double>::max();
523 double zmax = -std::numeric_limits<double>::max();
525 const double *x =
mX.constData();
526 const double *y =
mY.constData();
527 const double *z =
mZ.constData();
528 bool foundPointInBox =
false;
529 for (
int i = 0; i < nb; ++i )
531 const double px = *x++;
532 xmin = std::min( xmin, px );
533 xmax = std::max( xmax, px );
534 const double py = *y++;
535 ymin = std::min( ymin, py );
536 ymax = std::max( ymax, py );
537 const double pz = *z++;
538 zmin = std::min( zmin, pz );
539 zmax = std::max( zmax, pz );
541 if ( !foundPointInBox && box3d.
contains( px, py, pz ) )
543 foundPointInBox =
true;
559 if ( foundPointInBox )
572 QVector< QgsVertexId > res;
573 if (
mX.count() <= 1 )
576 const double *x =
mX.constData();
577 const double *y =
mY.constData();
579 bool useZ = hasZ && useZValues;
580 const double *z = useZ ?
mZ.constData() :
nullptr;
584 double prevZ = z ? *z++ : 0;
587 for (
int i = 1; i <
mX.count(); ++i )
589 double currentX = *x++;
590 double currentY = *y++;
591 double currentZ = useZ ? *z++ : 0;
609 const int nb =
mX.size();
612 const double *x =
mX.constData();
613 const double *y =
mY.constData();
614 QPointF *dest =
points.data();
615 for (
int i = 0; i < nb; ++i )
617 *dest++ = QPointF( *x++, *y++ );
623void simplifySection(
int i,
int j,
const double *x,
const double *y, std::vector< bool > &usePoint,
const double distanceToleranceSquared,
const double epsilon )
630 double maxDistanceSquared = -1.0;
635 for (
int k = i + 1; k < j; k++ )
639 if ( distanceSquared > maxDistanceSquared )
641 maxDistanceSquared = distanceSquared;
645 if ( maxDistanceSquared <= distanceToleranceSquared )
647 for (
int k = i + 1; k < j; k++ )
654 simplifySection( i, maxIndex, x, y, usePoint, distanceToleranceSquared, epsilon );
655 simplifySection( maxIndex, j, x, y, usePoint, distanceToleranceSquared, epsilon );
668 const double distanceToleranceSquared = tolerance * tolerance;
669 const double *
xData =
mX.constData();
670 const double *
yData =
mY.constData();
671 const double *
zData =
mZ.constData();
672 const double *
mData =
mM.constData();
674 const int size =
mX.size();
676 std::vector< bool > usePoint( size,
true );
678 constexpr double epsilon = 4 * std::numeric_limits<double>::epsilon();
681 QVector< double > newX;
682 newX.reserve( size );
683 QVector< double > newY;
684 newY.reserve( size );
686 const bool hasZ =
is3D();
688 QVector< double > newZ;
690 newZ.reserve( size );
691 QVector< double > newM;
693 newM.reserve( size );
695 for (
int i = 0, n = size; i < n; ++i )
697 if ( usePoint[i] || i == n - 1 )
699 newX.append(
xData[i] );
700 newY.append(
yData[i] );
702 newZ.append(
zData[i] );
704 newM.append(
mData[i] );
708 const bool simplifyRing =
isRing();
709 const int newSize = newX.size();
710 if ( simplifyRing && newSize > 3 )
715 if ( distanceSquared <= distanceToleranceSquared )
718 newX.last() = newX.first();
720 newY.last() = newY.first();
724 newZ.last() = newZ.first();
729 newM.last() = newM.first();
744 auto result2D = std::minmax_element(
mX.begin(),
mX.end() );
745 const double xmin = *result2D.first;
746 const double xmax = *result2D.second;
747 result2D = std::minmax_element(
mY.begin(),
mY.end() );
748 const double ymin = *result2D.first;
749 const double ymax = *result2D.second;
751 double zmin = std::numeric_limits< double >::quiet_NaN();
752 double zmax = std::numeric_limits< double >::quiet_NaN();
756 auto resultZ = std::minmax_element(
mZ.begin(),
mZ.end() );
757 zmin = *resultZ.first;
758 zmax = *resultZ.second;
761 return QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax );
779 QDomElement elemLineString = doc.createElementNS( ns, u
"LineString"_s );
782 return elemLineString;
786 return elemLineString;
794 QDomElement elemLineString = doc.createElementNS( ns, u
"LineString"_s );
797 return elemLineString;
800 return elemLineString;
819 kml.append(
"<LinearRing>"_L1 );
823 kml.append(
"<LineString>"_L1 );
826 kml.append(
"<altitudeMode>"_L1 );
829 kml.append(
"absolute"_L1 );
833 kml.append(
"clampToGround"_L1 );
835 kml.append(
"</altitudeMode>"_L1 );
836 kml.append(
"<coordinates>"_L1 );
838 int nPoints =
mX.size();
839 for (
int i = 0; i < nPoints; ++i )
843 kml.append(
" "_L1 );
846 kml.append(
","_L1 );
850 kml.append(
","_L1 );
855 kml.append(
",0"_L1 );
858 kml.append(
"</coordinates>"_L1 );
861 kml.append(
"</LinearRing>"_L1 );
865 kml.append(
"</LineString>"_L1 );
879 const int size =
mX.size();
883 const double *x =
mX.constData();
884 const double *y =
mY.constData();
890 for (
int i = 1; i < size; ++i )
894 total += std::sqrt( dx * dx + dy * dy );
904 QVector< double > x1, y1, z1, m1;
905 QVector< double > x2, y2, z2, m2;
908 std::unique_ptr< QgsLineString > first;
909 if ( x1.isEmpty() || ( x1.size() < 2 && x2.size() >= 2 ) )
910 first = std::make_unique< QgsLineString >();
912 first = std::make_unique< QgsLineString >( x1, y1, z1, m1 );
914 std::unique_ptr< QgsLineString > second;
915 if ( x2.isEmpty() || x2.size() < 2 )
916 second = std::make_unique< QgsLineString >();
918 second = std::make_unique< QgsLineString >( x2, y2, z2, m2 );
920 return std::make_tuple( std::move( first ), std::move( second ) );
925 const double *allPointsX =
xData();
926 const double *allPointsY =
yData();
928 QVector<double> partX;
929 QVector<double> partY;
930 QSet<QgsPointXY> partPointSet;
932 QVector<QgsLineString *> disjointParts;
933 for (
size_t i = 0; i < allPointsCount; i++ )
935 const QgsPointXY point( *allPointsX++, *allPointsY++ );
936 if ( partPointSet.contains( point ) )
940 disjointParts.push_back(
new QgsLineString( partX, partY ) );
942 partX = { partX.last() };
943 partY = { partY.last() };
944 partPointSet = {
QgsPointXY( partX[0], partY[0] ) };
946 partX.push_back( point.
x() );
947 partY.push_back( point.
y() );
948 partPointSet.insert( point );
951 if ( partX.size() > 1 || disjointParts.size() == 0 )
952 disjointParts.push_back(
new QgsLineString( partX, partY ) );
954 return disjointParts;
962 const int size =
mX.size();
966 const double *x =
mX.constData();
967 const double *y =
mY.constData();
968 const double *z =
mZ.constData();
975 for (
int i = 1; i < size; ++i )
980 total += std::sqrt( dx * dx + dy * dy + dz * dz );
1002 Q_UNUSED( tolerance )
1003 Q_UNUSED( toleranceType )
1023 const bool hasZ =
static_cast< bool >( z );
1024 const bool hasM =
static_cast< bool >( m );
1045 double *destX =
mX.data();
1046 double *destY =
mY.data();
1047 double *destZ =
nullptr;
1057 double *destM =
nullptr;
1068 for (
size_t i = 0; i < size; ++i )
1095 const double distance,
const std::function<
bool(
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double )> &visitPoint
1101 double distanceTraversed = 0;
1103 if ( totalPoints == 0 )
1106 const double *x =
mX.constData();
1107 const double *y =
mY.constData();
1108 const double *z =
is3D() ?
mZ.constData() :
nullptr;
1109 const double *m =
isMeasure() ?
mM.constData() :
nullptr;
1111 double prevX = *x++;
1112 double prevY = *y++;
1113 double prevZ = z ? *z++ : 0.0;
1114 double prevM = m ? *m++ : 0.0;
1118 visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
1122 double pZ = std::numeric_limits<double>::quiet_NaN();
1123 double pM = std::numeric_limits<double>::quiet_NaN();
1124 double nextPointDistance = distance;
1125 const double eps = 4 * nextPointDistance * std::numeric_limits<double>::epsilon();
1126 for (
int i = 1; i < totalPoints; ++i )
1128 double thisX = *x++;
1129 double thisY = *y++;
1130 double thisZ = z ? *z++ : 0.0;
1131 double thisM = m ? *m++ : 0.0;
1137 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed,
segmentLength );
1140 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &pZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &pM :
nullptr );
1142 if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1145 nextPointDistance += distance;
1167 std::unique_ptr< QgsPoint > res;
1168 visitPointsByRegularDistance( distance, [&](
double x,
double y,
double z,
double m,
double,
double,
double,
double,
double,
double,
double,
double ) ->
bool {
1169 res = std::make_unique< QgsPoint >( pointType, x, y, z, m );
1172 return res.release();
1177 return lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance,
false );
1180bool QgsLineString::lineLocatePointByMPrivate(
double m,
double &x,
double &y,
double &z,
double &distanceFromStart,
bool use3DDistance,
bool haveInterpolatedM )
const
1185 distanceFromStart = 0;
1187 if ( totalPoints == 0 )
1190 const double *
xData =
mX.constData();
1191 const double *
yData =
mY.constData();
1192 const double *
mData =
mM.constData();
1194 const double *
zData =
is3D() ?
mZ.constData() :
nullptr;
1195 use3DDistance &=
static_cast< bool >(
zData );
1197 double prevX = *
xData++;
1198 double prevY = *
yData++;
1200 double prevM = *
mData++;
1203 while ( i < totalPoints )
1205 double thisX = *
xData++;
1206 double thisY = *
yData++;
1208 double thisM = *
mData++;
1211 if ( std::isnan( thisM ) )
1213 if ( haveInterpolatedM )
1217 std::unique_ptr< QgsLineString > interpolatedM(
interpolateM( use3DDistance ) );
1218 return interpolatedM->lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance,
true );
1230 double totalLengthOfSegmentsWithConstantM = 0;
1231 for (
int j = 0; j < ( totalPoints - i ); ++j )
1241 distanceFromStart += totalLengthOfSegmentsWithConstantM / 2;
1251 const double delta = ( m - prevM ) / ( thisM - prevM );
1256 z = prevZ + ( thisZ - prevZ ) * delta;
1257 distanceFromStart += distanceToPoint;
1274 if ( startDistance < 0 && endDistance < 0 )
1277 endDistance = std::max( startDistance, endDistance );
1280 if ( totalPoints == 0 )
1283 QVector< QgsPoint > substringPoints;
1284 substringPoints.reserve( totalPoints );
1292 const double *x =
mX.constData();
1293 const double *y =
mY.constData();
1294 const double *z =
is3D() ?
mZ.constData() :
nullptr;
1295 const double *m =
isMeasure() ?
mM.constData() :
nullptr;
1297 double distanceTraversed = 0;
1298 double prevX = *x++;
1299 double prevY = *y++;
1300 double prevZ = z ? *z++ : 0.0;
1301 double prevM = m ? *m++ : 0.0;
1302 bool foundStart =
false;
1304 if ( startDistance < 0 )
1307 for (
int i = 1; i < totalPoints; ++i )
1309 double thisX = *x++;
1310 double thisY = *y++;
1311 double thisZ = z ? *z++ : 0.0;
1312 double thisM = m ? *m++ : 0.0;
1316 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed +
segmentLength )
1319 const double distanceToStart = startDistance - distanceTraversed;
1320 double startX, startY;
1324 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &startZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &startM :
nullptr );
1325 substringPoints <<
QgsPoint( pointType, startX, startY, startZ, startM );
1328 if ( foundStart && ( distanceTraversed +
segmentLength > endDistance ) )
1331 const double distanceToEnd = endDistance - distanceTraversed;
1336 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &endZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &endM :
nullptr );
1337 substringPoints <<
QgsPoint( pointType, endX, endY, endZ, endM );
1339 else if ( foundStart )
1341 substringPoints <<
QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1349 if ( distanceTraversed >= endDistance )
1354 if ( !foundStart &&
qgsDoubleNear( distanceTraversed, startDistance ) )
1356 substringPoints <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM ) <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1381 if ( path.isEmpty() || path.currentPosition() != QPointF(
mX.at( 0 ),
mY.at( 0 ) ) )
1383 path.moveTo(
mX.at( 0 ),
mY.at( 0 ) );
1386 for (
int i = 1; i < nPoints; ++i )
1388 path.lineTo(
mX.at( i ),
mY.at( i ) );
1401 return compoundCurve;
1406 if (
mX.size() < 2 ||
mY.size() < 2 )
1409 const bool extendStart = startDistance > 0;
1410 const bool extendEnd = endDistance > 0;
1415 const double currentLen = std::sqrt( std::pow(
mX.at( 0 ) -
mX.at( 1 ), 2 ) + std::pow(
mY.at( 0 ) -
mY.at( 1 ), 2 ) );
1416 const double newLen = currentLen + startDistance;
1417 mX[0] =
mX.at( 1 ) + (
mX.at( 0 ) -
mX.at( 1 ) ) / currentLen * newLen;
1418 mY[0] =
mY.at( 1 ) + (
mY.at( 0 ) -
mY.at( 1 ) ) / currentLen * newLen;
1423 const int last =
mX.size() - 1;
1424 const double currentLen = std::sqrt( std::pow(
mX.at( last ) -
mX.at( last - 1 ), 2 ) + std::pow(
mY.at( last ) -
mY.at( last - 1 ), 2 ) );
1425 const double newLen = currentLen + endDistance;
1426 mX[last] =
mX.at( last - 1 ) + (
mX.at( last ) -
mX.at( last - 1 ) ) / currentLen * newLen;
1427 mY[last] =
mY.at( last - 1 ) + (
mY.at( last ) -
mY.at( last - 1 ) ) / currentLen * newLen;
1430 if ( extendStart || extendEnd )
1436 auto result = std::make_unique< QgsLineString >();
1438 return result.release();
1443 return u
"LineString"_s;
1464 mX.insert( position.
vertex, vertex.
x() );
1465 mY.insert( position.
vertex, vertex.
y() );
1468 mZ.insert( position.
vertex, vertex.
z() );
1472 mM.insert( position.
vertex, vertex.
m() );
1507 if ( positions.isEmpty() )
1512 QList<QgsVertexId>
vertices( positions.begin(), positions.end() );
1532 mX.remove( position.vertex );
1533 mY.remove( position.vertex );
1536 mZ.remove( position.vertex );
1540 mM.remove( position.vertex );
1561 mX.append( pt.
x() );
1562 mY.append( pt.
y() );
1565 mZ.append( pt.
z() );
1569 mM.append( pt.
m() );
1576 double sqrDist = std::numeric_limits<double>::max();
1577 double leftOfDist = std::numeric_limits<double>::max();
1579 double prevLeftOfX = 0.0;
1580 double prevLeftOfY = 0.0;
1581 double testDist = 0;
1582 double segmentPtX, segmentPtY;
1587 const int size =
mX.size();
1588 if ( size == 0 || size == 1 )
1594 const double *
xData =
mX.constData();
1595 const double *
yData =
mY.constData();
1596 for (
int i = 1; i < size; ++i )
1598 double prevX =
xData[i - 1];
1599 double prevY =
yData[i - 1];
1600 double currentX =
xData[i];
1601 double currentY =
yData[i];
1603 if ( testDist < sqrDist )
1606 segmentPt.
setX( segmentPtX );
1607 segmentPt.
setY( segmentPtY );
1608 vertexAfter.
part = 0;
1609 vertexAfter.
ring = 0;
1620 if (
qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1632 prevLeftOf = *leftOf;
1633 leftOfDist = testDist;
1634 prevLeftOfX = prevX;
1635 prevLeftOfY = prevY;
1637 else if ( testDist < leftOfDist )
1640 leftOfDist = testDist;
1674 double totalLineLength = 0.0;
1675 double prevX =
mX.at( 0 );
1676 double prevY =
mY.at( 0 );
1682 double currentX =
mX.at( i );
1683 double currentY =
mY.at( i );
1684 double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) + std::pow( currentY - prevY, 2.0 ) );
1700 return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1718 const int maxIndex =
mX.size();
1725 const double *x =
mX.constData();
1726 const double *y =
mY.constData();
1727 double prevX = *x++;
1728 double prevY = *y++;
1729 for (
int i = 1; i < maxIndex; ++i )
1731 mSummedUpArea += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
1779 if ( planeNormal.
z() < 0 )
1781 planeNormal = -planeNormal;
1786 if ( planeNormal.
y() < 0 )
1787 planeNormal = -planeNormal;
1791 if ( planeNormal.
x() < 0 )
1792 planeNormal = -planeNormal;
1796 const double *x =
mX.constData();
1797 const double *y =
mY.constData();
1798 const double *z =
mZ.constData();
1800 double prevX = *x++;
1801 double prevY = *y++;
1802 double prevZ = *z++;
1804 double normalX = 0.0;
1805 double normalY = 0.0;
1806 double normalZ = 0.0;
1808 for (
unsigned int i = 1; i <
mX.size(); ++i )
1810 normalX += prevY * ( *z - prevZ ) - prevZ * ( *y - prevY );
1811 normalY += prevZ * ( *x - prevX ) - prevX * ( *z - prevZ );
1812 normalZ += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
1819 mSummedUpArea3D = 0.5 * ( normalX * planeNormal.
x() + normalY * planeNormal.
y() + normalZ * planeNormal.
z() );
1842 if (
mX.count() < 2 )
1854 double currentX =
mX.at( 0 );
1855 double currentY =
mY.at( 0 );
1856 double afterX =
mX.at( 1 );
1857 double afterY =
mY.at( 1 );
1860 else if ( vertex.
vertex == 0 )
1873 double previousX =
mX.at( vertex.
vertex - 1 );
1874 double previousY =
mY.at( vertex.
vertex - 1 );
1875 double currentX =
mX.at( vertex.
vertex );
1876 double currentY =
mY.at( vertex.
vertex );
1877 double afterX =
mX.at( vertex.
vertex + 1 );
1878 double afterY =
mY.at( vertex.
vertex + 1 );
1885 if ( startVertex.
vertex < 0 || startVertex.
vertex >=
mX.count() - 1 )
1890 return std::sqrt( dx * dx + dy * dy );
1909 addZValue( std::numeric_limits<double>::quiet_NaN() );
1922 std::unique_ptr< QgsLineString > cloned(
clone() );
1929 if (
isEmpty() || ( nbpoints < 2 ) )
1934 const double range = end - start;
1935 double lineLength =
length();
1936 double lengthSoFar = 0.0;
1939 double *mOut = cloned->mM.data();
1941 for (
int i = 1; i < nbpoints; ++i )
1944 if ( lineLength > 0.0 )
1945 *mOut++ = start + range * lengthSoFar / lineLength;
1946 else if ( lineLength == 0.0 && nbpoints > 1 )
1947 *mOut++ = start + range * i / ( nbpoints - 1 );
1961 if ( totalPoints < 2 )
1962 return std::unique_ptr< QgsLineString >(
clone() );
1964 const double *
xData =
mX.constData();
1965 const double *
yData =
mY.constData();
1966 const double *
mData =
mM.constData();
1967 const double *
zData =
is3D() ?
mZ.constData() :
nullptr;
1968 use3DDistance &=
static_cast< bool >(
zData );
1970 QVector< double > xOut( totalPoints );
1971 QVector< double > yOut( totalPoints );
1972 QVector< double > mOut( totalPoints );
1973 QVector< double > zOut(
static_cast< bool >(
zData ) ? totalPoints : 0 );
1975 double *xOutData = xOut.data();
1976 double *yOutData = yOut.data();
1977 double *mOutData = mOut.data();
1978 double *zOutData =
static_cast< bool >(
zData ) ? zOut.data() :
nullptr;
1981 double currentSegmentLength = 0;
1982 double lastValidM = std::numeric_limits< double >::quiet_NaN();
1983 double prevX = *
xData;
1984 double prevY = *
yData;
1986 while ( i < totalPoints )
1988 double thisX = *
xData++;
1989 double thisY = *
yData++;
1991 double thisM = *
mData++;
1995 if ( !std::isnan( thisM ) )
1997 *xOutData++ = thisX;
1998 *yOutData++ = thisY;
1999 *mOutData++ = thisM;
2001 *zOutData++ = thisZ;
2008 double scanAheadM = thisM;
2009 while ( i + j + 1 < totalPoints && std::isnan( scanAheadM ) )
2011 scanAheadM =
mData[j];
2014 if ( std::isnan( scanAheadM ) )
2019 *xOutData++ = thisX;
2020 *yOutData++ = thisY;
2021 *mOutData++ = scanAheadM;
2023 *zOutData++ = thisZ;
2024 for ( ; i < j; ++i )
2028 *xOutData++ = thisX;
2029 *yOutData++ = thisY;
2030 *mOutData++ = scanAheadM;
2033 *zOutData++ = *
zData++;
2035 lastValidM = scanAheadM;
2041 double scanAheadX = thisX;
2042 double scanAheadY = thisY;
2043 double scanAheadZ = thisZ;
2044 double distanceToNextValidM = currentSegmentLength;
2045 std::vector< double > scanAheadSegmentLengths;
2046 scanAheadSegmentLengths.emplace_back( currentSegmentLength );
2048 double nextValidM = std::numeric_limits< double >::quiet_NaN();
2049 while ( i + j < totalPoints - 1 )
2051 double nextScanAheadX =
xData[j];
2052 double nextScanAheadY =
yData[j];
2053 double nextScanAheadZ =
zData ?
zData[j] : 0;
2054 double nextScanAheadM =
mData[j];
2055 const double scanAheadSegmentLength = use3DDistance ?
QgsGeometryUtilsBase::distance3D( scanAheadX, scanAheadY, scanAheadZ, nextScanAheadX, nextScanAheadY, nextScanAheadZ )
2057 scanAheadSegmentLengths.emplace_back( scanAheadSegmentLength );
2058 distanceToNextValidM += scanAheadSegmentLength;
2060 if ( !std::isnan( nextScanAheadM ) )
2062 nextValidM = nextScanAheadM;
2066 scanAheadX = nextScanAheadX;
2067 scanAheadY = nextScanAheadY;
2068 scanAheadZ = nextScanAheadZ;
2072 if ( std::isnan( nextValidM ) )
2075 *xOutData++ = thisX;
2076 *yOutData++ = thisY;
2077 *mOutData++ = lastValidM;
2079 *zOutData++ = thisZ;
2081 for ( ; i < totalPoints; ++i )
2083 *xOutData++ = *
xData++;
2084 *yOutData++ = *
yData++;
2085 *mOutData++ = lastValidM;
2087 *zOutData++ = *
zData++;
2094 const double delta = ( nextValidM - lastValidM ) / distanceToNextValidM;
2095 *xOutData++ = thisX;
2096 *yOutData++ = thisY;
2097 *mOutData++ = lastValidM + delta * scanAheadSegmentLengths[0];
2098 double totalScanAheadLength = scanAheadSegmentLengths[0];
2100 *zOutData++ = thisZ;
2101 for (
int k = 1; k <= j; ++i, ++k )
2105 *xOutData++ = thisX;
2106 *yOutData++ = thisY;
2107 totalScanAheadLength += scanAheadSegmentLengths[k];
2108 *mOutData++ = lastValidM + delta * totalScanAheadLength;
2111 *zOutData++ = *
zData++;
2113 lastValidM = nextValidM;
2122 return std::make_unique< QgsLineString >( xOut, yOut, zOut, mOut );
2128 if ( fromVertex.
part != 0 || fromVertex.
ring != 0 || toVertex.
part != 0 || toVertex.
ring != 0 )
2131 const int fromVertexNumber = fromVertex.
vertex;
2132 const int toVertexNumber = toVertex.
vertex;
2135 if ( fromVertexNumber > toVertexNumber )
2141 if ( fromVertexNumber < 0 || fromVertexNumber >= nPoints || toVertexNumber < 0 || toVertexNumber >= nPoints )
2144 if ( fromVertexNumber == toVertexNumber )
2147 const bool is3DGeometry =
is3D();
2148 const double *
xData =
mX.constData();
2149 const double *
yData =
mY.constData();
2150 const double *
zData = is3DGeometry ?
mZ.constData() :
nullptr;
2151 double totalDistance = 0.0;
2154 for (
int i = fromVertexNumber; i < toVertexNumber; ++i )
2165 totalDistance += std::sqrt( dx * dx + dy * dy + dz * dz );
2168 return totalDistance;
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
VertexType
Types of vertex.
@ Segment
The actual start or end point of a segment.
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
WkbType
The WKB type describes the number of dimensions a geometry has.
@ LineString25D
LineString25D.
@ LineStringM
LineStringM.
@ LineStringZM
LineStringZM.
@ LineStringZ
LineStringZ.
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.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
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.
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.
A 3-dimensional box composed of x, y, z coordinates.
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Compound curve geometry type.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
bool mHasCachedSummedUpArea
virtual bool isRing() const
Returns true if the curve is a ring.
bool mHasCachedSummedUpArea3D
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM, bool removeRedundantPoints) const
Helper function for QgsCurve subclasses to snap to grids.
QgsBox3D mBoundingBox
Cached bounding box.
bool hasVertex(QgsVertexId position) const override
Returns true if the geometry contains a vertex matching the given position.
Convenience functions for geometry utils.
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double distance3D(double x1, double y1, double z1, double x2, double y2, double z2)
Returns the 3D distance between (x1, y1, z1) and (x2, y2, z2).
static void interpolatePointOnCubicBezier(double p0x, double p0y, double p0z, double p0m, double p1x, double p1y, double p1z, double p1m, double p2x, double p2y, double p2z, double p2m, double p3x, double p3y, double p3z, double p3m, double t, bool hasZ, bool hasM, double &outX, double &outY, double &outZ, double &outM)
Evaluates a point on a cubic Bézier curve defined by four control points.
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
static bool checkWeaklyFor3DPlane(const QgsAbstractGeometry *geom, QgsPoint &pt1, QgsPoint &pt2, QgsPoint &pt3, double epsilon=std::numeric_limits< double >::epsilon())
Checks if a 3D geometry has a plane defined by at least 3 non-collinear points.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static json pointsToJson(const QgsPointSequence &points, int precision, Qgis::GeoJsonProfile profile)
Returns coordinates as json object.
Represents a single 2D line segment, consisting of a 2D start and end vertex only.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
static std::unique_ptr< QgsLineString > fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex type of a point within the curve.
bool isClosed() const override
Returns true if the curve is closed.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
QVector< QgsLineString * > splitToDisjointXYParts() const
Divides the linestring into parts that don't share any points or lines.
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.
static std::unique_ptr< QgsLineString > fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
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.
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.
void sumUpArea3D(double &sum) const override
Calculates the shoelace/triangle formula sum for the points in the linestring.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
QgsLineString()
Constructor for an empty linestring geometry.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
std::unique_ptr< QgsLineString > interpolateM(bool use3DDistance=true) const
Returns a copy of this line with all missing (NaN) m values interpolated from m values of surrounding...
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...
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override
Returns true if the bounding box of this geometry intersects with a rectangle.
bool deleteVertices(const QSet< QgsVertexId > &positions) override
Deletes vertices within the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
std::unique_ptr< QgsLineString > measuredLine(double start, double end) const
Re-write the measure ordinate (or add one, if it isn't already there) interpolating the measure betwe...
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.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
void extend(double startDistance, double endDistance)
Extends the line geometry by extrapolating out the start or end of the line by a specified distance.
QgsCompoundCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type QgsCompoundCurve.
double distanceBetweenVertices(QgsVertexId fromVertex, QgsVertexId toVertex) const override
Returns the distance along the curve between two vertices.
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
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.
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.
Q_DECL_DEPRECATED QgsBox3D calculateBoundingBox3d() const
Calculates the minimal 3D bounding box for the geometry.
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.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsLineString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
json asJsonObject(int precision=17, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy) const override
Returns a json object representation of the geometry with the given precision and profile.
Point geometry type, with support for z-dimension and m-values.
void setY(double y)
Sets the point's y-coordinate.
void setX(double x)
Sets the point's x-coordinate.
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
bool dropMValue() override
Drops any measure values which exist in the geometry.
int numPoints() const override
Returns the number of points in the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the simple curve does not have m values.
void splitCurveAtVertexProtected(int index, QVector< double > &x1, QVector< double > &y1, QVector< double > &z1, QVector< double > &m1, QVector< double > &x2, QVector< double > &y2, QVector< double > &z2, QVector< double > &m2) const
Returns coordinate vectors for the split curves.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
const double * yData() const
Returns a const pointer to the y vertex data.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the simple curve.
QgsSimpleCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
const double * xData() const
Returns a const pointer to the x vertex data.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the simple curve does not have z values.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
void normalize()
Normalizes the current vector in place.
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
static Qgis::WkbType zmType(Qgis::WkbType type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QVector< QgsPoint > QgsPointSequence
void simplifySection(int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon)
QLineF segment(int index, QRectF rect, double radius)
double distance2D(const QgsPolylineXY &coords)
Utility class for identifying a unique vertex within a geometry.