QGIS API Documentation 4.1.0-Master (376402f9aeb)
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 "qgsellipsoidutils.h"
24#include "qgslogger.h"
25#include "qgsrectangle.h"
26#include "qgsunittypes.h"
27
28#include <QSizeF>
29#include <QString>
30
31using namespace Qt::StringLiterals;
32
34 : mDpi( dpi )
35 , mMapUnits( mapUnits )
36{
37 mEllipsoidDefinition.acronym = "PARAMETER:6378000:6357000"_L1;
38 mEllipsoidDefinition.parameters = QgsEllipsoidUtils::ellipsoidParameters( mEllipsoidDefinition.acronym );
39}
40
45
47{
48 mDpi = dpi;
49}
51{
52 return mDpi;
53}
54
56{
57 QgsDebugMsgLevel( u"Map units set to %1"_s.arg( qgsEnumValueToKey( mapUnits ) ), 3 );
58 mMapUnits = mapUnits;
59}
60
62{
63 QgsDebugMsgLevel( u"Map units returned as %1"_s.arg( qgsEnumValueToKey( mMapUnits ) ), 4 );
64 return mMapUnits;
65}
66
68{
69 mEllipsoidDefinition.acronym = ellipsoid;
70 mEllipsoidDefinition.parameters = QgsEllipsoidUtils::ellipsoidParameters( ellipsoid );
71
72 // If the ellipsoid parameters are not valid, set them to the approximate values for the WGS84
73 if ( !mEllipsoidDefinition.parameters.valid )
74 {
75 mEllipsoidDefinition.acronym = "PARAMETER:6378000:6357000"_L1;
76 mEllipsoidDefinition.parameters = QgsEllipsoidUtils::ellipsoidParameters( mEllipsoidDefinition.acronym );
77 }
78}
79
80
81double QgsScaleCalculator::calculate( const QgsRectangle &mapExtent, double canvasWidth ) const
82{
83 if ( qgsDoubleNear( canvasWidth, 0. ) || qgsDoubleNear( mDpi, 0.0 ) )
84 {
85 QgsDebugError( u"Can't calculate scale from the input values"_s );
86 return 0;
87 }
88
89 double conversionFactor = 0;
90 double delta = 0;
91 calculateMetrics( mapExtent, delta, conversionFactor );
92
93 const double scale = ( delta * conversionFactor ) / ( static_cast< double >( canvasWidth ) / mDpi );
94 QgsDebugMsgLevel( u"scale = %1 conversionFactor = %2"_s.arg( scale ).arg( conversionFactor ), 4 );
95 return scale;
96}
97
98QSizeF QgsScaleCalculator::calculateImageSize( const QgsRectangle &mapExtent, double scale ) const
99{
100 if ( qgsDoubleNear( scale, 0.0 ) || qgsDoubleNear( mDpi, 0.0 ) )
101 {
102 QgsDebugError( u"Can't calculate image size from the input values"_s );
103 return QSizeF();
104 }
105 double conversionFactor = 0;
106 double delta = 0;
107
108 calculateMetrics( mapExtent, delta, conversionFactor );
109 const double imageWidth = ( delta * conversionFactor ) / ( static_cast< double >( scale ) ) * mDpi;
110 const double deltaHeight = ( mapExtent.yMaximum() - mapExtent.yMinimum() ) * delta / ( mapExtent.xMaximum() - mapExtent.xMinimum() );
111 const double imageHeight = ( deltaHeight * conversionFactor ) / ( static_cast< double >( scale ) ) * mDpi;
112
113 QgsDebugMsgLevel( u"imageWidth = %1 imageHeight = %2 conversionFactor = %3"_s.arg( imageWidth ).arg( imageHeight ).arg( conversionFactor ), 4 );
114
115 return QSizeF( imageWidth, imageHeight );
116}
117
118void QgsScaleCalculator::calculateMetrics( const QgsRectangle &mapExtent, double &delta, double &conversionFactor ) const
119{
120 delta = mapExtent.xMaximum() - mapExtent.xMinimum();
121
122 switch ( mMapUnits )
123 {
125 conversionFactor = 1;
126 break;
127
175 // convert to inches
176 conversionFactor = QgsUnitTypes::fromUnitToUnitFactor( mMapUnits, Qgis::DistanceUnit::Inches );
177 break;
178
180 // assume degrees to maintain old API
181 [[fallthrough]];
182
184 // degrees require conversion to meters first
185 conversionFactor = 39.3700787;
186 delta = calculateGeographicDistance( mapExtent );
187 break;
188 }
189}
190
192{
193 switch ( mMethod )
194 {
196 return calculateGeographicDistanceAtLatitude( mapExtent.yMaximum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
197
199 return calculateGeographicDistanceAtLatitude( ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5, mapExtent.xMinimum(), mapExtent.xMaximum() );
200
202 return calculateGeographicDistanceAtLatitude( mapExtent.yMinimum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
203
205 {
206 const double dTop = calculateGeographicDistanceAtLatitude( mapExtent.yMaximum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
207 const double dMiddle = calculateGeographicDistanceAtLatitude( ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5, mapExtent.xMinimum(), mapExtent.xMaximum() );
208 const double dBottom = calculateGeographicDistanceAtLatitude( mapExtent.yMinimum(), mapExtent.xMinimum(), mapExtent.xMaximum() );
209 return ( dTop + dMiddle + dBottom ) / 3.0;
210 }
211
213 return calculateGeographicDistanceAtLatitude( 0, mapExtent.xMinimum(), mapExtent.xMaximum() );
214 }
215 // unreachable!
216 return 0;
217}
218
219double QgsScaleCalculator::calculateGeographicDistanceAtLatitude( double lat, double longitude1, double longitude2 ) const
220{
221 // need to calculate the x distance in meters
222
223 // Note this is an approximation (although very close) but calculating scale
224 // for geographic data over large extents is quasi-meaningless
225
226 // The distance between two points on a sphere can be estimated
227 // using the Haversine formula. This gives the shortest distance
228 // between two points on the sphere. However, what we're after is
229 // the distance from the left of the given extent and the right of
230 // it. This is not necessarily the shortest distance between two
231 // points on a sphere.
232 //
233 // The code below uses the Haversine formula, but with some changes
234 // to cope with the above problem, and also to deal with the extent
235 // possibly extending beyond +/-180 degrees:
236 //
237 // - Use the Halversine formula to calculate the distance from -90 to
238 // +90 degrees at desired latitude.
239 // - Scale this distance by the number of degrees between
240 // the two longitudes
241
242 const double semiMajor = mEllipsoidDefinition.parameters.semiMajor;
243 const double semiMinor = mEllipsoidDefinition.parameters.semiMinor;
244
245 // For a longitude change of 180 degrees
246 static const double RADS = ( 4.0 * std::atan( 1.0 ) ) / 180.0;
247 const double a = std::pow( std::cos( lat * RADS ), 2 );
248 const double c = 2.0 * std::atan2( std::sqrt( a ), std::sqrt( 1.0 - a ) );
249 // The eccentricity, derived from sqrt( (a*a-b*b)/(a*a))
250 const double E = std::sqrt( ( semiMajor * semiMajor - semiMinor * semiMinor ) / ( semiMajor * semiMajor ) );
251 const double radius = semiMajor * ( 1.0 - E * E ) / std::pow( 1.0 - E * E * std::sin( lat * RADS ) * std::sin( lat * RADS ), 1.5 );
252 const double meters = ( longitude2 - longitude1 ) / 180.0 * radius * c;
253
254 QgsDebugMsgLevel( "Distance across map extent (m): " + QString::number( meters ), 4 );
255
256 return meters;
257}
DistanceUnit
Units of distance.
Definition qgis.h:5326
@ YardsBritishSears1922Truncated
British yards (Sears 1922 truncated).
Definition qgis.h:5366
@ Feet
Imperial feet.
Definition qgis.h:5329
@ MilesUSSurvey
US Survey miles.
Definition qgis.h:5373
@ LinksBritishSears1922
British links (Sears 1922).
Definition qgis.h:5361
@ YardsBritishBenoit1895A
British yards (Benoit 1895 A).
Definition qgis.h:5364
@ LinksBritishBenoit1895A
British links (Benoit 1895 A).
Definition qgis.h:5358
@ Centimeters
Centimeters.
Definition qgis.h:5334
@ YardsIndian1975
Indian yards (1975).
Definition qgis.h:5372
@ FeetUSSurvey
US Survey feet.
Definition qgis.h:5356
@ Millimeters
Millimeters.
Definition qgis.h:5335
@ FeetBritishSears1922
British feet (Sears 1922).
Definition qgis.h:5349
@ YardsClarkes
Clarke's yards.
Definition qgis.h:5368
@ YardsIndian
Indian yards.
Definition qgis.h:5369
@ FeetBritishBenoit1895B
British feet (Benoit 1895 B).
Definition qgis.h:5347
@ Miles
Terrestrial miles.
Definition qgis.h:5332
@ LinksUSSurvey
US Survey links.
Definition qgis.h:5363
@ Meters
Meters.
Definition qgis.h:5327
@ ChainsUSSurvey
US Survey chains.
Definition qgis.h:5343
@ FeetClarkes
Clarke's feet.
Definition qgis.h:5350
@ Unknown
Unknown distance unit.
Definition qgis.h:5376
@ Yards
Imperial yards.
Definition qgis.h:5331
@ FeetBritish1936
British feet (1936).
Definition qgis.h:5345
@ FeetIndian1962
Indian feet (1962).
Definition qgis.h:5354
@ YardsBritishSears1922
British yards (Sears 1922).
Definition qgis.h:5367
@ FeetIndian1937
Indian feet (1937).
Definition qgis.h:5353
@ YardsIndian1937
Indian yards (1937).
Definition qgis.h:5370
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5333
@ ChainsBritishBenoit1895B
British chains (Benoit 1895 B).
Definition qgis.h:5339
@ LinksBritishSears1922Truncated
British links (Sears 1922 truncated).
Definition qgis.h:5360
@ ChainsBritishBenoit1895A
British chains (Benoit 1895 A).
Definition qgis.h:5338
@ YardsBritishBenoit1895B
British yards (Benoit 1895 B).
Definition qgis.h:5365
@ FeetBritish1865
British feet (1865).
Definition qgis.h:5344
@ YardsIndian1962
Indian yards (1962).
Definition qgis.h:5371
@ FeetBritishSears1922Truncated
British feet (Sears 1922 truncated).
Definition qgis.h:5348
@ MetersGermanLegal
German legal meter.
Definition qgis.h:5375
@ LinksBritishBenoit1895B
British links (Benoit 1895 B).
Definition qgis.h:5359
@ ChainsInternational
International chains.
Definition qgis.h:5337
@ Inches
Inches.
Definition qgis.h:5336
@ Fathoms
Fathoms.
Definition qgis.h:5374
@ LinksInternational
International links.
Definition qgis.h:5357
@ ChainsBritishSears1922Truncated
British chains (Sears 1922 truncated).
Definition qgis.h:5340
@ FeetIndian
Indian (geodetic) feet.
Definition qgis.h:5352
@ NauticalMiles
Nautical miles.
Definition qgis.h:5330
@ ChainsClarkes
Clarke's chains.
Definition qgis.h:5342
@ LinksClarkes
Clarke's links.
Definition qgis.h:5362
@ ChainsBritishSears1922
British chains (Sears 1922).
Definition qgis.h:5341
@ Kilometers
Kilometers.
Definition qgis.h:5328
@ FeetIndian1975
Indian feet (1975).
Definition qgis.h:5355
@ FeetGoldCoast
Gold Coast feet.
Definition qgis.h:5351
@ FeetBritishBenoit1895A
British feet (Benoit 1895 A).
Definition qgis.h:5346
ScaleCalculationMethod
Scale calculation logic.
Definition qgis.h:5603
@ HorizontalTop
Calculate horizontally, across top of map.
Definition qgis.h:5604
@ HorizontalMiddle
Calculate horizontally, across midle of map.
Definition qgis.h:5605
@ AtEquator
Always calculate the scale at the equator, regardless of the actual visible map extent....
Definition qgis.h:5608
@ HorizontalAverage
Calculate horizontally, using the average of the top, middle and bottom scales.
Definition qgis.h:5607
@ HorizontalBottom
Calculate horizontally, across bottom of map.
Definition qgis.h:5606
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
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.
QString ellipsoid() const
Returns ellipsoid's acronym.
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...
void setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
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:7316
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7134
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59