Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgsmaprenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprender.cpp - class for rendering map layer set
3  ----------------------
4  begin : January 2006
5  copyright : (C) 2006 by Martin Dobias
6  email : wonder.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 /* $Id$ */
16 
17 #include <cmath>
18 #include <cfloat>
19 
20 #include "qgscoordinatetransform.h"
21 #include "qgslogger.h"
22 #include "qgsmaprenderer.h"
23 #include "qgsscalecalculator.h"
24 #include "qgsmaptopixel.h"
25 #include "qgsmaplayer.h"
26 #include "qgsmaplayerregistry.h"
27 #include "qgsdistancearea.h"
31 #include "qgsvectorlayer.h"
32 #include "qgsvectoroverlay.h"
33 
34 
35 #include <QDomDocument>
36 #include <QDomNode>
37 #include <QPainter>
38 #include <QListIterator>
39 #include <QSettings>
40 #include <QTime>
41 #include <QCoreApplication>
42 
44 {
47 
48  mDrawing = false;
49  mOverview = false;
50 
51  // set default map units - we use WGS 84 thus use degrees
53 
54  mSize = QSize( 0, 0 );
55 
56  mProjectionsEnabled = false;
58 
60 
61  mLabelingEngine = NULL;
62 }
63 
65 {
66  delete mScaleCalculator;
67  delete mDistArea;
68  delete mDestCRS;
69  delete mLabelingEngine;
70 }
71 
72 
74 {
75  return mExtent;
76 }
77 
79 {
81 }
82 
84 {
85  //remember the previous extent
87 
88  // Don't allow zooms where the current extent is so small that it
89  // can't be accurately represented using a double (which is what
90  // currentExtent uses). Excluding 0 avoids a divide by zero and an
91  // infinite loop when rendering to a new canvas. Excluding extents
92  // greater than 1 avoids doing unnecessary calculations.
93 
94  // The scheme is to compare the width against the mean x coordinate
95  // (and height against mean y coordinate) and only allow zooms where
96  // the ratio indicates that there is more than about 12 significant
97  // figures (there are about 16 significant figures in a double).
98 
99  if ( extent.width() > 0 &&
100  extent.height() > 0 &&
101  extent.width() < 1 &&
102  extent.height() < 1 )
103  {
104  // Use abs() on the extent to avoid the case where the extent is
105  // symmetrical about 0.
106  double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5;
107  double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5;
108 
109  double xRange = extent.width() / xMean;
110  double yRange = extent.height() / yMean;
111 
112  static const double minProportion = 1e-12;
113  if ( xRange < minProportion || yRange < minProportion )
114  return false;
115  }
116 
117  mExtent = extent;
118  if ( !extent.isEmpty() )
120  return true;
121 }
122 
123 
124 
125 void QgsMapRenderer::setOutputSize( QSize size, int dpi )
126 {
127  mSize = QSizeF( size.width(), size.height() );
128  mScaleCalculator->setDpi( dpi );
130 }
131 
132 void QgsMapRenderer::setOutputSize( QSizeF size, double dpi )
133 {
134  mSize = size;
135  mScaleCalculator->setDpi( dpi );
137 }
138 
140 {
141  return mScaleCalculator->dpi();
142 }
143 
145 {
146  return mSize.toSize();
147 }
148 
150 {
151  return mSize;
152 }
153 
155 {
156  double myHeight = mSize.height();
157  double myWidth = mSize.width();
158 
159  QgsMapToPixel newCoordXForm;
160 
161  if ( !myWidth || !myHeight )
162  {
163  mScale = 1;
164  newCoordXForm.setParameters( 0, 0, 0, 0 );
165  return;
166  }
167 
168  // calculate the translation and scaling parameters
169  // mapUnitsPerPixel = map units per pixel
170  double mapUnitsPerPixelY = mExtent.height() / myHeight;
171  double mapUnitsPerPixelX = mExtent.width() / myWidth;
172  mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
173 
174  // calculate the actual extent of the mapCanvas
175  double dxmin, dxmax, dymin, dymax, whitespace;
176 
177  if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
178  {
179  dymin = mExtent.yMinimum();
180  dymax = mExtent.yMaximum();
181  whitespace = (( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5;
182  dxmin = mExtent.xMinimum() - whitespace;
183  dxmax = mExtent.xMaximum() + whitespace;
184  }
185  else
186  {
187  dxmin = mExtent.xMinimum();
188  dxmax = mExtent.xMaximum();
189  whitespace = (( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5;
190  dymin = mExtent.yMinimum() - whitespace;
191  dymax = mExtent.yMaximum() + whitespace;
192  }
193 
194  QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2\n" ).arg( mapUnitsPerPixelX ).arg( mapUnitsPerPixelY ) );
195  QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2\n" ).arg( myWidth ).arg( myHeight ) );
196  QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2\n" ).arg( mExtent.width() ).arg( mExtent.height() ) );
198 
199  // update extent
200  mExtent.setXMinimum( dxmin );
201  mExtent.setXMaximum( dxmax );
202  mExtent.setYMinimum( dymin );
203  mExtent.setYMaximum( dymax );
204 
205  // update the scale
206  updateScale();
207 
208  QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( mScale ) );
209 
210  newCoordXForm.setParameters( mMapUnitsPerPixel, dxmin, dymin, myHeight );
211  mRenderContext.setMapToPixel( newCoordXForm );
213 }
214 
215 
216 void QgsMapRenderer::render( QPainter* painter )
217 {
218  //flag to see if the render context has changed
219  //since the last time we rendered. If it hasnt changed we can
220  //take some shortcuts with rendering
221  bool mySameAsLastFlag = true;
222 
223  QgsDebugMsg( "========== Rendering ==========" );
224 
225  if ( mExtent.isEmpty() )
226  {
227  QgsDebugMsg( "empty extent... not rendering" );
228  return;
229  }
230 
231  if ( mSize.width() == 1 && mSize.height() == 1 )
232  {
233  QgsDebugMsg( "size 1x1... not rendering" );
234  return;
235  }
236 
237  QPaintDevice* thePaintDevice = painter->device();
238  if ( !thePaintDevice )
239  {
240  return;
241  }
242 
243  // wait
244  if ( mDrawing )
245  {
246  QgsDebugMsg( "already rendering" );
247  QCoreApplication::processEvents();
248  }
249 
250  if ( mDrawing )
251  {
252  QgsDebugMsg( "still rendering - skipping" );
253  return;
254  }
255 
256  mDrawing = true;
257 
259 
260 #ifdef QGISDEBUG
261  QgsDebugMsg( "Starting to render layer stack." );
262  QTime renderTime;
263  renderTime.start();
264 #endif
265 
266  if ( mOverview )
268 
269  mRenderContext.setPainter( painter );
271  //this flag is only for stopping during the current rendering progress,
272  //so must be false at every new render operation
274 
275  //calculate scale factor
276  //use the specified dpi and not those from the paint device
277  //because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene)
278  double sceneDpi = mScaleCalculator->dpi();
279  double scaleFactor = 1.0;
281  {
282  scaleFactor = sceneDpi / 25.4;
283  }
284  double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
285  if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
286  {
287  mRenderContext.setRasterScaleFactor( rasterScaleFactor );
288  mySameAsLastFlag = false;
289  }
290  if ( mRenderContext.scaleFactor() != scaleFactor )
291  {
292  mRenderContext.setScaleFactor( scaleFactor );
293  mySameAsLastFlag = false;
294  }
296  {
297  //add map scale to render context
299  mySameAsLastFlag = false;
300  }
301  if ( mLastExtent != mExtent )
302  {
304  mySameAsLastFlag = false;
305  }
306 
308  if ( mLabelingEngine )
309  mLabelingEngine->init( this );
310 
311  // know we know if this render is just a repeat of the last time, we
312  // can clear caches if it has changed
313  if ( !mySameAsLastFlag )
314  {
315  //clear the cache pixmap if we changed resolution / extent
316  QSettings mySettings;
317  if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
318  {
320  }
321  }
322 
324  QList<QgsVectorOverlay*> allOverlayList; //list of all overlays, used to draw them after layers have been rendered
325 
326  // render all layers in the stack, starting at the base
327  QListIterator<QString> li( mLayerSet );
328  li.toBack();
329 
330  QgsRectangle r1, r2;
331 
332  while ( li.hasPrevious() )
333  {
335  {
336  break;
337  }
338 
339  // Store the painter in case we need to swap it out for the
340  // cache painter
341  QPainter * mypContextPainter = mRenderContext.painter();
342 
343  QString layerId = li.previous();
344 
345  QgsDebugMsg( "Rendering at layer item " + layerId );
346 
347  // This call is supposed to cause the progress bar to
348  // advance. However, it seems that updating the progress bar is
349  // incompatible with having a QPainter active (the one that is
350  // passed into this function), as Qt produces a number of errors
351  // when try to do so. I'm (Gavin) not sure how to fix this, but
352  // added these comments and debug statement to help others...
353  QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" );
354 
355  //emit drawingProgress(myRenderCounter++, mLayerSet.size());
357 
358  if ( !ml )
359  {
360  QgsDebugMsg( "Layer not found in registry!" );
361  continue;
362  }
363 
364  QgsDebugMsg( "Rendering layer " + ml->name() );
365  QgsDebugMsg( " Layer minscale " + QString( "%1" ).arg( ml->minimumScale() ) );
366  QgsDebugMsg( " Layer maxscale " + QString( "%1" ).arg( ml->maximumScale() ) );
367  QgsDebugMsg( " Scale dep. visibility enabled? " + QString( "%1" ).arg( ml->hasScaleBasedVisibility() ) );
368  QgsDebugMsg( " Input extent: " + ml->extent().toString() );
369 
370  if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) || mOverview )
371  {
372  connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
373 
374  //
375  // Now do the call to the layer that actually does
376  // the rendering work!
377  //
378 
379  bool split = false;
380 
381  if ( hasCrsTransformEnabled() )
382  {
383  r1 = mExtent;
384  split = splitLayersExtent( ml, r1, r2 );
385  ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
387  QgsDebugMsg( " extent 1: " + r1.toString() );
388  QgsDebugMsg( " extent 2: " + r2.toString() );
389  if ( !r1.isFinite() || !r2.isFinite() ) //there was a problem transforming the extent. Skip the layer
390  {
391  continue;
392  }
393  }
394  else
395  {
396  ct = NULL;
397  }
398 
400 
401  //decide if we have to scale the raster
402  //this is necessary in case QGraphicsScene is used
403  bool scaleRaster = false;
404  QgsMapToPixel rasterMapToPixel;
405  QgsMapToPixel bk_mapToPixel;
406 
407  if ( ml->type() == QgsMapLayer::RasterLayer && qAbs( rasterScaleFactor - 1.0 ) > 0.000001 )
408  {
409  scaleRaster = true;
410  }
411 
412 
413  //create overlay objects for features within the view extent
414  if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager )
415  {
416  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
417  if ( vl )
418  {
419  QList<QgsVectorOverlay*> thisLayerOverlayList;
420  vl->vectorOverlays( thisLayerOverlayList );
421 
422  QList<QgsVectorOverlay*>::iterator overlayIt = thisLayerOverlayList.begin();
423  for ( ; overlayIt != thisLayerOverlayList.end(); ++overlayIt )
424  {
425  if (( *overlayIt )->displayFlag() )
426  {
427  ( *overlayIt )->createOverlayObjects( mRenderContext );
428  allOverlayList.push_back( *overlayIt );
429  }
430  }
431 
432  overlayManager->addLayer( vl, thisLayerOverlayList );
433  }
434  }
435 
436  // Force render of layers that are being edited
437  // or if there's a labeling engine that needs the layer to register features
438  if ( ml->type() == QgsMapLayer::VectorLayer )
439  {
440  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
441  if ( vl->isEditable() ||
443  {
444  ml->setCacheImage( 0 );
445  }
446  }
447 
448  QSettings mySettings;
449  if ( ! split )//render caching does not yet cater for split extents
450  {
451  if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
452  {
453  if ( !mySameAsLastFlag || ml->cacheImage() == 0 )
454  {
455  QgsDebugMsg( "\n\n\nCaching enabled but layer redraw forced by extent change or empty cache\n\n\n" );
456  QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(),
457  mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
458  mypImage->fill( 0 );
459  ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you
460  QPainter * mypPainter = new QPainter( ml->cacheImage() );
461  // Changed to enable anti aliasing by default in QGIS 1.7
462  if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
463  {
464  mypPainter->setRenderHint( QPainter::Antialiasing );
465  }
466  mRenderContext.setPainter( mypPainter );
467  }
468  else if ( mySameAsLastFlag )
469  {
470  //draw from cached image
471  QgsDebugMsg( "\n\n\nCaching enabled --- drawing layer from cached image\n\n\n" );
472  mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
473  disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
474  //short circuit as there is nothing else to do...
475  continue;
476  }
477  }
478  }
479 
480  if ( scaleRaster )
481  {
482  bk_mapToPixel = mRenderContext.mapToPixel();
483  rasterMapToPixel = mRenderContext.mapToPixel();
484  rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
485  rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor );
486  mRenderContext.setMapToPixel( rasterMapToPixel );
487  mRenderContext.painter()->save();
488  mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
489  }
490 
491 
492  if ( !ml->draw( mRenderContext ) )
493  {
494  emit drawError( ml );
495  }
496  else
497  {
498  QgsDebugMsg( "Layer rendered without issues" );
499  }
500 
501  if ( split )
502  {
504  if ( !ml->draw( mRenderContext ) )
505  {
506  emit drawError( ml );
507  }
508  }
509 
510  if ( scaleRaster )
511  {
512  mRenderContext.setMapToPixel( bk_mapToPixel );
513  mRenderContext.painter()->restore();
514  }
515 
516  if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
517  {
518  if ( !split )
519  {
520  // composite the cached image into our view and then clean up from caching
521  // by reinstating the painter as it was swapped out for caching renders
522  delete mRenderContext.painter();
523  mRenderContext.setPainter( mypContextPainter );
524  //draw from cached image that we created further up
525  mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
526  }
527  }
528  disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
529  }
530  else // layer not visible due to scale
531  {
532  QgsDebugMsg( "Layer not rendered because it is not within the defined "
533  "visibility scale range" );
534  }
535 
536  } // while (li.hasPrevious())
537 
538  QgsDebugMsg( "Done rendering map layers" );
539 
540  if ( !mOverview )
541  {
542  // render all labels for vector layers in the stack, starting at the base
543  li.toBack();
544  while ( li.hasPrevious() )
545  {
547  {
548  break;
549  }
550 
551  QString layerId = li.previous();
552 
553  // TODO: emit drawingProgress((myRenderCounter++),zOrder.size());
555 
556  if ( ml && ( ml->type() != QgsMapLayer::RasterLayer ) )
557  {
558  // only make labels if the layer is visible
559  // after scale dep viewing settings are checked
560  if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) )
561  {
562  bool split = false;
563 
564  if ( hasCrsTransformEnabled() )
565  {
566  QgsRectangle r1 = mExtent;
567  split = splitLayersExtent( ml, r1, r2 );
568  ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
570  }
571  else
572  {
573  ct = NULL;
574  }
575 
577 
578  ml->drawLabels( mRenderContext );
579  if ( split )
580  {
582  ml->drawLabels( mRenderContext );
583  }
584  }
585  }
586  }
587  } // if (!mOverview)
588 
589  //find overlay positions and draw the vector overlays
590  if ( overlayManager && allOverlayList.size() > 0 )
591  {
593  //draw all the overlays
594  QList<QgsVectorOverlay*>::iterator allOverlayIt = allOverlayList.begin();
595  for ( ; allOverlayIt != allOverlayList.end(); ++allOverlayIt )
596  {
597  ( *allOverlayIt )->drawOverlayObjects( mRenderContext );
598  }
599  overlayManager->removeLayers();
600  }
601 
602  delete overlayManager;
603  // make sure progress bar arrives at 100%!
604  emit drawingProgress( 1, 1 );
605 
606  if ( mLabelingEngine )
607  {
608  // set correct extent
611 
614  }
615 
616  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
617 
618  mDrawing = false;
619 }
620 
622 {
624 
625  // Since the map units have changed, force a recalculation of the scale.
626  updateScale();
627 
628  emit mapUnitsChanged();
629 }
630 
632 {
633  return mScaleCalculator->mapUnits();
634 }
635 
636 void QgsMapRenderer::onDrawingProgress( int current, int total )
637 {
638  // TODO: emit signal with progress
639 // QgsDebugMsg(QString("onDrawingProgress: %1 / %2").arg(current).arg(total));
640  emit updateMap();
641 }
642 
643 
644 
646 {
647  if ( mProjectionsEnabled != enabled )
648  {
649  mProjectionsEnabled = enabled;
650  QgsDebugMsg( "Adjusting DistArea projection on/off" );
651  mDistArea->setProjectionsEnabled( enabled );
653  emit hasCrsTransformEnabled( enabled );
654  }
655 }
656 
658 {
659  return mProjectionsEnabled;
660 }
661 
663 {
664  QgsDebugMsg( "* Setting destCRS : = " + crs.toProj4() );
665  QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( crs.srsid() ) );
666  if ( *mDestCRS != crs )
667  {
668  QgsDebugMsg( "Setting DistArea CRS to " + QString::number( crs.srsid() ) );
669  mDistArea->setSourceCrs( crs.srsid() );
670  *mDestCRS = crs;
672  emit destinationSrsChanged();
673  }
674 }
675 
677 {
678  QgsDebugMsgLevel( "* Returning destCRS", 3 );
679  QgsDebugMsgLevel( "* DestCRS.srsid() = " + QString::number( mDestCRS->srsid() ), 3 );
680  QgsDebugMsgLevel( "* DestCRS.proj4() = " + mDestCRS->toProj4(), 3 );
681  return *mDestCRS;
682 }
683 
684 
686 {
687  bool split = false;
688 
689  if ( hasCrsTransformEnabled() )
690  {
691  try
692  {
693  QgsCoordinateTransform tr( layer->crs(), *mDestCRS );
694 
695 #ifdef QGISDEBUG
696  // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__);
697 #endif
698  // Split the extent into two if the source CRS is
699  // geographic and the extent crosses the split in
700  // geographic coordinates (usually +/- 180 degrees,
701  // and is assumed to be so here), and draw each
702  // extent separately.
703  static const double splitCoord = 180.0;
704 
705  if ( tr.sourceCrs().geographicFlag() )
706  {
707  // Note: ll = lower left point
708  // and ur = upper right point
709  QgsPoint ll = tr.transform( extent.xMinimum(), extent.yMinimum(),
711 
712  QgsPoint ur = tr.transform( extent.xMaximum(), extent.yMaximum(),
714 
715  extent = tr.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
716 
717  if ( ll.x() > ur.x() )
718  {
719  r2 = extent;
720  extent.setXMinimum( splitCoord );
721  r2.setXMaximum( splitCoord );
722  split = true;
723  }
724  }
725  else // can't cross 180
726  {
727  extent = tr.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
728  }
729  }
730  catch ( QgsCsException &cse )
731  {
732  Q_UNUSED( cse );
733  QgsDebugMsg( "Transform error caught" );
734  extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX );
735  r2 = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX );
736  }
737  }
738  return split;
739 }
740 
741 
743 {
744  if ( hasCrsTransformEnabled() )
745  {
746  try
747  {
748  QgsCoordinateTransform tr( theLayer->crs(), *mDestCRS );
749  extent = tr.transformBoundingBox( extent );
750  }
751  catch ( QgsCsException &cse )
752  {
753  Q_UNUSED( cse );
754  QgsDebugMsg( QString( "Transform error caught: " ).arg( cse.what() ) );
755  }
756  }
757  else
758  {
759  // leave extent unchanged
760  }
761 
762  return extent;
763 }
764 
766 {
767  if ( hasCrsTransformEnabled() )
768  {
769  try
770  {
771  QgsCoordinateTransform tr( theLayer->crs(), *mDestCRS );
772  point = tr.transform( point, QgsCoordinateTransform::ForwardTransform );
773  }
774  catch ( QgsCsException &cse )
775  {
776  Q_UNUSED( cse );
777  QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
778  }
779  }
780  else
781  {
782  // leave point without transformation
783  }
784  return point;
785 }
786 
788 {
789  if ( hasCrsTransformEnabled() )
790  {
791  try
792  {
793  QgsCoordinateTransform tr( theLayer->crs(), *mDestCRS );
794  point = tr.transform( point, QgsCoordinateTransform::ReverseTransform );
795  }
796  catch ( QgsCsException &cse )
797  {
798  QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
799  throw cse; //let client classes know there was a transformation error
800  }
801  }
802  else
803  {
804  // leave point without transformation
805  }
806  return point;
807 }
808 
810 {
811  if ( hasCrsTransformEnabled() )
812  {
813  try
814  {
815  QgsCoordinateTransform tr( theLayer->crs(), *mDestCRS );
816  rect = tr.transform( rect, QgsCoordinateTransform::ReverseTransform );
817  }
818  catch ( QgsCsException &cse )
819  {
820  QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
821  throw cse; //let client classes know there was a transformation error
822  }
823  }
824  return rect;
825 }
826 
827 
829 {
830  QgsDebugMsg( "called." );
832 
833  // reset the map canvas extent since the extent may now be smaller
834  // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
836 
837  // iterate through the map layers and test each layers extent
838  // against the current min and max values
839  QStringList::iterator it = mLayerSet.begin();
840  while ( it != mLayerSet.end() )
841  {
842  QgsMapLayer * lyr = registry->mapLayer( *it );
843  if ( lyr == NULL )
844  {
845  QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) );
846  }
847  else
848  {
849  QgsDebugMsg( "Updating extent using " + lyr->name() );
850  QgsDebugMsg( "Input extent: " + lyr->extent().toString() );
851 
852  // Layer extents are stored in the coordinate system (CS) of the
853  // layer. The extent must be projected to the canvas CS
855 
856  QgsDebugMsg( "Output extent: " + extent.toString() );
857  mFullExtent.unionRect( extent );
858 
859  }
860  it++;
861  }
862 
863  if ( mFullExtent.width() == 0.0 || mFullExtent.height() == 0.0 )
864  {
865  // If all of the features are at the one point, buffer the
866  // rectangle a bit. If they are all at zero, do something a bit
867  // more crude.
868 
869  if ( mFullExtent.xMinimum() == 0.0 && mFullExtent.xMaximum() == 0.0 &&
870  mFullExtent.yMinimum() == 0.0 && mFullExtent.yMaximum() == 0.0 )
871  {
872  mFullExtent.set( -1.0, -1.0, 1.0, 1.0 );
873  }
874  else
875  {
876  const double padFactor = 1e-8;
877  double widthPad = mFullExtent.xMinimum() * padFactor;
878  double heightPad = mFullExtent.yMinimum() * padFactor;
879  double xmin = mFullExtent.xMinimum() - widthPad;
880  double xmax = mFullExtent.xMaximum() + widthPad;
881  double ymin = mFullExtent.yMinimum() - heightPad;
882  double ymax = mFullExtent.yMaximum() + heightPad;
883  mFullExtent.set( xmin, ymin, xmax, ymax );
884  }
885  }
886 
887  QgsDebugMsg( "Full extent: " + mFullExtent.toString() );
888 }
889 
891 {
893  return mFullExtent;
894 }
895 
896 void QgsMapRenderer::setLayerSet( const QStringList& layers )
897 {
898  mLayerSet = layers;
900 }
901 
903 {
904  return mLayerSet;
905 }
906 
908 {
909  QSettings settings;
910  QString overlayAlgorithmQString = settings.value( "qgis/overlayPlacementAlgorithm", "Central point" ).toString();
911 
913 
914  if ( overlayAlgorithmQString != "Central point" )
915  {
917  if ( overlayAlgorithmQString == "Chain" )
918  {
919  palManager->setPlacementAlgorithm( "Chain" );
920  }
921  else if ( overlayAlgorithmQString == "Popmusic tabu chain" )
922  {
923  palManager->setPlacementAlgorithm( "Popmusic tabu chain" );
924  }
925  else if ( overlayAlgorithmQString == "Popmusic tabu" )
926  {
927  palManager->setPlacementAlgorithm( "Popmusic tabu" );
928  }
929  else if ( overlayAlgorithmQString == "Popmusic chain" )
930  {
931  palManager->setPlacementAlgorithm( "Popmusic chain" );
932  }
933  result = palManager;
934  }
935  else
936  {
937  result = new QgsCentralPointPositionManager();
938  }
939 
940  return result;
941 }
942 
943 bool QgsMapRenderer::readXML( QDomNode & theNode )
944 {
945  QDomNode myNode = theNode.namedItem( "units" );
946  QDomElement element = myNode.toElement();
947 
948  // set units
949  QGis::UnitType units;
950  if ( "meters" == element.text() )
951  {
952  units = QGis::Meters;
953  }
954  else if ( "feet" == element.text() )
955  {
956  units = QGis::Feet;
957  }
958  else if ( "degrees" == element.text() )
959  {
960  units = QGis::Degrees;
961  }
962  else if ( "unknown" == element.text() )
963  {
964  units = QGis::UnknownUnit;
965  }
966  else
967  {
968  QgsDebugMsg( "Unknown map unit type " + element.text() );
969  units = QGis::Degrees;
970  }
971  setMapUnits( units );
972 
973 
974  // set extent
975  QgsRectangle aoi;
976  QDomNode extentNode = theNode.namedItem( "extent" );
977 
978  QDomNode xminNode = extentNode.namedItem( "xmin" );
979  QDomNode yminNode = extentNode.namedItem( "ymin" );
980  QDomNode xmaxNode = extentNode.namedItem( "xmax" );
981  QDomNode ymaxNode = extentNode.namedItem( "ymax" );
982 
983  QDomElement exElement = xminNode.toElement();
984  double xmin = exElement.text().toDouble();
985  aoi.setXMinimum( xmin );
986 
987  exElement = yminNode.toElement();
988  double ymin = exElement.text().toDouble();
989  aoi.setYMinimum( ymin );
990 
991  exElement = xmaxNode.toElement();
992  double xmax = exElement.text().toDouble();
993  aoi.setXMaximum( xmax );
994 
995  exElement = ymaxNode.toElement();
996  double ymax = exElement.text().toDouble();
997  aoi.setYMaximum( ymax );
998 
999  setExtent( aoi );
1000 
1001  // set projections flag
1002  QDomNode projNode = theNode.namedItem( "projections" );
1003  element = projNode.toElement();
1004  setProjectionsEnabled( element.text().toInt() );
1005 
1006  // set destination CRS
1008  QDomNode srsNode = theNode.namedItem( "destinationsrs" );
1009  srs.readXML( srsNode );
1010  setDestinationCrs( srs );
1011 
1012  return true;
1013 }
1014 
1015 bool QgsMapRenderer::writeXML( QDomNode & theNode, QDomDocument & theDoc )
1016 {
1017  // units
1018 
1019  QDomElement unitsNode = theDoc.createElement( "units" );
1020  theNode.appendChild( unitsNode );
1021 
1022  QString unitsString;
1023 
1024  switch ( mapUnits() )
1025  {
1026  case QGis::Meters:
1027  unitsString = "meters";
1028  break;
1029  case QGis::Feet:
1030  unitsString = "feet";
1031  break;
1032  case QGis::Degrees:
1033  unitsString = "degrees";
1034  break;
1035  case QGis::UnknownUnit:
1036  default:
1037  unitsString = "unknown";
1038  break;
1039  }
1040  QDomText unitsText = theDoc.createTextNode( unitsString );
1041  unitsNode.appendChild( unitsText );
1042 
1043 
1044  // Write current view extents
1045  QDomElement extentNode = theDoc.createElement( "extent" );
1046  theNode.appendChild( extentNode );
1047 
1048  QDomElement xMin = theDoc.createElement( "xmin" );
1049  QDomElement yMin = theDoc.createElement( "ymin" );
1050  QDomElement xMax = theDoc.createElement( "xmax" );
1051  QDomElement yMax = theDoc.createElement( "ymax" );
1052 
1053  QgsRectangle r = extent();
1054  QDomText xMinText = theDoc.createTextNode( QString::number( r.xMinimum(), 'f' ) );
1055  QDomText yMinText = theDoc.createTextNode( QString::number( r.yMinimum(), 'f' ) );
1056  QDomText xMaxText = theDoc.createTextNode( QString::number( r.xMaximum(), 'f' ) );
1057  QDomText yMaxText = theDoc.createTextNode( QString::number( r.yMaximum(), 'f' ) );
1058 
1059  xMin.appendChild( xMinText );
1060  yMin.appendChild( yMinText );
1061  xMax.appendChild( xMaxText );
1062  yMax.appendChild( yMaxText );
1063 
1064  extentNode.appendChild( xMin );
1065  extentNode.appendChild( yMin );
1066  extentNode.appendChild( xMax );
1067  extentNode.appendChild( yMax );
1068 
1069  // projections enabled
1070  QDomElement projNode = theDoc.createElement( "projections" );
1071  theNode.appendChild( projNode );
1072 
1073  QDomText projText = theDoc.createTextNode( QString::number( hasCrsTransformEnabled() ) );
1074  projNode.appendChild( projText );
1075 
1076  // destination CRS
1077  QDomElement srsNode = theDoc.createElement( "destinationsrs" );
1078  theNode.appendChild( srsNode );
1079  destinationCrs().writeXML( srsNode, theDoc );
1080 
1081  return true;
1082 }
1083 
1085 {
1086  if ( mLabelingEngine )
1087  delete mLabelingEngine;
1088 
1089  mLabelingEngine = iface;
1090 }
1091 
1092 bool QgsMapRenderer::mDrawing = false;