QGIS API Documentation 3.99.0-Master (26c88405ac0)
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
19
20#include <algorithm>
21#include <memory>
22
23#include "qgsapplication.h"
24#include "qgscolorrampshader.h"
26#include "qgslogger.h"
27#include "qgsmapclippingutils.h"
29#include "qgsmeshlayer.h"
34#include "qgsmeshlayerutils.h"
36#include "qgspointxy.h"
37#include "qgsrastershader.h"
39#include "qgsruntimeprofiler.h"
41#include "qgsthreadingutils.h"
42
43#include <QBrush>
44#include <QElapsedTimer>
45#include <QLinearGradient>
46#include <QPair>
47#include <QPointer>
48#include <QSet>
49
50#include "moc_qgsmeshlayerrenderer.cpp"
51
53 QgsMeshLayer *layer,
54 QgsRenderContext &context )
55 : QgsMapLayerRenderer( layer->id(), &context )
56 , mIsEditable( layer->isEditable() )
57 , mLayerName( layer->name() )
58 , mFeedback( new QgsMeshLayerRendererFeedback )
59 , mRendererSettings( layer->rendererSettings() )
60 , mEnableProfile( context.flags() & Qgis::RenderContextFlag::RecordProfile )
61 , mLayerOpacity( layer->opacity() )
62{
63 QElapsedTimer timer;
64 timer.start();
65
66 // make copies for mesh data
67 // cppcheck-suppress assertWithSideEffect
68 Q_ASSERT( layer->nativeMesh() );
69 // cppcheck-suppress assertWithSideEffect
70 Q_ASSERT( layer->triangularMesh() );
71 // cppcheck-suppress assertWithSideEffect
72 Q_ASSERT( layer->rendererCache() );
73 // cppcheck-suppress assertWithSideEffect
74 Q_ASSERT( layer->dataProvider() );
75
76 mReadyToCompose = false;
77
78 // copy native mesh
79 mNativeMesh = *( layer->nativeMesh() );
80 mLayerExtent = layer->extent();
81
82 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
83 {
84 QgsMeshLayerElevationProperties *elevProp = qobject_cast<QgsMeshLayerElevationProperties *>( layer->elevationProperties() );
85
87 mElevationScale = elevProp->zScale();
88 mElevationOffset = elevProp->zOffset();
89
90 if ( !context.zRange().isInfinite() )
91 {
92 switch ( elevProp->mode() )
93 {
95 // don't need to handle anything here -- the layer renderer will never be created if the
96 // render context range doesn't match the layer's fixed elevation range
97 break;
98
100 {
101 // TODO -- filtering by mesh z values is not currently implemented
102 break;
103 }
104
106 {
107 // find the top-most group which matches the map range and parent group
108 int currentMatchingVectorGroup = -1;
109 int currentMatchingScalarGroup = -1;
110 QgsDoubleRange currentMatchingVectorRange;
111 QgsDoubleRange currentMatchingScalarRange;
112
113 const QMap<int, QgsDoubleRange > rangePerGroup = elevProp->fixedRangePerGroup();
114
115 const int activeVectorDatasetGroup = mRendererSettings.activeVectorDatasetGroup();
116 const int activeScalarDatasetGroup = mRendererSettings.activeScalarDatasetGroup();
117
118 for ( auto it = rangePerGroup.constBegin(); it != rangePerGroup.constEnd(); ++it )
119 {
120 if ( it.value().overlaps( context.zRange() ) )
121 {
122 const bool matchesVectorParentGroup = QgsMeshLayerUtils::haveSameParentQuantity( layer, QgsMeshDatasetIndex( activeVectorDatasetGroup ), QgsMeshDatasetIndex( it.key() ) );
123 const bool matchesScalarParentGroup = QgsMeshLayerUtils::haveSameParentQuantity( layer, QgsMeshDatasetIndex( activeScalarDatasetGroup ), QgsMeshDatasetIndex( it.key() ) );
124
125 if ( matchesVectorParentGroup && (
126 currentMatchingVectorRange.isInfinite()
127 || ( it.value().includeUpper() && it.value().upper() >= currentMatchingVectorRange.upper() )
128 || ( !currentMatchingVectorRange.includeUpper() && it.value().upper() >= currentMatchingVectorRange.upper() ) ) )
129 {
130 currentMatchingVectorGroup = it.key();
131 currentMatchingVectorRange = it.value();
132 }
133
134 if ( matchesScalarParentGroup && (
135 currentMatchingScalarRange.isInfinite()
136 || ( it.value().includeUpper() && it.value().upper() >= currentMatchingScalarRange.upper() )
137 || ( !currentMatchingScalarRange.includeUpper() && it.value().upper() >= currentMatchingScalarRange.upper() ) ) )
138 {
139 currentMatchingScalarGroup = it.key();
140 currentMatchingScalarRange = it.value();
141 }
142 }
143 }
144 if ( currentMatchingVectorGroup >= 0 )
145 mRendererSettings.setActiveVectorDatasetGroup( currentMatchingVectorGroup );
146 if ( currentMatchingScalarGroup >= 0 )
147 mRendererSettings.setActiveScalarDatasetGroup( currentMatchingScalarGroup );
148 }
149 }
150 }
151 }
152
153 // copy triangular mesh
154 copyTriangularMeshes( layer, context );
155
156 // copy datasets
157 copyScalarDatasetValues( layer );
158 copyVectorDatasetValues( layer );
159
160 calculateOutputSize();
161
162 QSet<QString> attrs;
163 prepareLabeling( layer, attrs );
164
166
168 && !( context.flags() & Qgis::RenderContextFlag::Render3DMap ) )
169 {
170 const QgsMeshDatasetIndex activeDatasetIndex = layer->activeScalarDatasetIndex( context );
171
172 if ( activeDatasetIndex.isValid() )
173 {
174 QgsMeshRendererScalarSettings scalarRendererSettings = mRendererSettings.scalarSettings( activeDatasetIndex.group() );
175 const double previousMin = scalarRendererSettings.classificationMinimum();
176 const double previousMax = scalarRendererSettings.classificationMaximum();
177
178 if ( scalarRendererSettings.extent() == Qgis::MeshRangeExtent::UpdatedCanvas &&
179 scalarRendererSettings.limits() == Qgis::MeshRangeLimit::MinimumMaximum )
180 {
181 double min, max;
182
183 const bool found = layer->minimumMaximumActiveScalarDataset( context.extent(), activeDatasetIndex, min, max );
184
185 if ( found )
186 {
187 if ( previousMin != min || previousMax != max )
188 {
189
190 scalarRendererSettings.setClassificationMinimumMaximum( min, max );
191 mRendererSettings.setScalarSettings( activeDatasetIndex.group(), scalarRendererSettings );
192
193 QgsRenderedLayerStatistics *layerStatistics = new QgsRenderedLayerStatistics( layer->id(), previousMin, previousMax );
194
195 layerStatistics->setBoundingBox( context.extent() );
196 layerStatistics->setMaximum( 0, max );
197 layerStatistics->setMinimum( 0, min );
198
199 appendRenderedItemDetails( layerStatistics );
200 }
201 }
202 }
203 }
204 }
205
206 mPreparationTime = timer.elapsed();
207}
208
209void QgsMeshLayerRenderer::copyTriangularMeshes( QgsMeshLayer *layer, QgsRenderContext &context )
210{
211 // handle level of details of mesh
212 const QgsMeshSimplificationSettings simplificationSettings = layer->meshSimplificationSettings();
213 if ( simplificationSettings.isEnabled() )
214 {
215 const double triangleSize = simplificationSettings.meshResolution() * context.mapToPixel().mapUnitsPerPixel();
216 mTriangularMesh = *( layer->triangularMesh( triangleSize ) );
217 mIsMeshSimplificationActive = true;
218 }
219 else
220 {
221 mTriangularMesh = *( layer->triangularMesh() );
222 }
223}
224
226{
227 return mFeedback.get();
228}
229
230void QgsMeshLayerRenderer::calculateOutputSize()
231{
232 // figure out image size
233 const QgsRenderContext &context = *renderContext();
234 const QgsRectangle extent = context.mapExtent();
235 const QgsMapToPixel mapToPixel = context.mapToPixel();
236 const QgsRectangle screenBBox = QgsMeshLayerUtils::boundingBoxToScreenRectangle( mapToPixel, extent, context.devicePixelRatio() );
237 const int width = int( screenBBox.width() );
238 const int height = int( screenBBox.height() );
239 mOutputSize = QSize( width, height );
240}
241
242void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
243{
244 QgsMeshDatasetIndex datasetIndex;
245 if ( renderContext()->isTemporal() )
246 datasetIndex = layer->activeScalarDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeScalarDatasetGroup() );
247 else
248 datasetIndex = layer->staticScalarDatasetIndex( mRendererSettings.activeScalarDatasetGroup() );
249
250 // Find out if we can use cache up to date. If yes, use it and return
251 const int datasetGroupCount = layer->datasetGroupCount();
252 const QgsMeshRendererScalarSettings::DataResamplingMethod method = mRendererSettings.scalarSettings( datasetIndex.group() ).dataResamplingMethod();
253 QgsMeshLayerRendererCache *cache = layer->rendererCache();
254 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
255 ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
256 ( cache->mDataInterpolationMethod == method ) &&
257 ( QgsMesh3DAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
258 )
259 {
260 mScalarDatasetValues = cache->mScalarDatasetValues;
261 mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
262 mScalarDataType = cache->mScalarDataType;
263 mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
264 mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
265 return;
266 }
267
268 // Cache is not up-to-date, gather data
269 if ( datasetIndex.isValid() )
270 {
271 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex.group() );
272 mScalarDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
273
274 // populate scalar values
275 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mScalarDataType );
276 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
277 layer,
278 datasetIndex,
279 0,
280 count );
281
282 if ( vals.isValid() )
283 {
284 // vals could be scalar or vectors, for contour rendering we want always magnitude
285 mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
286 }
287 else
288 {
289 mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
290 }
291
292 // populate face active flag, always defined on faces
294 datasetIndex,
295 0,
296 mNativeMesh.faces.count() );
297
298 // for data on faces, there could be request to interpolate the data to vertices
300 {
302 {
304 mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
309 method
310 );
311 }
313 {
315 mScalarDatasetValues = QgsMeshLayerUtils::resampleFromVerticesToFaces(
320 method
321 );
322 }
323 }
324
325 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
326 mScalarDatasetMinimum = datasetMetadata.minimum();
327 mScalarDatasetMaximum = datasetMetadata.maximum();
328 }
329
330 // update cache
331 cache->mDatasetGroupsCount = datasetGroupCount;
332 cache->mActiveScalarDatasetIndex = datasetIndex;
333 cache->mDataInterpolationMethod = method;
334 cache->mScalarDatasetValues = mScalarDatasetValues;
335 cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
336 cache->mScalarDataType = mScalarDataType;
337 cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
338 cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
339 cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
340}
341
342
343void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
344{
345 QgsMeshDatasetIndex datasetIndex;
346 if ( renderContext()->isTemporal() )
347 datasetIndex = layer->activeVectorDatasetAtTime( renderContext()->temporalRange(), mRendererSettings.activeVectorDatasetGroup() );
348 else
349 datasetIndex = layer->staticVectorDatasetIndex( mRendererSettings.activeVectorDatasetGroup() );
350
351 // Find out if we can use cache up to date. If yes, use it and return
352 const int datasetGroupCount = layer->datasetGroupCount();
353 QgsMeshLayerRendererCache *cache = layer->rendererCache();
354 if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
355 ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
356 ( QgsMesh3DAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
357 )
358 {
359 mVectorDatasetValues = cache->mVectorDatasetValues;
360 mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
361 mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
362 mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
363 mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
364 mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
365 mVectorActiveFaceFlagValues = cache->mVectorActiveFaceFlagValues;
366 mVectorDataType = cache->mVectorDataType;
367 return;
368 }
369
370 // Cache is not up-to-date, gather data
371 if ( datasetIndex.isValid() )
372 {
373 const QgsMeshDatasetGroupMetadata metadata = layer->datasetGroupMetadata( datasetIndex );
374
375 const bool isScalar = metadata.isScalar();
376 if ( isScalar )
377 {
378 QgsDebugError( QStringLiteral( "Dataset has no vector values" ) );
379 }
380 else
381 {
382 mVectorDataType = QgsMeshLayerUtils::datasetValuesType( metadata.dataType() );
383
386
387 const int count = QgsMeshLayerUtils::datasetValuesCount( &mNativeMesh, mVectorDataType );
388 mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
389 layer,
390 datasetIndex,
391 0,
392 count );
393
394 if ( mVectorDatasetValues.isValid() )
395 mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
396 else
397 mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
398
399 // populate face active flag
400 mVectorActiveFaceFlagValues = layer->areFacesActive( datasetIndex, 0, mNativeMesh.faces.count() );
401
402 const QgsMeshDatasetMetadata datasetMetadata = layer->datasetMetadata( datasetIndex );
403 mVectorDatasetMagMinimum = datasetMetadata.minimum();
404 mVectorDatasetMagMaximum = datasetMetadata.maximum();
405 }
406 }
407
408 // update cache
409 cache->mDatasetGroupsCount = datasetGroupCount;
410 cache->mActiveVectorDatasetIndex = datasetIndex;
411 cache->mVectorDatasetValues = mVectorDatasetValues;
412 cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
413 cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
414 cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
415 cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
416 cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
417 cache->mVectorActiveFaceFlagValues = mVectorActiveFaceFlagValues;
418 cache->mVectorDataType = mVectorDataType;
419 cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
420}
421
423{
424 QgsScopedThreadName threadName( QStringLiteral( "render:%1" ).arg( mLayerName ) );
425
426 std::unique_ptr< QgsScopedRuntimeProfile > profile;
427 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
428 if ( mEnableProfile )
429 {
430 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
431 if ( mPreparationTime > 0 )
432 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
433 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
434 }
435
436 mReadyToCompose = false;
437 const QgsScopedQPainterState painterState( renderContext()->painter() );
438 if ( !mClippingRegions.empty() )
439 {
440 bool needsPainterClipPath = false;
441 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Mesh, needsPainterClipPath );
442 if ( needsPainterClipPath )
443 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
444 }
445
446 preparingProfile.reset();
447
448 renderScalarDataset();
449 mReadyToCompose = true;
450 renderMesh();
451 renderVectorDataset();
452
453 registerLabelFeatures();
454
455 return !renderContext()->renderingStopped();
456}
457
459{
460 switch ( renderContext()->rasterizedRenderingPolicy() )
461 {
464 break;
465
467 return false;
468 }
469
470 return !qgsDoubleNear( mLayerOpacity, 1.0 );
471}
472
473void QgsMeshLayerRenderer::renderMesh()
474{
475 if ( !mRendererSettings.nativeMeshSettings().isEnabled() && !mIsEditable &&
478 return;
479
480 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
481 if ( mEnableProfile )
482 {
483 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering mesh" ), QStringLiteral( "rendering" ) );
484 }
485
486 // triangular mesh
487 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
488 if ( mRendererSettings.triangularMeshSettings().isEnabled() )
489 {
490 renderFaceMesh(
491 mRendererSettings.triangularMeshSettings(),
492 mTriangularMesh.triangles(),
493 trianglesInExtent );
494 }
495
496 // native mesh
497 if ( ( mRendererSettings.nativeMeshSettings().isEnabled() || mIsEditable ) &&
498 mTriangularMesh.levelOfDetail() == 0 )
499 {
500 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
501 mTriangularMesh.trianglesToNativeFaces() );
502
503 renderFaceMesh(
504 mRendererSettings.nativeMeshSettings(),
505 mNativeMesh.faces,
506 nativeFacesInExtent.values() );
507 }
508
509 // edge mesh
510 if ( mRendererSettings.edgeMeshSettings().isEnabled() )
511 {
512 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( renderContext()->mapExtent() );
513 renderEdgeMesh( mRendererSettings.edgeMeshSettings(), edgesInExtent );
514 }
515}
516
517static QPainter *_painterForMeshFrame( QgsRenderContext &context, const QgsMeshRendererMeshSettings &settings )
518{
519 // Set up the render configuration options
520 QPainter *painter = context.painter();
521
522 painter->save();
523 context.setPainterFlagsUsingContext( painter );
524
525 QPen pen = painter->pen();
526 pen.setCapStyle( Qt::FlatCap );
527 pen.setJoinStyle( Qt::MiterJoin );
528
529 const double penWidth = context.convertToPainterUnits( settings.lineWidth(), settings.lineWidthUnit() );
530 pen.setWidthF( penWidth );
531 pen.setColor( settings.color() );
532 painter->setPen( pen );
533 return painter;
534}
535
536void QgsMeshLayerRenderer::renderEdgeMesh( const QgsMeshRendererMeshSettings &settings, const QList<int> &edgesInExtent )
537{
538 Q_ASSERT( settings.isEnabled() );
539
541 return;
542
543 QgsRenderContext &context = *renderContext();
544 QPainter *painter = _painterForMeshFrame( context, settings );
545
546 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
547 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
548
549 for ( const int i : edgesInExtent )
550 {
551 if ( context.renderingStopped() )
552 break;
553
554 if ( i >= edges.size() )
555 continue;
556
557 const QgsMeshEdge &edge = edges[i];
558 const int startVertexIndex = edge.first;
559 const int endVertexIndex = edge.second;
560
561 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
562 continue;
563
564 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
565 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
566 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
567 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
568 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
569 }
570 painter->restore();
571};
572
573void QgsMeshLayerRenderer::renderFaceMesh(
574 const QgsMeshRendererMeshSettings &settings,
575 const QVector<QgsMeshFace> &faces,
576 const QList<int> &facesInExtent )
577{
578 Q_ASSERT( settings.isEnabled() || mIsEditable );
579
581 return;
582
583 QgsRenderContext &context = *renderContext();
584 QPainter *painter = _painterForMeshFrame( context, settings );
585
586 const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
587 QSet<QPair<int, int>> drawnEdges;
588
589 for ( const int i : facesInExtent )
590 {
591 if ( context.renderingStopped() )
592 break;
593
594 if ( i < 0 || i >= faces.count() )
595 continue;
596
597 const QgsMeshFace &face = faces[i];
598 if ( face.size() < 2 )
599 continue;
600
601 for ( int j = 0; j < face.size(); ++j )
602 {
603 const int startVertexId = face[j];
604 const int endVertexId = face[( j + 1 ) % face.size()];
605 const QPair<int, int> thisEdge( startVertexId, endVertexId );
606 const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
607 if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
608 continue;
609 drawnEdges.insert( thisEdge );
610 drawnEdges.insert( thisEdgeReversed );
611
612 const QgsMeshVertex &startVertex = vertices[startVertexId];
613 const QgsMeshVertex &endVertex = vertices[endVertexId];
614 const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
615 const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
616 painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
617 }
618 }
619
620 painter->restore();
621}
622
623void QgsMeshLayerRenderer::renderScalarDataset()
624{
625 if ( mScalarDatasetValues.isEmpty() )
626 return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
627
628 if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
629 return; // only NODATA values
630
631 const int groupIndex = mRendererSettings.activeScalarDatasetGroup();
632 if ( groupIndex < 0 )
633 return; // no shader
634
635 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
636 if ( mEnableProfile )
637 {
638 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering scalar datasets" ), QStringLiteral( "rendering" ) );
639 }
640
641 const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( groupIndex );
642
643 if ( ( mTriangularMesh.contains( QgsMesh::ElementType::Face ) ) &&
645 {
646 renderScalarDatasetOnFaces( scalarSettings );
647 }
648
649 if ( ( mTriangularMesh.contains( QgsMesh::ElementType::Edge ) ) &&
651 {
652 renderScalarDatasetOnEdges( scalarSettings );
653 }
654}
655
656void QgsMeshLayerRenderer::renderScalarDatasetOnEdges( const QgsMeshRendererScalarSettings &scalarSettings )
657{
658 QgsRenderContext &context = *renderContext();
659 const QVector<QgsMeshEdge> edges = mTriangularMesh.edges();
660 const QVector<QgsMeshVertex> vertices = mTriangularMesh.vertices();
661 const QList<int> edgesInExtent = mTriangularMesh.edgeIndexesForRectangle( context.mapExtent() );
662
663 QgsInterpolatedLineRenderer edgePlotter;
664 edgePlotter.setInterpolatedColor( QgsInterpolatedLineColor( scalarSettings.colorRampShader() ) );
665 edgePlotter.setInterpolatedWidth( QgsInterpolatedLineWidth( scalarSettings.edgeStrokeWidth() ) );
666 edgePlotter.setWidthUnit( scalarSettings.edgeStrokeWidthUnit() );
667
668 for ( const int i : edgesInExtent )
669 {
670 if ( context.renderingStopped() )
671 break;
672
673 if ( i >= edges.size() )
674 continue;
675
676 const QgsMeshEdge &edge = edges[i];
677 const int startVertexIndex = edge.first;
678 const int endVertexIndex = edge.second;
679
680 if ( ( startVertexIndex >= vertices.size() ) || endVertexIndex >= vertices.size() )
681 continue;
682
683 const QgsMeshVertex &startVertex = vertices[startVertexIndex];
684 const QgsMeshVertex &endVertex = vertices[endVertexIndex];
685
687 {
688 edgePlotter.render( mScalarDatasetValues[i], mScalarDatasetValues[i], startVertex, endVertex, context );
689 }
690 else
691 {
692 edgePlotter.render( mScalarDatasetValues[startVertexIndex], mScalarDatasetValues[endVertexIndex], startVertex, endVertex, context );
693 }
694 }
695}
696
697QColor QgsMeshLayerRenderer::colorAt( QgsColorRampShader *shader, double val ) const
698{
699 int r, g, b, a;
700 if ( shader->shade( val, &r, &g, &b, &a ) )
701 {
702 return QColor( r, g, b, a );
703 }
704 return QColor();
705}
706
707QgsPointXY QgsMeshLayerRenderer::fractionPoint( const QgsPointXY &p1, const QgsPointXY &p2, double fraction ) const
708{
709 const QgsPointXY pt( p1.x() + fraction * ( p2.x() - p1.x() ),
710 p1.y() + fraction * ( p2.y() - p1.y() ) );
711 return pt;
712}
713
714void QgsMeshLayerRenderer::renderScalarDatasetOnFaces( const QgsMeshRendererScalarSettings &scalarSettings )
715{
716 QgsRenderContext &context = *renderContext();
717
718 QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
719 QgsRasterShader *sh = new QgsRasterShader();
720 sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
721 QgsMeshLayerInterpolator interpolator( mTriangularMesh,
725 context,
726 mOutputSize );
727 interpolator.setSpatialIndexActive( mIsMeshSimplificationActive );
728 interpolator.setElevationMapSettings( mRenderElevationMap, mElevationScale, mElevationOffset );
729 QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
730 renderer.setClassificationMin( scalarSettings.classificationMinimum() );
731 renderer.setClassificationMax( scalarSettings.classificationMaximum() );
732 renderer.setOpacity( scalarSettings.opacity() );
733
734 std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
735 QImage img = bl->image();
736 img.setDevicePixelRatio( context.devicePixelRatio() );
737
738 context.painter()->drawImage( 0, 0, img );
739}
740
741void QgsMeshLayerRenderer::renderVectorDataset()
742{
743 const int groupIndex = mRendererSettings.activeVectorDatasetGroup();
744 if ( groupIndex < 0 )
745 return;
746
747 if ( !mVectorDatasetValues.isValid() )
748 return; // no data at all
749
750 if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
751 return; // only NODATA values
752
753 if ( !( mVectorDatasetMagMaximum > 0 ) )
754 return; //all vector are null vector
755
756 std::unique_ptr< QgsScopedRuntimeProfile > renderProfile;
757 if ( mEnableProfile )
758 {
759 renderProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering vector datasets" ), QStringLiteral( "rendering" ) );
760 }
761
762 std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
770 mRendererSettings.vectorSettings( groupIndex ),
771 *renderContext(),
773 mFeedback.get(),
774 mOutputSize ) );
775
776 if ( renderer )
777 renderer->draw();
778}
779
780void QgsMeshLayerRenderer::prepareLabeling( QgsMeshLayer *layer, QSet<QString> &attributeNames )
781{
782 QgsRenderContext &context = *renderContext();
783
784 if ( QgsLabelingEngine *engine = context.labelingEngine() )
785 {
786 if ( layer->labelsEnabled() )
787 {
788 mLabelProvider = layer->labeling()->provider( layer );
789 if ( mLabelProvider )
790 {
791 auto c = context.expressionContext();
792
794 c.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mNativeMesh ) );
795 context.setExpressionContext( c );
796
797 engine->addProvider( mLabelProvider );
798 if ( !mLabelProvider->prepare( context, attributeNames ) )
799 {
800 engine->removeProvider( mLabelProvider );
801 mLabelProvider = nullptr; // deleted by engine
802 }
803 }
804 }
805 }
806}
807
808void QgsMeshLayerRenderer::registerLabelFeatures()
809{
810 if ( !mLabelProvider )
811 return;
812
813 QgsRenderContext &context = *renderContext();
814
815 QgsExpressionContextScope *scope = context.expressionContext().activeScopeForVariable( QStringLiteral( "_native_mesh" ) );
816
817 const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
818
819 if ( mLabelProvider->labelFaces() )
820 {
822 return;
823 const QSet<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
824 mTriangularMesh.trianglesToNativeFaces() );
825
826 for ( const int i : nativeFacesInExtent )
827 {
828 if ( context.renderingStopped() )
829 break;
830
831 if ( i < 0 || i >= mNativeMesh.faces.count() )
832 continue;
833
834 scope->setVariable( QStringLiteral( "_mesh_face_index" ), i, false );
835
836 QgsFeature f( i );
837 QgsGeometry geom = QgsMeshUtils::toGeometry( mNativeMesh.face( i ), mNativeMesh.vertices );
838 f.setGeometry( geom );
839 mLabelProvider->registerFeature( f, context );
840 }
841 }
842 else
843 {
845 return;
846 const QSet<int> nativeVerticesInExtent = QgsMeshUtils::nativeVerticesFromTriangles( trianglesInExtent,
847 mTriangularMesh.triangles() );
848
849 for ( const int i : nativeVerticesInExtent )
850 {
851 if ( context.renderingStopped() )
852 break;
853
854 if ( i < 0 || i >= mNativeMesh.vertexCount() )
855 continue;
856
857 scope->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
858
859 QgsFeature f( i );
860 QgsGeometry geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
861 f.setGeometry( geom );
862 mLabelProvider->registerFeature( f, context );
863 }
864 }
865}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:56
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2704
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2705
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2706
@ MinimumMaximum
Real min-max values.
Definition qgis.h:6050
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
Definition qgis.h:6063
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:194
@ RenderPreviewJob
Render is a 'canvas preview' render, and shortcuts should be taken to ensure fast rendering.
Definition qgis.h:2758
@ Render3DMap
Render is for a 3D map.
Definition qgis.h:2763
@ FromVertices
Elevation should be taken from mesh vertices.
Definition qgis.h:4037
@ FixedRangePerGroup
Layer has a fixed (manually specified) elevation range per group.
Definition qgis.h:4038
@ FixedElevationRange
Layer has a fixed elevation range.
Definition qgis.h:4036
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:233
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:287
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.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
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.
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...
bool mReadyToCompose
The flag must be set to false in renderer's constructor if wants to use the smarter map redraws funct...
QString layerId() const
Gets access to the ID of the layer rendered by this class.
void appendRenderedItemDetails(QgsRenderedItemDetails *details)
Appends the details of a rendered item to the renderer.
virtual Qgis::MapLayerRendererFlags flags() const
Returns flags which control how the map layer rendering behaves.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
QgsMapLayerRenderer(const QString &layerID, QgsRenderContext *context=nullptr)
Constructor for QgsMapLayerRenderer, with the associated layerID and render context.
QString id
Definition qgsmaplayer.h:83
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.
bool isValid() const
Whether the block is valid.
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.
An 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.
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.
QgsMeshDatasetGroupMetadata::DataType mScalarDataType
QgsTriangularMesh mTriangularMesh
QgsMeshLayerRenderer(QgsMeshLayer *layer, QgsRenderContext &context)
Ctor.
QVector< double > mScalarDatasetValues
std::unique_ptr< QgsMeshLayerRendererFeedback > mFeedback
feedback class for cancellation
QgsMeshDataBlock mScalarActiveFaceFlagValues
bool render() override
Do the rendering (based on data stored in the class).
QVector< double > mVectorDatasetValuesMag
QgsMeshRendererSettings mRendererSettings
bool forceRasterRender() const override
Returns true if the renderer must be rendered to a raster paint device (e.g.
QgsMeshDatasetGroupMetadata::DataType mVectorDataType
QList< QgsMapClippingRegion > mClippingRegions
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be nullptr).
QgsMeshDataBlock mVectorActiveFaceFlagValues
QgsMeshDataBlock mVectorDatasetValues
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsRectangle extent() const override
Returns the extent of the layer.
QgsMeshDatasetIndex activeScalarDatasetIndex(QgsRenderContext &rendererContext)
Returns current active scalar dataset index for current renderer context.
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange, int group=-1) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
int datasetGroupCount() const
Returns the dataset groups count handle by the layer.
const QgsAbstractMeshLayerLabeling * labeling() const
Access to const labeling configuration.
QgsMeshDatasetIndex staticVectorDatasetIndex(int group=-1) const
Returns the static vector dataset index that is rendered if the temporal properties is not active.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh).
bool minimumMaximumActiveScalarDataset(const QgsRectangle &extent, const QgsMeshDatasetIndex &datasetIndex, double &min, double &max)
Extracts minimum and maximum value for active scalar dataset on mesh faces.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsMeshSimplificationSettings meshSimplificationSettings() const
Returns mesh simplification settings.
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
QgsMeshDatasetMetadata datasetMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset metadata.
QgsMeshDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
QgsMeshDatasetIndex activeScalarDatasetAtTime(const QgsDateTimeRange &timeRange, int group=-1) const
Returns dataset index from active scalar group depending on the time range.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetIndex staticScalarDatasetIndex(int group=-1) const
Returns the static scalar dataset index that is rendered if the temporal properties is not active.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering).
Represents a mesh renderer settings for mesh objects.
QColor color() const
Returns color used for rendering.
double lineWidth() const
Returns line width used for rendering (in millimeters).
Qgis::RenderUnit lineWidthUnit() const
Returns units of the width of the mesh frame.
bool isEnabled() const
Returns whether mesh structure rendering is enabled.
Represents a mesh renderer settings for scalar datasets.
void setClassificationMinimumMaximum(double minimum, double maximum)
Sets min/max values used for creation of the color ramp shader.
Qgis::MeshRangeExtent extent() const
Returns the mesh extent for minimum maximum calculation.
double opacity() const
Returns opacity.
Qgis::MeshRangeLimit limits() const
Returns the range limits type for minimum maximum calculation.
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
double classificationMinimum() const
Returns min value used for creation of the color ramp shader.
DataResamplingMethod
Resampling of value from dataset.
@ NoResampling
Does not use resampling.
Qgis::RenderUnit edgeStrokeWidthUnit() const
Returns the stroke width unit used to render edges scalar dataset.
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.
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.
static QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
static QSet< int > nativeVerticesFromTriangles(const QList< int > &triangleIndexes, const QVector< QgsMeshFace > &triangles)
Returns unique native vertex indexes from list of vertices of triangles.
static QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
Represents 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
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
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
A rectangle specified with double values.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
bool testFlag(Qgis::RenderContextFlag flag) const
Check whether a particular flag is enabled.
float devicePixelRatio() const
Returns the device pixel ratio.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QgsDoubleRange zRange() const
Returns the range of z-values which should be rendered.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr).
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
void setBoundingBox(const QgsRectangle &bounds)
Sets the bounding box of the item (in map units).
Contains computed statistics for a layer render.
void setMaximum(QList< double > &maximum)
Sets the maximum values of the computed statistics.
void setMinimum(QList< double > &minimum)
Sets the minimum values of the computed statistics.
void record(const QString &name, double time, const QString &group="startup", const QString &id=QString())
Manually adds a profile event with the given name and total time (in seconds).
Scoped object for saving and restoring a QPainter object's state.
Scoped object for setting the current thread name.
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:6607
#define QgsDebugError(str)
Definition qgslogger.h:57
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
QgsPoint QgsMeshVertex
xyz coords of vertex