QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsfeedback.h"
19 #include "qgslabelingenginev2.h"
20 #include "qgslogger.h"
21 #include "qgsmaplayerregistry.h"
22 #include "qgsmaplayerrenderer.h"
23 #include "qgspallabeling.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsrendererv2.h"
26 
27 #define LABELING_V2
28 
30  : QgsMapRendererJob( settings )
31  , mPainter( painter )
32  , mLabelingEngine( nullptr )
33  , mLabelingEngineV2( nullptr )
34  , mActive( false )
35  , mRenderSynchronously( false )
36 {
37  QgsDebugMsg( "QPAINTER construct" );
38 }
39 
41 {
42  QgsDebugMsg( "QPAINTER destruct" );
43  Q_ASSERT( !mFutureWatcher.isRunning() );
44  //cancel();
45 
46  delete mLabelingEngine;
47  mLabelingEngine = nullptr;
48 
49  delete mLabelingEngineV2;
50  mLabelingEngineV2 = nullptr;
51 }
52 
54 {
55  if ( isActive() )
56  return;
57 
59 
60  mActive = true;
61 
62  mErrors.clear();
63 
64  QgsDebugMsg( "QPAINTER run!" );
65 
66  QgsDebugMsg( "Preparing list of layer jobs for rendering" );
67  QTime prepareTime;
68  prepareTime.start();
69 
70  // clear the background
72 
73  mPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );
74 
75 #ifndef QT_NO_DEBUG
76  QPaintDevice* thePaintDevice = mPainter->device();
77  QString errMsg = QString( "pre-set DPI not equal to painter's DPI (%1 vs %2)" ).arg( thePaintDevice->logicalDpiX() ).arg( mSettings.outputDpi() );
78  Q_ASSERT_X( thePaintDevice->logicalDpiX() == qgsRound( mSettings.outputDpi() ), "Job::startRender()", errMsg.toAscii().data() );
79 #endif
80 
81  delete mLabelingEngine;
82  mLabelingEngine = nullptr;
83 
84  delete mLabelingEngineV2;
85  mLabelingEngineV2 = nullptr;
86 
88  {
89 #ifdef LABELING_V2
90  mLabelingEngineV2 = new QgsLabelingEngineV2();
91  mLabelingEngineV2->readSettingsFromProject();
92  mLabelingEngineV2->setMapSettings( mSettings );
93 #else
94  mLabelingEngine = new QgsPalLabeling;
95  mLabelingEngine->loadEngineSettings();
96  mLabelingEngine->init( mSettings );
97 #endif
98  }
99 
100  mLayerJobs = prepareJobs( mPainter, mLabelingEngine, mLabelingEngineV2 );
101 
102  QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );
103 
104  if ( mRenderSynchronously )
105  {
106  // do the rendering right now!
107  doRender();
108  return;
109  }
110 
111  // now we are ready to start rendering!
112  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( futureFinished() ) );
113 
114  mFuture = QtConcurrent::run( staticRender, this );
115  mFutureWatcher.setFuture( mFuture );
116 }
117 
118 
120 {
121  if ( !isActive() )
122  {
123  QgsDebugMsg( "QPAINTER not running!" );
124  return;
125  }
126 
127  QgsDebugMsg( "QPAINTER cancelling" );
128  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
130 
131  QTime t;
132  t.start();
133 
134  mFutureWatcher.waitForFinished();
135 
136  QgsDebugMsg( QString( "QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ) );
137 
138  futureFinished();
139 
140  QgsDebugMsg( "QPAINTER canceled" );
141 }
142 
144 {
145  if ( !isActive() )
146  {
147  QgsDebugMsg( "QPAINTER not running!" );
148  return;
149  }
150 
151  mLabelingRenderContext.setRenderingStopped( true );
152  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
153  {
154  it->context.setRenderingStopped( true );
155  if ( it->renderer && it->renderer->feedback() )
156  it->renderer->feedback()->cancel();
157  }
158 }
159 
161 {
162  if ( !isActive() )
163  return;
164 
165  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
166 
167  QTime t;
168  t.start();
169 
170  mFutureWatcher.waitForFinished();
171 
172  QgsDebugMsg( QString( "waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ) );
173 
174  futureFinished();
175 }
176 
178 {
179  return mActive;
180 }
181 
182 
184 {
185  if ( mLabelingEngine )
186  return mLabelingEngine->takeResults();
187  else if ( mLabelingEngineV2 )
188  return mLabelingEngineV2->takeResults();
189  else
190  return nullptr;
191 }
192 
193 
195 {
196  QEventLoop loop;
197  connect( &mFutureWatcher, SIGNAL( finished() ), &loop, SLOT( quit() ) );
198  loop.exec( flags );
199 }
200 
201 
203 {
204  mRenderSynchronously = true;
205  start();
206  futureFinished();
207  mRenderSynchronously = false;
208 }
209 
210 
212 {
213  mActive = false;
215  QgsDebugMsg( "QPAINTER futureFinished" );
216 
217  logRenderingTime( mLayerJobs );
218 
219  // final cleanup
220  cleanupJobs( mLayerJobs );
221 
222  emit finished();
223 }
224 
225 
227 {
228  try
229  {
230  self->doRender();
231  }
232  catch ( QgsException & e )
233  {
234  Q_UNUSED( e );
235  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
236  }
237  catch ( std::exception & e )
238  {
239  Q_UNUSED( e );
240  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
241  }
242  catch ( ... )
243  {
244  QgsDebugMsg( "Caught unhandled unknown exception" );
245  }
246 }
247 
249 {
250  QgsDebugMsg( "Starting to render layer stack." );
251  QTime renderTime;
252  renderTime.start();
253 
254  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
255  {
256  LayerRenderJob& job = *it;
257 
258  if ( job.context.renderingStopped() )
259  break;
260 
261  if ( job.context.useAdvancedEffects() )
262  {
263  // Set the QPainter composition mode so that this layer is rendered using
264  // the desired blending mode
265  mPainter->setCompositionMode( job.blendMode );
266  }
267 
268  if ( !job.cached )
269  {
270  QTime layerTime;
271  layerTime.start();
272 
273  job.renderer->render();
274 
275  job.renderingTime = layerTime.elapsed();
276  }
277 
278  if ( job.img )
279  {
280  // If we flattened this layer for alternate blend modes, composite it now
281  mPainter->setOpacity( job.opacity );
282  mPainter->drawImage( 0, 0, *job.img );
283  mPainter->setOpacity( 1.0 );
284  }
285 
286  }
287 
288  QgsDebugMsg( "Done rendering map layers" );
289 
290  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelingRenderContext.renderingStopped() )
291  drawLabeling( mSettings, mLabelingRenderContext, mLabelingEngine, mLabelingEngineV2, mPainter );
292 
293  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
294 }
295 
296 
297 void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QgsLabelingEngineV2* labelingEngine2, QPainter* painter )
298 {
299  QgsDebugMsg( "Draw labeling start" );
300 
301  QTime t;
302  t.start();
303 
304  // Reset the composition mode before rendering the labels
305  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
306 
307  // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile
308  renderContext = QgsRenderContext::fromMapSettings( settings );
309  renderContext.setPainter( painter );
310  renderContext.setLabelingEngine( labelingEngine );
311 
312 #if !defined(QGIS_DISABLE_DEPRECATED)
313  // old labeling - to be removed at some point...
314  drawOldLabeling( settings, renderContext );
315 #endif
316  drawNewLabeling( settings, renderContext, labelingEngine );
317 
318  if ( labelingEngine2 )
319  {
320  // set correct extent
321  renderContext.setExtent( settings.visibleExtent() );
322  renderContext.setCoordinateTransform( nullptr );
323 
324  labelingEngine2->run( renderContext );
325  }
326 
327  QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) );
328 }
329 
330 
332 {
333  // render all labels for vector layers in the stack, starting at the base
334  QListIterator<QString> li( settings.layers() );
335  li.toBack();
336  while ( li.hasPrevious() )
337  {
338  if ( renderContext.renderingStopped() )
339  {
340  break;
341  }
342 
343  QString layerId = li.previous();
344 
346 
347  if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) )
348  continue;
349 
350  // only make labels if the layer is visible
351  // after scale dep viewing settings are checked
352  if ( !ml->isInScaleRange( settings.scale() ) )
353  continue;
354 
355  const QgsCoordinateTransform* ct = nullptr;
356  QgsRectangle r1 = settings.visibleExtent(), r2;
357 
358  if ( settings.hasCrsTransformEnabled() )
359  {
360  ct = settings.layerTransform( ml );
361  if ( ct )
362  reprojectToLayerExtent( ml, ct, r1, r2 );
363  }
364 
365  renderContext.setCoordinateTransform( ct );
366  renderContext.setExtent( r1 );
367 
368  ml->drawLabels( renderContext );
369  }
370 }
371 
372 
373 void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine )
374 {
375  if ( labelingEngine && !renderContext.renderingStopped() )
376  {
377  // set correct extent
378  renderContext.setExtent( settings.visibleExtent() );
379  renderContext.setCoordinateTransform( nullptr );
380 
381  labelingEngine->drawLabeling( renderContext );
382  labelingEngine->exit();
383  }
384 }
385 
387 {
389  for ( ; it != mGeometryCaches.constEnd(); ++it )
390  {
391  const QgsGeometryCache& cache = it.value();
392  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( it.key() ) ) )
393  * vl->cache() = cache;
394  }
396 }
397 
398 
400 {
401  if ( ml->type() == QgsMapLayer::VectorLayer )
402  {
403  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
404  if ( vl->rendererV2() && vl->rendererV2()->forceRasterRender() )
405  {
406  //raster rendering is forced for this layer
407  return true;
408  }
410  (( vl->blendMode() != QPainter::CompositionMode_SourceOver )
411  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
412  || ( vl->layerTransparency() != 0 ) ) )
413  {
414  //layer properties require rasterisation
415  return true;
416  }
417  }
418  else if ( ml->type() == QgsMapLayer::RasterLayer )
419  {
420  // preview of intermediate raster rendering results requires a temporary output image
422  return true;
423  }
424 
425  return false;
426 }
427 
QgsMapRendererCustomPainterJob(const QgsMapSettings &settings, QPainter *painter)
void clear()
QString fromAscii(const char *str, int size)
void setOpacity(qreal opacity)
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.
int width() const
Abstract base class for map rendering implementations.
virtual void drawLabels(QgsRenderContext &rendererContext)
Draw labels.
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 ...
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
QgsMapLayer * mapLayer(const QString &theLayerId) const
Retrieve a pointer to a registered layer by layer ID.
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
QColor backgroundColor() const
Get the background color of the map.
const_iterator constBegin() const
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
bool renderingStopped() const
The QgsLabelingEngineV2 class provides map labeling functionality.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
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)
QgsMapLayer::LayerType type() const
Get the type of the layer.
Definition: qgsmaplayer.cpp:99
void setExtent(const QgsRectangle &extent)
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
Enable drawing of labels on top of the map.
QString what() const
Definition: qgsexception.h:36
The QgsMapSettings class contains configuration for rendering of the map.
void setCoordinateTransform(const QgsCoordinateTransform *t)
Sets coordinate transformation.
double qgsRound(double x)
A round function which returns a double to guard against overflows.
Definition: qgis.h:386
QgsFeatureRendererV2 * rendererV2()
Return renderer V2.
int elapsed() const
virtual void start() override
Start the rendering job and immediately return.
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
double scale() const
Return the calculated scale of the map.
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-aliasing for map rendering.
QPaintDevice * device() const
void setPainter(QPainter *p)
QFuture< T > run(Function function,...)
int layerTransparency() const
Returns the current transparency for the vector layer.
const QgsCoordinateTransform * layerTransform(QgsMapLayer *layer) const
Return coordinate transform from layer&#39;s CRS to destination CRS.
void setFuture(const QFuture< T > &future)
QPainter::CompositionMode featureBlendMode() const
Returns the current blending mode for features.
QgsMapSettings mSettings
int logicalDpiX() const
iterator end()
virtual void cancelWithoutBlocking() override
Triggers cancellation of the rendering job without blocking.
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
void run(QgsRenderContext &context)
compute the labeling with given map settings and providers
static void drawOldLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext)
const Key key(const T &value) const
bool isRunning() const
void waitForFinished()
double outputDpi() const
Return DPI used for conversion between real world units (e.g.
static void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsPalLabeling *labelingEngine, QgsLabelingEngineV2 *labelingEngine2, QPainter *painter)
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)
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
int height() const
QgsMapLayerRenderer * renderer
Class for doing transforms between two map coordinate systems.
char * data()
QgsRenderContext context
void start()
bool forceRasterRender() const
Returns whether the renderer must render as a raster.
Class that stores computed placement from labeling engine.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
virtual QgsLabelingResults * takeLabelingResults() override
Get pointer to internal labeling engine (in order to get access to the results)
QStringList layers() const
Get list of layer IDs for map rendering The layers are stored in the reverse order of how they are re...
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()
QSize outputSize() const
Return the size of the resulting map image.
QByteArray toAscii() const
virtual void exit() override
called when we&#39;re done with rendering
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
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.