/
qgscoordinateutils.cpp
165 lines (147 loc) · 5.82 KB
/
qgscoordinateutils.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/***************************************************************************
qgscoordinateutils.cpp
----------------------
begin : February 2016
copyright : (C) 2016 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscoordinateutils.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include "qgsproject.h"
#include "qgis.h"
#include "qgsexception.h"
#include "qgscoordinateformatter.h"
///@cond NOT_STABLE_API
int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, const QgsCoordinateReferenceSystem &mapCrs, QgsProject *project )
{
if ( !project )
project = QgsProject::instance();
// Get the display precision from the project settings
bool automatic = project->readBoolEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ) );
int dp = 0;
if ( automatic )
{
QString format = project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DegreeFormat" ), QStringLiteral( "MU" ) );
bool formatGeographic = ( format == QLatin1String( "DM" ) || format == QLatin1String( "DMS" ) || format == QLatin1String( "D" ) );
// we can only calculate an automatic precision if one of these is true:
// - both map CRS and format are geographic
// - both map CRS and format are not geographic
// - map CRS is geographic but format is not geographic (i.e. map units)
if ( mapCrs.isGeographic() || !formatGeographic )
{
// Work out a suitable number of decimal places for the coordinates with the aim of always
// having enough decimal places to show the difference in position between adjacent pixels.
// Also avoid taking the log of 0.
if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) )
dp = static_cast<int>( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) );
}
else
{
if ( format == QLatin1String( "D" ) )
dp = 4;
else
dp = 2;
}
}
else
dp = project->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) );
// Keep dp sensible
if ( dp < 0 )
dp = 0;
return dp;
}
QString QgsCoordinateUtils::formatCoordinateForProject( QgsProject *project, const QgsPointXY &point, const QgsCoordinateReferenceSystem &destCrs, int precision )
{
if ( !project )
return QString();
QString format = project->readEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DegreeFormat" ), QStringLiteral( "MU" ) );
QgsPointXY geo = point;
if ( format == QLatin1String( "DM" ) || format == QLatin1String( "DMS" ) || format == QLatin1String( "D" ) )
{
// degrees
if ( destCrs.isValid() && !destCrs.isGeographic() )
{
// need to transform to geographic coordinates
QgsCoordinateTransform ct( destCrs, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), project );
try
{
geo = ct.transform( point );
}
catch ( QgsCsException & )
{
return QString();
}
}
if ( format == QLatin1String( "DM" ) )
return QgsCoordinateFormatter::format( geo, QgsCoordinateFormatter::FormatDegreesMinutes, precision, QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds | QgsCoordinateFormatter::FlagDegreesUseStringSuffix );
else if ( format == QLatin1String( "DMS" ) )
return QgsCoordinateFormatter::format( geo, QgsCoordinateFormatter::FormatDegreesMinutesSeconds, precision, QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds | QgsCoordinateFormatter::FlagDegreesUseStringSuffix );
else
return QgsCoordinateFormatter::asPair( geo.x(), geo.y(), precision );
}
else
{
// coordinates in map units
return QgsCoordinateFormatter::asPair( point.x(), point.y(), precision );
}
}
double QgsCoordinateUtils::dmsToDecimal( const QString &string, bool *ok )
{
const QString negative( QStringLiteral( "swSW-" ) );
double value = 0.0;
bool okValue = false;
if ( ok )
{
*ok = false;
}
else
{
ok = &okValue;
}
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 );
QRegularExpressionMatch match = dms.match( string.trimmed() );
if ( match.hasMatch() )
{
QString dms1 = match.captured( 2 );
QString dms2 = match.captured( 3 );
QString dms3 = match.captured( 4 );
double v = dms3.toDouble( ok );
if ( *ok == false )
return value;
// Allow for Degrees/minutes format as well as DMS
if ( !dms2.isEmpty() )
{
v = dms2.toInt( ok ) + v / 60.0;
if ( *ok == false )
return value;
}
v = dms1.toInt( ok ) + v / 60.0;
if ( *ok == false )
return value;
QString sign1 = match.captured( 1 );
QString sign2 = match.captured( 5 );
if ( sign1.isEmpty() )
{
value = !sign2.isEmpty() && negative.contains( sign2 ) ? -v : v;
}
else if ( sign2.isEmpty() )
{
value = !sign1.isEmpty() && negative.contains( sign1 ) ? -v : v;
}
else
{
*ok = false;
}
}
return value;
}
///@endcond