31 mSymbol.reset( static_cast<QgsFillSymbol *>( symbol ) );
42 if ( props.contains( QStringLiteral(
"arrow_width" ) ) )
43 l->
setArrowWidth( props[QStringLiteral(
"arrow_width" )].toDouble() );
45 if ( props.contains( QStringLiteral(
"arrow_width_unit" ) ) )
48 if ( props.contains( QStringLiteral(
"arrow_width_unit_scale" ) ) )
51 if ( props.contains( QStringLiteral(
"arrow_start_width" ) ) )
54 if ( props.contains( QStringLiteral(
"arrow_start_width_unit" ) ) )
57 if ( props.contains( QStringLiteral(
"arrow_start_width_unit_scale" ) ) )
60 if ( props.contains( QStringLiteral(
"is_curved" ) ) )
61 l->
setIsCurved( props[QStringLiteral(
"is_curved" )].toInt() == 1 );
63 if ( props.contains( QStringLiteral(
"is_repeated" ) ) )
64 l->
setIsRepeated( props[QStringLiteral(
"is_repeated" )].toInt() == 1 );
66 if ( props.contains( QStringLiteral(
"head_length" ) ) )
67 l->
setHeadLength( props[QStringLiteral(
"head_length" )].toDouble() );
69 if ( props.contains( QStringLiteral(
"head_length_unit" ) ) )
72 if ( props.contains( QStringLiteral(
"head_length_unit_scale" ) ) )
75 if ( props.contains( QStringLiteral(
"head_thickness" ) ) )
78 if ( props.contains( QStringLiteral(
"head_thickness_unit" ) ) )
81 if ( props.contains( QStringLiteral(
"head_thickness_unit_scale" ) ) )
84 if ( props.contains( QStringLiteral(
"head_type" ) ) )
85 l->
setHeadType( static_cast<HeadType>( props[QStringLiteral(
"head_type" )].toInt() ) );
87 if ( props.contains( QStringLiteral(
"arrow_type" ) ) )
88 l->
setArrowType( static_cast<ArrowType>( props[QStringLiteral(
"arrow_type" )].toInt() ) );
90 if ( props.contains( QStringLiteral(
"offset" ) ) )
91 l->
setOffset( props[QStringLiteral(
"offset" )].toDouble() );
93 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
96 if ( props.contains( QStringLiteral(
"offset_unit_scale" ) ) )
117 return QStringLiteral(
"ArrowLine" );
124 map[QStringLiteral(
"arrow_width" )] = QString::number(
arrowWidth() );
128 map[QStringLiteral(
"arrow_start_width" )] = QString::number(
arrowStartWidth() );
132 map[QStringLiteral(
"is_curved" )] = QString::number(
isCurved() ? 1 : 0 );
133 map[QStringLiteral(
"is_repeated" )] = QString::number(
isRepeated() ? 1 : 0 );
135 map[QStringLiteral(
"head_length" )] = QString::number(
headLength() );
139 map[QStringLiteral(
"head_thickness" )] = QString::number(
headThickness() );
143 map[QStringLiteral(
"head_type" )] = QString::number(
headType() );
144 map[QStringLiteral(
"arrow_type" )] = QString::number(
arrowType() );
146 map[QStringLiteral(
"offset" )] = QString::number(
offset() );
157 attributes.unite( mSymbol->usedAttributes( context ) );
184 return std::sqrt( ( po.x() - pd.x() ) * ( po.x() - pd.x() ) + ( po.y() - pd.y() ) * ( po.y() - pd.y() ) );
188 qreal startWidth, qreal
width,
189 qreal headWidth, qreal headHeight,
200 po = pd - ( pd - po ) / length * headWidth;
205 pd = po + ( pd - po ) / length * headWidth;
210 QPointF v = ( pd - po ) / length * headWidth;
211 QPointF npo = ( po + pd ) / 2.0 - v;
212 QPointF npd = ( po + pd ) / 2.0 + v;
215 length = 2 * headWidth;
218 qreal bodyLength = length - headWidth;
221 QPointF unitVec = ( pd - po ) / length;
223 QPointF perpVec( -unitVec.y(), unitVec.x() );
235 polygon << po + unitVec *headWidth + perpVec *headHeight;
236 polygon << po + unitVec *headWidth + perpVec * ( width * 0.5 );
238 polygon << po + unitVec *bodyLength + perpVec * ( width * 0.5 );
241 polygon << po + unitVec *bodyLength + perpVec *headHeight;
247 polygon << po + unitVec *bodyLength - perpVec *headHeight;
248 polygon << po + unitVec *bodyLength - perpVec * ( width * 0.5 );
251 polygon << po + unitVec *headWidth - perpVec * ( width * 0.5 );
252 polygon << po + unitVec *headWidth - perpVec *headHeight;
259 polygon << po + perpVec * ( startWidth * 0.5 );
260 polygon << po + unitVec *bodyLength + perpVec * ( width * 0.5 );
261 polygon << po + unitVec *bodyLength + perpVec *headHeight;
270 polygon << po + unitVec *bodyLength - perpVec *headHeight;
271 polygon << po + unitVec *bodyLength - perpVec * ( width * 0.5 );
272 polygon << po - perpVec * ( startWidth * 0.5 );
284 polygon << po + unitVec *headWidth + perpVec *headHeight;
285 polygon << po + unitVec *headWidth + perpVec * ( width * 0.5 );
287 polygon << pd + perpVec * ( startWidth * 0.5 );
295 polygon << pd - perpVec * ( startWidth * 0.5 );
297 polygon << po + unitVec *headWidth - perpVec * ( width * 0.5 );
298 polygon << po + unitVec *headWidth - perpVec *headHeight;
306 polygon << polygon.first();
325 bool pointsToCircle( QPointF a, QPointF b, QPointF c, QPointF ¢er, qreal &radius )
334 QPointF ab2 = ( a + b ) / 2.0;
335 QPointF bc2 = ( b + c ) / 2.0;
338 if ( std::fabs( ab.x() * bc.y() - ab.y() * bc.x() ) < 0.001 )
345 cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y();
348 else if ( bc.y() == 0 )
351 cy = ab2.y() - ( cx - ab2.x() ) * ab.x() / ab.y();
356 cx = ( bc2.y() - ab2.y() + bc.x() * bc2.x() / bc.y() - ab.x() * ab2.x() / ab.y() ) / ( bc.x() / bc.y() - ab.x() / ab.y() );
357 cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y();
360 radius = std::sqrt( ( a.x() - cx ) * ( a.x() - cx ) + ( a.y() - cy ) * ( a.y() - cy ) );
370 return QPointF( std::cos( -angle ) * radius + center.x(), std::sin( -angle ) * radius + center.y() );
373 void pathArcTo( QPainterPath &path, QPointF circleCenter, qreal circleRadius, qreal angle_o, qreal angle_d,
int direction )
375 QRectF circleRect( circleCenter - QPointF( circleRadius, circleRadius ), circleCenter + QPointF( circleRadius, circleRadius ) );
376 if ( direction == 1 )
378 if ( angle_o < angle_d )
379 path.arcTo( circleRect, angle_o / M_PI * 180.0, ( angle_d - angle_o ) / M_PI * 180.0 );
381 path.arcTo( circleRect, angle_o / M_PI * 180.0, 360.0 - ( angle_o - angle_d ) / M_PI * 180.0 );
385 if ( angle_o < angle_d )
386 path.arcTo( circleRect, angle_o / M_PI * 180.0, - ( 360.0 - ( angle_d - angle_o ) / M_PI * 180.0 ) );
388 path.arcTo( circleRect, angle_o / M_PI * 180.0, ( angle_d - angle_o ) / M_PI * 180.0 );
393 void spiralArcTo( QPainterPath &path, QPointF center, qreal startAngle, qreal startRadius, qreal endAngle, qreal endRadius,
int direction )
396 QPointF A =
circlePoint( center, startRadius, startAngle );
398 QPointF B =
circlePoint( center, endRadius, endAngle );
402 deltaAngle = endAngle - startAngle;
403 if ( direction * deltaAngle < 0.0 )
404 deltaAngle = deltaAngle + direction * 2 * M_PI;
406 QPointF I1 =
circlePoint( center, 0.75 * startRadius + 0.25 * endRadius, startAngle + 0.25 * deltaAngle );
407 QPointF I2 =
circlePoint( center, 0.50 * startRadius + 0.50 * endRadius, startAngle + 0.50 * deltaAngle );
408 QPointF I3 =
circlePoint( center, 0.25 * startRadius + 0.75 * endRadius, startAngle + 0.75 * deltaAngle );
421 qreal a1 = std::atan2( cCenter.y() - A.y(), A.x() - cCenter.x() );
422 qreal a2 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() );
423 pathArcTo( path, cCenter, cRadius, a1, a2, direction );
435 qreal a1 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() );
436 qreal a2 = std::atan2( cCenter.y() - B.y(), B.x() - cCenter.x() );
437 pathArcTo( path, cCenter, cRadius, a1, a2, direction );
442 qreal startWidth, qreal
width,
443 qreal headWidth, qreal headHeight,
448 QPointF circleCenter;
452 return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset );
456 qreal angle_o =
clampAngle( std::atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) );
457 qreal angle_m =
clampAngle( std::atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) );
458 qreal angle_d =
clampAngle( std::atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) );
470 qreal deltaAngle = angle_d - angle_o;
471 if ( direction * deltaAngle < 0.0 )
472 deltaAngle = deltaAngle + direction * 2 * M_PI;
480 return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset );
485 po =
circlePoint( circleCenter, circleRadius, angle_o );
486 pm =
circlePoint( circleCenter, circleRadius, angle_m );
487 pd =
circlePoint( circleCenter, circleRadius, angle_d );
489 qreal headAngle = direction * std::atan( headWidth / circleRadius );
499 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_o + headAngle ) );
501 pathArcTo( path, circleCenter, circleRadius + direction * width / 2, angle_o + headAngle, angle_d - headAngle, direction );
504 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_d - headAngle ) );
509 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
513 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_d - headAngle ) );
515 pathArcTo( path, circleCenter, circleRadius - direction * width / 2, angle_d - headAngle, angle_o + headAngle, -direction );
518 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_o + headAngle ) );
523 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
530 path.moveTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
532 spiralArcTo( path, circleCenter, angle_o, circleRadius + direction * startWidth / 2, angle_d - headAngle, circleRadius + direction * width / 2, direction );
535 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_d - headAngle ) );
541 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
545 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_d - headAngle ) );
547 spiralArcTo( path, circleCenter, angle_d - headAngle, circleRadius - direction * width / 2, angle_o, circleRadius - direction * startWidth / 2, -direction );
549 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
553 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
554 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
562 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * headHeight, angle_o + headAngle ) );
563 path.lineTo(
circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) );
565 spiralArcTo( path, circleCenter, angle_o + headAngle, circleRadius + direction * width / 2, angle_d, circleRadius + direction * startWidth / 2, direction );
569 pathArcTo( path, circleCenter, circleRadius, angle_o, angle_d, direction );
573 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * startWidth / 2, angle_d ) );
575 spiralArcTo( path, circleCenter, angle_d, circleRadius - direction * startWidth / 2, angle_o + headAngle, circleRadius - direction * width / 2, - direction );
577 path.lineTo(
circlePoint( circleCenter, circleRadius - direction * headHeight, angle_o + headAngle ) );
583 pathArcTo( path, circleCenter, circleRadius, angle_d, angle_o, -direction );
587 return path.toSubpathPolygons().at( 0 );
600 double w = exprVal.toDouble( &ok );
610 double w = exprVal.toDouble( &ok );
620 double w = exprVal.toDouble( &ok );
630 double w = exprVal.toDouble( &ok );
640 double w = exprVal.toDouble( &ok );
651 int h = exprVal.toInt( &ok );
654 mComputedHeadType =
static_cast<HeadType>( h );
662 int h = exprVal.toInt( &ok );
665 mComputedArrowType =
static_cast<ArrowType>( h );
684 _resolveDataDefined( context );
688 if ( points.size() >= 3 )
691 QPointF po( points.at( 0 ) );
693 QPointF pm( points.at( points.size() / 2 ) );
695 QPointF pd( points.back() );
697 QPolygonF poly =
curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
701 else if ( points.size() == 2 )
704 QPointF po( points.at( 0 ) );
706 QPointF pd( points.at( 1 ) );
708 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
714 for (
int pIdx = 0; pIdx < points.size() - 1; pIdx += 2 )
717 _resolveDataDefined( context );
719 if ( points.size() - pIdx >= 3 )
722 QPointF po( points.at( pIdx ) );
724 QPointF pm( points.at( pIdx + 1 ) );
726 QPointF pd( points.at( pIdx + 2 ) );
728 QPolygonF poly =
curvedArrow( po, pm, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
732 else if ( points.size() - pIdx == 2 )
735 QPointF po( points.at( pIdx ) );
737 QPointF pd( points.at( pIdx + 1 ) );
739 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
749 _resolveDataDefined( context );
751 if ( !points.isEmpty() )
754 QPointF po( points.at( 0 ) );
756 QPointF pd( points.back() );
758 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
765 for (
int pIdx = 0; pIdx < points.size() - 1; pIdx++ )
768 _resolveDataDefined( context );
771 QPointF po( points.at( pIdx ) );
773 QPointF pd( points.at( pIdx + 1 ) );
775 QPolygonF poly =
straightArrow( po, pd, mScaledArrowStartWidth, mScaledArrowWidth, mScaledHeadLength, mScaledHeadThickness, mComputedHeadType, mComputedArrowType, mScaledOffset );
786 mSymbol->setColor( c );
793 return mSymbol.get() ? mSymbol->color() :
mColor;
void setHeadThicknessUnitScale(const QgsMapUnitScale &scale)
Set the scale for the head height.
void setHeadLengthUnit(QgsUnitTypes::RenderUnit unit)
Set the unit for the head length.
void stopRender(QgsSymbolRenderContext &context) override
Single variable definition for use within a QgsExpressionContextScope.
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
QgsUnitTypes::RenderUnit headThicknessUnit() const
Get the unit for the head height.
double arrowStartWidth() const
Get current arrow start width. Only meaningful for single headed arrows.
double arrowWidth() const
Get current arrow width.
void setArrowWidthUnit(QgsUnitTypes::RenderUnit unit)
Set the unit for the arrow width.
const QgsMapUnitScale & offsetMapUnitScale() const
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
QPolygonF straightArrow(QPointF po, QPointF pd, qreal startWidth, qreal width, qreal headWidth, qreal headHeight, QgsArrowSymbolLayer::HeadType headType, QgsArrowSymbolLayer::ArrowType arrowType, qreal offset)
QgsMapUnitScale arrowStartWidthUnitScale() const
Get the scale for the arrow start width.
QgsMapUnitScale headLengthUnitScale() const
Get the scale for the head length.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
void pathArcTo(QPainterPath &path, QPointF circleCenter, qreal circleRadius, qreal angle_o, qreal angle_d, int direction)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
void setHeadThicknessUnit(QgsUnitTypes::RenderUnit unit)
Set the unit for the head height.
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
void setColor(const QColor &c) override
The fill color.
qreal clampAngle(qreal a)
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units for the line's offset.
QPolygonF curvedArrow(QPointF po, QPointF pm, QPointF pd, qreal startWidth, qreal width, qreal headWidth, qreal headHeight, QgsArrowSymbolLayer::HeadType headType, QgsArrowSymbolLayer::ArrowType arrowType, qreal offset)
ArrowType arrowType() const
Get the current arrow type.
bool setSubSymbol(QgsSymbol *symbol) override
set layer's subsymbol. takes ownership of the passed symbol
QMap< QString, QString > QgsStringMap
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
virtual double width() const
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
QPointF circlePoint(QPointF center, qreal radius, qreal angle)
bool isRepeated() const
Return whether the arrow is repeated along the line or not.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
bool pointsToCircle(QPointF a, QPointF b, QPointF c, QPointF ¢er, qreal &radius)
Compute the circumscribed circle from three points.
QgsArrowSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setHeadLength(double length)
Set the arrow head length.
void setHeadThickness(double thickness)
Set the arrow head height.
QgsUnitTypes::RenderUnit arrowStartWidthUnit() const
Get the unit for the arrow start width.
void spiralArcTo(QPainterPath &path, QPointF center, qreal startAngle, qreal startRadius, qreal endAngle, qreal endRadius, int direction)
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides...
void setArrowWidth(double width)
Set the arrow width.
ArrowType
Possible arrow types.
bool isCurved() const
Return whether it is a curved arrow or a straight one.
qreal euclidian_distance(QPointF po, QPointF pd)
double headLength() const
Get the current arrow head length.
void setIsRepeated(bool isRepeated)
Set whether the arrow is repeated along the line.
QgsArrowSymbolLayer()
Simple constructor.
QgsMapUnitScale arrowWidthUnitScale() const
Get the scale for the arrow width.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
Single scope for storing variables and functions for use within a QgsExpressionContext.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsUnitTypes::RenderUnit arrowWidthUnit() const
Get the unit for the arrow width.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setIsCurved(bool isCurved)
Set whether it is a curved arrow or a straight one.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setHeadLengthUnitScale(const QgsMapUnitScale &scale)
Set the scale for the head length.
void setArrowType(ArrowType type)
Set the arrow type.
void setArrowWidthUnitScale(const QgsMapUnitScale &scale)
Set the scale for the arrow width.
QgsUnitTypes::RenderUnit headLengthUnit() const
Get the unit for the head length.
void setHeadType(HeadType type)
Set the head type.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
const QgsFeature * feature() const
Current feature being rendered - may be null.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line's offset.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Create a new QgsArrowSymbolLayer.
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
QColor color() const override
The fill color.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QgsMapUnitScale headThicknessUnitScale() const
Get the scale for the head height.
Line symbol layer used for representing lines as arrows.
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
HeadType
Possible head types.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
void setArrowStartWidthUnit(QgsUnitTypes::RenderUnit unit)
Set the unit for the arrow start width.
HeadType headType() const
Get the current head type.
QgsPropertyCollection mDataDefinedProperties
void setArrowStartWidth(double width)
Set the arrow start width.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setArrowStartWidthUnitScale(const QgsMapUnitScale &scale)
Set the scale for the arrow start width.
void setOffset(double offset)
void startRender(QgsSymbolRenderContext &context) override
double headThickness() const
Get the current arrow head height.