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