35 #define M_PI 3.14159265358979323846 38 #define DEG2RAD(x) ((x)*M_PI/180) 44 mEllipsoidalMode =
false;
59 delete mCoordTransform;
65 if (
this == & origDA )
77 mEllipsoidalMode = origDA.mEllipsoidalMode;
78 mEllipsoid = origDA.mEllipsoid;
79 mSemiMajor = origDA.mSemiMajor;
80 mSemiMinor = origDA.mSemiMinor;
81 mInvFlattening = origDA.mInvFlattening;
90 mEllipsoidalMode = flag;
114 QString radius, parameter2;
120 sqlite3_stmt *myPreparedStatement;
134 if ( ellipsoid.startsWith(
"PARAMETER" ) )
136 QStringList paramList = ellipsoid.split(
":" );
137 bool semiMajorOk, semiMinorOk;
138 double semiMajor = paramList[1].toDouble( & semiMajorOk );
139 double semiMinor = paramList[2].toDouble( & semiMinorOk );
140 if ( semiMajorOk && semiMinorOk )
162 QString mySql =
"select radius, parameter2 from tbl_ellipsoid where acronym='" + ellipsoid +
"'";
163 myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
165 if ( myResult == SQLITE_OK )
167 if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
169 radius = QString((
char * )sqlite3_column_text( myPreparedStatement, 0 ) );
170 parameter2 = QString((
char * )sqlite3_column_text( myPreparedStatement, 1 ) );
174 sqlite3_finalize( myPreparedStatement );
175 sqlite3_close( myDatabase );
178 if ( radius.isEmpty() || parameter2.isEmpty() )
180 QgsDebugMsg( QString(
"setEllipsoid: no row in tbl_ellipsoid for acronym '%1'" ).arg( ellipsoid ) );
185 if ( radius.left( 2 ) ==
"a=" )
186 mSemiMajor = radius.mid( 2 ).toDouble();
189 QgsDebugMsg( QString(
"setEllipsoid: wrong format of radius field: '%1'" ).arg( radius ) );
196 if ( parameter2.left( 2 ) ==
"b=" )
198 mSemiMinor = parameter2.mid( 2 ).toDouble();
199 mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
201 else if ( parameter2.left( 3 ) ==
"rf=" )
203 mInvFlattening = parameter2.mid( 3 ).toDouble();
204 mSemiMinor = mSemiMajor - ( mSemiMajor / mInvFlattening );
208 QgsDebugMsg( QString(
"setEllipsoid: wrong format of parameter2 field: '%1'" ).arg( parameter2 ) );
212 QgsDebugMsg( QString(
"setEllipsoid: a=%1, b=%2, 1/f=%3" ).arg( mSemiMajor ).arg( mSemiMinor ).arg( mInvFlattening ) );
216 QString proj4 =
"+proj=longlat +ellps=" + ellipsoid +
" +no_defs";
223 if ( destCRS.
srsid() == 0 )
225 QString myName = QString(
" * %1 (%2)" )
226 .arg(
QObject::tr(
"Generated CRS",
"A CRS automatically generated from layer info get this prefix for description" ) )
248 mEllipsoid = QString(
"PARAMETER:%1:%2" ).arg( semiMajor ).arg( semiMinor );
249 mSemiMajor = semiMajor;
250 mSemiMinor = semiMinor;
251 mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor );
263 const unsigned char* wkb = geometry->
asWkb();
272 double res, resTotal = 0;
276 bool hasZptr =
false;
285 QgsDebugMsg(
"returning " + QString::number( res ) );
293 for ( i = 0; i < count; i++ )
298 QgsDebugMsg(
"returning " + QString::number( resTotal ) );
306 QgsDebugMsg(
"returning " + QString::number( res ) );
314 for ( i = 0; i < count; i++ )
324 QgsDebugMsg(
"returning " + QString::number( resTotal ) );
328 QgsDebugMsg( QString(
"measure: unexpected geometry type: %1" ).arg( wkbType ) );
338 const unsigned char* wkb = geometry->
asWkb();
346 double res = 0.0, resTotal = 0.0;
350 bool hasZptr =
false;
365 QgsDebugMsg(
"returning " + QString::number( res ) );
373 for ( i = 0; i < count; i++ )
383 QgsDebugMsg(
"returning " + QString::number( resTotal ) );
387 QgsDebugMsg( QString(
"measure: unexpected geometry type: %1" ).arg( wkbType ) );
399 QList<QgsPoint> points;
402 QgsDebugMsg(
"This feature WKB has " + QString::number( nPoints ) +
" points" );
404 for (
int i = 0; i < nPoints; ++i )
410 wkbPtr +=
sizeof( double );
422 if ( points.size() < 2 )
430 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
431 p1 = mCoordTransform->
transform( points[0] );
435 for ( QList<QgsPoint>::const_iterator i = points.begin(); i != points.end(); ++i )
437 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
478 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
481 QgsDebugMsgLevel( QString(
"Ellipsoidal calculations is enabled, using ellipsoid %1" ).arg( mEllipsoid ), 4 );
486 QgsDebugMsgLevel( QString(
"New points are %1 and %2, calculating..." ).arg( pp1.
toString( 4 ) ).arg( pp2.toString( 4 ) ), 4 );
527 QList<QgsPoint> points;
537 for (
int idx = 0; idx < numRings; idx++ )
544 for (
int jdx = 0; jdx < nPoints; jdx++ )
550 wkbPtr +=
sizeof( double );
555 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
559 points.append( pnt );
562 if ( points.size() > 2 )
605 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
608 for ( QList<QgsPoint>::const_iterator i = points.begin(); i != points.end(); ++i )
610 pts.append( mCoordTransform->
transform( *i ) );
633 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
641 double dx = p2.
x() - p1.
x();
642 double dy = p2.
y() - p1.
y();
643 bearing = atan2( dx, dy );
655 double* course1,
double* course2 )
657 if ( p1.
x() == p2.
x() && p1.
y() == p2.
y() )
661 double a = mSemiMajor;
662 double b = mSemiMinor;
663 double f = 1 / mInvFlattening;
668 double L = p2_lon - p1_lon;
669 double U1 = atan(( 1 - f ) * tan( p1_lat ) );
670 double U2 = atan(( 1 - f ) * tan( p2_lat ) );
671 double sinU1 = sin( U1 ), cosU1 = cos( U1 );
672 double sinU2 = sin( U2 ), cosU2 = cos( U2 );
674 double lambdaP = 2 *
M_PI;
676 double sinLambda = 0;
677 double cosLambda = 0;
682 double cosSqAlpha = 0;
683 double cos2SigmaM = 0;
689 while ( qAbs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 )
691 sinLambda = sin( lambda );
692 cosLambda = cos( lambda );
693 tu1 = ( cosU2 * sinLambda );
694 tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda );
695 sinSigma = sqrt( tu1 * tu1 + tu2 * tu2 );
696 cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
697 sigma = atan2( sinSigma, cosSigma );
698 alpha = asin( cosU1 * cosU2 * sinLambda / sinSigma );
699 cosSqAlpha = cos( alpha ) * cos( alpha );
700 cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
701 C = f / 16 * cosSqAlpha * ( 4 + f * ( 4 - 3 * cosSqAlpha ) );
703 lambda = L + ( 1 - C ) * f * sin( alpha ) *
704 ( sigma + C * sinSigma * ( cos2SigmaM + C * cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) ) );
707 if ( iterLimit == 0 )
710 double uSq = cosSqAlpha * ( a * a - b * b ) / ( b * b );
711 double A = 1 + uSq / 16384 * ( 4096 + uSq * ( -768 + uSq * ( 320 - 175 * uSq ) ) );
712 double B = uSq / 1024 * ( 256 + uSq * ( -128 + uSq * ( 74 - 47 * uSq ) ) );
713 double deltaSigma = B * sinSigma * ( cos2SigmaM + B / 4 * ( cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) -
714 B / 6 * cos2SigmaM * ( -3 + 4 * sinSigma * sinSigma ) * ( -3 + 4 * cos2SigmaM * cos2SigmaM ) ) );
715 double s = b * A * ( sigma - deltaSigma );
719 *course1 = atan2( tu1, tu2 );
724 *course2 = atan2( cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda ) +
M_PI;
732 return sqrt(( p2.
x() - p1.
x() ) * ( p2.
x() - p1.
x() ) + ( p2.
y() - p1.
y() ) * ( p2.
y() - p1.
y() ) );
737 if ( points.size() < 2 )
747 for ( QList<QgsPoint>::const_iterator i = points.begin(); i != points.end(); ++i )
750 if ( mEllipsoidalMode && ( mEllipsoid !=
GEO_NONE ) )
779 double QgsDistanceArea::getQ(
double x )
786 return sinx *( 1 + sinx2 *( m_QA + sinx2 *( m_QB + sinx2 * m_QC ) ) );
790 double QgsDistanceArea::getQbar(
double x )
797 return cosx *( m_QbarA + cosx2 *( m_QbarB + cosx2 *( m_QbarC + cosx2 * m_QbarD ) ) );
809 double a2 = ( mSemiMajor * mSemiMajor );
810 double e2 = 1 - ( a2 / ( mSemiMinor * mSemiMinor ) );
818 m_AE = a2 * ( 1 - e2 );
820 m_QA = ( 2.0 / 3.0 ) * e2;
821 m_QB = ( 3.0 / 5.0 ) * e4;
822 m_QC = ( 4.0 / 7.0 ) * e6;
824 m_QbarA = -1.0 - ( 2.0 / 3.0 ) * e2 - ( 3.0 / 5.0 ) * e4 - ( 4.0 / 7.0 ) * e6;
825 m_QbarB = ( 2.0 / 9.0 ) * e2 + ( 2.0 / 5.0 ) * e4 + ( 4.0 / 7.0 ) * e6;
826 m_QbarC = - ( 3.0 / 25.0 ) * e4 - ( 12.0 / 35.0 ) * e6;
827 m_QbarD = ( 4.0 / 49.0 ) * e6;
829 m_Qp = getQ( M_PI / 2 );
830 m_E = 4 * M_PI * m_Qp * m_AE;
838 double x1, y1, x2, y2, dx, dy;
843 if (( ! mEllipsoidalMode ) || ( mEllipsoid ==
GEO_NONE ) )
847 int n = points.size();
848 x2 =
DEG2RAD( points[n-1].x() );
849 y2 =
DEG2RAD( points[n-1].y() );
850 Qbar2 = getQbar( y2 );
854 for (
int i = 0; i < n; i++ )
862 Qbar2 = getQbar( y2 );
865 while ( x1 - x2 >
M_PI )
868 while ( x2 - x1 >
M_PI )
872 area += dx * ( m_Qp - getQ( y2 ) );
874 if (( dy = y2 - y1 ) != 0.0 )
875 area += dx * getQ( y2 ) - ( dx / dy ) * ( Qbar2 - Qbar1 );
877 if (( area *= m_AE ) < 0.0 )
887 if ( area > m_E / 2 )
899 size = points.size();
902 for ( i = 0; i < size; i++ )
907 area = area + points[i].x() * points[( i+1 ) % size].y() - points[( i+1 ) % size].x() * points[i].y();
925 unitLabel = QObject::trUtf8(
" m²" );
927 else if ( qAbs( value ) > 1000000.0 )
929 unitLabel = QObject::trUtf8(
" km²" );
930 value = value / 1000000.0;
932 else if ( qAbs( value ) > 10000.0 )
935 value = value / 10000.0;
939 unitLabel = QObject::trUtf8(
" m²" );
944 if ( keepBaseUnit || qAbs( value ) == 0.0 )
948 else if ( qAbs( value ) > 1000.0 )
951 value = value / 1000;
953 else if ( qAbs( value ) < 0.01 )
956 value = value * 1000;
958 else if ( qAbs( value ) < 0.1 )
972 if ( keepBaseUnit || qAbs( value ) <= 0.5*43560.0 )
977 else if ( qAbs( value ) <= 0.5*5280.0*5280.0 )
987 value /= 5280.0 * 5280.0;
992 if ( qAbs( value ) <= 528.0 || keepBaseUnit )
994 if ( qAbs( value ) == 1.0 )
1027 if ( qAbs( value ) == 1.0 )
1037 QgsDebugMsg( QString(
"Error: not picked up map units - actual value = %1" ).arg( u ) );
1040 return QLocale::system().toString( value,
'f', decimals ) + unitLabel;
1054 QgsDebugMsg(
"We're measuring on an ellipsoid or using projections, the system is returning meters" );
1056 else if ( mEllipsoidalMode && mEllipsoid ==
GEO_NONE )
1060 QgsDebugMsg(
"We're measuing on planimetric distance/area on given CRS, measured value is in CRS units" );
1066 factorUnits *= factorUnits;
1069 measure *= factorUnits;
1071 measureUnits = displayUnits;
double computePolygonFlatArea(const QList< QgsPoint > &points)
double computeDistance(const QList< QgsPoint > &points)
calculate distance with given coordinates (does not do a transform anymore)
~QgsDistanceArea()
Destructor.
bool saveAsUserCRS(QString name)
Copied from QgsCustomProjectionDialog /// Please refactor into SQL handler !!! ///.
void setSourceCrs(long srsid)
sets source spatial reference system (by QGIS CRS)
void computeAreaInit()
precalculates some values (must be called always when changing ellipsoid)
WkbType
Used for symbology operations.
bool setEllipsoid(const QString &ellipsoid)
sets ellipsoid by its acronym
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
bool createFromOgcWmsCrs(QString theCrs)
Set up this CRS from the given OGC CRS.
bool createFromSrsId(const long theSrsId)
double measurePerimeter(QgsGeometry *geometry)
measures perimeter of polygon
double measurePolygon(const QList< QgsPoint > &points)
measures polygon area
#define QgsDebugMsgLevel(str, level)
double measure(QgsGeometry *geometry)
general measurement (line distance or polygon area)
double computePolygonArea(const QList< QgsPoint > &points)
calculates area of polygon on ellipsoid algorithm has been taken from GRASS: gis/area_poly1.c
double bearing(const QgsPoint &p1, const QgsPoint &p2)
compute bearing - in radians
const long GEOCRS_ID
Magic number for a geographic coord sys in QGIS srs.db tbl_srs.srs_id.
QString toString() const
String representation of the point (x,y)
A class to represent a point.
QgsDistanceArea & operator=(const QgsDistanceArea &origDA)
Assignment operator.
static QString textUnit(double value, int decimals, QGis::UnitType u, bool isArea, bool keepBaseUnit=false)
General purpose distance and area calculator.
const QString & ellipsoid() const
returns ellipsoid's acronym
Class for storing a coordinate reference system (CRS)
static const QString srsDbFilePath()
Returns the path to the srs.db file.
double measureLine(const QList< QgsPoint > &points)
measures line
const CORE_EXPORT QString GEO_NONE
Constant that holds the string representation for "No ellips/No CRS".
static double fromUnitToUnitFactor(QGis::UnitType fromUnit, QGis::UnitType toUnit)
Returns the conversion factor between the specified units.
UnitType
Map units that qgis supports.
void convertMeasurement(double &measure, QGis::UnitType &measureUnits, QGis::UnitType displayUnits, bool isArea)
Helper for conversion between physical units.
Custom exception class for Coordinate Reference System related exceptions.
static QString toLiteral(QGis::UnitType unit)
Provides the canonical name of the type value.
double computeDistanceFlat(const QgsPoint &p1, const QgsPoint &p2)
uses flat / planimetric / Euclidean distance
const unsigned char * asWkb() const
Returns the buffer containing this geometry in WKB format.
QgsDistanceArea()
Constructor.
void setEllipsoidalMode(bool flag)
sets whether coordinates must be projected to ellipsoid before measuring
bool createFromProj4(const QString &theProjString)
QString toProj4() const
Get the Proj Proj4 string representation of this srs.
double computeDistanceBearing(const QgsPoint &p1, const QgsPoint &p2, double *course1=NULL, double *course2=NULL)
calculates distance from two points on ellipsoid based on inverse Vincenty's formulae ...
void setSourceAuthId(QString authid)
sets source spatial reference system by authid
QGis::UnitType mapUnits() const