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 );
127 double 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 )
616 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
618 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
620 QgsPoint newPoint( antiMeridianPoint );
626 if ( std::isfinite( newPoint.
x() ) && std::isfinite( newPoint.
y() ) )
628 newPoints << newPoint;
633 newPoints.reserve( line->
numPoints() - i + 1 );
636 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
638 antiMeridianPoint = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
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 )
735 p = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
737 p = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
739 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
753 p = mCoordTransform.
transform(
QgsPointXY( -180, lat180 ), Qgis::TransformDirection::Reverse );
755 p = mCoordTransform.
transform(
QgsPointXY( 180, lat180 ), Qgis::TransformDirection::Reverse );
757 if ( std::isfinite( p.
x() ) && std::isfinite( p.
y() ) )
772 currentPart << mCoordTransform.
transform(
QgsPointXY( lon, lat ), Qgis::TransformDirection::Reverse );
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 );
880 void QgsDistanceArea::computeAreaInit()
const
889 mGeod.reset(
new geod_geodesic() );
890 geod_init( mGeod.get(), mSemiMajor, 1 / mInvFlattening );
909 double 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 );
947 double 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 )
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).
QString toProj() const
Returns a Proj string representation of this CRS.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
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 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.
virtual void points(QgsPointSequence &pt) const =0
Returns a list of points within 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.
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.
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 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.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
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.
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.
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 GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static bool isCurvedType(Type 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)