25using namespace Qt::StringLiterals;
28#define M_DEG2RAD 0.0174532925
32 : mMinMag( datasetMagMinimumValue )
33 , mMaxMag( datasetMagMaximumValue )
36 , mVectorColoring( settings.vectorStrokeColoring() )
41 case Qgis::VectorFieldSymbology::WindBarbs:
43 const QgsCoordinateReferenceSystem mapCrs = mContext.coordinateTransform().destinationCrs();
44 mGeographicTransform = std::make_unique<QgsCoordinateTransform>( mapCrs, mapCrs.toGeographicCrs(), mContext.coordinateTransform().context() );
54 QPainter *painter = mContext.painter();
56 mScopedPainterState = std::make_unique<QgsScopedQPainterState>( painter );
57 mContext.setPainterFlagsUsingContext( painter );
59 QPen pen = painter->pen();
60 pen.setCapStyle( Qt::FlatCap );
61 pen.setJoinStyle( Qt::MiterJoin );
64 pen.setWidthF( penWidth );
65 painter->setPen( pen );
72 switch ( mCfg.symbology() )
75 drawArrow( lineStart, xVal, yVal, magnitude );
78 drawWindBarb( lineStart, xVal, yVal, magnitude );
92 auto field = std::make_unique<QgsVectorFieldStreamlinesField>( std::move( source ), mContext, mVectorColoring, feedback );
94 field->updateSize( mContext );
95 field->setPixelFillingDensity( mCfg.streamLinesSettings().seedingDensity() );
97 field->setColor( mCfg.color() );
98 field->setFilter( mCfg.filterMin(), mCfg.filterMax() );
100 switch ( mCfg.streamLinesSettings().seedingMethod() )
103 if ( mCfg.isOnUserDefinedGrid() )
104 field->addGriddedTraces( mCfg.userGridCellWidth(), mCfg.userGridCellHeight() );
106 field->addTracesOnDataPoints( mContext.mapExtent() );
109 field->addRandomTraces();
113 if ( mContext.renderingStopped() )
117 mContext.painter()->drawImage( field->topLeft(), field->image() );
125 auto field = std::make_unique<QgsVectorFieldParticleTracesField>( std::move( source ), mContext, mVectorColoring );
127 field->updateSize( mContext );
129 field->setParticlesCount( mCfg.tracesSettings().particlesCount() );
130 field->setTailFactor( 1 );
131 field->setStumpParticleWithLifeTime(
false );
134 field->setTimeStep( mContext.convertToPainterUnits( mCfg.tracesSettings().maximumTailLength(), mCfg.tracesSettings().maximumTailLengthUnit() ) );
136 field->addRandomParticles();
137 field->moveParticles();
139 if ( mContext.renderingStopped() )
142 mContext.painter()->drawImage( field->topLeft(), field->image() );
145bool QgsVectorFieldEngine::calcVectorLineEnd(
147 double &vectorLength,
158 if ( xVal == 0.0 && yVal == 0.0 )
171 cosAlpha = cos( vectorAngle );
172 sinAlpha = sin( vectorAngle );
183 const double minVal = mMinMag;
184 const double maxVal = mMaxMag;
185 const double k = ( magnitude - minVal ) / ( maxVal - minVal );
186 const double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
187 xDist = cosAlpha * L;
188 yDist = sinAlpha * L;
193 const double scaleFactor = mCfg.arrowSettings().scaleFactor();
194 xDist = scaleFactor * xVal;
195 yDist = scaleFactor * yVal;
202 xDist = cosAlpha * fixedShaftLength;
203 yDist = sinAlpha * fixedShaftLength;
211 if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
215 lineEnd = QgsPointXY( lineStart.
x() + xDist, lineStart.
y() + yDist );
217 vectorLength = sqrt( xDist * xDist + yDist * yDist );
220 if ( !QgsRectangle( lineStart, lineEnd ).intersects( QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
226void QgsVectorFieldEngine::drawArrow(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
230 double cosAlpha, sinAlpha;
231 if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha, lineStart, xVal, yVal, magnitude ) )
236 QgsPointXY vectorHeadPoints[3];
237 QVector<QPointF> finalVectorHeadPoints( 3 );
239 const double vectorHeadWidthRatio = mCfg.arrowSettings().arrowHeadWidthRatio();
240 const double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
243 vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
244 vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
247 vectorHeadPoints[1].
setX( 0.0 );
248 vectorHeadPoints[1].
setY( 0.0 );
251 vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
252 vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
255 for (
int j = 0; j < 3; j++ )
257 finalVectorHeadPoints[j].setX( lineEnd.
x() + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength ) - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength ) );
259 finalVectorHeadPoints[j].setY( lineEnd.
y() - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength ) - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength ) );
263 QPen pen( mContext.painter()->pen() );
264 pen.setColor( mVectorColoring.color( magnitude ) );
265 mContext.painter()->setPen( pen );
267 mContext.painter()->drawPolygon( finalVectorHeadPoints );
270void QgsVectorFieldEngine::drawWindBarb(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
273 if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
275 if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
278 QPen pen( mContext.painter()->pen() );
279 pen.setColor( mVectorColoring.color( magnitude ) );
280 mContext.painter()->setPen( pen );
283 QBrush brush( pen.color() );
284 mContext.painter()->setBrush( brush );
286 const double shaftLength = mContext.convertToPainterUnits( mCfg.windBarbSettings().shaftLength(), mCfg.windBarbSettings().shaftLengthUnits() );
287 if ( shaftLength < 1 )
291 const QgsPointXY mapPoint = mContext.mapToPixel().toMapCoordinates( lineStart.
x(), lineStart.
y() );
292 bool isNorthHemisphere =
true;
295 const QgsPointXY geoPoint = mGeographicTransform->transform( mapPoint );
296 isNorthHemisphere = geoPoint.
y() >= 0;
298 catch ( QgsCsException & )
300 QgsDebugError( u
"Could not transform wind barb coordinates to geographic ones"_s );
303 const double d = shaftLength / 25;
304 const double centerRadius = d;
305 const double zeroCircleRadius = 2 * d;
306 const double barbLength = 8 * d + pen.widthF();
307 const double barbAngle = 135;
308 const double barbOffset = 2 * d + pen.widthF();
309 const int sign = isNorthHemisphere ? 1 : -1;
313 const double vectorAngle = std::atan2( yVal, xVal ) - mContext.mapToPixel().mapRotation() *
M_DEG2RAD;
317 const double xDist = cos( vectorAngle ) * shaftLength;
318 const double yDist = -sin( vectorAngle ) * shaftLength;
321 const QgsPointXY lineEnd = QgsPointXY( lineStart.
x() - xDist, lineStart.
y() - yDist );
324 if ( !QgsRectangle( lineStart, lineEnd ).intersects( QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
328 double knots = magnitude * mCfg.windBarbSettings().magnitudeMultiplier();
329 QgsPointXY nextLineOrigin = lineEnd;
334 mContext.painter()->setBrush( Qt::NoBrush );
335 mContext.painter()->drawEllipse( lineStart.
toQPointF(), zeroCircleRadius, zeroCircleRadius );
336 mContext.painter()->setBrush( brush );
340 const double azimuth = lineEnd.
azimuth( lineStart );
343 if ( knots < 47.5 && knots > 7.5 )
347 mContext.painter()->drawPolyline( pts );
348 nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
358 mContext.painter()->drawEllipse( lineStart.
toQPointF(), centerRadius, centerRadius );
361 while ( knots > 47.5 )
363 const QVector< QPointF >
365 mContext.painter()->drawPolygon( pts );
370 nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414, azimuth );
372 nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414 + barbOffset, azimuth );
376 while ( knots > 7.5 )
378 mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength, azimuth + barbAngle * sign ).
toQPointF() );
379 nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
387 if ( nextLineOrigin == lineEnd )
388 nextLineOrigin = nextLineOrigin.
project( barbLength / 2, azimuth );
390 mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength / 2, azimuth + barbAngle * sign ).
toQPointF() );
@ Gridded
Seeds start points on data grid or user regular grid.
@ Random
Seeds start points randomly.
@ 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.
Feedback object tailored for raster block reading.
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...
void drawStreamlines(std::unique_ptr< QgsVectorFieldValueSource > source, QgsRasterBlockFeedback *feedback=nullptr)
Integrates and draws streamlines over the whole rendered extent, sampling the vector field from sourc...
void drawTraces(std::unique_ptr< QgsVectorFieldValueSource > source)
Seeds particles over the whole rendered extent, moves them one time step and draws their traces,...
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)