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