QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsmeshlayerrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshlayerrenderer.cpp
3 ------------------------
4 begin : April 2018
5 copyright : (C) 2018 by Peter Petrik
6 email : zilolv at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include <memory>
19#include <QSet>
20#include <QPair>
21#include <QLinearGradient>
22#include <QBrush>
23#include <QPointer>
24#include <algorithm>
25#include <QElapsedTimer>
26
28
29#include "qgslogger.h"
30#include "qgsmeshlayer.h"
31#include "qgspointxy.h"
33#include "qgsrastershader.h"
35#include "qgsmeshlayerutils.h"
39#include "qgsmapclippingutils.h"
40#include "qgscolorrampshader.h"
42#include "qgsapplication.h"
43#include "qgsruntimeprofiler.h"
46
48 QgsMeshLayer *layer,
49 QgsRenderContext &context )
50 : QgsMapLayerRenderer( layer->id(), &context )
51 , mIsEditable( layer->isEditable() )
52 , mLayerName( layer->name() )
53 , mFeedback( new QgsMeshLayerRendererFeedback )
54 , mRendererSettings( layer->rendererSettings() )
55 , mEnableProfile( context.flags() & Qgis::RenderContextFlag::RecordProfile )
56 , mLayerOpacity( layer->opacity() )
57{
58 QElapsedTimer timer;
59 timer.start();
60
61 // make copies for mesh data
62 // cppcheck-suppress assertWithSideEffect
63 Q_ASSERT( layer->nativeMesh() );
64 // cppcheck-suppress assertWithSideEffect
65 Q_ASSERT( layer->triangularMesh() );
66 // cppcheck-suppress assertWithSideEffect
67 Q_ASSERT( layer->rendererCache() );
68 // cppcheck-suppress assertWithSideEffect
69 Q_ASSERT( layer->dataProvider() );
70
71 mReadyToCompose = false;
72
73 // copy native mesh
74 mNativeMesh = *( layer->nativeMesh() );
75 mLayerExtent = layer->extent();
76
77 // copy triangular mesh
78 copyTriangularMeshes( layer, context );
79
80 // copy datasets
81 copyScalarDatasetValues( layer );
82 copyVectorDatasetValues( layer );
83
84 calculateOutputSize();
85
86 QSet<QString> attrs;
87 prepareLabeling( layer, attrs );
88
90
91 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
92 {
93 QgsMeshLayerElevationProperties *elevProp = qobject_cast<QgsMeshLayerElevationProperties *>( layer->elevationProperties() );
94
96 mElevationScale = elevProp->zScale();
97 mElevationOffset = elevProp->zOffset();
98
99 if ( !context.zRange().isInfinite() )
100 {
101 switch ( elevProp->mode() )
102 {
104 // don't need to handle anything here -- the layer renderer will never be created if the
105 // render context range doesn't match the layer's fixed elevation range
106 break;
107
109 {
110 // TODO -- filtering by mesh z values is not currently implemented
111 break;
112 }
113 }
114 }
115 }
116
117 mPreparationTime = timer.elapsed();
118}
119
120void QgsMeshLayerRenderer::copyTriangularMeshes( QgsMeshLayer *layer, QgsRenderContext &context )
121{
122 // handle level of details of mesh
123 const QgsMeshSimplificationSettings simplificationSettings = layer->meshSimplificationSettings();
124 if ( simplificationSettings.isEnabled() )
125 {
126 const double triangleSize = simplificationSettings.meshResolution() * context.mapToPixel().mapUnitsPerPixel();
127 mTriangularMesh = *( layer->triangularMesh( triangleSize ) );
128 mIsMeshSimplificationActive = true;
129 }
130 else
131 {
132 mTriangularMesh = *( layer->triangularMesh() );
133 }
134}
135
137{
138 return mFeedback.get();
139}
140
141void QgsMeshLayerRenderer::calculateOutputSize()
142{
143 // figure out image size
144 const QgsRenderContext &context = *renderContext();
145 const QgsRectangle extent = context.mapExtent();
146 const QgsMapToPixel mapToPixel = context.mapToPixel();
147 const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent, context.devicePixelRatio() );
148 const int width = int( screenBBox.width() );
149 const int height = int( screenBBox.height() );
150 mOutputSize = QSize( width, height );
151}
152
153void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
154{
155 QgsMeshDatasetIndex datasetIndex;
156 if ( renderContext()->isTemporal() )
157 datasetIndex = layer->activeScalarDatasetAtTime( renderContext()->temporalRange() );
158 else
159 datasetIndex = layer->staticScalarDatasetIndex();
160
161 // Find out if we can use cache up to date. If yes, use it and return
162 const int datasetGroupCount = layer->datasetGroupCount();
164 QgsMeshLayerRendererCache *cache = layer->rendererCache();
165 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
166 ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
167 ( cache->mDataInterpolationMethod == method ) &&
168 ( QgsMesh3DAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
169 )
170 {
171 mScalarDatasetValues = cache->mScalarDatasetValues;
172 mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
173 mScalarDataType = cache->mScalarDataType;
174 mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
175 mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
176 return;
177 }
178
179 // Cache is not up-to-date, gather data
180 if ( datasetIndex.isValid() )
181 {
182 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex.group() );
183 mScalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
184
185 // populate scalar values
186 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mScalarDataType );
187 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
188 layer,
189 datasetIndex,
190 0,
191 count );
192
193 if ( vals.isValid() )
194 {
195 // vals could be scalar or vectors, for contour rendering we want always magnitude
196 mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
197 }
198 else
199 {
200 mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
201 }
202
203 // populate face active flag, always defined on faces
205 datasetIndex,
206 0,
207 mNativeMesh.faces.count() );
208
209 // for data on faces, there could be request to interpolate the data to vertices
211 {
212 if ( mScalarDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnFaces )
213 {
214 mScalarDataType = QgsMeshDatasetGroupMetadata::DataType::DataOnVertices;
215 mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
220 method
221 );
222 }
223 else if ( mScalarDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnVertices )
224 {
225 mScalarDataType = QgsMeshDatasetGroupMetadata::DataType::DataOnFaces;
226 mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
231 method
232 );
233 }
234 }
235
236 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
237 mScalarDatasetMinimum = datasetMetadata.minimum();
238 mScalarDatasetMaximum = datasetMetadata.maximum();
239 }
240
241 // update cache
242 cache->mDatasetGroupsCount = datasetGroupCount;
243 cache->mActiveScalarDatasetIndex = datasetIndex;
244 cache->mDataInterpolationMethod = method;
245 cache->mScalarDatasetValues = mScalarDatasetValues;
246 cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
247 cache->mScalarDataType = mScalarDataType;
248 cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
249 cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
250 cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
251}
252
253
254void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
255{
256 QgsMeshDatasetIndex datasetIndex;
257 if ( renderContext()->isTemporal() )
258 datasetIndex = layer->activeVectorDatasetAtTime( renderContext()->temporalRange() );
259 else
260 datasetIndex = layer->staticVectorDatasetIndex();
261
262 // Find out if we can use cache up to date. If yes, use it and return
263 const int datasetGroupCount = layer->datasetGroupCount();
264 QgsMeshLayerRendererCache *cache = layer->rendererCache();
265 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
266 ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
267 ( QgsMesh3DAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
268 )
269 {
270 mVectorDatasetValues = cache->mVectorDatasetValues;
271 mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
272 mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
273 mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
274 mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
275 mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
276 mVectorActiveFaceFlagValues = cache->mVectorActiveFaceFlagValues;
277 mVectorDataType = cache->mVectorDataType;
278 return;
279 }
280
281 // Cache is not up-to-date, gather data
282 if ( datasetIndex.isValid() )
283 {
284 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex );
285
286 const bool isScalar = metadata.isScalar();
287 if ( isScalar )
288 {
289 QgsDebugError( QStringLiteral( "Dataset has no vector values" ) );
290 }
291 else
292 {
293 mVectorDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
294
297
298 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mVectorDataType );
299 mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
300 layer,
301 datasetIndex,
302 0,
303 count );
304
306 mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
307 else
308 mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
309
310 // populate face active flag
311 mVectorActiveFaceFlagValues = layer->areFacesActive( datasetIndex, 0, mNativeMesh.faces.count() );
312
313 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
314 mVectorDatasetMagMinimum = datasetMetadata.minimum();
315 mVectorDatasetMagMaximum = datasetMetadata.maximum();
316 }
317 }
318
319 // update cache
320 cache->mDatasetGroupsCount = datasetGroupCount;
321 cache->mActiveVectorDatasetIndex = datasetIndex;
322 cache->mVectorDatasetValues = mVectorDatasetValues;
323 cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
324 cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
325 cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
326 cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
327 cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
328 cache->mVectorActiveFaceFlagValues = mVectorActiveFaceFlagValues;
329 cache->mVectorDataType = mVectorDataType;
330 cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
331}
332
334{
335 std::unique_ptr< QgsScopedRuntimeProfile > profile;
336 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
337 if ( mEnableProfile )
338 {
339 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
340 if ( mPreparationTime > 0 )
341 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
342 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
343 }
344
345 mReadyToCompose = false;
346 const QgsScopedQPainterState painterState( renderContext()->painter() );
347 if ( !mClippingRegions.empty() )
348 {
349 bool needsPainterClipPath = false;
350 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Mesh, needsPainterClipPath );
351 if ( needsPainterClipPath )
352 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
353 }
354
355 preparingProfile.reset();
356
357 renderScalarDataset();
358 mReadyToCompose = true;
359 renderMesh();
360 renderVectorDataset();
361
362 registerLabelFeatures();
363
364 return !renderContext()->renderingStopped();
365}
366
368{
369 return renderContext()->testFlag( Qgis::RenderContextFlag::UseAdvancedEffects ) && ( !qgsDoubleNear( mLayerOpacity, 1.0 ) );
370}
371
372void QgsMeshLayerRenderer::renderMesh()
373{
374 if ( !mRendererSettings.nativeMeshSettings().isEnabled() && !mIsEditable &&
377 return;
378
379 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
380 if ( mEnableProfile )
381 {
382 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering mesh" ), QStringLiteral( "rendering" ) );
383 }
384
385 // triangular mesh
386 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
388 {
389 renderFaceMesh(
392 trianglesInExtent );
393 }
394
395 // native mesh
396 if ( ( mRendererSettings.nativeMeshSettings().isEnabled() || mIsEditable ) &&
398 {
399 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
401
402 renderFaceMesh(
405 nativeFacesInExtent.values() );
406 }
407
408 // edge mesh
410 {
411 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( renderContext()->mapExtent() );
412 renderEdgeMesh( mRendererSettings.edgeMeshSettings(), edgesInExtent );
413 }
414}
415
416static QPainter *_painterForMeshFrame( QgsRenderContext &context, const QgsMeshRendererMeshSettings &settings )
417{
418 // Set up the render configuration options
419 QPainter *painter = context.painter();
420
421 painter->save();
422 context.setPainterFlagsUsingContext( painter );
423
424 QPen pen = painter->pen();
425 pen.setCapStyle( Qt::FlatCap );
426 pen.setJoinStyle( Qt::MiterJoin );
427
428 const double penWidth = context.convertToPainterUnits( settings.lineWidth(), settings.lineWidthUnit() );
429 pen.setWidthF( penWidth );
430 pen.setColor( settings.color() );
431 painter->setPen( pen );
432 return painter;
433}
434
435void QgsMeshLayerRenderer::renderEdgeMesh( const QgsMeshRendererMeshSettings &settings, const QList<int> &edgesInExtent )
436{
437 Q_ASSERT( settings.isEnabled() );
438
439 if ( !mTriangularMesh.contains( QgsMesh::ElementType::Edge ) )
440 return;
441
442 QgsRenderContext &context = *renderContext();
443 QPainter *painter = _painterForMeshFrame( context, settings );
444
445 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
446 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
447
448 for ( const int i : edgesInExtent )
449 {
450 if ( context.renderingStopped() )
451 break;
452
453 if ( i >= edges.size() )
454 continue;
455
456 const QgsMeshEdge &edge = edges[i];
457 const int startVertexIndex = edge.first;
458 const int endVertexIndex = edge.second;
459
460 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
461 continue;
462
463 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
464 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
465 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
466 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
467 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
468 }
469 painter->restore();
470};
471
472void QgsMeshLayerRenderer::renderFaceMesh(
473 const QgsMeshRendererMeshSettings &settings,
474 const QVector<QgsMeshFace> &faces,
475 const QList<int> &facesInExtent )
476{
477 Q_ASSERT( settings.isEnabled() || mIsEditable );
478
479 if ( !mTriangularMesh.contains( QgsMesh::ElementType::Face ) )
480 return;
481
482 QgsRenderContext &context = *renderContext();
483 QPainter *painter = _painterForMeshFrame( context, settings );
484
485 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
486 QSet<QPair<int, int>> drawnEdges;
487
488 for ( const int i : facesInExtent )
489 {
490 if ( context.renderingStopped() )
491 break;
492
493 if ( i < 0 || i >= faces.count() )
494 continue;
495
496 const QgsMeshFace &face = faces[i];
497 if ( face.size() < 2 )
498 continue;
499
500 for ( int j = 0; j < face.size(); ++j )
501 {
502 const int startVertexId = face[j];
503 const int endVertexId = face[( j + 1 ) % face.size()];
504 const QPair<int, int> thisEdge( startVertexId, endVertexId );
505 const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
506 if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
507 continue;
508 drawnEdges.insert( thisEdge );
509 drawnEdges.insert( thisEdgeReversed );
510
511 const QgsMeshVertex &startVertex = vertices[startVertexId];
512 const QgsMeshVertex &endVertex = vertices[endVertexId];
513 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
514 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
515 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
516 }
517 }
518
519 painter->restore();
520}
521
522void QgsMeshLayerRenderer::renderScalarDataset()
523{
524 if ( mScalarDatasetValues.isEmpty() )
525 return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
526
527 if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
528 return; // only NODATA values
529
530 const int groupIndex = mRendererSettings.activeScalarDatasetGroup();
531 if ( groupIndex < 0 )
532 return; // no shader
533
534 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
535 if ( mEnableProfile )
536 {
537 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering scalar datasets" ), QStringLiteral( "rendering" ) );
538 }
539
540 const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( groupIndex );
541
542 if ( ( mTriangularMesh.contains( QgsMesh::ElementType::Face ) ) &&
543 ( mScalarDataType != QgsMeshDatasetGroupMetadata::DataType::DataOnEdges ) )
544 {
545 renderScalarDatasetOnFaces( scalarSettings );
546 }
547
548 if ( ( mTriangularMesh.contains( QgsMesh::ElementType::Edge ) ) &&
549 ( mScalarDataType != QgsMeshDatasetGroupMetadata::DataType::DataOnFaces ) )
550 {
551 renderScalarDatasetOnEdges( scalarSettings );
552 }
553}
554
555void QgsMeshLayerRenderer::renderScalarDatasetOnEdges( const QgsMeshRendererScalarSettings &scalarSettings )
556{
557 QgsRenderContext &context = *renderContext();
558 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
559 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
560 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( context.mapExtent() );
561
562 QgsInterpolatedLineRenderer edgePlotter;
563 edgePlotter.setInterpolatedColor( QgsInterpolatedLineColor( scalarSettings.colorRampShader() ) );
564 edgePlotter.setInterpolatedWidth( QgsInterpolatedLineWidth( scalarSettings.edgeStrokeWidth() ) );
565 edgePlotter.setWidthUnit( scalarSettings.edgeStrokeWidthUnit() );
566
567 for ( const int i : edgesInExtent )
568 {
569 if ( context.renderingStopped() )
570 break;
571
572 if ( i >= edges.size() )
573 continue;
574
575 const QgsMeshEdge &edge = edges[i];
576 const int startVertexIndex = edge.first;
577 const int endVertexIndex = edge.second;
578
579 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
580 continue;
581
582 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
583 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
584
585 if ( mScalarDataType == QgsMeshDatasetGroupMetadata::DataType::DataOnEdges )
586 {
587 edgePlotter.render( mScalarDatasetValues[i], mScalarDatasetValues[i], startVertex, endVertex, context );
588 }
589 else
590 {
591 edgePlotter.render( mScalarDatasetValues[startVertexIndex], mScalarDatasetValues[endVertexIndex], startVertex, endVertex, context );
592 }
593 }
594}
595
596QColor QgsMeshLayerRenderer::colorAt( QgsColorRampShader *shader, double val ) const
597{
598 int r, g, b, a;
599 if ( shader->shade( val, &r, &g, &b, &a ) )
600 {
601 return QColor( r, g, b, a );
602 }
603 return QColor();
604}
605
606QgsPointXY QgsMeshLayerRenderer::fractionPoint( const QgsPointXY &p1, const QgsPointXY &p2, double fraction ) const
607{
608 const QgsPointXY pt( p1.x() + fraction * ( p2.x() - p1.x() ),
609 p1.y() + fraction * ( p2.y() - p1.y() ) );
610 return pt;
611}
612
613void QgsMeshLayerRenderer::renderScalarDatasetOnFaces( const QgsMeshRendererScalarSettings &scalarSettings )
614{
615 QgsRenderContext &context = *renderContext();
616
617 QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
619 sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
620 QgsMeshLayerInterpolator interpolator( mTriangularMesh,
624 context,
625 mOutputSize );
626 interpolator.setSpatialIndexActive( mIsMeshSimplificationActive );
627 interpolator.setElevationMapSettings( mRenderElevationMap, mElevationScale, mElevationOffset );
628 QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
629 renderer.setClassificationMin( scalarSettings.classificationMinimum() );
630 renderer.setClassificationMax( scalarSettings.classificationMaximum() );
631 renderer.setOpacity( scalarSettings.opacity() );
632
633 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
634 QImage img = bl->image();
635 img.setDevicePixelRatio( context.devicePixelRatio() );
636
637 context.painter()->drawImage( 0, 0, img );
638}
639
640void QgsMeshLayerRenderer::renderVectorDataset()
641{
642 const int groupIndex = mRendererSettings.activeVectorDatasetGroup();
643 if ( groupIndex < 0 )
644 return;
645
647 return; // no data at all
648
649 if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
650 return; // only NODATA values
651
652 if ( !( mVectorDatasetMagMaximum > 0 ) )
653 return; //all vector are null vector
654
655 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
656 if ( mEnableProfile )
657 {
658 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering vector datasets" ), QStringLiteral( "rendering" ) );
659 }
660
661 std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
669 mRendererSettings.vectorSettings( groupIndex ),
670 *renderContext(),
672 mFeedback.get(),
673 mOutputSize ) );
674
675 if ( renderer )
676 renderer->draw();
677}
678
679void QgsMeshLayerRenderer::prepareLabeling( QgsMeshLayer *layer, QSet<QString> &attributeNames )
680{
681 QgsRenderContext &context = *renderContext();
682
683 if ( QgsLabelingEngine *engine = context.labelingEngine() )
684 {
685 if ( layer->labelsEnabled() )
686 {
687 mLabelProvider = layer->labeling()->provider( layer );
688 if ( mLabelProvider )
689 {
690 auto c = context.expressionContext();
691
693 c.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mNativeMesh ) );
694 context.setExpressionContext( c );
695
696 engine->addProvider( mLabelProvider );
697 if ( !mLabelProvider->prepare( context, attributeNames ) )
698 {
699 engine->removeProvider( mLabelProvider );
700 mLabelProvider = nullptr; // deleted by engine
701 }
702 }
703 }
704 }
705}
706
707void QgsMeshLayerRenderer::registerLabelFeatures()
708{
709 if ( !mLabelProvider )
710 return;
711
712 QgsRenderContext &context = *renderContext();
713
714 QgsExpressionContextScope *scope = context.expressionContext().activeScopeForVariable( QStringLiteral( "_native_mesh" ) );
715
716 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
717
718 if ( mLabelProvider->labelFaces() )
719 {
720 if ( !mTriangularMesh.contains( QgsMesh::ElementType::Face ) )
721 return;
722 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
724
725 for ( const int i : nativeFacesInExtent )
726 {
727 if ( context.renderingStopped() )
728 break;
729
730 if ( i < 0 || i >= mNativeMesh.faces.count() )
731 continue;
732
733 scope->setVariable( QStringLiteral( "_mesh_face_index" ), i, false );
734
735 QgsFeature f( i );
737 f.setGeometry( geom );
738 mLabelProvider->registerFeature( f, context );
739 }
740 }
741 else
742 {
743 if ( !mTriangularMesh.contains( QgsMesh::ElementType::Vertex ) )
744 return;
745 const QSet<int> nativeVerticesInExtent = QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent,
747
748 for ( const int i : nativeVerticesInExtent )
749 {
750 if ( context.renderingStopped() )
751 break;
752
753 if ( i < 0 || i >= mNativeMesh.vertexCount() )
754 continue;
755
756 scope->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
757
758 QgsFeature f( i );
759 QgsGeometry geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
760 f.setGeometry( geom );
761 mLabelProvider->registerFeature( f, context );
762 }
763 }
764}
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:54
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ FromVertices
Elevation should be taken from mesh vertices.
@ FixedElevationRange
Layer has a fixed elevation range.
virtual QgsMeshLayerLabelProvider * provider(QgsMeshLayer *layer) const
Factory for label provider implementation.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue) const override
Generates and new RGB value based on one input value.
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition: qgsrange.h:285
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static QgsExpressionContextScope * meshExpressionScope(QgsMesh::ElementType elementType)
Creates a new scope which contains functions relating to mesh layer element elementType.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
Class defining color to render mesh datasets.
Represents a simple line renderer with width and color varying depending on values.
void setInterpolatedColor(const QgsInterpolatedLineColor &strokeColoring)
Sets the stroke color used to render.
void setInterpolatedWidth(const QgsInterpolatedLineWidth &strokeWidth)
Sets the stroke width used to render.
void render(double value1, double value2, const QgsPointXY &point1, const QgsPointXY &point2, QgsRenderContext &context) const
Renders a line in the context between point1 and point2 with color and width that vary depending on v...
void setWidthUnit(Qgis::RenderUnit strokeWidthUnit)
Sets the unit of the stroke width.
Represents a width than can vary depending on values.
The QgsLabelingEngine class provides map labeling functionality.
static QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, Qgis::LayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
static QList< QgsMapClippingRegion > collectClippingRegionsForLayer(const QgsRenderContext &context, const QgsMapLayer *layer)
Collects the list of map clipping regions from a context which apply to a map layer.
double zScale() const
Returns the z scale, which is a scaling factor which should be applied to z values from the layer.
virtual bool hasElevation() const
Returns true if the layer has an elevation or z component.
double zOffset() const
Returns the z offset, which is a fixed offset amount which should be added to z values from the layer...
Base class for utility classes that encapsulate information necessary for rendering of map layers.
bool mReadyToCompose
The flag must be set to false in renderer's constructor if wants to use the smarter map redraws funct...
QString layerId() const
Gets access to the ID of the layer rendered by this class.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:88
static bool equals(const QgsMesh3DAveragingMethod *a, const QgsMesh3DAveragingMethod *b)
Returns whether two methods equal.
virtual QgsMesh3DAveragingMethod * clone() const =0
Clone the instance.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
bool isValid() const
Whether the block is valid.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
bool isScalar() const
Returns whether dataset group has scalar data.
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
double minimum() const
Returns minimum scalar value/vector magnitude present for whole dataset group.
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
int group() const
Returns a group index.
QgsMeshDatasetMetadata is a collection of mesh dataset metadata such as whether the data is valid or ...
double maximum() const
Returns maximum scalar value/vector magnitude present for the dataset.
double minimum() const
Returns minimum scalar value/vector magnitude present for the dataset.
Mesh layer specific subclass of QgsMapLayerElevationProperties.
Qgis::MeshElevationMode mode() const
Returns the elevation mode.
bool labelFaces() const
Returns false if labeling mesh vertices, true if labeling mesh faces.
virtual QList< QgsLabelFeature * > registerFeature(const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry=QgsGeometry(), const QgsSymbol *symbol=nullptr)
Register a feature for labeling as one or more QgsLabelFeature objects stored into mLabels.
virtual bool prepare(QgsRenderContext &context, QSet< QString > &attributeNames)
Prepare for registration of features.
QgsMeshDatasetGroupMetadata::DataType mScalarDataType
QgsTriangularMesh mTriangularMesh
QgsMeshLayerRenderer(QgsMeshLayer *layer, QgsRenderContext &context)
Ctor.
QVector< double > mScalarDatasetValues
std::unique_ptr< QgsMeshLayerRendererFeedback > mFeedback
feedback class for cancellation
QgsMeshDataBlock mScalarActiveFaceFlagValues
bool render() override
Do the rendering (based on data stored in the class).
QVector< double > mVectorDatasetValuesMag
QgsMeshRendererSettings mRendererSettings
bool forceRasterRender() const override
Returns true if the renderer must be rendered to a raster paint device (e.g.
QgsMeshDatasetGroupMetadata::DataType mVectorDataType
QList< QgsMapClippingRegion > mClippingRegions
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be nullptr)
QgsMeshDataBlock mVectorActiveFaceFlagValues
QgsMeshDataBlock mVectorDatasetValues
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:101
QgsRectangle extent() const override
Returns the extent of the layer.
int datasetGroupCount() const
Returns the dataset groups count handle by the layer.
QgsMeshDatasetIndex activeScalarDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active scalar group depending on the time range.
const QgsAbstractMeshLayerLabeling * labeling() const
Access to const labeling configuration.
Definition: qgsmeshlayer.h:915
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsMeshDatasetIndex staticVectorDatasetIndex() const
Returns the static vector dataset index that is rendered if the temporal properties is not active.
QgsMeshSimplificationSettings meshSimplificationSettings() const
Returns mesh simplification settings.
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
QgsMeshDatasetIndex staticScalarDatasetIndex() const
Returns the static scalar dataset index that is rendered if the temporal properties is not active.
QgsMeshDatasetMetadata datasetMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset metadata.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
Represents a mesh renderer settings for mesh object.
QColor color() const
Returns color used for rendering.
double lineWidth() const
Returns line width used for rendering (in millimeters)
Qgis::RenderUnit lineWidthUnit() const
Returns units of the width of the mesh frame.
bool isEnabled() const
Returns whether mesh structure rendering is enabled.
Represents a mesh renderer settings for scalar datasets.
double opacity() const
Returns opacity.
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
double classificationMinimum() const
Returns min value used for creation of the color ramp shader.
DataResamplingMethod
Resampling of value from dataset.
@ NoResampling
Does not use resampling.
Qgis::RenderUnit edgeStrokeWidthUnit() const
Returns the stroke width unit used to render edges scalar dataset.
DataResamplingMethod dataResamplingMethod() const
Returns the type of interpolation to use to convert face defined datasets to values on vertices.
double classificationMaximum() const
Returns max value used for creation of the color ramp shader.
QgsInterpolatedLineWidth edgeStrokeWidth() const
Returns the stroke width used to render edges scalar dataset.
QgsMeshRendererScalarSettings scalarSettings(int groupIndex) const
Returns renderer settings.
int activeVectorDatasetGroup() const
Returns the active vector dataset group.
QgsMesh3DAveragingMethod * averagingMethod() const
Returns averaging method for conversion of 3d stacked mesh data to 2d data.
int activeScalarDatasetGroup() const
Returns the active scalar dataset group.
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
QgsMeshRendererMeshSettings edgeMeshSettings() const
Returns edge mesh renderer settings.
QgsMeshRendererMeshSettings nativeMeshSettings() const
Returns native mesh renderer settings.
QgsMeshRendererMeshSettings triangularMeshSettings() const
Returns triangular mesh renderer settings.
Represents an overview renderer settings.
bool isEnabled() const
Returns if the overview is active.
int meshResolution() const
Returns the mesh resolution i.e., the minimum size (average) of triangles in pixels.
A class to represent a 2D point.
Definition: qgspointxy.h:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:166
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
Interface for all raster shaders.
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:236
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:243
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
bool testFlag(Qgis::RenderContextFlag flag) const
Check whether a particular flag is enabled.
float devicePixelRatio() const
Returns the device pixel ratio.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QgsDoubleRange zRange() const
Returns the range of z-values which should be rendered.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr).
void record(const QString &name, double time, const QString &group="startup", const QString &id=QString())
Manually adds a profile event with the given name and total time (in seconds).
Scoped object for saving and restoring a QPainter object's state.
Raster renderer pipe for single band pseudocolor.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
int levelOfDetail() const
Returns the corresponding index of level of detail on which this mesh is associated.
QList< int > edgeIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of edges intersecting given bounding box It uses spatial indexing.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
const QVector< QgsMeshEdge > & edges() const
Returns edges.
bool contains(const QgsMesh::ElementType &type) const
Returns whether the mesh contains mesh elements of given type.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
CORE_EXPORT QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
CORE_EXPORT QSet< int > nativeVerticesFromTriangles(const QList< int > &triangleIndexes, const QVector< QgsMeshFace > &triangles)
Returns unique native vertex indexes from list of vertices of triangles.
CORE_EXPORT QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
#define QgsDebugError(str)
Definition: qgslogger.h:38
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
QVector< QgsMeshFace > faces
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.