QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator 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 
24  : QgsMapRendererJob( settings )
25  , mPainter( painter )
26  , mLabelingEngine( 0 )
27  , mActive( false )
28  , mRenderSynchronously( false )
29 {
30  QgsDebugMsg( "QPAINTER construct" );
31 }
32 
34 {
35  QgsDebugMsg( "QPAINTER destruct" );
36  Q_ASSERT( !mFutureWatcher.isRunning() );
37  //cancel();
38 
39  delete mLabelingEngine;
40  mLabelingEngine = 0;
41 }
42 
44 {
45  if ( isActive() )
46  return;
47 
48  mRenderingStart.start();
49 
50  mActive = true;
51 
52  mErrors.clear();
53 
54  QgsDebugMsg( "QPAINTER run!" );
55 
56  QgsDebugMsg( "Preparing list of layer jobs for rendering" );
57  QTime prepareTime;
58  prepareTime.start();
59 
60  // clear the background
61  mPainter->fillRect( 0, 0, mSettings.outputSize().width(), mSettings.outputSize().height(), mSettings.backgroundColor() );
62 
63  mPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );
64 
65  QPaintDevice* thePaintDevice = mPainter->device();
66 
67  QString errMsg = QString( "pre-set DPI not equal to painter's DPI (%1 vs %2)" ).arg( thePaintDevice->logicalDpiX() ).arg( mSettings.outputDpi() );
68  Q_ASSERT_X( thePaintDevice->logicalDpiX() == mSettings.outputDpi(), "Job::startRender()", errMsg.toAscii().data() );
69 
70  delete mLabelingEngine;
71  mLabelingEngine = 0;
72 
74  {
75  mLabelingEngine = new QgsPalLabeling;
76  mLabelingEngine->loadEngineSettings();
77  mLabelingEngine->init( mSettings );
78  }
79 
80  mLayerJobs = prepareJobs( mPainter, mLabelingEngine );
81 
82  QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );
83 
84  if ( mRenderSynchronously )
85  {
86  // do the rendering right now!
87  doRender();
88  return;
89  }
90 
91  // now we are ready to start rendering!
92  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( futureFinished() ) );
93 
94  mFuture = QtConcurrent::run( staticRender, this );
95  mFutureWatcher.setFuture( mFuture );
96 }
97 
98 
100 {
101  if ( !isActive() )
102  {
103  QgsDebugMsg( "QPAINTER not running!" );
104  return;
105  }
106 
107  QgsDebugMsg( "QPAINTER cancelling" );
108  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
109 
110  mLabelingRenderContext.setRenderingStopped( true );
111  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
112  {
113  it->context.setRenderingStopped( true );
114  }
115 
116  QTime t;
117  t.start();
118 
119  mFutureWatcher.waitForFinished();
120 
121  QgsDebugMsg( QString( "QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ) );
122 
123  futureFinished();
124 
125  QgsDebugMsg( "QPAINTER cancelled" );
126 }
127 
129 {
130  if ( !isActive() )
131  return;
132 
133  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
134 
135  QTime t;
136  t.start();
137 
138  mFutureWatcher.waitForFinished();
139 
140  QgsDebugMsg( QString( "waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ) );
141 
142  futureFinished();
143 }
144 
146 {
147  return mActive;
148 }
149 
150 
152 {
153  return mLabelingEngine ? mLabelingEngine->takeResults() : 0;
154 }
155 
156 
157 void QgsMapRendererCustomPainterJob::waitForFinishedWithEventLoop( QEventLoop::ProcessEventsFlags flags )
158 {
159  QEventLoop loop;
160  connect( &mFutureWatcher, SIGNAL( finished() ), &loop, SLOT( quit() ) );
161  loop.exec( flags );
162 }
163 
164 
166 {
167  mRenderSynchronously = true;
168  start();
169  futureFinished();
170  mRenderSynchronously = false;
171 }
172 
173 
175 {
176  mActive = false;
177  mRenderingTime = mRenderingStart.elapsed();
178  QgsDebugMsg( "QPAINTER futureFinished" );
179 
180  // final cleanup
181  cleanupJobs( mLayerJobs );
182 
183  emit finished();
184 }
185 
186 
188 {
189  try
190  {
191  self->doRender();
192  }
193  catch ( QgsException & e )
194  {
195  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
196  }
197  catch ( std::exception & e )
198  {
199  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
200  }
201  catch ( ... )
202  {
203  QgsDebugMsg( "Caught unhandled unknown exception" );
204  }
205 }
206 
208 {
209  QgsDebugMsg( "Starting to render layer stack." );
210  QTime renderTime;
211  renderTime.start();
212 
213  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
214  {
215  LayerRenderJob& job = *it;
216 
217  if ( job.context.renderingStopped() )
218  break;
219 
220  if ( job.context.useAdvancedEffects() )
221  {
222  // Set the QPainter composition mode so that this layer is rendered using
223  // the desired blending mode
224  mPainter->setCompositionMode( job.blendMode );
225  }
226 
227  if ( !job.cached )
228  job.renderer->render();
229 
230  if ( job.img )
231  {
232  // If we flattened this layer for alternate blend modes, composite it now
233  mPainter->drawImage( 0, 0, *job.img );
234  }
235 
236  }
237 
238  QgsDebugMsg( "Done rendering map layers" );
239 
240  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelingRenderContext.renderingStopped() )
241  drawLabeling( mSettings, mLabelingRenderContext, mLabelingEngine, mPainter );
242 
243  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
244 }
245 
246 
247 void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QPainter* painter )
248 {
249  QgsDebugMsg( "Draw labeling start" );
250 
251  QTime t;
252  t.start();
253 
254  // Reset the composition mode before rendering the labels
255  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
256 
257  // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile
258  renderContext = QgsRenderContext::fromMapSettings( settings );
259  renderContext.setPainter( painter );
260  renderContext.setLabelingEngine( labelingEngine );
261 
262  // old labeling - to be removed at some point...
263  drawOldLabeling( settings, renderContext );
264 
265  drawNewLabeling( settings, renderContext, labelingEngine );
266 
267  QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) );
268 }
269 
270 
272 {
273  // render all labels for vector layers in the stack, starting at the base
274  QListIterator<QString> li( settings.layers() );
275  li.toBack();
276  while ( li.hasPrevious() )
277  {
278  if ( renderContext.renderingStopped() )
279  {
280  break;
281  }
282 
283  QString layerId = li.previous();
284 
286 
287  if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) )
288  continue;
289 
290  // only make labels if the layer is visible
291  // after scale dep viewing settings are checked
292  if ( ml->hasScaleBasedVisibility() && ( settings.scale() < ml->minimumScale() || settings.scale() > ml->maximumScale() ) )
293  continue;
294 
295  const QgsCoordinateTransform* ct = 0;
296  QgsRectangle r1 = settings.visibleExtent(), r2;
297 
298  if ( settings.hasCrsTransformEnabled() )
299  {
300  ct = settings.layerTransform( ml );
301  if ( ct )
302  reprojectToLayerExtent( ct, ml->crs().geographicFlag(), r1, r2 );
303  }
304 
305  renderContext.setCoordinateTransform( ct );
306  renderContext.setExtent( r1 );
307 
308  ml->drawLabels( renderContext );
309  }
310 }
311 
312 
313 void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine )
314 {
315  if ( labelingEngine && !renderContext.renderingStopped() )
316  {
317  // set correct extent
318  renderContext.setExtent( settings.visibleExtent() );
319  renderContext.setCoordinateTransform( NULL );
320 
321  labelingEngine->drawLabeling( renderContext );
322  labelingEngine->exit();
323  }
324 }
325 
327 {
328  foreach ( QString id, mGeometryCaches.keys() )
329  {
330  const QgsGeometryCache& cache = mGeometryCaches[id];
331  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( id ) ) )
332  * vl->cache() = cache;
333  }
334  mGeometryCaches.clear();
335 }
336 
337 
339 {
341  {
342  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
343  if ((( vl->blendMode() != QPainter::CompositionMode_SourceOver )
344  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
345  || ( vl->layerTransparency() != 0 ) ) )
346  return true;
347  }
348 
349  return false;
350 }
351 
QgsMapRendererCustomPainterJob(const QgsMapSettings &settings, QPainter *painter)
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:92
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 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
static QgsMapLayerRegistry * instance()
Definition: qgssingleton.h:23
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.
Enable layer transparency and blending effects.
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.
int outputDpi() const
Return DPI used for conversion between real world units (e.g.
QPainter::CompositionMode blendMode() const
Read blend mode for layer.
virtual void start() override
Start the rendering job and immediately return.
QPainter::CompositionMode featureBlendMode() const
Read blend mode for layer.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
QSize outputSize() const
Return the size of the resulting map image.
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.
void setPainter(QPainter *p)
QgsMapSettings mSettings
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
Read transparency for layer.
static void drawOldLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext)
QString what() const
Definition: qgsexception.h:35
Contains information about the context of a rendering operation.
virtual bool render()=0
Do the rendering (based on data stored in the class)
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)
QgsMapLayerRenderer * renderer
Class for doing transforms between two map coordinate systems.
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...
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
virtual void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
bool needTemporaryImage(QgsMapLayer *ml)
Defines a qgis exception class.
Definition: qgsexception.h:25
void renderSynchronously()
Render the map synchronously in this thread.
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.