40 #define DEG2RAD(x) ((x)*M_PI/180)
41 #define RAD2DEG(r) (180.0 * (r) / M_PI)
42 #define POW2(x) ((x)*(x))
49 mInvFlattening = -1.0;
83 setFromParams( params );
93 mSemiMajor = semiMajor;
94 mSemiMinor = semiMinor;
95 mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
102 double QgsDistanceArea::measure(
const QgsAbstractGeometry *geomV2, MeasureType type )
const
110 if ( geomDimension <= 0 )
115 MeasureType measureType = type;
116 if ( measureType == Default )
118 measureType = ( geomDimension == 1 ? Length : Area );
124 if ( measureType == Length )
130 return geomV2->
area();
142 sum += measure( collection->
geometryN( i ), measureType );
147 if ( measureType == Length )
149 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomV2 );
162 const QgsSurface *surface = qgsgeometry_cast<const QgsSurface *>( geomV2 );
189 return measure( geomV2, Area );
198 return measure( geomV2, Length );
207 if ( !geomV2 || geomV2->
dimension() < 2 )
218 QVector< const QgsSurface * > surfaces;
219 const QgsSurface *surf = qgsgeometry_cast<const QgsSurface *>( geomV2 );
222 surfaces.append( surf );
224 const QgsMultiSurface *multiSurf = qgsgeometry_cast<const QgsMultiSurface *>( geomV2 );
227 surfaces.reserve( ( surf ? 1 : 0 ) + multiSurf->
numGeometries() );
235 QVector<const QgsSurface *>::const_iterator surfaceIt = surfaces.constBegin();
236 for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
247 length += measure( outerRing );
250 for (
int i = 0; i < nInnerRings; ++i )
267 QVector<QgsPointXY> linePoints;
268 curve->
points( linePointsV2 );
275 if ( points.size() < 2 )
284 p1 = mCoordTransform.
transform( points[0] );
288 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
293 total += computeDistanceBearing( p1, p2 );
309 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) );
326 QgsDebugMsgLevel( QStringLiteral(
"Ellipsoidal calculations is enabled, using ellipsoid %1" ).arg( mEllipsoid ), 4 );
331 QgsDebugMsgLevel( QStringLiteral(
"New points are %1 and %2, calculating..." ).arg( pp1.
toString( 4 ), pp2.toString( 4 ) ), 4 );
332 result = computeDistanceBearing( pp1, pp2 );
336 QgsDebugMsgLevel( QStringLiteral(
"Cartesian calculation on canvas coordinates" ), 4 );
343 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) );
367 p2 = p1.
project( distance, azimuth );
369 QgsDebugMsgLevel( QStringLiteral(
"Converted distance of %1 %2 to %3 distance %4 %5, using azimuth[%6] from point[%7] to point[%8] sourceCrs[%9] mEllipsoid[%10] isGeographic[%11] [%12]" )
370 .arg( QString::number( distance,
'f', 7 ),
372 QString::number( result,
'f', 7 ),
373 mCoordTransform.
sourceCrs().
isGeographic() ? QStringLiteral(
"Geographic" ) : QStringLiteral(
"Cartesian" ),
381 .arg( QStringLiteral(
"SemiMajor[%1] SemiMinor[%2] InvFlattening[%3] " ).arg( QString::number( mSemiMajor,
'f', 7 ), QString::number( mSemiMinor,
'f', 7 ), QString::number( mInvFlattening,
'f', 7 ) ) ), 4 );
382 if ( projectedPoint )
397 const QgsPointXY &p1,
double distance,
double azimuth )
const
400 double a = mSemiMajor;
401 double b = mSemiMinor;
402 double f = 1 / mInvFlattening;
403 if ( ( ( a < 0 ) && ( b < 0 ) ) ||
404 ( ( p1.
x() < -180.0 ) || ( p1.
x() > 180.0 ) || ( p1.
y() < -85.05115 ) || ( p1.
y() > 85.05115 ) ) )
410 double radians_lat =
DEG2RAD( p1.
y() );
411 double radians_long =
DEG2RAD( p1.
x() );
412 double b2 =
POW2( b );
414 double tan_u1 = omf * std::tan( radians_lat );
415 double u1 = std::atan( tan_u1 );
416 double sigma, last_sigma, delta_sigma, two_sigma_m;
417 double sigma1, sin_alpha, alpha, cos_alphasq;
419 double lat2, lambda, lambda2, C, omega;
423 azimuth = azimuth + M_PI * 2.0;
425 if ( azimuth > ( M_PI * 2.0 ) )
427 azimuth = azimuth - M_PI * 2.0;
429 sigma1 = std::atan2( tan_u1, std::cos( azimuth ) );
430 sin_alpha = std::cos( u1 ) * std::sin( azimuth );
431 alpha = std::asin( sin_alpha );
432 cos_alphasq = 1.0 -
POW2( sin_alpha );
433 u2 =
POW2( std::cos( alpha ) ) * (
POW2( a ) - b2 ) / b2;
434 A = 1.0 + ( u2 / 16384.0 ) * ( 4096.0 + u2 * ( -768.0 + u2 * ( 320.0 - 175.0 * u2 ) ) );
435 B = ( u2 / 1024.0 ) * ( 256.0 + u2 * ( -128.0 + u2 * ( 74.0 - 47.0 * u2 ) ) );
436 sigma = ( distance / ( b * A ) );
439 two_sigma_m = 2.0 * sigma1 + sigma;
440 delta_sigma = B * std::sin( sigma ) * ( std::cos( two_sigma_m ) + ( B / 4.0 ) * ( std::cos( sigma ) * ( -1.0 + 2.0 *
POW2( std::cos( two_sigma_m ) ) - ( B / 6.0 ) * std::cos( two_sigma_m ) * ( -3.0 + 4.0 *
POW2( std::sin( sigma ) ) ) * ( -3.0 + 4.0 *
POW2( std::cos( two_sigma_m ) ) ) ) ) );
442 sigma = ( distance / ( b * A ) ) + delta_sigma;
445 while ( i < 999 && std::fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 );
447 lat2 = std::atan2( ( std::sin( u1 ) * std::cos( sigma ) + std::cos( u1 ) * std::sin( sigma ) *
448 std::cos( azimuth ) ), ( omf * std::sqrt(
POW2( sin_alpha ) +
449 POW2( std::sin( u1 ) * std::sin( sigma ) - std::cos( u1 ) * std::cos( sigma ) *
450 std::cos( azimuth ) ) ) ) );
451 lambda = std::atan2( ( std::sin( sigma ) * std::sin( azimuth ) ), ( std::cos( u1 ) * std::cos( sigma ) -
452 std::sin( u1 ) * std::sin( sigma ) * std::cos( azimuth ) ) );
453 C = ( f / 16.0 ) * cos_alphasq * ( 4.0 + f * ( 4.0 - 3.0 * cos_alphasq ) );
454 omega = lambda - ( 1.0 - C ) * f * sin_alpha * ( sigma + C * std::sin( sigma ) *
455 ( std::cos( two_sigma_m ) + C * std::cos( sigma ) * ( -1.0 + 2.0 *
POW2( std::cos( two_sigma_m ) ) ) ) );
456 lambda2 = radians_long + omega;
465 p1.
setX( p1.
x() + 360 );
467 p2.
setX( p2.
x() + 360 );
470 double p1x = p1.
x() < 180 ? p1.
x() : p2.
x();
471 double p1y = p1.
x() < 180 ? p1.
y() : p2.
y();
472 double p2x = p1.
x() < 180 ? p2.
x() : p1.
x();
473 double p2y = p1.
x() < 180 ? p2.
y() : p1.
y();
481 fractionAlongLine = ( 180 - p1x ) / ( p2x - p1x );
483 fractionAlongLine = 1 - fractionAlongLine;
484 return p1y + ( 180 - p1x ) / ( p2x - p1x ) * ( p2y - p1y );
488 geod_init( &geod, mSemiMajor, 1 / mInvFlattening );
490 geod_geodesicline line;
491 geod_inverseline( &line, &geod, p1y, p1x, p2y, p2x, GEOD_ALL );
493 const double totalDist = line.s13;
494 double intersectionDist = line.s13;
499 while ( std::fabs( lon - 180.0 ) > 0.00000001 && iterations < 100 )
501 if ( iterations > 0 && std::fabs( p2x - p1x ) > 5 )
514 QgsDebugMsgLevel( QStringLiteral(
"Narrowed window to %1, %2 - %3, %4" ).arg( p1x ).arg( p1y ).arg( p2x ).arg( p2y ), 4 );
516 geod_inverseline( &line, &geod, p1y, p1x, p2y, p2x, GEOD_ALL );
517 intersectionDist = line.s13 * 0.5;
524 intersectionDist *= ( 180.0 - p1x ) / ( lon - p1x );
529 geod_position( &line, intersectionDist, &lat, &lon, &t );
535 QgsDebugMsgLevel( QStringLiteral(
"After %1 iterations lon is %2, lat is %3, dist from p1: %4" ).arg( iterations ).arg( lon ).arg( lat ).arg( intersectionDist ), 4 );
538 fractionAlongLine = intersectionDist / totalDist;
540 fractionAlongLine = 1 - fractionAlongLine;
556 std::unique_ptr< QgsMultiLineString > res = qgis::make_unique< QgsMultiLineString >();
559 const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *part );
567 std::unique_ptr< QgsLineString > l = qgis::make_unique< QgsLineString >();
574 QVector< QgsPoint > newPoints;
582 for (
int i = 0; i < line->
numPoints(); i++ )
588 x = std::fmod( x, 360.0 );
599 if ( i > 0 && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
608 z = prevZ + ( p.
z() - prevZ ) * fract;
612 m = prevM + ( p.
m() - prevM ) * fract;
616 if ( prevLon < -120 )
621 QgsPoint newPoint( antiMeridianPoint );
627 if ( std::isfinite( newPoint.
x() ) && std::isfinite( newPoint.
y() ) )
629 newPoints << newPoint;
634 newPoints.reserve( line->
numPoints() - i + 1 );
641 if ( std::isfinite( antiMeridianPoint.
x() ) && std::isfinite( antiMeridianPoint.
y() ) )
645 newPoint.
setX( antiMeridianPoint.
x() );
646 newPoint.
setY( antiMeridianPoint.
y() );
647 newPoints << newPoint;
663 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform linestring. Unable to calculate break point." ) );
664 res->addGeometry( line->
clone() );
677 return QVector< QVector< QgsPointXY > >() << ( QVector< QgsPointXY >() << p1 << p2 );
681 geod_init( &geod, mSemiMajor, 1 / mInvFlattening );
691 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate geodesic line." ) );
692 return QVector< QVector< QgsPointXY > >();
695 geod_geodesicline line;
696 geod_inverseline( &line, &geod, pp1.
y(), pp1.
x(), pp2.
y(), pp2.
x(), GEOD_ALL );
697 const double totalDist = line.s13;
699 QVector< QVector< QgsPointXY > > res;
700 QVector< QgsPointXY > currentPart;
703 double prevLon = pp1.
x();
704 double prevLat = pp1.
y();
705 bool lastRun =
false;
719 geod_position( &line, d, &lat, &lon, &t );
722 if ( breakLine && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
733 if ( prevLon < -120 )
738 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
756 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
782 if ( d >= totalDist )
808 curve->
points( linePointsV2 );
809 QVector<QgsPointXY> linePoints;
821 QVector<QgsPointXY> pts;
822 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
824 pts.append( mCoordTransform.
transform( *i ) );
826 return computePolygonArea( pts );
830 return computePolygonArea( points );
836 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate polygon area." ) );
851 computeDistanceBearing( pp1, pp2, &
bearing );
855 double dx = p2.
x() - p1.
x();
856 double dy = p2.
y() - p1.
y();
857 bearing = std::atan2( dx, dy );
867 double QgsDistanceArea::computeDistanceBearing(
869 double *course1,
double *course2 )
const
875 double a = mSemiMajor;
876 double b = mSemiMinor;
877 double f = 1 / mInvFlattening;
882 double L = p2_lon - p1_lon;
883 double U1 = std::atan( ( 1 - f ) * std::tan( p1_lat ) );
884 double U2 = std::atan( ( 1 - f ) * std::tan( p2_lat ) );
885 double sinU1 = std::sin( U1 ), cosU1 = std::cos( U1 );
886 double sinU2 = std::sin( U2 ), cosU2 = std::cos( U2 );
888 double lambdaP = 2 * M_PI;
890 double sinLambda = 0;
891 double cosLambda = 0;
896 double cosSqAlpha = 0;
897 double cos2SigmaM = 0;
903 while ( std::fabs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 )
905 sinLambda = std::sin( lambda );
906 cosLambda = std::cos( lambda );
907 tu1 = ( cosU2 * sinLambda );
908 tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda );
909 sinSigma = std::sqrt( tu1 * tu1 + tu2 * tu2 );
910 cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
911 sigma = std::atan2( sinSigma, cosSigma );
912 alpha = std::asin( cosU1 * cosU2 * sinLambda / sinSigma );
913 cosSqAlpha = std::cos( alpha ) * std::cos( alpha );
914 cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
915 C = f / 16 * cosSqAlpha * ( 4 + f * ( 4 - 3 * cosSqAlpha ) );
917 lambda = L + ( 1 - C ) * f * std::sin( alpha ) *
918 ( sigma + C * sinSigma * ( cos2SigmaM + C * cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) ) );
921 if ( iterLimit == 0 )
924 double uSq = cosSqAlpha * ( a * a - b * b ) / ( b * b );
925 double A = 1 + uSq / 16384 * ( 4096 + uSq * ( -768 + uSq * ( 320 - 175 * uSq ) ) );
926 double B = uSq / 1024 * ( 256 + uSq * ( -128 + uSq * ( 74 - 47 * uSq ) ) );
927 double deltaSigma = B * sinSigma * ( cos2SigmaM + B / 4 * ( cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) -
928 B / 6 * cos2SigmaM * ( -3 + 4 * sinSigma * sinSigma ) * ( -3 + 4 * cos2SigmaM * cos2SigmaM ) ) );
929 double s = b * A * ( sigma - deltaSigma );
933 *course1 = std::atan2( tu1, tu2 );
938 *course2 = std::atan2( cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda ) + M_PI;
949 double QgsDistanceArea::getQ(
double x )
const
953 sinx = std::sin( x );
956 return sinx * ( 1 + sinx2 * ( m_QA + sinx2 * ( m_QB + sinx2 * m_QC ) ) );
960 double QgsDistanceArea::getQbar(
double x )
const
964 cosx = std::cos( x );
967 return cosx * ( m_QbarA + cosx2 * ( m_QbarB + cosx2 * ( m_QbarC + cosx2 * m_QbarD ) ) );
971 void QgsDistanceArea::computeAreaInit()
979 double a2 = ( mSemiMajor * mSemiMajor );
980 double e2 = 1 - ( ( mSemiMinor * mSemiMinor ) / a2 );
983 m_TwoPI = M_PI + M_PI;
988 m_AE = a2 * ( 1 - e2 );
990 m_QA = ( 2.0 / 3.0 ) * e2;
991 m_QB = ( 3.0 / 5.0 ) * e4;
992 m_QC = ( 4.0 / 7.0 ) * e6;
994 m_QbarA = -1.0 - ( 2.0 / 3.0 ) * e2 - ( 3.0 / 5.0 ) * e4 - ( 4.0 / 7.0 ) * e6;
995 m_QbarB = ( 2.0 / 9.0 ) * e2 + ( 2.0 / 5.0 ) * e4 + ( 4.0 / 7.0 ) * e6;
996 m_QbarC = - ( 3.0 / 25.0 ) * e4 - ( 12.0 / 35.0 ) * e6;
997 m_QbarD = ( 4.0 / 49.0 ) * e6;
999 m_Qp = getQ( M_PI_2 );
1000 m_E = 4 * M_PI * m_Qp * m_AE;
1022 double QgsDistanceArea::computePolygonArea(
const QVector<QgsPointXY> &points )
const
1024 if ( points.isEmpty() )
1033 double x1, y1, x2, y2, dx, dy;
1034 double Qbar1, Qbar2;
1040 const double thresh = 1e-6;
1045 return computePolygonFlatArea( points );
1047 int n = points.size();
1048 x2 =
DEG2RAD( points[n - 1].x() );
1049 y2 =
DEG2RAD( points[n - 1].y() );
1050 Qbar2 = getQbar( y2 );
1054 for (
int i = 0; i < n; i++ )
1060 x2 =
DEG2RAD( points[i].x() );
1061 y2 =
DEG2RAD( points[i].y() );
1062 Qbar2 = getQbar( y2 );
1065 while ( x1 - x2 > M_PI )
1068 while ( x2 - x1 > M_PI )
1073 if ( std::fabs( dy ) > thresh )
1076 area += dx * ( m_Qp - ( Qbar2 - Qbar1 ) / dy );
1088 area += dx * ( m_Qp - getQ( ( y1 + y2 ) / 2.0 ) );
1095 if ( ( area *= m_AE ) < 0.0 )
1105 if ( area > m_E / 2 )
1111 double QgsDistanceArea::computePolygonFlatArea(
const QVector<QgsPointXY> &points )
const
1117 size = points.size();
1120 for ( i = 0; i < size; i++ )
1125 area = area + points[i].x() * points[( i + 1 ) % size].y() - points[( i + 1 ) % size].x() * points[i].y();
1129 return std::fabs( area );
1148 double result = length * factorUnits;
1149 QgsDebugMsgLevel( QStringLiteral(
"Converted length of %1 %2 to %3 %4" ).arg( length )
1162 double result = area * factorUnits;
1163 QgsDebugMsgLevel( QStringLiteral(
"Converted area of %1 %2 to %3 %4" ).arg( area )