QGIS API Documentation  2.14.0-Essen
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 <cmath>
20 #include "qgslogger.h"
21 #include "qgsrectangle.h"
22 #include "qgsscalecalculator.h"
23 
25  : mDpi( dpi ), mMapUnits( mapUnits )
26 {}
27 
29 {
30  mDpi = dpi;
31 }
33 {
34  return mDpi;
35 }
36 
38 {
39  QgsDebugMsg( QString( "Map units set to %1" ).arg( QString::number( mapUnits ) ) );
40  mMapUnits = mapUnits;
41 }
42 
44 {
45  QgsDebugMsgLevel( QString( "Map units returned as %1" ).arg( QString::number( mMapUnits ) ), 4 );
46  return mMapUnits;
47 }
48 
49 double QgsScaleCalculator::calculate( const QgsRectangle &mapExtent, int canvasWidth )
50 {
51  double conversionFactor = 0;
52  double delta = 0;
53  // calculation is based on the map units and extent, the dpi of the
54  // users display, and the canvas width
55  switch ( mMapUnits )
56  {
57  case QGis::Meters:
58  // convert meters to inches
59  conversionFactor = 39.3700787;
60  delta = mapExtent.xMaximum() - mapExtent.xMinimum();
61  break;
62  case QGis::Feet:
63  conversionFactor = 12.0;
64  delta = mapExtent.xMaximum() - mapExtent.xMinimum();
65  break;
67  // convert nautical miles to inches
68  conversionFactor = 72913.4;
69  delta = mapExtent.xMaximum() - mapExtent.xMinimum();
70  break;
71 
72  default:
73  case QGis::Degrees:
74  // degrees require conversion to meters first
75  conversionFactor = 39.3700787;
76  delta = calculateGeographicDistance( mapExtent );
77  break;
78  }
79  if ( canvasWidth == 0 || qgsDoubleNear( mDpi, 0.0 ) )
80  {
81  QgsDebugMsg( "Can't calculate scale from the input values" );
82  return 0;
83  }
84  double scale = ( delta * conversionFactor ) / ( static_cast< double >( canvasWidth ) / mDpi );
85  QgsDebugMsg( QString( "scale = %1 conversionFactor = %2" ).arg( scale ).arg( conversionFactor ) );
86  return scale;
87 }
88 
89 
91 {
92  // need to calculate the x distance in meters
93  // We'll use the middle latitude for the calculation
94  // Note this is an approximation (although very close) but calculating scale
95  // for geographic data over large extents is quasi-meaningless
96 
97  // The distance between two points on a sphere can be estimated
98  // using the Haversine formula. This gives the shortest distance
99  // between two points on the sphere. However, what we're after is
100  // the distance from the left of the given extent and the right of
101  // it. This is not necessarily the shortest distance between two
102  // points on a sphere.
103  //
104  // The code below uses the Haversine formula, but with some changes
105  // to cope with the above problem, and also to deal with the extent
106  // possibly extending beyond +/-180 degrees:
107  //
108  // - Use the Halversine formula to calculate the distance from -90 to
109  // +90 degrees at the mean latitude.
110  // - Scale this distance by the number of degrees between
111  // mapExtent.xMinimum() and mapExtent.xMaximum();
112  // - For a slight improvemnt, allow for the ellipsoid shape of earth.
113 
114 
115  // For a longitude change of 180 degrees
116  double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5;
117  const static double rads = ( 4.0 * atan( 1.0 ) ) / 180.0;
118  double a = pow( cos( lat * rads ), 2 );
119  double c = 2.0 * atan2( sqrt( a ), sqrt( 1.0 - a ) );
120  const static double ra = 6378000; // [m]
121  // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set
122  // to 6357000 m.
123  const static double e = 0.0810820288;
124  double radius = ra * ( 1.0 - e * e ) /
125  pow( 1.0 - e * e * sin( lat * rads ) * sin( lat * rads ), 1.5 );
126  double meters = ( mapExtent.xMaximum() - mapExtent.xMinimum() ) / 180.0 * radius * c;
127 
128  QgsDebugMsg( "Distance across map extent (m): " + QString::number( meters ) );
129 
130  return meters;
131 }
void setMapUnits(QGis::UnitType mapUnits)
Set the map units.
double calculateGeographicDistance(const QgsRectangle &mapExtent)
Calculate the distance between two points in geographic coordinates.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDpi(double dpi)
Set the dpi to be used in scale calculations.
QGis::UnitType mapUnits() const
Returns current map units.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
QString number(int n, int base)
double calculate(const QgsRectangle &mapExtent, int canvasWidth)
Calculate the scale denominator.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:187
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
double dpi()
Accessor for dpi used in scale calculations.
QgsScaleCalculator(double dpi=0, QGis::UnitType mapUnits=QGis::Meters)
Constructor.
UnitType
Map units that qgis supports.
Definition: qgis.h:155
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:192