void QgsMapRendererJob::drawOldLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext ) { // render all labels for vector layers in the stack, starting at the base QListIterator<QString> li( settings.layers() ); li.toBack(); while ( li.hasPrevious() ) { if ( renderContext.renderingStopped() ) { break; } QString layerId = li.previous(); QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId ); if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) ) continue; // only make labels if the layer is visible // after scale dep viewing settings are checked if ( ml->hasScaleBasedVisibility() && ( settings.scale() < ml->minimumScale() || settings.scale() > ml->maximumScale() ) ) continue; bool split = false; const QgsCoordinateTransform* ct = 0; QgsRectangle r1 = settings.visibleExtent(), r2; if ( settings.hasCrsTransformEnabled() ) { ct = QgsCoordinateTransformCache::instance()->transform( ml->crs().authid(), settings.destinationCrs().authid() ); split = reprojectToLayerExtent( ct, ml->crs().geographicFlag(), r1, r2 ); } renderContext.setCoordinateTransform( ct ); renderContext.setExtent( r1 ); ml->drawLabels( renderContext ); if ( split ) { renderContext.setExtent( r2 ); ml->drawLabels( renderContext ); } } }
void QgsMapRenderer::render( QPainter* painter, double* forceWidthScale ) { //Lock render method for concurrent threads (e.g. from globe) QMutexLocker renderLock( &mRenderMutex ); //flag to see if the render context has changed //since the last time we rendered. If it hasnt changed we can //take some shortcuts with rendering bool mySameAsLastFlag = true; QgsDebugMsg( "========== Rendering ==========" ); if ( mExtent.isEmpty() ) { QgsDebugMsg( "empty extent... not rendering" ); return; } if ( mSize.width() == 1 && mSize.height() == 1 ) { QgsDebugMsg( "size 1x1... not rendering" ); return; } QPaintDevice* thePaintDevice = painter->device(); if ( !thePaintDevice ) { return; } // wait if ( mDrawing ) { QgsDebugMsg( "already rendering" ); QCoreApplication::processEvents(); } if ( mDrawing ) { QgsDebugMsg( "still rendering - skipping" ); return; } mDrawing = true; const QgsCoordinateTransform *ct; #ifdef QGISDEBUG QgsDebugMsg( "Starting to render layer stack." ); QTime renderTime; renderTime.start(); #endif if ( mOverview ) mRenderContext.setDrawEditingInformation( !mOverview ); mRenderContext.setPainter( painter ); mRenderContext.setCoordinateTransform( 0 ); //this flag is only for stopping during the current rendering progress, //so must be false at every new render operation mRenderContext.setRenderingStopped( false ); // set selection color QgsProject* prj = QgsProject::instance(); int myRed = prj->readNumEntry( "Gui", "/SelectionColorRedPart", 255 ); int myGreen = prj->readNumEntry( "Gui", "/SelectionColorGreenPart", 255 ); int myBlue = prj->readNumEntry( "Gui", "/SelectionColorBluePart", 0 ); int myAlpha = prj->readNumEntry( "Gui", "/SelectionColorAlphaPart", 255 ); mRenderContext.setSelectionColor( QColor( myRed, myGreen, myBlue, myAlpha ) ); //calculate scale factor //use the specified dpi and not those from the paint device //because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene) double sceneDpi = mScaleCalculator->dpi(); double scaleFactor = 1.0; if ( mOutputUnits == QgsMapRenderer::Millimeters ) { if ( forceWidthScale ) { scaleFactor = *forceWidthScale; } else { scaleFactor = sceneDpi / 25.4; } } double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi; if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor ) { mRenderContext.setRasterScaleFactor( rasterScaleFactor ); mySameAsLastFlag = false; } if ( mRenderContext.scaleFactor() != scaleFactor ) { mRenderContext.setScaleFactor( scaleFactor ); mySameAsLastFlag = false; } if ( mRenderContext.rendererScale() != mScale ) { //add map scale to render context mRenderContext.setRendererScale( mScale ); mySameAsLastFlag = false; } if ( mLastExtent != mExtent ) { mLastExtent = mExtent; mySameAsLastFlag = false; } mRenderContext.setLabelingEngine( mLabelingEngine ); if ( mLabelingEngine ) mLabelingEngine->init( this ); // know we know if this render is just a repeat of the last time, we // can clear caches if it has changed if ( !mySameAsLastFlag ) { //clear the cache pixmap if we changed resolution / extent QSettings mySettings; if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { QgsMapLayerRegistry::instance()->clearAllLayerCaches(); } } // render all layers in the stack, starting at the base QListIterator<QString> li( mLayerSet ); li.toBack(); QgsRectangle r1, r2; while ( li.hasPrevious() ) { if ( mRenderContext.renderingStopped() ) { break; } // Store the painter in case we need to swap it out for the // cache painter QPainter * mypContextPainter = mRenderContext.painter(); // Flattened image for drawing when a blending mode is set QImage * mypFlattenedImage = 0; QString layerId = li.previous(); QgsDebugMsg( "Rendering at layer item " + layerId ); // This call is supposed to cause the progress bar to // advance. However, it seems that updating the progress bar is // incompatible with having a QPainter active (the one that is // passed into this function), as Qt produces a number of errors // when try to do so. I'm (Gavin) not sure how to fix this, but // added these comments and debug statement to help others... QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" ); //emit drawingProgress(myRenderCounter++, mLayerSet.size()); QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId ); if ( !ml ) { QgsDebugMsg( "Layer not found in registry!" ); continue; } QgsDebugMsg( QString( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 extent:%5 blendmode:%6" ) .arg( ml->name() ) .arg( ml->minimumScale() ) .arg( ml->maximumScale() ) .arg( ml->hasScaleBasedVisibility() ) .arg( ml->extent().toString() ) .arg( ml->blendMode() ) ); if ( mRenderContext.useAdvancedEffects() ) { // Set the QPainter composition mode so that this layer is rendered using // the desired blending mode mypContextPainter->setCompositionMode( ml->blendMode() ); } if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() <= mScale && mScale < ml->maximumScale() ) || mOverview ) { connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); // // Now do the call to the layer that actually does // the rendering work! // bool split = false; if ( hasCrsTransformEnabled() ) { r1 = mExtent; split = splitLayersExtent( ml, r1, r2 ); ct = transformation( ml ); mRenderContext.setExtent( r1 ); QgsDebugMsg( " extent 1: " + r1.toString() ); QgsDebugMsg( " extent 2: " + r2.toString() ); if ( !r1.isFinite() || !r2.isFinite() ) //there was a problem transforming the extent. Skip the layer { continue; } } else { ct = NULL; } mRenderContext.setCoordinateTransform( ct ); //decide if we have to scale the raster //this is necessary in case QGraphicsScene is used bool scaleRaster = false; QgsMapToPixel rasterMapToPixel; QgsMapToPixel bk_mapToPixel; if ( ml->type() == QgsMapLayer::RasterLayer && qAbs( rasterScaleFactor - 1.0 ) > 0.000001 ) { scaleRaster = true; } // Force render of layers that are being edited // or if there's a labeling engine that needs the layer to register features if ( ml->type() == QgsMapLayer::VectorLayer ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl->isEditable() || ( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) ) { ml->setCacheImage( 0 ); } } QSettings mySettings; bool useRenderCaching = false; if ( ! split )//render caching does not yet cater for split extents { if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { useRenderCaching = true; if ( !mySameAsLastFlag || ml->cacheImage() == 0 ) { QgsDebugMsg( "Caching enabled but layer redraw forced by extent change or empty cache" ); QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(), mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 ); if ( mypImage->isNull() ) { QgsDebugMsg( "insufficient memory for image " + QString::number( mRenderContext.painter()->device()->width() ) + "x" + QString::number( mRenderContext.painter()->device()->height() ) ); emit drawError( ml ); painter->end(); // drawError is not caught by anyone, so we end painting to notify caller return; } mypImage->fill( 0 ); ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you QPainter * mypPainter = new QPainter( ml->cacheImage() ); // Changed to enable anti aliasing by default in QGIS 1.7 if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() ) { mypPainter->setRenderHint( QPainter::Antialiasing ); } mRenderContext.setPainter( mypPainter ); } else if ( mySameAsLastFlag ) { //draw from cached image QgsDebugMsg( "Caching enabled --- drawing layer from cached image" ); mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) ); disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); //short circuit as there is nothing else to do... continue; } } } // If we are drawing with an alternative blending mode then we need to render to a separate image // before compositing this on the map. This effectively flattens the layer and prevents // blending occuring between objects on the layer // (this is not required for raster layers or when layer caching is enabled, since that has the same effect) bool flattenedLayer = false; if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if (( !useRenderCaching ) && (( vl->blendMode() != QPainter::CompositionMode_SourceOver ) || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver ) || ( vl->layerTransparency() != 0 ) ) ) { flattenedLayer = true; mypFlattenedImage = new QImage( mRenderContext.painter()->device()->width(), mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 ); if ( mypFlattenedImage->isNull() ) { QgsDebugMsg( "insufficient memory for image " + QString::number( mRenderContext.painter()->device()->width() ) + "x" + QString::number( mRenderContext.painter()->device()->height() ) ); emit drawError( ml ); painter->end(); // drawError is not caught by anyone, so we end painting to notify caller return; } mypFlattenedImage->fill( 0 ); QPainter * mypPainter = new QPainter( mypFlattenedImage ); if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() ) { mypPainter->setRenderHint( QPainter::Antialiasing ); } mypPainter->scale( rasterScaleFactor, rasterScaleFactor ); mRenderContext.setPainter( mypPainter ); } } // Per feature blending mode if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver ) { // set the painter to the feature blend mode, so that features drawn // on this layer will interact and blend with each other mRenderContext.painter()->setCompositionMode( vl->featureBlendMode() ); } } if ( scaleRaster ) { bk_mapToPixel = mRenderContext.mapToPixel(); rasterMapToPixel = mRenderContext.mapToPixel(); rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor ); rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor ); mRenderContext.setMapToPixel( rasterMapToPixel ); mRenderContext.painter()->save(); mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ); } if ( !ml->draw( mRenderContext ) ) { emit drawError( ml ); } else { QgsDebugMsg( "Layer rendered without issues" ); } if ( split ) { mRenderContext.setExtent( r2 ); if ( !ml->draw( mRenderContext ) ) { emit drawError( ml ); } } if ( scaleRaster ) { mRenderContext.setMapToPixel( bk_mapToPixel ); mRenderContext.painter()->restore(); } //apply layer transparency for vector layers if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl->layerTransparency() != 0 ) { // a layer transparency has been set, so update the alpha for the flattened layer // by combining it with the layer transparency QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * vl->layerTransparency() / 100 ) ); // use destination in composition mode to merge source's alpha with destination mRenderContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn ); mRenderContext.painter()->fillRect( 0, 0, mRenderContext.painter()->device()->width(), mRenderContext.painter()->device()->height(), transparentFillColor ); } } if ( useRenderCaching ) { // composite the cached image into our view and then clean up from caching // by reinstating the painter as it was swapped out for caching renders delete mRenderContext.painter(); mRenderContext.setPainter( mypContextPainter ); //draw from cached image that we created further up if ( ml->cacheImage() ) mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) ); } else if ( flattenedLayer ) { // If we flattened this layer for alternate blend modes, composite it now delete mRenderContext.painter(); mRenderContext.setPainter( mypContextPainter ); mypContextPainter->save(); mypContextPainter->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ); mypContextPainter->drawImage( 0, 0, *( mypFlattenedImage ) ); mypContextPainter->restore(); delete mypFlattenedImage; mypFlattenedImage = 0; } disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); }
LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsPalLabeling* labelingEngine ) { LayerRenderJobs layerJobs; // render all layers in the stack, starting at the base QListIterator<QString> li( mSettings.layers() ); li.toBack(); if ( mCache ) { bool cacheValid = mCache->init( mSettings.visibleExtent(), mSettings.scale() ); QgsDebugMsg( QString( "CACHE VALID: %1" ).arg( cacheValid ) ); Q_UNUSED( cacheValid ); } mGeometryCaches.clear(); while ( li.hasPrevious() ) { QString layerId = li.previous(); QgsDebugMsg( "Rendering at layer item " + layerId ); QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId ); if ( !ml ) { mErrors.append( Error( layerId, "Layer not found in registry." ) ); continue; } QgsDebugMsg( QString( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 extent:%5 blendmode:%6" ) .arg( ml->name() ) .arg( ml->minimumScale() ) .arg( ml->maximumScale() ) .arg( ml->hasScaleBasedVisibility() ) .arg( ml->extent().toString() ) .arg( ml->blendMode() ) ); if ( ml->hasScaleBasedVisibility() && ( mSettings.scale() < ml->minimumScale() || mSettings.scale() > ml->maximumScale() ) ) //|| mOverview ) { QgsDebugMsg( "Layer not rendered because it is not within the defined visibility scale range" ); continue; } QgsRectangle r1 = mSettings.visibleExtent(), r2; const QgsCoordinateTransform* ct = 0; if ( mSettings.hasCrsTransformEnabled() ) { ct = mSettings.layerTransfrom( ml ); if ( ct ) { reprojectToLayerExtent( ct, ml->crs().geographicFlag(), r1, r2 ); } QgsDebugMsg( "extent: " + r1.toString() ); if ( !r1.isFinite() || !r2.isFinite() ) { mErrors.append( Error( layerId, "There was a problem transforming layer's' extent. Layer skipped." ) ); continue; } } // Force render of layers that are being edited // or if there's a labeling engine that needs the layer to register features if ( mCache && ml->type() == QgsMapLayer::VectorLayer ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl->isEditable() || ( labelingEngine && labelingEngine->willUseLayer( vl ) ) ) mCache->clearCacheImage( ml->id() ); } layerJobs.append( LayerRenderJob() ); LayerRenderJob& job = layerJobs.last(); job.cached = false; job.img = 0; job.blendMode = ml->blendMode(); job.layerId = ml->id(); job.context = QgsRenderContext::fromMapSettings( mSettings ); job.context.setPainter( painter ); job.context.setLabelingEngine( labelingEngine ); job.context.setCoordinateTransform( ct ); job.context.setExtent( r1 ); // if we can use the cache, let's do it and avoid rendering! if ( mCache && !mCache->cacheImage( ml->id() ).isNull() ) { job.cached = true; job.img = new QImage( mCache->cacheImage( ml->id() ) ); job.renderer = 0; job.context.setPainter( 0 ); continue; } // If we are drawing with an alternative blending mode then we need to render to a separate image // before compositing this on the map. This effectively flattens the layer and prevents // blending occuring between objects on the layer if ( mCache || !painter || needTemporaryImage( ml ) ) { // Flattened image for drawing when a blending mode is set QImage * mypFlattenedImage = 0; mypFlattenedImage = new QImage( mSettings.outputSize().width(), mSettings.outputSize().height(), mSettings.outputImageFormat() ); if ( mypFlattenedImage->isNull() ) { mErrors.append( Error( layerId, "Insufficient memory for image " + QString::number( mSettings.outputSize().width() ) + "x" + QString::number( mSettings.outputSize().height() ) ) ); delete mypFlattenedImage; layerJobs.removeLast(); continue; } mypFlattenedImage->fill( 0 ); job.img = mypFlattenedImage; QPainter* mypPainter = new QPainter( job.img ); mypPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) ); job.context.setPainter( mypPainter ); } job.renderer = ml->createMapRenderer( job.context ); if ( mRequestedGeomCacheForLayers.contains( ml->id() ) ) { if ( QgsVectorLayerRenderer* vlr = dynamic_cast<QgsVectorLayerRenderer*>( job.renderer ) ) { vlr->setGeometryCachePointer( &mGeometryCaches[ ml->id()] ); } } } // while (li.hasPrevious()) return layerJobs; }
void QgsMapRenderer::render( QPainter* painter ) { //flag to see if the render context has changed //since the last time we rendered. If it hasnt changed we can //take some shortcuts with rendering bool mySameAsLastFlag = true; QgsDebugMsg( "========== Rendering ==========" ); if ( mExtent.isEmpty() ) { QgsDebugMsg( "empty extent... not rendering" ); return; } if ( mSize.width() == 1 && mSize.height() == 1 ) { QgsDebugMsg( "size 1x1... not rendering" ); return; } QPaintDevice* thePaintDevice = painter->device(); if ( !thePaintDevice ) { return; } // wait if ( mDrawing ) { QgsDebugMsg( "already rendering" ); QCoreApplication::processEvents(); } if ( mDrawing ) { QgsDebugMsg( "still rendering - skipping" ); return; } mDrawing = true; QgsCoordinateTransform* ct; #ifdef QGISDEBUG QgsDebugMsg( "Starting to render layer stack." ); QTime renderTime; renderTime.start(); #endif if ( mOverview ) mRenderContext.setDrawEditingInformation( !mOverview ); mRenderContext.setPainter( painter ); mRenderContext.setCoordinateTransform( 0 ); //this flag is only for stopping during the current rendering progress, //so must be false at every new render operation mRenderContext.setRenderingStopped( false ); //calculate scale factor //use the specified dpi and not those from the paint device //because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene) double sceneDpi = mScaleCalculator->dpi(); double scaleFactor = 1.0; if ( mOutputUnits == QgsMapRenderer::Millimeters ) { scaleFactor = sceneDpi / 25.4; } double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi; if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor ) { mRenderContext.setRasterScaleFactor( rasterScaleFactor ); mySameAsLastFlag = false; } if ( mRenderContext.scaleFactor() != scaleFactor ) { mRenderContext.setScaleFactor( scaleFactor ); mySameAsLastFlag = false; } if ( mRenderContext.rendererScale() != mScale ) { //add map scale to render context mRenderContext.setRendererScale( mScale ); mySameAsLastFlag = false; } if ( mLastExtent != mExtent ) { mLastExtent = mExtent; mySameAsLastFlag = false; } mRenderContext.setLabelingEngine( mLabelingEngine ); if ( mLabelingEngine ) mLabelingEngine->init( this ); // know we know if this render is just a repeat of the last time, we // can clear caches if it has changed if ( !mySameAsLastFlag ) { //clear the cache pixmap if we changed resolution / extent QSettings mySettings; if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { QgsMapLayerRegistry::instance()->clearAllLayerCaches(); } } QgsOverlayObjectPositionManager* overlayManager = overlayManagerFromSettings(); QList<QgsVectorOverlay*> allOverlayList; //list of all overlays, used to draw them after layers have been rendered // render all layers in the stack, starting at the base QListIterator<QString> li( mLayerSet ); li.toBack(); QgsRectangle r1, r2; while ( li.hasPrevious() ) { if ( mRenderContext.renderingStopped() ) { break; } // Store the painter in case we need to swap it out for the // cache painter QPainter * mypContextPainter = mRenderContext.painter(); QString layerId = li.previous(); QgsDebugMsg( "Rendering at layer item " + layerId ); // This call is supposed to cause the progress bar to // advance. However, it seems that updating the progress bar is // incompatible with having a QPainter active (the one that is // passed into this function), as Qt produces a number of errors // when try to do so. I'm (Gavin) not sure how to fix this, but // added these comments and debug statement to help others... QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" ); //emit drawingProgress(myRenderCounter++, mLayerSet.size()); QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId ); if ( !ml ) { QgsDebugMsg( "Layer not found in registry!" ); continue; } QgsDebugMsg( "Rendering layer " + ml->name() ); QgsDebugMsg( " Layer minscale " + QString( "%1" ).arg( ml->minimumScale() ) ); QgsDebugMsg( " Layer maxscale " + QString( "%1" ).arg( ml->maximumScale() ) ); QgsDebugMsg( " Scale dep. visibility enabled? " + QString( "%1" ).arg( ml->hasScaleBasedVisibility() ) ); QgsDebugMsg( " Input extent: " + ml->extent().toString() ); if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) || mOverview ) { connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); // // Now do the call to the layer that actually does // the rendering work! // bool split = false; if ( hasCrsTransformEnabled() ) { r1 = mExtent; split = splitLayersExtent( ml, r1, r2 ); ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS ); mRenderContext.setExtent( r1 ); QgsDebugMsg( " extent 1: " + r1.toString() ); QgsDebugMsg( " extent 2: " + r2.toString() ); if ( !r1.isFinite() || !r2.isFinite() ) //there was a problem transforming the extent. Skip the layer { continue; } } else { ct = NULL; } mRenderContext.setCoordinateTransform( ct ); //decide if we have to scale the raster //this is necessary in case QGraphicsScene is used bool scaleRaster = false; QgsMapToPixel rasterMapToPixel; QgsMapToPixel bk_mapToPixel; if ( ml->type() == QgsMapLayer::RasterLayer && qAbs( rasterScaleFactor - 1.0 ) > 0.000001 ) { scaleRaster = true; } //create overlay objects for features within the view extent if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl ) { QList<QgsVectorOverlay*> thisLayerOverlayList; vl->vectorOverlays( thisLayerOverlayList ); QList<QgsVectorOverlay*>::iterator overlayIt = thisLayerOverlayList.begin(); for ( ; overlayIt != thisLayerOverlayList.end(); ++overlayIt ) { if (( *overlayIt )->displayFlag() ) { ( *overlayIt )->createOverlayObjects( mRenderContext ); allOverlayList.push_back( *overlayIt ); } } overlayManager->addLayer( vl, thisLayerOverlayList ); } } // Force render of layers that are being edited // or if there's a labeling engine that needs the layer to register features if ( ml->type() == QgsMapLayer::VectorLayer ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl->isEditable() || ( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) ) { ml->setCacheImage( 0 ); } } QSettings mySettings; if ( ! split )//render caching does not yet cater for split extents { if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { if ( !mySameAsLastFlag || ml->cacheImage() == 0 ) { QgsDebugMsg( "\n\n\nCaching enabled but layer redraw forced by extent change or empty cache\n\n\n" ); QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(), mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 ); mypImage->fill( 0 ); ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you QPainter * mypPainter = new QPainter( ml->cacheImage() ); // Changed to enable anti aliasing by default in QGIS 1.7 if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() ) { mypPainter->setRenderHint( QPainter::Antialiasing ); } mRenderContext.setPainter( mypPainter ); } else if ( mySameAsLastFlag ) { //draw from cached image QgsDebugMsg( "\n\n\nCaching enabled --- drawing layer from cached image\n\n\n" ); mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) ); disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); //short circuit as there is nothing else to do... continue; } } } if ( scaleRaster ) { bk_mapToPixel = mRenderContext.mapToPixel(); rasterMapToPixel = mRenderContext.mapToPixel(); rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor ); rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor ); mRenderContext.setMapToPixel( rasterMapToPixel ); mRenderContext.painter()->save(); mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ); } if ( !ml->draw( mRenderContext ) ) { emit drawError( ml ); } else { QgsDebugMsg( "Layer rendered without issues" ); } if ( split ) { mRenderContext.setExtent( r2 ); if ( !ml->draw( mRenderContext ) ) { emit drawError( ml ); } } if ( scaleRaster ) { mRenderContext.setMapToPixel( bk_mapToPixel ); mRenderContext.painter()->restore(); } if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { if ( !split ) { // composite the cached image into our view and then clean up from caching // by reinstating the painter as it was swapped out for caching renders delete mRenderContext.painter(); mRenderContext.setPainter( mypContextPainter ); //draw from cached image that we created further up mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) ); } } disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); } else // layer not visible due to scale {
LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEngine *labelingEngine2 ) { LayerRenderJobs layerJobs; // render all layers in the stack, starting at the base QListIterator<QgsMapLayer *> li( mSettings.layers() ); li.toBack(); if ( mCache ) { bool cacheValid = mCache->init( mSettings.visibleExtent(), mSettings.scale() ); Q_UNUSED( cacheValid ); QgsDebugMsgLevel( QString( "CACHE VALID: %1" ).arg( cacheValid ), 4 ); } bool requiresLabelRedraw = !( mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) ); while ( li.hasPrevious() ) { QgsMapLayer *ml = li.previous(); QgsDebugMsgLevel( QString( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5" ) .arg( ml->name() ) .arg( ml->minimumScale() ) .arg( ml->maximumScale() ) .arg( ml->hasScaleBasedVisibility() ) .arg( ml->blendMode() ) , 3 ); if ( !ml->isInScaleRange( mSettings.scale() ) ) //|| mOverview ) { QgsDebugMsgLevel( "Layer not rendered because it is not within the defined visibility scale range", 3 ); continue; } QgsRectangle r1 = mSettings.visibleExtent(), r2; QgsCoordinateTransform ct; ct = mSettings.layerTransform( ml ); if ( ct.isValid() ) { reprojectToLayerExtent( ml, ct, r1, r2 ); } QgsDebugMsgLevel( "extent: " + r1.toString(), 3 ); if ( !r1.isFinite() || !r2.isFinite() ) { mErrors.append( Error( ml->id(), tr( "There was a problem transforming the layer's extent. Layer skipped." ) ) ); continue; } // Force render of layers that are being edited // or if there's a labeling engine that needs the layer to register features if ( mCache && ml->type() == QgsMapLayer::VectorLayer ) { QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml ); bool requiresLabeling = false; requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( vl ) ) && requiresLabelRedraw; if ( vl->isEditable() || requiresLabeling ) { mCache->clearCacheImage( ml->id() ); } } layerJobs.append( LayerRenderJob() ); LayerRenderJob &job = layerJobs.last(); job.cached = false; job.img = nullptr; job.layer = ml; job.renderingTime = -1; job.context = QgsRenderContext::fromMapSettings( mSettings ); job.context.expressionContext().appendScope( QgsExpressionContextUtils::layerScope( ml ) ); job.context.setPainter( painter ); job.context.setLabelingEngine( labelingEngine2 ); job.context.setCoordinateTransform( ct ); job.context.setExtent( r1 ); if ( mFeatureFilterProvider ) job.context.setFeatureFilterProvider( mFeatureFilterProvider ); QgsMapLayerStyleOverride styleOverride( ml ); if ( mSettings.layerStyleOverrides().contains( ml->id() ) ) styleOverride.setOverrideStyle( mSettings.layerStyleOverrides().value( ml->id() ) ); job.blendMode = ml->blendMode(); job.opacity = 1.0; if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml ) ) { job.opacity = vl->opacity(); } // if we can use the cache, let's do it and avoid rendering! if ( mCache && mCache->hasCacheImage( ml->id() ) ) { job.cached = true; job.imageInitialized = true; job.img = new QImage( mCache->cacheImage( ml->id() ) ); job.renderer = nullptr; job.context.setPainter( nullptr ); continue; } // If we are drawing with an alternative blending mode then we need to render to a separate image // before compositing this on the map. This effectively flattens the layer and prevents // blending occurring between objects on the layer if ( mCache || !painter || needTemporaryImage( ml ) ) { // Flattened image for drawing when a blending mode is set QImage *mypFlattenedImage = nullptr; mypFlattenedImage = new QImage( mSettings.outputSize().width(), mSettings.outputSize().height(), mSettings.outputImageFormat() ); if ( mypFlattenedImage->isNull() ) { mErrors.append( Error( ml->id(), tr( "Insufficient memory for image %1x%2" ).arg( mSettings.outputSize().width() ).arg( mSettings.outputSize().height() ) ) ); delete mypFlattenedImage; layerJobs.removeLast(); continue; } job.img = mypFlattenedImage; QPainter *mypPainter = new QPainter( job.img ); mypPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) ); job.context.setPainter( mypPainter ); } QTime layerTime; layerTime.start(); job.renderer = ml->createMapRenderer( job.context ); job.renderingTime = layerTime.elapsed(); // include job preparation time in layer rendering time } // while (li.hasPrevious()) return layerJobs; }
QList< layerDef > getWmtsLayerList( QgsServerInterface *serverIface, const QgsProject *project ) { QList< layerDef > wmtsLayers; #ifdef HAVE_SERVER_PYTHON_PLUGINS QgsAccessControl *accessControl = serverIface->accessControls(); #else ( void )serverIface; #endif // WMTS Project configuration bool wmtsProject = project->readBoolEntry( QStringLiteral( "WMTSLayers" ), QStringLiteral( "Project" ) ); // Root Layer name QString rootLayerName = QgsServerProjectUtils::wmsRootName( *project ); if ( rootLayerName.isEmpty() && !project->title().isEmpty() ) { rootLayerName = project->title(); } if ( wmtsProject && !rootLayerName.isEmpty() ) { layerDef pLayer; pLayer.id = rootLayerName; if ( !project->title().isEmpty() ) { pLayer.title = project->title(); pLayer.abstract = project->title(); } //transform the project native CRS into WGS84 QgsRectangle projRect = QgsServerProjectUtils::wmsExtent( *project ); QgsCoordinateReferenceSystem projCrs = project->crs(); QgsCoordinateTransform exGeoTransform( projCrs, wgs84, project ); try { pLayer.wgs84BoundingRect = exGeoTransform.transformBoundingBox( projRect ); } catch ( const QgsCsException & ) { pLayer.wgs84BoundingRect = QgsRectangle( -180, -90, 180, 90 ); } // Formats bool wmtsPngProject = project->readBoolEntry( QStringLiteral( "WMTSPngLayers" ), QStringLiteral( "Project" ) ); if ( wmtsPngProject ) pLayer.formats << QStringLiteral( "image/png" ); bool wmtsJpegProject = project->readBoolEntry( QStringLiteral( "WMTSJpegLayers" ), QStringLiteral( "Project" ) ); if ( wmtsJpegProject ) pLayer.formats << QStringLiteral( "image/jpeg" ); // Project is not queryable in WMS //pLayer.queryable = ( nonIdentifiableLayers.count() != project->count() ); pLayer.queryable = false; wmtsLayers.append( pLayer ); } QStringList wmtsGroupNameList = project->readListEntry( QStringLiteral( "WMTSLayers" ), QStringLiteral( "Group" ) ); if ( !wmtsGroupNameList.isEmpty() ) { QgsLayerTreeGroup *treeRoot = project->layerTreeRoot(); QStringList wmtsPngGroupNameList = project->readListEntry( QStringLiteral( "WMTSPngLayers" ), QStringLiteral( "Group" ) ); QStringList wmtsJpegGroupNameList = project->readListEntry( QStringLiteral( "WMTSJpegLayers" ), QStringLiteral( "Group" ) ); for ( const QString &gName : wmtsGroupNameList ) { QgsLayerTreeGroup *treeGroup = treeRoot->findGroup( gName ); if ( !treeGroup ) { continue; } layerDef pLayer; pLayer.id = treeGroup->customProperty( QStringLiteral( "wmsShortName" ) ).toString(); if ( pLayer.id.isEmpty() ) pLayer.id = gName; pLayer.title = treeGroup->customProperty( QStringLiteral( "wmsTitle" ) ).toString(); if ( pLayer.title.isEmpty() ) pLayer.title = gName; pLayer.abstract = treeGroup->customProperty( QStringLiteral( "wmsAbstract" ) ).toString(); QgsRectangle wgs84BoundingRect; bool queryable = false; double maxScale = 0.0; double minScale = 0.0; for ( QgsLayerTreeLayer *layer : treeGroup->findLayers() ) { QgsMapLayer *l = layer->layer(); if ( !l ) { continue; } //transform the layer native CRS into WGS84 QgsCoordinateReferenceSystem layerCrs = l->crs(); QgsCoordinateTransform exGeoTransform( layerCrs, wgs84, project ); try { wgs84BoundingRect.combineExtentWith( exGeoTransform.transformBoundingBox( l->extent() ) ); } catch ( const QgsCsException & ) { wgs84BoundingRect.combineExtentWith( QgsRectangle( -180, -90, 180, 90 ) ); } if ( !queryable && l->flags().testFlag( QgsMapLayer::Identifiable ) ) { queryable = true; } if ( l->hasScaleBasedVisibility() ) { double lMaxScale = l->maximumScale(); if ( lMaxScale > 0.0 && lMaxScale > maxScale ) { maxScale = lMaxScale; } double lMinScale = l->minimumScale(); if ( lMinScale > 0.0 && ( minScale == 0.0 || lMinScale < minScale ) ) { minScale = lMinScale; } } } pLayer.wgs84BoundingRect = wgs84BoundingRect; pLayer.queryable = queryable; pLayer.maxScale = maxScale; pLayer.minScale = minScale; // Formats if ( wmtsPngGroupNameList.contains( gName ) ) pLayer.formats << QStringLiteral( "image/png" ); if ( wmtsJpegGroupNameList.contains( gName ) ) pLayer.formats << QStringLiteral( "image/jpeg" ); wmtsLayers.append( pLayer ); } } QStringList wmtsLayerIdList = project->readListEntry( QStringLiteral( "WMTSLayers" ), QStringLiteral( "Layer" ) ); QStringList wmtsPngLayerIdList = project->readListEntry( QStringLiteral( "WMTSPngLayers" ), QStringLiteral( "Layer" ) ); QStringList wmtsJpegLayerIdList = project->readListEntry( QStringLiteral( "WMTSJpegLayers" ), QStringLiteral( "Layer" ) ); for ( const QString &lId : wmtsLayerIdList ) { QgsMapLayer *l = project->mapLayer( lId ); if ( !l ) { continue; } #ifdef HAVE_SERVER_PYTHON_PLUGINS if ( !accessControl->layerReadPermission( l ) ) { continue; } #endif layerDef pLayer; pLayer.id = l->name(); if ( !l->shortName().isEmpty() ) pLayer.id = l->shortName(); pLayer.id = pLayer.id.replace( ' ', '_' ); pLayer.title = l->title(); pLayer.abstract = l->abstract(); //transform the layer native CRS into WGS84 QgsCoordinateReferenceSystem layerCrs = l->crs(); QgsCoordinateTransform exGeoTransform( layerCrs, wgs84, project ); try { pLayer.wgs84BoundingRect = exGeoTransform.transformBoundingBox( l->extent() ); } catch ( const QgsCsException & ) { pLayer.wgs84BoundingRect = QgsRectangle( -180, -90, 180, 90 ); } // Formats if ( wmtsPngLayerIdList.contains( lId ) ) pLayer.formats << QStringLiteral( "image/png" ); if ( wmtsJpegLayerIdList.contains( lId ) ) pLayer.formats << QStringLiteral( "image/jpeg" ); pLayer.queryable = ( l->flags().testFlag( QgsMapLayer::Identifiable ) ); if ( l->hasScaleBasedVisibility() ) { pLayer.maxScale = l->maximumScale(); pLayer.minScale = l->minimumScale(); } else { pLayer.maxScale = 0.0; pLayer.minScale = 0.0; } wmtsLayers.append( pLayer ); } return wmtsLayers; }
QgsComposition* QgsWMSConfigParser::createPrintComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, const QMap< QString, QString >& parameterMap ) const { QList<QgsComposerMap*> composerMaps; QList<QgsComposerLegend*> composerLegends; QList<QgsComposerLabel*> composerLabels; QList<const QgsComposerHtml*> composerHtmls; QgsComposition* c = initComposition( composerTemplate, mapRenderer, composerMaps, composerLegends, composerLabels, composerHtmls ); if ( !c ) { return 0; } QString dpi = parameterMap.value( "DPI" ); if ( !dpi.isEmpty() ) { c->setPrintResolution( dpi.toInt() ); } //replace composer map parameters Q_FOREACH ( QgsComposerMap* currentMap, composerMaps ) { if ( !currentMap ) { continue; } QString mapId = "MAP" + QString::number( currentMap->id() ); QString extent = parameterMap.value( mapId + ":EXTENT" ); if ( extent.isEmpty() ) //map extent is mandatory { //remove map from composition if not referenced by the request c->removeItem( currentMap ); delete currentMap; continue; } QStringList coordList = extent.split( "," ); if ( coordList.size() < 4 ) { c->removeItem( currentMap ); delete currentMap; continue; //need at least four coordinates } bool xMinOk, yMinOk, xMaxOk, yMaxOk; double xmin = coordList.at( 0 ).toDouble( &xMinOk ); double ymin = coordList.at( 1 ).toDouble( &yMinOk ); double xmax = coordList.at( 2 ).toDouble( &xMaxOk ); double ymax = coordList.at( 3 ).toDouble( &yMaxOk ); if ( !xMinOk || !yMinOk || !xMaxOk || !yMaxOk ) { c->removeItem( currentMap ); delete currentMap; continue; } QgsRectangle r( xmin, ymin, xmax, ymax ); //Change x- and y- of extent for WMS 1.3.0 if axis inverted QString version = parameterMap.value( "VERSION" ); if ( version == "1.3.0" && mapRenderer && mapRenderer->destinationCrs().axisInverted() ) { r.invert(); } currentMap->setNewExtent( r ); //scale QString scaleString = parameterMap.value( mapId + ":SCALE" ); if ( !scaleString.isEmpty() ) { bool scaleOk; double scale = scaleString.toDouble( &scaleOk ); if ( scaleOk ) { currentMap->setNewScale( scale ); } } //rotation QString rotationString = parameterMap.value( mapId + ":ROTATION" ); if ( !rotationString.isEmpty() ) { bool rotationOk; double rotation = rotationString.toDouble( &rotationOk ); if ( rotationOk ) { currentMap->setMapRotation( rotation ); } } //layers / styles QString layers = parameterMap.value( mapId + ":LAYERS" ); QString styles = parameterMap.value( mapId + ":STYLES" ); if ( !layers.isEmpty() ) { QStringList layerSet; QStringList wmsLayerList = layers.split( ",", QString::SkipEmptyParts ); QStringList wmsStyleList; if ( !styles.isEmpty() ) { wmsStyleList = styles.split( ",", QString::SkipEmptyParts ); } for ( int i = 0; i < wmsLayerList.size(); ++i ) { QString wmsLayer = wmsLayerList.at( i ); QString styleName; if ( wmsStyleList.size() > i ) { styleName = wmsStyleList.at( i ); } bool allowCaching = true; if ( wmsLayerList.count( wmsLayer ) > 1 ) { allowCaching = false; } QList<QgsMapLayer*> layerList = mapLayerFromStyle( wmsLayer, styleName, allowCaching ); int listIndex; for ( listIndex = layerList.size() - 1; listIndex >= 0; listIndex-- ) { QgsMapLayer* layer = layerList.at( listIndex ); if ( layer ) { layerSet.push_back( layer->id() ); } } } currentMap->setLayerSet( layerSet ); currentMap->setKeepLayerSet( true ); } //grid space x / y currentMap->grid()->setIntervalX( parameterMap.value( mapId + ":GRID_INTERVAL_X" ).toDouble() ); currentMap->grid()->setIntervalY( parameterMap.value( mapId + ":GRID_INTERVAL_Y" ).toDouble() ); } //update legend // if it has an auto-update model Q_FOREACH ( QgsComposerLegend* currentLegend, composerLegends ) { if ( !currentLegend ) { continue; } if ( currentLegend->autoUpdateModel() || currentLegend->legendFilterByMapEnabled() ) { // the legend has an auto-update model or // has to be filter by map // we will update it with map's layers const QgsComposerMap* map = currentLegend->composerMap(); if ( !map ) { continue; } // get model and layer tree root of the legend QgsLegendModelV2* model = currentLegend->modelV2(); QgsLayerTreeGroup* root = model->rootGroup(); // get layerIds find in the layer tree root QStringList layerIds = root->findLayerIds(); // get map layerIds QStringList layerSet = map->layerSet(); // get map scale double scale = map->scale(); // Q_FOREACH layer find in the layer tree // remove it if the layer id is not in map layerIds Q_FOREACH ( const QString& layerId, layerIds ) { QgsLayerTreeLayer* nodeLayer = root->findLayer( layerId ); if ( !nodeLayer ) { continue; } if ( !layerSet.contains( layerId ) ) { qobject_cast<QgsLayerTreeGroup*>( nodeLayer->parent() )->removeChildNode( nodeLayer ); } else { QgsMapLayer* layer = nodeLayer->layer(); if ( layer->hasScaleBasedVisibility() ) { if ( layer->minimumScale() > scale ) qobject_cast<QgsLayerTreeGroup*>( nodeLayer->parent() )->removeChildNode( nodeLayer ); else if ( layer->maximumScale() < scale ) qobject_cast<QgsLayerTreeGroup*>( nodeLayer->parent() )->removeChildNode( nodeLayer ); } } } root->removeChildrenGroupWithoutLayers(); } }