24using namespace Qt::StringLiterals;
27#define M_DEG2RAD 0.0174532925
31 : mMinMag( datasetMagMinimumValue )
32 , mMaxMag( datasetMagMaximumValue )
35 , mVectorColoring( settings.vectorStrokeColoring() )
40 case Qgis::VectorFieldSymbology::WindBarbs:
42 const QgsCoordinateReferenceSystem mapCrs = mContext.coordinateTransform().destinationCrs();
43 mGeographicTransform = std::make_unique<QgsCoordinateTransform>( mapCrs, mapCrs.toGeographicCrs(), mContext.coordinateTransform().context() );
53 QPainter *painter = mContext.painter();
55 mScopedPainterState = std::make_unique<QgsScopedQPainterState>( painter );
56 mContext.setPainterFlagsUsingContext( painter );
58 QPen pen = painter->pen();
59 pen.setCapStyle( Qt::FlatCap );
60 pen.setJoinStyle( Qt::MiterJoin );
63 pen.setWidthF( penWidth );
64 painter->setPen( pen );
71 switch ( mCfg.symbology() )
74 drawArrow( lineStart, xVal, yVal, magnitude );
77 drawWindBarb( lineStart, xVal, yVal, magnitude );
91 auto field = std::make_unique<QgsVectorFieldStreamlinesField>( std::move( source ), mContext, mVectorColoring, feedback );
93 field->updateSize( mContext );
94 field->setPixelFillingDensity( mCfg.streamLinesSettings().seedingDensity() );
96 field->setColor( mCfg.color() );
97 field->setFilter( mCfg.filterMin(), mCfg.filterMax() );
99 switch ( mCfg.streamLinesSettings().seedingMethod() )
102 if ( mCfg.isOnUserDefinedGrid() )
103 field->addGriddedTraces( mCfg.userGridCellWidth(), mCfg.userGridCellHeight() );
105 field->addTracesOnDataPoints( mContext.mapExtent() );
108 field->addRandomTraces();
112 if ( mContext.renderingStopped() )
116 mContext.painter()->drawImage( field->topLeft(), field->image() );
124 auto field = std::make_unique<QgsVectorFieldParticleTracesField>( std::move( source ), mContext, mVectorColoring );
126 field->updateSize( mContext );
128 field->setParticlesCount( mCfg.tracesSettings().particlesCount() );
129 field->setTailFactor( 1 );
130 field->setStumpParticleWithLifeTime(
false );
133 field->setTimeStep( mContext.convertToPainterUnits( mCfg.tracesSettings().maximumTailLength(), mCfg.tracesSettings().maximumTailLengthUnit() ) );
135 field->addRandomParticles();
136 field->moveParticles();
138 if ( mContext.renderingStopped() )
141 mContext.painter()->drawImage( field->topLeft(), field->image() );
144bool QgsVectorFieldEngine::calcVectorLineEnd(
146 double &vectorLength,
157 if ( xVal == 0.0 && yVal == 0.0 )
170 cosAlpha = cos( vectorAngle );
171 sinAlpha = sin( vectorAngle );
182 const double minVal = mMinMag;
183 const double maxVal = mMaxMag;
184 const double k = ( magnitude - minVal ) / ( maxVal - minVal );
185 const double L = minShaftLength + k * ( maxShaftLength - minShaftLength );
186 xDist = cosAlpha * L;
187 yDist = sinAlpha * L;
192 const double scaleFactor = mCfg.arrowSettings().scaleFactor();
193 xDist = scaleFactor * xVal;
194 yDist = scaleFactor * yVal;
201 xDist = cosAlpha * fixedShaftLength;
202 yDist = sinAlpha * fixedShaftLength;
210 if ( std::abs( xDist ) < 1 && std::abs( yDist ) < 1 )
214 lineEnd = QgsPointXY( lineStart.
x() + xDist, lineStart.
y() + yDist );
216 vectorLength = sqrt( xDist * xDist + yDist * yDist );
219 if ( !QgsRectangle( lineStart, lineEnd ).intersects( QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
225void QgsVectorFieldEngine::drawArrow(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
229 double cosAlpha, sinAlpha;
230 if ( calcVectorLineEnd( lineEnd, vectorLength, cosAlpha, sinAlpha, lineStart, xVal, yVal, magnitude ) )
235 QgsPointXY vectorHeadPoints[3];
236 QVector<QPointF> finalVectorHeadPoints( 3 );
238 const double vectorHeadWidthRatio = mCfg.arrowSettings().arrowHeadWidthRatio();
239 const double vectorHeadLengthRatio = mCfg.arrowSettings().arrowHeadLengthRatio();
242 vectorHeadPoints[0].
setX( -1.0 * vectorHeadLengthRatio );
243 vectorHeadPoints[0].
setY( vectorHeadWidthRatio * 0.5 );
246 vectorHeadPoints[1].
setX( 0.0 );
247 vectorHeadPoints[1].
setY( 0.0 );
250 vectorHeadPoints[2].
setX( -1.0 * vectorHeadLengthRatio );
251 vectorHeadPoints[2].
setY( -1.0 * vectorHeadWidthRatio * 0.5 );
254 for (
int j = 0; j < 3; j++ )
256 finalVectorHeadPoints[j].setX( lineEnd.
x() + ( vectorHeadPoints[j].x() * cosAlpha * vectorLength ) - ( vectorHeadPoints[j].y() * sinAlpha * vectorLength ) );
258 finalVectorHeadPoints[j].setY( lineEnd.
y() - ( vectorHeadPoints[j].x() * sinAlpha * vectorLength ) - ( vectorHeadPoints[j].y() * cosAlpha * vectorLength ) );
262 QPen pen( mContext.painter()->pen() );
263 pen.setColor( mVectorColoring.color( magnitude ) );
264 mContext.painter()->setPen( pen );
266 mContext.painter()->drawPolygon( finalVectorHeadPoints );
269void QgsVectorFieldEngine::drawWindBarb(
const QgsPointXY &lineStart,
double xVal,
double yVal,
double magnitude )
272 if ( mCfg.filterMin() >= 0 && magnitude < mCfg.filterMin() )
274 if ( mCfg.filterMax() >= 0 && magnitude > mCfg.filterMax() )
277 QPen pen( mContext.painter()->pen() );
278 pen.setColor( mVectorColoring.color( magnitude ) );
279 mContext.painter()->setPen( pen );
282 QBrush brush( pen.color() );
283 mContext.painter()->setBrush( brush );
285 const double shaftLength = mContext.convertToPainterUnits( mCfg.windBarbSettings().shaftLength(), mCfg.windBarbSettings().shaftLengthUnits() );
286 if ( shaftLength < 1 )
290 const QgsPointXY mapPoint = mContext.mapToPixel().toMapCoordinates( lineStart.
x(), lineStart.
y() );
291 bool isNorthHemisphere =
true;
294 const QgsPointXY geoPoint = mGeographicTransform->transform( mapPoint );
295 isNorthHemisphere = geoPoint.
y() >= 0;
297 catch ( QgsCsException & )
299 QgsDebugError( u
"Could not transform wind barb coordinates to geographic ones"_s );
302 const double d = shaftLength / 25;
303 const double centerRadius = d;
304 const double zeroCircleRadius = 2 * d;
305 const double barbLength = 8 * d + pen.widthF();
306 const double barbAngle = 135;
307 const double barbOffset = 2 * d + pen.widthF();
308 const int sign = isNorthHemisphere ? 1 : -1;
312 const double vectorAngle = std::atan2( yVal, xVal ) - mContext.mapToPixel().mapRotation() *
M_DEG2RAD;
316 const double xDist = cos( vectorAngle ) * shaftLength;
317 const double yDist = -sin( vectorAngle ) * shaftLength;
320 const QgsPointXY lineEnd = QgsPointXY( lineStart.
x() - xDist, lineStart.
y() - yDist );
323 if ( !QgsRectangle( lineStart, lineEnd ).intersects( QgsRectangle( 0, 0, mOutputSize.width(), mOutputSize.height() ) ) )
327 double knots = magnitude * mCfg.windBarbSettings().magnitudeMultiplier();
328 QgsPointXY nextLineOrigin = lineEnd;
333 mContext.painter()->setBrush( Qt::NoBrush );
334 mContext.painter()->drawEllipse( lineStart.
toQPointF(), zeroCircleRadius, zeroCircleRadius );
335 mContext.painter()->setBrush( brush );
339 const double azimuth = lineEnd.
azimuth( lineStart );
342 if ( knots < 47.5 && knots > 7.5 )
346 mContext.painter()->drawPolyline( pts );
347 nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
357 mContext.painter()->drawEllipse( lineStart.
toQPointF(), centerRadius, centerRadius );
360 while ( knots > 47.5 )
362 const QVector< QPointF >
364 mContext.painter()->drawPolygon( pts );
369 nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414, azimuth );
371 nextLineOrigin = nextLineOrigin.
project( barbLength / 1.414 + barbOffset, azimuth );
375 while ( knots > 7.5 )
377 mContext.painter()->drawLine( nextLineOrigin.
toQPointF(), nextLineOrigin.
project( barbLength, azimuth + barbAngle * sign ).
toQPointF() );
378 nextLineOrigin = nextLineOrigin.
project( barbOffset, azimuth );
386 if ( nextLineOrigin == lineEnd )
387 nextLineOrigin = nextLineOrigin.
project( barbLength / 2, azimuth );
389 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)