QGIS API Documentation 3.41.0-Master (88383c3d16f)
Loading...
Searching...
No Matches
qgscoordinateutils.cpp
Go to the documentation of this file.
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 "moc_qgscoordinateutils.cpp"
23#include "qgsproject.h"
24#include "qgis.h"
25#include "qgsexception.h"
27#include "qgsrectangle.h"
30
31#include <QLocale>
32#include <QRegularExpression>
33
35
36int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, const QgsCoordinateReferenceSystem &mapCrs, QgsProject *project )
37{
38 if ( !project )
39 project = QgsProject::instance(); // skip-keyword-check
40 // Get the display precision from the project settings
41 const bool automatic = project->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
42 int dp = 0;
43
44 if ( automatic )
45 {
46 const bool formatGeographic = project->displaySettings()->coordinateType() == Qgis::CoordinateDisplayType::MapGeographic ||
49
50 // we can only calculate an automatic precision if one of these is true:
51 // - both map CRS and format are geographic
52 // - both map CRS and format are not geographic
53 // - map CRS is geographic but format is not geographic (i.e. map units)
54 if ( mapCrs.isGeographic() || !formatGeographic )
55 {
56 // Work out a suitable number of decimal places for the coordinates with the aim of always
57 // having enough decimal places to show the difference in position between adjacent pixels.
58 // Also avoid taking the log of 0.
59 if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) )
60 dp = static_cast<int>( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) );
61 }
62 else
63 {
65 {
68 dp = 2;
69 break;
71 dp = 4;
72 break;
73 }
74 }
75 }
76 else
77 dp = project->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) );
78
79 // Keep dp sensible
80 if ( dp < 0 )
81 dp = 0;
82
83 return dp;
84}
85
86int QgsCoordinateUtils::calculateCoordinatePrecisionForCrs( const QgsCoordinateReferenceSystem &crs, QgsProject *project )
87{
88 QgsProject *prj = project;
89 if ( !prj )
90 {
91 prj = QgsProject::instance(); // skip-keyword-check
92 }
93
94 const bool automatic = prj->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
95 if ( !automatic )
96 {
97 return prj->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 6 );
98 }
99
100 return calculateCoordinatePrecision( crs );
101}
102
103int QgsCoordinateUtils::calculateCoordinatePrecision( const QgsCoordinateReferenceSystem &crs )
104{
105 const Qgis::DistanceUnit unit = crs.mapUnits();
106 if ( unit == Qgis::DistanceUnit::Degrees )
107 {
108 return 8;
109 }
110 else
111 {
112 return 3;
113 }
114}
115
116QString QgsCoordinateUtils::formatCoordinateForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision )
117{
118 if ( !project )
119 return QString();
120
121 QString formattedX;
122 QString formattedY;
123 formatCoordinatePartsForProject( project, point, destCrs, precision, formattedX, formattedY );
124
125 if ( formattedX.isEmpty() || formattedY.isEmpty() )
126 return QString();
127
128 const Qgis::CoordinateOrder axisOrder = project->displaySettings()->coordinateAxisOrder();
129
131 if ( !crs.isValid() && !destCrs.isValid() )
132 {
133 return QStringLiteral( "%1%2 %3" ).arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
134 }
135 else if ( !crs.isValid() )
136 {
137 crs = destCrs;
138 }
139
141 switch ( order )
142 {
145 return QStringLiteral( "%1%2 %3" ).arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
146
148 return QStringLiteral( "%1%2 %3" ).arg( formattedY, QgsCoordinateFormatter::separator(), formattedX );
149 }
151}
152
153void QgsCoordinateUtils::formatCoordinatePartsForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision, QString &x, QString &y )
154{
155 x.clear();
156 y.clear();
157 if ( !project )
158 return;
159
161 if ( !crs.isValid() && !destCrs.isValid() )
162 {
163 x = QgsCoordinateFormatter::formatAsPair( point.x(), precision );
164 y = QgsCoordinateFormatter::formatAsPair( point.y(), precision );
165 return;
166 }
167 else if ( !crs.isValid() )
168 {
169 crs = destCrs;
170 }
171
172 QgsPointXY p = point;
173 const bool isGeographic = crs.isGeographic();
174 if ( destCrs != crs )
175 {
176 const QgsCoordinateTransform ct( destCrs, crs, project );
177 try
178 {
179 p = ct.transform( point );
180 }
181 catch ( QgsCsException & )
182 {
183 return;
184 }
185 }
186
187 if ( isGeographic )
188 {
189 std::unique_ptr< QgsGeographicCoordinateNumericFormat > format( project->displaySettings()->geographicCoordinateFormat()->clone() );
190 format->setNumberDecimalPlaces( precision );
191
194 x = format->formatDouble( p.x(), context );
196 y = format->formatDouble( p.y(), context );
197 }
198 else
199 {
200 // coordinates in map units
201 x = QgsCoordinateFormatter::formatAsPair( p.x(), precision );
202 y = QgsCoordinateFormatter::formatAsPair( p.y(), precision );
203 }
204}
205
206QString QgsCoordinateUtils::formatExtentForProject( QgsProject *project, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &destCrs, int precision )
207{
208 const QgsPointXY p1( extent.xMinimum(), extent.yMinimum() );
209 const QgsPointXY p2( extent.xMaximum(), extent.yMaximum() );
210 return QStringLiteral( "%1 : %2" ).arg( QgsCoordinateUtils::formatCoordinateForProject( project, p1, destCrs, precision ),
211 QgsCoordinateUtils::formatCoordinateForProject( project, p2, destCrs, precision ) );
212}
213
214double QgsCoordinateUtils::degreeToDecimal( const QString &string, bool *ok, bool *isEasting )
215{
216 const QString negative( QStringLiteral( "swSW" ) );
217 const QString easting( QStringLiteral( "eEwW" ) );
218 double value = 0.0;
219 bool okValue = false;
220
221 if ( ok )
222 {
223 *ok = false;
224 }
225 else
226 {
227 ok = &okValue;
228 }
229
230 const QLocale locale;
231 QRegularExpression degreeWithSuffix( QStringLiteral( "^\\s*([-]?\\d{1,3}(?:[\\.\\%1]\\d+)?)\\s*([NSEWnsew])\\s*$" )
232 .arg( locale.decimalPoint() ) );
233 QRegularExpressionMatch match = degreeWithSuffix.match( string );
234 if ( match.hasMatch() )
235 {
236 const QString suffix = match.captured( 2 );
237 value = std::abs( match.captured( 1 ).toDouble( ok ) );
238 if ( *ok == false )
239 {
240 value = std::abs( locale.toDouble( match.captured( 1 ), ok ) );
241 }
242 if ( *ok )
243 {
244 value *= ( negative.contains( suffix ) ? -1 : 1 );
245 if ( isEasting )
246 {
247 *isEasting = easting.contains( suffix );
248 }
249 }
250 }
251 return value;
252}
253
254double QgsCoordinateUtils::dmsToDecimal( const QString &string, bool *ok, bool *isEasting )
255{
256 const QString negative( QStringLiteral( "swSW-" ) );
257 const QString easting( QStringLiteral( "eEwW" ) );
258 double value = 0.0;
259 bool okValue = false;
260
261 if ( ok )
262 {
263 *ok = false;
264 }
265 else
266 {
267 ok = &okValue;
268 }
269
270 const QLocale locale;
271 const QRegularExpression dms( QStringLiteral( "^\\s*(?:([-+nsew])\\s*)?(\\d{1,3})(?:[^0-9.]+([0-5]?\\d))?[^0-9.]+([0-5]?\\d(?:[\\.\\%1]\\d+)?)[^0-9.,]*?([-+nsew])?\\s*$" )
272 .arg( locale.decimalPoint() ), QRegularExpression::CaseInsensitiveOption );
273 const QRegularExpressionMatch match = dms.match( string.trimmed() );
274 if ( match.hasMatch() )
275 {
276 const QString dms1 = match.captured( 2 );
277 const QString dms2 = match.captured( 3 );
278 const QString dms3 = match.captured( 4 );
279
280 double v = dms3.toDouble( ok );
281 if ( *ok == false )
282 {
283 v = locale.toDouble( dms3, ok );
284 if ( *ok == false )
285 return value;
286 }
287 // Allow for Degrees/minutes format as well as DMS
288 if ( !dms2.isEmpty() )
289 {
290 v = dms2.toInt( ok ) + v / 60.0;
291 if ( *ok == false )
292 return value;
293 }
294 v = dms1.toInt( ok ) + v / 60.0;
295 if ( *ok == false )
296 return value;
297
298 const QString sign1 = match.captured( 1 );
299 const QString sign2 = match.captured( 5 );
300
301 if ( sign1.isEmpty() )
302 {
303 value = !sign2.isEmpty() && negative.contains( sign2 ) ? -v : v;
304 if ( isEasting )
305 {
306 *isEasting = easting.contains( sign2 );
307 }
308 }
309 else if ( sign2.isEmpty() )
310 {
311 value = !sign1.isEmpty() && negative.contains( sign1 ) ? -v : v;
312 if ( isEasting )
313 {
314 *isEasting = easting.contains( sign1 );
315 }
316 }
317 else
318 {
319 *ok = false;
320 }
321 }
322 return value;
323}
324
@ MapGeographic
Map Geographic CRS equivalent (stays unchanged if the map CRS is geographic)
DistanceUnit
Units of distance.
Definition qgis.h:4760
@ Degrees
Degrees, for planar geographic CRS distance measurements.
CoordinateOrder
Order of coordinates.
Definition qgis.h:2317
@ XY
Easting/Northing (or Longitude/Latitude for geographic CRS)
@ Default
Respect the default axis ordering for the CRS, as defined in the CRS's parameters.
@ YX
Northing/Easting (or Latitude/Longitude for geographic CRS)
static QChar separator()
Returns the character used as X/Y separator, this is a , on locales that do not use ,...
static Qgis::CoordinateOrder defaultCoordinateOrderForCrs(const QgsCoordinateReferenceSystem &crs)
Returns the default coordinate order to use for the specified crs.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
@ DegreesMinutes
Degrees and decimal minutes, eg 30 degrees 45.55'.
@ DecimalDegrees
Decimal degrees, eg 30.7555 degrees.
@ DegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30.
QgsGeographicCoordinateNumericFormat * clone() const override
Clones the format, returning a new object.
AngleFormat angleFormat() const
Returns the angle format, which controls how bearing the angles are formatted described in the return...
A context for numeric formats.
void setInterpretation(Interpretation interpretation)
Sets the interpretation of the numbers being converted.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
const QgsGeographicCoordinateNumericFormat * geographicCoordinateFormat() const
Returns the project's geographic coordinate format, which controls how geographic coordinates associa...
QgsCoordinateReferenceSystem coordinateCustomCrs
Qgis::CoordinateOrder coordinateAxisOrder
Qgis::CoordinateDisplayType coordinateType
QgsCoordinateReferenceSystem coordinateCrs
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsProjectDisplaySettings * displaySettings
Definition qgsproject.h:126
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Reads a boolean from the specified scope and key.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
#define BUILTIN_UNREACHABLE
Definition qgis.h:6779
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6125
const QgsCoordinateReferenceSystem & crs
int precision