QGIS API Documentation  2.99.0-Master (90ae728)
qgsmaprendererparalleljob.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprendererparalleljob.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 "qgsfeedback.h"
19 #include "qgslabelingengine.h"
20 #include "qgslogger.h"
21 #include "qgsmaplayerrenderer.h"
22 #include "qgspallabeling.h"
23 #include "qgsproject.h"
24 #include "qgsmaplayer.h"
25 #include "qgsmaplayerlistutils.h"
26 
27 #include <QtConcurrentMap>
28 
30  : QgsMapRendererQImageJob( settings )
31  , mStatus( Idle )
32 {
33 }
34 
36 {
37  if ( isActive() )
38  {
39  cancel();
40  }
41 }
42 
44 {
45  if ( isActive() )
46  return;
47 
48  mRenderingStart.start();
49 
50  mStatus = RenderingLayers;
51 
52  mLabelingEngineV2.reset();
53 
55  {
56  mLabelingEngineV2.reset( new QgsLabelingEngine() );
57  mLabelingEngineV2->readSettingsFromProject( QgsProject::instance() );
58  mLabelingEngineV2->setMapSettings( mSettings );
59  }
60 
61  bool canUseLabelCache = prepareLabelCache();
62  mLayerJobs = prepareJobs( nullptr, mLabelingEngineV2.get() );
63  mLabelJob = prepareLabelingJob( nullptr, mLabelingEngineV2.get(), canUseLabelCache );
64 
65  QgsDebugMsg( QString( "QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) );
66 
67  // start async job
68 
69  connect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
70 
71  mFuture = QtConcurrent::map( mLayerJobs, renderLayerStatic );
72  mFutureWatcher.setFuture( mFuture );
73 }
74 
76 {
77  if ( !isActive() )
78  return;
79 
80  QgsDebugMsg( QString( "PARALLEL cancel at status %1" ).arg( mStatus ) );
81 
82  mLabelJob.context.setRenderingStopped( true );
83  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
84  {
85  it->context.setRenderingStopped( true );
86  if ( it->renderer && it->renderer->feedback() )
87  it->renderer->feedback()->cancel();
88  }
89 
90  if ( mStatus == RenderingLayers )
91  {
92  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
93 
94  mFutureWatcher.waitForFinished();
95 
96  renderLayersFinished();
97  }
98 
99  if ( mStatus == RenderingLabels )
100  {
101  disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
102 
103  mLabelingFutureWatcher.waitForFinished();
104 
105  renderingFinished();
106  }
107 
108  Q_ASSERT( mStatus == Idle );
109 }
110 
112 {
113  if ( !isActive() )
114  return;
115 
116  if ( mStatus == RenderingLayers )
117  {
118  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
119 
120  QTime t;
121  t.start();
122 
123  mFutureWatcher.waitForFinished();
124 
125  QgsDebugMsg( QString( "waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
126 
127  renderLayersFinished();
128  }
129 
130  if ( mStatus == RenderingLabels )
131  {
132  disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
133 
134  QTime t;
135  t.start();
136 
137  mLabelingFutureWatcher.waitForFinished();
138 
139  QgsDebugMsg( QString( "waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
140 
141  renderingFinished();
142  }
143 
144  Q_ASSERT( mStatus == Idle );
145 }
146 
148 {
149  return mStatus != Idle;
150 }
151 
153 {
154  return mLabelJob.cached;
155 }
156 
158 {
159  if ( mLabelingEngineV2 )
160  return mLabelingEngineV2->takeResults();
161  else
162  return nullptr;
163 }
164 
166 {
167  if ( mStatus == RenderingLayers )
168  return composeImage( mSettings, mLayerJobs, mLabelJob );
169  else
170  return mFinalImage; // when rendering labels or idle
171 }
172 
173 void QgsMapRendererParallelJob::renderLayersFinished()
174 {
175  Q_ASSERT( mStatus == RenderingLayers );
176 
177  // compose final image
178  mFinalImage = composeImage( mSettings, mLayerJobs, mLabelJob );
179 
180  QgsDebugMsg( "PARALLEL layers finished" );
181 
182  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelJob.context.renderingStopped() )
183  {
184  mStatus = RenderingLabels;
185 
186  connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
187 
188  // now start rendering of labeling!
189  mLabelingFuture = QtConcurrent::run( renderLabelsStatic, this );
190  mLabelingFutureWatcher.setFuture( mLabelingFuture );
192  }
193  else
194  {
195  renderingFinished();
196  }
197 }
198 
199 void QgsMapRendererParallelJob::renderingFinished()
200 {
201  QgsDebugMsg( "PARALLEL finished" );
202 
203  logRenderingTime( mLayerJobs, mLabelJob );
204 
205  cleanupJobs( mLayerJobs );
206 
207  cleanupLabelJob( mLabelJob );
208 
209  mStatus = Idle;
210 
211  mRenderingTime = mRenderingStart.elapsed();
212 
213  emit finished();
214 }
215 
216 void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob& job )
217 {
218  if ( job.context.renderingStopped() )
219  return;
220 
221  if ( job.cached )
222  return;
223 
224  if ( job.img )
225  job.img->fill( 0 );
226 
227  QTime t;
228  t.start();
229  QgsDebugMsgLevel( QString( "job %1 start (layer %2)" ).arg( reinterpret_cast< quint64 >( &job ), 0, 16 ).arg( job.layer ? job.layer->id() : QString() ), 2 );
230 
231  try
232  {
233  job.renderer->render();
234  }
235  catch ( QgsException & e )
236  {
237  Q_UNUSED( e );
238  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
239  }
240  catch ( std::exception & e )
241  {
242  Q_UNUSED( e );
243  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
244  }
245  catch ( ... )
246  {
247  QgsDebugMsg( "Caught unhandled unknown exception" );
248  }
249 
250  job.renderingTime = t.elapsed();
251  QgsDebugMsgLevel( QString( "job %1 end [%2 ms] (layer %3)" ).arg( reinterpret_cast< quint64 >( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layer ? job.layer->id() : QString() ), 2 );
252 }
253 
254 
255 void QgsMapRendererParallelJob::renderLabelsStatic( QgsMapRendererParallelJob* self )
256 {
257  LabelRenderJob& job = self->mLabelJob;
258 
259  if ( !job.cached )
260  {
261  QTime labelTime;
262  labelTime.start();
263 
264  QPainter painter;
265  if ( job.img )
266  {
267  job.img->fill( 0 );
268  painter.begin( job.img );
269  }
270  else
271  {
272  painter.begin( &self->mFinalImage );
273  }
274 
275  // draw the labels!
276  try
277  {
278  drawLabeling( self->mSettings, job.context, self->mLabelingEngineV2.get(), &painter );
279  }
280  catch ( QgsException & e )
281  {
282  Q_UNUSED( e );
283  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
284  }
285  catch ( std::exception & e )
286  {
287  Q_UNUSED( e );
288  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
289  }
290  catch ( ... )
291  {
292  QgsDebugMsg( "Caught unhandled unknown exception" );
293  }
294 
295  painter.end();
296 
297  job.renderingTime = labelTime.elapsed();
298  job.complete = true;
299  job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
300  if ( job.img )
301  {
302  self->mFinalImage = composeImage( self->mSettings, self->mLayerJobs, self->mLabelJob );
303  }
304  }
305 }
306 
void finished()
emitted when asynchronous rendering is finished (or canceled).
virtual void waitForFinished() override
Block until the job has finished.
void cleanupJobs(LayerRenderJobs &jobs)
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
static void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
void renderingLayersFinished()
Emitted when the layers are rendered.
virtual void cancel() override
Stop the rendering job - does not return until the job has terminated.
Enable drawing of labels on top of the map.
QString what() const
Definition: qgsexception.h:38
The QgsMapSettings class contains configuration for rendering of the map.
QgsMapRendererParallelJob(const QgsMapSettings &settings)
static QImage composeImage(const QgsMapSettings &settings, const LayerRenderJobs &jobs, const LabelRenderJob &labelJob)
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
virtual bool usedCachedLabels() const override
Returns true if the render job was able to use a cached labeling solution.
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
Job implementation that renders all layers in parallel.
QgsMapSettings mSettings
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
The QgsLabelingEngine class provides map labeling functionality.
LayerRenderJobs prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2)
void logRenderingTime(const LayerRenderJobs &jobs, const LabelRenderJob &labelJob)
Intermediate base class adding functionality that allows client to query the rendered image...
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:355
virtual void start() override
Start the rendering job and immediately return.
Class that stores computed placement from labeling engine.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Defines a qgis exception class.
Definition: qgsexception.h:27
virtual bool isActive() const override
Tell whether the rendering job is currently running in background.
virtual QImage renderedImage() override
Get a preview/resulting image.
virtual QgsLabelingResults * takeLabelingResults() override
Get pointer to internal labeling engine (in order to get access to the results).