QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsmaprendererjob.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprendererjob.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 
16 #include "qgsmaprendererjob.h"
17 
18 #include <QPainter>
19 #include <QElapsedTimer>
20 #include <QTimer>
21 #include <QtConcurrentMap>
22 
23 #include "qgslogger.h"
24 #include "qgsrendercontext.h"
25 #include "qgsmaplayer.h"
26 #include "qgsproject.h"
27 #include "qgsmaplayerrenderer.h"
29 #include "qgsmaprenderercache.h"
30 #include "qgsmessagelog.h"
31 #include "qgspallabeling.h"
32 #include "qgsvectorlayerrenderer.h"
33 #include "qgsvectorlayer.h"
34 #include "qgsexception.h"
35 #include "qgslabelingengine.h"
36 #include "qgsmaplayerlistutils.h"
37 #include "qgsvectorlayerlabeling.h"
38 #include "qgssettings.h"
40 #include "qgssymbol.h"
41 #include "qgsrenderer.h"
42 #include "qgssymbollayer.h"
43 #include "qgsvectorlayerutils.h"
44 #include "qgssymbollayerutils.h"
46 
48 
49 const QString QgsMapRendererJob::LABEL_CACHE_ID = QStringLiteral( "_labels_" );
50 
52  : mSettings( settings )
53 
54 {
55 }
56 
57 
59  : QgsMapRendererJob( settings )
60 {
61 }
62 
63 
65 {
66  return mErrors;
67 }
68 
70 {
71  mCache = cache;
72 }
73 
74 QHash<QgsMapLayer *, int> QgsMapRendererJob::perLayerRenderingTime() const
75 {
76  QHash<QgsMapLayer *, int> result;
77  for ( auto it = mPerLayerRenderingTime.constBegin(); it != mPerLayerRenderingTime.constEnd(); ++it )
78  {
79  if ( it.key() )
80  result.insert( it.key(), it.value() );
81  }
82  return result;
83 }
84 
86 {
87  return mSettings;
88 }
89 
91 {
92  bool canCache = mCache;
93 
94  // calculate which layers will be labeled
95  QSet< QgsMapLayer * > labeledLayers;
96  const QList<QgsMapLayer *> layers = mSettings.layers();
97  for ( const QgsMapLayer *ml : layers )
98  {
99  QgsVectorLayer *vl = const_cast< QgsVectorLayer * >( qobject_cast<const QgsVectorLayer *>( ml ) );
100  if ( vl && QgsPalLabeling::staticWillUseLayer( vl ) )
101  labeledLayers << vl;
102  if ( vl && vl->labelsEnabled() && vl->labeling()->requiresAdvancedEffects() )
103  {
104  canCache = false;
105  break;
106  }
107  }
108 
110  {
111  // we may need to clear label cache and re-register labeled features - check for that here
112 
113  // can we reuse the cached label solution?
114  bool canUseCache = canCache && qgis::listToSet( mCache->dependentLayers( LABEL_CACHE_ID ) ) == labeledLayers;
115  if ( !canUseCache )
116  {
117  // no - participating layers have changed
119  }
120  }
121  return canCache;
122 }
123 
124 
125 bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform &ct, QgsRectangle &extent, QgsRectangle &r2 )
126 {
127  bool split = false;
128 
129  // we can safely use ballpark transforms without bothering the user here -- at the likely scale of layer extents there
130  // won't be an appreciable difference, and we aren't actually transforming any rendered points here anyway (just the layer extent)
131  QgsCoordinateTransform approxTransform = ct;
132  approxTransform.setBallparkTransformsAreAppropriate( true );
133 
134  try
135  {
136 #ifdef QGISDEBUG
137  // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__);
138 #endif
139  // Split the extent into two if the source CRS is
140  // geographic and the extent crosses the split in
141  // geographic coordinates (usually +/- 180 degrees,
142  // and is assumed to be so here), and draw each
143  // extent separately.
144  static const double SPLIT_COORD = 180.0;
145 
146  if ( ml->crs().isGeographic() )
147  {
148  if ( ml->type() == QgsMapLayerType::VectorLayer && !approxTransform.destinationCrs().isGeographic() )
149  {
150  // if we transform from a projected coordinate system check
151  // check if transforming back roughly returns the input
152  // extend - otherwise render the world.
154  QgsRectangle extent2 = approxTransform.transformBoundingBox( extent1, QgsCoordinateTransform::ForwardTransform );
155 
156  QgsDebugMsgLevel( QStringLiteral( "\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
157  .arg( extent.toString() ).arg( extent.width() ).arg( extent.height() )
158  .arg( extent1.toString(), extent2.toString() ).arg( extent2.width() ).arg( extent2.height() )
159  .arg( std::fabs( 1.0 - extent2.width() / extent.width() ) )
160  .arg( std::fabs( 1.0 - extent2.height() / extent.height() ) )
161  , 3 );
162 
163  // can differ by a maximum of up to 20% of height/width
164  if ( qgsDoubleNear( extent2.xMinimum(), extent.xMinimum(), extent.width() * 0.2 )
165  && qgsDoubleNear( extent2.xMaximum(), extent.xMaximum(), extent.width() * 0.2 )
166  && qgsDoubleNear( extent2.yMinimum(), extent.yMinimum(), extent.height() * 0.2 )
167  && qgsDoubleNear( extent2.yMaximum(), extent.yMaximum(), extent.height() * 0.2 )
168  )
169  {
170  extent = extent1;
171  }
172  else
173  {
174  extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
175  }
176  }
177  else
178  {
179  // Note: ll = lower left point
180  QgsPointXY ll = approxTransform.transform( extent.xMinimum(), extent.yMinimum(),
182 
183  // and ur = upper right point
184  QgsPointXY ur = approxTransform.transform( extent.xMaximum(), extent.yMaximum(),
186 
187  QgsDebugMsgLevel( QStringLiteral( "in:%1 (ll:%2 ur:%3)" ).arg( extent.toString(), ll.toString(), ur.toString() ), 4 );
188 
189  extent = approxTransform.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
190 
191  QgsDebugMsgLevel( QStringLiteral( "out:%1 (w:%2 h:%3)" ).arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ), 4 );
192 
193  if ( ll.x() > ur.x() )
194  {
195  // the coordinates projected in reverse order than what one would expect.
196  // we are probably looking at an area that includes longitude of 180 degrees.
197  // we need to take into account coordinates from two intervals: (-180,x1) and (x2,180)
198  // so let's use (-180,180). This hopefully does not add too much overhead. It is
199  // more straightforward than rendering with two separate extents and more consistent
200  // for rendering, labeling and caching as everything is rendered just in one go
201  extent.setXMinimum( -SPLIT_COORD );
202  extent.setXMaximum( SPLIT_COORD );
203  }
204  }
205 
206  // TODO: the above rule still does not help if using a projection that covers the whole
207  // world. E.g. with EPSG:3857 the longitude spectrum -180 to +180 is mapped to approx.
208  // -2e7 to +2e7. Converting extent from -5e7 to +5e7 is transformed as -90 to +90,
209  // but in fact the extent should cover the whole world.
210  }
211  else // can't cross 180
212  {
213  if ( approxTransform.destinationCrs().isGeographic() &&
214  ( extent.xMinimum() <= -180 || extent.xMaximum() >= 180 ||
215  extent.yMinimum() <= -90 || extent.yMaximum() >= 90 ) )
216  // Use unlimited rectangle because otherwise we may end up transforming wrong coordinates.
217  // E.g. longitude -200 to +160 would be understood as +40 to +160 due to periodicity.
218  // We could try to clamp coords to (-180,180) for lon resp. (-90,90) for lat,
219  // but this seems like a safer choice.
220  extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
221  else
222  extent = approxTransform.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
223  }
224  }
225  catch ( QgsCsException &cse )
226  {
227  Q_UNUSED( cse )
228  QgsDebugMsg( QStringLiteral( "Transform error caught" ) );
229  extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
230  r2 = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
231  }
232 
233  return split;
234 }
235 
236 QImage *QgsMapRendererJob::allocateImage( QString layerId )
237 {
238  QImage *image = new QImage( mSettings.deviceOutputSize(),
240  image->setDevicePixelRatio( static_cast<qreal>( mSettings.devicePixelRatio() ) );
241  if ( image->isNull() )
242  {
243  mErrors.append( Error( layerId, tr( "Insufficient memory for image %1x%2" ).arg( mSettings.outputSize().width() ).arg( mSettings.outputSize().height() ) ) );
244  delete image;
245  return nullptr;
246  }
247  return image;
248 }
249 
250 QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image )
251 {
252  QPainter *painter = nullptr;
253  image = allocateImage( layerId );
254  if ( image )
255  {
256  painter = new QPainter( image );
257  painter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );
258  }
259  return painter;
260 }
261 
262 LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet )
263 {
264  LayerRenderJobs layerJobs;
265 
266  // render all layers in the stack, starting at the base
267  QListIterator<QgsMapLayer *> li( mSettings.layers() );
268  li.toBack();
269 
270  if ( mCache )
271  {
272  bool cacheValid = mCache->init( mSettings.visibleExtent(), mSettings.scale() );
273  Q_UNUSED( cacheValid )
274  QgsDebugMsgLevel( QStringLiteral( "CACHE VALID: %1" ).arg( cacheValid ), 4 );
275  }
276 
277  bool requiresLabelRedraw = !( mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) );
278 
279  while ( li.hasPrevious() )
280  {
281  QgsMapLayer *ml = li.previous();
282 
283  QgsDebugMsgLevel( QStringLiteral( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6" )
284  .arg( ml->name() )
285  .arg( ml->minimumScale() )
286  .arg( ml->maximumScale() )
287  .arg( ml->hasScaleBasedVisibility() )
288  .arg( ml->blendMode() )
289  .arg( ml->isValid() )
290  , 3 );
291 
292  if ( !ml->isValid() )
293  {
294  QgsDebugMsgLevel( QStringLiteral( "Invalid Layer skipped" ), 3 );
295  continue;
296  }
297 
298  if ( !ml->isInScaleRange( mSettings.scale() ) ) //|| mOverview )
299  {
300  QgsDebugMsgLevel( QStringLiteral( "Layer not rendered because it is not within the defined visibility scale range" ), 3 );
301  continue;
302  }
303 
305  {
306  QgsDebugMsgLevel( QStringLiteral( "Layer not rendered because it is not visible within the map's time range" ), 3 );
307  continue;
308  }
309 
311  r1.grow( mSettings.extentBuffer() );
313 
314  ct = mSettings.layerTransform( ml );
315  if ( ct.isValid() )
316  {
317  reprojectToLayerExtent( ml, ct, r1, r2 );
318  }
319  QgsDebugMsgLevel( "extent: " + r1.toString(), 3 );
320  if ( !r1.isFinite() || !r2.isFinite() )
321  {
322  mErrors.append( Error( ml->id(), tr( "There was a problem transforming the layer's extent. Layer skipped." ) ) );
323  continue;
324  }
325 
326  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
327 
328  // Force render of layers that are being edited
329  // or if there's a labeling engine that needs the layer to register features
330  if ( mCache && vl )
331  {
332  bool requiresLabeling = false;
333  requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( vl ) ) && requiresLabelRedraw;
334  if ( vl->isEditable() || requiresLabeling )
335  {
336  mCache->clearCacheImage( ml->id() );
337  }
338  }
339 
340  layerJobs.append( LayerRenderJob() );
341  LayerRenderJob &job = layerJobs.last();
342  job.cached = false;
343  job.img = nullptr;
344  job.layer = ml;
345  job.layerId = ml->id();
346  job.renderingTime = -1;
347 
349  job.context.expressionContext().appendScope( QgsExpressionContextUtils::layerScope( ml ) );
350  job.context.setPainter( painter );
351  job.context.setLabelingEngine( labelingEngine2 );
352  job.context.setCoordinateTransform( ct );
353  job.context.setExtent( r1 );
354 
355  if ( mFeatureFilterProvider )
356  job.context.setFeatureFilterProvider( mFeatureFilterProvider );
357 
358  QgsMapLayerStyleOverride styleOverride( ml );
359  if ( mSettings.layerStyleOverrides().contains( ml->id() ) )
360  styleOverride.setOverrideStyle( mSettings.layerStyleOverrides().value( ml->id() ) );
361 
362  job.blendMode = ml->blendMode();
363  job.opacity = 1.0;
364  if ( vl )
365  {
366  job.opacity = vl->opacity();
367  }
368 
369  // if we can use the cache, let's do it and avoid rendering!
370  if ( mCache && mCache->hasCacheImage( ml->id() ) )
371  {
372  job.cached = true;
373  job.imageInitialized = true;
374  job.img = new QImage( mCache->cacheImage( ml->id() ) );
375  job.img->setDevicePixelRatio( static_cast<qreal>( mSettings.devicePixelRatio() ) );
376  job.renderer = nullptr;
377  job.context.setPainter( nullptr );
378  continue;
379  }
380 
381  // If we are drawing with an alternative blending mode then we need to render to a separate image
382  // before compositing this on the map. This effectively flattens the layer and prevents
383  // blending occurring between objects on the layer
384  if ( mCache || ( !painter && !deferredPainterSet ) || needTemporaryImage( ml ) )
385  {
386  // Flattened image for drawing when a blending mode is set
387  job.context.setPainter( allocateImageAndPainter( ml->id(), job.img ) );
388  if ( ! job.img )
389  {
390  layerJobs.removeLast();
391  continue;
392  }
393  }
394 
395  QElapsedTimer layerTime;
396  layerTime.start();
397  job.renderer = ml->createMapRenderer( job.context );
398  job.renderingTime = layerTime.elapsed(); // include job preparation time in layer rendering time
399  } // while (li.hasPrevious())
400 
401  return layerJobs;
402 }
403 
404 LayerRenderJobs QgsMapRendererJob::prepareSecondPassJobs( LayerRenderJobs &firstPassJobs, LabelRenderJob &labelJob )
405 {
406  LayerRenderJobs secondPassJobs;
407 
408  // We will need to quickly access the associated rendering job of a layer
409  QHash<QString, LayerRenderJob *> layerJobMapping;
410 
411  // ... and whether a layer has a mask defined
412  QSet<QString> layerHasMask;
413 
414  struct MaskSource
415  {
416  QString layerId;
417  QString labelRuleId;
418  int labelMaskId;
419  MaskSource( const QString &layerId_, const QString &labelRuleId_, int labelMaskId_ ):
420  layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ) {}
421  };
422 
423  // We collect for each layer, the set of symbol layers that will be "masked"
424  // and the list of source layers that have a mask
425  QHash<QString, QPair<QSet<QgsSymbolLayerId>, QList<MaskSource>>> maskedSymbolLayers;
426 
427  for ( LayerRenderJob &job : firstPassJobs )
428  {
429  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
430  if ( ! vl )
431  continue;
432 
433  layerJobMapping[job.layer->id()] = &job;
434 
435  // lambda function to factor code for both label masks and symbol layer masks
436  auto collectMasks = [&]( QHash<QString, QSet<QgsSymbolLayerId>> *masks, QString sourceLayerId, QString ruleId = QString(), int labelMaskId = -1 )
437  {
438  for ( auto it = masks->begin(); it != masks->end(); ++it )
439  {
440  auto lit = maskedSymbolLayers.find( it.key() );
441  if ( lit == maskedSymbolLayers.end() )
442  {
443  maskedSymbolLayers[it.key()] = qMakePair( it.value(), QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId ) );
444  }
445  else
446  {
447  if ( lit->first != it.value() )
448  {
449  QgsLogger::warning( QStringLiteral( "Layer %1 : Different sets of symbol layers are masked by different sources ! Only one (arbitrary) set will be retained !" ).arg( it.key() ) );
450  continue;
451  }
452  lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId ) );
453  }
454  }
455  if ( ! masks->isEmpty() )
456  layerHasMask.insert( sourceLayerId );
457  };
458 
459  // collect label masks
460  QHash<QString, QHash<QString, QSet<QgsSymbolLayerId>>> labelMasks = QgsVectorLayerUtils::labelMasks( vl );
461  for ( auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
462  {
463  QString labelRule = it.key();
464  QHash<QString, QSet<QgsSymbolLayerId>> masks = it.value();
465 
466  // group layers by QSet<QgsSymbolLayerReference>
467  QSet<QgsSymbolLayerReference> slRefs;
468  for ( auto mit = masks.begin(); mit != masks.end(); mit++ )
469  {
470  for ( auto slIt = mit.value().begin(); slIt != mit.value().end(); slIt++ )
471  {
472  slRefs.insert( QgsSymbolLayerReference( mit.key(), *slIt ) );
473  }
474  }
475  // generate a new mask id for this set
476  int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->id(), it.key(), slRefs );
477 
478  // now collect masks
479  collectMasks( &masks, vl->id(), labelRule, labelMaskId );
480  }
481 
482  // collect symbol layer masks
483  QHash<QString, QSet<QgsSymbolLayerId>> symbolLayerMasks = QgsVectorLayerUtils::symbolLayerMasks( vl );
484  collectMasks( &symbolLayerMasks, vl->id() );
485  }
486 
487  if ( maskedSymbolLayers.isEmpty() )
488  return secondPassJobs;
489 
490  // Now that we know some layers have a mask, we have to allocate a mask image and painter
491  // for them in the first pass job
492  for ( LayerRenderJob &job : firstPassJobs )
493  {
494  QgsMapLayer *ml = job.layer;
495 
496  if ( job.img == nullptr )
497  {
498  job.context.setPainter( allocateImageAndPainter( ml->id(), job.img ) );
499  }
500  if ( layerHasMask.contains( ml->id() ) )
501  {
502  // Note: we only need an alpha channel here, rather than a full RGBA image
503  job.context.setMaskPainter( allocateImageAndPainter( ml->id(), job.maskImage ) );
504  job.maskImage->fill( 0 );
505  }
506  }
507 
508  // Allocate an image for labels
509  if ( labelJob.img == nullptr )
510  {
511  labelJob.img = allocateImage( QStringLiteral( "labels" ) );
512  }
513 
514  // Prepare label mask images
515  for ( int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
516  {
517  QImage *maskImage;
518  labelJob.context.setMaskPainter( allocateImageAndPainter( QStringLiteral( "label mask" ), maskImage ), maskId );
519  maskImage->fill( 0 );
520  labelJob.maskImages.push_back( maskImage );
521  }
522  labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
523 
524  // Prepare second pass jobs
525  for ( LayerRenderJob &job : firstPassJobs )
526  {
527  QgsMapLayer *ml = job.layer;
528 
529  auto it = maskedSymbolLayers.find( ml->id() );
530  if ( it == maskedSymbolLayers.end() )
531  continue;
532 
533  QList<MaskSource> &sourceList = it->second;
534  const QSet<QgsSymbolLayerId> &symbolList = it->first;
535 
536  // copy the initial job ...
537  secondPassJobs.append( LayerRenderJob() );
538  LayerRenderJob &job2 = secondPassJobs.last();
539  job2 = job;
540  job2.cached = false;
541  job2.firstPassJob = &job;
542  QgsVectorLayer *vl1 = qobject_cast<QgsVectorLayer *>( job.layer );
543 
544  // ... but clear the image
545  job2.context.setMaskPainter( nullptr );
546  job2.context.setPainter( allocateImageAndPainter( vl1->id(), job2.img ) );
547  if ( ! job2.img )
548  {
549  secondPassJobs.removeLast();
550  continue;
551  }
552 
553  // Points to the first pass job. This will be needed during the second pass composition.
554  for ( MaskSource &source : sourceList )
555  {
556  if ( source.labelMaskId != -1 )
557  job2.maskJobs.push_back( qMakePair( nullptr, source.labelMaskId ) );
558  else
559  job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
560  }
561 
562  // FIXME: another possibility here, to avoid allocating a new map renderer and reuse the one from
563  // the first pass job, would be to be able to call QgsMapLayerRenderer::render() with a QgsRenderContext.
564  QgsVectorLayerRenderer *mapRenderer = static_cast<QgsVectorLayerRenderer *>( vl1->createMapRenderer( job2.context ) );
565  job2.renderer = mapRenderer;
566 
567  // Modify the render context so that symbol layers get disabled as needed.
568  // The map renderer stores a reference to the context, so we can modify it even after the map renderer creation (what we need here)
569  job2.context.setDisabledSymbolLayers( QgsSymbolLayerUtils::toSymbolLayerPointers( mapRenderer->featureRenderer(), symbolList ) );
570  }
571 
572  return secondPassJobs;
573 }
574 
575 LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache )
576 {
577  LabelRenderJob job;
579  job.context.setPainter( painter );
580  job.context.setLabelingEngine( labelingEngine2 );
581  job.context.setExtent( mSettings.visibleExtent() );
582  job.context.setFeatureFilterProvider( mFeatureFilterProvider );
583 
584  // if we can use the cache, let's do it and avoid rendering!
585  bool hasCache = canUseLabelCache && mCache && mCache->hasCacheImage( LABEL_CACHE_ID );
586  if ( hasCache )
587  {
588  job.cached = true;
589  job.complete = true;
590  job.img = new QImage( mCache->cacheImage( LABEL_CACHE_ID ) );
591  Q_ASSERT( job.img->devicePixelRatio() == mSettings.devicePixelRatio() );
592  job.context.setPainter( nullptr );
593  }
594  else
595  {
596  if ( canUseLabelCache && ( mCache || !painter ) )
597  {
598  job.img = allocateImage( QStringLiteral( "labels" ) );
599  }
600  }
601 
602  return job;
603 }
604 
605 
606 void QgsMapRendererJob::cleanupJobs( LayerRenderJobs &jobs )
607 {
608  for ( LayerRenderJobs::iterator it = jobs.begin(); it != jobs.end(); ++it )
609  {
610  LayerRenderJob &job = *it;
611  if ( job.img )
612  {
613  delete job.context.painter();
614  job.context.setPainter( nullptr );
615 
616  if ( mCache && !job.cached && !job.context.renderingStopped() && job.layer )
617  {
618  QgsDebugMsgLevel( QStringLiteral( "caching image for %1" ).arg( job.layerId ), 2 );
619  mCache->setCacheImage( job.layerId, *job.img, QList< QgsMapLayer * >() << job.layer );
620  }
621 
622  delete job.img;
623  job.img = nullptr;
624  }
625 
626  // delete the mask image and painter
627  if ( job.maskImage )
628  {
629  delete job.context.maskPainter();
630  job.context.setMaskPainter( nullptr );
631  delete job.maskImage;
632  }
633 
634  if ( job.renderer )
635  {
636  const auto constErrors = job.renderer->errors();
637  for ( const QString &message : constErrors )
638  mErrors.append( Error( job.renderer->layerId(), message ) );
639 
640  delete job.renderer;
641  job.renderer = nullptr;
642  }
643 
644  if ( job.layer )
645  mPerLayerRenderingTime.insert( job.layer, job.renderingTime );
646  }
647 
648  jobs.clear();
649 }
650 
651 void QgsMapRendererJob::cleanupSecondPassJobs( LayerRenderJobs &jobs )
652 {
653  for ( auto &job : jobs )
654  {
655  if ( job.img )
656  {
657  delete job.context.painter();
658  job.context.setPainter( nullptr );
659 
660  delete job.img;
661  job.img = nullptr;
662  }
663 
664  if ( job.renderer )
665  {
666  delete job.renderer;
667  job.renderer = nullptr;
668  }
669 
670  if ( job.layer )
671  mPerLayerRenderingTime.insert( job.layer, job.renderingTime );
672  }
673 
674  jobs.clear();
675 }
676 
677 void QgsMapRendererJob::cleanupLabelJob( LabelRenderJob &job )
678 {
679  if ( job.img )
680  {
681  if ( mCache && !job.cached && !job.context.renderingStopped() )
682  {
683  QgsDebugMsgLevel( QStringLiteral( "caching label result image" ), 2 );
684  mCache->setCacheImage( LABEL_CACHE_ID, *job.img, _qgis_listQPointerToRaw( job.participatingLayers ) );
685  }
686 
687  delete job.img;
688  job.img = nullptr;
689  }
690 
691  for ( int maskId = 0; maskId < job.maskImages.size(); maskId++ )
692  {
693  delete job.context.maskPainter( maskId );
694  job.context.setMaskPainter( nullptr, maskId );
695  delete job.maskImages[maskId];
696  }
697 }
698 
699 
700 #define DEBUG_RENDERING 0
701 
702 QImage QgsMapRendererJob::composeImage( const QgsMapSettings &settings, const LayerRenderJobs &jobs, const LabelRenderJob &labelJob )
703 {
704  QImage image( settings.deviceOutputSize(), settings.outputImageFormat() );
705  image.setDevicePixelRatio( settings.devicePixelRatio() );
706  image.setDotsPerMeterX( static_cast<int>( settings.outputDpi() * 39.37 ) );
707  image.setDotsPerMeterY( static_cast<int>( settings.outputDpi() * 39.37 ) );
708  image.fill( settings.backgroundColor().rgba() );
709 
710  QPainter painter( &image );
711 
712 #if DEBUG_RENDERING
713  int i = 0;
714 #endif
715  for ( LayerRenderJobs::const_iterator it = jobs.constBegin(); it != jobs.constEnd(); ++it )
716  {
717  const LayerRenderJob &job = *it;
718 
719  if ( job.layer && job.layer->customProperty( QStringLiteral( "rendering/renderAboveLabels" ) ).toBool() )
720  continue; // skip layer for now, it will be rendered after labels
721 
722  if ( !job.imageInitialized )
723  continue; // img not safe to compose
724 
725  painter.setCompositionMode( job.blendMode );
726  painter.setOpacity( job.opacity );
727 
728 #if DEBUG_RENDERING
729  job.img->save( QString( "/tmp/final_%1.png" ).arg( i ) );
730  i++;
731 #endif
732  Q_ASSERT( job.img );
733 
734  painter.drawImage( 0, 0, *job.img );
735  }
736 
737  // IMPORTANT - don't draw labelJob img before the label job is complete,
738  // as the image is uninitialized and full of garbage before the label job
739  // commences
740  if ( labelJob.img && labelJob.complete )
741  {
742  painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
743  painter.setOpacity( 1.0 );
744  painter.drawImage( 0, 0, *labelJob.img );
745  }
746 
747  // render any layers with the renderAboveLabels flag now
748  for ( LayerRenderJobs::const_iterator it = jobs.constBegin(); it != jobs.constEnd(); ++it )
749  {
750  const LayerRenderJob &job = *it;
751 
752  if ( !job.layer || !job.layer->customProperty( QStringLiteral( "rendering/renderAboveLabels" ) ).toBool() )
753  continue;
754 
755  if ( !job.imageInitialized )
756  continue; // img not safe to compose
757 
758  painter.setCompositionMode( job.blendMode );
759  painter.setOpacity( job.opacity );
760 
761  Q_ASSERT( job.img );
762 
763  painter.drawImage( 0, 0, *job.img );
764  }
765 
766  painter.end();
767 #if DEBUG_RENDERING
768  image.save( "/tmp/final.png" );
769 #endif
770  return image;
771 }
772 
773 void QgsMapRendererJob::composeSecondPass( LayerRenderJobs &secondPassJobs, LabelRenderJob &labelJob )
774 {
775 #if DEBUG_RENDERING
776  int i = 0;
777 #endif
778  // compose the second pass with the mask
779  for ( LayerRenderJob &job : secondPassJobs )
780  {
781 #if DEBUG_RENDERING
782  i++;
783  job.img->save( QString( "/tmp/second_%1.png" ).arg( i ) );
784  int mask = 0;
785 #endif
786 
787  // Merge all mask images into the first one if we have more than one mask image
788  if ( job.maskJobs.size() > 1 )
789  {
790  QPainter *maskPainter = nullptr;
791  for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
792  {
793  QImage *maskImage = p.first ? p.first->maskImage : labelJob.maskImages[p.second];
794 #if DEBUG_RENDERING
795  maskImage->save( QString( "/tmp/mask_%1_%2.png" ).arg( i ).arg( mask++ ) );
796 #endif
797  if ( ! maskPainter )
798  {
799  maskPainter = p.first ? p.first->context.maskPainter() : labelJob.context.maskPainter( p.second );
800  }
801  else
802  {
803  maskPainter->drawImage( 0, 0, *maskImage );
804  }
805  }
806  }
807 
808  if ( ! job.maskJobs.isEmpty() )
809  {
810  // All have been merged into the first
811  QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
812  QImage *maskImage = p.first ? p.first->maskImage : labelJob.maskImages[p.second];
813 #if DEBUG_RENDERING
814  maskImage->save( QString( "/tmp/mask_%1.png" ).arg( i ) );
815 #endif
816 
817  // Only retain parts of the second rendering that are "inside" the mask image
818  QPainter *painter = job.context.painter();
819  painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
820 
821  //Create an "alpha binarized" image of the maskImage to :
822  //* Eliminate antialiasing artefact
823  //* Avoid applying mask opacity to elements under the mask but not masked
824  QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
825  QVector<QRgb> mswTable;
826  mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
827  mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
828  maskBinAlpha.setColorTable( mswTable );
829  painter->drawImage( 0, 0, maskBinAlpha );
830 #if DEBUG_RENDERING
831  job.img->save( QString( "/tmp/second_%1_a.png" ).arg( i ) );
832 #endif
833 
834  // Modify the first pass' image ...
835  {
836  QPainter tempPainter;
837 
838  // reuse the first pass painter, if available
839  QPainter *painter1 = job.firstPassJob->context.painter();
840  if ( ! painter1 )
841  {
842  tempPainter.begin( job.firstPassJob->img );
843  painter1 = &tempPainter;
844  }
845 #if DEBUG_RENDERING
846  job.firstPassJob->img->save( QString( "/tmp/second_%1_first_pass_1.png" ).arg( i ) );
847 #endif
848  // ... first retain parts that are "outside" the mask image
849  painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
850  painter1->drawImage( 0, 0, *maskImage );
851 
852 #if DEBUG_RENDERING
853  job.firstPassJob->img->save( QString( "/tmp/second_%1_first_pass_2.png" ).arg( i ) );
854 #endif
855  // ... and overpaint the second pass' image on it
856  painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
857  painter1->drawImage( 0, 0, *job.img );
858 #if DEBUG_RENDERING
859  job.img->save( QString( "/tmp/second_%1_b.png" ).arg( i ) );
860  if ( job.firstPassJob )
861  job.firstPassJob->img->save( QString( "/tmp/second_%1_first_pass_3.png" ).arg( i ) );
862 #endif
863  }
864  }
865  }
866 }
867 
868 void QgsMapRendererJob::logRenderingTime( const LayerRenderJobs &jobs, const LayerRenderJobs &secondPassJobs, const LabelRenderJob &labelJob )
869 {
870  QgsSettings settings;
871  if ( !settings.value( QStringLiteral( "Map/logCanvasRefreshEvent" ), false ).toBool() )
872  return;
873 
874  QMultiMap<int, QString> elapsed;
875  const auto constJobs = jobs;
876  for ( const LayerRenderJob &job : constJobs )
877  elapsed.insert( job.renderingTime, job.layerId );
878  const auto constSecondPassJobs = secondPassJobs;
879  for ( const LayerRenderJob &job : constSecondPassJobs )
880  elapsed.insert( job.renderingTime, job.layerId + QString( " (second pass)" ) );
881 
882  elapsed.insert( labelJob.renderingTime, tr( "Labeling" ) );
883 
884  QList<int> tt( elapsed.uniqueKeys() );
885  std::sort( tt.begin(), tt.end(), std::greater<int>() );
886  const auto constTt = tt;
887  for ( int t : constTt )
888  {
889  QgsMessageLog::logMessage( tr( "%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QStringLiteral( ", " ) ) ), tr( "Rendering" ) );
890  }
891  QgsMessageLog::logMessage( QStringLiteral( "---" ), tr( "Rendering" ) );
892 }
893 
894 bool QgsMapRendererJob::needTemporaryImage( QgsMapLayer *ml )
895 {
896  switch ( ml->type() )
897  {
899  {
900  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
901  if ( vl->renderer() && vl->renderer()->forceRasterRender() )
902  {
903  //raster rendering is forced for this layer
904  return true;
905  }
907  ( ( vl->blendMode() != QPainter::CompositionMode_SourceOver )
908  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
909  || ( !qgsDoubleNear( vl->opacity(), 1.0 ) ) ) )
910  {
911  //layer properties require rasterization
912  return true;
913  }
914  break;
915  }
917  {
918  // preview of intermediate raster rendering results requires a temporary output image
920  return true;
921  break;
922  }
923 
927  break;
928  }
929 
930  return false;
931 }
932 
933 void QgsMapRendererJob::drawLabeling( QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter )
934 {
935  QgsDebugMsgLevel( QStringLiteral( "Draw labeling start" ), 5 );
936 
937  QElapsedTimer t;
938  t.start();
939 
940  // Reset the composition mode before rendering the labels
941  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
942 
943  renderContext.setPainter( painter );
944 
945  if ( labelingEngine2 )
946  {
947  labelingEngine2->run( renderContext );
948  }
949 
950  QgsDebugMsgLevel( QStringLiteral( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
951 }
952 
953 void QgsMapRendererJob::drawLabeling( const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter )
954 {
955  Q_UNUSED( settings )
956 
957  drawLabeling( renderContext, labelingEngine2, painter );
958 }
959 
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:88
qgsexpressioncontextutils.h
QgsRectangle::isFinite
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Definition: qgsrectangle.h:527
QgsVectorLayer::featureBlendMode
QPainter::CompositionMode featureBlendMode() const
Returns the current blending mode for features.
Definition: qgsvectorlayer.cpp:4319
qgspallabeling.h
QgsMapRendererJob::logRenderingTime
void logRenderingTime(const LayerRenderJobs &jobs, const LayerRenderJobs &secondPassJobs, const LabelRenderJob &labelJob)
qgsmaplayerstylemanager.h
QgsMapRendererJob::cleanupJobs
void cleanupJobs(LayerRenderJobs &jobs)
QgsMapRendererJob::mCache
QgsMapRendererCache * mCache
Definition: qgsmaprendererjob.h:319
QgsMapRendererJob::mapSettings
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsMapLayerType::MeshLayer
@ MeshLayer
Added in 3.2.
QgsMapSettings::outputSize
QSize outputSize() const
Returns the size of the resulting map image.
Definition: qgsmapsettings.cpp:234
QgsMapRendererJob::mErrors
Errors mErrors
Definition: qgsmaprendererjob.h:317
QgsMapSettings::layerTransform
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
Definition: qgsmapsettings.cpp:418
QgsRenderContext::fromMapSettings
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Definition: qgsrendercontext.cpp:170
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsMapSettings::extentBuffer
double extentBuffer() const
Returns the buffer in map units to use around the visible extent for rendering symbols whose correspo...
Definition: qgsmapsettings.cpp:91
QgsMapRendererJob::LABEL_CACHE_ID
static const QString LABEL_CACHE_ID
QgsMapRendererCache ID string for cached label image.
Definition: qgsmaprendererjob.h:297
QgsMapRendererJob::QgsMapRendererJob
QgsMapRendererJob(const QgsMapSettings &settings)
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsMapLayer::isValid
bool isValid() const
Returns the status of the layer.
Definition: qgsmaplayer.cpp:656
QgsMapLayer::blendMode
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
Definition: qgsmaplayer.cpp:212
QgsTemporalRangeObject::isTemporal
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
Definition: qgstemporalrangeobject.cpp:30
QgsMapSettings::devicePixelRatio
float devicePixelRatio() const
Returns device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" di...
Definition: qgsmapsettings.cpp:246
QgsMapRendererCache::hasCacheImage
bool hasCacheImage(const QString &cacheKey) const
Returns true if the cache contains an image with the specified cacheKey.
Definition: qgsmaprenderercache.cpp:128
qgsvectorlayerrenderer.h
QgsRenderContext::setPainter
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Definition: qgsrendercontext.h:475
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
qgssymbollayerutils.h
QgsSymbolLayerReference
Definition: qgssymbollayerreference.h:117
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:264
QgsTemporalRangeObject::temporalRange
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Definition: qgstemporalrangeobject.cpp:43
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsMapRendererJob::prepareLabelCache
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
QgsMapSettings::layerStyleOverrides
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
Definition: qgsmapsettings.cpp:299
QgsSettings
Definition: qgssettings.h:61
QgsMapRendererJob::prepareJobs
LayerRenderJobs prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet=false)
Creates a list of layer rendering jobs and prepares them for later render.
QgsMapSettings::outputImageFormat
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
Definition: qgsmapsettings.h:358
QgsMapSettings::RenderPartialOutput
@ RenderPartialOutput
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
Definition: qgsmapsettings.h:311
QgsVectorLayerUtils::symbolLayerMasks
static QHash< QString, QSet< QgsSymbolLayerId > > symbolLayerMasks(const QgsVectorLayer *)
Returns all masks that may be defined on symbol layers for a given vector layer.
Definition: qgsvectorlayerutils.cpp:914
QgsMapRendererJob::mPerLayerRenderingTime
QHash< QgsWeakMapLayerPointer, int > mPerLayerRenderingTime
Render time (in ms) per layer, by layer ID.
Definition: qgsmaprendererjob.h:324
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsMapRendererCache::init
bool init(const QgsRectangle &extent, double scale)
Initialize cache: set new parameters and clears the cache if any parameters have changed since last i...
Definition: qgsmaprenderercache.cpp:84
qgsmaprenderercache.h
QgsMapRendererCache::cacheImage
QImage cacheImage(const QString &cacheKey) const
Returns the cached image for the specified cacheKey.
Definition: qgsmaprenderercache.cpp:133
QgsCoordinateTransform::transform
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:239
QgsCoordinateTransform::isValid
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Definition: qgscoordinatetransform.cpp:876
QgsMapLayerStyleOverride
Definition: qgsmaplayerstyle.h:81
QgsPalLabeling::staticWillUseLayer
static bool staticWillUseLayer(QgsVectorLayer *layer)
called to find out whether the layer is used for labeling
Definition: qgspallabeling.cpp:3536
QgsRectangle
Definition: qgsrectangle.h:41
QgsMapRendererCache
Definition: qgsmaprenderercache.h:42
QgsCoordinateTransform::ReverseTransform
@ ReverseTransform
Transform from destination to source CRS.
Definition: qgscoordinatetransform.h:61
QgsMapRendererJob::cleanupSecondPassJobs
void cleanupSecondPassJobs(LayerRenderJobs &jobs)
QgsMapRendererJob::prepareLabelingJob
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
QgsVectorLayerUtils::labelMasks
static QHash< QString, QHash< QString, QSet< QgsSymbolLayerId > > > labelMasks(const QgsVectorLayer *)
Returns masks defined in labeling options of a layer.
Definition: qgsvectorlayerutils.cpp:871
QgsCoordinateTransform::transformBoundingBox
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:511
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3586
QgsMapSettings::UseAdvancedEffects
@ UseAdvancedEffects
Enable layer opacity and blending effects.
Definition: qgsmapsettings.h:305
QgsCoordinateTransform::destinationCrs
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Definition: qgscoordinatetransform.cpp:234
QgsMapRendererJob::errors
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
QgsMapLayer::isInScaleRange
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
Definition: qgsmaplayer.cpp:669
QgsCoordinateReferenceSystem::isGeographic
bool isGeographic
Definition: qgscoordinatereferencesystem.h:211
qgsmaplayertemporalproperties.h
QgsCsException
Definition: qgsexception.h:65
QgsMapRendererJob::perLayerRenderingTime
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
QgsCoordinateTransform::setBallparkTransformsAreAppropriate
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Definition: qgscoordinatetransform.cpp:919
QgsPointXY::toString
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Definition: qgspointxy.cpp:51
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsMapRendererJob::setCache
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
QgsVectorLayer::labeling
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
Definition: qgsvectorlayer.h:1598
qgsmaplayer.h
QgsMapLayerType::RasterLayer
@ RasterLayer
QgsMapSettings::Antialiasing
@ Antialiasing
Enable anti-aliasing for map rendering.
Definition: qgsmapsettings.h:302
QgsMapRendererJob::composeImage
static QImage composeImage(const QgsMapSettings &settings, const LayerRenderJobs &jobs, const LabelRenderJob &labelJob)
QgsMapSettings::backgroundColor
QColor backgroundColor() const
Gets the background color of the map.
Definition: qgsmapsettings.h:292
QgsMapLayer::temporalProperties
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1194
QgsMapLayerTemporalProperties::isVisibleInTemporalRange
virtual bool isVisibleInTemporalRange(const QgsDateTimeRange &range) const
Returns true if the layer should be visible and rendered for the specified time range.
Definition: qgsmaplayertemporalproperties.cpp:25
qgsrendercontext.h
QgsLabelingEngine::run
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
qgsvectorlayerutils.h
qgsmaprendererjob.h
QgsMapRendererCache::dependentLayers
QList< QgsMapLayer * > dependentLayers(const QString &cacheKey) const
Returns a list of map layers on which an image in the cache depends.
Definition: qgsmaprenderercache.cpp:139
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
qgsmaplayerrenderer.h
QgsMapRendererJob::cleanupLabelJob
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
qgssymbollayer.h
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsMapSettings::scale
double scale() const
Returns the calculated map scale.
Definition: qgsmapsettings.cpp:395
QgsMapLayer::hasScaleBasedVisibility
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
Definition: qgsmaplayer.cpp:676
qgsrenderer.h
QgsMapLayer::minimumScale
double minimumScale() const
Returns the minimum map scale (i.e.
Definition: qgsmaplayer.cpp:741
QgsMapLayer::maximumScale
double maximumScale() const
Returns the maximum map scale (i.e.
Definition: qgsmaplayer.cpp:725
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsCoordinateTransform::ForwardTransform
@ ForwardTransform
Transform from source to destination CRS.
Definition: qgscoordinatetransform.h:60
QgsLabelingEngine
The QgsLabelingEngine class provides map labeling functionality. The input for the engine is a list o...
Definition: qgslabelingengine.h:214
QgsRectangle::toString
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Definition: qgsrectangle.cpp:127
qgsvectorlayer.h
QgsPointXY
Definition: qgspointxy.h:43
QgsFeatureRenderer::forceRasterRender
bool forceRasterRender() const
Returns whether the renderer must render as a raster.
Definition: qgsrenderer.h:412
QgsMapSettings::deviceOutputSize
QSize deviceOutputSize() const
Returns the device output size of the map canvas This is equivalent to the output size multiplicated ...
Definition: qgsmapsettings.cpp:257
QgsMapSettings::testFlag
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Definition: qgsmapsettings.cpp:354
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsMapRendererCache::setCacheImage
void setCacheImage(const QString &cacheKey, const QImage &image, const QList< QgsMapLayer * > &dependentLayers=QList< QgsMapLayer * >())
Set the cached image for a particular cacheKey.
Definition: qgsmaprenderercache.cpp:102
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsMapRendererJob::mSettings
QgsMapSettings mSettings
Definition: qgsmaprendererjob.h:315
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsVectorLayerRenderer::featureRenderer
QgsFeatureRenderer * featureRenderer()
Returns the feature renderer.
Definition: qgsvectorlayerrenderer.h:86
qgssettings.h
QgsMapRendererJob::composeSecondPass
static void composeSecondPass(LayerRenderJobs &secondPassJobs, LabelRenderJob &labelJob)
Compose second pass images into first pass images.
QgsRectangle::height
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Added in 3.14.
QgsMapRendererJob
Definition: qgsmaprendererjob.h:186
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:85
QgsMapRendererCache::clearCacheImage
void clearCacheImage(const QString &cacheKey)
Removes an image from the cache with matching cacheKey.
Definition: qgsmaprenderercache.cpp:177
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsMapSettings::layers
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
Definition: qgsmapsettings.cpp:281
QgsAbstractVectorLayerLabeling::requiresAdvancedEffects
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
qgsexception.h
qgsvectorlayerlabeling.h
QgsVectorLayer::opacity
double opacity
Definition: qgsvectorlayer.h:394
QgsMapRendererJob::Errors
QList< QgsMapRendererJob::Error > Errors
Definition: qgsmaprendererjob.h:261
QgsRectangle::grow
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:275
qgsmaplayerlistutils.h
qgslogger.h
QgsMapSettings::outputDpi
double outputDpi() const
Returns DPI used for conversion between real world units (e.g.
Definition: qgsmapsettings.cpp:262
QgsMapRendererQImageJob::QgsMapRendererQImageJob
QgsMapRendererQImageJob(const QgsMapSettings &settings)
QgsMapRendererJob::prepareSecondPassJobs
LayerRenderJobs prepareSecondPassJobs(LayerRenderJobs &firstPassJobs, LabelRenderJob &labelJob)
Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
QgsMapSettings
Definition: qgsmapsettings.h:86
QgsMapSettings::visibleExtent
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
Definition: qgsmapsettings.cpp:370
QgsCoordinateTransform
Definition: qgscoordinatetransform.h:52
QgsVectorLayer::createMapRenderer
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) FINAL
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
Definition: qgsvectorlayer.cpp:387
QgsMapLayer::type
QgsMapLayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:129
qgssymbol.h
qgsproject.h
QgsRectangle::width
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsMapLayerType::PluginLayer
@ PluginLayer
symbolLayerMasks
QList< QPair< QgsSymbolLayerId, QList< QgsSymbolLayerReference > > > symbolLayerMasks(const QgsVectorLayer *layer)
Symbol layer masks collector.
Definition: qgsmaskingwidget.cpp:150
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsVectorLayer::labelsEnabled
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
Definition: qgsvectorlayer.cpp:728
qgslabelingengine.h
QgsVectorLayerRenderer
Definition: qgsvectorlayerrenderer.h:74
QgsMapLayer::createMapRenderer
virtual QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext)=0
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
qgsmessagelog.h
QgsSymbolLayerUtils::toSymbolLayerPointers
static QSet< const QgsSymbolLayer * > toSymbolLayerPointers(QgsFeatureRenderer *renderer, const QSet< QgsSymbolLayerId > &symbolLayerIds)
Converts a set of symbol layer id to a set of pointers to actual symbol layers carried by the feature...
Definition: qgssymbollayerutils.cpp:4417
QgsVectorLayer::renderer
QgsFeatureRenderer * renderer()
Returns renderer.
Definition: qgsvectorlayer.h:881
QgsMapRendererJob::drawLabeling
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)