Quantum GIS API Documentation  master-ce49b66
src/core/qgsscalecalculator.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               qgsscalecalculator.h
00003                  Calculates scale based on map extent and units
00004                               -------------------
00005   begin                : May 18, 2004
00006   copyright            : (C) 2004 by Gary E.Sherman
00007   email                : sherman at mrcc.com
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 
00019 #include <cmath>
00020 #include "qgslogger.h"
00021 #include "qgsrectangle.h"
00022 #include "qgsscalecalculator.h"
00023 
00024 QgsScaleCalculator::QgsScaleCalculator( double dpi, QGis::UnitType mapUnits )
00025     : mDpi( dpi ), mMapUnits( mapUnits )
00026 {}
00027 
00028 QgsScaleCalculator::~QgsScaleCalculator()
00029 {}
00030 
00031 void QgsScaleCalculator::setDpi( double dpi )
00032 {
00033   mDpi = dpi;
00034 }
00035 double QgsScaleCalculator::dpi()
00036 {
00037   return mDpi;
00038 }
00039 
00040 void QgsScaleCalculator::setMapUnits( QGis::UnitType mapUnits )
00041 {
00042   QgsDebugMsg( QString( "Map units set to %1" ).arg( QString::number( mapUnits ) ) );
00043   mMapUnits = mapUnits;
00044 }
00045 
00046 QGis::UnitType QgsScaleCalculator::mapUnits() const
00047 {
00048   QgsDebugMsgLevel( QString( "Map units returned as %1" ).arg( QString::number( mMapUnits ) ), 4 );
00049   return mMapUnits;
00050 }
00051 
00052 double QgsScaleCalculator::calculate( const QgsRectangle &mapExtent, int canvasWidth )
00053 {
00054   double conversionFactor = 0;
00055   double delta = 0;
00056   // calculation is based on the map units and extent, the dpi of the
00057   // users display, and the canvas width
00058   switch ( mMapUnits )
00059   {
00060     case QGis::Meters:
00061       // convert meters to inches
00062       conversionFactor = 39.3700787;
00063       delta = mapExtent.xMaximum() - mapExtent.xMinimum();
00064       break;
00065     case QGis::Feet:
00066       conversionFactor = 12.0;
00067       delta = mapExtent.xMaximum() - mapExtent.xMinimum();
00068       break;
00069 
00070     default:
00071     case QGis::Degrees:
00072       // degrees require conversion to meters first
00073       conversionFactor = 39.3700787;
00074       delta = calculateGeographicDistance( mapExtent );
00075       break;
00076   }
00077   if ( canvasWidth == 0 || mDpi == 0 )
00078   {
00079     QgsDebugMsg( "Can't calculate scale from the input values" );
00080     return 0;
00081   }
00082   double scale = ( delta * conversionFactor ) / (( double )canvasWidth / mDpi );
00083   QgsDebugMsg( QString( "scale = %1 conversionFactor = %2" ).arg( scale ).arg( conversionFactor ) );
00084   return scale;
00085 }
00086 
00087 
00088 double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapExtent )
00089 {
00090   // need to calculate the x distance in meters
00091   // We'll use the middle latitude for the calculation
00092   // Note this is an approximation (although very close) but calculating scale
00093   // for geographic data over large extents is quasi-meaningless
00094 
00095   // The distance between two points on a sphere can be estimated
00096   // using the Haversine formula. This gives the shortest distance
00097   // between two points on the sphere. However, what we're after is
00098   // the distance from the left of the given extent and the right of
00099   // it. This is not necessarily the shortest distance between two
00100   // points on a sphere.
00101   //
00102   // The code below uses the Haversine formula, but with some changes
00103   // to cope with the above problem, and also to deal with the extent
00104   // possibly extending beyond +/-180 degrees:
00105   //
00106   // - Use the Halversine formula to calculate the distance from -90 to
00107   //   +90 degrees at the mean latitude.
00108   // - Scale this distance by the number of degrees between
00109   //   mapExtent.xMinimum() and mapExtent.xMaximum();
00110   // - For a slight improvemnt, allow for the ellipsoid shape of earth.
00111 
00112 
00113   // For a longitude change of 180 degrees
00114   double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5;
00115   const static double rads = ( 4.0 * atan( 1.0 ) ) / 180.0;
00116   double a = pow( cos( lat * rads ), 2 );
00117   double c = 2.0 * atan2( sqrt( a ), sqrt( 1.0 - a ) );
00118   const static double ra = 6378000; // [m]
00119   // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set
00120   // to 6357000 m.
00121   const static double e = 0.0810820288;
00122   double radius = ra * ( 1.0 - e * e ) /
00123                   pow( 1.0 - e * e * sin( lat * rads ) * sin( lat * rads ), 1.5 );
00124   double meters = ( mapExtent.xMaximum() - mapExtent.xMinimum() ) / 180.0 * radius * c;
00125 
00126   QgsDebugMsg( "Distance across map extent (m): " + QString::number( meters ) );
00127 
00128   return meters;
00129 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines