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