QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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#include <QString>
34
35#include "moc_qgscoordinateutils.cpp"
36
37using namespace Qt::StringLiterals;
38
40
41int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, const QgsCoordinateReferenceSystem &mapCrs, QgsProject *project )
42{
43 if ( !project )
44 project = QgsProject::instance(); // skip-keyword-check
45 // Get the display precision from the project settings
46 const bool automatic = project->readBoolEntry( u"PositionPrecision"_s, u"/Automatic"_s );
47 int dp = 0;
48
49 if ( automatic )
50 {
51 const bool formatGeographic = project->displaySettings()->coordinateType() == Qgis::CoordinateDisplayType::MapGeographic
52 || ( project->displaySettings()->coordinateType() == Qgis::CoordinateDisplayType::CustomCrs && project->displaySettings()->coordinateCustomCrs().isGeographic() );
53
54 // we can only calculate an automatic precision if one of these is true:
55 // - both map CRS and format are geographic
56 // - both map CRS and format are not geographic
57 // - map CRS is geographic but format is not geographic (i.e. map units)
58 if ( mapCrs.isGeographic() || !formatGeographic )
59 {
60 // Work out a suitable number of decimal places for the coordinates with the aim of always
61 // having enough decimal places to show the difference in position between adjacent pixels.
62 // Also avoid taking the log of 0.
63 if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) )
64 dp = static_cast<int>( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) );
65 }
66 else
67 {
69 {
72 dp = 2;
73 break;
75 dp = 4;
76 break;
77 }
78 }
79 }
80 else
81 dp = project->readNumEntry( u"PositionPrecision"_s, u"/DecimalPlaces"_s );
82
83 // Keep dp sensible
84 if ( dp < 0 )
85 dp = 0;
86
87 return dp;
88}
89
90int QgsCoordinateUtils::calculateCoordinatePrecisionForCrs( const QgsCoordinateReferenceSystem &crs, QgsProject *project )
91{
92 QgsProject *prj = project;
93 if ( !prj )
94 {
95 prj = QgsProject::instance(); // skip-keyword-check
96 }
97
98 const bool automatic = prj->readBoolEntry( u"PositionPrecision"_s, u"/Automatic"_s );
99 if ( !automatic )
100 {
101 return prj->readNumEntry( u"PositionPrecision"_s, u"/DecimalPlaces"_s, 6 );
102 }
103
104 return calculateCoordinatePrecision( crs );
105}
106
107int QgsCoordinateUtils::calculateCoordinatePrecision( const QgsCoordinateReferenceSystem &crs )
108{
109 const Qgis::DistanceUnit unit = crs.mapUnits();
110 if ( unit == Qgis::DistanceUnit::Degrees )
111 {
112 return 8;
113 }
114 else
115 {
116 return 3;
117 }
118}
119
120QString QgsCoordinateUtils::formatCoordinateForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision )
121{
122 if ( !project )
123 return QString();
124
125 QString formattedX;
126 QString formattedY;
127 formatCoordinatePartsForProject( project, point, destCrs, precision, formattedX, formattedY );
128
129 if ( formattedX.isEmpty() || formattedY.isEmpty() )
130 return QString();
131
132 const Qgis::CoordinateOrder axisOrder = project->displaySettings()->coordinateAxisOrder();
133
135 if ( !crs.isValid() && !destCrs.isValid() )
136 {
137 return u"%1%2 %3"_s.arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
138 }
139 else if ( !crs.isValid() )
140 {
141 crs = destCrs;
142 }
143
145 switch ( order )
146 {
149 return u"%1%2 %3"_s.arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
150
152 return u"%1%2 %3"_s.arg( formattedY, QgsCoordinateFormatter::separator(), formattedX );
153 }
155}
156
157void QgsCoordinateUtils::formatCoordinatePartsForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision, QString &x, QString &y )
158{
159 x.clear();
160 y.clear();
161 if ( !project )
162 return;
163
165 if ( !crs.isValid() && !destCrs.isValid() )
166 {
167 x = QgsCoordinateFormatter::formatAsPair( point.x(), precision );
168 y = QgsCoordinateFormatter::formatAsPair( point.y(), precision );
169 return;
170 }
171 else if ( !crs.isValid() )
172 {
173 crs = destCrs;
174 }
175
176 QgsPointXY p = point;
177 const bool isGeographic = crs.isGeographic();
178 if ( destCrs != crs )
179 {
180 const QgsCoordinateTransform ct( destCrs, crs, project );
181 try
182 {
183 p = ct.transform( point );
184 }
185 catch ( QgsCsException & )
186 {
187 return;
188 }
189 }
190
191 if ( isGeographic )
192 {
193 std::unique_ptr< QgsGeographicCoordinateNumericFormat > format( project->displaySettings()->geographicCoordinateFormat()->clone() );
194 format->setNumberDecimalPlaces( precision );
195
198 x = format->formatDouble( p.x(), context );
200 y = format->formatDouble( p.y(), context );
201 }
202 else
203 {
204 // coordinates in map units
205 x = QgsCoordinateFormatter::formatAsPair( p.x(), precision );
206 y = QgsCoordinateFormatter::formatAsPair( p.y(), precision );
207 }
208}
209
210QString QgsCoordinateUtils::formatExtentForProject( QgsProject *project, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &destCrs, int precision )
211{
212 const QgsPointXY p1( extent.xMinimum(), extent.yMinimum() );
213 const QgsPointXY p2( extent.xMaximum(), extent.yMaximum() );
214 return u"%1 : %2"_s.arg( QgsCoordinateUtils::formatCoordinateForProject( project, p1, destCrs, precision ), QgsCoordinateUtils::formatCoordinateForProject( project, p2, destCrs, precision ) );
215}
216
217double QgsCoordinateUtils::degreeToDecimal( const QString &string, bool *ok, bool *isEasting )
218{
219 const QString negative( u"swSW"_s );
220 const QString easting( u"eEwW"_s );
221 double value = 0.0;
222 bool okValue = false;
223
224 if ( ok )
225 {
226 *ok = false;
227 }
228 else
229 {
230 ok = &okValue;
231 }
232
233 const QLocale locale;
234 QRegularExpression degreeWithSuffix( u"^\\s*([-]?\\d{1,3}(?:[\\.\\%1]\\d+)?)\\s*([NSEWnsew])\\s*$"_s.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( u"swSW-"_s );
259 const QString easting( u"eEwW"_s );
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
274 dms( u"^\\s*(?:([-+nsew])\\s*)?(\\d{1,3})(?:[^0-9.]+([0-5]?\\d))?[^0-9.]+([0-5]?\\d(?:[\\.\\%1]\\d+)?)[^0-9.,]*?([-+nsew])?\\s*$"_s.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:4598
@ CustomCrs
Custom CRS.
Definition qgis.h:4599
DistanceUnit
Units of distance.
Definition qgis.h:5170
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5177
CoordinateOrder
Order of coordinates.
Definition qgis.h:2484
@ XY
Easting/Northing (or Longitude/Latitude for geographic CRS).
Definition qgis.h:2486
@ Default
Respect the default axis ordering for the CRS, as defined in the CRS's parameters.
Definition qgis.h:2485
@ YX
Northing/Easting (or Latitude/Longitude for geographic CRS).
Definition qgis.h:2487
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:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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:113
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:133
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:7540
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975