QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgslayoutitempolyline.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitempolyline.cpp
3 begin : March 2016
4 copyright : (C) 2016 Paul Blottiere, Oslandia
5 email : paul dot blottiere at oslandia dot com
6 ***************************************************************************/
7
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include <limits>
20
21#include "qgscolorutils.h"
22#include "qgslayout.h"
25#include "qgslayoututils.h"
26#include "qgslinesymbol.h"
27#include "qgsreadwritecontext.h"
29#include "qgssvgcache.h"
30#include "qgssymbol.h"
31#include "qgssymbollayerutils.h"
32
33#include <QGraphicsPathItem>
34#include <QString>
35#include <QSvgRenderer>
36#include <QVector2D>
37
38#include "moc_qgslayoutitempolyline.cpp"
39
40using namespace Qt::StringLiterals;
41
44{
45 createDefaultPolylineStyleSymbol();
46}
47
49 : QgsLayoutNodesItem( polyline, layout )
50{
51 createDefaultPolylineStyleSymbol();
52}
53
55
60
65
67{
68 return QgsApplication::getThemeIcon( u"/mLayoutItemPolyline.svg"_s );
69}
70
71bool QgsLayoutItemPolyline::_addNode( const int indexPoint, QPointF newPoint, const double radius )
72{
73 const double distStart = computeDistance( newPoint, mPolygon[0] );
74 const double distEnd = computeDistance( newPoint, mPolygon[mPolygon.size() - 1] );
75
76 if ( indexPoint == ( mPolygon.size() - 1 ) )
77 {
78 if ( distEnd < radius )
79 mPolygon.append( newPoint );
80 else if ( distStart < radius )
81 mPolygon.insert( 0, newPoint );
82 }
83 else
84 mPolygon.insert( indexPoint + 1, newPoint );
85
86 return true;
87}
88
90{
91 if ( index < 0 || index >= mPolygon.size() || mPolygon.size() <= 2 )
92 return false;
93
94 mPolygon.remove( index );
95
96 int newSelectNode = index;
97 if ( index >= mPolygon.size() )
98 newSelectNode = mPolygon.size() - 1;
99 setSelectedNode( newSelectNode );
100
101 return true;
102}
103
104void QgsLayoutItemPolyline::createDefaultPolylineStyleSymbol()
105{
106 QVariantMap properties;
107 properties.insert( u"color"_s, u"0,0,0,255"_s );
108 properties.insert( u"width"_s, u"0.3"_s );
109 properties.insert( u"capstyle"_s, u"square"_s );
110
111 mPolylineStyleSymbol = QgsLineSymbol::createSimple( properties );
112 refreshSymbol();
113}
114
115void QgsLayoutItemPolyline::refreshSymbol()
116{
117 if ( auto *lLayout = layout() )
118 {
119 const QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( lLayout, nullptr, lLayout->renderContext().dpi() );
120 mMaxSymbolBleed = ( 25.4 / lLayout->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mPolylineStyleSymbol.get(), rc );
121 }
122
124
125 emit frameChanged();
126}
127
128void QgsLayoutItemPolyline::drawStartMarker( QPainter *painter )
129{
130 if ( mPolygon.size() < 2 )
131 return;
132
133 switch ( mStartMarker )
134 {
136 break;
137
139 {
140 // calculate angle at start of line
141 const QLineF startLine( mPolygon.at( 0 ), mPolygon.at( 1 ) );
142 const double angle = startLine.angle();
143 drawArrow( painter, mPolygon.at( 0 ), angle );
144 break;
145 }
146
148 {
149 // calculate angle at start of line
150 const QLineF startLine( mPolygon.at( 0 ), mPolygon.at( 1 ) );
151 const double angle = startLine.angle();
152 drawSvgMarker( painter, mPolygon.at( 0 ), angle, mStartMarkerFile, mStartArrowHeadHeight );
153 break;
154 }
155 }
156}
157
158void QgsLayoutItemPolyline::drawEndMarker( QPainter *painter )
159{
160 if ( mPolygon.size() < 2 )
161 return;
162
163 switch ( mEndMarker )
164 {
166 break;
167
169 {
170 // calculate angle at end of line
171 const QLineF endLine( mPolygon.at( mPolygon.count() - 2 ), mPolygon.at( mPolygon.count() - 1 ) );
172 const double angle = endLine.angle();
173
174 //move end point depending on arrow width
175 const QVector2D dir = QVector2D( endLine.dx(), endLine.dy() ).normalized();
176 QPointF endPoint = endLine.p2();
177 endPoint += ( dir * 0.5 * mArrowHeadWidth ).toPointF();
178
179 drawArrow( painter, endPoint, angle );
180 break;
181 }
183 {
184 // calculate angle at end of line
185 const QLineF endLine( mPolygon.at( mPolygon.count() - 2 ), mPolygon.at( mPolygon.count() - 1 ) );
186 const double angle = endLine.angle();
187 drawSvgMarker( painter, endLine.p2(), angle, mEndMarkerFile, mEndArrowHeadHeight );
188 break;
189 }
190 }
191}
192
193void QgsLayoutItemPolyline::drawArrow( QPainter *painter, QPointF center, double angle )
194{
195 // translate angle from ccw from axis to cw from north
196 angle = 90 - angle;
197 QPen p;
198 p.setColor( mArrowHeadStrokeColor );
199 p.setWidthF( mArrowHeadStrokeWidth );
200 painter->setPen( p );
201 QBrush b;
202 b.setColor( mArrowHeadFillColor );
203 painter->setBrush( b );
204
205 drawArrowHead( painter, center.x(), center.y(), angle, mArrowHeadWidth );
206}
207
208void QgsLayoutItemPolyline::updateMarkerSvgSizes()
209{
210 setStartSvgMarkerPath( mStartMarkerFile );
211 setEndSvgMarkerPath( mEndMarkerFile );
212}
213
214void QgsLayoutItemPolyline::drawArrowHead( QPainter *p, const double x, const double y, const double angle, const double arrowHeadWidth )
215{
216 if ( !p )
217 return;
218
219 const double angleRad = angle / 180.0 * M_PI;
220 const QPointF middlePoint( x, y );
221
222 //rotate both arrow points
223 const QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
224 const QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
225
226 QPointF p1Rotated, p2Rotated;
227 p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -std::sin( angleRad ) );
228 p1Rotated.setY( p1.x() * std::sin( angleRad ) + p1.y() * std::cos( angleRad ) );
229 p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -std::sin( angleRad ) );
230 p2Rotated.setY( p2.x() * std::sin( angleRad ) + p2.y() * std::cos( angleRad ) );
231
232 QPolygonF arrowHeadPoly;
233 arrowHeadPoly << middlePoint;
234 arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
235 arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
236 QPen arrowPen = p->pen();
237 arrowPen.setJoinStyle( Qt::RoundJoin );
238 QBrush arrowBrush = p->brush();
239 arrowBrush.setStyle( Qt::SolidPattern );
240 p->setPen( arrowPen );
241 p->setBrush( arrowBrush );
242 arrowBrush.setStyle( Qt::SolidPattern );
243 p->drawPolygon( arrowHeadPoly );
244}
245
246void QgsLayoutItemPolyline::drawSvgMarker( QPainter *p, QPointF point, double angle, const QString &markerPath, double height ) const
247{
248 // translate angle from ccw from axis to cw from north
249 angle = 90 - angle;
250
251 if ( mArrowHeadWidth <= 0 || height <= 0 )
252 {
253 //bad image size
254 return;
255 }
256
257 if ( markerPath.isEmpty() )
258 return;
259
260 QSvgRenderer r;
261 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( markerPath, mArrowHeadWidth, mArrowHeadFillColor, mArrowHeadStrokeColor, mArrowHeadStrokeWidth, 1.0 );
262 r.load( svgContent );
263
264 const QgsScopedQPainterState painterState( p );
265 p->translate( point.x(), point.y() );
266 p->rotate( angle );
267 p->translate( -mArrowHeadWidth / 2.0, -height / 2.0 );
268 r.render( p, QRectF( 0, 0, mArrowHeadWidth, height ) );
269}
270
272{
273 if ( !id().isEmpty() )
274 return id();
275
276 return tr( "<Polyline>" );
277}
278
279void QgsLayoutItemPolyline::_draw( QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem * )
280{
281 QgsRenderContext renderContext = context.renderContext();
282 // symbol clipping messes with geometry generators used in the symbol for this item, and has no
283 // valid use here. See https://github.com/qgis/QGIS/issues/58909
285
286 const QgsScopedQPainterState painterState( renderContext.painter() );
287 //setup painter scaling to dots so that raster symbology is drawn to scale
288 const double scale = renderContext.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
289 const QTransform t = QTransform::fromScale( scale, scale );
290
291 mPolylineStyleSymbol->startRender( renderContext );
292 mPolylineStyleSymbol->renderPolyline( t.map( mPolygon ), nullptr, renderContext );
293 mPolylineStyleSymbol->stopRender( renderContext );
294
295 // painter is scaled to dots, so scale back to layout units
296 renderContext.painter()->scale( renderContext.scaleFactor(), renderContext.scaleFactor() );
297
298 drawStartMarker( renderContext.painter() );
299 drawEndMarker( renderContext.painter() );
300}
301
302void QgsLayoutItemPolyline::_readXmlStyle( const QDomElement &elmt, const QgsReadWriteContext &context )
303{
304 mPolylineStyleSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( elmt, context );
305}
306
308{
309 mPolylineStyleSymbol.reset( static_cast<QgsLineSymbol *>( symbol->clone() ) );
310 refreshSymbol();
311}
312
314{
315 mStartMarker = mode;
316 update();
317}
318
320{
321 mEndMarker = mode;
322 update();
323}
324
326{
327 mArrowHeadWidth = width;
328 updateMarkerSvgSizes();
329 update();
330}
331
333{
334 QPainterPath path;
335 path.addPolygon( mPolygon );
336
337 QPainterPathStroker ps;
338
339 ps.setWidth( 2 * mMaxSymbolBleed );
340 const QPainterPath strokedOutline = ps.createStroke( path );
341
342 return strokedOutline;
343}
344
346{
347 // A Polyline is valid if it has at least 2 unique points
348 QList<QPointF> uniquePoints;
349 int seen = 0;
350 for ( QPointF point : mPolygon )
351 {
352 if ( !uniquePoints.contains( point ) )
353 {
354 uniquePoints.append( point );
355 if ( ++seen > 1 )
356 return true;
357 }
358 }
359 return false;
360}
361
363{
364 return mPolylineStyleSymbol.get();
365}
366
368{
369 QSvgRenderer r;
370 mStartMarkerFile = path;
371 if ( path.isEmpty() || !r.load( path ) )
372 {
373 mStartArrowHeadHeight = 0;
374 }
375 else
376 {
377 //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
378 const QRect viewBox = r.viewBox();
379 mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
380 }
382}
383
385{
386 QSvgRenderer r;
387 mEndMarkerFile = path;
388 if ( path.isEmpty() || !r.load( path ) )
389 {
390 mEndArrowHeadHeight = 0;
391 }
392 else
393 {
394 //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
395 const QRect viewBox = r.viewBox();
396 mEndArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
397 }
399}
400
402{
403 mArrowHeadStrokeColor = color;
404 update();
405}
406
408{
409 mArrowHeadFillColor = color;
410 update();
411}
412
414{
415 mArrowHeadStrokeWidth = width;
417 update();
418}
419
421{
422 if ( mPolylineStyleSymbol )
423 {
424 QgsStyleSymbolEntity entity( mPolylineStyleSymbol.get() );
425 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
426 return false;
427 }
428
429 return true;
430}
431
432void QgsLayoutItemPolyline::_writeXmlStyle( QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context ) const
433{
434 const QDomElement pe = QgsSymbolLayerUtils::saveSymbol( QString(), mPolylineStyleSymbol.get(), doc, context );
435 elmt.appendChild( pe );
436}
437
438bool QgsLayoutItemPolyline::writePropertiesToElement( QDomElement &elmt, QDomDocument &doc, const QgsReadWriteContext &context ) const
439{
441
442 // absolute paths to relative
443 const QString startMarkerPath = QgsSymbolLayerUtils::svgSymbolPathToName( mStartMarkerFile, context.pathResolver() );
444 const QString endMarkerPath = QgsSymbolLayerUtils::svgSymbolPathToName( mEndMarkerFile, context.pathResolver() );
445
446 elmt.setAttribute( u"arrowHeadWidth"_s, QString::number( mArrowHeadWidth ) );
447 elmt.setAttribute( u"arrowHeadFillColor"_s, QgsColorUtils::colorToString( mArrowHeadFillColor ) );
448 elmt.setAttribute( u"arrowHeadOutlineColor"_s, QgsColorUtils::colorToString( mArrowHeadStrokeColor ) );
449 elmt.setAttribute( u"outlineWidth"_s, QString::number( mArrowHeadStrokeWidth ) );
450 elmt.setAttribute( u"markerMode"_s, mEndMarker );
451 elmt.setAttribute( u"startMarkerMode"_s, mStartMarker );
452 elmt.setAttribute( u"startMarkerFile"_s, startMarkerPath );
453 elmt.setAttribute( u"endMarkerFile"_s, endMarkerPath );
454
455 return true;
456}
457
458bool QgsLayoutItemPolyline::readPropertiesFromElement( const QDomElement &elmt, const QDomDocument &doc, const QgsReadWriteContext &context )
459{
460 mArrowHeadWidth = elmt.attribute( u"arrowHeadWidth"_s, u"2.0"_s ).toDouble();
461 mArrowHeadFillColor = QgsColorUtils::colorFromString( elmt.attribute( u"arrowHeadFillColor"_s, u"0,0,0,255"_s ) );
462 mArrowHeadStrokeColor = QgsColorUtils::colorFromString( elmt.attribute( u"arrowHeadOutlineColor"_s, u"0,0,0,255"_s ) );
463 mArrowHeadStrokeWidth = elmt.attribute( u"outlineWidth"_s, u"1.0"_s ).toDouble();
464 // relative paths to absolute
465 const QString startMarkerPath = elmt.attribute( u"startMarkerFile"_s, QString() );
466 const QString endMarkerPath = elmt.attribute( u"endMarkerFile"_s, QString() );
469 mEndMarker = static_cast< QgsLayoutItemPolyline::MarkerMode >( elmt.attribute( u"markerMode"_s, u"0"_s ).toInt() );
470 mStartMarker = static_cast< QgsLayoutItemPolyline::MarkerMode >( elmt.attribute( u"startMarkerMode"_s, u"0"_s ).toInt() );
471
473
475 return true;
476}
477
479{
480 QRectF br = rect();
481
482 double margin = std::max( mMaxSymbolBleed, computeMarkerMargin() );
483 if ( mEndMarker == ArrowHead )
484 {
485 margin += 0.5 * mArrowHeadWidth;
486 }
487 br.adjust( -margin, -margin, margin, margin );
488 prepareGeometryChange();
490
491 // update
492 update();
493}
494
495
496double QgsLayoutItemPolyline::computeMarkerMargin() const
497{
498 double margin = 0;
499
500 if ( mStartMarker == ArrowHead || mEndMarker == ArrowHead )
501 {
502 margin = mArrowHeadStrokeWidth / 2.0 + mArrowHeadWidth * M_SQRT2;
503 }
504
505 if ( mStartMarker == SvgMarker )
506 {
507 const double startMarkerMargin = std::sqrt( 0.25 * ( mStartArrowHeadHeight * mStartArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
508 margin = std::max( startMarkerMargin, margin );
509 }
510
511 if ( mEndMarker == SvgMarker )
512 {
513 const double endMarkerMargin = std::sqrt( 0.25 * ( mEndArrowHeadHeight * mEndArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) );
514 margin = std::max( endMarkerMargin, margin );
515 }
516
517 return margin;
518}
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ DisableSymbolClippingToExtent
Force symbol clipping to map extent to be disabled in all situations. This will result in slower rend...
Definition qgis.h:2867
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
bool isValid() const override
Must be reimplemented in subclasses.
void setArrowHeadWidth(double width)
Sets the width of line arrow heads in mm.
void setEndMarker(MarkerMode mode)
Sets the end marker mode, which controls what marker is drawn at the end of the line.
void setEndSvgMarkerPath(const QString &path)
Sets the path to a SVG marker to draw at the end of the line.
void _writeXmlStyle(QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context) const override
Method called in writeXml.
void setArrowHeadStrokeWidth(double width)
Sets the pen width in millimeters for the stroke of the arrow head.
void _readXmlStyle(const QDomElement &elmt, const QgsReadWriteContext &context) override
Method called in readXml.
void setArrowHeadFillColor(const QColor &color)
Sets the color used to fill the arrow head.
bool _removeNode(int nodeIndex) override
Method called in removeNode.
QgsLineSymbol * symbol()
Returns the line symbol used to draw the shape.
void setArrowHeadStrokeColor(const QColor &color)
Sets the color used to draw the stroke around the arrow head.
void setStartMarker(MarkerMode mode)
Sets the start marker mode, which controls what marker is drawn at the start of the line.
MarkerMode
Vertex marker mode.
@ ArrowHead
Show arrow marker.
@ NoMarker
Don't show marker.
QPainterPath shape() const override
~QgsLayoutItemPolyline() override
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QString displayName() const override
Gets item display name.
static QgsLayoutItemPolyline * create(QgsLayout *layout)
Returns a new polyline item for the specified layout.
bool _addNode(int indexPoint, QPointF newPoint, double radius) override
Method called in addNode.
void setSymbol(QgsLineSymbol *symbol)
Sets the symbol used to draw the shape.
QIcon icon() const override
Returns the item's icon.
double arrowHeadWidth() const
Returns the width of line arrow heads in mm.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void _draw(QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem *itemStyle=nullptr) override
Method called in paint.
void setStartSvgMarkerPath(const QString &path)
Sets the path to a SVG marker to draw at the start of the line.
QgsLayoutItemPolyline(QgsLayout *layout)
Constructor for QgsLayoutItemPolyline for the specified layout.
@ LayoutPolyline
Polyline shape item.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
friend class QgsLayout
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
void frameChanged()
Emitted if the item's frame style changes.
double mMaxSymbolBleed
Max symbol bleed.
QRectF mCurrentRectangle
Current bounding rectangle of shape.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool setSelectedNode(int index)
Selects a node by index.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void updateSceneRect()
Update the current scene rectangle for this item.
double computeDistance(QPointF pt1, QPointF pt2) const
Compute an euclidean distance between 2 nodes.
QgsLayoutNodesItem(QgsLayout *layout)
Constructor for QgsLayoutNodesItem, attached to the specified layout.
QPolygonF mPolygon
Shape's nodes.
const QgsLayout * layout() const
Returns the layout the object is attached to.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
A line symbol type, for rendering LineString and MultiLineString geometries.
static std::unique_ptr< QgsLineSymbol > createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
A container for the context for various read/write operations on objects.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
Scoped object for saving and restoring a QPainter object's state.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1393
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
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).
Contains information relating to the style entity currently being visited.