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;
58 : mCoordTransform( other.mCoordTransform )
59 , mEllipsoid( other.mEllipsoid )
60 , mSemiMajor( other.mSemiMajor )
61 , mSemiMinor( other.mSemiMinor )
62 , mInvFlattening( other.mInvFlattening )
69 mCoordTransform = other.mCoordTransform;
70 mEllipsoid = other.mEllipsoid;
71 mSemiMajor = other.mSemiMajor;
72 mSemiMinor = other.mSemiMinor;
73 mInvFlattening = other.mInvFlattening;
108 setFromParams( params );
118 mSemiMajor = semiMajor;
119 mSemiMinor = semiMinor;
120 mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
127double QgsDistanceArea::measure(
const QgsAbstractGeometry *geomV2, MeasureType type )
const
134 const int geomDimension = geomV2->
dimension();
135 if ( geomDimension <= 0 )
140 MeasureType measureType = type;
141 if ( measureType == Default )
143 measureType = ( geomDimension == 1 ? Length : Area );
149 if ( measureType == Length )
155 return geomV2->
area();
167 sum += measure( collection->
geometryN( i ), measureType );
172 if ( measureType == Length )
174 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomV2 );
187 const QgsSurface *surface = qgsgeometry_cast<const QgsSurface *>( geomV2 );
214 return measure( geomV2, Area );
223 return measure( geomV2, Length );
232 if ( !geomV2 || geomV2->
dimension() < 2 )
243 QVector< const QgsSurface * > surfaces;
244 const QgsSurface *surf = qgsgeometry_cast<const QgsSurface *>( geomV2 );
247 surfaces.append( surf );
249 const QgsMultiSurface *multiSurf = qgsgeometry_cast<const QgsMultiSurface *>( geomV2 );
252 surfaces.reserve( ( surf ? 1 : 0 ) + multiSurf->
numGeometries() );
260 QVector<const QgsSurface *>::const_iterator surfaceIt = surfaces.constBegin();
261 for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
272 length += measure( outerRing );
275 for (
int i = 0; i < nInnerRings; ++i )
292 QVector<QgsPointXY> linePoints;
293 curve->
points( linePointsV2 );
300 if ( points.size() < 2 )
310 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::measureLine()",
"Error creating geod_geodesic object" );
318 p1 = mCoordTransform.
transform( points[0] );
322 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
331 geod_inverse( mGeod.get(), p1.
y(), p1.
x(), p2.
y(), p2.
x(), &distance, &azimuth1, &azimuth2 );
348 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) );
362 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::measureLine()",
"Error creating geod_geodesic object" );
374 QgsDebugMsgLevel( QStringLiteral(
"Ellipsoidal calculations is enabled, using ellipsoid %1" ).arg( mEllipsoid ), 4 );
379 QgsDebugMsgLevel( QStringLiteral(
"New points are %1 and %2, calculating..." ).arg( pp1.
toString( 4 ), pp2.toString( 4 ) ), 4 );
383 geod_inverse( mGeod.get(), pp1.
y(), pp1.
x(), pp2.y(), pp2.x(), &result, &azimuth1, &azimuth2 );
387 QgsDebugMsgLevel( QStringLiteral(
"Cartesian calculation on canvas coordinates" ), 4 );
394 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) );
418 p2 = p1.
project( distance, azimuth );
420 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]" )
421 .arg( QString::number( distance,
'f', 7 ),
423 QString::number( result,
'f', 7 ),
424 mCoordTransform.
sourceCrs().
isGeographic() ? QStringLiteral(
"Geographic" ) : QStringLiteral(
"Cartesian" ),
432 .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 );
433 if ( projectedPoint )
441 const QgsPointXY &p1,
double distance,
double azimuth )
const
451 geod_direct( mGeod.get(), p1.
y(), p1.
x(),
RAD2DEG( azimuth ), distance, &lat2, &lon2, &azimuth2 );
461 p1.
setX( p1.
x() + 360 );
463 p2.
setX( p2.
x() + 360 );
466 double p1x = p1.
x() < 180 ? p1.
x() : p2.
x();
467 double p1y = p1.
x() < 180 ? p1.
y() : p2.
y();
468 double p2x = p1.
x() < 180 ? p2.
x() : p1.
x();
469 double p2y = p1.
x() < 180 ? p2.
y() : p1.
y();
477 fractionAlongLine = ( 180 - p1x ) / ( p2x - p1x );
479 fractionAlongLine = 1 - fractionAlongLine;
480 return p1y + ( 180 - p1x ) / ( p2x - p1x ) * ( p2y - p1y );
485 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::latitudeGeodesicCrossesAntimeridian()",
"Error creating geod_geodesic object" );
489 geod_geodesicline line;
490 geod_inverseline( &line, mGeod.get(), p1y, p1x, p2y, p2x, GEOD_ALL );
492 const double totalDist = line.s13;
493 double intersectionDist = line.s13;
498 while ( std::fabs( lon - 180.0 ) > 0.00000001 && iterations < 100 )
500 if ( iterations > 0 && std::fabs( p2x - p1x ) > 5 )
513 QgsDebugMsgLevel( QStringLiteral(
"Narrowed window to %1, %2 - %3, %4" ).arg( p1x ).arg( p1y ).arg( p2x ).arg( p2y ), 4 );
515 geod_inverseline( &line, mGeod.get(), p1y, p1x, p2y, p2x, GEOD_ALL );
516 intersectionDist = line.s13 * 0.5;
523 intersectionDist *= ( 180.0 - p1x ) / ( lon - p1x );
528 geod_position( &line, intersectionDist, &lat, &lon, &t );
534 QgsDebugMsgLevel( QStringLiteral(
"After %1 iterations lon is %2, lat is %3, dist from p1: %4" ).arg( iterations ).arg( lon ).arg( lat ).arg( intersectionDist ), 4 );
537 fractionAlongLine = intersectionDist / totalDist;
539 fractionAlongLine = 1 - fractionAlongLine;
555 std::unique_ptr< QgsMultiLineString > res = std::make_unique< QgsMultiLineString >();
558 const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *part );
566 const std::unique_ptr< QgsLineString > l = std::make_unique< QgsLineString >();
573 QVector< QgsPoint > newPoints;
581 for (
int i = 0; i < line->
numPoints(); i++ )
587 x = std::fmod( x, 360.0 );
598 if ( i > 0 && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
607 z = prevZ + ( p.
z() - prevZ ) * fract;
611 m = prevM + ( p.
m() - prevM ) * fract;
615 if ( prevLon < -120 )
620 QgsPoint newPoint( antiMeridianPoint );
626 if ( std::isfinite( newPoint.
x() ) && std::isfinite( newPoint.
y() ) )
628 newPoints << newPoint;
633 newPoints.reserve( line->
numPoints() - i + 1 );
640 if ( std::isfinite( antiMeridianPoint.
x() ) && std::isfinite( antiMeridianPoint.
y() ) )
644 newPoint.
setX( antiMeridianPoint.
x() );
645 newPoint.
setY( antiMeridianPoint.
y() );
646 newPoints << newPoint;
662 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform linestring. Unable to calculate break point." ) );
663 res->addGeometry( line->
clone() );
676 return QVector< QVector< QgsPointXY > >() << ( QVector< QgsPointXY >() << p1 << p2 );
682 return QVector< QVector< QgsPointXY > >();
692 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate geodesic line." ) );
693 return QVector< QVector< QgsPointXY > >();
696 geod_geodesicline line;
697 geod_inverseline( &line, mGeod.get(), pp1.y(), pp1.x(), pp2.y(), pp2.x(), GEOD_ALL );
698 const double totalDist = line.s13;
700 QVector< QVector< QgsPointXY > > res;
701 QVector< QgsPointXY > currentPart;
704 double prevLon = pp1.
x();
705 double prevLat = pp1.y();
706 bool lastRun =
false;
720 geod_position( &line, d, &lat, &lon, &t );
723 if ( breakLine && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
734 if ( prevLon < -120 )
739 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
757 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
783 if ( d >= totalDist )
809 curve->
points( linePointsV2 );
810 QVector<QgsPointXY> linePoints;
822 QVector<QgsPointXY> pts;
823 for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
825 pts.append( mCoordTransform.
transform( *i ) );
827 return computePolygonArea( pts );
831 return computePolygonArea( points );
837 QgsMessageLog::logMessage( QObject::tr(
"Caught a coordinate system exception while trying to transform a point. Unable to calculate polygon area." ) );
855 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::bearing()",
"Error creating geod_geodesic object" );
862 geod_inverse( mGeod.get(), pp1.
y(), pp1.
x(), pp2.y(), pp2.x(), &distance, &azimuth1, &azimuth2 );
868 const double dx = p2.
x() - p1.
x();
869 const double dy = p2.
y() - p1.
y();
874 bearing = std::atan2( dx, dy );
880void QgsDistanceArea::computeAreaInit()
const
889 mGeod.reset(
new geod_geodesic() );
890 geod_init( mGeod.get(), mSemiMajor, 1 / mInvFlattening );
909double QgsDistanceArea::computePolygonArea(
const QVector<QgsPointXY> &points )
const
911 if ( points.isEmpty() )
919 return computePolygonFlatArea( points );
924 Q_ASSERT_X(
static_cast<bool>( mGeod ),
"QgsDistanceArea::computePolygonArea()",
"Error creating geod_geodesic object" );
928 struct geod_polygon p;
929 geod_polygon_init( &p, 0 );
931 const bool isClosed = points.constFirst() == points.constLast();
936 int i = points.size();
937 while ( ( isClosed && --i ) || ( !isClosed && --i >= 0 ) )
938 geod_polygon_addpoint( mGeod.get(), &p, points.at( i ).y(), points.at( i ).x() );
941 double perimeter = 0;
942 geod_polygon_compute( mGeod.get(), &p, 0, 1, &area, &perimeter );
944 return std::fabs( area );
947double QgsDistanceArea::computePolygonFlatArea(
const QVector<QgsPointXY> &points )
const
953 size = points.size();
956 for ( i = 0; i < size; i++ )
961 area = area + points[i].x() * points[( i + 1 ) % size].y() - points[( i + 1 ) % size].x() * points[i].y();
965 return std::fabs( area );
984 const double result = length * factorUnits;
985 QgsDebugMsgLevel( QStringLiteral(
"Converted length of %1 %2 to %3 %4" ).arg( length )
998 const double result = area * factorUnits;
999 QgsDebugMsgLevel( QStringLiteral(
"Converted area of %1 %2 to %3 %4" ).arg( area )
@ 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.
This class represents a coordinate reference system (CRS).
QString toProj() const
Returns a Proj string representation of this CRS.
QgsUnitTypes::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
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.
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)
static QString formatDistance(double distance, int decimals, QgsUnitTypes::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
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...
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source spatial reference system.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
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.
static QString formatArea(double area, int decimals, QgsUnitTypes::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
QgsGeometry splitGeometryAtAntimeridian(const QgsGeometry &geometry) const
Splits a (Multi)LineString geometry at the antimeridian (longitude +/- 180 degrees).
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...
QgsUnitTypes::AreaUnit areaUnits() const
Returns the units of area for areal calculations made by this object.
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.
QgsUnitTypes::DistanceUnit lengthUnits() const
Returns the units of distance for length calculations made by this object.
bool willUseEllipsoid() const
Returns whether calculations will use the ellipsoid.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
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.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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.
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)
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
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.
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)
Sets the point's x-coordinate.
QgsPolygon * surfaceToPolygon() const override
Gets a polygon representation of this surface.
virtual QgsPolygon * surfaceToPolygon() const =0
Gets a polygon representation of this surface.
DistanceUnit
Units of distance.
static Q_INVOKABLE QgsUnitTypes::AreaUnit distanceToAreaUnit(QgsUnitTypes::DistanceUnit distanceUnit)
Converts a distance unit to its corresponding area unit, e.g., meters to square meters.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE QString formatArea(double area, int decimals, QgsUnitTypes::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
@ AreaSquareMeters
Square meters.
static Q_INVOKABLE QString formatDistance(double distance, int decimals, QgsUnitTypes::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
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)