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 : }
|