Quantum GIS API Documentation
1.7.4
|
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;