QGIS API Documentation 3.41.0-Master (02257426e5a)
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 && !( context.flags() & Qgis::RenderContextFlag::Render3DMap ) )
166 {
167 const QgsMeshDatasetIndex activeDatasetIndex = layer->activeScalarDatasetIndex( context );
168
169 if ( activeDatasetIndex.isValid() )
170 {
171 QgsMeshRendererScalarSettings scalarRendererSettings = mRendererSettings.scalarSettings( activeDatasetIndex.group() );
172 const double previousMin = scalarRendererSettings.classificationMinimum();
173 const double previousMax = scalarRendererSettings.classificationMaximum();
174
175 if ( scalarRendererSettings.extent() == Qgis::MeshRangeExtent::UpdatedCanvas &&
176 scalarRendererSettings.limits() == Qgis::MeshRangeLimit::MinimumMaximum )
177 {
178 double min, max;
179
180 const bool found = layer->minimumMaximumActiveScalarDataset( context.extent(), activeDatasetIndex, min, max );
181
182 if ( found )
183 {
184 if ( previousMin != min || previousMax != max )
185 {
186
187 scalarRendererSettings.setClassificationMinimumMaximum( min, max );
188 mRendererSettings.setScalarSettings( activeDatasetIndex.group(), scalarRendererSettings );
189
190 QgsRenderedLayerStatistics *layerStatistics = new QgsRenderedLayerStatistics( layer->id(), previousMin, previousMax );
191
192 layerStatistics->setBoundingBox( context.extent() );
193 layerStatistics->setMaximum( 0, max );
194 layerStatistics->setMinimum( 0, min );
195
196 appendRenderedItemDetails( layerStatistics );
197 }
198 }
199 }
200 }
201 }
202
203 mPreparationTime = timer.elapsed();
204}
205
206void QgsMeshLayerRenderer::copyTriangularMeshes( QgsMeshLayer *layer, QgsRenderContext &context )
207{
208 // handle level of details of mesh
209 const QgsMeshSimplificationSettings simplificationSettings = layer->meshSimplificationSettings();
210 if ( simplificationSettings.isEnabled() )
211 {
212 const double triangleSize = simplificationSettings.meshResolution() * context.mapToPixel().mapUnitsPerPixel();
213 mTriangularMesh = *( layer->triangularMesh( triangleSize ) );
214 mIsMeshSimplificationActive = true;
215 }
216 else
217 {
218 mTriangularMesh = *( layer->triangularMesh() );
219 }
220}
221
223{
224 return mFeedback.get();
225}
226
227void QgsMeshLayerRenderer::calculateOutputSize()
228{
229 // figure out image size
230 const QgsRenderContext &context = *renderContext();
231 const QgsRectangle extent = context.mapExtent();
232 const QgsMapToPixel mapToPixel = context.mapToPixel();
233 const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent, context.devicePixelRatio() );
234 const int width = int( screenBBox.width() );
235 const int height = int( screenBBox.height() );
236 mOutputSize = QSize( width, height );
237}
238
239void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
240{
241 QgsMeshDatasetIndex datasetIndex;
242 if ( renderContext()->isTemporal() )
243 datasetIndex = layer->activeScalarDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeScalarDatasetGroup() );
244 else
246
247 // Find out if we can use cache up to date. If yes, use it and return
248 const int datasetGroupCount = layer->datasetGroupCount();
250 QgsMeshLayerRendererCache *cache = layer->rendererCache();
251 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
252 ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
253 ( cache->mDataInterpolationMethod == method ) &&
254 ( QgsMesh3DAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
255 )
256 {
257 mScalarDatasetValues = cache->mScalarDatasetValues;
258 mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
259 mScalarDataType = cache->mScalarDataType;
260 mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
261 mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
262 return;
263 }
264
265 // Cache is not up-to-date, gather data
266 if ( datasetIndex.isValid() )
267 {
268 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex.group() );
269 mScalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
270
271 // populate scalar values
272 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mScalarDataType );
273 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
274 layer,
275 datasetIndex,
276 0,
277 count );
278
279 if ( vals.isValid() )
280 {
281 // vals could be scalar or vectors, for contour rendering we want always magnitude
282 mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
283 }
284 else
285 {
286 mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
287 }
288
289 // populate face active flag, always defined on faces
291 datasetIndex,
292 0,
293 mNativeMesh.faces.count() );
294
295 // for data on faces, there could be request to interpolate the data to vertices
297 {
299 {
301 mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
306 method
307 );
308 }
310 {
312 mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
317 method
318 );
319 }
320 }
321
322 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
323 mScalarDatasetMinimum = datasetMetadata.minimum();
324 mScalarDatasetMaximum = datasetMetadata.maximum();
325 }
326
327 // update cache
328 cache->mDatasetGroupsCount = datasetGroupCount;
329 cache->mActiveScalarDatasetIndex = datasetIndex;
330 cache->mDataInterpolationMethod = method;
331 cache->mScalarDatasetValues = mScalarDatasetValues;
332 cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
333 cache->mScalarDataType = mScalarDataType;
334 cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
335 cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
336 cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
337}
338
339
340void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
341{
342 QgsMeshDatasetIndex datasetIndex;
343 if ( renderContext()->isTemporal() )
344 datasetIndex = layer->activeVectorDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeVectorDatasetGroup() );
345 else
347
348 // Find out if we can use cache up to date. If yes, use it and return
349 const int datasetGroupCount = layer->datasetGroupCount();
350 QgsMeshLayerRendererCache *cache = layer->rendererCache();
351 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
352 ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
353 ( QgsMesh3DAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
354 )
355 {
356 mVectorDatasetValues = cache->mVectorDatasetValues;
357 mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
358 mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
359 mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
360 mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
361 mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
362 mVectorActiveFaceFlagValues = cache->mVectorActiveFaceFlagValues;
363 mVectorDataType = cache->mVectorDataType;
364 return;
365 }
366
367 // Cache is not up-to-date, gather data
368 if ( datasetIndex.isValid() )
369 {
370 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex );
371
372 const bool isScalar = metadata.isScalar();
373 if ( isScalar )
374 {
375 QgsDebugError( QStringLiteral( "Dataset has no vector values" ) );
376 }
377 else
378 {
379 mVectorDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
380
383
384 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mVectorDataType );
385 mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
386 layer,
387 datasetIndex,
388 0,
389 count );
390
392 mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
393 else
394 mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
395
396 // populate face active flag
397 mVectorActiveFaceFlagValues = layer->areFacesActive( datasetIndex, 0, mNativeMesh.faces.count() );
398
399 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
400 mVectorDatasetMagMinimum = datasetMetadata.minimum();
401 mVectorDatasetMagMaximum = datasetMetadata.maximum();
402 }
403 }
404
405 // update cache
406 cache->mDatasetGroupsCount = datasetGroupCount;
407 cache->mActiveVectorDatasetIndex = datasetIndex;
408 cache->mVectorDatasetValues = mVectorDatasetValues;
409 cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
410 cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
411 cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
412 cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
413 cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
414 cache->mVectorActiveFaceFlagValues = mVectorActiveFaceFlagValues;
415 cache->mVectorDataType = mVectorDataType;
416 cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
417}
418
420{
421 std::unique_ptr< QgsScopedRuntimeProfile > profile;
422 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
423 if ( mEnableProfile )
424 {
425 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
426 if ( mPreparationTime > 0 )
427 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
428 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
429 }
430
431 mReadyToCompose = false;
432 const QgsScopedQPainterState painterState( renderContext()->painter() );
433 if ( !mClippingRegions.empty() )
434 {
435 bool needsPainterClipPath = false;
436 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Mesh, needsPainterClipPath );
437 if ( needsPainterClipPath )
438 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
439 }
440
441 preparingProfile.reset();
442
443 renderScalarDataset();
444 mReadyToCompose = true;
445 renderMesh();
446 renderVectorDataset();
447
448 registerLabelFeatures();
449
450 return !renderContext()->renderingStopped();
451}
452
457
458void QgsMeshLayerRenderer::renderMesh()
459{
460 if ( !mRendererSettings.nativeMeshSettings().isEnabled() && !mIsEditable &&
463 return;
464
465 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
466 if ( mEnableProfile )
467 {
468 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering mesh" ), QStringLiteral( "rendering" ) );
469 }
470
471 // triangular mesh
472 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
474 {
475 renderFaceMesh(
478 trianglesInExtent );
479 }
480
481 // native mesh
482 if ( ( mRendererSettings.nativeMeshSettings().isEnabled() || mIsEditable ) &&
484 {
485 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
487
488 renderFaceMesh(
491 nativeFacesInExtent.values() );
492 }
493
494 // edge mesh
496 {
497 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( renderContext()->mapExtent() );
498 renderEdgeMesh( mRendererSettings.edgeMeshSettings(), edgesInExtent );
499 }
500}
501
502static QPainter *_painterForMeshFrame( QgsRenderContext &context, const QgsMeshRendererMeshSettings &settings )
503{
504 // Set up the render configuration options
505 QPainter *painter = context.painter();
506
507 painter->save();
508 context.setPainterFlagsUsingContext( painter );
509
510 QPen pen = painter->pen();
511 pen.setCapStyle( Qt::FlatCap );
512 pen.setJoinStyle( Qt::MiterJoin );
513
514 const double penWidth = context.convertToPainterUnits( settings.lineWidth(), settings.lineWidthUnit() );
515 pen.setWidthF( penWidth );
516 pen.setColor( settings.color() );
517 painter->setPen( pen );
518 return painter;
519}
520
521void QgsMeshLayerRenderer::renderEdgeMesh( const QgsMeshRendererMeshSettings &settings, const QList<int> &edgesInExtent )
522{
523 Q_ASSERT( settings.isEnabled() );
524
526 return;
527
528 QgsRenderContext &context = *renderContext();
529 QPainter *painter = _painterForMeshFrame( context, settings );
530
531 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
532 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
533
534 for ( const int i : edgesInExtent )
535 {
536 if ( context.renderingStopped() )
537 break;
538
539 if ( i >= edges.size() )
540 continue;
541
542 const QgsMeshEdge &edge = edges[i];
543 const int startVertexIndex = edge.first;
544 const int endVertexIndex = edge.second;
545
546 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
547 continue;
548
549 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
550 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
551 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
552 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
553 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
554 }
555 painter->restore();
556};
557
558void QgsMeshLayerRenderer::renderFaceMesh(
559 const QgsMeshRendererMeshSettings &settings,
560 const QVector<QgsMeshFace> &faces,
561 const QList<int> &facesInExtent )
562{
563 Q_ASSERT( settings.isEnabled() || mIsEditable );
564
566 return;
567
568 QgsRenderContext &context = *renderContext();
569 QPainter *painter = _painterForMeshFrame( context, settings );
570
571 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
572 QSet<QPair<int, int>> drawnEdges;
573
574 for ( const int i : facesInExtent )
575 {
576 if ( context.renderingStopped() )
577 break;
578
579 if ( i < 0 || i >= faces.count() )
580 continue;
581
582 const QgsMeshFace &face = faces[i];
583 if ( face.size() < 2 )
584 continue;
585
586 for ( int j = 0; j < face.size(); ++j )
587 {
588 const int startVertexId = face[j];
589 const int endVertexId = face[( j + 1 ) % face.size()];
590 const QPair<int, int> thisEdge( startVertexId, endVertexId );
591 const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
592 if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
593 continue;
594 drawnEdges.insert( thisEdge );
595 drawnEdges.insert( thisEdgeReversed );
596
597 const QgsMeshVertex &startVertex = vertices[startVertexId];
598 const QgsMeshVertex &endVertex = vertices[endVertexId];
599 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
600 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
601 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
602 }
603 }
604
605 painter->restore();
606}
607
608void QgsMeshLayerRenderer::renderScalarDataset()
609{
610 if ( mScalarDatasetValues.isEmpty() )
611 return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
612
613 if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
614 return; // only NODATA values
615
616 const int groupIndex = mRendererSettings.activeScalarDatasetGroup();
617 if ( groupIndex < 0 )
618 return; // no shader
619
620 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
621 if ( mEnableProfile )
622 {
623 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering scalar datasets" ), QStringLiteral( "rendering" ) );
624 }
625
626 const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( groupIndex );
627
630 {
631 renderScalarDatasetOnFaces( scalarSettings );
632 }
633
636 {
637 renderScalarDatasetOnEdges( scalarSettings );
638 }
639}
640
641void QgsMeshLayerRenderer::renderScalarDatasetOnEdges( const QgsMeshRendererScalarSettings &scalarSettings )
642{
643 QgsRenderContext &context = *renderContext();
644 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
645 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
646 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( context.mapExtent() );
647
648 QgsInterpolatedLineRenderer edgePlotter;
649 edgePlotter.setInterpolatedColor( QgsInterpolatedLineColor( scalarSettings.colorRampShader() ) );
650 edgePlotter.setInterpolatedWidth( QgsInterpolatedLineWidth( scalarSettings.edgeStrokeWidth() ) );
651 edgePlotter.setWidthUnit( scalarSettings.edgeStrokeWidthUnit() );
652
653 for ( const int i : edgesInExtent )
654 {
655 if ( context.renderingStopped() )
656 break;
657
658 if ( i >= edges.size() )
659 continue;
660
661 const QgsMeshEdge &edge = edges[i];
662 const int startVertexIndex = edge.first;
663 const int endVertexIndex = edge.second;
664
665 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
666 continue;
667
668 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
669 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
670
672 {
673 edgePlotter.render( mScalarDatasetValues[i], mScalarDatasetValues[i], startVertex, endVertex, context );
674 }
675 else
676 {
677 edgePlotter.render( mScalarDatasetValues[startVertexIndex], mScalarDatasetValues[endVertexIndex], startVertex, endVertex, context );
678 }
679 }
680}
681
682QColor QgsMeshLayerRenderer::colorAt( QgsColorRampShader *shader, double val ) const
683{
684 int r, g, b, a;
685 if ( shader->shade( val, &r, &g, &b, &a ) )
686 {
687 return QColor( r, g, b, a );
688 }
689 return QColor();
690}
691
692QgsPointXY QgsMeshLayerRenderer::fractionPoint( const QgsPointXY &p1, const QgsPointXY &p2, double fraction ) const
693{
694 const QgsPointXY pt( p1.x() + fraction * ( p2.x() - p1.x() ),
695 p1.y() + fraction * ( p2.y() - p1.y() ) );
696 return pt;
697}
698
699void QgsMeshLayerRenderer::renderScalarDatasetOnFaces( const QgsMeshRendererScalarSettings &scalarSettings )
700{
701 QgsRenderContext &context = *renderContext();
702
703 QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
705 sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
706 QgsMeshLayerInterpolator interpolator( mTriangularMesh,
710 context,
711 mOutputSize );
712 interpolator.setSpatialIndexActive( mIsMeshSimplificationActive );
713 interpolator.setElevationMapSettings( mRenderElevationMap, mElevationScale, mElevationOffset );
714 QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
715 renderer.setClassificationMin( scalarSettings.classificationMinimum() );
716 renderer.setClassificationMax( scalarSettings.classificationMaximum() );
717 renderer.setOpacity( scalarSettings.opacity() );
718
719 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
720 QImage img = bl->image();
721 img.setDevicePixelRatio( context.devicePixelRatio() );
722
723 context.painter()->drawImage( 0, 0, img );
724}
725
726void QgsMeshLayerRenderer::renderVectorDataset()
727{
728 const int groupIndex = mRendererSettings.activeVectorDatasetGroup();
729 if ( groupIndex < 0 )
730 return;
731
733 return; // no data at all
734
735 if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
736 return; // only NODATA values
737
738 if ( !( mVectorDatasetMagMaximum > 0 ) )
739 return; //all vector are null vector
740
741 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
742 if ( mEnableProfile )
743 {
744 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering vector datasets" ), QStringLiteral( "rendering" ) );
745 }
746
747 std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
755 mRendererSettings.vectorSettings( groupIndex ),
756 *renderContext(),
758 mFeedback.get(),
759 mOutputSize ) );
760
761 if ( renderer )
762 renderer->draw();
763}
764
765void QgsMeshLayerRenderer::prepareLabeling( QgsMeshLayer *layer, QSet<QString> &attributeNames )
766{
767 QgsRenderContext &context = *renderContext();
768
769 if ( QgsLabelingEngine *engine = context.labelingEngine() )
770 {
771 if ( layer->labelsEnabled() )
772 {
773 mLabelProvider = layer->labeling()->provider( layer );
774 if ( mLabelProvider )
775 {
776 auto c = context.expressionContext();
777
779 c.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mNativeMesh ) );
780 context.setExpressionContext( c );
781
782 engine->addProvider( mLabelProvider );
783 if ( !mLabelProvider->prepare( context, attributeNames ) )
784 {
785 engine->removeProvider( mLabelProvider );
786 mLabelProvider = nullptr; // deleted by engine
787 }
788 }
789 }
790 }
791}
792
793void QgsMeshLayerRenderer::registerLabelFeatures()
794{
795 if ( !mLabelProvider )
796 return;
797
798 QgsRenderContext &context = *renderContext();
799
800 QgsExpressionContextScope *scope = context.expressionContext().activeScopeForVariable( QStringLiteral( "_native_mesh" ) );
801
802 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
803
804 if ( mLabelProvider->labelFaces() )
805 {
807 return;
808 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
810
811 for ( const int i : nativeFacesInExtent )
812 {
813 if ( context.renderingStopped() )
814 break;
815
816 if ( i < 0 || i >= mNativeMesh.faces.count() )
817 continue;
818
819 scope->setVariable( QStringLiteral( "_mesh_face_index" ), i, false );
820
821 QgsFeature f( i );
822 QgsGeometry geom = QgsMeshUtils::toGeometry( mNativeMesh.face( i ), mNativeMesh.vertices );
823 f.setGeometry( geom );
824 mLabelProvider->registerFeature( f, context );
825 }
826 }
827 else
828 {
830 return;
831 const QSet<int> nativeVerticesInExtent = QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent,
833
834 for ( const int i : nativeVerticesInExtent )
835 {
836 if ( context.renderingStopped() )
837 break;
838
839 if ( i < 0 || i >= mNativeMesh.vertexCount() )
840 continue;
841
842 scope->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
843
844 QgsFeature f( i );
845 QgsGeometry geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
846 f.setGeometry( geom );
847 mLabelProvider->registerFeature( f, context );
848 }
849 }
850}
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.
@ Render3DMap
Render is for a 3D map.
@ 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:237
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:291
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).
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
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:6091
#define QgsDebugError(str)
Definition qgslogger.h:40
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.