Quantum GIS API Documentation  1.8
src/core/qgsmaprenderer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsmaprender.cpp  -  class for rendering map layer set
00003     ----------------------
00004     begin                : January 2006
00005     copyright            : (C) 2006 by Martin Dobias
00006     email                : wonder.sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include <cmath>
00017 #include <cfloat>
00018 
00019 #include "qgscoordinatetransform.h"
00020 #include "qgslogger.h"
00021 #include "qgsmessagelog.h"
00022 #include "qgsmaprenderer.h"
00023 #include "qgsscalecalculator.h"
00024 #include "qgsmaptopixel.h"
00025 #include "qgsmaplayer.h"
00026 #include "qgsmaplayerregistry.h"
00027 #include "qgsdistancearea.h"
00028 #include "qgscentralpointpositionmanager.h"
00029 #include "qgsoverlayobjectpositionmanager.h"
00030 #include "qgspalobjectpositionmanager.h"
00031 #include "qgsvectorlayer.h"
00032 #include "qgsvectoroverlay.h"
00033 
00034 
00035 #include <QDomDocument>
00036 #include <QDomNode>
00037 #include <QMutexLocker>
00038 #include <QPainter>
00039 #include <QListIterator>
00040 #include <QSettings>
00041 #include <QTime>
00042 #include <QCoreApplication>
00043 
00044 QgsMapRenderer::QgsMapRenderer()
00045 {
00046   mScaleCalculator = new QgsScaleCalculator;
00047   mDistArea = new QgsDistanceArea;
00048   mCachedTrForLayer = 0;
00049   mCachedTr = 0;
00050 
00051   mDrawing = false;
00052   mOverview = false;
00053 
00054   // set default map units - we use WGS 84 thus use degrees
00055   setMapUnits( QGis::Degrees );
00056 
00057   mSize = QSize( 0, 0 );
00058 
00059   mProjectionsEnabled = false;
00060   mDestCRS = new QgsCoordinateReferenceSystem( GEOCRS_ID, QgsCoordinateReferenceSystem::InternalCrsId ); //WGS 84
00061 
00062   mOutputUnits = QgsMapRenderer::Millimeters;
00063 
00064   mLabelingEngine = NULL;
00065 }
00066 
00067 QgsMapRenderer::~QgsMapRenderer()
00068 {
00069   delete mScaleCalculator;
00070   delete mDistArea;
00071   delete mDestCRS;
00072   delete mLabelingEngine;
00073   delete mCachedTr;
00074 }
00075 
00076 
00077 QgsRectangle QgsMapRenderer::extent() const
00078 {
00079   return mExtent;
00080 }
00081 
00082 void QgsMapRenderer::updateScale()
00083 {
00084   mScale = mScaleCalculator->calculate( mExtent, mSize.width() );
00085 }
00086 
00087 bool QgsMapRenderer::setExtent( const QgsRectangle& extent )
00088 {
00089   //remember the previous extent
00090   mLastExtent = mExtent;
00091 
00092   // Don't allow zooms where the current extent is so small that it
00093   // can't be accurately represented using a double (which is what
00094   // currentExtent uses). Excluding 0 avoids a divide by zero and an
00095   // infinite loop when rendering to a new canvas. Excluding extents
00096   // greater than 1 avoids doing unnecessary calculations.
00097 
00098   // The scheme is to compare the width against the mean x coordinate
00099   // (and height against mean y coordinate) and only allow zooms where
00100   // the ratio indicates that there is more than about 12 significant
00101   // figures (there are about 16 significant figures in a double).
00102 
00103   if ( extent.width()  > 0 &&
00104        extent.height() > 0 &&
00105        extent.width()  < 1 &&
00106        extent.height() < 1 )
00107   {
00108     // Use abs() on the extent to avoid the case where the extent is
00109     // symmetrical about 0.
00110     double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5;
00111     double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5;
00112 
00113     double xRange = extent.width() / xMean;
00114     double yRange = extent.height() / yMean;
00115 
00116     static const double minProportion = 1e-12;
00117     if ( xRange < minProportion || yRange < minProportion )
00118       return false;
00119   }
00120 
00121   mExtent = extent;
00122   if ( !extent.isEmpty() )
00123     adjustExtentToSize();
00124   return true;
00125 }
00126 
00127 
00128 
00129 void QgsMapRenderer::setOutputSize( QSize size, int dpi )
00130 {
00131   mSize = QSizeF( size.width(), size.height() );
00132   mScaleCalculator->setDpi( dpi );
00133   adjustExtentToSize();
00134 }
00135 
00136 void QgsMapRenderer::setOutputSize( QSizeF size, double dpi )
00137 {
00138   mSize = size;
00139   mScaleCalculator->setDpi( dpi );
00140   adjustExtentToSize();
00141 }
00142 
00143 double QgsMapRenderer::outputDpi()
00144 {
00145   return mScaleCalculator->dpi();
00146 }
00147 
00148 QSize QgsMapRenderer::outputSize()
00149 {
00150   return mSize.toSize();
00151 }
00152 
00153 QSizeF QgsMapRenderer::outputSizeF()
00154 {
00155   return mSize;
00156 }
00157 
00158 void QgsMapRenderer::adjustExtentToSize()
00159 {
00160   double myHeight = mSize.height();
00161   double myWidth = mSize.width();
00162 
00163   QgsMapToPixel newCoordXForm;
00164 
00165   if ( !myWidth || !myHeight )
00166   {
00167     mScale = 1;
00168     newCoordXForm.setParameters( 0, 0, 0, 0 );
00169     return;
00170   }
00171 
00172   // calculate the translation and scaling parameters
00173   // mapUnitsPerPixel = map units per pixel
00174   double mapUnitsPerPixelY = mExtent.height() / myHeight;
00175   double mapUnitsPerPixelX = mExtent.width() / myWidth;
00176   mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
00177 
00178   // calculate the actual extent of the mapCanvas
00179   double dxmin, dxmax, dymin, dymax, whitespace;
00180 
00181   if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
00182   {
00183     dymin = mExtent.yMinimum();
00184     dymax = mExtent.yMaximum();
00185     whitespace = (( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5;
00186     dxmin = mExtent.xMinimum() - whitespace;
00187     dxmax = mExtent.xMaximum() + whitespace;
00188   }
00189   else
00190   {
00191     dxmin = mExtent.xMinimum();
00192     dxmax = mExtent.xMaximum();
00193     whitespace = (( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5;
00194     dymin = mExtent.yMinimum() - whitespace;
00195     dymax = mExtent.yMaximum() + whitespace;
00196   }
00197 
00198   QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( mapUnitsPerPixelX, 0, 'f', 8 ).arg( mapUnitsPerPixelY, 0, 'f', 8 ) );
00199   QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( myWidth, 0, 'f', 8 ).arg( myHeight, 0, 'f', 8 ) );
00200   QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( mExtent.width(), 0, 'f', 8 ).arg( mExtent.height(), 0, 'f', 8 ) );
00201   QgsDebugMsg( mExtent.toString() );
00202 
00203   // update extent
00204   mExtent.setXMinimum( dxmin );
00205   mExtent.setXMaximum( dxmax );
00206   mExtent.setYMinimum( dymin );
00207   mExtent.setYMaximum( dymax );
00208 
00209   // update the scale
00210   updateScale();
00211 
00212   QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( mScale, 0, 'f', 8 ) );
00213 
00214   newCoordXForm.setParameters( mMapUnitsPerPixel, dxmin, dymin, myHeight );
00215   mRenderContext.setMapToPixel( newCoordXForm );
00216   mRenderContext.setExtent( mExtent );
00217 }
00218 
00219 
00220 void QgsMapRenderer::render( QPainter* painter, double* forceWidthScale )
00221 {
00222   //Lock render method for concurrent threads (e.g. from globe)
00223   QMutexLocker renderLock( &mRenderMutex );
00224 
00225   //flag to see if the render context has changed
00226   //since the last time we rendered. If it hasnt changed we can
00227   //take some shortcuts with rendering
00228   bool mySameAsLastFlag = true;
00229 
00230   QgsDebugMsg( "========== Rendering ==========" );
00231 
00232   if ( mExtent.isEmpty() )
00233   {
00234     QgsDebugMsg( "empty extent... not rendering" );
00235     return;
00236   }
00237 
00238   if ( mSize.width() == 1 && mSize.height() == 1 )
00239   {
00240     QgsDebugMsg( "size 1x1... not rendering" );
00241     return;
00242   }
00243 
00244   QPaintDevice* thePaintDevice = painter->device();
00245   if ( !thePaintDevice )
00246   {
00247     return;
00248   }
00249 
00250   // wait
00251   if ( mDrawing )
00252   {
00253     QgsDebugMsg( "already rendering" );
00254     QCoreApplication::processEvents();
00255   }
00256 
00257   if ( mDrawing )
00258   {
00259     QgsDebugMsg( "still rendering - skipping" );
00260     return;
00261   }
00262 
00263   mDrawing = true;
00264 
00265   QgsCoordinateTransform* ct;
00266 
00267 #ifdef QGISDEBUG
00268   QgsDebugMsg( "Starting to render layer stack." );
00269   QTime renderTime;
00270   renderTime.start();
00271 #endif
00272 
00273   if ( mOverview )
00274     mRenderContext.setDrawEditingInformation( !mOverview );
00275 
00276   mRenderContext.setPainter( painter );
00277   mRenderContext.setCoordinateTransform( 0 );
00278   //this flag is only for stopping during the current rendering progress,
00279   //so must be false at every new render operation
00280   mRenderContext.setRenderingStopped( false );
00281 
00282   //calculate scale factor
00283   //use the specified dpi and not those from the paint device
00284   //because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene)
00285   double sceneDpi = mScaleCalculator->dpi();
00286   double scaleFactor = 1.0;
00287   if ( mOutputUnits == QgsMapRenderer::Millimeters )
00288   {
00289     if ( forceWidthScale )
00290     {
00291       scaleFactor = *forceWidthScale;
00292     }
00293     else
00294     {
00295       scaleFactor = sceneDpi / 25.4;
00296     }
00297   }
00298   double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
00299   if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
00300   {
00301     mRenderContext.setRasterScaleFactor( rasterScaleFactor );
00302     mySameAsLastFlag = false;
00303   }
00304   if ( mRenderContext.scaleFactor() != scaleFactor )
00305   {
00306     mRenderContext.setScaleFactor( scaleFactor );
00307     mySameAsLastFlag = false;
00308   }
00309   if ( mRenderContext.rendererScale() != mScale )
00310   {
00311     //add map scale to render context
00312     mRenderContext.setRendererScale( mScale );
00313     mySameAsLastFlag = false;
00314   }
00315   if ( mLastExtent != mExtent )
00316   {
00317     mLastExtent = mExtent;
00318     mySameAsLastFlag = false;
00319   }
00320 
00321   mRenderContext.setLabelingEngine( mLabelingEngine );
00322   if ( mLabelingEngine )
00323     mLabelingEngine->init( this );
00324 
00325   // know we know if this render is just a repeat of the last time, we
00326   // can clear caches if it has changed
00327   if ( !mySameAsLastFlag )
00328   {
00329     //clear the cache pixmap if we changed resolution / extent
00330     QSettings mySettings;
00331     if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
00332     {
00333       QgsMapLayerRegistry::instance()->clearAllLayerCaches();
00334     }
00335   }
00336 
00337   QgsOverlayObjectPositionManager* overlayManager = overlayManagerFromSettings();
00338   QList<QgsVectorOverlay*> allOverlayList; //list of all overlays, used to draw them after layers have been rendered
00339 
00340   // render all layers in the stack, starting at the base
00341   QListIterator<QString> li( mLayerSet );
00342   li.toBack();
00343 
00344   QgsRectangle r1, r2;
00345 
00346   while ( li.hasPrevious() )
00347   {
00348     if ( mRenderContext.renderingStopped() )
00349     {
00350       break;
00351     }
00352 
00353     // Store the painter in case we need to swap it out for the
00354     // cache painter
00355     QPainter * mypContextPainter = mRenderContext.painter();
00356 
00357     QString layerId = li.previous();
00358 
00359     QgsDebugMsg( "Rendering at layer item " + layerId );
00360 
00361     // This call is supposed to cause the progress bar to
00362     // advance. However, it seems that updating the progress bar is
00363     // incompatible with having a QPainter active (the one that is
00364     // passed into this function), as Qt produces a number of errors
00365     // when try to do so. I'm (Gavin) not sure how to fix this, but
00366     // added these comments and debug statement to help others...
00367     QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" );
00368 
00369     //emit drawingProgress(myRenderCounter++, mLayerSet.size());
00370     QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
00371 
00372     if ( !ml )
00373     {
00374       QgsDebugMsg( "Layer not found in registry!" );
00375       continue;
00376     }
00377 
00378     QgsDebugMsg( QString( "layer %1:  minscale:%2  maxscale:%3  scaledepvis:%4  extent:%5" )
00379                  .arg( ml->name() )
00380                  .arg( ml->minimumScale() )
00381                  .arg( ml->maximumScale() )
00382                  .arg( ml->hasScaleBasedVisibility() )
00383                  .arg( ml->extent().toString() )
00384                );
00385 
00386     if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) || mOverview )
00387     {
00388       connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
00389 
00390       //
00391       // Now do the call to the layer that actually does
00392       // the rendering work!
00393       //
00394 
00395       bool split = false;
00396 
00397       if ( hasCrsTransformEnabled() )
00398       {
00399         r1 = mExtent;
00400         split = splitLayersExtent( ml, r1, r2 );
00401         ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
00402         mRenderContext.setExtent( r1 );
00403         QgsDebugMsg( "  extent 1: " + r1.toString() );
00404         QgsDebugMsg( "  extent 2: " + r2.toString() );
00405         if ( !r1.isFinite() || !r2.isFinite() ) //there was a problem transforming the extent. Skip the layer
00406         {
00407           continue;
00408         }
00409       }
00410       else
00411       {
00412         ct = NULL;
00413       }
00414 
00415       mRenderContext.setCoordinateTransform( ct );
00416 
00417       //decide if we have to scale the raster
00418       //this is necessary in case QGraphicsScene is used
00419       bool scaleRaster = false;
00420       QgsMapToPixel rasterMapToPixel;
00421       QgsMapToPixel bk_mapToPixel;
00422 
00423       if ( ml->type() == QgsMapLayer::RasterLayer && qAbs( rasterScaleFactor - 1.0 ) > 0.000001 )
00424       {
00425         scaleRaster = true;
00426       }
00427 
00428 
00429       //create overlay objects for features within the view extent
00430       if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager )
00431       {
00432         QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
00433         if ( vl )
00434         {
00435           QList<QgsVectorOverlay*> thisLayerOverlayList;
00436           vl->vectorOverlays( thisLayerOverlayList );
00437 
00438           QList<QgsVectorOverlay*>::iterator overlayIt = thisLayerOverlayList.begin();
00439           for ( ; overlayIt != thisLayerOverlayList.end(); ++overlayIt )
00440           {
00441             if (( *overlayIt )->displayFlag() )
00442             {
00443               ( *overlayIt )->createOverlayObjects( mRenderContext );
00444               allOverlayList.push_back( *overlayIt );
00445             }
00446           }
00447 
00448           overlayManager->addLayer( vl, thisLayerOverlayList );
00449         }
00450       }
00451 
00452       // Force render of layers that are being edited
00453       // or if there's a labeling engine that needs the layer to register features
00454       if ( ml->type() == QgsMapLayer::VectorLayer )
00455       {
00456         QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
00457         if ( vl->isEditable() ||
00458              ( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) )
00459         {
00460           ml->setCacheImage( 0 );
00461         }
00462       }
00463 
00464       QSettings mySettings;
00465       if ( ! split )//render caching does not yet cater for split extents
00466       {
00467         if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
00468         {
00469           if ( !mySameAsLastFlag || ml->cacheImage() == 0 )
00470           {
00471             QgsDebugMsg( "Caching enabled but layer redraw forced by extent change or empty cache" );
00472             QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(),
00473                                             mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
00474             mypImage->fill( 0 );
00475             ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you
00476             QPainter * mypPainter = new QPainter( ml->cacheImage() );
00477             // Changed to enable anti aliasing by default in QGIS 1.7
00478             if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
00479             {
00480               mypPainter->setRenderHint( QPainter::Antialiasing );
00481             }
00482             mRenderContext.setPainter( mypPainter );
00483           }
00484           else if ( mySameAsLastFlag )
00485           {
00486             //draw from cached image
00487             QgsDebugMsg( "Caching enabled --- drawing layer from cached image" );
00488             mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
00489             disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
00490             //short circuit as there is nothing else to do...
00491             continue;
00492           }
00493         }
00494       }
00495 
00496       if ( scaleRaster )
00497       {
00498         bk_mapToPixel = mRenderContext.mapToPixel();
00499         rasterMapToPixel = mRenderContext.mapToPixel();
00500         rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
00501         rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor );
00502         mRenderContext.setMapToPixel( rasterMapToPixel );
00503         mRenderContext.painter()->save();
00504         mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
00505       }
00506 
00507 
00508       if ( !ml->draw( mRenderContext ) )
00509       {
00510         emit drawError( ml );
00511       }
00512       else
00513       {
00514         QgsDebugMsg( "Layer rendered without issues" );
00515       }
00516 
00517       if ( split )
00518       {
00519         mRenderContext.setExtent( r2 );
00520         if ( !ml->draw( mRenderContext ) )
00521         {
00522           emit drawError( ml );
00523         }
00524       }
00525 
00526       if ( scaleRaster )
00527       {
00528         mRenderContext.setMapToPixel( bk_mapToPixel );
00529         mRenderContext.painter()->restore();
00530       }
00531 
00532       if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
00533       {
00534         if ( !split )
00535         {
00536           // composite the cached image into our view and then clean up from caching
00537           // by reinstating the painter as it was swapped out for caching renders
00538           delete mRenderContext.painter();
00539           mRenderContext.setPainter( mypContextPainter );
00540           //draw from cached image that we created further up
00541           mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
00542         }
00543       }
00544       disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
00545     }
00546     else // layer not visible due to scale
00547     {
00548       QgsDebugMsg( "Layer not rendered because it is not within the defined "
00549                    "visibility scale range" );
00550     }
00551 
00552   } // while (li.hasPrevious())
00553 
00554   QgsDebugMsg( "Done rendering map layers" );
00555 
00556   if ( !mOverview )
00557   {
00558     // render all labels for vector layers in the stack, starting at the base
00559     li.toBack();
00560     while ( li.hasPrevious() )
00561     {
00562       if ( mRenderContext.renderingStopped() )
00563       {
00564         break;
00565       }
00566 
00567       QString layerId = li.previous();
00568 
00569       // TODO: emit drawingProgress((myRenderCounter++),zOrder.size());
00570       QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
00571 
00572       if ( ml && ( ml->type() != QgsMapLayer::RasterLayer ) )
00573       {
00574         // only make labels if the layer is visible
00575         // after scale dep viewing settings are checked
00576         if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) )
00577         {
00578           bool split = false;
00579 
00580           if ( hasCrsTransformEnabled() )
00581           {
00582             QgsRectangle r1 = mExtent;
00583             split = splitLayersExtent( ml, r1, r2 );
00584             ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
00585             mRenderContext.setExtent( r1 );
00586           }
00587           else
00588           {
00589             ct = NULL;
00590           }
00591 
00592           mRenderContext.setCoordinateTransform( ct );
00593 
00594           ml->drawLabels( mRenderContext );
00595           if ( split )
00596           {
00597             mRenderContext.setExtent( r2 );
00598             ml->drawLabels( mRenderContext );
00599           }
00600         }
00601       }
00602     }
00603   } // if (!mOverview)
00604 
00605   //find overlay positions and draw the vector overlays
00606   if ( overlayManager && allOverlayList.size() > 0 )
00607   {
00608     overlayManager->findObjectPositions( mRenderContext, mScaleCalculator->mapUnits() );
00609     //draw all the overlays
00610     QList<QgsVectorOverlay*>::iterator allOverlayIt = allOverlayList.begin();
00611     for ( ; allOverlayIt != allOverlayList.end(); ++allOverlayIt )
00612     {
00613       ( *allOverlayIt )->drawOverlayObjects( mRenderContext );
00614     }
00615     overlayManager->removeLayers();
00616   }
00617 
00618   delete overlayManager;
00619   // make sure progress bar arrives at 100%!
00620   emit drawingProgress( 1, 1 );
00621 
00622   if ( mLabelingEngine )
00623   {
00624     // set correct extent
00625     mRenderContext.setExtent( mExtent );
00626     mRenderContext.setCoordinateTransform( NULL );
00627 
00628     mLabelingEngine->drawLabeling( mRenderContext );
00629     mLabelingEngine->exit();
00630   }
00631 
00632   QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
00633 
00634   mDrawing = false;
00635 }
00636 
00637 void QgsMapRenderer::setMapUnits( QGis::UnitType u )
00638 {
00639   mScaleCalculator->setMapUnits( u );
00640 
00641   // Since the map units have changed, force a recalculation of the scale.
00642   updateScale();
00643 
00644   emit mapUnitsChanged();
00645 }
00646 
00647 QGis::UnitType QgsMapRenderer::mapUnits() const
00648 {
00649   return mScaleCalculator->mapUnits();
00650 }
00651 
00652 void QgsMapRenderer::onDrawingProgress( int current, int total )
00653 {
00654   Q_UNUSED( current );
00655   Q_UNUSED( total );
00656   // TODO: emit signal with progress
00657 // QgsDebugMsg(QString("onDrawingProgress: %1 / %2").arg(current).arg(total));
00658   emit updateMap();
00659 }
00660 
00661 void QgsMapRenderer::setProjectionsEnabled( bool enabled )
00662 {
00663   if ( mProjectionsEnabled != enabled )
00664   {
00665     mProjectionsEnabled = enabled;
00666     QgsDebugMsg( "Adjusting DistArea projection on/off" );
00667     mDistArea->setProjectionsEnabled( enabled );
00668     updateFullExtent();
00669     mLastExtent.setMinimal();
00670     emit hasCrsTransformEnabled( enabled );
00671   }
00672 }
00673 
00674 bool QgsMapRenderer::hasCrsTransformEnabled()
00675 {
00676   return mProjectionsEnabled;
00677 }
00678 
00679 void QgsMapRenderer::setDestinationCrs( const QgsCoordinateReferenceSystem& crs )
00680 {
00681   QgsDebugMsg( "* Setting destCRS : = " + crs.toProj4() );
00682   QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( crs.srsid() ) );
00683   if ( *mDestCRS != crs )
00684   {
00685     QgsRectangle rect;
00686     if ( !mExtent.isEmpty() )
00687     {
00688       QgsCoordinateTransform transform( *mDestCRS, crs );
00689       rect = transform.transformBoundingBox( mExtent );
00690     }
00691 
00692     invalidateCachedLayerCrs();
00693     QgsDebugMsg( "Setting DistArea CRS to " + QString::number( crs.srsid() ) );
00694     mDistArea->setSourceCrs( crs.srsid() );
00695     *mDestCRS = crs;
00696     updateFullExtent();
00697 
00698     if ( !rect.isEmpty() )
00699     {
00700       setExtent( rect );
00701     }
00702 
00703     emit destinationSrsChanged();
00704   }
00705 }
00706 
00707 const QgsCoordinateReferenceSystem& QgsMapRenderer::destinationCrs()
00708 {
00709   QgsDebugMsgLevel( "* Returning destCRS", 3 );
00710   QgsDebugMsgLevel( "* DestCRS.srsid() = " + QString::number( mDestCRS->srsid() ), 3 );
00711   QgsDebugMsgLevel( "* DestCRS.proj4() = " + mDestCRS->toProj4(), 3 );
00712   return *mDestCRS;
00713 }
00714 
00715 
00716 bool QgsMapRenderer::splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent, QgsRectangle& r2 )
00717 {
00718   bool split = false;
00719 
00720   if ( hasCrsTransformEnabled() )
00721   {
00722     try
00723     {
00724 #ifdef QGISDEBUG
00725       // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__);
00726 #endif
00727       // Split the extent into two if the source CRS is
00728       // geographic and the extent crosses the split in
00729       // geographic coordinates (usually +/- 180 degrees,
00730       // and is assumed to be so here), and draw each
00731       // extent separately.
00732       static const double splitCoord = 180.0;
00733 
00734       if ( mCachedTr->sourceCrs().geographicFlag() )
00735       {
00736         // Note: ll = lower left point
00737         //   and ur = upper right point
00738         QgsPoint ll = tr( layer )->transform( extent.xMinimum(), extent.yMinimum(),
00739                                               QgsCoordinateTransform::ReverseTransform );
00740 
00741         QgsPoint ur = tr( layer )->transform( extent.xMaximum(), extent.yMaximum(),
00742                                               QgsCoordinateTransform::ReverseTransform );
00743 
00744         extent = tr( layer )->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
00745 
00746         if ( ll.x() > ur.x() )
00747         {
00748           r2 = extent;
00749           extent.setXMinimum( splitCoord );
00750           r2.setXMaximum( splitCoord );
00751           split = true;
00752         }
00753       }
00754       else // can't cross 180
00755       {
00756         extent = tr( layer )->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
00757       }
00758     }
00759     catch ( QgsCsException &cse )
00760     {
00761       Q_UNUSED( cse );
00762       QgsDebugMsg( "Transform error caught" );
00763       extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX );
00764       r2     = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX );
00765     }
00766   }
00767   return split;
00768 }
00769 
00770 
00771 QgsRectangle QgsMapRenderer::layerExtentToOutputExtent( QgsMapLayer* theLayer, QgsRectangle extent )
00772 {
00773   QgsDebugMsg( QString( "sourceCrs = " + tr( theLayer )->sourceCrs().authid() ) );
00774   QgsDebugMsg( QString( "destCRS = " + tr( theLayer )->destCRS().authid() ) );
00775   QgsDebugMsg( QString( "extent = " + extent.toString() ) );
00776   if ( hasCrsTransformEnabled() )
00777   {
00778     try
00779     {
00780       extent = tr( theLayer )->transformBoundingBox( extent );
00781     }
00782     catch ( QgsCsException &cse )
00783     {
00784       QgsMessageLog::logMessage( tr( "Transform error caught: %1" ).arg( cse.what() ), tr( "CRS" ) );
00785     }
00786   }
00787 
00788   QgsDebugMsg( QString( "proj extent = " + extent.toString() ) );
00789 
00790   return extent;
00791 }
00792 
00793 QgsPoint QgsMapRenderer::layerToMapCoordinates( QgsMapLayer* theLayer, QgsPoint point )
00794 {
00795   if ( hasCrsTransformEnabled() )
00796   {
00797     try
00798     {
00799       point = tr( theLayer )->transform( point, QgsCoordinateTransform::ForwardTransform );
00800     }
00801     catch ( QgsCsException &cse )
00802     {
00803       Q_UNUSED( cse );
00804       QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
00805     }
00806   }
00807   else
00808   {
00809     // leave point without transformation
00810   }
00811   return point;
00812 }
00813 
00814 QgsPoint QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsPoint point )
00815 {
00816   if ( hasCrsTransformEnabled() )
00817   {
00818     try
00819     {
00820       point = tr( theLayer )->transform( point, QgsCoordinateTransform::ReverseTransform );
00821     }
00822     catch ( QgsCsException &cse )
00823     {
00824       QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
00825       throw cse; //let client classes know there was a transformation error
00826     }
00827   }
00828   else
00829   {
00830     // leave point without transformation
00831   }
00832   return point;
00833 }
00834 
00835 QgsRectangle QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsRectangle rect )
00836 {
00837   if ( hasCrsTransformEnabled() )
00838   {
00839     try
00840     {
00841       rect = tr( theLayer )->transform( rect, QgsCoordinateTransform::ReverseTransform );
00842     }
00843     catch ( QgsCsException &cse )
00844     {
00845       QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
00846       throw cse; //let client classes know there was a transformation error
00847     }
00848   }
00849   return rect;
00850 }
00851 
00852 
00853 void QgsMapRenderer::updateFullExtent()
00854 {
00855   QgsDebugMsg( "called." );
00856   QgsMapLayerRegistry* registry = QgsMapLayerRegistry::instance();
00857 
00858   // reset the map canvas extent since the extent may now be smaller
00859   // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
00860   mFullExtent.setMinimal();
00861 
00862   // iterate through the map layers and test each layers extent
00863   // against the current min and max values
00864   QStringList::iterator it = mLayerSet.begin();
00865   QgsDebugMsg( QString( "Layer count: %1" ).arg( mLayerSet.count() ) );
00866   while ( it != mLayerSet.end() )
00867   {
00868     QgsMapLayer * lyr = registry->mapLayer( *it );
00869     if ( lyr == NULL )
00870     {
00871       QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) );
00872     }
00873     else
00874     {
00875       QgsDebugMsg( "Updating extent using " + lyr->name() );
00876       QgsDebugMsg( "Input extent: " + lyr->extent().toString() );
00877 
00878       // Layer extents are stored in the coordinate system (CS) of the
00879       // layer. The extent must be projected to the canvas CS
00880       QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() );
00881 
00882       QgsDebugMsg( "Output extent: " + extent.toString() );
00883       mFullExtent.unionRect( extent );
00884 
00885     }
00886     it++;
00887   }
00888 
00889   if ( mFullExtent.width() == 0.0 || mFullExtent.height() == 0.0 )
00890   {
00891     // If all of the features are at the one point, buffer the
00892     // rectangle a bit. If they are all at zero, do something a bit
00893     // more crude.
00894 
00895     if ( mFullExtent.xMinimum() == 0.0 && mFullExtent.xMaximum() == 0.0 &&
00896          mFullExtent.yMinimum() == 0.0 && mFullExtent.yMaximum() == 0.0 )
00897     {
00898       mFullExtent.set( -1.0, -1.0, 1.0, 1.0 );
00899     }
00900     else
00901     {
00902       const double padFactor = 1e-8;
00903       double widthPad = mFullExtent.xMinimum() * padFactor;
00904       double heightPad = mFullExtent.yMinimum() * padFactor;
00905       double xmin = mFullExtent.xMinimum() - widthPad;
00906       double xmax = mFullExtent.xMaximum() + widthPad;
00907       double ymin = mFullExtent.yMinimum() - heightPad;
00908       double ymax = mFullExtent.yMaximum() + heightPad;
00909       mFullExtent.set( xmin, ymin, xmax, ymax );
00910     }
00911   }
00912 
00913   QgsDebugMsg( "Full extent: " + mFullExtent.toString() );
00914 }
00915 
00916 QgsRectangle QgsMapRenderer::fullExtent()
00917 {
00918   updateFullExtent();
00919   return mFullExtent;
00920 }
00921 
00922 void QgsMapRenderer::setLayerSet( const QStringList& layers )
00923 {
00924   QgsDebugMsg( QString( "Entering: %1" ).arg( layers.join( ", " ) ) );
00925   mLayerSet = layers;
00926   updateFullExtent();
00927 }
00928 
00929 QStringList& QgsMapRenderer::layerSet()
00930 {
00931   return mLayerSet;
00932 }
00933 
00934 QgsOverlayObjectPositionManager* QgsMapRenderer::overlayManagerFromSettings()
00935 {
00936   QSettings settings;
00937   QString overlayAlgorithmQString = settings.value( "qgis/overlayPlacementAlgorithm", "Central point" ).toString();
00938 
00939   QgsOverlayObjectPositionManager* result = 0;
00940 
00941   if ( overlayAlgorithmQString != "Central point" )
00942   {
00943     QgsPALObjectPositionManager* palManager = new QgsPALObjectPositionManager();
00944     if ( overlayAlgorithmQString == "Chain" )
00945     {
00946       palManager->setPlacementAlgorithm( "Chain" );
00947     }
00948     else if ( overlayAlgorithmQString == "Popmusic tabu chain" )
00949     {
00950       palManager->setPlacementAlgorithm( "Popmusic tabu chain" );
00951     }
00952     else if ( overlayAlgorithmQString == "Popmusic tabu" )
00953     {
00954       palManager->setPlacementAlgorithm( "Popmusic tabu" );
00955     }
00956     else if ( overlayAlgorithmQString == "Popmusic chain" )
00957     {
00958       palManager->setPlacementAlgorithm( "Popmusic chain" );
00959     }
00960     result = palManager;
00961   }
00962   else
00963   {
00964     result = new QgsCentralPointPositionManager();
00965   }
00966 
00967   return result;
00968 }
00969 
00970 bool QgsMapRenderer::readXML( QDomNode & theNode )
00971 {
00972   QDomNode myNode = theNode.namedItem( "units" );
00973   QDomElement element = myNode.toElement();
00974 
00975   // set units
00976   QGis::UnitType units;
00977   if ( "meters" == element.text() )
00978   {
00979     units = QGis::Meters;
00980   }
00981   else if ( "feet" == element.text() )
00982   {
00983     units = QGis::Feet;
00984   }
00985   else if ( "degrees" == element.text() )
00986   {
00987     units = QGis::Degrees;
00988   }
00989   else if ( "unknown" == element.text() )
00990   {
00991     units = QGis::UnknownUnit;
00992   }
00993   else
00994   {
00995     QgsDebugMsg( "Unknown map unit type " + element.text() );
00996     units = QGis::Degrees;
00997   }
00998   setMapUnits( units );
00999 
01000   // set projections flag
01001   QDomNode projNode = theNode.namedItem( "projections" );
01002   element = projNode.toElement();
01003   setProjectionsEnabled( element.text().toInt() );
01004 
01005   // set destination CRS
01006   QgsCoordinateReferenceSystem srs;
01007   QDomNode srsNode = theNode.namedItem( "destinationsrs" );
01008   srs.readXML( srsNode );
01009   setDestinationCrs( srs );
01010 
01011   // set extent
01012   QgsRectangle aoi;
01013   QDomNode extentNode = theNode.namedItem( "extent" );
01014 
01015   QDomNode xminNode = extentNode.namedItem( "xmin" );
01016   QDomNode yminNode = extentNode.namedItem( "ymin" );
01017   QDomNode xmaxNode = extentNode.namedItem( "xmax" );
01018   QDomNode ymaxNode = extentNode.namedItem( "ymax" );
01019 
01020   QDomElement exElement = xminNode.toElement();
01021   double xmin = exElement.text().toDouble();
01022   aoi.setXMinimum( xmin );
01023 
01024   exElement = yminNode.toElement();
01025   double ymin = exElement.text().toDouble();
01026   aoi.setYMinimum( ymin );
01027 
01028   exElement = xmaxNode.toElement();
01029   double xmax = exElement.text().toDouble();
01030   aoi.setXMaximum( xmax );
01031 
01032   exElement = ymaxNode.toElement();
01033   double ymax = exElement.text().toDouble();
01034   aoi.setYMaximum( ymax );
01035 
01036   setExtent( aoi );
01037   return true;
01038 }
01039 
01040 bool QgsMapRenderer::writeXML( QDomNode & theNode, QDomDocument & theDoc )
01041 {
01042   // units
01043 
01044   QDomElement unitsNode = theDoc.createElement( "units" );
01045   theNode.appendChild( unitsNode );
01046 
01047   QString unitsString;
01048 
01049   switch ( mapUnits() )
01050   {
01051     case QGis::Meters:
01052       unitsString = "meters";
01053       break;
01054     case QGis::Feet:
01055       unitsString = "feet";
01056       break;
01057     case QGis::Degrees:
01058       unitsString = "degrees";
01059       break;
01060     case QGis::UnknownUnit:
01061     default:
01062       unitsString = "unknown";
01063       break;
01064   }
01065   QDomText unitsText = theDoc.createTextNode( unitsString );
01066   unitsNode.appendChild( unitsText );
01067 
01068 
01069   // Write current view extents
01070   QDomElement extentNode = theDoc.createElement( "extent" );
01071   theNode.appendChild( extentNode );
01072 
01073   QDomElement xMin = theDoc.createElement( "xmin" );
01074   QDomElement yMin = theDoc.createElement( "ymin" );
01075   QDomElement xMax = theDoc.createElement( "xmax" );
01076   QDomElement yMax = theDoc.createElement( "ymax" );
01077 
01078   QgsRectangle r = extent();
01079   QDomText xMinText = theDoc.createTextNode( QString::number( r.xMinimum(), 'f' ) );
01080   QDomText yMinText = theDoc.createTextNode( QString::number( r.yMinimum(), 'f' ) );
01081   QDomText xMaxText = theDoc.createTextNode( QString::number( r.xMaximum(), 'f' ) );
01082   QDomText yMaxText = theDoc.createTextNode( QString::number( r.yMaximum(), 'f' ) );
01083 
01084   xMin.appendChild( xMinText );
01085   yMin.appendChild( yMinText );
01086   xMax.appendChild( xMaxText );
01087   yMax.appendChild( yMaxText );
01088 
01089   extentNode.appendChild( xMin );
01090   extentNode.appendChild( yMin );
01091   extentNode.appendChild( xMax );
01092   extentNode.appendChild( yMax );
01093 
01094   // projections enabled
01095   QDomElement projNode = theDoc.createElement( "projections" );
01096   theNode.appendChild( projNode );
01097 
01098   QDomText projText = theDoc.createTextNode( QString::number( hasCrsTransformEnabled() ) );
01099   projNode.appendChild( projText );
01100 
01101   // destination CRS
01102   QDomElement srsNode = theDoc.createElement( "destinationsrs" );
01103   theNode.appendChild( srsNode );
01104   destinationCrs().writeXML( srsNode, theDoc );
01105 
01106   return true;
01107 }
01108 
01109 void QgsMapRenderer::setLabelingEngine( QgsLabelingEngineInterface* iface )
01110 {
01111   if ( mLabelingEngine )
01112     delete mLabelingEngine;
01113 
01114   mLabelingEngine = iface;
01115 }
01116 
01117 QgsCoordinateTransform *QgsMapRenderer::tr( QgsMapLayer *layer )
01118 {
01119   if ( mCachedTrForLayer != layer )
01120   {
01121     invalidateCachedLayerCrs();
01122 
01123     delete mCachedTr;
01124     mCachedTr = new QgsCoordinateTransform( layer->crs(), *mDestCRS );
01125     mCachedTrForLayer = layer;
01126 
01127     connect( layer, SIGNAL( layerCrsChanged() ), this, SLOT( invalidateCachedLayerCrs() ) );
01128     connect( layer, SIGNAL( destroyed() ), this, SLOT( cachedLayerDestroyed() ) );
01129   }
01130 
01131   return mCachedTr;
01132 }
01133 
01134 void QgsMapRenderer::cachedLayerDestroyed()
01135 {
01136   if ( mCachedTrForLayer == sender() )
01137     mCachedTrForLayer = 0;
01138 }
01139 
01140 void QgsMapRenderer::invalidateCachedLayerCrs()
01141 {
01142   if ( mCachedTrForLayer )
01143     disconnect( mCachedTrForLayer, SIGNAL( layerCrsChanged() ), this, SLOT( invalidateCachedLayerCrs() ) );
01144 
01145   mCachedTrForLayer = 0;
01146 }
01147 
01148 bool QgsMapRenderer::mDrawing = false;
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines