22using namespace Qt::StringLiterals;
25#define M_DEG2RAD 0.0174532925
29 : mMinMag( datasetMagMinimumValue )
30 , mMaxMag( datasetMagMaximumValue )
33 , mVectorColoring( settings.vectorStrokeColoring() )
38 case Qgis::VectorFieldSymbology::WindBarbs:
40 const QgsCoordinateReferenceSystem mapCrs = mContext.coordinateTransform().destinationCrs();
41 mGeographicTransform = std::make_unique<QgsCoordinateTransform>( mapCrs, mapCrs.toGeographicCrs(), mContext.coordinateTransform().context() );
51 QPainter *painter = mContext.painter();
53 mScopedPainterState = std::make_unique<QgsScopedQPainterState>( painter );
54 mContext.setPainterFlagsUsingContext( painter );
56 QPen pen = painter->pen();
57 pen.setCapStyle( Qt::FlatCap );
58 pen.setJoinStyle( Qt::MiterJoin );
61 pen.setWidthF( penWidth );
62 painter->setPen( pen );
69 switch ( mCfg.symbology() )
72 drawArrow( lineStart, xVal, yVal, magnitude );
75 drawWindBarb( lineStart, xVal, yVal, magnitude );
84bool QgsVectorFieldEngine::calcVectorLineEnd(
97 if ( xVal == 0.0 && yVal == 0.0 )
110 cosAlpha = cos( vectorAngle );
111 sinAlpha = sin( vectorAngle );
122 const double minVal = mMinMag;
123 const double maxVal = mMaxMag;
124 const double k = ( magnitude - minVal ) / ( maxVal - minVal );
125 const double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
126 xDist = cosAlpha * L;
127 yDist = sinAlpha * L;
132 const double scaleFactor = mCfg.arrowSettings().scaleFactor();
133 xDist = scaleFactor * xVal;
134 yDist = scaleFactor * yVal;
141 xDist = cosAlpha * fixedShaftLength;
142 yDist = sinAlpha * fixedShaftLength;
150 if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
154 lineEnd = QgsPointXY( lineStart.
x() + xDist, lineStart.
y() + yDist );
156 vectorLength = sqrt( xDist * xDist + yDist * yDist );
159 if ( !QgsRectangle( lineStart, lineEnd ).intersects( QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
165void QgsVectorFieldEngine::drawArrow(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
169 double cosAlpha, sinAlpha;
170 if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha, lineStart, xVal, yVal, magnitude ) )
175 QgsPointXY vectorHeadPoints[3];
176 QVector<QPointF> finalVectorHeadPoints( 3 );
178 const double vectorHeadWidthRatio = mCfg.arrowSettings().arrowHeadWidthRatio();
179 const double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
182 vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
183 vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
186 vectorHeadPoints[1].
setX( 0.0 );
187 vectorHeadPoints[1].
setY( 0.0 );
190 vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
191 vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
194 for (
int j = 0; j < 3; j++ )
196 finalVectorHeadPoints[j].setX( lineEnd.
x() + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength ) - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength ) );
198 finalVectorHeadPoints[j].setY( lineEnd.
y() - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength ) - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength ) );
202 QPen pen( mContext.painter()->pen() );
203 pen.setColor( mVectorColoring.color( magnitude ) );
204 mContext.painter()->setPen( pen );
206 mContext.painter()->drawPolygon( finalVectorHeadPoints );
209void QgsVectorFieldEngine::drawWindBarb(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
212 if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
214 if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
217 QPen pen( mContext.painter()->pen() );
218 pen.setColor( mVectorColoring.color( magnitude ) );
219 mContext.painter()->setPen( pen );
222 QBrush brush( pen.color() );
223 mContext.painter()->setBrush( brush );
225 const double shaftLength = mContext.convertToPainterUnits( mCfg.windBarbSettings().shaftLength(), mCfg.windBarbSettings().shaftLengthUnits() );
226 if ( shaftLength < 1 )
230 const QgsPointXY mapPoint = mContext.mapToPixel().toMapCoordinates( lineStart.
x(), lineStart.
y() );
231 bool isNorthHemisphere =
true;
234 const QgsPointXY geoPoint = mGeographicTransform->transform( mapPoint );
235 isNorthHemisphere = geoPoint.
y() >= 0;
237 catch ( QgsCsException & )
239 QgsDebugError( u
"Could not transform wind barb coordinates to geographic ones"_s );
242 const double d = shaftLength / 25;
243 const double centerRadius = d;
244 const double zeroCircleRadius = 2 * d;
245 const double barbLength = 8 * d + pen.widthF();
246 const double barbAngle = 135;
247 const double barbOffset = 2 * d + pen.widthF();
248 const int sign = isNorthHemisphere ? 1 : -1;
252 const double vectorAngle = std::atan2( yVal, xVal ) - mContext.mapToPixel().mapRotation() *
M_DEG2RAD;
256 const double xDist = cos( vectorAngle ) * shaftLength;
257 const double yDist = -sin( vectorAngle ) * shaftLength;
260 const QgsPointXY lineEnd = QgsPointXY( lineStart.
x() - xDist, lineStart.
y() - yDist );
263 if ( !QgsRectangle( lineStart, lineEnd ).intersects( QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
267 double knots = magnitude * mCfg.windBarbSettings().magnitudeMultiplier();
268 QgsPointXY nextLineOrigin = lineEnd;
273 mContext.painter()->setBrush( Qt::NoBrush );
274 mContext.painter()->drawEllipse( lineStart.
toQPointF(), zeroCircleRadius, zeroCircleRadius );
275 mContext.painter()->setBrush( brush );
279 const double azimuth = lineEnd.
azimuth( lineStart );
282 if ( knots < 47.5 && knots > 7.5 )
286 mContext.painter()->drawPolyline( pts );
287 nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
297 mContext.painter()->drawEllipse( lineStart.
toQPointF(), centerRadius, centerRadius );
300 while ( knots > 47.5 )
302 const QVector< QPointF >
304 mContext.painter()->drawPolygon( pts );
309 nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414, azimuth );
311 nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414 + barbOffset, azimuth );
315 while ( knots > 7.5 )
317 mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength, azimuth + barbAngle * sign ).
toQPointF() );
318 nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
326 if ( nextLineOrigin == lineEnd )
327 nextLineOrigin = nextLineOrigin.
project( barbLength / 2, azimuth );
329 mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength / 2, azimuth + barbAngle * sign ).
toQPointF() );
@ WindBarbs
Displaying vector dataset with wind barbs.
@ Arrows
Displaying vector dataset with arrows.
@ Traces
Displaying vector dataset with particle traces.
@ Streamlines
Displaying vector dataset with streamlines.
@ Millimeters
Millimeters.
@ Fixed
Use fixed length fixedShaftLength() regardless of vector's magnitude.
@ Scaled
Scale vector magnitude by factor scaleFactor().
@ MinMax
Scale vector magnitude linearly to fit in range of vectorFilterMin() and vectorFilterMax().
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
QgsPointXY project(double distance, double bearing) const
Returns a new point which corresponds to this point projected by a specified distance in a specified ...
void setY(double y)
Sets the y value of the point.
double azimuth(const QgsPointXY &other) const
Calculates azimuth between this point and other one (clockwise in degree, starting from north).
void setX(double x)
Sets the x value of the point.
QPointF toQPointF() const
Converts a point to a QPointF.
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).
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
double minShaftLength() const
Returns mininimum shaft length (in millimeters).
Qgis::VectorFieldArrowScalingMethod shaftLengthMethod() const
Returns method used for drawing arrows.
double maxShaftLength() const
Returns maximum shaft length (in millimeters).
QgsVectorFieldEngine(double datasetMagMaximumValue, double datasetMagMinimumValue, const QgsVectorFieldSettings &settings, QgsRenderContext &context, QSize size)
Ctor.
~QgsVectorFieldEngine()
Dtor.
void drawGlyph(const QgsPointXY &lineStart, double xVal, double yVal, double magnitude)
Draws a single glyph at lineStart, in painter coordinates, using the symbology of the settings the en...
Represents a renderer settings for vector datasets.
double filterMin() const
Returns filter value for vector magnitudes.
QgsVectorFieldArrowSettings arrowSettings() const
Returns settings for vector rendered with arrows.
Qgis::VectorFieldSymbology symbology() const
Returns the displaying method used to render vector datasets.
double filterMax() const
Returns filter value for vector magnitudes.
#define QgsDebugError(str)