QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 
22 #include "qgsmeshlayerrenderer.h"
23 
24 #include "qgsfield.h"
25 #include "qgslogger.h"
26 #include "qgsmeshlayer.h"
27 #include "qgspointxy.h"
28 #include "qgsrenderer.h"
30 #include "qgsrastershader.h"
32 #include "qgsmeshlayerutils.h"
33 #include "qgsmeshvectorrenderer.h"
34 #include "qgsmeshtracerenderer.h"
35 #include "qgsfillsymbollayer.h"
36 #include "qgssettings.h"
37 #include "qgsstyle.h"
38 
39 
41  : QgsMapLayerRenderer( layer->id(), &context )
42  , mFeedback( new QgsMeshLayerRendererFeedback )
43  , mRendererSettings( layer->rendererSettings() )
44 {
45  // make copies for mesh data
46  Q_ASSERT( layer->nativeMesh() );
47  Q_ASSERT( layer->triangularMesh() );
48  Q_ASSERT( layer->rendererCache() );
49  Q_ASSERT( layer->dataProvider() );
50 
51  mNativeMesh = *( layer->nativeMesh() );
52  mTriangularMesh = *( layer->triangularMesh() );
53  mLayerExtent = layer->extent();
54 
55  copyScalarDatasetValues( layer );
56  copyVectorDatasetValues( layer );
57 
58  calculateOutputSize();
59 }
60 
62 {
63  return mFeedback.get();
64 }
65 
66 void QgsMeshLayerRenderer::calculateOutputSize()
67 {
68  // figure out image size
69  QgsRenderContext &context = *renderContext();
70  QgsRectangle extent = context.mapExtent();
71  QgsMapToPixel mapToPixel = context.mapToPixel();
72  QgsPointXY topleft = mapToPixel.transform( extent.xMinimum(), extent.yMaximum() );
73  QgsPointXY bottomright = mapToPixel.transform( extent.xMaximum(), extent.yMinimum() );
74  int width = int( bottomright.x() - topleft.x() );
75  int height = int( bottomright.y() - topleft.y() );
76  mOutputSize = QSize( width, height );
77 }
78 
79 void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
80 {
82 
83  // Find out if we can use cache up to date. If yes, use it and return
84  const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
85  const QgsMeshRendererScalarSettings::DataInterpolationMethod method = mRendererSettings.scalarSettings( datasetIndex.group() ).dataInterpolationMethod();
86  QgsMeshLayerRendererCache *cache = layer->rendererCache();
87  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
88  ( cache->mActiveScalarDatasetIndex == datasetIndex ) &&
89  ( cache->mDataInterpolationMethod == method ) &&
90  ( QgsMesh3dAveragingMethod::equals( cache->mScalarAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
91  )
92  {
93  mScalarDatasetValues = cache->mScalarDatasetValues;
94  mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
95  mScalarDataOnVertices = cache->mScalarDataOnVertices;
96  mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
97  mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
98  return;
99  }
100 
101  // Cache is not up-to-date, gather data
102  if ( datasetIndex.isValid() )
103  {
104  const QgsMeshDatasetGroupMetadata metadata = layer->dataProvider()->datasetGroupMetadata( datasetIndex );
106 
107  // populate scalar values
108  const int count = mScalarDataOnVertices ? mNativeMesh.vertices.count() : mNativeMesh.faces.count();
109  QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
110  layer,
111  datasetIndex,
112  0,
113  count );
114 
115  if ( vals.isValid() )
116  {
117  // vals could be scalar or vectors, for contour rendering we want always magnitude
118  mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
119  }
120  else
121  {
122  mScalarDatasetValues = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
123  }
124 
125  // populate face active flag, always defined on faces
127  datasetIndex,
128  0,
129  mNativeMesh.faces.count() );
130 
131  // for data on faces, there could be request to interpolate the data to vertices
133  {
134  mScalarDataOnVertices = true;
135  mScalarDatasetValues = QgsMeshLayerUtils::interpolateFromFacesData(
137  &mNativeMesh,
139  &mScalarActiveFaceFlagValues,
140  method
141  );
142  }
143 
144  const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex );
145  mScalarDatasetMinimum = datasetMetadata.minimum();
146  mScalarDatasetMaximum = datasetMetadata.maximum();
147  }
148 
149  // update cache
150  cache->mDatasetGroupsCount = datasetGroupCount;
151  cache->mActiveScalarDatasetIndex = datasetIndex;
152  cache->mDataInterpolationMethod = method;
153  cache->mScalarDatasetValues = mScalarDatasetValues;
154  cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
155  cache->mScalarDataOnVertices = mScalarDataOnVertices;
156  cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
157  cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
158  cache->mScalarAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
159 }
160 
161 void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
162 {
164 
165  // Find out if we can use cache up to date. If yes, use it and return
166  const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
167  QgsMeshLayerRendererCache *cache = layer->rendererCache();
168  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
169  ( cache->mActiveVectorDatasetIndex == datasetIndex ) &&
170  ( QgsMesh3dAveragingMethod::equals( cache->mVectorAveragingMethod.get(), mRendererSettings.averagingMethod() ) )
171  )
172  {
173  mVectorDatasetValues = cache->mVectorDatasetValues;
174  mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
175  mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
176  mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
177  mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
178  mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
179  mVectorDataOnVertices = cache->mVectorDataOnVertices;
180  return;
181  }
182 
183  // Cache is not up-to-date, gather data
184  if ( datasetIndex.isValid() )
185  {
186  const QgsMeshDatasetGroupMetadata metadata = layer->dataProvider()->datasetGroupMetadata( datasetIndex );
187 
188  bool isScalar = metadata.isScalar();
189  if ( isScalar )
190  {
191  QgsDebugMsg( QStringLiteral( "Dataset has no vector values" ) );
192  }
193  else
194  {
198 
199  int count;
200  if ( mVectorDataOnVertices )
201  count = mNativeMesh.vertices.count();
202  else
203  count = mNativeMesh.faces.count();
204 
205  mVectorDatasetValues = QgsMeshLayerUtils::datasetValues(
206  layer,
207  datasetIndex,
208  0,
209  count );
210 
212  mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
213  else
214  mVectorDatasetValuesMag = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
215 
216  const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex );
217  mVectorDatasetMagMinimum = datasetMetadata.minimum();
218  mVectorDatasetMagMaximum = datasetMetadata.maximum();
219  }
220  }
221 
222  // update cache
223  cache->mDatasetGroupsCount = datasetGroupCount;
224  cache->mActiveVectorDatasetIndex = datasetIndex;
225  cache->mVectorDatasetValues = mVectorDatasetValues;
226  cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
227  cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
228  cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
229  cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
230  cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
231  cache->mVectorDataOnVertices = mVectorDataOnVertices;
232  cache->mVectorAveragingMethod.reset( mRendererSettings.averagingMethod() ? mRendererSettings.averagingMethod()->clone() : nullptr );
233 }
234 
236 {
237  renderScalarDataset();
238  renderMesh();
239  renderVectorDataset();
240  return true;
241 }
242 
243 void QgsMeshLayerRenderer::renderMesh()
244 {
247  return;
248 
249  // triangular mesh
250  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( renderContext()->mapExtent() );
252  {
255  trianglesInExtent );
256  }
257 
258  // native mesh
260  {
261  const QList<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
265  nativeFacesInExtent );
266  }
267 };
268 
269 void QgsMeshLayerRenderer::renderMesh( const QgsMeshRendererMeshSettings &settings, const QVector<QgsMeshFace> &faces, const QList<int> &facesInExtent )
270 {
271  Q_ASSERT( settings.isEnabled() );
272 
273  QgsRenderContext &context = *renderContext();
274  // Set up the render configuration options
275  QPainter *painter = context.painter();
276  painter->save();
277  if ( context.flags() & QgsRenderContext::Antialiasing )
278  painter->setRenderHint( QPainter::Antialiasing, true );
279 
280  QPen pen = painter->pen();
281  pen.setCapStyle( Qt::FlatCap );
282  pen.setJoinStyle( Qt::MiterJoin );
283 
284  double penWidth = context.convertToPainterUnits( settings.lineWidth(),
285  QgsUnitTypes::RenderUnit::RenderMillimeters );
286  pen.setWidthF( penWidth );
287  pen.setColor( settings.color() );
288  painter->setPen( pen );
289 
290  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
291  QSet<QPair<int, int>> drawnEdges;
292 
293  for ( const int i : facesInExtent )
294  {
295  if ( context.renderingStopped() )
296  break;
297 
298  const QgsMeshFace &face = faces[i];
299  if ( face.size() < 2 )
300  continue;
301 
302  for ( int j = 0; j < face.size(); ++j )
303  {
304  const int startVertexId = face[j];
305  const int endVertexId = face[( j + 1 ) % face.size()];
306  const QPair<int, int> thisEdge( startVertexId, endVertexId );
307  const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
308  if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
309  continue;
310  drawnEdges.insert( thisEdge );
311  drawnEdges.insert( thisEdgeReversed );
312 
313  const QgsMeshVertex &startVertex = vertices[startVertexId];
314  const QgsMeshVertex &endVertex = vertices[endVertexId];
315  const QgsPointXY lineStart = context.mapToPixel().transform( startVertex.x(), startVertex.y() );
316  const QgsPointXY lineEnd = context.mapToPixel().transform( endVertex.x(), endVertex.y() );
317  painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
318  }
319  }
320 
321  painter->restore();
322 }
323 
324 void QgsMeshLayerRenderer::renderScalarDataset()
325 {
326  if ( mScalarDatasetValues.isEmpty() )
327  return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
328 
329  if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
330  return; // only NODATA values
331 
333  if ( !index.isValid() )
334  return; // no shader
335 
336  QgsRenderContext &context = *renderContext();
337  const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( index.group() );
338  QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
339  QgsRasterShader *sh = new QgsRasterShader();
340  sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
341  QgsMeshLayerInterpolator interpolator( mTriangularMesh,
345  context,
346  mOutputSize );
347  QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
348  renderer.setClassificationMin( scalarSettings.classificationMinimum() );
349  renderer.setClassificationMax( scalarSettings.classificationMaximum() );
350  renderer.setOpacity( scalarSettings.opacity() );
351 
352  std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, context.mapExtent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
353  QImage img = bl->image();
354 
355  context.painter()->drawImage( 0, 0, img );
356 }
357 
358 void QgsMeshLayerRenderer::renderVectorDataset()
359 {
361  if ( !index.isValid() )
362  return;
363 
364  if ( !mVectorDatasetValues.isValid() )
365  return; // no data at all
366 
367  if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
368  return; // only NODATA values
369 
370  std::unique_ptr<QgsMeshVectorRenderer> renderer( QgsMeshVectorRenderer::makeVectorRenderer(
379  *renderContext(),
380  mLayerExtent,
381  mOutputSize ) );
382 
383  if ( renderer )
384  renderer->draw();
385 }
386 
A rectangle specified with double values.
Definition: qgsrectangle.h:41
double y
Definition: qgspoint.h:42
Interface for all raster shaders.
Use antialiasing while drawing.
QgsMeshRendererSettings mRendererSettings
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
double y
Definition: qgspointxy.h:48
Represents a mesh renderer settings for mesh object.
A class to represent a 2D point.
Definition: qgspointxy.h:43
Use data defined on face centers, do not interpolate to vertices.
double maximum() const
Returns maximum scalar value/vector magnitude present for the dataset.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e...
Represents a mesh renderer settings for scalar datasets.
bool isEnabled() const
Returns whether mesh structure rendering is enabled.
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
QVector< double > mScalarDatasetValues
QgsMeshRendererScalarSettings scalarSettings(int groupIndex) const
Returns renderer settings.
int group() const
Returns a group index.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering)
static bool equals(const QgsMesh3dAveragingMethod *a, const QgsMesh3dAveragingMethod *b)
Returns whether two methods equal.
QgsMeshDataBlock mVectorDatasetValues
QVector< double > mVectorDatasetValuesMag
QVector< QgsMeshVertex > vertices
vertices
QgsTriangularMesh mTriangularMesh
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
double opacity() const
Returns opacity.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:45
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
double lineWidth() const
Returns line width used for rendering (in millimeters)
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be nullptr)
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
bool render() override
Do the rendering (based on data stored in the class)
QgsMeshDatasetIndex activeVectorDataset() const
Returns active vector dataset.
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:154
QgsRectangle extent() const override
Returns the extent of the layer.
virtual QgsMesh3dAveragingMethod * clone() const =0
Clone the instance.
DataInterpolationMethod
Interpolation of value defined on vertices from datasets with data defined on faces.
Raster renderer pipe for single band pseudocolor.
QgsMeshDataProvider * dataProvider() override
Returns the layer&#39;s data provider, it may be nullptr.
double minimum() const
Returns minimum scalar value/vector magnitude present for the dataset.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
std::unique_ptr< QgsMeshLayerRendererFeedback > mFeedback
feedback class for cancellation
double x
Definition: qgspointxy.h:47
CORE_EXPORT QList< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
bool isScalar() const
Returns whether dataset group has scalar data.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
QgsMesh3dAveragingMethod * averagingMethod() const
Returns averaging method for conversion of 3d stacked mesh data to 2d data.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
virtual QgsMeshDatasetMetadata datasetMetadata(QgsMeshDatasetIndex index) const =0
Returns dataset metadata.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
QgsMeshLayerRenderer(QgsMeshLayer *layer, QgsRenderContext &context)
Ctor.
QgsMeshRendererMeshSettings nativeMeshSettings() const
Returns renderer settings.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
bool isValid() const
Whether the block is valid.
QColor color() const
Returns color used for rendering.
QVector< int > QgsMeshFace
List of vertex indexes.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
QgsMeshRendererMeshSettings triangularMeshSettings() const
Returns renderer settings.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
bool isValid() const
Returns whether index is valid, ie at least groups is set.
Base class for utility classes that encapsulate information necessary for rendering of map layers...
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:91
QVector< QgsMeshFace > faces
faces
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
void setOpacity(double opacity)
Sets the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1...
QgsMeshDatasetMetadata is a collection of mesh dataset metadata such as whether the data is valid or ...
QgsMeshDataBlock mScalarActiveFaceFlagValues
double minimum() const
Returns minimum scalar value/vector magnitude present for whole dataset group.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
double classificationMaximum() const
Returns max value used for creation of the color ramp shader.
QgsTriangularMesh * triangularMesh()
Returns triangular mesh (nullptr before rendering)
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
double classificationMinimum() const
Returns min value used for creation of the color ramp shader.
virtual QgsMeshDataBlock areFacesActive(QgsMeshDatasetIndex index, int faceIndex, int count) const =0
Returns whether the faces are active for particular dataset.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
double x
Definition: qgspoint.h:41
QgsMeshDatasetIndex activeScalarDataset() const
Returns active scalar dataset.