QGIS API Documentation  2.17.0-Master (973e4b0)
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 "qgslabelingenginev2.h"
19 #include "qgslogger.h"
20 #include "qgsmaplayerrenderer.h"
21 #include "qgspallabeling.h"
22 
23 #include <QtConcurrentMap>
24 
25 #define LABELING_V2
26 
28  : QgsMapRendererQImageJob( settings )
29  , mStatus( Idle )
30  , mLabelingEngine( nullptr )
31  , mLabelingEngineV2( nullptr )
32 {
33 }
34 
36 {
37  if ( isActive() )
38  {
39  cancel();
40  }
41 
42  delete mLabelingEngine;
43  mLabelingEngine = nullptr;
44 
45  delete mLabelingEngineV2;
46  mLabelingEngineV2 = nullptr;
47 }
48 
50 {
51  if ( isActive() )
52  return;
53 
55 
57 
58  delete mLabelingEngine;
59  mLabelingEngine = nullptr;
60 
61  delete mLabelingEngineV2;
62  mLabelingEngineV2 = nullptr;
63 
65  {
66 #ifdef LABELING_V2
70 #else
74 #endif
75  }
76 
78  // prepareJobs calls mapLayer->createMapRenderer may involve cloning a RasterDataProvider,
79  // whose constructor may need to download some data (i.e. WMS, AMS) and doing so runs a
80  // QEventLoop waiting for the network request to complete. If unluckily someone calls
81  // mapCanvas->refresh() while this is happening, QgsMapRendererCustomPainterJob::cancel is
82  // called, deleting the QgsMapRendererCustomPainterJob while this function is running.
83  // Hence we need to check whether the job is still active before proceeding
84  if ( !isActive() )
85  return;
86 
87  QgsDebugMsg( QString( "QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) );
88 
89  // start async job
90 
91  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( renderLayersFinished() ) );
92 
95 }
96 
98 {
99  if ( !isActive() )
100  return;
101 
102  QgsDebugMsg( QString( "PARALLEL cancel at status %1" ).arg( mStatus ) );
103 
105  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
106  {
107  it->context.setRenderingStopped( true );
108  }
109 
110  if ( mStatus == RenderingLayers )
111  {
112  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( renderLayersFinished() ) );
113 
115 
117  }
118 
119  if ( mStatus == RenderingLabels )
120  {
121  disconnect( &mLabelingFutureWatcher, SIGNAL( finished() ), this, SLOT( renderingFinished() ) );
122 
124 
126  }
127 
128  Q_ASSERT( mStatus == Idle );
129 }
130 
132 {
133  if ( !isActive() )
134  return;
135 
136  if ( mStatus == RenderingLayers )
137  {
138  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( renderLayersFinished() ) );
139 
140  QTime t;
141  t.start();
142 
144 
145  QgsDebugMsg( QString( "waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
146 
148  }
149 
150  if ( mStatus == RenderingLabels )
151  {
152  disconnect( &mLabelingFutureWatcher, SIGNAL( finished() ), this, SLOT( renderingFinished() ) );
153 
154  QTime t;
155  t.start();
156 
158 
159  QgsDebugMsg( QString( "waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
160 
162  }
163 
164  Q_ASSERT( mStatus == Idle );
165 }
166 
168 {
169  return mStatus != Idle;
170 }
171 
173 {
174  if ( mLabelingEngine )
175  return mLabelingEngine->takeResults();
176  else if ( mLabelingEngineV2 )
177  return mLabelingEngineV2->takeResults();
178  else
179  return nullptr;
180 }
181 
183 {
184  if ( mStatus == RenderingLayers )
185  return composeImage( mSettings, mLayerJobs );
186  else
187  return mFinalImage; // when rendering labels or idle
188 }
189 
191 {
192  Q_ASSERT( mStatus == RenderingLayers );
193 
194  // compose final image
196 
198 
200 
201  QgsDebugMsg( "PARALLEL layers finished" );
202 
204  {
206 
207  connect( &mLabelingFutureWatcher, SIGNAL( finished() ), this, SLOT( renderingFinished() ) );
208 
209  // now start rendering of labeling!
212  }
213  else
214  {
216  }
217 }
218 
220 {
221  QgsDebugMsg( "PARALLEL finished" );
222 
223  mStatus = Idle;
224 
226 
227  emit finished();
228 }
229 
231 {
232  if ( job.context.renderingStopped() )
233  return;
234 
235  if ( job.cached )
236  return;
237 
238  QTime t;
239  t.start();
240  QgsDebugMsg( QString( "job %1 start (layer %2)" ).arg( reinterpret_cast< ulong >( &job ), 0, 16 ).arg( job.layerId ) );
241 
242  try
243  {
244  job.renderer->render();
245  }
246  catch ( QgsException & e )
247  {
248  Q_UNUSED( e );
249  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
250  }
251  catch ( std::exception & e )
252  {
253  Q_UNUSED( e );
254  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
255  }
256  catch ( ... )
257  {
258  QgsDebugMsg( "Caught unhandled unknown exception" );
259  }
260 
261  job.renderingTime = t.elapsed();
262  QgsDebugMsg( QString( "job %1 end [%2 ms] (layer %3)" ).arg( reinterpret_cast< ulong >( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ) );
263 }
264 
265 
267 {
268  QPainter painter( &self->mFinalImage );
269 
270  try
271  {
272  drawLabeling( self->mSettings, self->mLabelingRenderContext, self->mLabelingEngine, self->mLabelingEngineV2, &painter );
273  }
274  catch ( QgsException & e )
275  {
276  Q_UNUSED( e );
277  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
278  }
279  catch ( std::exception & e )
280  {
281  Q_UNUSED( e );
282  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
283  }
284  catch ( ... )
285  {
286  QgsDebugMsg( "Caught unhandled unknown exception" );
287  }
288 
289  painter.end();
290 }
291 
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()
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
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
bool renderingStopped() 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.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
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)
QString what() const
Definition: qgsexception.h:36
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.
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)