QGIS API Documentation  2.17.0-Master (eef6f05)
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 "qgslabelingenginev2.h"
20 #include "qgslogger.h"
21 #include "qgsmaplayerrenderer.h"
22 #include "qgspallabeling.h"
23 
24 #include <QtConcurrentMap>
25 
26 #define LABELING_V2
27 
29  : QgsMapRendererQImageJob( settings )
30  , mStatus( Idle )
31  , mLabelingEngine( nullptr )
32  , mLabelingEngineV2( nullptr )
33 {
34 }
35 
37 {
38  if ( isActive() )
39  {
40  cancel();
41  }
42 
43  delete mLabelingEngine;
44  mLabelingEngine = nullptr;
45 
46  delete mLabelingEngineV2;
47  mLabelingEngineV2 = nullptr;
48 }
49 
51 {
52  if ( isActive() )
53  return;
54 
56 
58 
59  delete mLabelingEngine;
60  mLabelingEngine = nullptr;
61 
62  delete mLabelingEngineV2;
63  mLabelingEngineV2 = nullptr;
64 
66  {
67 #ifdef LABELING_V2
71 #else
75 #endif
76  }
77 
79  // prepareJobs calls mapLayer->createMapRenderer may involve cloning a RasterDataProvider,
80  // whose constructor may need to download some data (i.e. WMS, AMS) and doing so runs a
81  // QEventLoop waiting for the network request to complete. If unluckily someone calls
82  // mapCanvas->refresh() while this is happening, QgsMapRendererCustomPainterJob::cancel is
83  // called, deleting the QgsMapRendererCustomPainterJob while this function is running.
84  // Hence we need to check whether the job is still active before proceeding
85  if ( !isActive() )
86  return;
87 
88  QgsDebugMsg( QString( "QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) );
89 
90  // start async job
91 
92  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( renderLayersFinished() ) );
93 
96 }
97 
99 {
100  if ( !isActive() )
101  return;
102 
103  QgsDebugMsg( QString( "PARALLEL cancel at status %1" ).arg( mStatus ) );
104 
106  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
107  {
108  it->context.setRenderingStopped( true );
109  if ( it->renderer && it->renderer->feedback() )
110  it->renderer->feedback()->cancel();
111  }
112 
113  if ( mStatus == RenderingLayers )
114  {
115  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( renderLayersFinished() ) );
116 
118 
120  }
121 
122  if ( mStatus == RenderingLabels )
123  {
124  disconnect( &mLabelingFutureWatcher, SIGNAL( finished() ), this, SLOT( renderingFinished() ) );
125 
127 
129  }
130 
131  Q_ASSERT( mStatus == Idle );
132 }
133 
135 {
136  if ( !isActive() )
137  return;
138 
139  if ( mStatus == RenderingLayers )
140  {
141  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( renderLayersFinished() ) );
142 
143  QTime t;
144  t.start();
145 
147 
148  QgsDebugMsg( QString( "waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
149 
151  }
152 
153  if ( mStatus == RenderingLabels )
154  {
155  disconnect( &mLabelingFutureWatcher, SIGNAL( finished() ), this, SLOT( renderingFinished() ) );
156 
157  QTime t;
158  t.start();
159 
161 
162  QgsDebugMsg( QString( "waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
163 
165  }
166 
167  Q_ASSERT( mStatus == Idle );
168 }
169 
171 {
172  return mStatus != Idle;
173 }
174 
176 {
177  if ( mLabelingEngine )
178  return mLabelingEngine->takeResults();
179  else if ( mLabelingEngineV2 )
180  return mLabelingEngineV2->takeResults();
181  else
182  return nullptr;
183 }
184 
186 {
187  if ( mStatus == RenderingLayers )
188  return composeImage( mSettings, mLayerJobs );
189  else
190  return mFinalImage; // when rendering labels or idle
191 }
192 
194 {
195  Q_ASSERT( mStatus == RenderingLayers );
196 
197  // compose final image
199 
201 
203 
204  QgsDebugMsg( "PARALLEL layers finished" );
205 
207  {
209 
210  connect( &mLabelingFutureWatcher, SIGNAL( finished() ), this, SLOT( renderingFinished() ) );
211 
212  // now start rendering of labeling!
215  }
216  else
217  {
219  }
220 }
221 
223 {
224  QgsDebugMsg( "PARALLEL finished" );
225 
226  mStatus = Idle;
227 
229 
230  emit finished();
231 }
232 
234 {
235  if ( job.context.renderingStopped() )
236  return;
237 
238  if ( job.cached )
239  return;
240 
241  QTime t;
242  t.start();
243  QgsDebugMsg( QString( "job %1 start (layer %2)" ).arg( reinterpret_cast< ulong >( &job ), 0, 16 ).arg( job.layerId ) );
244 
245  try
246  {
247  job.renderer->render();
248  }
249  catch ( QgsException & e )
250  {
251  Q_UNUSED( e );
252  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
253  }
254  catch ( std::exception & e )
255  {
256  Q_UNUSED( e );
257  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
258  }
259  catch ( ... )
260  {
261  QgsDebugMsg( "Caught unhandled unknown exception" );
262  }
263 
264  job.renderingTime = t.elapsed();
265  QgsDebugMsg( QString( "job %1 end [%2 ms] (layer %3)" ).arg( reinterpret_cast< ulong >( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ) );
266 }
267 
268 
270 {
271  QPainter painter( &self->mFinalImage );
272 
273  try
274  {
275  drawLabeling( self->mSettings, self->mLabelingRenderContext, self->mLabelingEngine, self->mLabelingEngineV2, &painter );
276  }
277  catch ( QgsException & e )
278  {
279  Q_UNUSED( e );
280  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
281  }
282  catch ( std::exception & e )
283  {
284  Q_UNUSED( e );
285  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
286  }
287  catch ( ... )
288  {
289  QgsDebugMsg( "Caught unhandled unknown exception" );
290  }
291 
292  painter.end();
293 }
294 
QString fromAscii(const char *str, int size)
void finished()
emitted when asynchronous rendering is finished (or canceled).
void setRenderingStopped(bool stopped)
void renderingFinished()
all rendering is finished, including labeling
bool end()
virtual void waitForFinished() override
Block until the job has finished.
void cleanupJobs(LayerRenderJobs &jobs)
QgsLabelingResults * takeResults()
Return pointer to recently computed results (in drawLabeling()) and pass the ownership of results to ...
static QImage composeImage(const QgsMapSettings &settings, const LayerRenderJobs &jobs)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void loadEngineSettings()
load/save engine settings to project file
void logRenderingTime(const LayerRenderJobs &jobs)
QThreadPool * globalInstance()
bool renderingStopped() const
The QgsLabelingEngineV2 class provides map labeling functionality.
void readSettingsFromProject()
Read configuration of the labeling engine from the current project file.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QgsPalLabeling * mLabelingEngine
Old labeling engine.
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.
QFutureWatcher< void > mFutureWatcher
QString what() const
Definition: qgsexception.h:36
The QgsMapSettings class contains configuration for rendering of the map.
virtual Q_DECL_DEPRECATED void init(QgsMapRenderer *mr) override
called when we&#39;re going to start with rendering
QgsMapRendererParallelJob(const QgsMapSettings &settings)
int elapsed() const
Job implementation that renders all layers in parallel.
static void renderLabelsStatic(QgsMapRendererParallelJob *self)
QFuture< T > run(Function function,...)
QFuture< void > map(Sequence &sequence, MapFunction function)
void renderLayersFinished()
layers are rendered, labeling is still pending
void setFuture(const QFuture< T > &future)
QgsMapSettings mSettings
iterator end()
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
void waitForFinished()
QgsLabelingEngineV2 * mLabelingEngineV2
New labeling engine.
enum QgsMapRendererParallelJob::@0 mStatus
static void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsPalLabeling *labelingEngine, QgsLabelingEngineV2 *labelingEngine2, QPainter *painter)
virtual bool render()=0
Do the rendering (based on data stored in the class)
LayerRenderJobs prepareJobs(QPainter *painter, QgsPalLabeling *labelingEngine, QgsLabelingEngineV2 *labelingEngine2)
int renderingTime
time it took to render the layer in ms (it is -1 if not rendered or still rendering) ...
Intermediate base class adding functionality that allows client to query the rendered image...
virtual void start() override
Start the rendering job and immediately return.
QFutureWatcher< void > mLabelingFutureWatcher
QgsMapLayerRenderer * renderer
QgsRenderContext context
void start()
Class that stores computed placement from labeling engine.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
static void renderLayerStatic(LayerRenderJob &job)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
Defines a qgis exception class.
Definition: qgsexception.h:25
QgsLabelingResults * takeResults()
Return pointer to recently computed results and pass the ownership of results to the caller...
iterator begin()
Structure keeping low-level rendering job information.
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)