QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 "qgsfillsymbollayer.h"
35 #include "qgssettings.h"
36 #include "qgsstyle.h"
37 
38 
40  : QgsMapLayerRenderer( layer->id() )
41  , mFeedback( new QgsMeshLayerRendererFeedback )
42  , mContext( context )
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 
54  copyScalarDatasetValues( layer );
55  copyVectorDatasetValues( layer );
56 
57  calculateOutputSize();
58 }
59 
61 {
62  return mFeedback.get();
63 }
64 
65 void QgsMeshLayerRenderer::calculateOutputSize()
66 {
67  // figure out image size
68  QgsRectangle extent = mContext.extent(); // this is extent in layer's coordinate system - but we need it in map coordinate system
69  QgsMapToPixel mapToPixel = mContext.mapToPixel();
70  QgsPointXY topleft = mapToPixel.transform( extent.xMinimum(), extent.yMaximum() );
71  QgsPointXY bottomright = mapToPixel.transform( extent.xMaximum(), extent.yMinimum() );
72  int width = int( bottomright.x() - topleft.x() );
73  int height = int( bottomright.y() - topleft.y() );
74  mOutputSize = QSize( width, height );
75 }
76 
77 void QgsMeshLayerRenderer::copyScalarDatasetValues( QgsMeshLayer *layer )
78 {
80 
81  // Find out if we can use cache up to date. If yes, use it and return
82  const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
83  QgsMeshLayerRendererCache *cache = layer->rendererCache();
84  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
85  ( cache->mActiveScalarDatasetIndex == datasetIndex ) )
86  {
87  mScalarDatasetValues = cache->mScalarDatasetValues;
88  mScalarActiveFaceFlagValues = cache->mScalarActiveFaceFlagValues;
89  mScalarDataOnVertices = cache->mScalarDataOnVertices;
90  mScalarDatasetMinimum = cache->mScalarDatasetMinimum;
91  mScalarDatasetMaximum = cache->mScalarDatasetMaximum;
92  return;
93  }
94 
95  // Cache is not up-to-date, gather data
96  if ( datasetIndex.isValid() )
97  {
98  const QgsMeshDatasetGroupMetadata metadata = layer->dataProvider()->datasetGroupMetadata( datasetIndex );
100 
101  // populate scalar values
103  datasetIndex,
104  0,
106 
107  // vals could be scalar or vectors, for contour rendering we want always magnitude
108  mScalarDatasetValues = QgsMeshLayerUtils::calculateMagnitudes( vals );
109 
110  // populate face active flag, always defined on faces
112  datasetIndex,
113  0,
114  mNativeMesh.faces.count() );
115 
116  const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex );
117  mScalarDatasetMinimum = datasetMetadata.minimum();
118  mScalarDatasetMaximum = datasetMetadata.maximum();
119  }
120 
121  // update cache
122  cache->mDatasetGroupsCount = datasetGroupCount;
123  cache->mActiveScalarDatasetIndex = datasetIndex;
124  cache->mScalarDatasetValues = mScalarDatasetValues;
125  cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
126  cache->mScalarDataOnVertices = mScalarDataOnVertices;
127  cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
128  cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
129 }
130 
131 void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
132 {
134 
135  // Find out if we can use cache up to date. If yes, use it and return
136  const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
137  QgsMeshLayerRendererCache *cache = layer->rendererCache();
138  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
139  ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
140  {
141  mVectorDatasetValues = cache->mVectorDatasetValues;
142  mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
143  mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
144  mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
145  mVectorDatasetGroupMagMinimum = cache->mVectorDatasetMagMinimum;
146  mVectorDatasetGroupMagMaximum = cache->mVectorDatasetMagMaximum;
147  mVectorDataOnVertices = cache->mVectorDataOnVertices;
148  return;
149  }
150 
151 
152  // Cache is not up-to-date, gather data
153  if ( datasetIndex.isValid() )
154  {
155  const QgsMeshDatasetGroupMetadata metadata = layer->dataProvider()->datasetGroupMetadata( datasetIndex );
156 
157  bool isScalar = metadata.isScalar();
158  if ( isScalar )
159  {
160  QgsDebugMsg( QStringLiteral( "Dataset has no vector values" ) );
161  }
162  else
163  {
167 
168  int count;
169  if ( mVectorDataOnVertices )
170  count = mNativeMesh.vertices.count();
171  else
172  count = mNativeMesh.faces.count();
173 
174 
176  datasetIndex,
177  0,
178  count );
179 
180  mVectorDatasetValuesMag = QgsMeshLayerUtils::calculateMagnitudes( mVectorDatasetValues );
181 
182  const QgsMeshDatasetMetadata datasetMetadata = layer->dataProvider()->datasetMetadata( datasetIndex );
183  mVectorDatasetMagMinimum = datasetMetadata.minimum();
184  mVectorDatasetMagMaximum = datasetMetadata.maximum();
185  }
186  }
187 
188  // update cache
189  cache->mDatasetGroupsCount = datasetGroupCount;
190  cache->mActiveVectorDatasetIndex = datasetIndex;
191  cache->mVectorDatasetValues = mVectorDatasetValues;
192  cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
193  cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
194  cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
195  cache->mVectorDatasetGroupMagMinimum = mVectorDatasetMagMinimum;
196  cache->mVectorDatasetGroupMagMaximum = mVectorDatasetMagMaximum;
197  cache->mVectorDataOnVertices = mVectorDataOnVertices;
198 }
199 
201 {
202  renderScalarDataset();
203  renderMesh();
204  renderVectorDataset();
205  return true;
206 }
207 
208 void QgsMeshLayerRenderer::renderMesh()
209 {
212  return;
213 
214  // triangular mesh
215  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mContext.extent() );
217  {
220  trianglesInExtent );
221  }
222 
223  // native mesh
225  {
226  const QList<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
230  nativeFacesInExtent );
231  }
232 };
233 
234 void QgsMeshLayerRenderer::renderMesh( const QgsMeshRendererMeshSettings &settings, const QVector<QgsMeshFace> &faces, const QList<int> facesInExtent )
235 {
236  Q_ASSERT( settings.isEnabled() );
237 
238  // Set up the render configuration options
239  QPainter *painter = mContext.painter();
240  painter->save();
242  painter->setRenderHint( QPainter::Antialiasing, true );
243 
244  QPen pen = painter->pen();
245  pen.setCapStyle( Qt::FlatCap );
246  pen.setJoinStyle( Qt::MiterJoin );
247 
248  double penWidth = mContext.convertToPainterUnits( settings.lineWidth(),
249  QgsUnitTypes::RenderUnit::RenderMillimeters );
250  pen.setWidthF( penWidth );
251  pen.setColor( settings.color() );
252  painter->setPen( pen );
253 
254  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
255  QSet<QPair<int, int>> drawnEdges;
256 
257  for ( const int i : facesInExtent )
258  {
259  if ( mContext.renderingStopped() )
260  break;
261 
262  const QgsMeshFace &face = faces[i];
263  if ( face.size() < 2 )
264  continue;
265 
266  for ( int j = 0; j < face.size(); ++j )
267  {
268  const int startVertexId = face[j];
269  const int endVertexId = face[( j + 1 ) % face.size()];
270  const QPair<int, int> thisEdge( startVertexId, endVertexId );
271  const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
272  if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
273  continue;
274  drawnEdges.insert( thisEdge );
275  drawnEdges.insert( thisEdgeReversed );
276 
277  const QgsMeshVertex &startVertex = vertices[startVertexId];
278  const QgsMeshVertex &endVertex = vertices[endVertexId];
279  const QgsPointXY lineStart = mContext.mapToPixel().transform( startVertex.x(), startVertex.y() );
280  const QgsPointXY lineEnd = mContext.mapToPixel().transform( endVertex.x(), endVertex.y() );
281  painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
282  }
283  }
284 
285  painter->restore();
286 }
287 
288 void QgsMeshLayerRenderer::renderScalarDataset()
289 {
290  if ( mScalarDatasetValues.isEmpty() )
291  return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
292 
293  if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
294  return; // only NODATA values
295 
297  if ( !index.isValid() )
298  return; // no shader
299 
300  const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( index.group() );
301  QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
302  QgsRasterShader *sh = new QgsRasterShader();
303  sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
304  QgsMeshLayerInterpolator interpolator( mTriangularMesh,
308  mContext,
309  mOutputSize );
310  QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
311  renderer.setClassificationMin( scalarSettings.classificationMinimum() );
312  renderer.setClassificationMax( scalarSettings.classificationMaximum() );
313  renderer.setOpacity( scalarSettings.opacity() );
314 
315  std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, mContext.extent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
316  QImage img = bl->image();
317 
318  mContext.painter()->drawImage( 0, 0, img );
319 }
320 
321 void QgsMeshLayerRenderer::renderVectorDataset()
322 {
324  if ( !index.isValid() )
325  return;
326 
327  if ( !mVectorDatasetValues.isValid() )
328  return; // no data at all
329 
330  if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
331  return; // only NODATA values
332 
333  QgsMeshVectorRenderer renderer( mTriangularMesh,
340  mContext,
341  mOutputSize );
342 
343  renderer.draw();
344 }
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
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.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
Flags flags() const
Returns combination of flags used for rendering.
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)
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.
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:44
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:148
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
Raster renderer pipe for single band pseudocolor.
virtual QgsMeshDataBlock datasetValues(QgsMeshDatasetIndex index, int valueIndex, int count) const =0
Returns N vector/scalar values from the index from the dataset.
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.
QgsRenderContext & mContext
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.
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.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
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:90
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.
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.