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