QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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"
20 #include "qgscoordinatetransform.h"
21 #include "qgsproject.h"
22 #include "qgis.h"
23 #include "qgsexception.h"
24 #include "qgscoordinateformatter.h"
26 
27 int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, const QgsCoordinateReferenceSystem &mapCrs, QgsProject *project )
28 {
29  if ( !project )
30  project = QgsProject::instance();
31  // Get the display precision from the project settings
32  bool automatic = project->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
33  int dp = 0;
34 
35  if ( automatic )
36  {
37  QString format = project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DegreeFormat" ), QStringLiteral( "MU" ) );
38  bool formatGeographic = ( format == QLatin1String( "DM" ) || format == QLatin1String( "DMS" ) || format == QLatin1String( "D" ) );
39 
40  // we can only calculate an automatic precision if one of these is true:
41  // - both map CRS and format are geographic
42  // - both map CRS and format are not geographic
43  // - map CRS is geographic but format is not geographic (i.e. map units)
44  if ( mapCrs.isGeographic() || !formatGeographic )
45  {
46  // Work out a suitable number of decimal places for the coordinates with the aim of always
47  // having enough decimal places to show the difference in position between adjacent pixels.
48  // Also avoid taking the log of 0.
49  if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) )
50  dp = static_cast<int>( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) );
51  }
52  else
53  {
54  if ( format == QLatin1String( "D" ) )
55  dp = 4;
56  else
57  dp = 2;
58  }
59  }
60  else
61  dp = project->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) );
62 
63  // Keep dp sensible
64  if ( dp < 0 )
65  dp = 0;
66 
67  return dp;
68 }
69 
70 QString QgsCoordinateUtils::formatCoordinateForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision )
71 {
72  if ( !project )
73  return QString();
74 
75  QString format = project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DegreeFormat" ), QStringLiteral( "MU" ) );
76 
77  QgsPointXY geo = point;
78  if ( format == QLatin1String( "DM" ) || format == QLatin1String( "DMS" ) || format == QLatin1String( "D" ) )
79  {
80  // degrees
81  if ( destCrs.isValid() && !destCrs.isGeographic() )
82  {
83  // need to transform to geographic coordinates
84  QgsCoordinateTransform ct( destCrs, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), project );
85  try
86  {
87  geo = ct.transform( point );
88  }
89  catch ( QgsCsException & )
90  {
91  return QString();
92  }
93  }
94 
95  if ( format == QLatin1String( "DM" ) )
97  else if ( format == QLatin1String( "DMS" ) )
99  else
100  return QgsCoordinateFormatter::asPair( geo.x(), geo.y(), precision );
101  }
102  else
103  {
104  // coordinates in map units
105  return QgsCoordinateFormatter::asPair( point.x(), point.y(), precision );
106  }
107 }
108 
109 double QgsCoordinateUtils::dmsToDecimal( const QString &string, bool *ok )
110 {
111  const QString negative( QStringLiteral( "swSW-" ) );
112  double value = 0.0;
113  bool okValue = false;
114 
115  if ( ok )
116  {
117  *ok = false;
118  }
119  else
120  {
121  ok = &okValue;
122  }
123 
124  QRegularExpression dms( "^\\s*(?:([-+nsew])\\s*)?(\\d{1,3})(?:[^0-9.]+([0-5]?\\d))?[^0-9.]+([0-5]?\\d(?:\\.\\d+)?)[^0-9.]*?([-+nsew])?\\s*$", QRegularExpression::CaseInsensitiveOption );
125  QRegularExpressionMatch match = dms.match( string.trimmed() );
126  if ( match.hasMatch() )
127  {
128  QString dms1 = match.captured( 2 );
129  QString dms2 = match.captured( 3 );
130  QString dms3 = match.captured( 4 );
131 
132  double v = dms3.toDouble( ok );
133  if ( *ok == false )
134  return value;
135  // Allow for Degrees/minutes format as well as DMS
136  if ( !dms2.isEmpty() )
137  {
138  v = dms2.toInt( ok ) + v / 60.0;
139  if ( *ok == false )
140  return value;
141  }
142  v = dms1.toInt( ok ) + v / 60.0;
143  if ( *ok == false )
144  return value;
145 
146  QString sign1 = match.captured( 1 );
147  QString sign2 = match.captured( 5 );
148 
149  if ( sign1.isEmpty() )
150  {
151  value = !sign2.isEmpty() && negative.contains( sign2 ) ? -v : v;
152  }
153  else if ( sign2.isEmpty() )
154  {
155  value = !sign1.isEmpty() && negative.contains( sign1 ) ? -v : v;
156  }
157  else
158  {
159  *ok = false;
160  }
161  }
162  return value;
163 }
164 
QgsPointXY::y
double y
Definition: qgspointxy.h:48
qgscoordinateutils.h
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
qgis.h
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsProject::readEntry
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.
Definition: qgsproject.cpp:2526
QgsProject::readBoolEntry
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Reads a boolean from the specified scope and key.
Definition: qgsproject.cpp:2601
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:95
QgsCoordinateFormatter::FormatDegreesMinutes
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
Definition: qgscoordinateformatter.h:51
precision
int precision
Definition: qgswfsgetfeature.cpp:49
QgsCoordinateReferenceSystem::isGeographic
bool isGeographic
Definition: qgscoordinatereferencesystem.h:210
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
QgsCoordinateFormatter::asPair
static QString asPair(double x, double y, int precision=12)
Formats coordinates as an "\a x,\a y" pair, with optional decimal precision (number of decimal places...
Definition: qgscoordinateformatter.cpp:67
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:924
qgscoordinatetransform.h
QgsCoordinateFormatter::format
static QString format(const QgsPointXY &point, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a point according to the specified parameters.
Definition: qgscoordinateformatter.cpp:61
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:206
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsCoordinateFormatter::FormatDegreesMinutesSeconds
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
Definition: qgscoordinateformatter.h:50
QgsCoordinateFormatter::FlagDegreesUseStringSuffix
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
Definition: qgscoordinateformatter.h:60
qgsexception.h
QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
Definition: qgscoordinateformatter.h:61
qgscoordinateformatter.h
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:53
qgscoordinatereferencesystem.h
qgsproject.h
QgsProject::readNumEntry
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
Definition: qgsproject.cpp:2552