QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
22#include "qgsproject.h"
23#include "qgis.h"
24#include "qgsexception.h"
26#include "qgsrectangle.h"
29
30#include <QLocale>
31#include <QRegularExpression>
32
34
35int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, const QgsCoordinateReferenceSystem &mapCrs, QgsProject *project )
36{
37 if ( !project )
38 project = QgsProject::instance();
39 // Get the display precision from the project settings
40 const bool automatic = project->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
41 int dp = 0;
42
43 if ( automatic )
44 {
45 const bool formatGeographic = project->displaySettings()->coordinateType() == Qgis::CoordinateDisplayType::MapGeographic ||
48
49 // we can only calculate an automatic precision if one of these is true:
50 // - both map CRS and format are geographic
51 // - both map CRS and format are not geographic
52 // - map CRS is geographic but format is not geographic (i.e. map units)
53 if ( mapCrs.isGeographic() || !formatGeographic )
54 {
55 // Work out a suitable number of decimal places for the coordinates with the aim of always
56 // having enough decimal places to show the difference in position between adjacent pixels.
57 // Also avoid taking the log of 0.
58 if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) )
59 dp = static_cast<int>( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) );
60 }
61 else
62 {
64 {
67 dp = 2;
68 break;
70 dp = 4;
71 break;
72 }
73 }
74 }
75 else
76 dp = project->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) );
77
78 // Keep dp sensible
79 if ( dp < 0 )
80 dp = 0;
81
82 return dp;
83}
84
85int QgsCoordinateUtils::calculateCoordinatePrecisionForCrs( const QgsCoordinateReferenceSystem &crs, QgsProject *project )
86{
87 QgsProject *prj = project;
88 if ( !prj )
89 {
91 }
92
93 const bool automatic = prj->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
94 if ( !automatic )
95 {
96 return prj->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 6 );
97 }
98
100 if ( unit == QgsUnitTypes::DistanceDegrees )
101 {
102 return 8;
103 }
104 else
105 {
106 return 3;
107 }
108}
109
110QString QgsCoordinateUtils::formatCoordinateForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision )
111{
112 if ( !project )
113 return QString();
114
115 QString formattedX;
116 QString formattedY;
117 formatCoordinatePartsForProject( project, point, destCrs, precision, formattedX, formattedY );
118
119 if ( formattedX.isEmpty() || formattedY.isEmpty() )
120 return QString();
121
122 const Qgis::CoordinateOrder axisOrder = project->displaySettings()->coordinateAxisOrder();
123
125 if ( !crs.isValid() && !destCrs.isValid() )
126 {
127 return QString();
128 }
129 else if ( !crs.isValid() )
130 {
131 crs = destCrs;
132 }
133
135 switch ( order )
136 {
139 return QStringLiteral( "%1%2%3" ).arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
140
142 return QStringLiteral( "%1%2%3" ).arg( formattedY, QgsCoordinateFormatter::separator(), formattedX );
143 }
145}
146
147void QgsCoordinateUtils::formatCoordinatePartsForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision, QString &x, QString &y )
148{
149 x.clear();
150 y.clear();
151 if ( !project )
152 return;
153
155 if ( !crs.isValid() && !destCrs.isValid() )
156 {
157 return;
158 }
159 else if ( !crs.isValid() )
160 {
161 crs = destCrs;
162 }
163
164 QgsPointXY p = point;
165 const bool isGeographic = crs.isGeographic();
166 if ( destCrs != crs )
167 {
168 const QgsCoordinateTransform ct( destCrs, crs, project );
169 try
170 {
171 p = ct.transform( point );
172 }
173 catch ( QgsCsException & )
174 {
175 return;
176 }
177 }
178
179 if ( isGeographic )
180 {
181 std::unique_ptr< QgsGeographicCoordinateNumericFormat > format( project->displaySettings()->geographicCoordinateFormat()->clone() );
182 format->setNumberDecimalPlaces( precision );
183
186 x = format->formatDouble( p.x(), context );
188 y = format->formatDouble( p.y(), context );
189 }
190 else
191 {
192 // coordinates in map units
193 x = QgsCoordinateFormatter::formatAsPair( p.x(), precision );
194 y = QgsCoordinateFormatter::formatAsPair( p.y(), precision );
195 }
196}
197
198QString QgsCoordinateUtils::formatExtentForProject( QgsProject *project, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &destCrs, int precision )
199{
200 const QgsPointXY p1( extent.xMinimum(), extent.yMinimum() );
201 const QgsPointXY p2( extent.xMaximum(), extent.yMaximum() );
202 return QStringLiteral( "%1 : %2" ).arg( QgsCoordinateUtils::formatCoordinateForProject( project, p1, destCrs, precision ),
203 QgsCoordinateUtils::formatCoordinateForProject( project, p2, destCrs, precision ) );
204}
205
206double QgsCoordinateUtils::degreeToDecimal( const QString &string, bool *ok, bool *isEasting )
207{
208 const QString negative( QStringLiteral( "swSW" ) );
209 const QString easting( QStringLiteral( "eEwW" ) );
210 double value = 0.0;
211 bool okValue = false;
212
213 if ( ok )
214 {
215 *ok = false;
216 }
217 else
218 {
219 ok = &okValue;
220 }
221
222 const QLocale locale;
223 QRegularExpression degreeWithSuffix( QStringLiteral( "^\\s*([-]?\\d{1,3}(?:[\\.\\%1]\\d+)?)\\s*([NSEWnsew])\\s*$" )
224 .arg( locale.decimalPoint() ) );
225 QRegularExpressionMatch match = degreeWithSuffix.match( string );
226 if ( match.hasMatch() )
227 {
228 const QString suffix = match.captured( 2 );
229 value = std::abs( match.captured( 1 ).toDouble( ok ) );
230 if ( *ok == false )
231 {
232 value = std::abs( locale.toDouble( match.captured( 1 ), ok ) );
233 }
234 if ( *ok )
235 {
236 value *= ( negative.contains( suffix ) ? -1 : 1 );
237 if ( isEasting )
238 {
239 *isEasting = easting.contains( suffix );
240 }
241 }
242 }
243 return value;
244}
245
246double QgsCoordinateUtils::dmsToDecimal( const QString &string, bool *ok, bool *isEasting )
247{
248 const QString negative( QStringLiteral( "swSW-" ) );
249 const QString easting( QStringLiteral( "eEwW" ) );
250 double value = 0.0;
251 bool okValue = false;
252
253 if ( ok )
254 {
255 *ok = false;
256 }
257 else
258 {
259 ok = &okValue;
260 }
261
262 const QLocale locale;
263 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*$" )
264 .arg( locale.decimalPoint() ), QRegularExpression::CaseInsensitiveOption );
265 const QRegularExpressionMatch match = dms.match( string.trimmed() );
266 if ( match.hasMatch() )
267 {
268 const QString dms1 = match.captured( 2 );
269 const QString dms2 = match.captured( 3 );
270 const QString dms3 = match.captured( 4 );
271
272 double v = dms3.toDouble( ok );
273 if ( *ok == false )
274 {
275 v = locale.toDouble( dms3, ok );
276 if ( *ok == false )
277 return value;
278 }
279 // Allow for Degrees/minutes format as well as DMS
280 if ( !dms2.isEmpty() )
281 {
282 v = dms2.toInt( ok ) + v / 60.0;
283 if ( *ok == false )
284 return value;
285 }
286 v = dms1.toInt( ok ) + v / 60.0;
287 if ( *ok == false )
288 return value;
289
290 const QString sign1 = match.captured( 1 );
291 const QString sign2 = match.captured( 5 );
292
293 if ( sign1.isEmpty() )
294 {
295 value = !sign2.isEmpty() && negative.contains( sign2 ) ? -v : v;
296 if ( isEasting )
297 {
298 *isEasting = easting.contains( sign2 );
299 }
300 }
301 else if ( sign2.isEmpty() )
302 {
303 value = !sign1.isEmpty() && negative.contains( sign1 ) ? -v : v;
304 if ( isEasting )
305 {
306 *isEasting = easting.contains( sign2 );
307 }
308 }
309 else
310 {
311 *ok = false;
312 }
313 }
314 return value;
315}
316
@ MapGeographic
Map Geographic CRS equivalent (stays unchanged if the map CRS is geographic)
CoordinateOrder
Order of coordinates.
Definition: qgis.h:1167
@ 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.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
@ 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:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
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:104
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.
Definition: qgsproject.cpp:477
QgsProjectDisplaySettings * displaySettings
Definition: qgsproject.h:123
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceDegrees
Degrees, for planar geographic CRS distance measurements.
Definition: qgsunittypes.h:75
#define BUILTIN_UNREACHABLE
Definition: qgis.h:3148
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
const QgsCoordinateReferenceSystem & crs
int precision