Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgscoordinateutils.cpp
3 : : ----------------------
4 : : begin : February 2016
5 : : copyright : (C) 2016 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgscoordinateutils.h"
19 : : #include "qgscoordinatereferencesystem.h"
20 : : #include "qgscoordinatetransform.h"
21 : : #include "qgsproject.h"
22 : : #include "qgis.h"
23 : : #include "qgsexception.h"
24 : : #include "qgscoordinateformatter.h"
25 : : #include "qgsrectangle.h"
26 : : #include <QRegularExpression>
27 : :
28 : : ///@cond NOT_STABLE_API
29 : :
30 : 0 : int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, const QgsCoordinateReferenceSystem &mapCrs, QgsProject *project )
31 : : {
32 : 0 : if ( !project )
33 : 0 : project = QgsProject::instance();
34 : : // Get the display precision from the project settings
35 : 0 : bool automatic = project->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
36 : 0 : int dp = 0;
37 : :
38 : 0 : if ( automatic )
39 : : {
40 : 0 : QString format = project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DegreeFormat" ), QStringLiteral( "MU" ) );
41 : 0 : bool formatGeographic = ( format == QLatin1String( "DM" ) || format == QLatin1String( "DMS" ) || format == QLatin1String( "D" ) );
42 : :
43 : : // we can only calculate an automatic precision if one of these is true:
44 : : // - both map CRS and format are geographic
45 : : // - both map CRS and format are not geographic
46 : : // - map CRS is geographic but format is not geographic (i.e. map units)
47 : 0 : if ( mapCrs.isGeographic() || !formatGeographic )
48 : : {
49 : : // Work out a suitable number of decimal places for the coordinates with the aim of always
50 : : // having enough decimal places to show the difference in position between adjacent pixels.
51 : : // Also avoid taking the log of 0.
52 : 0 : if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) )
53 : 0 : dp = static_cast<int>( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) );
54 : 0 : }
55 : : else
56 : : {
57 : 0 : if ( format == QLatin1String( "D" ) )
58 : 0 : dp = 4;
59 : : else
60 : 0 : dp = 2;
61 : : }
62 : 0 : }
63 : : else
64 : 0 : dp = project->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) );
65 : :
66 : : // Keep dp sensible
67 : 0 : if ( dp < 0 )
68 : 0 : dp = 0;
69 : :
70 : 0 : return dp;
71 : 0 : }
72 : :
73 : 0 : int QgsCoordinateUtils::calculateCoordinatePrecisionForCrs( const QgsCoordinateReferenceSystem &crs, QgsProject *project )
74 : : {
75 : 0 : QgsProject *prj = project;
76 : 0 : if ( !prj )
77 : : {
78 : 0 : prj = QgsProject::instance();
79 : 0 : }
80 : :
81 : 0 : bool automatic = prj->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
82 : 0 : if ( !automatic )
83 : : {
84 : 0 : return prj->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 6 );
85 : : }
86 : :
87 : 0 : QgsUnitTypes::DistanceUnit unit = crs.mapUnits();
88 : 0 : if ( unit == QgsUnitTypes::DistanceDegrees )
89 : : {
90 : 0 : return 8;
91 : : }
92 : : else
93 : : {
94 : 0 : return 3;
95 : : }
96 : 0 : }
97 : :
98 : 0 : QString QgsCoordinateUtils::formatCoordinateForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision )
99 : : {
100 : 0 : if ( !project )
101 : 0 : return QString();
102 : :
103 : 0 : QString format = project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DegreeFormat" ), QStringLiteral( "MU" ) );
104 : :
105 : 0 : QgsPointXY geo = point;
106 : 0 : if ( format == QLatin1String( "DM" ) || format == QLatin1String( "DMS" ) || format == QLatin1String( "D" ) )
107 : : {
108 : : // degrees
109 : 0 : if ( destCrs.isValid() && !destCrs.isGeographic() )
110 : : {
111 : : // need to transform to geographic coordinates
112 : 0 : QgsCoordinateTransform ct( destCrs, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), project );
113 : : try
114 : : {
115 : 0 : geo = ct.transform( point );
116 : 0 : }
117 : : catch ( QgsCsException & )
118 : : {
119 : 0 : return QString();
120 : 0 : }
121 : 0 : }
122 : :
123 : 0 : if ( format == QLatin1String( "DM" ) )
124 : 0 : return QgsCoordinateFormatter::format( geo, QgsCoordinateFormatter::FormatDegreesMinutes, precision, QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds | QgsCoordinateFormatter::FlagDegreesUseStringSuffix );
125 : 0 : else if ( format == QLatin1String( "DMS" ) )
126 : 0 : return QgsCoordinateFormatter::format( geo, QgsCoordinateFormatter::FormatDegreesMinutesSeconds, precision, QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds | QgsCoordinateFormatter::FlagDegreesUseStringSuffix );
127 : : else
128 : 0 : return QgsCoordinateFormatter::asPair( geo.x(), geo.y(), precision );
129 : : }
130 : : else
131 : : {
132 : : // coordinates in map units
133 : 0 : return QgsCoordinateFormatter::asPair( point.x(), point.y(), precision );
134 : : }
135 : 0 : }
136 : :
137 : 0 : QString QgsCoordinateUtils::formatExtentForProject( QgsProject *project, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &destCrs, int precision )
138 : : {
139 : 0 : const QgsPointXY p1( extent.xMinimum(), extent.yMinimum() );
140 : 0 : const QgsPointXY p2( extent.xMaximum(), extent.yMaximum() );
141 : 0 : return QStringLiteral( "%1 : %2" ).arg( QgsCoordinateUtils::formatCoordinateForProject( project, p1, destCrs, precision ),
142 : 0 : QgsCoordinateUtils::formatCoordinateForProject( project, p2, destCrs, precision ) );
143 : 0 : }
144 : :
145 : 0 : double QgsCoordinateUtils::dmsToDecimal( const QString &string, bool *ok, bool *isEasting )
146 : : {
147 : 0 : const QString negative( QStringLiteral( "swSW-" ) );
148 : 0 : const QString easting( QStringLiteral( "eEwW" ) );
149 : 0 : double value = 0.0;
150 : 0 : bool okValue = false;
151 : :
152 : 0 : if ( ok )
153 : : {
154 : 0 : *ok = false;
155 : 0 : }
156 : : else
157 : : {
158 : 0 : ok = &okValue;
159 : : }
160 : :
161 : 0 : QRegularExpression dms( "^\\s*(?:([-+nsew])\\s*)?(\\d{1,3})(?:[^0-9.]+([0-5]?\\d))?[^0-9.]+([0-5]?\\d(?:\\.\\d+)?)[^0-9.,]*?([-+nsew])?\\s*$", QRegularExpression::CaseInsensitiveOption );
162 : 0 : QRegularExpressionMatch match = dms.match( string.trimmed() );
163 : 0 : if ( match.hasMatch() )
164 : : {
165 : 0 : QString dms1 = match.captured( 2 );
166 : 0 : QString dms2 = match.captured( 3 );
167 : 0 : QString dms3 = match.captured( 4 );
168 : :
169 : 0 : double v = dms3.toDouble( ok );
170 : 0 : if ( *ok == false )
171 : 0 : return value;
172 : : // Allow for Degrees/minutes format as well as DMS
173 : 0 : if ( !dms2.isEmpty() )
174 : : {
175 : 0 : v = dms2.toInt( ok ) + v / 60.0;
176 : 0 : if ( *ok == false )
177 : 0 : return value;
178 : 0 : }
179 : 0 : v = dms1.toInt( ok ) + v / 60.0;
180 : 0 : if ( *ok == false )
181 : 0 : return value;
182 : :
183 : 0 : QString sign1 = match.captured( 1 );
184 : 0 : QString sign2 = match.captured( 5 );
185 : :
186 : 0 : if ( sign1.isEmpty() )
187 : : {
188 : 0 : value = !sign2.isEmpty() && negative.contains( sign2 ) ? -v : v;
189 : 0 : if ( isEasting )
190 : : {
191 : 0 : *isEasting = easting.contains( sign2 );
192 : 0 : }
193 : 0 : }
194 : 0 : else if ( sign2.isEmpty() )
195 : : {
196 : 0 : value = !sign1.isEmpty() && negative.contains( sign1 ) ? -v : v;
197 : 0 : if ( isEasting )
198 : : {
199 : 0 : *isEasting = easting.contains( sign2 );
200 : 0 : }
201 : 0 : }
202 : : else
203 : : {
204 : 0 : *ok = false;
205 : : }
206 : 0 : }
207 : 0 : return value;
208 : 0 : }
209 : :
210 : : ///@endcond
|