42using namespace Qt::StringLiterals;
44#define DEG2RAD( x ) ( ( x ) * M_PI / 180 )
45#define RAD2DEG( r ) ( 180.0 * ( r ) / M_PI )
46#define POW2( x ) ( ( x ) * ( x ) )
53 mInvFlattening = -1.0;
63 : mCachedSourceToEllipsoid( other.mCachedSourceToEllipsoid )
64 , mEllipsoid( other.mEllipsoid )
65 , mSemiMajor( other.mSemiMajor )
66 , mSemiMinor( other.mSemiMinor )
67 , mInvFlattening( other.mInvFlattening )
68 , mDestinationCrs( other.mDestinationCrs )
69 , mSourceCrs( other.mSourceCrs )
70 , mCoordTransformContext( other.mCoordTransformContext )
71 , mCoordTransformDirty( other.mCoordTransformDirty )
79 : mCachedSourceToEllipsoid( std::move( other.mCachedSourceToEllipsoid ) )
80 , mEllipsoid( std::move( other.mEllipsoid ) )
81 , mSemiMajor( other.mSemiMajor )
82 , mSemiMinor( other.mSemiMinor )
83 , mInvFlattening( other.mInvFlattening )
84 , mGeod( std::move( other.mGeod ) )
85 , mDestinationCrs( std::move( other.mDestinationCrs ) )
86 , mSourceCrs( std::move( other.mSourceCrs ) )
87 , mCoordTransformContext( std::move( other.mCoordTransformContext ) )
88 , mCoordTransformDirty( other.mCoordTransformDirty )
97 mCachedSourceToEllipsoid = other.mCachedSourceToEllipsoid;
98 mEllipsoid = other.mEllipsoid;
99 mSemiMajor = other.mSemiMajor;
100 mSemiMinor = other.mSemiMinor;
101 mInvFlattening = other.mInvFlattening;
102 mDestinationCrs = other.mDestinationCrs;
103 mSourceCrs = other.mSourceCrs;
104 mCoordTransformContext = other.mCoordTransformContext;
105 mCoordTransformDirty = other.mCoordTransformDirty;
113 if ( &other ==
this )
116 mCachedSourceToEllipsoid = other.mCachedSourceToEllipsoid;
117 mEllipsoid = other.mEllipsoid;
118 mSemiMajor = other.mSemiMajor;
119 mSemiMinor = other.mSemiMinor;
120 mInvFlattening = other.mInvFlattening;
121 mSourceCrs = other.mSourceCrs;
122 mDestinationCrs = other.mDestinationCrs;
123 mCoordTransformContext = other.mCoordTransformContext;
124 mCoordTransformDirty = other.mCoordTransformDirty;
125 mGeod = std::move( other.mGeod );
137 mCoordTransformContext = context;
138 mCoordTransformDirty =
true;
148 mCoordTransformDirty =
true;
162 setFromParams( params );
175 setFromParams( params );
180double QgsDistanceArea::measure(
const QgsAbstractGeometry *geomV2, MeasureType type )
const
187 const int geomDimension = geomV2->
dimension();
188 if ( geomDimension <= 0 )
193 MeasureType measureType = type;
194 if ( measureType == Default )
196 measureType = ( geomDimension == 1 ? Length : Area );
202 if ( measureType == Length )
208 return geomV2->
area();
220 sum += measure( collection->
geometryN( i ), measureType );
225 if ( measureType == Length )
271 return measure( geomV2, Area );
280 return measure( geomV2, Length );
289 if ( !geomV2 || geomV2->
dimension() < 2 )
300 QVector< const QgsSurface * > surfaces;
304 surfaces.append( surf );
309 surfaces.reserve( ( surf ? 1 : 0 ) + multiSurf->
numGeometries() );
317 QVector<const QgsSurface *>::const_iterator surfaceIt = surfaces.constBegin();
318 for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
332 length += measure( outerRing );
335 for (
int i = 0; i < nInnerRings; ++i )
353 QVector<QgsPointXY> linePoints;
354 curve->
points( linePointsV2 );
361 if ( points.size() < 2 )
371 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::measureLine()",
"Error creating geod_geodesic object" );
379 p1 = sourceToEllipsoidTransform.
transform( points[0] );
383 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
387 p2 = sourceToEllipsoidTransform.
transform( *i );
392 geod_inverse( mGeod.get(), p1.
y(), p1.
x(), p2.
y(), p2.
x(), &distance, &azimuth1, &azimuth2 );
415 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::measureLine()",
"Error creating geod_geodesic object" );
426 QgsDebugMsgLevel( u
"Ellipsoidal calculations is enabled, using ellipsoid %1"_s.arg( mEllipsoid ), 4 );
429 pp1 = sourceToEllipsoidTransform.
transform( p1 );
430 pp2 = sourceToEllipsoidTransform.
transform( p2 );
435 geod_inverse( mGeod.get(), pp1.
y(), pp1.
x(), pp2.y(), pp2.x(), &result, &azimuth1, &azimuth2 );
464 p2 = p1.
project( distance, azimuth );
469 p2 = p1.
project( distance, azimuth );
473 u
"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]"_s
475 QString::number( distance,
'f', 7 ),
477 QString::number( result,
'f', 7 ),
478 sourceToEllipsoid().
sourceCrs().isGeographic() ? u
"Geographic"_s : u
"Cartesian"_s,
484 .arg( u
"SemiMajor[%1] SemiMinor[%2] InvFlattening[%3] "_s.arg( QString::number( mSemiMajor,
'f', 7 ), QString::number( mSemiMinor,
'f', 7 ), QString::number( mInvFlattening,
'f', 7 ) ) ),
487 if ( projectedPoint )
504 geod_direct( mGeod.get(), p1.
y(), p1.
x(),
RAD2DEG( azimuth ), distance, &lat2, &lon2, &azimuth2 );
514 p1.
setX( p1.
x() + 360 );
516 p2.
setX( p2.
x() + 360 );
519 double p1x = p1.
x() < 180 ? p1.
x() : p2.
x();
520 double p1y = p1.
x() < 180 ? p1.
y() : p2.
y();
521 double p2x = p1.
x() < 180 ? p2.
x() : p1.
x();
522 double p2y = p1.
x() < 180 ? p2.
y() : p1.
y();
530 fractionAlongLine = ( 180 - p1x ) / ( p2x - p1x );
532 fractionAlongLine = 1 - fractionAlongLine;
533 return p1y + ( 180 - p1x ) / ( p2x - p1x ) * ( p2y - p1y );
538 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::latitudeGeodesicCrossesAntimeridian()",
"Error creating geod_geodesic object" );
542 geod_geodesicline line;
543 geod_inverseline( &line, mGeod.get(), p1y, p1x, p2y, p2x, GEOD_ALL );
545 const double totalDist = line.s13;
546 double intersectionDist = line.s13;
551 while ( std::fabs( lon - 180.0 ) > 0.00000001 && iterations < 100 )
553 if ( iterations > 0 && std::fabs( p2x - p1x ) > 5 )
566 QgsDebugMsgLevel( u
"Narrowed window to %1, %2 - %3, %4"_s.arg( p1x ).arg( p1y ).arg( p2x ).arg( p2y ), 4 );
568 geod_inverseline( &line, mGeod.get(), p1y, p1x, p2y, p2x, GEOD_ALL );
569 intersectionDist = line.s13 * 0.5;
576 intersectionDist *= ( 180.0 - p1x ) / ( lon - p1x );
581 geod_position( &line, intersectionDist, &lat, &lon, &t );
587 QgsDebugMsgLevel( u
"After %1 iterations lon is %2, lat is %3, dist from p1: %4"_s.arg( iterations ).arg( lon ).arg( lat ).arg( intersectionDist ), 4 );
590 fractionAlongLine = intersectionDist / totalDist;
592 fractionAlongLine = 1 - fractionAlongLine;
610 auto res = std::make_unique< QgsMultiLineString >();
621 const std::unique_ptr< QgsLineString > l = std::make_unique< QgsLineString >();
628 QVector< QgsPoint > newPoints;
636 for (
int i = 0; i < line->
numPoints(); i++ )
642 x = std::fmod( x, 360.0 );
653 if ( i > 0 && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
662 z = prevZ + ( p.
z() - prevZ ) * fract;
666 m = prevM + ( p.
m() - prevM ) * fract;
670 if ( prevLon < -120 )
675 QgsPoint newPoint( antiMeridianPoint );
681 if ( std::isfinite( newPoint.
x() ) && std::isfinite( newPoint.
y() ) )
683 newPoints << newPoint;
688 newPoints.reserve( line->
numPoints() - i + 1 );
695 if ( std::isfinite( antiMeridianPoint.
x() ) && std::isfinite( antiMeridianPoint.
y() ) )
699 newPoint.
setX( antiMeridianPoint.
x() );
700 newPoint.
setY( antiMeridianPoint.
y() );
701 newPoints << newPoint;
717 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform linestring. Unable to calculate break point." ) );
718 res->addGeometry( line->
clone() );
733 return QVector< QVector< QgsPointXY > >() << ( QVector< QgsPointXY >() << p1 << p2 );
739 return QVector< QVector< QgsPointXY > >();
744 pp1 = sourceToEllipsoidTransform.
transform( p1 );
745 pp2 = sourceToEllipsoidTransform.
transform( p2 );
749 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate geodesic line." ) );
750 return QVector< QVector< QgsPointXY > >();
753 geod_geodesicline line;
754 geod_inverseline( &line, mGeod.get(), pp1.y(), pp1.x(), pp2.y(), pp2.x(), GEOD_ALL );
755 const double totalDist = line.s13;
757 QVector< QVector< QgsPointXY > > res;
758 QVector< QgsPointXY > currentPart;
761 double prevLon = pp1.x();
762 double prevLat = pp1.y();
763 bool lastRun =
false;
777 geod_position( &line, d, &lat, &lon, &t );
780 if ( breakLine && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
791 if ( prevLon < -120 )
796 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
814 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
839 if ( d >= totalDist )
864 curve->
points( linePointsV2 );
865 QVector<QgsPointXY> linePoints;
875 QVector<QgsPointXY> pts;
876 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
878 pts.append( sourceToEllipsoid().transform( *i ) );
880 return computePolygonArea( pts );
884 return computePolygonArea( points );
897 pp1 = sourceToEllipsoidTransform.
transform( p1 );
898 pp2 = sourceToEllipsoidTransform.
transform( p2 );
902 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::bearing()",
"Error creating geod_geodesic object" );
909 geod_inverse( mGeod.get(), pp1.
y(), pp1.
x(), pp2.y(), pp2.x(), &distance, &azimuth1, &azimuth2 );
915 const double dx = p2.
x() - p1.
x();
916 const double dy = p2.
y() - p1.
y();
921 bearing = std::atan2( dx, dy );
927void QgsDistanceArea::computeAreaInit()
const
936 mGeod = std::make_unique<geod_geodesic>();
937 geod_init( mGeod.get(), mSemiMajor, 1 / mInvFlattening );
942 mCoordTransformDirty =
true;
947 mDestinationCrs = params.
crs;
952double QgsDistanceArea::computePolygonArea(
const QVector<QgsPointXY> &points )
const
954 if ( points.isEmpty() )
962 return computePolygonFlatArea( points );
967 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::computePolygonArea()",
"Error creating geod_geodesic object" );
971 struct geod_polygon p;
972 geod_polygon_init( &p, 0 );
974 const bool isClosed = points.constFirst() == points.constLast();
979 int i = points.size();
980 while ( ( isClosed && --i ) || ( !isClosed && --i >= 0 ) )
981 geod_polygon_addpoint( mGeod.get(), &p, points.at( i ).y(), points.at( i ).x() );
984 double perimeter = 0;
985 geod_polygon_compute( mGeod.get(), &p, 0, 1, &area, &perimeter );
987 return std::fabs( area );
990double QgsDistanceArea::computePolygonFlatArea(
const QVector<QgsPointXY> &points )
const
996 size = points.size();
999 for ( i = 0; i < size; i++ )
1004 area = area + points[i].x() * points[( i + 1 ) % size].y() - points[( i + 1 ) % size].x() * points[i].y();
1008 return std::fabs( area );
1027 const double result = length * factorUnits;
1038 const double result = area * factorUnits;
1045 if ( mCoordTransformDirty )
1047 mCachedSourceToEllipsoid =
QgsCoordinateTransform( mSourceCrs, mDestinationCrs, mCoordTransformContext );
1048 mCoordTransformDirty =
false;
1050 return mCachedSourceToEllipsoid;
DistanceUnit
Units of distance.
@ SquareMeters
Square meters.
static QString geoNone()
Constant that holds the string representation for "No ellipse/No CRS".
@ Reverse
Reverse/inverse transform (from destination to source).
Abstract base class for all geometries.
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.
virtual double perimeter() const
Returns the planar, 2-dimensional perimeter of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
virtual double area() const
Returns the planar, 2-dimensional area of the geometry.
Represents a coordinate reference system (CRS).
QString toProj() const
Returns a Proj string representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
virtual QgsPolygon * surfaceToPolygon() const
Gets a polygon representation of this surface.
Abstract base class for curved geometry type.
virtual void points(QgsPointSequence &pt) const =0
Returns a list of points within the curve.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
QgsDistanceArea & operator=(const QgsDistanceArea &other)
double latitudeGeodesicCrossesAntimeridian(const QgsPointXY &p1, const QgsPointXY &p2, double &fractionAlongLine) const
Calculates the latitude at which the geodesic line joining p1 and p2 crosses the antimeridian (longit...
static QString formatDistance(double distance, int decimals, Qgis::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source spatial reference system.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
QVector< QVector< QgsPointXY > > geodesicLine(const QgsPointXY &p1, const QgsPointXY &p2, double interval, bool breakLine=false) const
Calculates the geodesic line between p1 and p2, which represents the shortest path on the ellipsoid b...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
QString ellipsoid() const
Returns ellipsoid's acronym.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
QgsGeometry splitGeometryAtAntimeridian(const QgsGeometry &geometry) const
Splits a (Multi)LineString geometry at the antimeridian (longitude +/- 180 degrees).
Qgis::DistanceUnit lengthUnits() const
Returns the units of distance for length calculations made by this object.
double measurePolygon(const QVector< QgsPointXY > &points) const
Measures the area of the polygon described by a set of points.
double measureLineProjected(const QgsPointXY &p1, double distance=1, double azimuth=M_PI_2, QgsPointXY *projectedPoint=nullptr) const
Calculates the distance from one point with distance in meters and azimuth (direction) When the sourc...
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QgsPointXY computeSpheroidProject(const QgsPointXY &p1, double distance=1, double azimuth=M_PI_2) const
Given a location, an azimuth and a distance, computes the location of the projected point.
bool willUseEllipsoid() const
Returns whether calculations will use the ellipsoid.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
static QString formatArea(double area, int decimals, Qgis::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
Qgis::AreaUnit areaUnits() const
Returns the units of area for areal calculations made by this object.
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
A geometry is the spatial representation of a feature.
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
Line string geometry type, with support for z-dimension and m-values.
bool isEmpty() const override
Returns true if the geometry is empty.
int numPoints() const override
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Multi surface geometry collection.
QgsPointXY project(double distance, double bearing) const
Returns a new point which corresponds to this point projected by a specified distance in a specified ...
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
QString asWkt() const
Returns the well known text representation for the point (e.g.
void setX(double x)
Sets the x value of the point.
Point geometry type, with support for z-dimension and m-values.
void setY(double y)
Sets the point's y-coordinate.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
void setX(double x)
Sets the point's x-coordinate.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE QString formatArea(double area, int decimals, Qgis::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE QString formatDistance(double distance, int decimals, Qgis::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
static Q_INVOKABLE Qgis::AreaUnit distanceToAreaUnit(Qgis::DistanceUnit distanceUnit)
Converts a distance unit to its corresponding area unit, e.g., meters to square meters.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Contains parameters for an ellipsoid.
double semiMajor
Semi-major axis, in meters.
bool valid
Whether ellipsoid parameters are valid.
double semiMinor
Semi-minor axis, in meters.
QgsCoordinateReferenceSystem crs
Associated coordinate reference system.
double inverseFlattening
Inverse flattening.