QGIS API Documentation  2.14.0-Essen
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 "qgslabelingenginev2.h"
19 #include "qgslogger.h"
20 #include "qgsmaplayerregistry.h"
21 #include "qgsmaplayerrenderer.h"
22 #include "qgspallabeling.h"
23 #include "qgsvectorlayer.h"
24 #include "qgsrendererv2.h"
25 
26 #define LABELING_V2
27 
29  : QgsMapRendererJob( settings )
30  , mPainter( painter )
31  , mLabelingEngine( nullptr )
32  , mLabelingEngineV2( nullptr )
33  , mActive( false )
34  , mRenderSynchronously( false )
35 {
36  QgsDebugMsg( "QPAINTER construct" );
37 }
38 
40 {
41  QgsDebugMsg( "QPAINTER destruct" );
42  Q_ASSERT( !mFutureWatcher.isRunning() );
43  //cancel();
44 
45  delete mLabelingEngine;
46  mLabelingEngine = nullptr;
47 
48  delete mLabelingEngineV2;
49  mLabelingEngineV2 = nullptr;
50 }
51 
53 {
54  if ( isActive() )
55  return;
56 
58 
59  mActive = true;
60 
61  mErrors.clear();
62 
63  QgsDebugMsg( "QPAINTER run!" );
64 
65  QgsDebugMsg( "Preparing list of layer jobs for rendering" );
66  QTime prepareTime;
67  prepareTime.start();
68 
69  // clear the background
71 
72  mPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );
73 
74  QPaintDevice* thePaintDevice = mPainter->device();
75 
76  QString errMsg = QString( "pre-set DPI not equal to painter's DPI (%1 vs %2)" ).arg( thePaintDevice->logicalDpiX() ).arg( mSettings.outputDpi() );
77  Q_ASSERT_X( thePaintDevice->logicalDpiX() == mSettings.outputDpi(), "Job::startRender()", errMsg.toAscii().data() );
78 
79  delete mLabelingEngine;
80  mLabelingEngine = nullptr;
81 
82  delete mLabelingEngineV2;
83  mLabelingEngineV2 = nullptr;
84 
86  {
87 #ifdef LABELING_V2
88  mLabelingEngineV2 = new QgsLabelingEngineV2();
89  mLabelingEngineV2->readSettingsFromProject();
90  mLabelingEngineV2->setMapSettings( mSettings );
91 #else
92  mLabelingEngine = new QgsPalLabeling;
93  mLabelingEngine->loadEngineSettings();
94  mLabelingEngine->init( mSettings );
95 #endif
96  }
97 
98  mLayerJobs = prepareJobs( mPainter, mLabelingEngine, mLabelingEngineV2 );
99 
100  QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );
101 
102  if ( mRenderSynchronously )
103  {
104  // do the rendering right now!
105  doRender();
106  return;
107  }
108 
109  // now we are ready to start rendering!
110  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( futureFinished() ) );
111 
112  mFuture = QtConcurrent::run( staticRender, this );
113  mFutureWatcher.setFuture( mFuture );
114 }
115 
116 
118 {
119  if ( !isActive() )
120  {
121  QgsDebugMsg( "QPAINTER not running!" );
122  return;
123  }
124 
125  QgsDebugMsg( "QPAINTER cancelling" );
126  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
127 
128  mLabelingRenderContext.setRenderingStopped( true );
129  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
130  {
131  it->context.setRenderingStopped( true );
132  }
133 
134  QTime t;
135  t.start();
136 
137  mFutureWatcher.waitForFinished();
138 
139  QgsDebugMsg( QString( "QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ) );
140 
141  futureFinished();
142 
143  QgsDebugMsg( "QPAINTER cancelled" );
144 }
145 
147 {
148  if ( !isActive() )
149  return;
150 
151  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
152 
153  QTime t;
154  t.start();
155 
156  mFutureWatcher.waitForFinished();
157 
158  QgsDebugMsg( QString( "waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ) );
159 
160  futureFinished();
161 }
162 
164 {
165  return mActive;
166 }
167 
168 
170 {
171  if ( mLabelingEngine )
172  return mLabelingEngine->takeResults();
173  else if ( mLabelingEngineV2 )
174  return mLabelingEngineV2->takeResults();
175  else
176  return nullptr;
177 }
178 
179 
181 {
182  QEventLoop loop;
183  connect( &mFutureWatcher, SIGNAL( finished() ), &loop, SLOT( quit() ) );
184  loop.exec( flags );
185 }
186 
187 
189 {
190  mRenderSynchronously = true;
191  start();
192  futureFinished();
193  mRenderSynchronously = false;
194 }
195 
196 
198 {
199  mActive = false;
201  QgsDebugMsg( "QPAINTER futureFinished" );
202 
203  logRenderingTime( mLayerJobs );
204 
205  // final cleanup
206  cleanupJobs( mLayerJobs );
207 
208  emit finished();
209 }
210 
211 
213 {
214  try
215  {
216  self->doRender();
217  }
218  catch ( QgsException & e )
219  {
220  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
221  }
222  catch ( std::exception & e )
223  {
224  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
225  }
226  catch ( ... )
227  {
228  QgsDebugMsg( "Caught unhandled unknown exception" );
229  }
230 }
231 
233 {
234  QgsDebugMsg( "Starting to render layer stack." );
235  QTime renderTime;
236  renderTime.start();
237 
238  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
239  {
240  LayerRenderJob& job = *it;
241 
242  if ( job.context.renderingStopped() )
243  break;
244 
245  if ( job.context.useAdvancedEffects() )
246  {
247  // Set the QPainter composition mode so that this layer is rendered using
248  // the desired blending mode
249  mPainter->setCompositionMode( job.blendMode );
250  }
251 
252  if ( !job.cached )
253  {
254  QTime layerTime;
255  layerTime.start();
256 
257  job.renderer->render();
258 
259  job.renderingTime = layerTime.elapsed();
260  }
261 
262  if ( job.img )
263  {
264  // If we flattened this layer for alternate blend modes, composite it now
265  mPainter->drawImage( 0, 0, *job.img );
266  }
267 
268  }
269 
270  QgsDebugMsg( "Done rendering map layers" );
271 
272  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelingRenderContext.renderingStopped() )
273  drawLabeling( mSettings, mLabelingRenderContext, mLabelingEngine, mLabelingEngineV2, mPainter );
274 
275  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
276 }
277 
278 
279 void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QgsLabelingEngineV2* labelingEngine2, QPainter* painter )
280 {
281  QgsDebugMsg( "Draw labeling start" );
282 
283  QTime t;
284  t.start();
285 
286  // Reset the composition mode before rendering the labels
287  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
288 
289  // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile
290  renderContext = QgsRenderContext::fromMapSettings( settings );
291  renderContext.setPainter( painter );
292  renderContext.setLabelingEngine( labelingEngine );
293 
294  // old labeling - to be removed at some point...
295  drawOldLabeling( settings, renderContext );
296 
297  drawNewLabeling( settings, renderContext, labelingEngine );
298 
299  if ( labelingEngine2 )
300  {
301  // set correct extent
302  renderContext.setExtent( settings.visibleExtent() );
303  renderContext.setCoordinateTransform( nullptr );
304 
305  labelingEngine2->run( renderContext );
306  }
307 
308  QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) );
309 }
310 
311 
313 {
314  // render all labels for vector layers in the stack, starting at the base
315  QListIterator<QString> li( settings.layers() );
316  li.toBack();
317  while ( li.hasPrevious() )
318  {
319  if ( renderContext.renderingStopped() )
320  {
321  break;
322  }
323 
324  QString layerId = li.previous();
325 
327 
328  if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) )
329  continue;
330 
331  // only make labels if the layer is visible
332  // after scale dep viewing settings are checked
333  if ( ml->hasScaleBasedVisibility() && ( settings.scale() < ml->minimumScale() || settings.scale() > ml->maximumScale() ) )
334  continue;
335 
336  const QgsCoordinateTransform* ct = nullptr;
337  QgsRectangle r1 = settings.visibleExtent(), r2;
338 
339  if ( settings.hasCrsTransformEnabled() )
340  {
341  ct = settings.layerTransform( ml );
342  if ( ct )
343  reprojectToLayerExtent( ml, ct, r1, r2 );
344  }
345 
346  renderContext.setCoordinateTransform( ct );
347  renderContext.setExtent( r1 );
348 
349  ml->drawLabels( renderContext );
350  }
351 }
352 
353 
354 void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine )
355 {
356  if ( labelingEngine && !renderContext.renderingStopped() )
357  {
358  // set correct extent
359  renderContext.setExtent( settings.visibleExtent() );
360  renderContext.setCoordinateTransform( nullptr );
361 
362  labelingEngine->drawLabeling( renderContext );
363  labelingEngine->exit();
364  }
365 }
366 
368 {
370  for ( ; it != mGeometryCaches.constEnd(); ++it )
371  {
372  const QgsGeometryCache& cache = it.value();
373  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( it.key() ) ) )
374  * vl->cache() = cache;
375  }
377 }
378 
379 
381 {
382  if ( ml->type() == QgsMapLayer::VectorLayer )
383  {
384  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
385  if ( vl->rendererV2() && vl->rendererV2()->forceRasterRender() )
386  {
387  //raster rendering is forced for this layer
388  return true;
389  }
391  (( vl->blendMode() != QPainter::CompositionMode_SourceOver )
392  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
393  || ( vl->layerTransparency() != 0 ) ) )
394  {
395  //layer properties require rasterisation
396  return true;
397  }
398  }
399 
400  return false;
401 }
402 
QgsMapRendererCustomPainterJob(const QgsMapSettings &settings, QPainter *painter)
void clear()
QString fromAscii(const char *str, int size)
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:49
Job implementation that renders everything sequentially using a custom painter.
QgsMapLayer::LayerType type() const
Get the type of the layer.
Definition: qgsmaplayer.cpp:99
int width() const
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 fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
void setRenderHint(RenderHint hint, bool on)
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&#39;s CRS to destination CRS.
void updateLayerGeometryCaches()
called when rendering has finished to update all layers&#39; geometry caches
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void loadEngineSettings()
load/save engine settings to project file
void logRenderingTime(const LayerRenderJobs &jobs)
static bool reprojectToLayerExtent(const QgsMapLayer *ml, const QgsCoordinateTransform *ct, QgsRectangle &extent, QgsRectangle &r2)
Convenience function to project an extent into the layer source CRS, but also split it into two exten...
QMap< QString, QgsGeometryCache > mGeometryCaches
map of geometry caches
const_iterator constBegin() const
float minimumScale() const
Returns the minimum scale denominator at which the layer is visible.
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
The QgsLabelingEngineV2 class provides map labeling functionality.
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
virtual void cancel() override
Stop the rendering job - does not return until the job has terminated.
void readSettingsFromProject()
Read configuration of the labeling engine from the current project file.
void clear()
Enable layer transparency and blending effects.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void setExtent(const QgsRectangle &extent)
Enable drawing of labels on top of the map.
QgsMapLayer * mapLayer(const QString &theLayerId)
Retrieve a pointer to a loaded layer by id.
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.
QgsFeatureRendererV2 * rendererV2()
Return renderer V2.
int elapsed() const
int outputDpi() const
Return DPI used for conversion between real world units (e.g.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
bool forceRasterRender() const
Returns whether the renderer must render as a raster.
virtual void start() override
Start the rendering job and immediately return.
QPainter::CompositionMode featureBlendMode() const
Returns the current blending mode for features.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
QSize outputSize() const
Return the size of the resulting map image.
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
bool renderingStopped() const
float maximumScale() const
Returns the maximum scale denominator at which the layer is visible.
static void staticRender(QgsMapRendererCustomPainterJob *self)
virtual void drawLabeling(QgsRenderContext &context) override
called when the map is drawn and labels should be placed
const_iterator constEnd() const
Enable anti-aliasin for map rendering.
QPaintDevice * device() const
void setPainter(QPainter *p)
QFuture< T > run(Function function,...)
void setFuture(const QFuture< T > &future)
QgsMapSettings mSettings
int logicalDpiX() const
iterator end()
QColor backgroundColor() const
Get the background color of the map.
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
void run(QgsRenderContext &context)
compute the labeling with given map settings and providers
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
int layerTransparency() const
Returns the current transparency for the vector layer.
static void drawOldLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext)
const Key key(const T &value) const
bool isRunning() const
void waitForFinished()
static void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsPalLabeling *labelingEngine, QgsLabelingEngineV2 *labelingEngine2, QPainter *painter)
QString what() const
Definition: qgsexception.h:36
typedef ProcessEventsFlags
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.
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
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) ...
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void setLabelingEngine(QgsLabelingEngineInterface *iface)
int height() const
QgsMapLayerRenderer * renderer
Class for doing transforms between two map coordinate systems.
char * data()
QgsRenderContext context
QStringList layers() const
Get list of layer IDs for map rendering The layers are stored in the reverse order of how they are re...
void start()
Class that stores computed placement from labeling engine.
virtual QgsLabelingResults * takeLabelingResults() override
Get pointer to internal labeling engine (in order to get access to the results)
QPainter::CompositionMode blendMode
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual void waitForFinished() override
Block until the job has finished.
Represents a vector layer which manages a vector based data sets.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool needTemporaryImage(QgsMapLayer *ml)
void waitForFinishedWithEventLoop(const QEventLoop::ProcessEventsFlags &flags=QEventLoop::AllEvents)
Wait for the job to be finished - and keep the thread&#39;s event loop running while waiting.
Defines a qgis exception class.
Definition: qgsexception.h:25
void renderSynchronously()
Render the map synchronously in this thread.
QgsLabelingResults * takeResults()
Return pointer to recently computed results and pass the ownership of results to the caller...
iterator begin()
QByteArray toAscii() const
virtual void exit() override
called when we&#39;re done with rendering
Structure keeping low-level rendering job information.
const T value(const Key &key) const
virtual bool isActive() const override
Tell whether the rendering job is currently running in background.