QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsmaprenderercustompainterjob.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprenderercustompainterjob.cpp
3  --------------------------------------
4  Date : December 2013
5  Copyright : (C) 2013 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgslogger.h"
19 #include "qgsmaplayerregistry.h"
20 #include "qgsmaplayerrenderer.h"
21 #include "qgspallabeling.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsrendererv2.h"
24 
26  : QgsMapRendererJob( settings )
27  , mPainter( painter )
28  , mLabelingEngine( 0 )
29  , mActive( false )
30  , mRenderSynchronously( false )
31 {
32  QgsDebugMsg( "QPAINTER construct" );
33 }
34 
36 {
37  QgsDebugMsg( "QPAINTER destruct" );
38  Q_ASSERT( !mFutureWatcher.isRunning() );
39  //cancel();
40 
41  delete mLabelingEngine;
42  mLabelingEngine = 0;
43 }
44 
46 {
47  if ( isActive() )
48  return;
49 
51 
52  mActive = true;
53 
54  mErrors.clear();
55 
56  QgsDebugMsg( "QPAINTER run!" );
57 
58  QgsDebugMsg( "Preparing list of layer jobs for rendering" );
59  QTime prepareTime;
60  prepareTime.start();
61 
62  // clear the background
64 
65  mPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );
66 
67  QPaintDevice* thePaintDevice = mPainter->device();
68 
69  QString errMsg = QString( "pre-set DPI not equal to painter's DPI (%1 vs %2)" ).arg( thePaintDevice->logicalDpiX() ).arg( mSettings.outputDpi() );
70  Q_ASSERT_X( thePaintDevice->logicalDpiX() == mSettings.outputDpi(), "Job::startRender()", errMsg.toAscii().data() );
71 
72  delete mLabelingEngine;
73  mLabelingEngine = 0;
74 
76  {
77  mLabelingEngine = new QgsPalLabeling;
78  mLabelingEngine->loadEngineSettings();
79  mLabelingEngine->init( mSettings );
80  }
81 
82  mLayerJobs = prepareJobs( mPainter, mLabelingEngine );
83 
84  QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );
85 
86  if ( mRenderSynchronously )
87  {
88  // do the rendering right now!
89  doRender();
90  return;
91  }
92 
93  // now we are ready to start rendering!
94  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( futureFinished() ) );
95 
96  mFuture = QtConcurrent::run( staticRender, this );
97  mFutureWatcher.setFuture( mFuture );
98 }
99 
100 
102 {
103  if ( !isActive() )
104  {
105  QgsDebugMsg( "QPAINTER not running!" );
106  return;
107  }
108 
109  QgsDebugMsg( "QPAINTER cancelling" );
110  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
111 
112  mLabelingRenderContext.setRenderingStopped( true );
113  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
114  {
115  it->context.setRenderingStopped( true );
116  }
117 
118  QTime t;
119  t.start();
120 
121  mFutureWatcher.waitForFinished();
122 
123  QgsDebugMsg( QString( "QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ) );
124 
125  futureFinished();
126 
127  QgsDebugMsg( "QPAINTER cancelled" );
128 }
129 
131 {
132  if ( !isActive() )
133  return;
134 
135  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
136 
137  QTime t;
138  t.start();
139 
140  mFutureWatcher.waitForFinished();
141 
142  QgsDebugMsg( QString( "waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ) );
143 
144  futureFinished();
145 }
146 
148 {
149  return mActive;
150 }
151 
152 
154 {
155  return mLabelingEngine ? mLabelingEngine->takeResults() : 0;
156 }
157 
158 
160 {
161  QEventLoop loop;
162  connect( &mFutureWatcher, SIGNAL( finished() ), &loop, SLOT( quit() ) );
163  loop.exec( flags );
164 }
165 
166 
168 {
169  mRenderSynchronously = true;
170  start();
171  futureFinished();
172  mRenderSynchronously = false;
173 }
174 
175 
177 {
178  mActive = false;
180  QgsDebugMsg( "QPAINTER futureFinished" );
181 
182  // final cleanup
183  cleanupJobs( mLayerJobs );
184 
185  emit finished();
186 }
187 
188 
190 {
191  try
192  {
193  self->doRender();
194  }
195  catch ( QgsException & e )
196  {
197  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
198  }
199  catch ( std::exception & e )
200  {
201  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
202  }
203  catch ( ... )
204  {
205  QgsDebugMsg( "Caught unhandled unknown exception" );
206  }
207 }
208 
210 {
211  QgsDebugMsg( "Starting to render layer stack." );
212  QTime renderTime;
213  renderTime.start();
214 
215  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
216  {
217  LayerRenderJob& job = *it;
218 
219  if ( job.context.renderingStopped() )
220  break;
221 
222  if ( job.context.useAdvancedEffects() )
223  {
224  // Set the QPainter composition mode so that this layer is rendered using
225  // the desired blending mode
226  mPainter->setCompositionMode( job.blendMode );
227  }
228 
229  if ( !job.cached )
230  job.renderer->render();
231 
232  if ( job.img )
233  {
234  // If we flattened this layer for alternate blend modes, composite it now
235  mPainter->drawImage( 0, 0, *job.img );
236  }
237 
238  }
239 
240  QgsDebugMsg( "Done rendering map layers" );
241 
242  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelingRenderContext.renderingStopped() )
243  drawLabeling( mSettings, mLabelingRenderContext, mLabelingEngine, mPainter );
244 
245  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
246 }
247 
248 
249 void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QPainter* painter )
250 {
251  QgsDebugMsg( "Draw labeling start" );
252 
253  QTime t;
254  t.start();
255 
256  // Reset the composition mode before rendering the labels
257  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
258 
259  // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile
260  renderContext = QgsRenderContext::fromMapSettings( settings );
261  renderContext.setPainter( painter );
262  renderContext.setLabelingEngine( labelingEngine );
263 
264  // old labeling - to be removed at some point...
265  drawOldLabeling( settings, renderContext );
266 
267  drawNewLabeling( settings, renderContext, labelingEngine );
268 
269  QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) );
270 }
271 
272 
274 {
275  // render all labels for vector layers in the stack, starting at the base
276  QListIterator<QString> li( settings.layers() );
277  li.toBack();
278  while ( li.hasPrevious() )
279  {
280  if ( renderContext.renderingStopped() )
281  {
282  break;
283  }
284 
285  QString layerId = li.previous();
286 
288 
289  if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) )
290  continue;
291 
292  // only make labels if the layer is visible
293  // after scale dep viewing settings are checked
294  if ( ml->hasScaleBasedVisibility() && ( settings.scale() < ml->minimumScale() || settings.scale() > ml->maximumScale() ) )
295  continue;
296 
297  const QgsCoordinateTransform* ct = 0;
298  QgsRectangle r1 = settings.visibleExtent(), r2;
299 
300  if ( settings.hasCrsTransformEnabled() )
301  {
302  ct = settings.layerTransform( ml );
303  if ( ct )
304  reprojectToLayerExtent( ct, ml->crs().geographicFlag(), r1, r2 );
305  }
306 
307  renderContext.setCoordinateTransform( ct );
308  renderContext.setExtent( r1 );
309 
310  ml->drawLabels( renderContext );
311  }
312 }
313 
314 
315 void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine )
316 {
317  if ( labelingEngine && !renderContext.renderingStopped() )
318  {
319  // set correct extent
320  renderContext.setExtent( settings.visibleExtent() );
321  renderContext.setCoordinateTransform( NULL );
322 
323  labelingEngine->drawLabeling( renderContext );
324  labelingEngine->exit();
325  }
326 }
327 
329 {
330  foreach ( QString id, mGeometryCaches.keys() )
331  {
332  const QgsGeometryCache& cache = mGeometryCaches[id];
333  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( id ) ) )
334  * vl->cache() = cache;
335  }
337 }
338 
339 
341 {
342  if ( ml->type() == QgsMapLayer::VectorLayer )
343  {
344  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
345  if ( vl->rendererV2()->forceRasterRender() )
346  {
347  //raster rendering is forced for this layer
348  return true;
349  }
351  (( vl->blendMode() != QPainter::CompositionMode_SourceOver )
352  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
353  || ( vl->layerTransparency() != 0 ) ) )
354  {
355  //layer properties require rasterisation
356  return true;
357  }
358  }
359 
360  return false;
361 }
362 
QgsMapRendererCustomPainterJob(const QgsMapSettings &settings, QPainter *painter)
void clear()
QString fromAscii(const char *str, int size)
void finished()
emitted when asynchronous rendering is finished (or canceled).
static void drawNewLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsPalLabeling *labelingEngine)
void setRenderingStopped(bool stopped)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:49
Job implementation that renders everything sequentially using a custom painter.
QgsMapLayer::LayerType type() const
Get the type of the layer.
Definition: qgsmaplayer.cpp:93
int width() const
Abstract base class for map rendering implementations.
virtual void drawLabels(QgsRenderContext &rendererContext)
Draw labels.
double scale() const
Return the calculated scale of the map.
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
void setRenderHint(RenderHint hint, bool on)
void cleanupJobs(LayerRenderJobs &jobs)
QgsLabelingResults * takeResults()
Return pointer to recently computed results (in drawLabeling()) and pass the ownership of results to ...
const QgsCoordinateTransform * layerTransform(QgsMapLayer *layer) const
Return coordinate transform from layer's CRS to destination CRS.
void updateLayerGeometryCaches()
called when rendering has finished to update all layers' geometry caches
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void loadEngineSettings()
load/save engine settings to project file
QMap< QString, QgsGeometryCache > mGeometryCaches
map of geometry caches
float minimumScale() const
Returns the minimum scale denominator at which the layer is visible.
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
virtual void cancel() override
Stop the rendering job - does not return until the job has terminated.
void clear()
Enable layer transparency and blending effects.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void setExtent(const QgsRectangle &extent)
LayerRenderJobs prepareJobs(QPainter *painter, QgsPalLabeling *labelingEngine)
void waitForFinishedWithEventLoop(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Wait for the job to be finished - and keep the thread's event loop running while waiting.
Enable drawing of labels on top of the map.
The QgsMapSettings class contains configuration for rendering of the map.
void setCoordinateTransform(const QgsCoordinateTransform *t)
Sets coordinate transformation.
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
QList< Key > keys() const
int elapsed() const
int outputDpi() const
Return DPI used for conversion between real world units (e.g.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
bool forceRasterRender() const
Returns whether the renderer must render as a raster.
virtual void start() override
Start the rendering job and immediately return.
QPainter::CompositionMode featureBlendMode() const
Returns the current blending mode for features.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
QSize outputSize() const
Return the size of the resulting map image.
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
bool renderingStopped() const
float maximumScale() const
Returns the maximum scale denominator at which the layer is visible.
static void staticRender(QgsMapRendererCustomPainterJob *self)
virtual void drawLabeling(QgsRenderContext &context) override
called when the map is drawn and labels should be placed
Enable anti-aliasin for map rendering.
QPaintDevice * device() const
void setPainter(QPainter *p)
QFuture< T > run(Function function,...)
void setFuture(const QFuture< T > &future)
QgsMapSettings mSettings
int logicalDpiX() const
iterator end()
QColor backgroundColor() const
Get the background color of the map.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
int layerTransparency() const
Returns the current transparency for the vector layer.
static void drawOldLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext)
bool isRunning() const
void waitForFinished()
QgsFeatureRendererV2 * rendererV2() const
Return renderer V2.
QString what() const
Definition: qgsexception.h:35
typedef ProcessEventsFlags
Contains information about the context of a rendering operation.
virtual bool render()=0
Do the rendering (based on data stored in the class)
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void setLabelingEngine(QgsLabelingEngineInterface *iface)
static void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsPalLabeling *labelingEngine, QPainter *painter)
int height() const
QgsMapLayerRenderer * renderer
Class for doing transforms between two map coordinate systems.
char * data()
QgsRenderContext context
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
QStringList layers() const
Get list of layer IDs for map rendering The layers are stored in the reverse order of how they are re...
void start()
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
Class that stores computed placement from labeling engine.
static bool reprojectToLayerExtent(const QgsCoordinateTransform *ct, bool layerCrsGeographic, QgsRectangle &extent, QgsRectangle &r2)
Convenience function to project an extent into the layer source CRS, but also split it into two exten...
virtual QgsLabelingResults * takeLabelingResults() override
Get pointer to internal labeling engine (in order to get access to the results)
QPainter::CompositionMode blendMode
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
bool geographicFlag() const
Get this Geographic? flag.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool needTemporaryImage(QgsMapLayer *ml)
Defines a qgis exception class.
Definition: qgsexception.h:25
void renderSynchronously()
Render the map synchronously in this thread.
iterator begin()
QByteArray toAscii() const
virtual void exit() override
called when we're done with rendering
Structure keeping low-level rendering job information.
virtual bool isActive() const override
Tell whether the rendering job is currently running in background.