QGIS API Documentation 3.36.0-Maidenhead (09951dc0acf)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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"
45
47 QgsMeshLayer *layer,
48 QgsRenderContext &context )
49 : QgsMapLayerRenderer( layer->id(), &context )
50 , mIsEditable( layer->isEditable() )
51 , mLayerName( layer->name() )
52 , mFeedback( new QgsMeshLayerRendererFeedback )
53 , mRendererSettings( layer->rendererSettings() )
54 , mEnableProfile( context.flags() & Qgis::RenderContextFlag::RecordProfile )
55 , mLayerOpacity( layer->opacity() )
56{
57 QElapsedTimer timer;
58 timer.start();
59
60 // make copies for mesh data
61 // cppcheck-suppress assertWithSideEffect
62 Q_ASSERT( layer->nativeMesh() );
63 // cppcheck-suppress assertWithSideEffect
64 Q_ASSERT( layer->triangularMesh() );
65 // cppcheck-suppress assertWithSideEffect
66 Q_ASSERT( layer->rendererCache() );
67 // cppcheck-suppress assertWithSideEffect
68 Q_ASSERT( layer->dataProvider() );
69
70 mReadyToCompose = false;
71
72 // copy native mesh
73 mNativeMesh = *( layer->nativeMesh() );
74 mLayerExtent = layer->extent();
75
76 // copy triangular mesh
77 copyTriangularMeshes( layer, context );
78
79 // copy datasets
80 copyScalarDatasetValues( layer );
81 copyVectorDatasetValues( layer );
82
83 calculateOutputSize();
84
85 QSet<QString> attrs;
86 prepareLabeling( layer, attrs );
87
89
90 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
91 {
95 }
96
97 mPreparationTime = timer.elapsed();
98}
99
100void QgsMeshLayerRenderer::copyTriangularMeshes( QgsMeshLayer *layer, QgsRenderContext &context )
101{
102 // handle level of details of mesh
103 const QgsMeshSimplificationSettings simplificationSettings = layer->meshSimplificationSettings();
104 if ( simplificationSettings.isEnabled() )
105 {
106 const double triangleSize = simplificationSettings.meshResolution() * context.mapToPixel().mapUnitsPerPixel();
107 mTriangularMesh = *( layer->triangularMesh( triangleSize ) );
108 mIsMeshSimplificationActive = true;
109 }
110 else
111 {
112 mTriangularMesh = *( layer->triangularMesh() );
113 }
114}
115
117{
118 return mFeedback.get();
119}
120
121void QgsMeshLayerRenderer::calculateOutputSize()
122{
123 // figure out image size
124 const QgsRenderContext &context = *renderContext();
125 const QgsRectangle extent = context.mapExtent();
126 const QgsMapToPixel mapToPixel = context.mapToPixel();
127 const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent, context.devicePixelRatio() );
128 const int width = int( screenBBox.width() );
129 const int height = int( screenBBox.height() );
130 mOutputSize = QSize( width, height );
131}
132
133void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
134{
135 QgsMeshDatasetIndex datasetIndex;
136 if ( renderContext()->isTemporal() )
137 datasetIndex = layer->activeScalarDatasetAtTime( renderContext()->temporalRange() );
138 else
139 datasetIndex = layer->staticScalarDatasetIndex();
140
141 // Find out if we can use cache up to date. If yes, use it and return
142 const int datasetGroupCount = layer->datasetGroupCount();
144 QgsMeshLayerRendererCache *cache = layer->rendererCache();
145 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
146 ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
147 ( cache->mDataInterpolationMethod == method ) &&
148 ( QgsMesh3DAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
149 )
150 {
151 mScalarDatasetValues = cache->mScalarDatasetValues;
152 mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
153 mScalarDataType = cache->mScalarDataType;
154 mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
155 mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
156 return;
157 }
158
159 // Cache is not up-to-date, gather data
160 if ( datasetIndex.isValid() )
161 {
162 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex.group() );
163 mScalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
164
165 // populate scalar values
166 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mScalarDataType );
167 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
168 layer,
169 datasetIndex,
170 0,
171 count );
172
173 if ( vals.isValid() )
174 {
175 // vals could be scalar or vectors, for contour rendering we want always magnitude
176 mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
177 }
178 else
179 {
180 mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
181 }
182
183 // populate face active flag, always defined on faces
185 datasetIndex,
186 0,
187 mNativeMesh.faces.count() );
188
189 // for data on faces, there could be request to interpolate the data to vertices
191 {
193 {
195 mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
200 method
201 );
202 }
204 {
206 mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
211 method
212 );
213 }
214 }
215
216 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
217 mScalarDatasetMinimum = datasetMetadata.minimum();
218 mScalarDatasetMaximum = datasetMetadata.maximum();
219 }
220
221 // update cache
222 cache->mDatasetGroupsCount = datasetGroupCount;
223 cache->mActiveScalarDatasetIndex = datasetIndex;
224 cache->mDataInterpolationMethod = method;
225 cache->mScalarDatasetValues = mScalarDatasetValues;
226 cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
227 cache->mScalarDataType = mScalarDataType;
228 cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
229 cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
230 cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
231}
232
233
234void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
235{
236 QgsMeshDatasetIndex datasetIndex;
237 if ( renderContext()->isTemporal() )
238 datasetIndex = layer->activeVectorDatasetAtTime( renderContext()->temporalRange() );
239 else
240 datasetIndex = layer->staticVectorDatasetIndex();
241
242 // Find out if we can use cache up to date. If yes, use it and return
243 const int datasetGroupCount = layer->datasetGroupCount();
244 QgsMeshLayerRendererCache *cache = layer->rendererCache();
245 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
246 ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
247 ( QgsMesh3DAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
248 )
249 {
250 mVectorDatasetValues = cache->mVectorDatasetValues;
251 mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
252 mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
253 mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
254 mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
255 mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
256 mVectorActiveFaceFlagValues = cache->mVectorActiveFaceFlagValues;
257 mVectorDataType = cache->mVectorDataType;
258 return;
259 }
260
261 // Cache is not up-to-date, gather data
262 if ( datasetIndex.isValid() )
263 {
264 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex );
265
266 const bool isScalar = metadata.isScalar();
267 if ( isScalar )
268 {
269 QgsDebugError( QStringLiteral( "Dataset has no vector values" ) );
270 }
271 else
272 {
273 mVectorDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
274
277
278 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mVectorDataType );
279 mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
280 layer,
281 datasetIndex,
282 0,
283 count );
284
286 mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
287 else
288 mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
289
290 // populate face active flag
291 mVectorActiveFaceFlagValues = layer->areFacesActive( datasetIndex, 0, mNativeMesh.faces.count() );
292
293 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
294 mVectorDatasetMagMinimum = datasetMetadata.minimum();
295 mVectorDatasetMagMaximum = datasetMetadata.maximum();
296 }
297 }
298
299 // update cache
300 cache->mDatasetGroupsCount = datasetGroupCount;
301 cache->mActiveVectorDatasetIndex = datasetIndex;
302 cache->mVectorDatasetValues = mVectorDatasetValues;
303 cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
304 cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
305 cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
306 cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
307 cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
308 cache->mVectorActiveFaceFlagValues = mVectorActiveFaceFlagValues;
309 cache->mVectorDataType = mVectorDataType;
310 cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
311}
312
314{
315 std::unique_ptr< QgsScopedRuntimeProfile > profile;
316 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
317 if ( mEnableProfile )
318 {
319 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
320 if ( mPreparationTime > 0 )
321 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
322 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
323 }
324
325 mReadyToCompose = false;
326 const QgsScopedQPainterState painterState( renderContext()->painter() );
327 if ( !mClippingRegions.empty() )
328 {
329 bool needsPainterClipPath = false;
330 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Mesh, needsPainterClipPath );
331 if ( needsPainterClipPath )
332 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
333 }
334
335 preparingProfile.reset();
336
337 renderScalarDataset();
338 mReadyToCompose = true;
339 renderMesh();
340 renderVectorDataset();
341
342 registerLabelFeatures();
343
344 return !renderContext()->renderingStopped();
345}
346
351
352void QgsMeshLayerRenderer::renderMesh()
353{
354 if ( !mRendererSettings.nativeMeshSettings().isEnabled() && !mIsEditable &&
357 return;
358
359 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
360 if ( mEnableProfile )
361 {
362 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering mesh" ), QStringLiteral( "rendering" ) );
363 }
364
365 // triangular mesh
366 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
368 {
369 renderFaceMesh(
372 trianglesInExtent );
373 }
374
375 // native mesh
376 if ( ( mRendererSettings.nativeMeshSettings().isEnabled() || mIsEditable ) &&
378 {
379 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
381
382 renderFaceMesh(
385 nativeFacesInExtent.values() );
386 }
387
388 // edge mesh
390 {
391 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( renderContext()->mapExtent() );
392 renderEdgeMesh( mRendererSettings.edgeMeshSettings(), edgesInExtent );
393 }
394}
395
396static QPainter *_painterForMeshFrame( QgsRenderContext &context, const QgsMeshRendererMeshSettings &settings )
397{
398 // Set up the render configuration options
399 QPainter *painter = context.painter();
400
401 painter->save();
402 context.setPainterFlagsUsingContext( painter );
403
404 QPen pen = painter->pen();
405 pen.setCapStyle( Qt::FlatCap );
406 pen.setJoinStyle( Qt::MiterJoin );
407
408 const double penWidth = context.convertToPainterUnits( settings.lineWidth(), settings.lineWidthUnit() );
409 pen.setWidthF( penWidth );
410 pen.setColor( settings.color() );
411 painter->setPen( pen );
412 return painter;
413}
414
415void QgsMeshLayerRenderer::renderEdgeMesh( const QgsMeshRendererMeshSettings &settings, const QList<int> &edgesInExtent )
416{
417 Q_ASSERT( settings.isEnabled() );
418
420 return;
421
422 QgsRenderContext &context = *renderContext();
423 QPainter *painter = _painterForMeshFrame( context, settings );
424
425 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
426 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
427
428 for ( const int i : edgesInExtent )
429 {
430 if ( context.renderingStopped() )
431 break;
432
433 if ( i >= edges.size() )
434 continue;
435
436 const QgsMeshEdge &edge = edges[i];
437 const int startVertexIndex = edge.first;
438 const int endVertexIndex = edge.second;
439
440 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
441 continue;
442
443 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
444 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
445 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
446 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
447 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
448 }
449 painter->restore();
450};
451
452void QgsMeshLayerRenderer::renderFaceMesh(
453 const QgsMeshRendererMeshSettings &settings,
454 const QVector<QgsMeshFace> &faces,
455 const QList<int> &facesInExtent )
456{
457 Q_ASSERT( settings.isEnabled() || mIsEditable );
458
460 return;
461
462 QgsRenderContext &context = *renderContext();
463 QPainter *painter = _painterForMeshFrame( context, settings );
464
465 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
466 QSet<QPair<int, int>> drawnEdges;
467
468 for ( const int i : facesInExtent )
469 {
470 if ( context.renderingStopped() )
471 break;
472
473 if ( i < 0 || i >= faces.count() )
474 continue;
475
476 const QgsMeshFace &face = faces[i];
477 if ( face.size() < 2 )
478 continue;
479
480 for ( int j = 0; j < face.size(); ++j )
481 {
482 const int startVertexId = face[j];
483 const int endVertexId = face[( j + 1 ) % face.size()];
484 const QPair<int, int> thisEdge( startVertexId, endVertexId );
485 const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
486 if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
487 continue;
488 drawnEdges.insert( thisEdge );
489 drawnEdges.insert( thisEdgeReversed );
490
491 const QgsMeshVertex &startVertex = vertices[startVertexId];
492 const QgsMeshVertex &endVertex = vertices[endVertexId];
493 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
494 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
495 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
496 }
497 }
498
499 painter->restore();
500}
501
502void QgsMeshLayerRenderer::renderScalarDataset()
503{
504 if ( mScalarDatasetValues.isEmpty() )
505 return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
506
507 if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
508 return; // only NODATA values
509
510 const int groupIndex = mRendererSettings.activeScalarDatasetGroup();
511 if ( groupIndex < 0 )
512 return; // no shader
513
514 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
515 if ( mEnableProfile )
516 {
517 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering scalar datasets" ), QStringLiteral( "rendering" ) );
518 }
519
520 const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( groupIndex );
521
524 {
525 renderScalarDatasetOnFaces( scalarSettings );
526 }
527
530 {
531 renderScalarDatasetOnEdges( scalarSettings );
532 }
533}
534
535void QgsMeshLayerRenderer::renderScalarDatasetOnEdges( const QgsMeshRendererScalarSettings &scalarSettings )
536{
537 QgsRenderContext &context = *renderContext();
538 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
539 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
540 const QList<int> egdesInExtent = mTriangularMesh.edgeIndexesForRectangle( context.mapExtent() );
541
542 QgsInterpolatedLineRenderer edgePlotter;
543 edgePlotter.setInterpolatedColor( QgsInterpolatedLineColor( scalarSettings.colorRampShader() ) );
544 edgePlotter.setInterpolatedWidth( QgsInterpolatedLineWidth( scalarSettings.edgeStrokeWidth() ) );
545 edgePlotter.setWidthUnit( scalarSettings.edgeStrokeWidthUnit() );
546
547 for ( const int i : egdesInExtent )
548 {
549 if ( context.renderingStopped() )
550 break;
551
552 if ( i >= edges.size() )
553 continue;
554
555 const QgsMeshEdge &edge = edges[i];
556 const int startVertexIndex = edge.first;
557 const int endVertexIndex = edge.second;
558
559 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
560 continue;
561
562 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
563 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
564
566 {
567 edgePlotter.render( mScalarDatasetValues[i], mScalarDatasetValues[i], startVertex, endVertex, context );
568 }
569 else
570 {
571 edgePlotter.render( mScalarDatasetValues[startVertexIndex], mScalarDatasetValues[endVertexIndex], startVertex, endVertex, context );
572 }
573 }
574}
575
576QColor QgsMeshLayerRenderer::colorAt( QgsColorRampShader *shader, double val ) const
577{
578 int r, g, b, a;
579 if ( shader->shade( val, &r, &g, &b, &a ) )
580 {
581 return QColor( r, g, b, a );
582 }
583 return QColor();
584}
585
586QgsPointXY QgsMeshLayerRenderer::fractionPoint( const QgsPointXY &p1, const QgsPointXY &p2, double fraction ) const
587{
588 const QgsPointXY pt( p1.x() + fraction * ( p2.x() - p1.x() ),
589 p1.y() + fraction * ( p2.y() - p1.y() ) );
590 return pt;
591}
592
593void QgsMeshLayerRenderer::renderScalarDatasetOnFaces( const QgsMeshRendererScalarSettings &scalarSettings )
594{
595 QgsRenderContext &context = *renderContext();
596
597 QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
599 sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
600 QgsMeshLayerInterpolator interpolator( mTriangularMesh,
604 context,
605 mOutputSize );
606 interpolator.setSpatialIndexActive( mIsMeshSimplificationActive );
607 interpolator.setElevationMapSettings( mRenderElevationMap, mElevationScale, mElevationOffset );
608 QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
609 renderer.setClassificationMin( scalarSettings.classificationMinimum() );
610 renderer.setClassificationMax( scalarSettings.classificationMaximum() );
611 renderer.setOpacity( scalarSettings.opacity() );
612
613 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
614 QImage img = bl->image();
615 img.setDevicePixelRatio( context.devicePixelRatio() );
616
617 context.painter()->drawImage( 0, 0, img );
618}
619
620void QgsMeshLayerRenderer::renderVectorDataset()
621{
622 const int groupIndex = mRendererSettings.activeVectorDatasetGroup();
623 if ( groupIndex < 0 )
624 return;
625
627 return; // no data at all
628
629 if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
630 return; // only NODATA values
631
632 if ( !( mVectorDatasetMagMaximum > 0 ) )
633 return; //all vector are null vector
634
635 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
636 if ( mEnableProfile )
637 {
638 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering vector datasets" ), QStringLiteral( "rendering" ) );
639 }
640
641 std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
649 mRendererSettings.vectorSettings( groupIndex ),
650 *renderContext(),
652 mFeedback.get(),
653 mOutputSize ) );
654
655 if ( renderer )
656 renderer->draw();
657}
658
659void QgsMeshLayerRenderer::prepareLabeling( QgsMeshLayer *layer, QSet<QString> &attributeNames )
660{
661 QgsRenderContext &context = *renderContext();
662
663 if ( QgsLabelingEngine *engine = context.labelingEngine() )
664 {
665 if ( layer->labelsEnabled() )
666 {
667 mLabelProvider = layer->labeling()->provider( layer );
668 if ( mLabelProvider )
669 {
670 auto c = context.expressionContext();
671
673 c.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mNativeMesh ) );
674 context.setExpressionContext( c );
675
676 engine->addProvider( mLabelProvider );
677 if ( !mLabelProvider->prepare( context, attributeNames ) )
678 {
679 engine->removeProvider( mLabelProvider );
680 mLabelProvider = nullptr; // deleted by engine
681 }
682 }
683 }
684 }
685}
686
687void QgsMeshLayerRenderer::registerLabelFeatures()
688{
689 if ( !mLabelProvider )
690 return;
691
692 QgsRenderContext &context = *renderContext();
693
694 QgsExpressionContextScope *scope = context.expressionContext().activeScopeForVariable( QStringLiteral( "_native_mesh" ) );
695
696 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
697
698 if ( mLabelProvider->labelFaces() )
699 {
701 return;
702 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
704
705 for ( const int i : nativeFacesInExtent )
706 {
707 if ( context.renderingStopped() )
708 break;
709
710 if ( i < 0 || i >= mNativeMesh.faces.count() )
711 continue;
712
713 scope->setVariable( QStringLiteral( "_mesh_face_index" ), i, false );
714
715 QgsFeature f( i );
716 QgsGeometry geom = QgsMeshUtils::toGeometry( mNativeMesh.face( i ), mNativeMesh.vertices );
717 f.setGeometry( geom );
718 mLabelProvider->registerFeature( f, context );
719 }
720 }
721 else
722 {
724 return;
725 const QSet<int> nativeVerticesInExtent = QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent,
727
728 for ( const int i : nativeVerticesInExtent )
729 {
730 if ( context.renderingStopped() )
731 break;
732
733 if ( i < 0 || i >= mNativeMesh.vertexCount() )
734 continue;
735
736 scope->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
737
738 QgsFeature f( i );
739 QgsGeometry geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
740 f.setGeometry( geom );
741 mLabelProvider->registerFeature( f, context );
742 }
743 }
744}
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.
virtual QgsMeshLayerLabelProvider * provider(QgsMeshLayer *layer) const
Factory for label provider implementation.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue) const override
Generates and new RGB value based on one input value.
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:56
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
A geometry is the spatial representation of a feature.
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.
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.
int datasetGroupCount() const
Returns the dataset groups count handle by the layer.
QgsMeshDatasetIndex activeScalarDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active scalar group depending on the time range.
const QgsAbstractMeshLayerLabeling * labeling() const
Access to const labeling configuration.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsMeshDatasetIndex staticVectorDatasetIndex() const
Returns the static vector dataset index that is rendered if the temporal properties is not active.
QgsMeshSimplificationSettings meshSimplificationSettings() const
Returns mesh simplification settings.
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
QgsMeshDatasetIndex staticScalarDatasetIndex() const
Returns the static scalar dataset index that is rendered if the temporal properties is not active.
QgsMeshDatasetMetadata datasetMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset metadata.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
Represents a mesh renderer settings for mesh object.
QColor color() const
Returns color used for rendering.
double lineWidth() const
Returns line width used for rendering (in millimeters)
Qgis::RenderUnit lineWidthUnit() const
Returns units of the width of the mesh frame.
bool isEnabled() const
Returns whether mesh structure rendering is enabled.
Represents a mesh renderer settings for scalar datasets.
double opacity() const
Returns opacity.
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
double classificationMinimum() const
Returns min value used for creation of the color ramp shader.
DataResamplingMethod
Resampling of value from dataset.
@ NoResampling
Does not use resampling.
Qgis::RenderUnit edgeStrokeWidthUnit() const
Returns the stroke width unit used to render edges scalar dataset.
DataResamplingMethod dataResamplingMethod() const
Returns the type of interpolation to use to convert face defined datasets to values on vertices.
double classificationMaximum() const
Returns max value used for creation of the color ramp shader.
QgsInterpolatedLineWidth edgeStrokeWidth() const
Returns the stroke width used to render edges scalar dataset.
QgsMeshRendererScalarSettings scalarSettings(int groupIndex) const
Returns renderer settings.
int activeVectorDatasetGroup() const
Returns the active vector dataset group.
QgsMesh3DAveragingMethod * averagingMethod() const
Returns averaging method for conversion of 3d stacked mesh data to 2d data.
int activeScalarDatasetGroup() const
Returns the active scalar dataset group.
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
QgsMeshRendererMeshSettings edgeMeshSettings() const
Returns edge mesh renderer settings.
QgsMeshRendererMeshSettings nativeMeshSettings() const
Returns native mesh renderer settings.
QgsMeshRendererMeshSettings triangularMeshSettings() const
Returns triangular mesh renderer settings.
Represents an overview renderer settings.
bool isEnabled() const
Returns if the overview is active.
int meshResolution() const
Returns the mesh resolution i.e., the minimum size (average) of triangles in pixels.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:166
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
Interface for all raster shaders.
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
A rectangle specified with double values.
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...
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:5144
#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.