QGIS API Documentation  2.99.0-Master (716ff6c)
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 "qgsproject.h"
23 #include "qgsmaplayer.h"
24 #include "qgsmaplayerlistutils.h"
25 
26 #include <QtConcurrentMap>
27 
29  : QgsMapRendererQImageJob( settings )
30  , mStatus( Idle )
31 {
32 }
33 
35 {
36  if ( isActive() )
37  {
38  cancel();
39  }
40 }
41 
43 {
44  if ( isActive() )
45  return;
46 
47  mRenderingStart.start();
48 
49  mStatus = RenderingLayers;
50 
51  mLabelingEngineV2.reset();
52 
54  {
55  mLabelingEngineV2.reset( new QgsLabelingEngine() );
56  mLabelingEngineV2->setMapSettings( mSettings );
57  }
58 
59  bool canUseLabelCache = prepareLabelCache();
60  mLayerJobs = prepareJobs( nullptr, mLabelingEngineV2.get() );
61  mLabelJob = prepareLabelingJob( nullptr, mLabelingEngineV2.get(), canUseLabelCache );
62 
63  QgsDebugMsg( QString( "QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) );
64 
65  // start async job
66 
67  connect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
68 
69  mFuture = QtConcurrent::map( mLayerJobs, renderLayerStatic );
70  mFutureWatcher.setFuture( mFuture );
71 }
72 
74 {
75  if ( !isActive() )
76  return;
77 
78  QgsDebugMsg( QString( "PARALLEL cancel at status %1" ).arg( mStatus ) );
79 
80  mLabelJob.context.setRenderingStopped( true );
81  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
82  {
83  it->context.setRenderingStopped( true );
84  if ( it->renderer && it->renderer->feedback() )
85  it->renderer->feedback()->cancel();
86  }
87 
88  if ( mStatus == RenderingLayers )
89  {
90  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
91 
92  mFutureWatcher.waitForFinished();
93 
94  renderLayersFinished();
95  }
96 
97  if ( mStatus == RenderingLabels )
98  {
99  disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
100 
101  mLabelingFutureWatcher.waitForFinished();
102 
103  renderingFinished();
104  }
105 
106  Q_ASSERT( mStatus == Idle );
107 }
108 
110 {
111  if ( !isActive() )
112  return;
113 
114  QgsDebugMsg( QString( "PARALLEL cancel at status %1" ).arg( mStatus ) );
115 
116  mLabelJob.context.setRenderingStopped( true );
117  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
118  {
119  it->context.setRenderingStopped( true );
120  if ( it->renderer && it->renderer->feedback() )
121  it->renderer->feedback()->cancel();
122  }
123 
124  if ( mStatus == RenderingLayers )
125  {
126  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
127  connect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
128  }
129 }
130 
132 {
133  if ( !isActive() )
134  return;
135 
136  if ( mStatus == RenderingLayers )
137  {
138  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderLayersFinished );
139 
140  QTime t;
141  t.start();
142 
143  mFutureWatcher.waitForFinished();
144 
145  QgsDebugMsg( QString( "waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
146 
147  renderLayersFinished();
148  }
149 
150  if ( mStatus == RenderingLabels )
151  {
152  disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
153 
154  QTime t;
155  t.start();
156 
157  mLabelingFutureWatcher.waitForFinished();
158 
159  QgsDebugMsg( QString( "waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
160 
161  renderingFinished();
162  }
163 
164  Q_ASSERT( mStatus == Idle );
165 }
166 
168 {
169  return mStatus != Idle;
170 }
171 
173 {
174  return mLabelJob.cached;
175 }
176 
178 {
179  if ( mLabelingEngineV2 )
180  return mLabelingEngineV2->takeResults();
181  else
182  return nullptr;
183 }
184 
186 {
187  if ( mStatus == RenderingLayers )
188  return composeImage( mSettings, mLayerJobs, mLabelJob );
189  else
190  return mFinalImage; // when rendering labels or idle
191 }
192 
193 void QgsMapRendererParallelJob::renderLayersFinished()
194 {
195  Q_ASSERT( mStatus == RenderingLayers );
196 
197  // compose final image
198  mFinalImage = composeImage( mSettings, mLayerJobs, mLabelJob );
199 
200  QgsDebugMsg( "PARALLEL layers finished" );
201 
202  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelJob.context.renderingStopped() )
203  {
204  mStatus = RenderingLabels;
205 
206  connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsMapRendererParallelJob::renderingFinished );
207 
208  // now start rendering of labeling!
209  mLabelingFuture = QtConcurrent::run( renderLabelsStatic, this );
210  mLabelingFutureWatcher.setFuture( mLabelingFuture );
212  }
213  else
214  {
215  renderingFinished();
216  }
217 }
218 
219 void QgsMapRendererParallelJob::renderingFinished()
220 {
221  QgsDebugMsg( "PARALLEL finished" );
222 
223  logRenderingTime( mLayerJobs, mLabelJob );
224 
225  cleanupJobs( mLayerJobs );
226 
227  cleanupLabelJob( mLabelJob );
228 
229  mStatus = Idle;
230 
231  mRenderingTime = mRenderingStart.elapsed();
232 
233  emit finished();
234 }
235 
236 void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
237 {
238  if ( job.context.renderingStopped() )
239  return;
240 
241  if ( job.cached )
242  return;
243 
244  if ( job.img )
245  {
246  job.img->fill( 0 );
247  job.imageInitialized = true;
248  }
249 
250  QTime t;
251  t.start();
252  QgsDebugMsgLevel( QString( "job %1 start (layer %2)" ).arg( reinterpret_cast< quint64 >( &job ), 0, 16 ).arg( job.layer ? job.layer->id() : QString() ), 2 );
253 
254  try
255  {
256  job.renderer->render();
257  }
258  catch ( QgsException &e )
259  {
260  Q_UNUSED( e );
261  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
262  }
263  catch ( std::exception &e )
264  {
265  Q_UNUSED( e );
266  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
267  }
268  catch ( ... )
269  {
270  QgsDebugMsg( "Caught unhandled unknown exception" );
271  }
272 
273  job.renderingTime = t.elapsed();
274  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 );
275 }
276 
277 
278 void QgsMapRendererParallelJob::renderLabelsStatic( QgsMapRendererParallelJob *self )
279 {
280  LabelRenderJob &job = self->mLabelJob;
281 
282  if ( !job.cached )
283  {
284  QTime labelTime;
285  labelTime.start();
286 
287  QPainter painter;
288  if ( job.img )
289  {
290  job.img->fill( 0 );
291  painter.begin( job.img );
292  }
293  else
294  {
295  painter.begin( &self->mFinalImage );
296  }
297 
298  // draw the labels!
299  try
300  {
301  drawLabeling( self->mSettings, job.context, self->mLabelingEngineV2.get(), &painter );
302  }
303  catch ( QgsException &e )
304  {
305  Q_UNUSED( e );
306  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
307  }
308  catch ( std::exception &e )
309  {
310  Q_UNUSED( e );
311  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
312  }
313  catch ( ... )
314  {
315  QgsDebugMsg( "Caught unhandled unknown exception" );
316  }
317 
318  painter.end();
319 
320  job.renderingTime = labelTime.elapsed();
321  job.complete = true;
322  job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
323  if ( job.img )
324  {
325  self->mFinalImage = composeImage( self->mSettings, self->mLayerJobs, self->mLabelJob );
326  }
327  }
328 }
329 
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)
virtual void cancelWithoutBlocking() override
Triggers cancelation of the rendering job without blocking.
Intermediate base class adding functionality that allows client to query the rendered image...
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).