QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsmapsettings.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapsettings.cpp
3  --------------------------------------
4  Date : December 2013
5  Copyright : (C) 2013 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmapsettings.h"
17 
18 #include "qgsscalecalculator.h"
19 #include "qgsmaprendererjob.h"
20 #include "qgsmaptopixel.h"
21 #include "qgslogger.h"
22 
23 #include "qgscrscache.h"
24 #include "qgsmessagelog.h"
25 #include "qgsmaplayer.h"
26 #include "qgsmaplayerregistry.h"
27 #include "qgsxmlutils.h"
28 
29 
30 Q_GUI_EXPORT extern int qt_defaultDpiX();
31 
32 
34  : mDpi( qt_defaultDpiX() ) // DPI that will be used by default for QImage instances
35  , mSize( QSize( 0, 0 ) )
36  , mExtent()
37  , mProjectionsEnabled( false )
38  , mDestCRS( GEOCRS_ID, QgsCoordinateReferenceSystem::InternalCrsId ) // WGS 84
39  , mDatumTransformStore( mDestCRS )
40  , mBackgroundColor( Qt::white )
41  , mSelectionColor( Qt::yellow )
42  , mFlags( Antialiasing | UseAdvancedEffects | DrawLabeling | DrawSelection )
43  , mImageFormat( QImage::Format_ARGB32_Premultiplied )
44 {
45  updateDerived();
46 
47  // set default map units - we use WGS 84 thus use degrees
49 }
50 
51 
53 {
54  return mExtent;
55 }
56 
58 {
59  mExtent = extent;
60 
61  updateDerived();
62 }
63 
64 
66 {
68 
69  if ( extent.isEmpty() || !extent.isFinite() )
70  {
71  mValid = false;
72  return;
73  }
74 
75  // Don't allow zooms where the current extent is so small that it
76  // can't be accurately represented using a double (which is what
77  // currentExtent uses). Excluding 0 avoids a divide by zero and an
78  // infinite loop when rendering to a new canvas. Excluding extents
79  // greater than 1 avoids doing unnecessary calculations.
80 
81  // The scheme is to compare the width against the mean x coordinate
82  // (and height against mean y coordinate) and only allow zooms where
83  // the ratio indicates that there is more than about 12 significant
84  // figures (there are about 16 significant figures in a double).
85 
86  if ( extent.width() > 0 &&
87  extent.height() > 0 &&
88  extent.width() < 1 &&
89  extent.height() < 1 )
90  {
91  // Use abs() on the extent to avoid the case where the extent is
92  // symmetrical about 0.
93  double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5;
94  double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5;
95 
96  double xRange = extent.width() / xMean;
97  double yRange = extent.height() / yMean;
98 
99  static const double minProportion = 1e-12;
100  if ( xRange < minProportion || yRange < minProportion )
101  {
102  mValid = false;
103  return;
104  }
105  }
106 
107  double myHeight = mSize.height();
108  double myWidth = mSize.width();
109 
110  if ( !myWidth || !myHeight )
111  {
112  mValid = false;
113  return;
114  }
115 
116  // calculate the translation and scaling parameters
117  double mapUnitsPerPixelY = mExtent.height() / myHeight;
118  double mapUnitsPerPixelX = mExtent.width() / myWidth;
119  mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
120 
121  // calculate the actual extent of the mapCanvas
122  double dxmin = mExtent.xMinimum(), dxmax = mExtent.xMaximum(),
123  dymin = mExtent.yMinimum(), dymax = mExtent.yMaximum(), whitespace;
124 
125  if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
126  {
127  whitespace = (( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5;
128  dxmin -= whitespace;
129  dxmax += whitespace;
130  }
131  else
132  {
133  whitespace = (( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5;
134  dymin -= whitespace;
135  dymax += whitespace;
136  }
137 
138  mVisibleExtent.set( dxmin, dymin, dxmax, dymax );
139 
140  // update the scale
143 
144  mMapToPixel = QgsMapToPixel( mapUnitsPerPixel(), outputSize().height(), visibleExtent().yMinimum(), visibleExtent().xMinimum() );
145 
146  QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mapUnitsPerPixelX ) ).arg( qgsDoubleToString( mapUnitsPerPixelY ) ) );
147  QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( myWidth ) ).arg( qgsDoubleToString( myHeight ) ) );
148  QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mExtent.width() ) ).arg( qgsDoubleToString( mExtent.height() ) ) );
150  QgsDebugMsg( QString( "Adjusted map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / myWidth ) ).arg( qgsDoubleToString( mVisibleExtent.height() / myHeight ) ) );
151  QgsDebugMsg( QString( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / mMapUnitsPerPixel ) ).arg( qgsDoubleToString( mVisibleExtent.height() / mMapUnitsPerPixel ) ) );
152  QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mScale ) ) );
153 
154  mValid = true;
155 }
156 
157 
159 {
160  return mSize;
161 }
162 
164 {
165  mSize = size;
166 
167  updateDerived();
168 }
169 
171 {
172  return mDpi;
173 }
174 
176 {
177  mDpi = dpi;
178 
179  updateDerived();
180 }
181 
182 
183 QStringList QgsMapSettings::layers() const
184 {
185  return mLayers;
186 }
187 
188 void QgsMapSettings::setLayers( const QStringList& layers )
189 {
190  mLayers = layers;
191 }
192 
194 {
195  mProjectionsEnabled = enabled;
196 }
197 
199 {
200  return mProjectionsEnabled;
201 }
202 
203 
205 {
206  mDestCRS = crs;
208 }
209 
211 {
212  return mDestCRS;
213 }
214 
215 
217 {
219 
220  // Since the map units have changed, force a recalculation of the scale.
221  updateDerived();
222 }
223 
224 void QgsMapSettings::setFlags( QgsMapSettings::Flags flags )
225 {
226  mFlags = flags;
227 }
228 
230 {
231  if ( on )
232  mFlags |= flag;
233  else
234  mFlags &= ~flag;
235 }
236 
237 QgsMapSettings::Flags QgsMapSettings::flags() const
238 {
239  return mFlags;
240 }
241 
243 {
244  return mFlags.testFlag( flag );
245 }
246 
248 {
249  return mScaleCalculator.mapUnits();
250 }
251 
252 
254 {
255  return mValid;
256 }
257 
259 {
260  return mVisibleExtent;
261 }
262 
264 {
265  return mMapUnitsPerPixel;
266 }
267 
268 double QgsMapSettings::scale() const
269 {
270  return mScale;
271 }
272 
273 
274 
275 
276 
278 {
279  return mDatumTransformStore.transformation( layer );
280 }
281 
282 
283 
285 {
286  if ( hasCrsTransformEnabled() )
287  {
288  try
289  {
290  if ( const QgsCoordinateTransform* ct = layerTransfrom( theLayer ) )
291  {
292  QgsDebugMsg( QString( "sourceCrs = " + ct->sourceCrs().authid() ) );
293  QgsDebugMsg( QString( "destCRS = " + ct->destCRS().authid() ) );
294  QgsDebugMsg( QString( "extent = " + extent.toString() ) );
295  extent = ct->transformBoundingBox( extent );
296  }
297  }
298  catch ( QgsCsException &cse )
299  {
300  QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" );
301  }
302  }
303 
304  QgsDebugMsg( QString( "proj extent = " + extent.toString() ) );
305 
306  return extent;
307 }
308 
309 
311 {
312  if ( hasCrsTransformEnabled() )
313  {
314  try
315  {
316  if ( const QgsCoordinateTransform* ct = layerTransfrom( theLayer ) )
317  {
318  QgsDebugMsg( QString( "sourceCrs = " + ct->sourceCrs().authid() ) );
319  QgsDebugMsg( QString( "destCRS = " + ct->destCRS().authid() ) );
320  QgsDebugMsg( QString( "extent = " + extent.toString() ) );
321  extent = ct->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
322  }
323  }
324  catch ( QgsCsException &cse )
325  {
326  QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" );
327  }
328  }
329 
330  QgsDebugMsg( QString( "proj extent = " + extent.toString() ) );
331 
332  return extent;
333 }
334 
335 
337 {
338  if ( hasCrsTransformEnabled() )
339  {
340  try
341  {
342  if ( const QgsCoordinateTransform* ct = layerTransfrom( theLayer ) )
343  point = ct->transform( point, QgsCoordinateTransform::ForwardTransform );
344  }
345  catch ( QgsCsException &cse )
346  {
347  QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" );
348  }
349  }
350  else
351  {
352  // leave point without transformation
353  }
354  return point;
355 }
356 
357 
359 {
360  if ( hasCrsTransformEnabled() )
361  {
362  try
363  {
364  if ( const QgsCoordinateTransform* ct = layerTransfrom( theLayer ) )
365  rect = ct->transform( rect, QgsCoordinateTransform::ForwardTransform );
366  }
367  catch ( QgsCsException &cse )
368  {
369  QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" );
370  }
371  }
372  else
373  {
374  // leave point without transformation
375  }
376  return rect;
377 }
378 
379 
381 {
382  if ( hasCrsTransformEnabled() )
383  {
384  try
385  {
386  if ( const QgsCoordinateTransform* ct = layerTransfrom( theLayer ) )
387  point = ct->transform( point, QgsCoordinateTransform::ReverseTransform );
388  }
389  catch ( QgsCsException &cse )
390  {
391  QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" );
392  }
393  }
394  else
395  {
396  // leave point without transformation
397  }
398  return point;
399 }
400 
401 
403 {
404  if ( hasCrsTransformEnabled() )
405  {
406  try
407  {
408  if ( const QgsCoordinateTransform* ct = layerTransfrom( theLayer ) )
409  rect = ct->transform( rect, QgsCoordinateTransform::ReverseTransform );
410  }
411  catch ( QgsCsException &cse )
412  {
413  QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" );
414  }
415  }
416  return rect;
417 }
418 
419 
420 
422 {
423  QgsDebugMsg( "called." );
425 
426  // reset the map canvas extent since the extent may now be smaller
427  // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
429  fullExtent.setMinimal();
430 
431  // iterate through the map layers and test each layers extent
432  // against the current min and max values
433  QStringList::const_iterator it = mLayers.begin();
434  QgsDebugMsg( QString( "Layer count: %1" ).arg( mLayers.count() ) );
435  while ( it != mLayers.end() )
436  {
437  QgsMapLayer * lyr = registry->mapLayer( *it );
438  if ( lyr == NULL )
439  {
440  QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) );
441  }
442  else
443  {
444  QgsDebugMsg( "Updating extent using " + lyr->name() );
445  QgsDebugMsg( "Input extent: " + lyr->extent().toString() );
446 
447  if ( lyr->extent().isNull() )
448  {
449  it++;
450  continue;
451  }
452 
453  // Layer extents are stored in the coordinate system (CS) of the
454  // layer. The extent must be projected to the canvas CS
456 
457  QgsDebugMsg( "Output extent: " + extent.toString() );
458  fullExtent.unionRect( extent );
459 
460  }
461  it++;
462  }
463 
464  if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 )
465  {
466  // If all of the features are at the one point, buffer the
467  // rectangle a bit. If they are all at zero, do something a bit
468  // more crude.
469 
470  if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 &&
471  fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 )
472  {
473  fullExtent.set( -1.0, -1.0, 1.0, 1.0 );
474  }
475  else
476  {
477  const double padFactor = 1e-8;
478  double widthPad = fullExtent.xMinimum() * padFactor;
479  double heightPad = fullExtent.yMinimum() * padFactor;
480  double xmin = fullExtent.xMinimum() - widthPad;
481  double xmax = fullExtent.xMaximum() + widthPad;
482  double ymin = fullExtent.yMinimum() - heightPad;
483  double ymax = fullExtent.yMaximum() + heightPad;
484  fullExtent.set( xmin, ymin, xmax, ymax );
485  }
486  }
487 
488  QgsDebugMsg( "Full extent: " + fullExtent.toString() );
489  return fullExtent;
490 }
491 
492 
493 void QgsMapSettings::readXML( QDomNode& theNode )
494 {
495  // set units
496  QDomNode mapUnitsNode = theNode.namedItem( "units" );
497  QGis::UnitType units = QgsXmlUtils::readMapUnits( mapUnitsNode.toElement() );
498  setMapUnits( units );
499 
500  // set projections flag
501  QDomNode projNode = theNode.namedItem( "projections" );
502  setCrsTransformEnabled( projNode.toElement().text().toInt() );
503 
504  // set destination CRS
506  QDomNode srsNode = theNode.namedItem( "destinationsrs" );
507  srs.readXML( srsNode );
508  setDestinationCrs( srs );
509 
510  // set extent
511  QDomNode extentNode = theNode.namedItem( "extent" );
512  QgsRectangle aoi = QgsXmlUtils::readRectangle( extentNode.toElement() );
513  setExtent( aoi );
514 
515  mDatumTransformStore.readXML( theNode );
516 }
517 
518 
519 
520 void QgsMapSettings::writeXML( QDomNode& theNode, QDomDocument& theDoc )
521 {
522  // units
523  theNode.appendChild( QgsXmlUtils::writeMapUnits( mapUnits(), theDoc ) );
524 
525  // Write current view extents
526  theNode.appendChild( QgsXmlUtils::writeRectangle( extent(), theDoc ) );
527 
528  // projections enabled
529  QDomElement projNode = theDoc.createElement( "projections" );
530  projNode.appendChild( theDoc.createTextNode( QString::number( hasCrsTransformEnabled() ) ) );
531  theNode.appendChild( projNode );
532 
533  // destination CRS
534  QDomElement srsNode = theDoc.createElement( "destinationsrs" );
535  theNode.appendChild( srsNode );
536  destinationCrs().writeXML( srsNode, theDoc );
537 
538  mDatumTransformStore.writeXML( theNode, theDoc );
539 }
void setMapUnits(QGis::UnitType mapUnits)
Set the map units.
void unionRect(const QgsRectangle &rect)
updates rectangle to include passed argument
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:48
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer's CRS to output CRS
bool isEmpty() const
test if rectangle is empty.
QgsRectangle mVisibleExtent
extent with some additional white space that matches the output aspect ratio
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
QgsMapToPixel mMapToPixel
double scale() const
Return the calculated scale of the map.
void readXML(QDomNode &theNode)
QgsRectangle fullExtent() const
returns current extent of layer set
bool isFinite() const
Returns true if the rectangle has finite boundaries.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:194
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
void setOutputDpi(int dpi)
Set DPI used for conversion between real world units (e.g. mm) and pixels.
QgsCoordinateReferenceSystem mDestCRS
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
void setDpi(double dpi)
Set the dpi to be used in scale calculations.
void setDestinationCrs(const QgsCoordinateReferenceSystem &destCrs)
static QDomElement writeRectangle(const QgsRectangle &rect, QDomDocument &doc)
Definition: qgsxmlutils.cpp:96
QGis::UnitType mapUnits() const
Returns current map units.
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
QgsRectangle layerExtentToOutputExtent(QgsMapLayer *theLayer, QgsRectangle extent) const
transform bounding box from layer's CRS to output CRS
Flags flags() const
Return combination of flags used for rendering.
QgsPoint mapToLayerCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from output CRS to layer's CRS
void setLayers(const QStringList &layers)
Set list of layer IDs for map rendering.
Q_GUI_EXPORT int qt_defaultDpiX()
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void set(const QgsPoint &p1, const QgsPoint &p2)
Set the rectangle from two QgsPoints.
const QString & name() const
Get the display name of the layer.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:33
bool mValid
whether the actual settings are valid (set in updateDerived())
int outputDpi() const
Return DPI used for conversion between real world units (e.g.
QgsRectangle mExtent
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:199
Flag
Enumeration of flags that adjust the way how map is rendered.
QSize outputSize() const
Return the size of the resulting map image.
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:184
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
void setMapUnits(QGis::UnitType u)
Set units of map's geographical coordinates - used for scale calculation.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
void writeXML(QDomNode &parentNode, QDomDocument &theDoc) const
const long GEOCRS_ID
Magic number for a geographic coord sys in QGIS srs.db tbl_srs.srs_id.
Definition: qgis.h:384
double mapUnitsPerPixel() const
Return the distance in geographical coordinates that equals to one pixel in the map.
QGis::UnitType mapUnits() const
Get units of map's geographical coordinates - used for scale calculation.
const QgsCoordinateTransform * transformation(QgsMapLayer *layer) const
will return transform from layer's CRS to current destination CRS.
QgsScaleCalculator mScaleCalculator
A class to represent a point geometry.
Definition: qgspoint.h:63
static QGis::UnitType readMapUnits(const QDomElement &element)
Definition: qgsxmlutils.cpp:9
This class tracks map layers that are currently loaded and provides a means to fetch a pointer to a m...
QString qgsDoubleToString(const double &a, const int &precision=17)
Definition: qgis.h:316
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QString what() const
Definition: qgsexception.h:35
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
double mMapUnitsPerPixel
void readXML(const QDomNode &parentNode)
void setOutputSize(const QSize &size)
Set the size of the resulting map image.
Class for storing a coordinate reference system (CRS)
void setExtent(const QgsRectangle &rect)
Set coordinates of the rectangle which should be rendered.
QgsRectangle extent() const
Return geographical coordinates of the rectangle that should be rendered.
Class for doing transforms between two map coordinate systems.
UnitType
Map units that qgis supports.
Definition: qgis.h:229
QStringList layers() const
Get list of layer IDs for map rendering The layers are stored in the reverse order of how they are re...
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
Custom exception class for Coordinate Reference System related exceptions.
QStringList mLayers
QgsRectangle outputExtentToLayerExtent(QgsMapLayer *theLayer, QgsRectangle extent) const
transform bounding box from output CRS to layer's CRS
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:204
QgsDatumTransformStore mDatumTransformStore
const QgsCoordinateTransform * layerTransfrom(QgsMapLayer *layer) const
Return coordinate transform from layer's CRS to destination CRS.
virtual QgsRectangle extent()
Return the extent of the layer.
double size
Definition: qgssvgcache.cpp:77
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
void setFlags(Flags flags)
Set combination of flags that will be used for rendering.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:189
static QgsRectangle readRectangle(const QDomElement &element)
Definition: qgsxmlutils.cpp:38
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:209
static QDomElement writeMapUnits(QGis::UnitType units, QDomDocument &doc)
Definition: qgsxmlutils.cpp:68
void setCrsTransformEnabled(bool enabled)
sets whether to use projections for this layer set
void writeXML(QDomNode &theNode, QDomDocument &theDoc)