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