QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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#include "moc_qgsmeshlayerrenderer.cpp"
29
30#include "qgslogger.h"
31#include "qgsmeshlayer.h"
32#include "qgspointxy.h"
34#include "qgsrastershader.h"
36#include "qgsmeshlayerutils.h"
40#include "qgsmapclippingutils.h"
41#include "qgscolorrampshader.h"
43#include "qgsapplication.h"
44#include "qgsruntimeprofiler.h"
48
50 QgsMeshLayer *layer,
51 QgsRenderContext &context )
52 : QgsMapLayerRenderer( layer->id(), &context )
53 , mIsEditable( layer->isEditable() )
54 , mLayerName( layer->name() )
55 , mFeedback( new QgsMeshLayerRendererFeedback )
56 , mRendererSettings( layer->rendererSettings() )
57 , mEnableProfile( context.flags() & Qgis::RenderContextFlag::RecordProfile )
58 , mLayerOpacity( layer->opacity() )
59{
60 QElapsedTimer timer;
61 timer.start();
62
63 // make copies for mesh data
64 // cppcheck-suppress assertWithSideEffect
65 Q_ASSERT( layer->nativeMesh() );
66 // cppcheck-suppress assertWithSideEffect
67 Q_ASSERT( layer->triangularMesh() );
68 // cppcheck-suppress assertWithSideEffect
69 Q_ASSERT( layer->rendererCache() );
70 // cppcheck-suppress assertWithSideEffect
71 Q_ASSERT( layer->dataProvider() );
72
73 mReadyToCompose = false;
74
75 // copy native mesh
76 mNativeMesh = *( layer->nativeMesh() );
77 mLayerExtent = layer->extent();
78
79 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
80 {
81 QgsMeshLayerElevationProperties *elevProp = qobject_cast<QgsMeshLayerElevationProperties *>( layer->elevationProperties() );
82
84 mElevationScale = elevProp->zScale();
85 mElevationOffset = elevProp->zOffset();
86
87 if ( !context.zRange().isInfinite() )
88 {
89 switch ( elevProp->mode() )
90 {
92 // don't need to handle anything here -- the layer renderer will never be created if the
93 // render context range doesn't match the layer's fixed elevation range
94 break;
95
97 {
98 // TODO -- filtering by mesh z values is not currently implemented
99 break;
100 }
101
103 {
104 // find the top-most group which matches the map range and parent group
105 int currentMatchingVectorGroup = -1;
106 int currentMatchingScalarGroup = -1;
107 QgsDoubleRange currentMatchingVectorRange;
108 QgsDoubleRange currentMatchingScalarRange;
109
110 const QMap<int, QgsDoubleRange > rangePerGroup = elevProp->fixedRangePerGroup();
111
112 const int activeVectorDatasetGroup = mRendererSettings.activeVectorDatasetGroup();
113 const int activeScalarDatasetGroup = mRendererSettings.activeScalarDatasetGroup();
114
115 for ( auto it = rangePerGroup.constBegin(); it != rangePerGroup.constEnd(); ++it )
116 {
117 if ( it.value().overlaps( context.zRange() ) )
118 {
119 const bool matchesVectorParentGroup = QgsMeshLayerUtils::haveSameParentQuantity( layer, QgsMeshDatasetIndex( activeVectorDatasetGroup ), QgsMeshDatasetIndex( it.key() ) );
120 const bool matchesScalarParentGroup = QgsMeshLayerUtils::haveSameParentQuantity( layer, QgsMeshDatasetIndex( activeScalarDatasetGroup ), QgsMeshDatasetIndex( it.key() ) );
121
122 if ( matchesVectorParentGroup && (
123 currentMatchingVectorRange.isInfinite()
124 || ( it.value().includeUpper() && it.value().upper() >= currentMatchingVectorRange.upper() )
125 || ( !currentMatchingVectorRange.includeUpper() && it.value().upper() >= currentMatchingVectorRange.upper() ) ) )
126 {
127 currentMatchingVectorGroup = it.key();
128 currentMatchingVectorRange = it.value();
129 }
130
131 if ( matchesScalarParentGroup && (
132 currentMatchingScalarRange.isInfinite()
133 || ( it.value().includeUpper() && it.value().upper() >= currentMatchingScalarRange.upper() )
134 || ( !currentMatchingScalarRange.includeUpper() && it.value().upper() >= currentMatchingScalarRange.upper() ) ) )
135 {
136 currentMatchingScalarGroup = it.key();
137 currentMatchingScalarRange = it.value();
138 }
139 }
140 }
141 if ( currentMatchingVectorGroup >= 0 )
142 mRendererSettings.setActiveVectorDatasetGroup( currentMatchingVectorGroup );
143 if ( currentMatchingScalarGroup >= 0 )
144 mRendererSettings.setActiveScalarDatasetGroup( currentMatchingScalarGroup );
145 }
146 }
147 }
148 }
149
150 // copy triangular mesh
151 copyTriangularMeshes( layer, context );
152
153 // copy datasets
154 copyScalarDatasetValues( layer );
155 copyVectorDatasetValues( layer );
156
157 calculateOutputSize();
158
159 QSet<QString> attrs;
160 prepareLabeling( layer, attrs );
161
163
165 {
166 QgsMeshDatasetIndex activeDatasetIndex = layer->activeScalarDatasetIndex( context );
167
168 if ( activeDatasetIndex.isValid() )
169 {
170 QgsMeshRendererScalarSettings scalarRendererSettings = mRendererSettings.scalarSettings( activeDatasetIndex.group() );
171 double previousMin = scalarRendererSettings.classificationMinimum();
172 double previousMax = scalarRendererSettings.classificationMaximum();
173
174 QgsRenderedLayerStatistics *layerStatistics = new QgsRenderedLayerStatistics( layer->id(), previousMin, previousMax );
175 layerStatistics->setBoundingBox( layer->extent() );
176
177 if ( scalarRendererSettings.extent() == Qgis::MeshRangeExtent::UpdatedCanvas &&
178 scalarRendererSettings.limits() == Qgis::MeshRangeLimit::MinimumMaximum )
179 {
180 double min, max;
181
182 bool found = layer->minimumMaximumActiveScalarDataset( context.extent(), activeDatasetIndex, min, max );
183
184 if ( found )
185 {
186 if ( previousMin != min || previousMax != max )
187 {
188 layerStatistics->setBoundingBox( context.extent() );
189 layerStatistics->setMaximum( 0, max );
190 layerStatistics->setMinimum( 0, min );
191
192 scalarRendererSettings.setClassificationMinimumMaximum( min, max );
193 mRendererSettings.setScalarSettings( activeDatasetIndex.group(), scalarRendererSettings );
194 }
195 }
196 }
197
198 appendRenderedItemDetails( layerStatistics );
199 }
200 }
201
202 mPreparationTime = timer.elapsed();
203}
204
205void QgsMeshLayerRenderer::copyTriangularMeshes( QgsMeshLayer *layer, QgsRenderContext &context )
206{
207 // handle level of details of mesh
208 const QgsMeshSimplificationSettings simplificationSettings = layer->meshSimplificationSettings();
209 if ( simplificationSettings.isEnabled() )
210 {
211 const double triangleSize = simplificationSettings.meshResolution() * context.mapToPixel().mapUnitsPerPixel();
212 mTriangularMesh = *( layer->triangularMesh( triangleSize ) );
213 mIsMeshSimplificationActive = true;
214 }
215 else
216 {
217 mTriangularMesh = *( layer->triangularMesh() );
218 }
219}
220
222{
223 return mFeedback.get();
224}
225
226void QgsMeshLayerRenderer::calculateOutputSize()
227{
228 // figure out image size
229 const QgsRenderContext &context = *renderContext();
230 const QgsRectangle extent = context.mapExtent();
231 const QgsMapToPixel mapToPixel = context.mapToPixel();
232 const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent, context.devicePixelRatio() );
233 const int width = int( screenBBox.width() );
234 const int height = int( screenBBox.height() );
235 mOutputSize = QSize( width, height );
236}
237
238void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
239{
240 QgsMeshDatasetIndex datasetIndex;
241 if ( renderContext()->isTemporal() )
242 datasetIndex = layer->activeScalarDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeScalarDatasetGroup() );
243 else
245
246 // Find out if we can use cache up to date. If yes, use it and return
247 const int datasetGroupCount = layer->datasetGroupCount();
249 QgsMeshLayerRendererCache *cache = layer->rendererCache();
250 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
251 ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
252 ( cache->mDataInterpolationMethod == method ) &&
253 ( QgsMesh3DAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
254 )
255 {
256 mScalarDatasetValues = cache->mScalarDatasetValues;
257 mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
258 mScalarDataType = cache->mScalarDataType;
259 mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
260 mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
261 return;
262 }
263
264 // Cache is not up-to-date, gather data
265 if ( datasetIndex.isValid() )
266 {
267 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex.group() );
268 mScalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
269
270 // populate scalar values
271 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mScalarDataType );
272 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
273 layer,
274 datasetIndex,
275 0,
276 count );
277
278 if ( vals.isValid() )
279 {
280 // vals could be scalar or vectors, for contour rendering we want always magnitude
281 mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
282 }
283 else
284 {
285 mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
286 }
287
288 // populate face active flag, always defined on faces
290 datasetIndex,
291 0,
292 mNativeMesh.faces.count() );
293
294 // for data on faces, there could be request to interpolate the data to vertices
296 {
298 {
300 mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
305 method
306 );
307 }
309 {
311 mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
316 method
317 );
318 }
319 }
320
321 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
322 mScalarDatasetMinimum = datasetMetadata.minimum();
323 mScalarDatasetMaximum = datasetMetadata.maximum();
324 }
325
326 // update cache
327 cache->mDatasetGroupsCount = datasetGroupCount;
328 cache->mActiveScalarDatasetIndex = datasetIndex;
329 cache->mDataInterpolationMethod = method;
330 cache->mScalarDatasetValues = mScalarDatasetValues;
331 cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
332 cache->mScalarDataType = mScalarDataType;
333 cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
334 cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
335 cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
336}
337
338
339void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
340{
341 QgsMeshDatasetIndex datasetIndex;
342 if ( renderContext()->isTemporal() )
343 datasetIndex = layer->activeVectorDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeVectorDatasetGroup() );
344 else
346
347 // Find out if we can use cache up to date. If yes, use it and return
348 const int datasetGroupCount = layer->datasetGroupCount();
349 QgsMeshLayerRendererCache *cache = layer->rendererCache();
350 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
351 ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
352 ( QgsMesh3DAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
353 )
354 {
355 mVectorDatasetValues = cache->mVectorDatasetValues;
356 mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
357 mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
358 mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
359 mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
360 mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
361 mVectorActiveFaceFlagValues = cache->mVectorActiveFaceFlagValues;
362 mVectorDataType = cache->mVectorDataType;
363 return;
364 }
365
366 // Cache is not up-to-date, gather data
367 if ( datasetIndex.isValid() )
368 {
369 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex );
370
371 const bool isScalar = metadata.isScalar();
372 if ( isScalar )
373 {
374 QgsDebugError( QStringLiteral( "Dataset has no vector values" ) );
375 }
376 else
377 {
378 mVectorDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
379
382
383 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mVectorDataType );
384 mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
385 layer,
386 datasetIndex,
387 0,
388 count );
389
391 mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
392 else
393 mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
394
395 // populate face active flag
396 mVectorActiveFaceFlagValues = layer->areFacesActive( datasetIndex, 0, mNativeMesh.faces.count() );
397
398 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
399 mVectorDatasetMagMinimum = datasetMetadata.minimum();
400 mVectorDatasetMagMaximum = datasetMetadata.maximum();
401 }
402 }
403
404 // update cache
405 cache->mDatasetGroupsCount = datasetGroupCount;
406 cache->mActiveVectorDatasetIndex = datasetIndex;
407 cache->mVectorDatasetValues = mVectorDatasetValues;
408 cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
409 cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
410 cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
411 cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
412 cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
413 cache->mVectorActiveFaceFlagValues = mVectorActiveFaceFlagValues;
414 cache->mVectorDataType = mVectorDataType;
415 cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
416}
417
419{
420 std::unique_ptr< QgsScopedRuntimeProfile > profile;
421 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
422 if ( mEnableProfile )
423 {
424 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
425 if ( mPreparationTime > 0 )
426 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
427 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
428 }
429
430 mReadyToCompose = false;
431 const QgsScopedQPainterState painterState( renderContext()->painter() );
432 if ( !mClippingRegions.empty() )
433 {
434 bool needsPainterClipPath = false;
435 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Mesh, needsPainterClipPath );
436 if ( needsPainterClipPath )
437 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
438 }
439
440 preparingProfile.reset();
441
442 renderScalarDataset();
443 mReadyToCompose = true;
444 renderMesh();
445 renderVectorDataset();
446
447 registerLabelFeatures();
448
449 return !renderContext()->renderingStopped();
450}
451
456
457void QgsMeshLayerRenderer::renderMesh()
458{
459 if ( !mRendererSettings.nativeMeshSettings().isEnabled() && !mIsEditable &&
462 return;
463
464 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
465 if ( mEnableProfile )
466 {
467 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering mesh" ), QStringLiteral( "rendering" ) );
468 }
469
470 // triangular mesh
471 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
473 {
474 renderFaceMesh(
477 trianglesInExtent );
478 }
479
480 // native mesh
481 if ( ( mRendererSettings.nativeMeshSettings().isEnabled() || mIsEditable ) &&
483 {
484 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
486
487 renderFaceMesh(
490 nativeFacesInExtent.values() );
491 }
492
493 // edge mesh
495 {
496 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( renderContext()->mapExtent() );
497 renderEdgeMesh( mRendererSettings.edgeMeshSettings(), edgesInExtent );
498 }
499}
500
501static QPainter *_painterForMeshFrame( QgsRenderContext &context, const QgsMeshRendererMeshSettings &settings )
502{
503 // Set up the render configuration options
504 QPainter *painter = context.painter();
505
506 painter->save();
507 context.setPainterFlagsUsingContext( painter );
508
509 QPen pen = painter->pen();
510 pen.setCapStyle( Qt::FlatCap );
511 pen.setJoinStyle( Qt::MiterJoin );
512
513 const double penWidth = context.convertToPainterUnits( settings.lineWidth(), settings.lineWidthUnit() );
514 pen.setWidthF( penWidth );
515 pen.setColor( settings.color() );
516 painter->setPen( pen );
517 return painter;
518}
519
520void QgsMeshLayerRenderer::renderEdgeMesh( const QgsMeshRendererMeshSettings &settings, const QList<int> &edgesInExtent )
521{
522 Q_ASSERT( settings.isEnabled() );
523
525 return;
526
527 QgsRenderContext &context = *renderContext();
528 QPainter *painter = _painterForMeshFrame( context, settings );
529
530 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
531 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
532
533 for ( const int i : edgesInExtent )
534 {
535 if ( context.renderingStopped() )
536 break;
537
538 if ( i >= edges.size() )
539 continue;
540
541 const QgsMeshEdge &edge = edges[i];
542 const int startVertexIndex = edge.first;
543 const int endVertexIndex = edge.second;
544
545 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
546 continue;
547
548 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
549 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
550 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
551 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
552 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
553 }
554 painter->restore();
555};
556
557void QgsMeshLayerRenderer::renderFaceMesh(
558 const QgsMeshRendererMeshSettings &settings,
559 const QVector<QgsMeshFace> &faces,
560 const QList<int> &facesInExtent )
561{
562 Q_ASSERT( settings.isEnabled() || mIsEditable );
563
565 return;
566
567 QgsRenderContext &context = *renderContext();
568 QPainter *painter = _painterForMeshFrame( context, settings );
569
570 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
571 QSet<QPair<int, int>> drawnEdges;
572
573 for ( const int i : facesInExtent )
574 {
575 if ( context.renderingStopped() )
576 break;
577
578 if ( i < 0 || i >= faces.count() )
579 continue;
580
581 const QgsMeshFace &face = faces[i];
582 if ( face.size() < 2 )
583 continue;
584
585 for ( int j = 0; j < face.size(); ++j )
586 {
587 const int startVertexId = face[j];
588 const int endVertexId = face[( j + 1 ) % face.size()];
589 const QPair<int, int> thisEdge( startVertexId, endVertexId );
590 const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
591 if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
592 continue;
593 drawnEdges.insert( thisEdge );
594 drawnEdges.insert( thisEdgeReversed );
595
596 const QgsMeshVertex &startVertex = vertices[startVertexId];
597 const QgsMeshVertex &endVertex = vertices[endVertexId];
598 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
599 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
600 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
601 }
602 }
603
604 painter->restore();
605}
606
607void QgsMeshLayerRenderer::renderScalarDataset()
608{
609 if ( mScalarDatasetValues.isEmpty() )
610 return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
611
612 if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
613 return; // only NODATA values
614
615 const int groupIndex = mRendererSettings.activeScalarDatasetGroup();
616 if ( groupIndex < 0 )
617 return; // no shader
618
619 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
620 if ( mEnableProfile )
621 {
622 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering scalar datasets" ), QStringLiteral( "rendering" ) );
623 }
624
625 const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( groupIndex );
626
629 {
630 renderScalarDatasetOnFaces( scalarSettings );
631 }
632
635 {
636 renderScalarDatasetOnEdges( scalarSettings );
637 }
638}
639
640void QgsMeshLayerRenderer::renderScalarDatasetOnEdges( const QgsMeshRendererScalarSettings &scalarSettings )
641{
642 QgsRenderContext &context = *renderContext();
643 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
644 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
645 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( context.mapExtent() );
646
647 QgsInterpolatedLineRenderer edgePlotter;
648 edgePlotter.setInterpolatedColor( QgsInterpolatedLineColor( scalarSettings.colorRampShader() ) );
649 edgePlotter.setInterpolatedWidth( QgsInterpolatedLineWidth( scalarSettings.edgeStrokeWidth() ) );
650 edgePlotter.setWidthUnit( scalarSettings.edgeStrokeWidthUnit() );
651
652 for ( const int i : edgesInExtent )
653 {
654 if ( context.renderingStopped() )
655 break;
656
657 if ( i >= edges.size() )
658 continue;
659
660 const QgsMeshEdge &edge = edges[i];
661 const int startVertexIndex = edge.first;
662 const int endVertexIndex = edge.second;
663
664 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
665 continue;
666
667 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
668 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
669
671 {
672 edgePlotter.render( mScalarDatasetValues[i], mScalarDatasetValues[i], startVertex, endVertex, context );
673 }
674 else
675 {
676 edgePlotter.render( mScalarDatasetValues[startVertexIndex], mScalarDatasetValues[endVertexIndex], startVertex, endVertex, context );
677 }
678 }
679}
680
681QColor QgsMeshLayerRenderer::colorAt( QgsColorRampShader *shader, double val ) const
682{
683 int r, g, b, a;
684 if ( shader->shade( val, &r, &g, &b, &a ) )
685 {
686 return QColor( r, g, b, a );
687 }
688 return QColor();
689}
690
691QgsPointXY QgsMeshLayerRenderer::fractionPoint( const QgsPointXY &p1, const QgsPointXY &p2, double fraction ) const
692{
693 const QgsPointXY pt( p1.x() + fraction * ( p2.x() - p1.x() ),
694 p1.y() + fraction * ( p2.y() - p1.y() ) );
695 return pt;
696}
697
698void QgsMeshLayerRenderer::renderScalarDatasetOnFaces( const QgsMeshRendererScalarSettings &scalarSettings )
699{
700 QgsRenderContext &context = *renderContext();
701
702 QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
704 sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
705 QgsMeshLayerInterpolator interpolator( mTriangularMesh,
709 context,
710 mOutputSize );
711 interpolator.setSpatialIndexActive( mIsMeshSimplificationActive );
712 interpolator.setElevationMapSettings( mRenderElevationMap, mElevationScale, mElevationOffset );
713 QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
714 renderer.setClassificationMin( scalarSettings.classificationMinimum() );
715 renderer.setClassificationMax( scalarSettings.classificationMaximum() );
716 renderer.setOpacity( scalarSettings.opacity() );
717
718 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
719 QImage img = bl->image();
720 img.setDevicePixelRatio( context.devicePixelRatio() );
721
722 context.painter()->drawImage( 0, 0, img );
723}
724
725void QgsMeshLayerRenderer::renderVectorDataset()
726{
727 const int groupIndex = mRendererSettings.activeVectorDatasetGroup();
728 if ( groupIndex < 0 )
729 return;
730
732 return; // no data at all
733
734 if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
735 return; // only NODATA values
736
737 if ( !( mVectorDatasetMagMaximum > 0 ) )
738 return; //all vector are null vector
739
740 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
741 if ( mEnableProfile )
742 {
743 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering vector datasets" ), QStringLiteral( "rendering" ) );
744 }
745
746 std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
754 mRendererSettings.vectorSettings( groupIndex ),
755 *renderContext(),
757 mFeedback.get(),
758 mOutputSize ) );
759
760 if ( renderer )
761 renderer->draw();
762}
763
764void QgsMeshLayerRenderer::prepareLabeling( QgsMeshLayer *layer, QSet<QString> &attributeNames )
765{
766 QgsRenderContext &context = *renderContext();
767
768 if ( QgsLabelingEngine *engine = context.labelingEngine() )
769 {
770 if ( layer->labelsEnabled() )
771 {
772 mLabelProvider = layer->labeling()->provider( layer );
773 if ( mLabelProvider )
774 {
775 auto c = context.expressionContext();
776
778 c.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mNativeMesh ) );
779 context.setExpressionContext( c );
780
781 engine->addProvider( mLabelProvider );
782 if ( !mLabelProvider->prepare( context, attributeNames ) )
783 {
784 engine->removeProvider( mLabelProvider );
785 mLabelProvider = nullptr; // deleted by engine
786 }
787 }
788 }
789 }
790}
791
792void QgsMeshLayerRenderer::registerLabelFeatures()
793{
794 if ( !mLabelProvider )
795 return;
796
797 QgsRenderContext &context = *renderContext();
798
799 QgsExpressionContextScope *scope = context.expressionContext().activeScopeForVariable( QStringLiteral( "_native_mesh" ) );
800
801 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
802
803 if ( mLabelProvider->labelFaces() )
804 {
806 return;
807 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
809
810 for ( const int i : nativeFacesInExtent )
811 {
812 if ( context.renderingStopped() )
813 break;
814
815 if ( i < 0 || i >= mNativeMesh.faces.count() )
816 continue;
817
818 scope->setVariable( QStringLiteral( "_mesh_face_index" ), i, false );
819
820 QgsFeature f( i );
821 QgsGeometry geom = QgsMeshUtils::toGeometry( mNativeMesh.face( i ), mNativeMesh.vertices );
822 f.setGeometry( geom );
823 mLabelProvider->registerFeature( f, context );
824 }
825 }
826 else
827 {
829 return;
830 const QSet<int> nativeVerticesInExtent = QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent,
832
833 for ( const int i : nativeVerticesInExtent )
834 {
835 if ( context.renderingStopped() )
836 break;
837
838 if ( i < 0 || i >= mNativeMesh.vertexCount() )
839 continue;
840
841 scope->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
842
843 QgsFeature f( i );
844 QgsGeometry geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
845 f.setGeometry( geom );
846 mLabelProvider->registerFeature( f, context );
847 }
848 }
849}
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ MinimumMaximum
Real min-max values.
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ RenderPreviewJob
Render is a 'canvas preview' render, and shortcuts should be taken to ensure fast rendering.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ FromVertices
Elevation should be taken from mesh vertices.
@ FixedRangePerGroup
Layer has a fixed (manually specified) elevation range per group.
@ 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 an new RGBA value based on one input value.
QgsRange which stores a range of double values.
Definition qgsrange.h:231
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.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
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.
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.
void appendRenderedItemDetails(QgsRenderedItemDetails *details)
Appends the details of a rendered item to the renderer.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
QString id
Definition qgsmaplayer.h:79
Perform transforms between map coordinates and device coordinates.
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.
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.
@ DataOnEdges
Data is defined on edges.
@ DataOnFaces
Data is defined on faces.
@ DataOnVertices
Data is defined on vertices.
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.
QMap< int, QgsDoubleRange > fixedRangePerGroup() const
Returns the fixed elevation range for each group.
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.
QgsRectangle extent() const override
Returns the extent of the layer.
QgsMeshDatasetIndex activeScalarDatasetIndex(QgsRenderContext &rendererContext)
Returns current active scalar dataset index for current renderer context.
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange, int group=-1) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
int datasetGroupCount() const
Returns the dataset groups count handle by the layer.
const QgsAbstractMeshLayerLabeling * labeling() const
Access to const labeling configuration.
QgsMeshDatasetIndex staticVectorDatasetIndex(int group=-1) const
Returns the static vector dataset index that is rendered if the temporal properties is not active.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
bool minimumMaximumActiveScalarDataset(const QgsRectangle &extent, const QgsMeshDatasetIndex &datasetIndex, double &min, double &max)
Extracts minimum and maximum value for active scalar dataset on mesh faces.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsMeshSimplificationSettings meshSimplificationSettings() const
Returns mesh simplification settings.
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
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.
QgsMeshDatasetIndex activeScalarDatasetAtTime(const QgsDateTimeRange &timeRange, int group=-1) const
Returns dataset index from active scalar group depending on the time range.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetIndex staticScalarDatasetIndex(int group=-1) const
Returns the static scalar dataset index that is rendered if the temporal properties is not active.
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.
void setClassificationMinimumMaximum(double minimum, double maximum)
Sets min/max values used for creation of the color ramp shader.
Qgis::MeshRangeExtent extent() const
Returns the mesh extent for minimum maximum calculation.
double opacity() const
Returns opacity.
Qgis::MeshRangeLimit limits() const
Returns the range limits type for minimum maximum calculation.
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.
void setActiveVectorDatasetGroup(int activeVectorDatasetGroup)
Sets the active vector dataset group.
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.
void setActiveScalarDatasetGroup(int activeScalarDatasetGroup)
Sets the active scalar dataset group.
QgsMeshRendererMeshSettings edgeMeshSettings() const
Returns edge mesh renderer settings.
QgsMeshRendererMeshSettings nativeMeshSettings() const
Returns native mesh renderer settings.
void setScalarSettings(int groupIndex, const QgsMeshRendererScalarSettings &settings)
Sets new 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
double x
Definition qgspointxy.h:63
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:165
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
bool includeUpper() const
Returns true if the upper bound is inclusive, or false if the upper bound is exclusive.
Definition qgsrange.h:101
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:85
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.
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.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
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 setBoundingBox(const QgsRectangle &bounds)
Sets the bounding box of the item (in map units).
Contains computed statistics for a layer render.
void setMaximum(QList< double > &maximum)
Sets the maximum values of the computed statistics.
void setMinimum(QList< double > &minimum)
Sets the minimum values of the computed statistics.
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.
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:6066
#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.
Mesh - vertices, edges and faces.
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.