QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsscalecalculator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsscalecalculator.h
3 Calculates scale based on map extent and units
4 -------------------
5 begin : May 18, 2004
6 copyright : (C) 2004 by Gary E.Sherman
7 email : sherman at mrcc.com
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgsscalecalculator.h"
20
21#include <cmath>
22
23#include "qgslogger.h"
24#include "qgsrectangle.h"
25#include "qgsunittypes.h"
26
27#include <QSizeF>
28#include <QString>
29
30using namespace Qt::StringLiterals;
31
36
41
43{
44 mDpi = dpi;
45}
47{
48 return mDpi;
49}
50
52{
53 QgsDebugMsgLevel( u"Map units set to %1"_s.arg( qgsEnumValueToKey( mapUnits ) ), 3 );
54 mMapUnits = mapUnits;
55}
56
58{
59 QgsDebugMsgLevel( u"Map units returned as %1"_s.arg( qgsEnumValueToKey( mMapUnits ) ), 4 );
60 return mMapUnits;
61}
62
63double QgsScaleCalculator::calculate( const QgsRectangle &mapExtent, double canvasWidth ) const
64{
65 if ( qgsDoubleNear( canvasWidth, 0. ) || qgsDoubleNear( mDpi, 0.0 ) )
66 {
67 QgsDebugError( u"Can't calculate scale from the input values"_s );
68 return 0;
69 }
70
71 double conversionFactor = 0;
72 double delta = 0;
73 calculateMetrics( mapExtent, delta, conversionFactor );
74
75 const double scale = ( delta * conversionFactor ) / ( static_cast< double >( canvasWidth ) / mDpi );
76 QgsDebugMsgLevel( u"scale = %1 conversionFactor = %2"_s.arg( scale ).arg( conversionFactor ), 4 );
77 return scale;
78}
79
80QSizeF QgsScaleCalculator::calculateImageSize( const QgsRectangle &mapExtent, double scale ) const
81{
82 if ( qgsDoubleNear( scale, 0.0 ) || qgsDoubleNear( mDpi, 0.0 ) )
83 {
84 QgsDebugError( u"Can't calculate image size from the input values"_s );
85 return QSizeF();
86 }
87 double conversionFactor = 0;
88 double delta = 0;
89
90 calculateMetrics( mapExtent, delta, conversionFactor );
91 const double imageWidth = ( delta * conversionFactor ) / ( static_cast< double >( scale ) ) * mDpi;
92 const double deltaHeight = ( mapExtent.yMaximum() - mapExtent.yMinimum() ) * delta / ( mapExtent.xMaximum() - mapExtent.xMinimum() );
93 const double imageHeight = ( deltaHeight * conversionFactor ) / ( static_cast< double >( scale ) ) * mDpi;
94
95 QgsDebugMsgLevel( u"imageWidth = %1 imageHeight = %2 conversionFactor = %3"_s.arg( imageWidth ).arg( imageHeight ).arg( conversionFactor ), 4 );
96
97 return QSizeF( imageWidth, imageHeight );
98}
99
100void QgsScaleCalculator::calculateMetrics( const QgsRectangle &mapExtent, double &delta, double &conversionFactor ) const
101{
102 delta = mapExtent.xMaximum() - mapExtent.xMinimum();
103
104 switch ( mMapUnits )
105 {
107 conversionFactor = 1;
108 break;
109
157 // convert to inches
158 conversionFactor = QgsUnitTypes::fromUnitToUnitFactor( mMapUnits, Qgis::DistanceUnit::Inches );
159 break;
160
162 // assume degrees to maintain old API
163 [[fallthrough]];
164
166 // degrees require conversion to meters first
167 conversionFactor = 39.3700787;
168 delta = calculateGeographicDistance( mapExtent );
169 break;
170 }
171}
172
174{
175 switch ( mMethod )
176 {
178 return calculateGeographicDistanceAtLatitude( mapExtent.yMaximum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
179
181 return calculateGeographicDistanceAtLatitude( ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5, mapExtent.xMinimum(), mapExtent.xMaximum() );
182
184 return calculateGeographicDistanceAtLatitude( mapExtent.yMinimum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
185
187 {
188 const double dTop = calculateGeographicDistanceAtLatitude( mapExtent.yMaximum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
189 const double dMiddle = calculateGeographicDistanceAtLatitude( ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5, mapExtent.xMinimum(), mapExtent.xMaximum() );
190 const double dBottom = calculateGeographicDistanceAtLatitude( mapExtent.yMinimum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
191 return ( dTop + dMiddle + dBottom ) / 3.0;
192 }
193
195 return calculateGeographicDistanceAtLatitude( 0, mapExtent.xMinimum(), mapExtent.xMaximum() );
196 }
197 // unreachable!
198 return 0;
199}
200
201double QgsScaleCalculator::calculateGeographicDistanceAtLatitude( double lat, double longitude1, double longitude2 ) const
202{
203 // need to calculate the x distance in meters
204
205 // Note this is an approximation (although very close) but calculating scale
206 // for geographic data over large extents is quasi-meaningless
207
208 // The distance between two points on a sphere can be estimated
209 // using the Haversine formula. This gives the shortest distance
210 // between two points on the sphere. However, what we're after is
211 // the distance from the left of the given extent and the right of
212 // it. This is not necessarily the shortest distance between two
213 // points on a sphere.
214 //
215 // The code below uses the Haversine formula, but with some changes
216 // to cope with the above problem, and also to deal with the extent
217 // possibly extending beyond +/-180 degrees:
218 //
219 // - Use the Halversine formula to calculate the distance from -90 to
220 // +90 degrees at desired latitude.
221 // - Scale this distance by the number of degrees between
222 // the two longitudes
223
224
225 // - TODO: respect the actual ellipsoid parameters!!
226
227 // For a longitude change of 180 degrees
228 static const double RADS = ( 4.0 * std::atan( 1.0 ) ) / 180.0;
229 const double a = std::pow( std::cos( lat * RADS ), 2 );
230 const double c = 2.0 * std::atan2( std::sqrt( a ), std::sqrt( 1.0 - a ) );
231 static const double RA = 6378000; // [m]
232 // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set
233 // to 6357000 m.
234 static const double E = 0.0810820288;
235 const double radius = RA * ( 1.0 - E * E ) / std::pow( 1.0 - E * E * std::sin( lat * RADS ) * std::sin( lat * RADS ), 1.5 );
236 const double meters = ( longitude2 - longitude1 ) / 180.0 * radius * c;
237
238 QgsDebugMsgLevel( "Distance across map extent (m): " + QString::number( meters ), 4 );
239
240 return meters;
241}
DistanceUnit
Units of distance.
Definition qgis.h:5170
@ YardsBritishSears1922Truncated
British yards (Sears 1922 truncated).
Definition qgis.h:5210
@ Feet
Imperial feet.
Definition qgis.h:5173
@ MilesUSSurvey
US Survey miles.
Definition qgis.h:5217
@ LinksBritishSears1922
British links (Sears 1922).
Definition qgis.h:5205
@ YardsBritishBenoit1895A
British yards (Benoit 1895 A).
Definition qgis.h:5208
@ LinksBritishBenoit1895A
British links (Benoit 1895 A).
Definition qgis.h:5202
@ Centimeters
Centimeters.
Definition qgis.h:5178
@ YardsIndian1975
Indian yards (1975).
Definition qgis.h:5216
@ FeetUSSurvey
US Survey feet.
Definition qgis.h:5200
@ Millimeters
Millimeters.
Definition qgis.h:5179
@ FeetBritishSears1922
British feet (Sears 1922).
Definition qgis.h:5193
@ YardsClarkes
Clarke's yards.
Definition qgis.h:5212
@ YardsIndian
Indian yards.
Definition qgis.h:5213
@ FeetBritishBenoit1895B
British feet (Benoit 1895 B).
Definition qgis.h:5191
@ Miles
Terrestrial miles.
Definition qgis.h:5176
@ LinksUSSurvey
US Survey links.
Definition qgis.h:5207
@ Meters
Meters.
Definition qgis.h:5171
@ ChainsUSSurvey
US Survey chains.
Definition qgis.h:5187
@ FeetClarkes
Clarke's feet.
Definition qgis.h:5194
@ Unknown
Unknown distance unit.
Definition qgis.h:5220
@ Yards
Imperial yards.
Definition qgis.h:5175
@ FeetBritish1936
British feet (1936).
Definition qgis.h:5189
@ FeetIndian1962
Indian feet (1962).
Definition qgis.h:5198
@ YardsBritishSears1922
British yards (Sears 1922).
Definition qgis.h:5211
@ FeetIndian1937
Indian feet (1937).
Definition qgis.h:5197
@ YardsIndian1937
Indian yards (1937).
Definition qgis.h:5214
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5177
@ ChainsBritishBenoit1895B
British chains (Benoit 1895 B).
Definition qgis.h:5183
@ LinksBritishSears1922Truncated
British links (Sears 1922 truncated).
Definition qgis.h:5204
@ ChainsBritishBenoit1895A
British chains (Benoit 1895 A).
Definition qgis.h:5182
@ YardsBritishBenoit1895B
British yards (Benoit 1895 B).
Definition qgis.h:5209
@ FeetBritish1865
British feet (1865).
Definition qgis.h:5188
@ YardsIndian1962
Indian yards (1962).
Definition qgis.h:5215
@ FeetBritishSears1922Truncated
British feet (Sears 1922 truncated).
Definition qgis.h:5192
@ MetersGermanLegal
German legal meter.
Definition qgis.h:5219
@ LinksBritishBenoit1895B
British links (Benoit 1895 B).
Definition qgis.h:5203
@ ChainsInternational
International chains.
Definition qgis.h:5181
@ Inches
Inches.
Definition qgis.h:5180
@ Fathoms
Fathoms.
Definition qgis.h:5218
@ LinksInternational
International links.
Definition qgis.h:5201
@ ChainsBritishSears1922Truncated
British chains (Sears 1922 truncated).
Definition qgis.h:5184
@ FeetIndian
Indian (geodetic) feet.
Definition qgis.h:5196
@ NauticalMiles
Nautical miles.
Definition qgis.h:5174
@ ChainsClarkes
Clarke's chains.
Definition qgis.h:5186
@ LinksClarkes
Clarke's links.
Definition qgis.h:5206
@ ChainsBritishSears1922
British chains (Sears 1922).
Definition qgis.h:5185
@ Kilometers
Kilometers.
Definition qgis.h:5172
@ FeetIndian1975
Indian feet (1975).
Definition qgis.h:5199
@ FeetGoldCoast
Gold Coast feet.
Definition qgis.h:5195
@ FeetBritishBenoit1895A
British feet (Benoit 1895 A).
Definition qgis.h:5190
ScaleCalculationMethod
Scale calculation logic.
Definition qgis.h:5447
@ HorizontalTop
Calculate horizontally, across top of map.
Definition qgis.h:5448
@ HorizontalMiddle
Calculate horizontally, across midle of map.
Definition qgis.h:5449
@ AtEquator
Always calculate the scale at the equator, regardless of the actual visible map extent....
Definition qgis.h:5452
@ HorizontalAverage
Calculate horizontally, using the average of the top, middle and bottom scales.
Definition qgis.h:5451
@ HorizontalBottom
Calculate horizontally, across bottom of map.
Definition qgis.h:5450
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
double calculate(const QgsRectangle &mapExtent, double canvasWidth) const
Calculate the scale denominator.
void setDpi(double dpi)
Sets the dpi (dots per inch) for the output resolution, to be used in scale calculations.
void setMapUnits(Qgis::DistanceUnit mapUnits)
Set the map units.
double calculateGeographicDistance(const QgsRectangle &mapExtent) const
Calculate the distance in meters, horizontally across the specified map extent (in geographic coordin...
QSizeF calculateImageSize(const QgsRectangle &mapExtent, double scale) const
Calculate the image size in pixel (physical) units.
Qgis::ScaleCalculationMethod method() const
Returns the method to use for map scale calculations.
double dpi() const
Returns the DPI (dots per inch) used in scale calculations.
Qgis::DistanceUnit mapUnits() const
Returns current map units.
void setMethod(Qgis::ScaleCalculationMethod method)
Sets the method to use for map scale calculations.
QgsScaleCalculator(double dpi=0, Qgis::DistanceUnit mapUnits=Qgis::DistanceUnit::Meters)
Constructor.
double calculateGeographicDistanceAtLatitude(double latitude, double longitude1, double longitude2) const
Calculate the distance in meters, horizontally between two longitudes at a specified latitude.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7157
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
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59