28 #include <QJsonObject>
30 #include <QPainterPath>
32 #include <nlohmann/json.hpp>
42 bool hasZ = p1.
is3D();
87 if ( mX.count() != otherLine->mX.count() )
90 for (
int i = 0; i < mX.count(); ++i )
108 auto result = qgis::make_unique< QgsCircularString >();
110 return result.release();
115 return QStringLiteral(
"CircularString" );
142 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
146 bbox = segmentBoundingBox(
QgsPoint( mX[i], mY[i] ),
QgsPoint( mX[i + 1], mY[i + 1] ),
QgsPoint( mX[i + 2], mY[i + 2] ) );
155 if ( nPoints > 0 && nPoints % 2 == 0 )
168 double centerX, centerY, radius;
177 bbox.combineExtentWith( pt3.
x(), pt3.
y() );
179 QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
180 QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
181 for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
183 bbox.combineExtentWith( cpIt->x(), cpIt->y() );
188 QgsPointSequence QgsCircularString::compassPointsOnSegment(
double p1Angle,
double p2Angle,
double p3Angle,
double centerX,
double centerY,
double radius )
192 QgsPoint nPoint( centerX, centerY + radius );
193 QgsPoint ePoint( centerX + radius, centerY );
194 QgsPoint sPoint( centerX, centerY - radius );
195 QgsPoint wPoint( centerX - radius, centerY );
197 if ( p3Angle >= p1Angle )
199 if ( p2Angle > p1Angle && p2Angle < p3Angle )
201 if ( p1Angle <= 90 && p3Angle >= 90 )
203 pointList.append( nPoint );
205 if ( p1Angle <= 180 && p3Angle >= 180 )
207 pointList.append( wPoint );
209 if ( p1Angle <= 270 && p3Angle >= 270 )
211 pointList.append( sPoint );
216 pointList.append( ePoint );
217 if ( p1Angle >= 90 || p3Angle <= 90 )
219 pointList.append( nPoint );
221 if ( p1Angle >= 180 || p3Angle <= 180 )
223 pointList.append( wPoint );
225 if ( p1Angle >= 270 || p3Angle <= 270 )
227 pointList.append( sPoint );
233 if ( p2Angle < p1Angle && p2Angle > p3Angle )
235 if ( p1Angle >= 270 && p3Angle <= 270 )
237 pointList.append( sPoint );
239 if ( p1Angle >= 180 && p3Angle <= 180 )
241 pointList.append( wPoint );
243 if ( p1Angle >= 90 && p3Angle <= 90 )
245 pointList.append( nPoint );
250 pointList.append( ePoint );
251 if ( p1Angle <= 270 || p3Angle >= 270 )
253 pointList.append( sPoint );
255 if ( p1Angle <= 180 || p3Angle >= 180 )
257 pointList.append( wPoint );
259 if ( p1Angle <= 90 || p3Angle >= 90 )
261 pointList.append( nPoint );
286 mX.resize( nVertices );
287 mY.resize( nVertices );
288 hasZ ? mZ.resize( nVertices ) : mZ.clear();
289 hasM ? mM.resize( nVertices ) : mM.clear();
290 for (
int i = 0; i < nVertices; ++i )
317 if (
parts.second.compare( QLatin1String(
"EMPTY" ), Qt::CaseInsensitive ) == 0 )
326 int binarySize =
sizeof( char ) +
sizeof( quint32 ) +
sizeof( quint32 );
330 wkbArray.resize( binarySize );
333 wkb << static_cast<quint32>(
wkbType() );
345 wkt += QStringLiteral(
"EMPTY" );
358 std::unique_ptr< QgsLineString > line(
curveToLine() );
359 QDomElement gml = line->asGml2( doc,
precision, ns, axisOrder );
368 QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral(
"Curve" ) );
373 QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral(
"segments" ) );
374 QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral(
"ArcString" ) );
376 elemSegments.appendChild( elemArcString );
377 elemCurve.appendChild( elemSegments );
385 std::unique_ptr< QgsLineString > line(
curveToLine() );
399 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
430 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
444 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
445 result->mX, result->mY, result->mZ, result->mM );
447 return result.release();
454 if ( mX.count() <= 3 )
457 double prevX = mX.at( 0 );
458 double prevY = mY.at( 0 );
460 bool useZ = hasZ && useZValues;
461 double prevZ = useZ ? mZ.at( 0 ) : 0;
463 int remaining = mX.count();
466 while ( i + 1 < remaining )
468 double currentCurveX = mX.at( i );
469 double currentCurveY = mY.at( i );
470 double currentX = mX.at( i + 1 );
471 double currentY = mY.at( i + 1 );
472 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
505 return std::min( mX.size(), mY.size() );
510 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
515 double x = mX.at( i );
516 double y = mY.at( i );
547 if ( index >= 0 && index < mX.size() )
548 return mX.at( index );
555 if ( index >= 0 && index < mY.size() )
556 return mY.at( index );
565 int size = mX.size();
567 double *srcX = mX.data();
568 double *srcY = mY.data();
569 double *srcM = hasM ? mM.data() :
nullptr;
570 double *srcZ = hasZ ? mZ.data() :
nullptr;
572 double *destX = srcX;
573 double *destY = srcY;
574 double *destM = srcM;
575 double *destZ = srcZ;
577 int filteredPoints = 0;
578 for (
int i = 0; i < size; ++i )
582 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
583 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
585 if ( filter(
QgsPoint( x, y, z, m ) ) )
597 mX.resize( filteredPoints );
598 mY.resize( filteredPoints );
600 mZ.resize( filteredPoints );
602 mM.resize( filteredPoints );
611 int size = mX.size();
613 double *srcX = mX.data();
614 double *srcY = mY.data();
615 double *srcM = hasM ? mM.data() :
nullptr;
616 double *srcZ = hasZ ? mZ.data() :
nullptr;
618 for (
int i = 0; i < size; ++i )
622 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
623 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
639 for (
int i = 0; i < nPts; ++i )
641 pts.push_back(
pointN( i ) );
661 bool hasZ = firstPt.
is3D();
666 mX.resize(
points.size() );
667 mY.resize(
points.size() );
670 mZ.resize(
points.size() );
678 mM.resize(
points.size() );
685 for (
int i = 0; i <
points.size(); ++i )
691 double z =
points.at( i ).z();
692 mZ[i] = std::isnan( z ) ? 0 : z;
696 double m =
points.at( i ).m();
697 mM[i] = std::isnan( m ) ? 0 : m;
713 double *zArray = mZ.data();
717 bool useDummyZ = !hasZ || !transformZ;
720 zArray =
new double[nPoints];
721 for (
int i = 0; i < nPoints; ++i )
740 for (
int i = 0; i < nPoints; ++i )
743 t.map( mX.at( i ), mY.at( i ), &x, &y );
748 mZ[i] = mZ.at( i ) * zScale + zTranslate;
752 mM[i] = mM.at( i ) * mScale + mTranslate;
765 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
767 path.moveTo( QPointF( mX[0], mY[0] ) );
770 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
774 for (
int j = 1; j < pt.size(); ++j )
776 path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
784 if ( nPoints % 2 == 0 )
786 path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
791 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
793 double centerX, centerY, radius;
795 radius, centerX, centerY );
800 double diameter = 2 * radius;
801 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
812 if ( position.
vertex >= mX.size() || position.
vertex < 1 )
817 mX.insert( position.
vertex, vertex.
x() );
818 mY.insert( position.
vertex, vertex.
y() );
821 mZ.insert( position.
vertex, vertex.
z() );
825 mM.insert( position.
vertex, vertex.
m() );
828 bool vertexNrEven = ( position.
vertex % 2 == 0 );
843 if ( position.
vertex < 0 || position.
vertex >= mX.size() )
848 mX[position.
vertex] = newPos.
x();
849 mY[position.
vertex] = newPos.
y();
852 mZ[position.
vertex] = newPos.
z();
856 mM[position.
vertex] = newPos.
m();
870 if ( position.
vertex < 0 || position.
vertex > ( nVertices - 1 ) )
875 if ( position.
vertex < ( nVertices - 2 ) )
908 double minDist = std::numeric_limits<double>::max();
911 int minDistLeftOf = 0;
913 double currentDist = 0.0;
916 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
918 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter,
leftOf, epsilon );
919 if ( currentDist < minDist )
921 minDist = currentDist;
922 minDistSegmentPoint = segmentPt;
931 if ( minDist == std::numeric_limits<double>::max() )
934 segmentPt = minDistSegmentPoint;
935 vertexAfter = minDistVertexAfter;
936 vertexAfter.
part = 0;
937 vertexAfter.
ring = 0;
960 for (
int i = 0; i < maxIndex; i += 2 )
963 QgsPoint p2( mX[i + 1], mY[i + 1] );
964 QgsPoint p3( mX[i + 2], mY[i + 2] );
974 sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
977 double midPointX = ( p1.
x() + p3.
x() ) / 2.0;
978 double midPointY = ( p1.
y() + p3.
y() ) / 2.0;
980 double radius, centerX, centerY;
984 double r2 = radius * radius;
995 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
996 double circleChordArea = 0;
997 if ( circlePointLeftOfLine == centerPointLeftOfLine )
999 circleChordArea = M_PI * r2 * ( 1 - cov );
1003 circleChordArea = M_PI * r2 * cov;
1006 if ( !circlePointLeftOfLine )
1008 sum += circleChordArea;
1012 sum -= circleChordArea;
1022 double QgsCircularString::closestPointOnArc(
double x1,
double y1,
double x2,
double y2,
double x3,
double y3,
1025 double radius, centerX, centerY;
1050 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1051 vertexAfter.
vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1058 segmentPt.
setX( pt.
x() );
1059 segmentPt.
setY( pt.
y() );
1065 double sqrDistancePointToCenter = ( pt.
x() - centerX ) * ( pt.
x() - centerX ) + ( pt.
y() - centerY ) * ( pt.
y() - centerY );
1066 *
leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1067 : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1073 void QgsCircularString::insertVertexBetween(
int after,
int before,
int pointOnCircle )
1075 double xAfter = mX.at( after );
1076 double yAfter = mY.at( after );
1077 double xBefore = mX.at( before );
1078 double yBefore = mY.at( before );
1079 double xOnCircle = mX.at( pointOnCircle );
1080 double yOnCircle = mY.at( pointOnCircle );
1082 double radius, centerX, centerY;
1085 double x = ( xAfter + xBefore ) / 2.0;
1086 double y = ( yAfter + yBefore ) / 2.0;
1089 mX.insert( before, newVertex.
x() );
1090 mY.insert( before, newVertex.
y() );
1094 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1098 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1111 int before = vId.
vertex - 1;
1113 int after = vId.
vertex + 1;
1115 if ( vId.
vertex % 2 != 0 )
1145 int vertex1 = vId.
vertex - 2;
1146 int vertex2 = vId.
vertex - 1;
1147 int vertex3 = vId.
vertex;
1149 QgsPoint( mX[vertex1], mY[vertex1] ),
QgsPoint( mX[vertex2], mY[vertex2] ),
QgsPoint( mX[vertex3], mY[vertex3] ) );
1150 int vertex4 = vId.
vertex + 1;
1151 int vertex5 = vId.
vertex + 2;
1153 QgsPoint( mX[vertex3], mY[vertex3] ),
QgsPoint( mX[vertex4], mY[vertex4] ),
QgsPoint( mX[vertex5], mY[vertex5] ) );
1162 if ( startVertex.
vertex < 0 || startVertex.
vertex >= mX.count() - 2 )
1165 if ( startVertex.
vertex % 2 == 1 )
1168 double x1 = mX.at( startVertex.
vertex );
1169 double y1 = mY.at( startVertex.
vertex );
1170 double x2 = mX.at( startVertex.
vertex + 1 );
1171 double y2 = mY.at( startVertex.
vertex + 1 );
1172 double x3 = mX.at( startVertex.
vertex + 2 );
1173 double y3 = mY.at( startVertex.
vertex + 2 );
1180 std::reverse( copy->mX.begin(), copy->mX.end() );
1181 std::reverse( copy->mY.begin(), copy->mY.end() );
1184 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1188 std::reverse( copy->mM.begin(), copy->mM.end() );
1198 double distanceTraversed = 0;
1200 if ( totalPoints == 0 )
1209 const double *x = mX.constData();
1210 const double *y = mY.constData();
1211 const double *z =
is3D() ? mZ.constData() :
nullptr;
1212 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1214 double prevX = *x++;
1215 double prevY = *y++;
1216 double prevZ = z ? *z++ : 0.0;
1217 double prevM = m ? *m++ : 0.0;
1221 return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1224 for (
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1233 double z2 = z ? *z++ : 0.0;
1234 double m2 = m ? *m++ : 0.0;
1238 double z3 = z ? *z++ : 0.0;
1239 double m3 = m ? *m++ : 0.0;
1245 const double distanceToPoint = std::min( distance - distanceTraversed,
segmentLength );
1247 QgsPoint( pointType, x2, y2, z2, m2 ),
1248 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1264 if ( startDistance < 0 && endDistance < 0 )
1267 endDistance = std::max( startDistance, endDistance );
1269 double distanceTraversed = 0;
1271 if ( totalPoints == 0 )
1274 QVector< QgsPoint > substringPoints;
1282 const double *x = mX.constData();
1283 const double *y = mY.constData();
1284 const double *z =
is3D() ? mZ.constData() :
nullptr;
1285 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1287 double prevX = *x++;
1288 double prevY = *y++;
1289 double prevZ = z ? *z++ : 0.0;
1290 double prevM = m ? *m++ : 0.0;
1291 bool foundStart =
false;
1293 if (
qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1295 substringPoints <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1299 substringPoints.reserve( totalPoints );
1301 for (
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1310 double z2 = z ? *z++ : 0.0;
1311 double m2 = m ? *m++ : 0.0;
1315 double z3 = z ? *z++ : 0.0;
1316 double m3 = m ? *m++ : 0.0;
1318 bool addedSegmentEnd =
false;
1320 if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1323 const double distanceToStart = startDistance - distanceTraversed;
1325 QgsPoint( pointType, x2, y2, z2, m2 ),
1326 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1329 const bool endPointOnSegment = distanceTraversed +
segmentLength > endDistance;
1330 if ( endPointOnSegment )
1332 const double distanceToEnd = endDistance - distanceTraversed;
1333 const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1336 QgsPoint( pointType, x2, y2, z2, m2 ),
1337 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1339 QgsPoint( pointType, x2, y2, z2, m2 ),
1340 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1341 addedSegmentEnd =
true;
1345 const double midPointDistance = (
segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1348 QgsPoint( pointType, x2, y2, z2, m2 ),
1349 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1350 <<
QgsPoint( pointType, x3, y3, z3, m3 );
1351 addedSegmentEnd =
true;
1355 if ( !addedSegmentEnd && foundStart && ( distanceTraversed +
segmentLength > endDistance ) )
1358 const double distanceToEnd = endDistance - distanceTraversed;
1361 QgsPoint( pointType, x2, y2, z2, m2 ),
1362 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1365 QgsPoint( pointType, x2, y2, z2, m2 ),
1366 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1368 else if ( !addedSegmentEnd && foundStart )
1370 substringPoints <<
QgsPoint( pointType, x2, y2, z2, m2 )
1371 <<
QgsPoint( pointType, x3, y3, z3, m3 );
1375 if ( distanceTraversed > endDistance )
1384 std::unique_ptr< QgsCircularString > result = qgis::make_unique< QgsCircularString >();
1385 result->setPoints( substringPoints );
1386 return result.release();
1399 mZ.reserve( nPoints );
1400 for (
int i = 0; i < nPoints; ++i )
1417 mM.reserve( nPoints );
1418 for (
int i = 0; i < nPoints; ++i )
1451 std::swap( mX, mY );