QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsmeshvectorrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmeshvectorrenderer.cpp
3  -------------------------
4  begin : May 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 "qgsmeshvectorrenderer.h"
19 #include "qgsrendercontext.h"
20 #include "qgscoordinatetransform.h"
21 #include "qgsmaptopixel.h"
22 #include "qgsunittypes.h"
23 
24 #include <cstdlib>
25 #include <ctime>
26 #include <algorithm>
27 #include <QPen>
28 #include <QPainter>
29 #include <cmath>
30 
32 
33 inline double mag( double input )
34 {
35  if ( input < 0.0 )
36  {
37  return -1.0;
38  }
39  return 1.0;
40 }
41 
42 QgsMeshVectorRenderer::QgsMeshVectorRenderer( const QgsTriangularMesh &m,
43  const QVector<double> &datasetValuesX,
44  const QVector<double> &datasetValuesY,
45  const QVector<double> &datasetValuesMag,
46  bool dataIsOnVertices,
47  const QgsMeshRendererVectorSettings &settings,
48  QgsRenderContext &context, const QSize &size )
49  : mTriangularMesh( m )
50  , mDatasetValuesX( datasetValuesX )
51  , mDatasetValuesY( datasetValuesY )
52  , mDatasetValuesMag( datasetValuesMag )
53  , mContext( context )
54  , mCfg( settings )
55  , mDataOnVertices( dataIsOnVertices )
56  , mOutputSize( size )
57 {
58  auto bounds = std::minmax_element( mDatasetValuesX.constBegin(), mDatasetValuesX.constEnd() );
59  mMinX = *bounds.first;
60  mMaxX = *bounds.second;
61 
62  bounds = std::minmax_element( mDatasetValuesY.constBegin(), mDatasetValuesY.constEnd() );
63  mMinY = *bounds.first;
64  mMaxY = *bounds.second;
65 
66  bounds = std::minmax_element( mDatasetValuesMag.constBegin(), mDatasetValuesMag.constEnd() );
67  mMinMag = *bounds.first;
68  mMaxMag = *bounds.second;
69 }
70 
71 QgsMeshVectorRenderer::~QgsMeshVectorRenderer() = default;
72 
73 void QgsMeshVectorRenderer::draw()
74 {
75  // Set up the render configuration options
76  QPainter *painter = mContext.painter();
77  painter->save();
78  if ( mContext.flags() & QgsRenderContext::Antialiasing )
79  painter->setRenderHint( QPainter::Antialiasing, true );
80 
81  painter->setRenderHint( QPainter::Antialiasing );
82  QPen pen = painter->pen();
83  pen.setCapStyle( Qt::FlatCap );
84  pen.setJoinStyle( Qt::MiterJoin );
85 
86  double penWidth = mContext.convertToPainterUnits( mCfg.lineWidth(),
87  QgsUnitTypes::RenderUnit::RenderMillimeters );
88  pen.setWidthF( penWidth );
89  pen.setColor( mCfg.color() );
90  painter->setPen( pen );
91 
92  if ( mDataOnVertices )
93  drawVectorDataOnVertices();
94  else
95  drawVectorDataOnFaces();
96 
97  painter->restore();
98 }
99 
100 bool QgsMeshVectorRenderer::calcVectorLineEnd(
101  QgsPointXY &lineEnd,
102  double &vectorLength,
103  double &cosAlpha,
104  double &sinAlpha, //out
105  const QgsPointXY &lineStart,
106  double xVal,
107  double yVal,
108  double magnitude //in
109 )
110 {
111  // return true on error
112 
113  if ( xVal == 0.0 && yVal == 0.0 )
114  return true;
115 
116  // do not render if magnitude is outside of the filtered range (if filtering is enabled)
117  if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
118  return true;
119  if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
120  return true;
121 
122  // Determine the angle of the vector, counter-clockwise, from east
123  // (and associated trigs)
124  double vectorAngle = -1.0 * atan( ( -1.0 * yVal ) / xVal );
125  cosAlpha = cos( vectorAngle ) * mag( xVal );
126  sinAlpha = sin( vectorAngle ) * mag( xVal );
127 
128  // Now determine the X and Y distances of the end of the line from the start
129  double xDist = 0.0;
130  double yDist = 0.0;
131  switch ( mCfg.shaftLengthMethod() )
132  {
133  case QgsMeshRendererVectorSettings::ArrowScalingMethod::MinMax:
134  {
135  double minShaftLength = mContext.convertToPainterUnits( mCfg.minShaftLength(),
136  QgsUnitTypes::RenderUnit::RenderMillimeters );
137  double maxShaftLength = mContext.convertToPainterUnits( mCfg.maxShaftLength(),
138  QgsUnitTypes::RenderUnit::RenderMillimeters );
139  double minVal = mMinMag;
140  double maxVal = mMaxMag;
141  double k = ( magnitude - minVal ) / ( maxVal - minVal );
142  double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
143  xDist = cosAlpha * L;
144  yDist = sinAlpha * L;
145  break;
146  }
147  case QgsMeshRendererVectorSettings::ArrowScalingMethod::Scaled:
148  {
149  double scaleFactor = mCfg.scaleFactor();
150  xDist = scaleFactor * xVal;
151  yDist = scaleFactor * yVal;
152  break;
153  }
154  case QgsMeshRendererVectorSettings::ArrowScalingMethod::Fixed:
155  {
156  // We must be using a fixed length
157  double fixedShaftLength = mContext.convertToPainterUnits( mCfg.fixedShaftLength(),
158  QgsUnitTypes::RenderUnit::RenderMillimeters );
159  xDist = cosAlpha * fixedShaftLength;
160  yDist = sinAlpha * fixedShaftLength;
161  break;
162  }
163  }
164 
165  // Flip the Y axis (pixel vs real-world axis)
166  yDist *= -1.0;
167 
168  if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
169  return true;
170 
171  // Determine the line coords
172  lineEnd = QgsPointXY( lineStart.x() + xDist,
173  lineStart.y() + yDist );
174 
175  vectorLength = sqrt( xDist * xDist + yDist * yDist );
176 
177  // Check to see if any of the coords are outside the QImage area, if so, skip the whole vector
178  if ( lineStart.x() < 0 || lineStart.x() > mOutputSize.width() ||
179  lineStart.y() < 0 || lineStart.y() > mOutputSize.height() ||
180  lineEnd.x() < 0 || lineEnd.x() > mOutputSize.width() ||
181  lineEnd.y() < 0 || lineEnd.y() > mOutputSize.height() )
182  return true;
183 
184  return false; //success
185 }
186 
187 
188 void QgsMeshVectorRenderer::drawVectorDataOnVertices()
189 {
190  const QVector<QgsMeshVertex> &vertices = mTriangularMesh.vertices();
191 
192  // currently expecting that triangulation does not add any new extra vertices on the way
193  Q_ASSERT( mDatasetValuesMag.count() == vertices.count() );
194 
195  for ( int i = 0; i < vertices.size(); ++i )
196  {
197  const QgsMeshVertex &vertex = vertices.at( i );
198  //if (!nodeInsideView(nodeIndex))
199  // continue;
200 
201  double xVal = mDatasetValuesX[i];
202  double yVal = mDatasetValuesY[i];
203  double V = mDatasetValuesMag[i]; // pre-calculated magnitude
204  QgsPointXY lineStart = mContext.mapToPixel().transform( vertex.x(), vertex.y() );
205 
206  drawVectorArrow( lineStart, xVal, yVal, V );
207  }
208 }
209 
210 void QgsMeshVectorRenderer::drawVectorDataOnFaces()
211 {
212  const QVector<QgsMeshVertex> &centroids = mTriangularMesh.centroids();
213 
214  for ( int i = 0; i < centroids.count(); i++ )
215  {
216  //if (elemOutsideView(elemIndex))
217  // continue;
218 
219  QgsPointXY center = centroids.at( i );
220  double xVal = mDatasetValuesX[i];
221  double yVal = mDatasetValuesY[i];
222  double V = mDatasetValuesMag[i]; // pre-calculated magnitude
223  QgsPointXY lineStart = mContext.mapToPixel().transform( center.x(), center.y() );
224 
225  drawVectorArrow( lineStart, xVal, yVal, V );
226  }
227 }
228 
229 
230 void QgsMeshVectorRenderer::drawVectorArrow( const QgsPointXY &lineStart, double xVal, double yVal, double magnitude )
231 {
232  QgsPointXY lineEnd;
233  double vectorLength;
234  double cosAlpha, sinAlpha;
235  if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha,
236  lineStart, xVal, yVal, magnitude ) )
237  return;
238 
239  // Make a set of vector head coordinates that we will place at the end of each vector,
240  // scale, translate and rotate.
241  QgsPointXY vectorHeadPoints[3];
242  QVector<QPointF> finalVectorHeadPoints( 3 );
243 
244  double vectorHeadWidthRatio = mCfg.arrowHeadWidthRatio();
245  double vectorHeadLengthRatio = mCfg.arrowHeadLengthRatio();
246 
247  // First head point: top of ->
248  vectorHeadPoints[0].setX( -1.0 * vectorHeadLengthRatio );
249  vectorHeadPoints[0].setY( vectorHeadWidthRatio * 0.5 );
250 
251  // Second head point: right of ->
252  vectorHeadPoints[1].setX( 0.0 );
253  vectorHeadPoints[1].setY( 0.0 );
254 
255  // Third head point: bottom of ->
256  vectorHeadPoints[2].setX( -1.0 * vectorHeadLengthRatio );
257  vectorHeadPoints[2].setY( -1.0 * vectorHeadWidthRatio * 0.5 );
258 
259  // Determine the arrow head coords
260  for ( int j = 0; j < 3; j++ )
261  {
262  finalVectorHeadPoints[j].setX( lineEnd.x()
263  + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength )
264  - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength )
265  );
266 
267  finalVectorHeadPoints[j].setY( lineEnd.y()
268  - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength )
269  - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength )
270  );
271  }
272 
273  // Now actually draw the vector
274  mContext.painter()->drawLine( lineStart.toQPointF(), lineEnd.toQPointF() );
275  mContext.painter()->drawPolygon( finalVectorHeadPoints );
276 }
277 
double y
Definition: qgspoint.h:42
double minShaftLength() const
Returns mininimum shaft length (in millimeters)
Triangular/Derived Mesh.
double fixedShaftLength() const
Returns fixed arrow length (in millimeters)
Use antialiasing while drawing.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:148
Represents a mesh renderer settings for vector datasets.
void setY(double y)
Sets the y value of the point.
Definition: qgspointxy.h:113
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:104
double x
Definition: qgspointxy.h:47
double maxShaftLength() const
Returns maximum shaft length (in millimeters)
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns scale factor.
double x
Definition: qgspoint.h:41