LCOV - code coverage report
Current view: top level - core/vector - qgsvectorlayerrenderer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 444 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsvectorlayerrenderer.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 "qgsvectorlayerrenderer.h"
      17                 :            : 
      18                 :            : #include "diagram/qgsdiagram.h"
      19                 :            : 
      20                 :            : #include "qgsdiagramrenderer.h"
      21                 :            : #include "qgsmessagelog.h"
      22                 :            : #include "qgspallabeling.h"
      23                 :            : #include "qgsrenderer.h"
      24                 :            : #include "qgsrendercontext.h"
      25                 :            : #include "qgssinglesymbolrenderer.h"
      26                 :            : #include "qgssymbollayer.h"
      27                 :            : #include "qgssymbollayerutils.h"
      28                 :            : #include "qgssymbol.h"
      29                 :            : #include "qgsvectorlayer.h"
      30                 :            : #include "qgsvectorlayerdiagramprovider.h"
      31                 :            : #include "qgsvectorlayerfeatureiterator.h"
      32                 :            : #include "qgsvectorlayerlabeling.h"
      33                 :            : #include "qgsvectorlayerlabelprovider.h"
      34                 :            : #include "qgspainteffect.h"
      35                 :            : #include "qgsfeaturefilterprovider.h"
      36                 :            : #include "qgsexception.h"
      37                 :            : #include "qgslogger.h"
      38                 :            : #include "qgssettings.h"
      39                 :            : #include "qgsexpressioncontextutils.h"
      40                 :            : #include "qgsrenderedfeaturehandlerinterface.h"
      41                 :            : #include "qgsvectorlayertemporalproperties.h"
      42                 :            : #include "qgsmapclippingutils.h"
      43                 :            : #include "qgsfeaturerenderergenerator.h"
      44                 :            : 
      45                 :            : #include <QPicture>
      46                 :            : #include <QTimer>
      47                 :            : 
      48                 :          0 : QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer *layer, QgsRenderContext &context )
      49                 :          0 :   : QgsMapLayerRenderer( layer->id(), &context )
      50                 :          0 :   , mLayer( layer )
      51                 :          0 :   , mFields( layer->fields() )
      52                 :          0 :   , mLabeling( false )
      53                 :          0 :   , mDiagrams( false )
      54                 :          0 : {
      55                 :          0 :   mSource = std::make_unique< QgsVectorLayerFeatureSource >( layer );
      56                 :            : 
      57                 :          0 :   std::unique_ptr< QgsFeatureRenderer > mainRenderer( layer->renderer() ? layer->renderer()->clone() : nullptr );
      58                 :            : 
      59                 :          0 :   if ( !mainRenderer )
      60                 :          0 :     return;
      61                 :            : 
      62                 :          0 :   QList< const QgsFeatureRendererGenerator * > generators = layer->featureRendererGenerators();
      63                 :          0 :   std::sort( generators.begin(), generators.end(), []( const QgsFeatureRendererGenerator * g1, const QgsFeatureRendererGenerator * g2 )
      64                 :            :   {
      65                 :          0 :     return g1->level() < g2->level();
      66                 :            :   } );
      67                 :            : 
      68                 :          0 :   bool insertedMainRenderer = false;
      69                 :          0 :   double prevLevel = std::numeric_limits< double >::lowest();
      70                 :          0 :   mRenderer = mainRenderer.get();
      71                 :          0 :   for ( const QgsFeatureRendererGenerator *generator : std::as_const( generators ) )
      72                 :            :   {
      73                 :          0 :     if ( generator->level() >= 0 && prevLevel < 0 && !insertedMainRenderer )
      74                 :            :     {
      75                 :            :       // insert main renderer when level changes from <0 to >0
      76                 :          0 :       mRenderers.emplace_back( std::move( mainRenderer ) );
      77                 :          0 :       insertedMainRenderer = true;
      78                 :          0 :     }
      79                 :          0 :     mRenderers.emplace_back( generator->createRenderer() );
      80                 :          0 :     prevLevel = generator->level();
      81                 :            :   }
      82                 :            :   // cppcheck-suppress accessMoved
      83                 :          0 :   if ( mainRenderer )
      84                 :            :   {
      85                 :            :     // cppcheck-suppress accessMoved
      86                 :          0 :     mRenderers.emplace_back( std::move( mainRenderer ) );
      87                 :          0 :   }
      88                 :            : 
      89                 :          0 :   mSelectedFeatureIds = layer->selectedFeatureIds();
      90                 :            : 
      91                 :          0 :   mDrawVertexMarkers = nullptr != layer->editBuffer();
      92                 :            : 
      93                 :          0 :   mGeometryType = layer->geometryType();
      94                 :            : 
      95                 :          0 :   mFeatureBlendMode = layer->featureBlendMode();
      96                 :            : 
      97                 :          0 :   if ( context.isTemporal() )
      98                 :            :   {
      99                 :          0 :     QgsVectorLayerTemporalContext temporalContext;
     100                 :          0 :     temporalContext.setLayer( layer );
     101                 :          0 :     mTemporalFilter = qobject_cast< const QgsVectorLayerTemporalProperties * >( layer->temporalProperties() )->createFilterString( temporalContext, context.temporalRange() );
     102                 :          0 :   }
     103                 :            : 
     104                 :            :   // if there's already a simplification method specified via the context, we respect that. Otherwise, we fall back
     105                 :            :   // to the layer's individual setting
     106                 :          0 :   if ( renderContext()->vectorSimplifyMethod().simplifyHints() != QgsVectorSimplifyMethod::NoSimplification )
     107                 :            :   {
     108                 :          0 :     mSimplifyMethod = renderContext()->vectorSimplifyMethod();
     109                 :          0 :     mSimplifyGeometry = renderContext()->vectorSimplifyMethod().simplifyHints() & QgsVectorSimplifyMethod::GeometrySimplification ||
     110                 :          0 :                         renderContext()->vectorSimplifyMethod().simplifyHints() & QgsVectorSimplifyMethod::FullSimplification;
     111                 :          0 :   }
     112                 :            :   else
     113                 :            :   {
     114                 :          0 :     mSimplifyMethod = layer->simplifyMethod();
     115                 :          0 :     mSimplifyGeometry = layer->simplifyDrawingCanbeApplied( *renderContext(), QgsVectorSimplifyMethod::GeometrySimplification );
     116                 :            :   }
     117                 :            : 
     118                 :          0 :   QgsSettings settings;
     119                 :          0 :   mVertexMarkerOnlyForSelection = settings.value( QStringLiteral( "qgis/digitizing/marker_only_for_selected" ), true ).toBool();
     120                 :            : 
     121                 :          0 :   QString markerTypeString = settings.value( QStringLiteral( "qgis/digitizing/marker_style" ), "Cross" ).toString();
     122                 :          0 :   if ( markerTypeString == QLatin1String( "Cross" ) )
     123                 :            :   {
     124                 :          0 :     mVertexMarkerStyle = QgsSymbolLayerUtils::Cross;
     125                 :          0 :   }
     126                 :          0 :   else if ( markerTypeString == QLatin1String( "SemiTransparentCircle" ) )
     127                 :            :   {
     128                 :          0 :     mVertexMarkerStyle = QgsSymbolLayerUtils::SemiTransparentCircle;
     129                 :          0 :   }
     130                 :            :   else
     131                 :            :   {
     132                 :          0 :     mVertexMarkerStyle = QgsSymbolLayerUtils::NoMarker;
     133                 :            :   }
     134                 :            : 
     135                 :          0 :   mVertexMarkerSize = settings.value( QStringLiteral( "qgis/digitizing/marker_size_mm" ), 2.0 ).toDouble();
     136                 :            : 
     137                 :          0 :   QgsDebugMsgLevel( "rendering v2:\n  " + mRenderer->dump(), 2 );
     138                 :            : 
     139                 :          0 :   if ( mDrawVertexMarkers )
     140                 :          0 :   {
     141                 :          0 :     // set editing vertex markers style (main renderer only)
     142                 :          0 :     mRenderer->setVertexMarkerAppearance( mVertexMarkerStyle, mVertexMarkerSize );
     143                 :          0 :   }
     144                 :          0 :   renderContext()->expressionContext() << QgsExpressionContextUtils::layerScope( layer );
     145                 :            : 
     146                 :          0 :   for ( const std::unique_ptr< QgsFeatureRenderer > &renderer : mRenderers )
     147                 :            :   {
     148                 :          0 :     mAttrNames.unite( renderer->usedAttributes( context ) );
     149                 :            :   }
     150                 :          0 :   if ( context.hasRenderedFeatureHandlers() )
     151                 :            :   {
     152                 :          0 :     const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
     153                 :          0 :     for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
     154                 :          0 :       mAttrNames.unite( handler->usedAttributes( layer, context ) );
     155                 :          0 :   }
     156                 :          0 : 
     157                 :            :   //register label and diagram layer to the labeling engine
     158                 :          0 :   prepareLabeling( layer, mAttrNames );
     159                 :          0 :   prepareDiagrams( layer, mAttrNames );
     160                 :            : 
     161                 :          0 :   mClippingRegions = QgsMapClippingUtils::collectClippingRegionsForLayer( context, layer );
     162                 :          0 : 
     163                 :          0 :   for ( const std::unique_ptr< QgsFeatureRenderer > &renderer : mRenderers )
     164                 :            :   {
     165                 :          0 :     if ( renderer->forceRasterRender() )
     166                 :            :     {
     167                 :            :       //raster rendering is forced for this layer
     168                 :          0 :       mForceRasterRender = true;
     169                 :          0 :       break;
     170                 :            :     }
     171                 :          0 :   }
     172                 :            : 
     173                 :          0 :   if ( context.testFlag( QgsRenderContext::UseAdvancedEffects ) &&
     174                 :          0 :        ( ( layer->blendMode() != QPainter::CompositionMode_SourceOver )
     175                 :          0 :          || ( layer->featureBlendMode() != QPainter::CompositionMode_SourceOver )
     176                 :          0 :          || ( !qgsDoubleNear( layer->opacity(), 1.0 ) ) ) )
     177                 :            :   {
     178                 :          0 :     //layer properties require rasterization
     179                 :          0 :     mForceRasterRender = true;
     180                 :          0 :   }
     181                 :            : 
     182                 :          0 :   mReadyToCompose = false;
     183                 :          0 : }
     184                 :            : 
     185                 :          0 : QgsVectorLayerRenderer::~QgsVectorLayerRenderer() = default;
     186                 :            : 
     187                 :          0 : void QgsVectorLayerRenderer::setLayerRenderingTimeHint( int time )
     188                 :            : {
     189                 :          0 :   mRenderTimeHint = time;
     190                 :          0 : }
     191                 :            : 
     192                 :          0 : QgsFeedback *QgsVectorLayerRenderer::feedback() const
     193                 :            : {
     194                 :          0 :   return mInterruptionChecker.get();
     195                 :            : }
     196                 :            : 
     197                 :          0 : bool QgsVectorLayerRenderer::forceRasterRender() const
     198                 :            : {
     199                 :          0 :   return mForceRasterRender;
     200                 :            : }
     201                 :            : 
     202                 :          0 : bool QgsVectorLayerRenderer::render()
     203                 :            : {
     204                 :          0 :   if ( mGeometryType == QgsWkbTypes::NullGeometry || mGeometryType == QgsWkbTypes::UnknownGeometry )
     205                 :            :   {
     206                 :          0 :     mReadyToCompose = true;
     207                 :          0 :     return true;
     208                 :            :   }
     209                 :            : 
     210                 :          0 :   if ( mRenderers.empty() )
     211                 :            :   {
     212                 :          0 :     mReadyToCompose = true;
     213                 :          0 :     mErrors.append( QObject::tr( "No renderer for drawing." ) );
     214                 :          0 :     return false;
     215                 :            :   }
     216                 :            : 
     217                 :            :   // if the previous layer render was relatively quick (e.g. less than 3 seconds), the we show any previously
     218                 :            :   // cached version of the layer during rendering instead of the usual progressive updates
     219                 :          0 :   if ( mRenderTimeHint > 0 && mRenderTimeHint <= MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
     220                 :            :   {
     221                 :          0 :     mBlockRenderUpdates = true;
     222                 :          0 :     mElapsedTimer.start();
     223                 :          0 :   }
     224                 :            : 
     225                 :          0 :   bool res = true;
     226                 :          0 :   for ( const std::unique_ptr< QgsFeatureRenderer > &renderer : mRenderers )
     227                 :            :   {
     228                 :          0 :     res = renderInternal( renderer.get() ) && res;
     229                 :            :   }
     230                 :            : 
     231                 :          0 :   mReadyToCompose = true;
     232                 :          0 :   return res && !renderContext()->renderingStopped();
     233                 :          0 : }
     234                 :            : 
     235                 :          0 : bool QgsVectorLayerRenderer::renderInternal( QgsFeatureRenderer *renderer )
     236                 :            : {
     237                 :          0 :   const bool isMainRenderer = renderer == mRenderer;
     238                 :            : 
     239                 :          0 :   if ( renderer->type() == QLatin1String( "nullSymbol" ) )
     240                 :            :   {
     241                 :            :     // a little shortcut for the null symbol renderer - most of the time it is not going to render anything
     242                 :            :     // so we can even skip the whole loop to fetch features
     243                 :          0 :     if ( !isMainRenderer ||
     244                 :          0 :          ( !mDrawVertexMarkers && !mLabelProvider && !mDiagramProvider && mSelectedFeatureIds.isEmpty() ) )
     245                 :          0 :       return true;
     246                 :          0 :   }
     247                 :            : 
     248                 :          0 :   QgsRenderContext &context = *renderContext();
     249                 :            : 
     250                 :          0 :   QgsScopedQPainterState painterState( context.painter() );
     251                 :            : 
     252                 :            :   // MUST be created in the thread doing the rendering
     253                 :          0 :   mInterruptionChecker = std::make_unique< QgsVectorLayerRendererInterruptionChecker >( context );
     254                 :          0 :   bool usingEffect = false;
     255                 :          0 :   if ( renderer->paintEffect() && renderer->paintEffect()->enabled() )
     256                 :            :   {
     257                 :          0 :     usingEffect = true;
     258                 :          0 :     renderer->paintEffect()->begin( context );
     259                 :          0 :   }
     260                 :            : 
     261                 :            :   // Per feature blending mode
     262                 :          0 :   if ( context.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver )
     263                 :            :   {
     264                 :            :     // set the painter to the feature blend mode, so that features drawn
     265                 :            :     // on this layer will interact and blend with each other
     266                 :          0 :     context.painter()->setCompositionMode( mFeatureBlendMode );
     267                 :          0 :   }
     268                 :            : 
     269                 :          0 :   renderer->startRender( context, mFields );
     270                 :            : 
     271                 :          0 :   QString rendererFilter = renderer->filter( mFields );
     272                 :            : 
     273                 :          0 :   QgsRectangle requestExtent = context.extent();
     274                 :          0 :   if ( !mClippingRegions.empty() )
     275                 :            :   {
     276                 :          0 :     mClipFilterGeom = QgsMapClippingUtils::calculateFeatureRequestGeometry( mClippingRegions, context, mApplyClipFilter );
     277                 :          0 :     requestExtent = requestExtent.intersect( mClipFilterGeom.boundingBox() );
     278                 :            : 
     279                 :          0 :     mClipFeatureGeom = QgsMapClippingUtils::calculateFeatureIntersectionGeometry( mClippingRegions, context, mApplyClipGeometries );
     280                 :            : 
     281                 :          0 :     bool needsPainterClipPath = false;
     282                 :          0 :     const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, context, QgsMapLayerType::VectorLayer, needsPainterClipPath );
     283                 :          0 :     if ( needsPainterClipPath )
     284                 :          0 :       context.painter()->setClipPath( path, Qt::IntersectClip );
     285                 :            : 
     286                 :          0 :     mLabelClipFeatureGeom = QgsMapClippingUtils::calculateLabelIntersectionGeometry( mClippingRegions, context, mApplyLabelClipGeometries );
     287                 :            : 
     288                 :          0 :     if ( mDiagramProvider )
     289                 :          0 :       mDiagramProvider->setClipFeatureGeometry( mLabelClipFeatureGeom );
     290                 :          0 :   }
     291                 :          0 :   renderer->modifyRequestExtent( requestExtent, context );
     292                 :            : 
     293                 :          0 :   QgsFeatureRequest featureRequest = QgsFeatureRequest()
     294                 :          0 :                                      .setFilterRect( requestExtent )
     295                 :          0 :                                      .setSubsetOfAttributes( mAttrNames, mFields )
     296                 :          0 :                                      .setExpressionContext( context.expressionContext() );
     297                 :          0 :   if ( renderer->orderByEnabled() )
     298                 :            :   {
     299                 :          0 :     featureRequest.setOrderBy( renderer->orderBy() );
     300                 :          0 :   }
     301                 :            : 
     302                 :          0 :   const QgsFeatureFilterProvider *featureFilterProvider = context.featureFilterProvider();
     303                 :          0 :   if ( featureFilterProvider )
     304                 :            :   {
     305                 :          0 :     featureFilterProvider->filterFeatures( mLayer, featureRequest );
     306                 :          0 :   }
     307                 :          0 :   if ( !rendererFilter.isEmpty() && rendererFilter != QLatin1String( "TRUE" ) )
     308                 :            :   {
     309                 :          0 :     featureRequest.combineFilterExpression( rendererFilter );
     310                 :          0 :   }
     311                 :          0 :   if ( !mTemporalFilter.isEmpty() )
     312                 :            :   {
     313                 :          0 :     featureRequest.combineFilterExpression( mTemporalFilter );
     314                 :          0 :   }
     315                 :            : 
     316                 :          0 :   if ( renderer->usesEmbeddedSymbols() )
     317                 :            :   {
     318                 :          0 :     featureRequest.setFlags( featureRequest.flags() | QgsFeatureRequest::EmbeddedSymbols );
     319                 :          0 :   }
     320                 :            : 
     321                 :            :   // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine.
     322                 :          0 :   if ( mSimplifyGeometry )
     323                 :            :   {
     324                 :          0 :     double map2pixelTol = mSimplifyMethod.threshold();
     325                 :          0 :     bool validTransform = true;
     326                 :            : 
     327                 :          0 :     const QgsMapToPixel &mtp = context.mapToPixel();
     328                 :          0 :     map2pixelTol *= mtp.mapUnitsPerPixel();
     329                 :          0 :     QgsCoordinateTransform ct = context.coordinateTransform();
     330                 :            : 
     331                 :            :     // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem
     332                 :          0 :     if ( ct.isValid() && !ct.isShortCircuited() )
     333                 :            :     {
     334                 :            :       try
     335                 :            :       {
     336                 :          0 :         QgsPointXY center = context.extent().center();
     337                 :          0 :         double rectSize = ct.sourceCrs().isGeographic() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100;
     338                 :            : 
     339                 :          0 :         QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize );
     340                 :          0 :         QgsRectangle targetRect = ct.transform( sourceRect );
     341                 :            : 
     342                 :          0 :         QgsDebugMsgLevel( QStringLiteral( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ), 4 );
     343                 :          0 :         QgsDebugMsgLevel( QStringLiteral( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ), 4 );
     344                 :            : 
     345                 :          0 :         if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() )
     346                 :            :         {
     347                 :          0 :           QgsPointXY minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() );
     348                 :          0 :           QgsPointXY maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() );
     349                 :          0 :           QgsPointXY minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() );
     350                 :          0 :           QgsPointXY maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() );
     351                 :            : 
     352                 :          0 :           double sourceHypothenuse = std::sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) );
     353                 :          0 :           double targetHypothenuse = std::sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) );
     354                 :            : 
     355                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ), 4 );
     356                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ), 4 );
     357                 :            : 
     358                 :          0 :           if ( !qgsDoubleNear( targetHypothenuse, 0.0 ) )
     359                 :          0 :             map2pixelTol *= ( sourceHypothenuse / targetHypothenuse );
     360                 :          0 :         }
     361                 :          0 :       }
     362                 :            :       catch ( QgsCsException &cse )
     363                 :            :       {
     364                 :          0 :         QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
     365                 :          0 :         validTransform = false;
     366                 :          0 :       }
     367                 :          0 :     }
     368                 :            : 
     369                 :          0 :     if ( validTransform )
     370                 :            :     {
     371                 :          0 :       QgsSimplifyMethod simplifyMethod;
     372                 :          0 :       simplifyMethod.setMethodType( QgsSimplifyMethod::OptimizeForRendering );
     373                 :          0 :       simplifyMethod.setTolerance( map2pixelTol );
     374                 :          0 :       simplifyMethod.setThreshold( mSimplifyMethod.threshold() );
     375                 :          0 :       simplifyMethod.setForceLocalOptimization( mSimplifyMethod.forceLocalOptimization() );
     376                 :          0 :       featureRequest.setSimplifyMethod( simplifyMethod );
     377                 :            : 
     378                 :          0 :       QgsVectorSimplifyMethod vectorMethod = mSimplifyMethod;
     379                 :          0 :       vectorMethod.setTolerance( map2pixelTol );
     380                 :          0 :       context.setVectorSimplifyMethod( vectorMethod );
     381                 :          0 :     }
     382                 :            :     else
     383                 :            :     {
     384                 :          0 :       QgsVectorSimplifyMethod vectorMethod;
     385                 :          0 :       vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification );
     386                 :          0 :       context.setVectorSimplifyMethod( vectorMethod );
     387                 :            :     }
     388                 :          0 :   }
     389                 :            :   else
     390                 :            :   {
     391                 :          0 :     QgsVectorSimplifyMethod vectorMethod;
     392                 :          0 :     vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification );
     393                 :          0 :     context.setVectorSimplifyMethod( vectorMethod );
     394                 :            :   }
     395                 :            : 
     396                 :          0 :   QgsFeatureIterator fit = mSource->getFeatures( featureRequest );
     397                 :            :   // Attach an interruption checker so that iterators that have potentially
     398                 :            :   // slow fetchFeature() implementations, such as in the WFS provider, can
     399                 :            :   // check it, instead of relying on just the mContext.renderingStopped() check
     400                 :            :   // in drawRenderer()
     401                 :          0 :   fit.setInterruptionChecker( mInterruptionChecker.get() );
     402                 :            : 
     403                 :          0 :   if ( ( renderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) && renderer->usingSymbolLevels() )
     404                 :          0 :     drawRendererLevels( renderer, fit );
     405                 :            :   else
     406                 :          0 :     drawRenderer( renderer, fit );
     407                 :            : 
     408                 :          0 :   if ( !fit.isValid() )
     409                 :            :   {
     410                 :          0 :     mErrors.append( QStringLiteral( "Data source invalid" ) );
     411                 :          0 :   }
     412                 :            : 
     413                 :          0 :   if ( usingEffect )
     414                 :            :   {
     415                 :          0 :     renderer->paintEffect()->end( context );
     416                 :          0 :   }
     417                 :            : 
     418                 :          0 :   mInterruptionChecker.reset();
     419                 :          0 :   return true;
     420                 :          0 : }
     421                 :            : 
     422                 :            : 
     423                 :          0 : void QgsVectorLayerRenderer::drawRenderer( QgsFeatureRenderer *renderer, QgsFeatureIterator &fit )
     424                 :            : {
     425                 :          0 :   const bool isMainRenderer = renderer == mRenderer;
     426                 :            : 
     427                 :          0 :   QgsExpressionContextScope *symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr, new QgsExpressionContextScope() );
     428                 :          0 :   QgsRenderContext &context = *renderContext();
     429                 :          0 :   context.expressionContext().appendScope( symbolScope );
     430                 :            : 
     431                 :          0 :   std::unique_ptr< QgsGeometryEngine > clipEngine;
     432                 :          0 :   if ( mApplyClipFilter )
     433                 :            :   {
     434                 :          0 :     clipEngine.reset( QgsGeometry::createGeometryEngine( mClipFilterGeom.constGet() ) );
     435                 :          0 :     clipEngine->prepareGeometry();
     436                 :          0 :   }
     437                 :            : 
     438                 :          0 :   QgsFeature fet;
     439                 :          0 :   while ( fit.nextFeature( fet ) )
     440                 :            :   {
     441                 :            :     try
     442                 :            :     {
     443                 :          0 :       if ( context.renderingStopped() )
     444                 :            :       {
     445                 :          0 :         QgsDebugMsgLevel( QStringLiteral( "Drawing of vector layer %1 canceled." ).arg( layerId() ), 2 );
     446                 :          0 :         break;
     447                 :            :       }
     448                 :            : 
     449                 :          0 :       if ( !fet.hasGeometry() || fet.geometry().isEmpty() )
     450                 :          0 :         continue; // skip features without geometry
     451                 :            : 
     452                 :          0 :       if ( clipEngine && !clipEngine->intersects( fet.geometry().constGet() ) )
     453                 :          0 :         continue; // skip features outside of clipping region
     454                 :            : 
     455                 :          0 :       if ( mApplyClipGeometries )
     456                 :          0 :         context.setFeatureClipGeometry( mClipFeatureGeom );
     457                 :            : 
     458                 :          0 :       context.expressionContext().setFeature( fet );
     459                 :            : 
     460                 :          0 :       bool sel = isMainRenderer && context.showSelection() && mSelectedFeatureIds.contains( fet.id() );
     461                 :          0 :       bool drawMarker = isMainRenderer && ( mDrawVertexMarkers && context.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) );
     462                 :            : 
     463                 :            :       // render feature
     464                 :          0 :       bool rendered = renderer->renderFeature( fet, context, -1, sel, drawMarker );
     465                 :            : 
     466                 :            :       // labeling - register feature
     467                 :          0 :       if ( rendered )
     468                 :            :       {
     469                 :            :         // as soon as first feature is rendered, we can start showing layer updates.
     470                 :            :         // but if we are blocking render updates (so that a previously cached image is being shown), we wait
     471                 :            :         // at most e.g. 3 seconds before we start forcing progressive updates.
     472                 :          0 :         if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
     473                 :            :         {
     474                 :          0 :           mReadyToCompose = true;
     475                 :          0 :         }
     476                 :            : 
     477                 :            :         // new labeling engine
     478                 :          0 :         if ( isMainRenderer && context.labelingEngine() && ( mLabelProvider || mDiagramProvider ) )
     479                 :            :         {
     480                 :          0 :           QgsGeometry obstacleGeometry;
     481                 :          0 :           QgsSymbolList symbols = renderer->originalSymbolsForFeature( fet, context );
     482                 :          0 :           QgsSymbol *symbol = nullptr;
     483                 :          0 :           if ( !symbols.isEmpty() && fet.geometry().type() == QgsWkbTypes::PointGeometry )
     484                 :            :           {
     485                 :          0 :             obstacleGeometry = QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, context, symbols );
     486                 :          0 :           }
     487                 :            : 
     488                 :          0 :           if ( !symbols.isEmpty() )
     489                 :            :           {
     490                 :          0 :             symbol = symbols.at( 0 );
     491                 :          0 :             QgsExpressionContextUtils::updateSymbolScope( symbol, symbolScope );
     492                 :          0 :           }
     493                 :            : 
     494                 :          0 :           if ( mApplyLabelClipGeometries )
     495                 :          0 :             context.setFeatureClipGeometry( mLabelClipFeatureGeom );
     496                 :            : 
     497                 :          0 :           if ( mLabelProvider )
     498                 :            :           {
     499                 :          0 :             mLabelProvider->registerFeature( fet, context, obstacleGeometry, symbol );
     500                 :          0 :           }
     501                 :          0 :           if ( mDiagramProvider )
     502                 :            :           {
     503                 :          0 :             mDiagramProvider->registerFeature( fet, context, obstacleGeometry );
     504                 :          0 :           }
     505                 :            : 
     506                 :          0 :           if ( mApplyLabelClipGeometries )
     507                 :          0 :             context.setFeatureClipGeometry( QgsGeometry() );
     508                 :          0 :         }
     509                 :          0 :       }
     510                 :          0 :     }
     511                 :            :     catch ( const QgsCsException &cse )
     512                 :            :     {
     513                 :          0 :       Q_UNUSED( cse )
     514                 :          0 :       QgsDebugMsg( QStringLiteral( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
     515                 :            :                    .arg( fet.id() ).arg( cse.what() ) );
     516                 :          0 :     }
     517                 :            :   }
     518                 :            : 
     519                 :          0 :   delete context.expressionContext().popScope();
     520                 :            : 
     521                 :          0 :   stopRenderer( renderer, nullptr );
     522                 :          0 : }
     523                 :            : 
     524                 :          0 : void QgsVectorLayerRenderer::drawRendererLevels( QgsFeatureRenderer *renderer, QgsFeatureIterator &fit )
     525                 :            : {
     526                 :          0 :   const bool isMainRenderer = renderer == mRenderer;
     527                 :            : 
     528                 :          0 :   QHash< QgsSymbol *, QList<QgsFeature> > features; // key = symbol, value = array of features
     529                 :          0 :   QgsRenderContext &context = *renderContext();
     530                 :            : 
     531                 :          0 :   QgsSingleSymbolRenderer *selRenderer = nullptr;
     532                 :          0 :   if ( !mSelectedFeatureIds.isEmpty() )
     533                 :            :   {
     534                 :          0 :     selRenderer = new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( mGeometryType ) );
     535                 :          0 :     selRenderer->symbol()->setColor( context.selectionColor() );
     536                 :          0 :     selRenderer->setVertexMarkerAppearance( mVertexMarkerStyle, mVertexMarkerSize );
     537                 :          0 :     selRenderer->startRender( context, mFields );
     538                 :          0 :   }
     539                 :            : 
     540                 :          0 :   QgsExpressionContextScope *symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr, new QgsExpressionContextScope() );
     541                 :          0 :   std::unique_ptr< QgsExpressionContextScopePopper > scopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
     542                 :            : 
     543                 :            : 
     544                 :          0 :   std::unique_ptr< QgsGeometryEngine > clipEngine;
     545                 :          0 :   if ( mApplyClipFilter )
     546                 :            :   {
     547                 :          0 :     clipEngine.reset( QgsGeometry::createGeometryEngine( mClipFilterGeom.constGet() ) );
     548                 :          0 :     clipEngine->prepareGeometry();
     549                 :          0 :   }
     550                 :            : 
     551                 :          0 :   if ( mApplyLabelClipGeometries )
     552                 :          0 :     context.setFeatureClipGeometry( mLabelClipFeatureGeom );
     553                 :            : 
     554                 :            :   // 1. fetch features
     555                 :          0 :   QgsFeature fet;
     556                 :          0 :   while ( fit.nextFeature( fet ) )
     557                 :            :   {
     558                 :          0 :     if ( context.renderingStopped() )
     559                 :            :     {
     560                 :          0 :       qDebug( "rendering stop!" );
     561                 :          0 :       stopRenderer( renderer, selRenderer );
     562                 :          0 :       return;
     563                 :            :     }
     564                 :            : 
     565                 :          0 :     if ( !fet.hasGeometry() )
     566                 :          0 :       continue; // skip features without geometry
     567                 :            : 
     568                 :          0 :     if ( clipEngine && !clipEngine->intersects( fet.geometry().constGet() ) )
     569                 :          0 :       continue; // skip features outside of clipping region
     570                 :            : 
     571                 :          0 :     context.expressionContext().setFeature( fet );
     572                 :          0 :     QgsSymbol *sym = renderer->symbolForFeature( fet, context );
     573                 :          0 :     if ( !sym )
     574                 :            :     {
     575                 :          0 :       continue;
     576                 :            :     }
     577                 :            : 
     578                 :          0 :     if ( !features.contains( sym ) )
     579                 :            :     {
     580                 :          0 :       features.insert( sym, QList<QgsFeature>() );
     581                 :          0 :     }
     582                 :          0 :     features[sym].append( fet );
     583                 :            : 
     584                 :            :     // new labeling engine
     585                 :          0 :     if ( isMainRenderer && context.labelingEngine() && ( mLabelProvider || mDiagramProvider ) )
     586                 :            :     {
     587                 :          0 :       QgsGeometry obstacleGeometry;
     588                 :          0 :       QgsSymbolList symbols = renderer->originalSymbolsForFeature( fet, context );
     589                 :          0 :       QgsSymbol *symbol = nullptr;
     590                 :          0 :       if ( !symbols.isEmpty() && fet.geometry().type() == QgsWkbTypes::PointGeometry )
     591                 :            :       {
     592                 :          0 :         obstacleGeometry = QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, context, symbols );
     593                 :          0 :       }
     594                 :            : 
     595                 :          0 :       if ( !symbols.isEmpty() )
     596                 :            :       {
     597                 :          0 :         symbol = symbols.at( 0 );
     598                 :          0 :         QgsExpressionContextUtils::updateSymbolScope( symbol, symbolScope );
     599                 :          0 :       }
     600                 :            : 
     601                 :          0 :       if ( mLabelProvider )
     602                 :            :       {
     603                 :          0 :         mLabelProvider->registerFeature( fet, context, obstacleGeometry, symbol );
     604                 :          0 :       }
     605                 :          0 :       if ( mDiagramProvider )
     606                 :            :       {
     607                 :          0 :         mDiagramProvider->registerFeature( fet, context, obstacleGeometry );
     608                 :          0 :       }
     609                 :          0 :     }
     610                 :            :   }
     611                 :            : 
     612                 :          0 :   if ( mApplyLabelClipGeometries )
     613                 :          0 :     context.setFeatureClipGeometry( QgsGeometry() );
     614                 :            : 
     615                 :          0 :   scopePopper.reset();
     616                 :            : 
     617                 :          0 :   if ( features.empty() )
     618                 :            :   {
     619                 :            :     // nothing to draw
     620                 :          0 :     stopRenderer( renderer, selRenderer );
     621                 :          0 :     return;
     622                 :            :   }
     623                 :            : 
     624                 :            :   // find out the order
     625                 :          0 :   QgsSymbolLevelOrder levels;
     626                 :          0 :   QgsSymbolList symbols = renderer->symbols( context );
     627                 :          0 :   for ( int i = 0; i < symbols.count(); i++ )
     628                 :            :   {
     629                 :          0 :     QgsSymbol *sym = symbols[i];
     630                 :          0 :     for ( int j = 0; j < sym->symbolLayerCount(); j++ )
     631                 :            :     {
     632                 :          0 :       int level = sym->symbolLayer( j )->renderingPass();
     633                 :          0 :       if ( level < 0 || level >= 1000 ) // ignore invalid levels
     634                 :          0 :         continue;
     635                 :          0 :       QgsSymbolLevelItem item( sym, j );
     636                 :          0 :       while ( level >= levels.count() ) // append new empty levels
     637                 :          0 :         levels.append( QgsSymbolLevel() );
     638                 :          0 :       levels[level].append( item );
     639                 :          0 :     }
     640                 :          0 :   }
     641                 :            : 
     642                 :          0 :   if ( mApplyClipGeometries )
     643                 :          0 :     context.setFeatureClipGeometry( mClipFeatureGeom );
     644                 :            : 
     645                 :            :   // 2. draw features in correct order
     646                 :          0 :   for ( int l = 0; l < levels.count(); l++ )
     647                 :            :   {
     648                 :          0 :     QgsSymbolLevel &level = levels[l];
     649                 :          0 :     for ( int i = 0; i < level.count(); i++ )
     650                 :            :     {
     651                 :          0 :       QgsSymbolLevelItem &item = level[i];
     652                 :          0 :       if ( !features.contains( item.symbol() ) )
     653                 :            :       {
     654                 :          0 :         QgsDebugMsg( QStringLiteral( "level item's symbol not found!" ) );
     655                 :          0 :         continue;
     656                 :            :       }
     657                 :          0 :       int layer = item.layer();
     658                 :          0 :       QList<QgsFeature> &lst = features[item.symbol()];
     659                 :          0 :       QList<QgsFeature>::iterator fit;
     660                 :          0 :       for ( fit = lst.begin(); fit != lst.end(); ++fit )
     661                 :            :       {
     662                 :          0 :         if ( context.renderingStopped() )
     663                 :            :         {
     664                 :          0 :           stopRenderer( renderer, selRenderer );
     665                 :          0 :           return;
     666                 :            :         }
     667                 :            : 
     668                 :          0 :         bool sel = isMainRenderer && context.showSelection() && mSelectedFeatureIds.contains( fit->id() );
     669                 :            :         // maybe vertex markers should be drawn only during the last pass...
     670                 :          0 :         bool drawMarker = isMainRenderer && ( mDrawVertexMarkers && context.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) );
     671                 :            : 
     672                 :          0 :         context.expressionContext().setFeature( *fit );
     673                 :            : 
     674                 :            :         try
     675                 :            :         {
     676                 :          0 :           renderer->renderFeature( *fit, context, layer, sel, drawMarker );
     677                 :            : 
     678                 :            :           // as soon as first feature is rendered, we can start showing layer updates.
     679                 :            :           // but if we are blocking render updates (so that a previously cached image is being shown), we wait
     680                 :            :           // at most e.g. 3 seconds before we start forcing progressive updates.
     681                 :          0 :           if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
     682                 :            :           {
     683                 :          0 :             mReadyToCompose = true;
     684                 :          0 :           }
     685                 :          0 :         }
     686                 :            :         catch ( const QgsCsException &cse )
     687                 :            :         {
     688                 :          0 :           Q_UNUSED( cse )
     689                 :          0 :           QgsDebugMsg( QStringLiteral( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
     690                 :            :                        .arg( fet.id() ).arg( cse.what() ) );
     691                 :          0 :         }
     692                 :          0 :       }
     693                 :          0 :     }
     694                 :          0 :   }
     695                 :            : 
     696                 :          0 :   stopRenderer( renderer, selRenderer );
     697                 :          0 : }
     698                 :            : 
     699                 :          0 : void QgsVectorLayerRenderer::stopRenderer( QgsFeatureRenderer *renderer, QgsSingleSymbolRenderer *selRenderer )
     700                 :            : {
     701                 :          0 :   QgsRenderContext &context = *renderContext();
     702                 :          0 :   renderer->stopRender( context );
     703                 :          0 :   if ( selRenderer )
     704                 :            :   {
     705                 :          0 :     selRenderer->stopRender( context );
     706                 :          0 :     delete selRenderer;
     707                 :          0 :   }
     708                 :          0 : }
     709                 :            : 
     710                 :          0 : void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer *layer, QSet<QString> &attributeNames )
     711                 :            : {
     712                 :          0 :   QgsRenderContext &context = *renderContext();
     713                 :            :   // TODO: add attributes for geometry generator
     714                 :          0 :   if ( QgsLabelingEngine *engine2 = context.labelingEngine() )
     715                 :            :   {
     716                 :          0 :     if ( layer->labelsEnabled() )
     717                 :            :     {
     718                 :          0 :       mLabelProvider = layer->labeling()->provider( layer );
     719                 :          0 :       if ( mLabelProvider )
     720                 :            :       {
     721                 :          0 :         engine2->addProvider( mLabelProvider );
     722                 :          0 :         if ( !mLabelProvider->prepare( context, attributeNames ) )
     723                 :            :         {
     724                 :          0 :           engine2->removeProvider( mLabelProvider );
     725                 :          0 :           mLabelProvider = nullptr; // deleted by engine
     726                 :          0 :         }
     727                 :          0 :       }
     728                 :          0 :     }
     729                 :          0 :   }
     730                 :            : 
     731                 :            : #if 0 // TODO: limit of labels, font not found
     732                 :            :   QgsPalLayerSettings &palyr = mContext.labelingEngine()->layer( mLayerID );
     733                 :            : 
     734                 :            :   // see if feature count limit is set for labeling
     735                 :            :   if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 )
     736                 :            :   {
     737                 :            :     QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
     738                 :            :                                           .setFilterRect( mContext.extent() )
     739                 :            :                                           .setNoAttributes() );
     740                 :            : 
     741                 :            :     // total number of features that may be labeled
     742                 :            :     QgsFeature f;
     743                 :            :     int nFeatsToLabel = 0;
     744                 :            :     while ( fit.nextFeature( f ) )
     745                 :            :     {
     746                 :            :       nFeatsToLabel++;
     747                 :            :     }
     748                 :            :     palyr.mFeaturesToLabel = nFeatsToLabel;
     749                 :            :   }
     750                 :            : 
     751                 :            :   // notify user about any font substitution
     752                 :            :   if ( !palyr.mTextFontFound && !mLabelFontNotFoundNotified )
     753                 :            :   {
     754                 :            :     emit labelingFontNotFound( this, palyr.mTextFontFamily );
     755                 :            :     mLabelFontNotFoundNotified = true;
     756                 :            :   }
     757                 :            : #endif
     758                 :          0 : }
     759                 :            : 
     760                 :          0 : void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer *layer, QSet<QString> &attributeNames )
     761                 :            : {
     762                 :          0 :   QgsRenderContext &context = *renderContext();
     763                 :          0 :   if ( QgsLabelingEngine *engine2 = context.labelingEngine() )
     764                 :            :   {
     765                 :          0 :     if ( layer->diagramsEnabled() )
     766                 :            :     {
     767                 :          0 :       mDiagramProvider = new QgsVectorLayerDiagramProvider( layer );
     768                 :            :       // need to be added before calling prepare() - uses map settings from engine
     769                 :          0 :       engine2->addProvider( mDiagramProvider );
     770                 :          0 :       if ( !mDiagramProvider->prepare( context, attributeNames ) )
     771                 :            :       {
     772                 :          0 :         engine2->removeProvider( mDiagramProvider );
     773                 :          0 :         mDiagramProvider = nullptr;  // deleted by engine
     774                 :          0 :       }
     775                 :          0 :     }
     776                 :          0 :   }
     777                 :          0 : }
     778                 :            : 
     779                 :            : /*  -----------------------------------------  */
     780                 :            : /*  QgsVectorLayerRendererInterruptionChecker  */
     781                 :            : /*  -----------------------------------------  */
     782                 :            : 
     783                 :          0 : QgsVectorLayerRendererInterruptionChecker::QgsVectorLayerRendererInterruptionChecker
     784                 :            : ( const QgsRenderContext &context )
     785                 :          0 :   : mContext( context )
     786                 :          0 :   , mTimer( new QTimer( this ) )
     787                 :          0 : {
     788                 :          0 :   connect( mTimer, &QTimer::timeout, this, [ = ]
     789                 :            :   {
     790                 :          0 :     if ( mContext.renderingStopped() )
     791                 :            :     {
     792                 :          0 :       mTimer->stop();
     793                 :          0 :       cancel();
     794                 :          0 :     }
     795                 :          0 :   } );
     796                 :          0 :   mTimer->start( 50 );
     797                 :            : 
     798                 :          0 : }

Generated by: LCOV version 1.14