QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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  int count;
101  if ( mScalarDataOnVertices )
102  count = mNativeMesh.vertices.count();
103  else
104  count = mNativeMesh.faces.count();
105 
106  mScalarDatasetValues.resize( count );
107  for ( int i = 0; i < count; ++i )
108  {
109  double v = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar();
110  mScalarDatasetValues[i] = v;
111  }
112 
113  // populate face active flag, always defined on faces
115  for ( int i = 0; i < mNativeMesh.faces.count(); ++i )
116  {
117  bool active = layer->dataProvider()->isFaceActive( datasetIndex, i );
118  mScalarActiveFaceFlagValues[i] = active;
119  }
120 
121  QgsMeshLayerUtils::calculateMinimumMaximum( mScalarDatasetMinimum, mScalarDatasetMaximum, mScalarDatasetValues );
122  }
123 
124  // update cache
125  cache->mDatasetGroupsCount = datasetGroupCount;
126  cache->mActiveScalarDatasetIndex = datasetIndex;
127  cache->mScalarDatasetValues = mScalarDatasetValues;
128  cache->mScalarActiveFaceFlagValues = mScalarActiveFaceFlagValues;
129  cache->mScalarDataOnVertices = mScalarDataOnVertices;
130  cache->mScalarDatasetMinimum = mScalarDatasetMinimum;
131  cache->mScalarDatasetMaximum = mScalarDatasetMaximum;
132 }
133 
134 void QgsMeshLayerRenderer::copyVectorDatasetValues( QgsMeshLayer *layer )
135 {
137 
138  // Find out if we can use cache up to date. If yes, use it and return
139  const int datasetGroupCount = layer->dataProvider()->datasetGroupCount();
140  QgsMeshLayerRendererCache *cache = layer->rendererCache();
141  if ( ( cache->mDatasetGroupsCount == datasetGroupCount ) &&
142  ( cache->mActiveVectorDatasetIndex == datasetIndex ) )
143  {
144  mVectorDatasetValuesX = cache->mVectorDatasetValuesX;
145  mVectorDatasetValuesY = cache->mVectorDatasetValuesY;
146  mVectorDatasetValuesMag = cache->mVectorDatasetValuesMag;
147  mVectorDatasetMagMinimum = cache->mVectorDatasetMagMinimum;
148  mVectorDatasetMagMaximum = cache->mVectorDatasetMagMaximum;
149  mVectorDataOnVertices = cache->mVectorDataOnVertices;
150  return;
151  }
152 
153 
154  // Cache is not up-to-date, gather data
155  if ( datasetIndex.isValid() )
156  {
157  const QgsMeshDatasetGroupMetadata metadata = layer->dataProvider()->datasetGroupMetadata( datasetIndex );
158 
159  bool isScalar = metadata.isScalar();
160  if ( isScalar )
161  {
162  QgsDebugMsg( QStringLiteral( "Dataset has no vector values" ) );
163  }
164  else
165  {
167  int count;
168  if ( mVectorDataOnVertices )
169  count = mNativeMesh.vertices.count();
170  else
171  count = mNativeMesh.faces.count();
172 
173  mVectorDatasetValuesX.resize( count );
174  mVectorDatasetValuesY.resize( count );
175  mVectorDatasetValuesMag.resize( count );
176  for ( int i = 0; i < count; ++i )
177  {
178  double x = layer->dataProvider()->datasetValue( datasetIndex, i ).x();
179  mVectorDatasetValuesX[i] = x;
180 
181  double y = layer->dataProvider()->datasetValue( datasetIndex, i ).y();
182  mVectorDatasetValuesY[i] = y;
183 
184  double mag = layer->dataProvider()->datasetValue( datasetIndex, i ).scalar();
185  mVectorDatasetValuesMag[i] = mag;
186  }
187  }
188 
189  QgsMeshLayerUtils::calculateMinimumMaximum( mVectorDatasetMagMinimum, mVectorDatasetMagMaximum, mVectorDatasetValuesMag );
190  }
191 
192  // update cache
193  cache->mDatasetGroupsCount = datasetGroupCount;
194  cache->mActiveVectorDatasetIndex = datasetIndex;
195  cache->mVectorDatasetValuesX = mVectorDatasetValuesX;
196  cache->mVectorDatasetValuesY = mVectorDatasetValuesY;
197  cache->mVectorDatasetValuesMag = mVectorDatasetValuesMag;
198  cache->mVectorDatasetMagMinimum = mVectorDatasetMagMinimum;
199  cache->mVectorDatasetMagMaximum = mVectorDatasetMagMaximum;
200  cache->mVectorDataOnVertices = mVectorDataOnVertices;
201 }
202 
204 {
205  renderScalarDataset();
206  renderMesh();
207  renderVectorDataset();
208  return true;
209 }
210 
211 void QgsMeshLayerRenderer::renderMesh()
212 {
215  return;
216 
217  // triangular mesh
218  const QList<int> trianglesInExtent = mTriangularMesh.faceIndexesForRectangle( mContext.extent() );
220  {
223  trianglesInExtent );
224  }
225 
226  // native mesh
228  {
229  const QList<int> nativeFacesInExtent = QgsMeshUtils::nativeFacesFromTriangles( trianglesInExtent,
233  nativeFacesInExtent );
234  }
235 };
236 
237 void QgsMeshLayerRenderer::renderMesh( const QgsMeshRendererMeshSettings &settings, const QVector<QgsMeshFace> &faces, const QList<int> facesInExtent )
238 {
239  Q_ASSERT( settings.isEnabled() );
240 
241  // Set up the render configuration options
242  QPainter *painter = mContext.painter();
243  painter->save();
245  painter->setRenderHint( QPainter::Antialiasing, true );
246 
247  QPen pen = painter->pen();
248  pen.setCapStyle( Qt::FlatCap );
249  pen.setJoinStyle( Qt::MiterJoin );
250 
251  double penWidth = mContext.convertToPainterUnits( settings.lineWidth(),
252  QgsUnitTypes::RenderUnit::RenderMillimeters );
253  pen.setWidthF( penWidth );
254  pen.setColor( settings.color() );
255  painter->setPen( pen );
256 
257  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices(); //Triangular mesh vertices contains also native mesh vertices
258  QSet<QPair<int, int>> drawnEdges;
259 
260  for ( const int i : facesInExtent )
261  {
262  if ( mContext.renderingStopped() )
263  break;
264 
265  const QgsMeshFace &face = faces[i];
266  if ( face.size() < 2 )
267  continue;
268 
269  for ( int j = 0; j < face.size(); ++j )
270  {
271  const int startVertexId = face[j];
272  const int endVertexId = face[( j + 1 ) % face.size()];
273  const QPair<int, int> thisEdge( startVertexId, endVertexId );
274  const QPair<int, int> thisEdgeReversed( endVertexId, startVertexId );
275  if ( drawnEdges.contains( thisEdge ) || drawnEdges.contains( thisEdgeReversed ) )
276  continue;
277  drawnEdges.insert( thisEdge );
278  drawnEdges.insert( thisEdgeReversed );
279 
280  const QgsMeshVertex &startVertex = vertices[startVertexId];
281  const QgsMeshVertex &endVertex = vertices[endVertexId];
282  const QgsPointXY lineStart = mContext.mapToPixel().transform( startVertex.x(), startVertex.y() );
283  const QgsPointXY lineEnd = mContext.mapToPixel().transform( endVertex.x(), endVertex.y() );
284  painter->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
285  }
286  }
287 
288  painter->restore();
289 }
290 
291 void QgsMeshLayerRenderer::renderScalarDataset()
292 {
293  if ( mScalarDatasetValues.isEmpty() )
294  return; // activeScalarDataset == NO_ACTIVE_MESH_DATASET
295 
296  if ( std::isnan( mScalarDatasetMinimum ) || std::isnan( mScalarDatasetMaximum ) )
297  return; // only NODATA values
298 
300  if ( !index.isValid() )
301  return; // no shader
302 
303  const QgsMeshRendererScalarSettings scalarSettings = mRendererSettings.scalarSettings( index.group() );
304  QgsColorRampShader *fcn = new QgsColorRampShader( scalarSettings.colorRampShader() );
305  QgsRasterShader *sh = new QgsRasterShader();
306  sh->setRasterShaderFunction( fcn ); // takes ownership of fcn
307  QgsMeshLayerInterpolator interpolator( mTriangularMesh, mScalarDatasetValues, mScalarActiveFaceFlagValues,
309  QgsSingleBandPseudoColorRenderer renderer( &interpolator, 0, sh ); // takes ownership of sh
310  renderer.setClassificationMin( scalarSettings.classificationMinimum() );
311  renderer.setClassificationMax( scalarSettings.classificationMaximum() );
312  renderer.setOpacity( scalarSettings.opacity() );
313 
314  std::unique_ptr<QgsRasterBlock> bl( renderer.block( 0, mContext.extent(), mOutputSize.width(), mOutputSize.height(), mFeedback.get() ) );
315  QImage img = bl->image();
316 
317  mContext.painter()->drawImage( 0, 0, img );
318 }
319 
320 void QgsMeshLayerRenderer::renderVectorDataset()
321 {
323  if ( !index.isValid() )
324  return;
325 
326  if ( mVectorDatasetValuesX.isEmpty() )
327  return;
328 
329  if ( std::isnan( mVectorDatasetMagMinimum ) || std::isnan( mVectorDatasetMagMaximum ) )
330  return; // only NODATA values
331 
332  if ( mVectorDatasetValuesX.size() != mVectorDatasetValuesY.size() )
333  return;
334 
335  QgsMeshVectorRenderer renderer( mTriangularMesh,
339 
340  renderer.draw();
341 }
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:148
bool isScalar() const
Returns whether dataset group has scalar data.
QVector< bool > mScalarActiveFaceFlagValues
QgsMeshRendererScalarSettings scalarSettings(int groupIndex) const
Returns renderer settings.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
double y
Definition: qgspoint.h:42
Interface for all raster shaders.
QVector< double > mVectorDatasetValuesX
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
Use antialiasing while drawing.
QgsMeshRendererSettings mRendererSettings
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
QgsMeshRendererMeshSettings triangularMeshSettings() const
Returns renderer settings.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
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
Represents a mesh renderer settings for scalar datasets.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
QVector< double > mScalarDatasetValues
double classificationMinimum() const
Returns min value used for creation of the color ramp shader.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering)
QVector< double > mVectorDatasetValuesMag
QVector< QgsMeshVertex > vertices
vertices
QgsTriangularMesh mTriangularMesh
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
bool isEnabled() const
Returns whether mesh structure rendering is enabled.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be null)
bool render() override
Do the rendering (based on data stored in the class)
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
bool renderingStopped() const
Returns TRUE if the rendering operation has been stopped and any ongoing rendering should be canceled...
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
QColor color() const
Returns color used for rendering.
Raster renderer pipe for single band pseudocolor.
double y() const
Returns y value.
QgsMeshDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
QVector< double > mVectorDatasetValuesY
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 scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
double x
Definition: qgspointxy.h:47
QList< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces.
QgsRenderContext & mContext
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsMeshLayerRenderer(QgsMeshLayer *layer, QgsRenderContext &context)
Ctor.
virtual bool isFaceActive(QgsMeshDatasetIndex index, int faceIndex) const =0
Returns whether the face is active for particular dataset.
double classificationMaximum() const
Returns max value used for creation of the color ramp shader.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
QVector< int > QgsMeshFace
List of vertex indexes.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
Flags flags() const
Returns combination of flags used for rendering.
QgsMeshLayerRendererCache * rendererCache()
Returns native mesh (nullptr before rendering)
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
Base class for utility classes that encapsulate information necessary for rendering of map layers...
QgsMeshDatasetIndex activeVectorDataset() const
Returns active vector dataset.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:89
QVector< QgsMeshFace > faces
faces
int group() const
Returns a group index.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original 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...
double x() const
Returns x value.
virtual QgsMeshDatasetValue datasetValue(QgsMeshDatasetIndex index, int valueIndex) const =0
Returns vector/scalar value associated with the index from the dataset.
QgsMeshDatasetIndex activeScalarDataset() const
Returns active scalar dataset.
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
QgsTriangularMesh * triangularMesh()
Returns triangular mesh (nullptr before rendering)
double opacity() const
Returns opacity.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
QgsMeshRendererVectorSettings vectorSettings(int groupIndex) const
Returns renderer settings.
QgsMeshRendererMeshSettings nativeMeshSettings() const
Returns renderer settings.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
double x
Definition: qgspoint.h:41
double lineWidth() const
Returns line width used for rendering (in millimeters)