QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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"
47
49 QgsMeshLayer *layer,
50 QgsRenderContext &context )
51 : QgsMapLayerRenderer( layer->id(), &context )
52 , mIsEditable( layer->isEditable() )
53 , mLayerName( layer->name() )
54 , mFeedback( new QgsMeshLayerRendererFeedback )
55 , mRendererSettings( layer->rendererSettings() )
56 , mEnableProfile( context.flags() & Qgis::RenderContextFlag::RecordProfile )
57 , mLayerOpacity( layer->opacity() )
58{
59 QElapsedTimer timer;
60 timer.start();
61
62 // make copies for mesh data
63 // cppcheck-suppress assertWithSideEffect
64 Q_ASSERT( layer->nativeMesh() );
65 // cppcheck-suppress assertWithSideEffect
66 Q_ASSERT( layer->triangularMesh() );
67 // cppcheck-suppress assertWithSideEffect
68 Q_ASSERT( layer->rendererCache() );
69 // cppcheck-suppress assertWithSideEffect
70 Q_ASSERT( layer->dataProvider() );
71
72 mReadyToCompose = false;
73
74 // copy native mesh
75 mNativeMesh = *( layer->nativeMesh() );
76 mLayerExtent = layer->extent();
77
78 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
79 {
80 QgsMeshLayerElevationProperties *elevProp = qobject_cast<QgsMeshLayerElevationProperties *>( layer->elevationProperties() );
81
83 mElevationScale = elevProp->zScale();
84 mElevationOffset = elevProp->zOffset();
85
86 if ( !context.zRange().isInfinite() )
87 {
88 switch ( elevProp->mode() )
89 {
91 // don't need to handle anything here -- the layer renderer will never be created if the
92 // render context range doesn't match the layer's fixed elevation range
93 break;
94
96 {
97 // TODO -- filtering by mesh z values is not currently implemented
98 break;
99 }
100
102 {
103 // find the top-most group which matches the map range and parent group
104 int currentMatchingVectorGroup = -1;
105 int currentMatchingScalarGroup = -1;
106 QgsDoubleRange currentMatchingVectorRange;
107 QgsDoubleRange currentMatchingScalarRange;
108
109 const QMap<int, QgsDoubleRange > rangePerGroup = elevProp->fixedRangePerGroup();
110
111 const int activeVectorDatasetGroup = mRendererSettings.activeVectorDatasetGroup();
112 const int activeScalarDatasetGroup = mRendererSettings.activeScalarDatasetGroup();
113
114 for ( auto it = rangePerGroup.constBegin(); it != rangePerGroup.constEnd(); ++it )
115 {
116 if ( it.value().overlaps( context.zRange() ) )
117 {
118 const bool matchesVectorParentGroup = QgsMeshLayerUtils::haveSameParentQuantity( layer, QgsMeshDatasetIndex( activeVectorDatasetGroup ), QgsMeshDatasetIndex( it.key() ) );
119 const bool matchesScalarParentGroup = QgsMeshLayerUtils::haveSameParentQuantity( layer, QgsMeshDatasetIndex( activeScalarDatasetGroup ), QgsMeshDatasetIndex( it.key() ) );
120
121 if ( matchesVectorParentGroup && (
122 currentMatchingVectorRange.isInfinite()
123 || ( it.value().includeUpper() && it.value().upper() >= currentMatchingVectorRange.upper() )
124 || ( !currentMatchingVectorRange.includeUpper() && it.value().upper() >= currentMatchingVectorRange.upper() ) ) )
125 {
126 currentMatchingVectorGroup = it.key();
127 currentMatchingVectorRange = it.value();
128 }
129
130 if ( matchesScalarParentGroup && (
131 currentMatchingScalarRange.isInfinite()
132 || ( it.value().includeUpper() && it.value().upper() >= currentMatchingScalarRange.upper() )
133 || ( !currentMatchingScalarRange.includeUpper() && it.value().upper() >= currentMatchingScalarRange.upper() ) ) )
134 {
135 currentMatchingScalarGroup = it.key();
136 currentMatchingScalarRange = it.value();
137 }
138 }
139 }
140 if ( currentMatchingVectorGroup >= 0 )
141 mRendererSettings.setActiveVectorDatasetGroup( currentMatchingVectorGroup );
142 if ( currentMatchingScalarGroup >= 0 )
143 mRendererSettings.setActiveScalarDatasetGroup( currentMatchingScalarGroup );
144 }
145 }
146 }
147 }
148
149 // copy triangular mesh
150 copyTriangularMeshes( layer, context );
151
152 // copy datasets
153 copyScalarDatasetValues( layer );
154 copyVectorDatasetValues( layer );
155
156 calculateOutputSize();
157
158 QSet<QString> attrs;
159 prepareLabeling( layer, attrs );
160
162
163 mPreparationTime = timer.elapsed();
164}
165
166void QgsMeshLayerRenderer::copyTriangularMeshes( QgsMeshLayer *layer, QgsRenderContext &context )
167{
168 // handle level of details of mesh
169 const QgsMeshSimplificationSettings simplificationSettings = layer->meshSimplificationSettings();
170 if ( simplificationSettings.isEnabled() )
171 {
172 const double triangleSize = simplificationSettings.meshResolution() * context.mapToPixel().mapUnitsPerPixel();
173 mTriangularMesh = *( layer->triangularMesh( triangleSize ) );
174 mIsMeshSimplificationActive = true;
175 }
176 else
177 {
178 mTriangularMesh = *( layer->triangularMesh() );
179 }
180}
181
183{
184 return mFeedback.get();
185}
186
187void QgsMeshLayerRenderer::calculateOutputSize()
188{
189 // figure out image size
190 const QgsRenderContext &context = *renderContext();
191 const QgsRectangle extent = context.mapExtent();
192 const QgsMapToPixel mapToPixel = context.mapToPixel();
193 const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent, context.devicePixelRatio() );
194 const int width = int( screenBBox.width() );
195 const int height = int( screenBBox.height() );
196 mOutputSize = QSize( width, height );
197}
198
199void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
200{
201 QgsMeshDatasetIndex datasetIndex;
202 if ( renderContext()->isTemporal() )
203 datasetIndex = layer->activeScalarDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeScalarDatasetGroup() );
204 else
206
207 // Find out if we can use cache up to date. If yes, use it and return
208 const int datasetGroupCount = layer->datasetGroupCount();
210 QgsMeshLayerRendererCache *cache = layer->rendererCache();
211 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
212 ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
213 ( cache->mDataInterpolationMethod == method ) &&
214 ( QgsMesh3DAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
215 )
216 {
217 mScalarDatasetValues = cache->mScalarDatasetValues;
218 mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
219 mScalarDataType = cache->mScalarDataType;
220 mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
221 mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
222 return;
223 }
224
225 // Cache is not up-to-date, gather data
226 if ( datasetIndex.isValid() )
227 {
228 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex.group() );
229 mScalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
230
231 // populate scalar values
232 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mScalarDataType );
233 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
234 layer,
235 datasetIndex,
236 0,
237 count );
238
239 if ( vals.isValid() )
240 {
241 // vals could be scalar or vectors, for contour rendering we want always magnitude
242 mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
243 }
244 else
245 {
246 mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
247 }
248
249 // populate face active flag, always defined on faces
251 datasetIndex,
252 0,
253 mNativeMesh.faces.count() );
254
255 // for data on faces, there could be request to interpolate the data to vertices
257 {
259 {
261 mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
266 method
267 );
268 }
270 {
272 mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
277 method
278 );
279 }
280 }
281
282 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
283 mScalarDatasetMinimum = datasetMetadata.minimum();
284 mScalarDatasetMaximum = datasetMetadata.maximum();
285 }
286
287 // update cache
288 cache->mDatasetGroupsCount = datasetGroupCount;
289 cache->mActiveScalarDatasetIndex = datasetIndex;
290 cache->mDataInterpolationMethod = method;
291 cache->mScalarDatasetValues = mScalarDatasetValues;
292 cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
293 cache->mScalarDataType = mScalarDataType;
294 cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
295 cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
296 cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
297}
298
299
300void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
301{
302 QgsMeshDatasetIndex datasetIndex;
303 if ( renderContext()->isTemporal() )
304 datasetIndex = layer->activeVectorDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeVectorDatasetGroup() );
305 else
307
308 // Find out if we can use cache up to date. If yes, use it and return
309 const int datasetGroupCount = layer->datasetGroupCount();
310 QgsMeshLayerRendererCache *cache = layer->rendererCache();
311 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
312 ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
313 ( QgsMesh3DAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
314 )
315 {
316 mVectorDatasetValues = cache->mVectorDatasetValues;
317 mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
318 mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
319 mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
320 mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
321 mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
322 mVectorActiveFaceFlagValues = cache->mVectorActiveFaceFlagValues;
323 mVectorDataType = cache->mVectorDataType;
324 return;
325 }
326
327 // Cache is not up-to-date, gather data
328 if ( datasetIndex.isValid() )
329 {
330 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex );
331
332 const bool isScalar = metadata.isScalar();
333 if ( isScalar )
334 {
335 QgsDebugError( QStringLiteral( "Dataset has no vector values" ) );
336 }
337 else
338 {
339 mVectorDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
340
343
344 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mVectorDataType );
345 mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
346 layer,
347 datasetIndex,
348 0,
349 count );
350
352 mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
353 else
354 mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
355
356 // populate face active flag
357 mVectorActiveFaceFlagValues = layer->areFacesActive( datasetIndex, 0, mNativeMesh.faces.count() );
358
359 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
360 mVectorDatasetMagMinimum = datasetMetadata.minimum();
361 mVectorDatasetMagMaximum = datasetMetadata.maximum();
362 }
363 }
364
365 // update cache
366 cache->mDatasetGroupsCount = datasetGroupCount;
367 cache->mActiveVectorDatasetIndex = datasetIndex;
368 cache->mVectorDatasetValues = mVectorDatasetValues;
369 cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
370 cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
371 cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
372 cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
373 cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
374 cache->mVectorActiveFaceFlagValues = mVectorActiveFaceFlagValues;
375 cache->mVectorDataType = mVectorDataType;
376 cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
377}
378
380{
381 std::unique_ptr< QgsScopedRuntimeProfile > profile;
382 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
383 if ( mEnableProfile )
384 {
385 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
386 if ( mPreparationTime > 0 )
387 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
388 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
389 }
390
391 mReadyToCompose = false;
392 const QgsScopedQPainterState painterState( renderContext()->painter() );
393 if ( !mClippingRegions.empty() )
394 {
395 bool needsPainterClipPath = false;
396 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Mesh, needsPainterClipPath );
397 if ( needsPainterClipPath )
398 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
399 }
400
401 preparingProfile.reset();
402
403 renderScalarDataset();
404 mReadyToCompose = true;
405 renderMesh();
406 renderVectorDataset();
407
408 registerLabelFeatures();
409
410 return !renderContext()->renderingStopped();
411}
412
417
418void QgsMeshLayerRenderer::renderMesh()
419{
420 if ( !mRendererSettings.nativeMeshSettings().isEnabled() && !mIsEditable &&
423 return;
424
425 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
426 if ( mEnableProfile )
427 {
428 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering mesh" ), QStringLiteral( "rendering" ) );
429 }
430
431 // triangular mesh
432 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
434 {
435 renderFaceMesh(
438 trianglesInExtent );
439 }
440
441 // native mesh
442 if ( ( mRendererSettings.nativeMeshSettings().isEnabled() || mIsEditable ) &&
444 {
445 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
447
448 renderFaceMesh(
451 nativeFacesInExtent.values() );
452 }
453
454 // edge mesh
456 {
457 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( renderContext()->mapExtent() );
458 renderEdgeMesh( mRendererSettings.edgeMeshSettings(), edgesInExtent );
459 }
460}
461
462static QPainter *_painterForMeshFrame( QgsRenderContext &context, const QgsMeshRendererMeshSettings &settings )
463{
464 // Set up the render configuration options
465 QPainter *painter = context.painter();
466
467 painter->save();
468 context.setPainterFlagsUsingContext( painter );
469
470 QPen pen = painter->pen();
471 pen.setCapStyle( Qt::FlatCap );
472 pen.setJoinStyle( Qt::MiterJoin );
473
474 const double penWidth = context.convertToPainterUnits( settings.lineWidth(), settings.lineWidthUnit() );
475 pen.setWidthF( penWidth );
476 pen.setColor( settings.color() );
477 painter->setPen( pen );
478 return painter;
479}
480
481void QgsMeshLayerRenderer::renderEdgeMesh( const QgsMeshRendererMeshSettings &settings, const QList<int> &edgesInExtent )
482{
483 Q_ASSERT( settings.isEnabled() );
484
486 return;
487
488 QgsRenderContext &context = *renderContext();
489 QPainter *painter = _painterForMeshFrame( context, settings );
490
491 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
492 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
493
494 for ( const int i : edgesInExtent )
495 {
496 if ( context.renderingStopped() )
497 break;
498
499 if ( i >= edges.size() )
500 continue;
501
502 const QgsMeshEdge &edge = edges[i];
503 const int startVertexIndex = edge.first;
504 const int endVertexIndex = edge.second;
505
506 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
507 continue;
508
509 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
510 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
511 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
512 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
513 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
514 }
515 painter->restore();
516};
517
518void QgsMeshLayerRenderer::renderFaceMesh(
519 const QgsMeshRendererMeshSettings &settings,
520 const QVector<QgsMeshFace> &faces,
521 const QList<int> &facesInExtent )
522{
523 Q_ASSERT( settings.isEnabled() || mIsEditable );
524
526 return;
527
528 QgsRenderContext &context = *renderContext();
529 QPainter *painter = _painterForMeshFrame( context, settings );
530
531 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
532 QSet<QPair<int, int>> drawnEdges;
533
534 for ( const int i : facesInExtent )
535 {
536 if ( context.renderingStopped() )
537 break;
538
539 if ( i < 0 || i >= faces.count() )
540 continue;
541
542 const QgsMeshFace &face = faces[i];
543 if ( face.size() < 2 )
544 continue;
545
546 for ( int j = 0; j < face.size(); ++j )
547 {
548 const int startVertexId = face[j];
549 const int endVertexId = face[( j + 1 ) % face.size()];
550 const QPair<int, int> thisEdge( startVertexId, endVertexId );
551 const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
552 if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
553 continue;
554 drawnEdges.insert( thisEdge );
555 drawnEdges.insert( thisEdgeReversed );
556
557 const QgsMeshVertex &startVertex = vertices[startVertexId];
558 const QgsMeshVertex &endVertex = vertices[endVertexId];
559 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
560 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
561 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
562 }
563 }
564
565 painter->restore();
566}
567
568void QgsMeshLayerRenderer::renderScalarDataset()
569{
570 if ( mScalarDatasetValues.isEmpty() )
571 return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
572
573 if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
574 return; // only NODATA values
575
576 const int groupIndex = mRendererSettings.activeScalarDatasetGroup();
577 if ( groupIndex < 0 )
578 return; // no shader
579
580 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
581 if ( mEnableProfile )
582 {
583 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering scalar datasets" ), QStringLiteral( "rendering" ) );
584 }
585
586 const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( groupIndex );
587
590 {
591 renderScalarDatasetOnFaces( scalarSettings );
592 }
593
596 {
597 renderScalarDatasetOnEdges( scalarSettings );
598 }
599}
600
601void QgsMeshLayerRenderer::renderScalarDatasetOnEdges( const QgsMeshRendererScalarSettings &scalarSettings )
602{
603 QgsRenderContext &context = *renderContext();
604 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
605 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
606 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( context.mapExtent() );
607
608 QgsInterpolatedLineRenderer edgePlotter;
609 edgePlotter.setInterpolatedColor( QgsInterpolatedLineColor( scalarSettings.colorRampShader() ) );
610 edgePlotter.setInterpolatedWidth( QgsInterpolatedLineWidth( scalarSettings.edgeStrokeWidth() ) );
611 edgePlotter.setWidthUnit( scalarSettings.edgeStrokeWidthUnit() );
612
613 for ( const int i : edgesInExtent )
614 {
615 if ( context.renderingStopped() )
616 break;
617
618 if ( i >= edges.size() )
619 continue;
620
621 const QgsMeshEdge &edge = edges[i];
622 const int startVertexIndex = edge.first;
623 const int endVertexIndex = edge.second;
624
625 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
626 continue;
627
628 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
629 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
630
632 {
633 edgePlotter.render( mScalarDatasetValues[i], mScalarDatasetValues[i], startVertex, endVertex, context );
634 }
635 else
636 {
637 edgePlotter.render( mScalarDatasetValues[startVertexIndex], mScalarDatasetValues[endVertexIndex], startVertex, endVertex, context );
638 }
639 }
640}
641
642QColor QgsMeshLayerRenderer::colorAt( QgsColorRampShader *shader, double val ) const
643{
644 int r, g, b, a;
645 if ( shader->shade( val, &r, &g, &b, &a ) )
646 {
647 return QColor( r, g, b, a );
648 }
649 return QColor();
650}
651
652QgsPointXY QgsMeshLayerRenderer::fractionPoint( const QgsPointXY &p1, const QgsPointXY &p2, double fraction ) const
653{
654 const QgsPointXY pt( p1.x() + fraction * ( p2.x() - p1.x() ),
655 p1.y() + fraction * ( p2.y() - p1.y() ) );
656 return pt;
657}
658
659void QgsMeshLayerRenderer::renderScalarDatasetOnFaces( const QgsMeshRendererScalarSettings &scalarSettings )
660{
661 QgsRenderContext &context = *renderContext();
662
663 QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
665 sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
666 QgsMeshLayerInterpolator interpolator( mTriangularMesh,
670 context,
671 mOutputSize );
672 interpolator.setSpatialIndexActive( mIsMeshSimplificationActive );
673 interpolator.setElevationMapSettings( mRenderElevationMap, mElevationScale, mElevationOffset );
674 QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
675 renderer.setClassificationMin( scalarSettings.classificationMinimum() );
676 renderer.setClassificationMax( scalarSettings.classificationMaximum() );
677 renderer.setOpacity( scalarSettings.opacity() );
678
679 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
680 QImage img = bl->image();
681 img.setDevicePixelRatio( context.devicePixelRatio() );
682
683 context.painter()->drawImage( 0, 0, img );
684}
685
686void QgsMeshLayerRenderer::renderVectorDataset()
687{
688 const int groupIndex = mRendererSettings.activeVectorDatasetGroup();
689 if ( groupIndex < 0 )
690 return;
691
693 return; // no data at all
694
695 if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
696 return; // only NODATA values
697
698 if ( !( mVectorDatasetMagMaximum > 0 ) )
699 return; //all vector are null vector
700
701 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
702 if ( mEnableProfile )
703 {
704 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering vector datasets" ), QStringLiteral( "rendering" ) );
705 }
706
707 std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
715 mRendererSettings.vectorSettings( groupIndex ),
716 *renderContext(),
718 mFeedback.get(),
719 mOutputSize ) );
720
721 if ( renderer )
722 renderer->draw();
723}
724
725void QgsMeshLayerRenderer::prepareLabeling( QgsMeshLayer *layer, QSet<QString> &attributeNames )
726{
727 QgsRenderContext &context = *renderContext();
728
729 if ( QgsLabelingEngine *engine = context.labelingEngine() )
730 {
731 if ( layer->labelsEnabled() )
732 {
733 mLabelProvider = layer->labeling()->provider( layer );
734 if ( mLabelProvider )
735 {
736 auto c = context.expressionContext();
737
739 c.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mNativeMesh ) );
740 context.setExpressionContext( c );
741
742 engine->addProvider( mLabelProvider );
743 if ( !mLabelProvider->prepare( context, attributeNames ) )
744 {
745 engine->removeProvider( mLabelProvider );
746 mLabelProvider = nullptr; // deleted by engine
747 }
748 }
749 }
750 }
751}
752
753void QgsMeshLayerRenderer::registerLabelFeatures()
754{
755 if ( !mLabelProvider )
756 return;
757
758 QgsRenderContext &context = *renderContext();
759
760 QgsExpressionContextScope *scope = context.expressionContext().activeScopeForVariable( QStringLiteral( "_native_mesh" ) );
761
762 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
763
764 if ( mLabelProvider->labelFaces() )
765 {
767 return;
768 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
770
771 for ( const int i : nativeFacesInExtent )
772 {
773 if ( context.renderingStopped() )
774 break;
775
776 if ( i < 0 || i >= mNativeMesh.faces.count() )
777 continue;
778
779 scope->setVariable( QStringLiteral( "_mesh_face_index" ), i, false );
780
781 QgsFeature f( i );
782 QgsGeometry geom = QgsMeshUtils::toGeometry( mNativeMesh.face( i ), mNativeMesh.vertices );
783 f.setGeometry( geom );
784 mLabelProvider->registerFeature( f, context );
785 }
786 }
787 else
788 {
790 return;
791 const QSet<int> nativeVerticesInExtent = QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent,
793
794 for ( const int i : nativeVerticesInExtent )
795 {
796 if ( context.renderingStopped() )
797 break;
798
799 if ( i < 0 || i >= mNativeMesh.vertexCount() )
800 continue;
801
802 scope->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
803
804 QgsFeature f( i );
805 QgsGeometry geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
806 f.setGeometry( geom );
807 mLabelProvider->registerFeature( f, context );
808 }
809 }
810}
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.
@ 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.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
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 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)
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.
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.
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.
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.
double width() const
Returns the width of the rectangle.
double height() const
Returns the height of the rectangle.
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.
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:5917
#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.