QGIS API Documentation  2.15.0-Master (5773d0d)
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 #ifndef QT_NO_DEBUG
75  QPaintDevice* thePaintDevice = mPainter->device();
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( qgsDoubleNear( thePaintDevice->logicalDpiX(), mSettings.outputDpi() ), "Job::startRender()", errMsg.toAscii().data() );
78 #endif
79 
80  delete mLabelingEngine;
81  mLabelingEngine = nullptr;
82 
83  delete mLabelingEngineV2;
84  mLabelingEngineV2 = nullptr;
85 
87  {
88 #ifdef LABELING_V2
89  mLabelingEngineV2 = new QgsLabelingEngineV2();
90  mLabelingEngineV2->readSettingsFromProject();
91  mLabelingEngineV2->setMapSettings( mSettings );
92 #else
93  mLabelingEngine = new QgsPalLabeling;
94  mLabelingEngine->loadEngineSettings();
95  mLabelingEngine->init( mSettings );
96 #endif
97  }
98 
99  mLayerJobs = prepareJobs( mPainter, mLabelingEngine, mLabelingEngineV2 );
100 
101  QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );
102 
103  if ( mRenderSynchronously )
104  {
105  // do the rendering right now!
106  doRender();
107  return;
108  }
109 
110  // now we are ready to start rendering!
111  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( futureFinished() ) );
112 
113  mFuture = QtConcurrent::run( staticRender, this );
114  mFutureWatcher.setFuture( mFuture );
115 }
116 
117 
119 {
120  if ( !isActive() )
121  {
122  QgsDebugMsg( "QPAINTER not running!" );
123  return;
124  }
125 
126  QgsDebugMsg( "QPAINTER cancelling" );
127  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
128 
129  mLabelingRenderContext.setRenderingStopped( true );
130  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
131  {
132  it->context.setRenderingStopped( true );
133  }
134 
135  QTime t;
136  t.start();
137 
138  mFutureWatcher.waitForFinished();
139 
140  QgsDebugMsg( QString( "QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ) );
141 
142  futureFinished();
143 
144  QgsDebugMsg( "QPAINTER cancelled" );
145 }
146 
148 {
149  if ( !isActive() )
150  return;
151 
152  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
153 
154  QTime t;
155  t.start();
156 
157  mFutureWatcher.waitForFinished();
158 
159  QgsDebugMsg( QString( "waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ) );
160 
161  futureFinished();
162 }
163 
165 {
166  return mActive;
167 }
168 
169 
171 {
172  if ( mLabelingEngine )
173  return mLabelingEngine->takeResults();
174  else if ( mLabelingEngineV2 )
175  return mLabelingEngineV2->takeResults();
176  else
177  return nullptr;
178 }
179 
180 
182 {
183  QEventLoop loop;
184  connect( &mFutureWatcher, SIGNAL( finished() ), &loop, SLOT( quit() ) );
185  loop.exec( flags );
186 }
187 
188 
190 {
191  mRenderSynchronously = true;
192  start();
193  futureFinished();
194  mRenderSynchronously = false;
195 }
196 
197 
199 {
200  mActive = false;
202  QgsDebugMsg( "QPAINTER futureFinished" );
203 
204  logRenderingTime( mLayerJobs );
205 
206  // final cleanup
207  cleanupJobs( mLayerJobs );
208 
209  emit finished();
210 }
211 
212 
214 {
215  try
216  {
217  self->doRender();
218  }
219  catch ( QgsException & e )
220  {
221  Q_UNUSED( e );
222  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
223  }
224  catch ( std::exception & e )
225  {
226  Q_UNUSED( e );
227  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
228  }
229  catch ( ... )
230  {
231  QgsDebugMsg( "Caught unhandled unknown exception" );
232  }
233 }
234 
236 {
237  QgsDebugMsg( "Starting to render layer stack." );
238  QTime renderTime;
239  renderTime.start();
240 
241  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
242  {
243  LayerRenderJob& job = *it;
244 
245  if ( job.context.renderingStopped() )
246  break;
247 
248  if ( job.context.useAdvancedEffects() )
249  {
250  // Set the QPainter composition mode so that this layer is rendered using
251  // the desired blending mode
252  mPainter->setCompositionMode( job.blendMode );
253  }
254 
255  if ( !job.cached )
256  {
257  QTime layerTime;
258  layerTime.start();
259 
260  job.renderer->render();
261 
262  job.renderingTime = layerTime.elapsed();
263  }
264 
265  if ( job.img )
266  {
267  // If we flattened this layer for alternate blend modes, composite it now
268  mPainter->drawImage( 0, 0, *job.img );
269  }
270 
271  }
272 
273  QgsDebugMsg( "Done rendering map layers" );
274 
275  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelingRenderContext.renderingStopped() )
276  drawLabeling( mSettings, mLabelingRenderContext, mLabelingEngine, mLabelingEngineV2, mPainter );
277 
278  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
279 }
280 
281 
282 void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QgsLabelingEngineV2* labelingEngine2, QPainter* painter )
283 {
284  QgsDebugMsg( "Draw labeling start" );
285 
286  QTime t;
287  t.start();
288 
289  // Reset the composition mode before rendering the labels
290  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
291 
292  // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile
293  renderContext = QgsRenderContext::fromMapSettings( settings );
294  renderContext.setPainter( painter );
295  renderContext.setLabelingEngine( labelingEngine );
296 
297 #if !defined(QGIS_DISABLE_DEPRECATED)
298  // old labeling - to be removed at some point...
299  drawOldLabeling( settings, renderContext );
300 #endif
301  drawNewLabeling( settings, renderContext, labelingEngine );
302 
303  if ( labelingEngine2 )
304  {
305  // set correct extent
306  renderContext.setExtent( settings.visibleExtent() );
307  renderContext.setCoordinateTransform( nullptr );
308 
309  labelingEngine2->run( renderContext );
310  }
311 
312  QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) );
313 }
314 
315 
317 {
318  // render all labels for vector layers in the stack, starting at the base
319  QListIterator<QString> li( settings.layers() );
320  li.toBack();
321  while ( li.hasPrevious() )
322  {
323  if ( renderContext.renderingStopped() )
324  {
325  break;
326  }
327 
328  QString layerId = li.previous();
329 
331 
332  if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) )
333  continue;
334 
335  // only make labels if the layer is visible
336  // after scale dep viewing settings are checked
337  if ( !ml->isInScaleRange( settings.scale() ) )
338  continue;
339 
340  const QgsCoordinateTransform* ct = nullptr;
341  QgsRectangle r1 = settings.visibleExtent(), r2;
342 
343  if ( settings.hasCrsTransformEnabled() )
344  {
345  ct = settings.layerTransform( ml );
346  if ( ct )
347  reprojectToLayerExtent( ml, ct, r1, r2 );
348  }
349 
350  renderContext.setCoordinateTransform( ct );
351  renderContext.setExtent( r1 );
352 
353  ml->drawLabels( renderContext );
354  }
355 }
356 
357 
358 void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine )
359 {
360  if ( labelingEngine && !renderContext.renderingStopped() )
361  {
362  // set correct extent
363  renderContext.setExtent( settings.visibleExtent() );
364  renderContext.setCoordinateTransform( nullptr );
365 
366  labelingEngine->drawLabeling( renderContext );
367  labelingEngine->exit();
368  }
369 }
370 
372 {
374  for ( ; it != mGeometryCaches.constEnd(); ++it )
375  {
376  const QgsGeometryCache& cache = it.value();
377  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( it.key() ) ) )
378  * vl->cache() = cache;
379  }
381 }
382 
383 
385 {
386  if ( ml->type() == QgsMapLayer::VectorLayer )
387  {
388  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
389  if ( vl->rendererV2() && vl->rendererV2()->forceRasterRender() )
390  {
391  //raster rendering is forced for this layer
392  return true;
393  }
395  (( vl->blendMode() != QPainter::CompositionMode_SourceOver )
396  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
397  || ( vl->layerTransparency() != 0 ) ) )
398  {
399  //layer properties require rasterisation
400  return true;
401  }
402  }
403 
404  return false;
405 }
406 
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:97
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
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)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:352
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
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 isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QSize outputSize() const
Return the size of the resulting map image.
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
bool renderingStopped() const
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)
double outputDpi() const
Return DPI used for conversion between real world units (e.g.
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.