QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgstextdiagram.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstextdiagram.cpp
3 ---------------------
4 begin : March 2011
5 copyright : (C) 2011 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include "qgstextdiagram.h"
16
17#include "qgsdiagramrenderer.h"
18#include "qgsexpression.h"
19#include "qgsrendercontext.h"
20
21#include <QPainter>
22#include <QString>
23
24using namespace Qt::StringLiterals;
25
26const QString QgsTextDiagram::DIAGRAM_NAME_TEXT = u"Text"_s;
27
29{
30 mPen.setWidthF( 2.0 );
31 mPen.setColor( QColor( 0, 0, 0 ) );
32 mPen.setCapStyle( Qt::FlatCap );
33 mBrush.setStyle( Qt::SolidPattern );
34}
35
37{
38 return new QgsTextDiagram( *this );
39}
40
42{
43 QgsExpressionContext expressionContext = c.expressionContext();
44 expressionContext.setFeature( feature );
45 if ( !feature.fields().isEmpty() )
46 expressionContext.setFields( feature.fields() );
47
48 QVariant attrVal;
50 {
51 QgsExpression *expression = getExpression( is.classificationAttributeExpression, expressionContext );
52 attrVal = expression->evaluate( &expressionContext );
53 }
54 else
55 {
56 attrVal = feature.attribute( is.classificationField );
57 }
58
59 bool ok = false;
60 const double val = attrVal.toDouble( &ok );
61 if ( !ok )
62 {
63 return QSizeF(); //zero size if attribute is missing
64 }
65
66 return sizeForValue( val, s, is );
67}
68
69double QgsTextDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const
70{
71 const QSizeF size = sizeForValue( value, s, is );
72 return std::max( size.width(), size.height() );
73}
74
79
81{
82 Q_UNUSED( c )
83 Q_UNUSED( attributes )
84
85 return s.size;
86}
87
88void QgsTextDiagram::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position )
89{
90 QPainter *p = c.painter();
91 if ( !p )
92 {
93 return;
94 }
95
96 //convert from mm / map units to painter units
97 const QSizeF spu = sizePainterUnits( s.size, s, c );
98 const double w = spu.width();
99 const double h = spu.height();
100
101 const double baseX = position.x();
102 const double baseY = position.y() - h;
103
104 QVector<QPointF> textPositions; //midpoints for text placement
105 const int nCategories = s.categoryAttributes.size();
106 for ( int i = 0; i < nCategories; ++i )
107 {
108 if ( mOrientation == Horizontal )
109 {
110 textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
111 }
112 else //vertical
113 {
114 textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
115 }
116 }
117
118 mPen.setColor( s.penColor );
119 setPenWidth( mPen, s, c );
120 p->setPen( mPen );
121 mBrush.setColor( s.backgroundColor );
122 p->setBrush( mBrush );
123
124 //draw shapes and separator lines first
125 if ( mShape == Circle )
126 {
127 p->drawEllipse( baseX, baseY, w, h );
128
129 //draw separator lines
130 QList<QPointF> intersect; //intersections between shape and separation lines
131 const QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
132 const double r1 = w / 2.0;
133 const double r2 = h / 2.0;
134
135 for ( int i = 1; i < nCategories; ++i )
136 {
137 if ( mOrientation == Horizontal )
138 {
139 lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
140 }
141 else //vertical
142 {
143 lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
144 }
145 if ( intersect.size() > 1 )
146 {
147 p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
148 }
149 }
150 }
151 else if ( mShape == Rectangle )
152 {
153 p->drawRect( QRectF( baseX, baseY, w, h ) );
154 for ( int i = 1; i < nCategories; ++i )
155 {
156 if ( mOrientation == Horizontal )
157 {
158 p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
159 }
160 else
161 {
162 p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
163 }
164 }
165 }
166 else //triangle
167 {
168 QPolygonF triangle;
169 triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
170 p->drawPolygon( triangle );
171
172 const QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
173 const QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
174 QPointF intersectionPoint1, intersectionPoint2;
175
176 for ( int i = 1; i < nCategories; ++i )
177 {
178 if ( mOrientation == Horizontal )
179 {
180 const QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
181 if ( baseX + w / nCategories * i < baseX + w / 2.0 )
182 {
183 verticalLine.intersects( triangleEdgeLeft, &intersectionPoint1 );
184 }
185 else
186 {
187 verticalLine.intersects( triangleEdgeRight, &intersectionPoint1 );
188 }
189 p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
190 }
191 else //vertical
192 {
193 const QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
194 horizontalLine.intersects( triangleEdgeLeft, &intersectionPoint1 );
195 horizontalLine.intersects( triangleEdgeRight, &intersectionPoint2 );
196 p->drawLine( intersectionPoint1, intersectionPoint2 );
197 }
198 }
199 }
200
201 //draw text
202 const QFont sFont = scaledFont( s, c );
203 const QFontMetricsF fontMetrics( sFont );
204 p->setFont( sFont );
205
206 QgsExpressionContext expressionContext = c.expressionContext();
207 expressionContext.setFeature( feature );
208 if ( !feature.fields().isEmpty() )
209 expressionContext.setFields( feature.fields() );
210
211 for ( int i = 0; i < textPositions.size(); ++i )
212 {
213 QgsExpression *expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
214 const QString val = expression->evaluate( &expressionContext ).toString();
215
216 //find out dimensions
217 const double textWidth = fontMetrics.horizontalAdvance( val );
218 const double textHeight = fontMetrics.height();
219
220 mPen.setColor( s.categoryColors.at( i ) );
221 p->setPen( mPen );
222 const QPointF position = textPositions.at( i );
223
224 // Calculate vertical placement
225 double xOffset = 0;
226
227 switch ( s.labelPlacementMethod )
228 {
230 xOffset = textHeight / 2.0;
231 break;
232
234 xOffset = fontMetrics.xHeight();
235 break;
236 }
237 p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
238 }
239}
240
241void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd, QPointF ellipseMid, double r1, double r2, QList<QPointF> &result ) const
242{
243 result.clear();
244
245 const double rrx = r1 * r1;
246 const double rry = r2 * r2;
247 const double x21 = lineEnd.x() - lineStart.x();
248 const double y21 = lineEnd.y() - lineStart.y();
249 const double x10 = lineStart.x() - ellipseMid.x();
250 const double y10 = lineStart.y() - ellipseMid.y();
251 const double a = x21 * x21 / rrx + y21 * y21 / rry;
252 const double b = x21 * x10 / rrx + y21 * y10 / rry;
253 const double c = x10 * x10 / rrx + y10 * y10 / rry;
254 const double d = b * b - a * ( c - 1 );
255 if ( d > 0 )
256 {
257 const double e = std::sqrt( d );
258 const double u1 = ( -b - e ) / a;
259 const double u2 = ( -b + e ) / a;
260 //work with a tolerance of 0.00001 because of limited numerical precision
261 if ( -0.00001 <= u1 && u1 < 1.00001 )
262 {
263 result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
264 }
265 if ( -0.00001 <= u2 && u2 <= 1.00001 )
266 {
267 result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
268 }
269 }
270}
A vector of attributes.
Additional diagram settings for interpolated size rendering.
QString classificationField
Name of the field for classification.
Stores the settings for rendering a single diagram.
LabelPlacementMethod labelPlacementMethod
QList< QString > categoryAttributes
QList< QColor > categoryColors
void setPenWidth(QPen &pen, const QgsDiagramSettings &s, const QgsRenderContext &c)
Changes the pen width to match the current settings and rendering context.
QSizeF sizePainterUnits(QSizeF size, const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
QgsExpression * getExpression(const QString &expression, const QgsExpressionContext &context)
Returns a prepared expression for the specified context.
QSizeF sizeForValue(double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &interpolationSettings) const
Returns the scaled size of a diagram for a value, respecting the specified diagram interpolation sett...
QFont scaledFont(const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
QVariant evaluate()
Evaluate the feature and return the result.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:70
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
bool isEmpty
Definition qgsfields.h:49
Contains information about the context of a rendering operation.
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position) override
Draws the diagram at the given position (in pixel coordinates).
double legendSize(double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &interpolationSettings) const override
Returns the size of the legend item for the diagram corresponding to a specified value.
QgsTextDiagram * clone() const override
Returns an instance that is equivalent to this one.
QSizeF diagramSize(const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s) override
Returns the size in map units the diagram will use to render.
QString diagramName() const override
Gets a descriptive name for this diagram type.
static const QString DIAGRAM_NAME_TEXT
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c