QGIS API Documentation 3.27.0-Master (95e00c50d2)
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 const Qgis::CoordinateOrder axisOrder = qgsEnumKeyToValue( project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/CoordinateOrder" ) ), Qgis::CoordinateOrder::Default );
116
118 if ( !crs.isValid() && !destCrs.isValid() )
119 {
120 return QString();
121 }
122 else if ( !crs.isValid() )
123 {
124 crs = destCrs;
125 }
126
127 QgsPointXY p = point;
128 const bool isGeographic = crs.isGeographic();
129 if ( destCrs != crs )
130 {
131 const QgsCoordinateTransform ct( destCrs, crs, project );
132 try
133 {
134 p = ct.transform( point );
135 }
136 catch ( QgsCsException & )
137 {
138 return QString();
139 }
140 }
141
142 if ( isGeographic )
143 {
145
146 std::unique_ptr< QgsGeographicCoordinateNumericFormat > format( project->displaySettings()->geographicCoordinateFormat()->clone() );
147 format->setNumberDecimalPlaces( precision );
148
151 const QString formattedX = format->formatDouble( p.x(), context );
153 const QString formattedY = format->formatDouble( p.y(), context );
154
155 switch ( order )
156 {
159 return QStringLiteral( "%1%2%3" ).arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
160
162 return QStringLiteral( "%1%2%3" ).arg( formattedY, QgsCoordinateFormatter::separator(), formattedX );
163 }
165 }
166 else
167 {
168 // coordinates in map units
170 return QgsCoordinateFormatter::asPair( p.x(), p.y(), precision, order );
171 }
172}
173
174QString QgsCoordinateUtils::formatExtentForProject( QgsProject *project, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &destCrs, int precision )
175{
176 const QgsPointXY p1( extent.xMinimum(), extent.yMinimum() );
177 const QgsPointXY p2( extent.xMaximum(), extent.yMaximum() );
178 return QStringLiteral( "%1 : %2" ).arg( QgsCoordinateUtils::formatCoordinateForProject( project, p1, destCrs, precision ),
179 QgsCoordinateUtils::formatCoordinateForProject( project, p2, destCrs, precision ) );
180}
181
182double QgsCoordinateUtils::degreeToDecimal( const QString &string, bool *ok, bool *isEasting )
183{
184 const QString negative( QStringLiteral( "swSW" ) );
185 const QString easting( QStringLiteral( "eEwW" ) );
186 double value = 0.0;
187 bool okValue = false;
188
189 if ( ok )
190 {
191 *ok = false;
192 }
193 else
194 {
195 ok = &okValue;
196 }
197
198 const QLocale locale;
199 QRegularExpression degreeWithSuffix( QStringLiteral( "^\\s*([-]?\\d{1,3}(?:[\\.\\%1]\\d+)?)\\s*([NSEWnsew])\\s*$" )
200 .arg( locale.decimalPoint() ) );
201 QRegularExpressionMatch match = degreeWithSuffix.match( string );
202 if ( match.hasMatch() )
203 {
204 const QString suffix = match.captured( 2 );
205 value = std::abs( match.captured( 1 ).toDouble( ok ) );
206 if ( *ok == false )
207 {
208 value = std::abs( locale.toDouble( match.captured( 1 ), ok ) );
209 }
210 if ( *ok )
211 {
212 value *= ( negative.contains( suffix ) ? -1 : 1 );
213 if ( isEasting )
214 {
215 *isEasting = easting.contains( suffix );
216 }
217 }
218 }
219 return value;
220}
221
222double QgsCoordinateUtils::dmsToDecimal( const QString &string, bool *ok, bool *isEasting )
223{
224 const QString negative( QStringLiteral( "swSW-" ) );
225 const QString easting( QStringLiteral( "eEwW" ) );
226 double value = 0.0;
227 bool okValue = false;
228
229 if ( ok )
230 {
231 *ok = false;
232 }
233 else
234 {
235 ok = &okValue;
236 }
237
238 const QLocale locale;
239 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*$" )
240 .arg( locale.decimalPoint() ), QRegularExpression::CaseInsensitiveOption );
241 const QRegularExpressionMatch match = dms.match( string.trimmed() );
242 if ( match.hasMatch() )
243 {
244 const QString dms1 = match.captured( 2 );
245 const QString dms2 = match.captured( 3 );
246 const QString dms3 = match.captured( 4 );
247
248 double v = dms3.toDouble( ok );
249 if ( *ok == false )
250 {
251 v = locale.toDouble( dms3, ok );
252 if ( *ok == false )
253 return value;
254 }
255 // Allow for Degrees/minutes format as well as DMS
256 if ( !dms2.isEmpty() )
257 {
258 v = dms2.toInt( ok ) + v / 60.0;
259 if ( *ok == false )
260 return value;
261 }
262 v = dms1.toInt( ok ) + v / 60.0;
263 if ( *ok == false )
264 return value;
265
266 const QString sign1 = match.captured( 1 );
267 const QString sign2 = match.captured( 5 );
268
269 if ( sign1.isEmpty() )
270 {
271 value = !sign2.isEmpty() && negative.contains( sign2 ) ? -v : v;
272 if ( isEasting )
273 {
274 *isEasting = easting.contains( sign2 );
275 }
276 }
277 else if ( sign2.isEmpty() )
278 {
279 value = !sign1.isEmpty() && negative.contains( sign1 ) ? -v : v;
280 if ( isEasting )
281 {
282 *isEasting = easting.contains( sign2 );
283 }
284 }
285 else
286 {
287 *ok = false;
288 }
289 }
290 return value;
291}
292
@ 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 QString asPair(double x, double y, int precision=12, Qgis::CoordinateOrder order=Qgis::CoordinateOrder::XY)
Formats coordinates as an "\a x,\a y" pair, with optional decimal precision (number of decimal places...
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::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
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
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
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition: qgis.h:2700
#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