39#define DEG2RAD(x) ((x)*M_PI/180)
40#define RAD2DEG(r) (180.0 * (r) / M_PI)
41#define POW2(x) ((x)*(x))
48 mInvFlattening = -1.0;
57 : mCoordTransform( other.mCoordTransform )
58 , mEllipsoid( other.mEllipsoid )
59 , mSemiMajor( other.mSemiMajor )
60 , mSemiMinor( other.mSemiMinor )
61 , mInvFlattening( other.mInvFlattening )
68 mCoordTransform = other.mCoordTransform;
69 mEllipsoid = other.mEllipsoid;
70 mSemiMajor = other.mSemiMajor;
71 mSemiMinor = other.mSemiMinor;
72 mInvFlattening = other.mInvFlattening;
107 setFromParams( params );
117 mSemiMajor = semiMajor;
118 mSemiMinor = semiMinor;
119 mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
126double QgsDistanceArea::measure(
const QgsAbstractGeometry *geomV2, MeasureType type )
const
133 const int geomDimension = geomV2->
dimension();
134 if ( geomDimension <= 0 )
139 MeasureType measureType = type;
140 if ( measureType == Default )
142 measureType = ( geomDimension == 1 ? Length : Area );
148 if ( measureType == Length )
154 return geomV2->
area();
166 sum += measure( collection->
geometryN( i ), measureType );
171 if ( measureType == Length )
173 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomV2 );
186 const QgsSurface *surface = qgsgeometry_cast<const QgsSurface *>( geomV2 );
213 return measure( geomV2, Area );
222 return measure( geomV2, Length );
231 if ( !geomV2 || geomV2->
dimension() < 2 )
242 QVector< const QgsSurface * > surfaces;
243 const QgsSurface *surf = qgsgeometry_cast<const QgsSurface *>( geomV2 );
246 surfaces.append( surf );
248 const QgsMultiSurface *multiSurf = qgsgeometry_cast<const QgsMultiSurface *>( geomV2 );
251 surfaces.reserve( ( surf ? 1 : 0 ) + multiSurf->
numGeometries() );
259 QVector<const QgsSurface *>::const_iterator surfaceIt = surfaces.constBegin();
260 for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
271 length += measure( outerRing );
274 for (
int i = 0; i < nInnerRings; ++i )
291 QVector<QgsPointXY> linePoints;
292 curve->
points( linePointsV2 );
299 if ( points.size() < 2 )
309 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::measureLine()",
"Error creating geod_geodesic object" );
317 p1 = mCoordTransform.
transform( points[0] );
321 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
330 geod_inverse( mGeod.get(), p1.
y(), p1.
x(), p2.
y(), p2.
x(), &distance, &azimuth1, &azimuth2 );
347 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) );
361 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::measureLine()",
"Error creating geod_geodesic object" );
373 QgsDebugMsgLevel( QStringLiteral(
"Ellipsoidal calculations is enabled, using ellipsoid %1" ).arg( mEllipsoid ), 4 );
378 QgsDebugMsgLevel( QStringLiteral(
"New points are %1 and %2, calculating..." ).arg( pp1.
toString( 4 ), pp2.toString( 4 ) ), 4 );
382 geod_inverse( mGeod.get(), pp1.
y(), pp1.
x(), pp2.y(), pp2.x(), &result, &azimuth1, &azimuth2 );
386 QgsDebugMsgLevel( QStringLiteral(
"Cartesian calculation on canvas coordinates" ), 4 );
393 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) );
412 if (
sourceCrs().mapUnits() != Qgis::DistanceUnit::Meters )
417 p2 = p1.
project( distance, azimuth );
419 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]" )
420 .arg( QString::number( distance,
'f', 7 ),
422 QString::number( result,
'f', 7 ),
423 mCoordTransform.
sourceCrs().
isGeographic() ? QStringLiteral(
"Geographic" ) : QStringLiteral(
"Cartesian" ),
431 .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 );
432 if ( projectedPoint )
440 const QgsPointXY &p1,
double distance,
double azimuth )
const
450 geod_direct( mGeod.get(), p1.
y(), p1.
x(),
RAD2DEG( azimuth ), distance, &lat2, &lon2, &azimuth2 );
460 p1.
setX( p1.
x() + 360 );
462 p2.
setX( p2.
x() + 360 );
465 double p1x = p1.
x() < 180 ? p1.
x() : p2.
x();
466 double p1y = p1.
x() < 180 ? p1.
y() : p2.
y();
467 double p2x = p1.
x() < 180 ? p2.
x() : p1.
x();
468 double p2y = p1.
x() < 180 ? p2.
y() : p1.
y();
476 fractionAlongLine = ( 180 - p1x ) / ( p2x - p1x );
478 fractionAlongLine = 1 - fractionAlongLine;
479 return p1y + ( 180 - p1x ) / ( p2x - p1x ) * ( p2y - p1y );
484 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::latitudeGeodesicCrossesAntimeridian()",
"Error creating geod_geodesic object" );
488 geod_geodesicline line;
489 geod_inverseline( &line, mGeod.get(), p1y, p1x, p2y, p2x, GEOD_ALL );
491 const double totalDist = line.s13;
492 double intersectionDist = line.s13;
497 while ( std::fabs( lon - 180.0 ) > 0.00000001 && iterations < 100 )
499 if ( iterations > 0 && std::fabs( p2x - p1x ) > 5 )
512 QgsDebugMsgLevel( QStringLiteral(
"Narrowed window to %1, %2 - %3, %4" ).arg( p1x ).arg( p1y ).arg( p2x ).arg( p2y ), 4 );
514 geod_inverseline( &line, mGeod.get(), p1y, p1x, p2y, p2x, GEOD_ALL );
515 intersectionDist = line.s13 * 0.5;
522 intersectionDist *= ( 180.0 - p1x ) / ( lon - p1x );
527 geod_position( &line, intersectionDist, &lat, &lon, &t );
533 QgsDebugMsgLevel( QStringLiteral(
"After %1 iterations lon is %2, lat is %3, dist from p1: %4" ).arg( iterations ).arg( lon ).arg( lat ).arg( intersectionDist ), 4 );
536 fractionAlongLine = intersectionDist / totalDist;
538 fractionAlongLine = 1 - fractionAlongLine;
554 std::unique_ptr< QgsMultiLineString > res = std::make_unique< QgsMultiLineString >();
557 const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *part );
565 const std::unique_ptr< QgsLineString > l = std::make_unique< QgsLineString >();
572 QVector< QgsPoint > newPoints;
580 for (
int i = 0; i < line->
numPoints(); i++ )
586 x = std::fmod( x, 360.0 );
597 if ( i > 0 && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
606 z = prevZ + ( p.
z() - prevZ ) * fract;
610 m = prevM + ( p.
m() - prevM ) * fract;
614 if ( prevLon < -120 )
615 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
617 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
619 QgsPoint newPoint( antiMeridianPoint );
625 if ( std::isfinite( newPoint.
x() ) && std::isfinite( newPoint.
y() ) )
627 newPoints << newPoint;
632 newPoints.reserve( line->
numPoints() - i + 1 );
635 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
637 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
639 if ( std::isfinite( antiMeridianPoint.
x() ) && std::isfinite( antiMeridianPoint.
y() ) )
643 newPoint.
setX( antiMeridianPoint.
x() );
644 newPoint.
setY( antiMeridianPoint.
y() );
645 newPoints << newPoint;
661 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform linestring. Unable to calculate break point." ) );
662 res->addGeometry( line->
clone() );
675 return QVector< QVector< QgsPointXY > >() << ( QVector< QgsPointXY >() << p1 << p2 );
681 return QVector< QVector< QgsPointXY > >();
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, mGeod.get(), 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 )
734 p = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
736 p = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
738 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
752 p = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
754 p = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
756 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
771 currentPart << mCoordTransform.
transform(
QgsPointXY( lon, lat ), Qgis::TransformDirection::Reverse );
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." ) );
854 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::bearing()",
"Error creating geod_geodesic object" );
861 geod_inverse( mGeod.get(), pp1.
y(), pp1.
x(), pp2.y(), pp2.x(), &distance, &azimuth1, &azimuth2 );
867 const double dx = p2.
x() - p1.
x();
868 const double dy = p2.
y() - p1.
y();
873 bearing = std::atan2( dx, dy );
879void QgsDistanceArea::computeAreaInit()
const
888 mGeod.reset(
new geod_geodesic() );
889 geod_init( mGeod.get(), mSemiMajor, 1 / mInvFlattening );
908double QgsDistanceArea::computePolygonArea(
const QVector<QgsPointXY> &points )
const
910 if ( points.isEmpty() )
918 return computePolygonFlatArea( points );
923 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::computePolygonArea()",
"Error creating geod_geodesic object" );
927 struct geod_polygon p;
928 geod_polygon_init( &p, 0 );
930 const bool isClosed = points.constFirst() == points.constLast();
935 int i = points.size();
936 while ( ( isClosed && --i ) || ( !isClosed && --i >= 0 ) )
937 geod_polygon_addpoint( mGeod.get(), &p, points.at( i ).y(), points.at( i ).x() );
940 double perimeter = 0;
941 geod_polygon_compute( mGeod.get(), &p, 0, 1, &area, &perimeter );
943 return std::fabs( area );
946double QgsDistanceArea::computePolygonFlatArea(
const QVector<QgsPointXY> &points )
const
952 size = points.size();
955 for ( i = 0; i < size; i++ )
960 area = area + points[i].x() * points[( i + 1 ) % size].y() - points[( i + 1 ) % size].x() * points[i].y();
964 return std::fabs( area );
983 const double result = length * factorUnits;
984 QgsDebugMsgLevel( QStringLiteral(
"Converted length of %1 %2 to %3 %4" ).arg( length )
997 const double result = area * factorUnits;
998 QgsDebugMsgLevel( QStringLiteral(
"Converted area of %1 %2 to %3 %4" ).arg( area )
DistanceUnit
Units of distance.
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
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.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
This class represents a coordinate reference system (CRS).
Q_GADGET Qgis::DistanceUnit mapUnits
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.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
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.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
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.
QString ellipsoid() const
Returns ellipsoid's acronym.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const SIP_THROW(QgsCsException)
Computes the bearing (in radians) between two points.
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.
QgsDistanceArea()
Constructor.
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 SIP_HOLDGIL
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.
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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.
Line string geometry type, with support for z-dimension and m-values.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
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)
Adds a message to the log instance (and creates it if necessary).
Multi surface geometry collection.
A class to represent a 2D point.
QgsPointXY project(double distance, double bearing) const SIP_HOLDGIL
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.
QString asWkt() const
Returns the well known text representation for the point (e.g.
void setX(double x) SIP_HOLDGIL
Sets the x value of the point.
double distance(double x, double y) const SIP_HOLDGIL
Returns the distance between this point and a specified x, y coordinate.
Point geometry type, with support for z-dimension and m-values.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void clear() override
Clears the geometry, ie reset it to a null geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
QgsPolygon * surfaceToPolygon() const override
Gets a polygon representation of this surface.
virtual QgsPolygon * surfaceToPolygon() const =0
Gets a polygon representation of this surface.
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) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static bool isCurvedType(Qgis::WkbType type) SIP_HOLDGIL
Returns true if the WKB type is a curved type or can contain curved geometries.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Contains parameters for an ellipsoid.
double semiMajor
Semi-major axis.
bool valid
Whether ellipsoid parameters are valid.
double semiMinor
Semi-minor axis.
QgsCoordinateReferenceSystem crs
Associated coordinate reference system.
double inverseFlattening
Inverse flattening.
bool useCustomParameters
Whether custom parameters alone should be used (semiMajor/semiMinor only)