QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 #include "qgsdiagramrenderer.h"
17 #include "qgsrendercontext.h"
18 #include "qgsexpression.h"
19 
20 #include <QPainter>
21 
23 {
24  mPen.setWidthF( 2.0 );
25  mPen.setColor( QColor( 0, 0, 0 ) );
26  mPen.setCapStyle( Qt::FlatCap );
27  mBrush.setStyle( Qt::SolidPattern );
28 }
29 
31 {
32  return new QgsTextDiagram( *this );
33 }
34 
36 {
37  QgsExpressionContext expressionContext = c.expressionContext();
38  expressionContext.setFeature( feature );
39  if ( !feature.fields().isEmpty() )
40  expressionContext.setFields( feature.fields() );
41 
42  QVariant attrVal;
44  {
45  QgsExpression *expression = getExpression( is.classificationAttributeExpression, expressionContext );
46  attrVal = expression->evaluate( &expressionContext );
47  }
48  else
49  {
50  attrVal = feature.attribute( is.classificationField );
51  }
52 
53  bool ok = false;
54  double val = attrVal.toDouble( &ok );
55  if ( !ok )
56  {
57  return QSizeF(); //zero size if attribute is missing
58  }
59 
60  return sizeForValue( val, s, is );
61 }
62 
63 double QgsTextDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const
64 {
65  QSizeF size = sizeForValue( value, s, is );
66  return std::max( size.width(), size.height() );
67 }
68 
70 {
71  return DIAGRAM_NAME_TEXT;
72 }
73 
74 QSizeF QgsTextDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s )
75 {
76  Q_UNUSED( c )
77  Q_UNUSED( attributes )
78 
79  return s.size;
80 }
81 
82 void QgsTextDiagram::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position )
83 {
84  QPainter *p = c.painter();
85  if ( !p )
86  {
87  return;
88  }
89 
90  //convert from mm / map units to painter units
91  QSizeF spu = sizePainterUnits( s.size, s, c );
92  double w = spu.width();
93  double h = spu.height();
94 
95  double baseX = position.x();
96  double baseY = position.y() - h;
97 
98  QVector<QPointF> textPositions; //midpoints for text placement
99  int nCategories = s.categoryAttributes.size();
100  for ( int i = 0; i < nCategories; ++i )
101  {
102  if ( mOrientation == Horizontal )
103  {
104  textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
105  }
106  else //vertical
107  {
108  textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
109  }
110  }
111 
112  mPen.setColor( s.penColor );
113  setPenWidth( mPen, s, c );
114  p->setPen( mPen );
115  mBrush.setColor( s.backgroundColor );
116  p->setBrush( mBrush );
117 
118  //draw shapes and separator lines first
119  if ( mShape == Circle )
120  {
121  p->drawEllipse( baseX, baseY, w, h );
122 
123  //draw separator lines
124  QList<QPointF> intersect; //intersections between shape and separation lines
125  QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
126  double r1 = w / 2.0;
127  double r2 = h / 2.0;
128 
129  for ( int i = 1; i < nCategories; ++i )
130  {
131  if ( mOrientation == Horizontal )
132  {
133  lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
134  }
135  else //vertical
136  {
137  lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
138  }
139  if ( intersect.size() > 1 )
140  {
141  p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
142  }
143  }
144  }
145  else if ( mShape == Rectangle )
146  {
147  p->drawRect( QRectF( baseX, baseY, w, h ) );
148  for ( int i = 1; i < nCategories; ++i )
149  {
150  if ( mOrientation == Horizontal )
151  {
152  p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
153  }
154  else
155  {
156  p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
157  }
158  }
159  }
160  else //triangle
161  {
162  QPolygonF triangle;
163  triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
164  p->drawPolygon( triangle );
165 
166  QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
167  QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
168  QPointF intersectionPoint1, intersectionPoint2;
169 
170  for ( int i = 1; i < nCategories; ++i )
171  {
172  if ( mOrientation == Horizontal )
173  {
174  QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
175  if ( baseX + w / nCategories * i < baseX + w / 2.0 )
176  {
177 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
178  verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
179 #else
180  verticalLine.intersects( triangleEdgeLeft, &intersectionPoint1 );
181 #endif
182  }
183  else
184  {
185 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
186  verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
187 #else
188  verticalLine.intersects( triangleEdgeRight, &intersectionPoint1 );
189 #endif
190  }
191  p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
192  }
193  else //vertical
194  {
195  QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
196 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
197  horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
198  horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
199 #else
200  horizontalLine.intersects( triangleEdgeLeft, &intersectionPoint1 );
201  horizontalLine.intersects( triangleEdgeRight, &intersectionPoint2 );
202 #endif
203  p->drawLine( intersectionPoint1, intersectionPoint2 );
204  }
205  }
206  }
207 
208  //draw text
209  QFont sFont = scaledFont( s, c );
210  QFontMetricsF fontMetrics( sFont );
211  p->setFont( sFont );
212 
213  QgsExpressionContext expressionContext = c.expressionContext();
214  expressionContext.setFeature( feature );
215  if ( !feature.fields().isEmpty() )
216  expressionContext.setFields( feature.fields() );
217 
218  for ( int i = 0; i < textPositions.size(); ++i )
219  {
220  QgsExpression *expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
221  QString val = expression->evaluate( &expressionContext ).toString();
222 
223  //find out dimensions
224 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
225  double textWidth = fontMetrics.width( val );
226 #else
227  double textWidth = fontMetrics.horizontalAdvance( val );
228 #endif
229  double textHeight = fontMetrics.height();
230 
231  mPen.setColor( s.categoryColors.at( i ) );
232  p->setPen( mPen );
233  QPointF position = textPositions.at( i );
234 
235  // Calculate vertical placement
236  double xOffset = 0;
237 
238  switch ( s.labelPlacementMethod )
239  {
241  xOffset = textHeight / 2.0;
242  break;
243 
245  xOffset = fontMetrics.xHeight();
246  break;
247  }
248  p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
249  }
250 }
251 
252 void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd, QPointF ellipseMid, double r1, double r2, QList<QPointF> &result ) const
253 {
254  result.clear();
255 
256  double rrx = r1 * r1;
257  double rry = r2 * r2;
258  double x21 = lineEnd.x() - lineStart.x();
259  double y21 = lineEnd.y() - lineStart.y();
260  double x10 = lineStart.x() - ellipseMid.x();
261  double y10 = lineStart.y() - ellipseMid.y();
262  double a = x21 * x21 / rrx + y21 * y21 / rry;
263  double b = x21 * x10 / rrx + y21 * y10 / rry;
264  double c = x10 * x10 / rrx + y10 * y10 / rry;
265  double d = b * b - a * ( c - 1 );
266  if ( d > 0 )
267  {
268  double e = std::sqrt( d );
269  double u1 = ( -b - e ) / a;
270  double u2 = ( -b + e ) / a;
271  //work with a tolerance of 0.00001 because of limited numerical precision
272  if ( -0.00001 <= u1 && u1 < 1.00001 )
273  {
274  result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
275  }
276  if ( -0.00001 <= u2 && u2 <= 1.00001 )
277  {
278  result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
279  }
280  }
281 }
QgsTextDiagram::Horizontal
@ Horizontal
Definition: qgstextdiagram.h:49
QgsDiagramSettings::backgroundColor
QColor backgroundColor
Definition: qgsdiagramrenderer.h:449
QgsDiagram::getExpression
QgsExpression * getExpression(const QString &expression, const QgsExpressionContext &context)
Returns a prepared expression for the specified context.
Definition: qgsdiagram.cpp:37
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
DIAGRAM_NAME_TEXT
#define DIAGRAM_NAME_TEXT
Definition: qgstextdiagram.h:18
QgsDiagram::sizeForValue
QSizeF sizeForValue(double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is) const
Returns the scaled size of a diagram for a value, respecting the specified diagram interpolation sett...
Definition: qgsdiagram.cpp:80
QgsFields::isEmpty
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
QgsDiagramSettings::categoryColors
QList< QColor > categoryColors
Definition: qgsdiagramrenderer.h:420
QgsDiagramSettings::penColor
QColor penColor
Definition: qgsdiagramrenderer.h:450
qgsexpression.h
QgsDiagramSettings::labelPlacementMethod
LabelPlacementMethod labelPlacementMethod
Definition: qgsdiagramrenderer.h:452
QgsTextDiagram::Rectangle
@ Rectangle
Definition: qgstextdiagram.h:43
QgsTextDiagram::diagramSize
QSizeF diagramSize(const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s) override
Returns the size in map units the diagram will use to render.
Definition: qgstextdiagram.cpp:74
QgsTextDiagram::clone
QgsTextDiagram * clone() const override
Returns an instance that is equivalent to this one.
Definition: qgstextdiagram.cpp:30
QgsDiagramSettings::XHeight
@ XHeight
Definition: qgsdiagramrenderer.h:387
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsExpressionContext::setFields
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Definition: qgsexpressioncontext.cpp:553
QgsTextDiagram::legendSize
double legendSize(double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is) const override
Returns the size of the legend item for the diagram corresponding to a specified value.
Definition: qgstextdiagram.cpp:63
QgsDiagram::scaledFont
QFont scaledFont(const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:64
QgsDiagramInterpolationSettings::classificationField
QString classificationField
Name of the field for classification.
Definition: qgsdiagramrenderer.h:670
QgsTextDiagram
Definition: qgstextdiagram.h:38
QgsDiagram::setPenWidth
void setPenWidth(QPen &pen, const QgsDiagramSettings &s, const QgsRenderContext &c)
Changes the pen width to match the current settings and rendering context.
Definition: qgsdiagram.cpp:48
QgsTextDiagram::QgsTextDiagram
QgsTextDiagram()
Definition: qgstextdiagram.cpp:22
QgsDiagram::sizePainterUnits
QSizeF sizePainterUnits(QSizeF size, const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:54
qgsrendercontext.h
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsDiagramSettings::Height
@ Height
Definition: qgsdiagramrenderer.h:386
qgsdiagramrenderer.h
QgsDiagramInterpolationSettings
Additional diagram settings for interpolated size rendering.
Definition: qgsdiagramrenderer.h:662
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:346
QgsDiagramInterpolationSettings::classificationAttributeIsExpression
bool classificationAttributeIsExpression
Definition: qgsdiagramrenderer.h:673
QgsDiagramSettings::size
QSizeF size
Definition: qgsdiagramrenderer.h:424
QgsTextDiagram::Circle
@ Circle
Definition: qgstextdiagram.h:42
QgsTextDiagram::diagramName
QString diagramName() const override
Gets a descriptive name for this diagram type.
Definition: qgstextdiagram.cpp:69
c
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
Definition: porting_processing.dox:1
QgsTextDiagram::renderDiagram
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position) override
Draws the diagram at the given position (in pixel coordinates)
Definition: qgstextdiagram.cpp:82
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:66
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:105
QgsDiagramSettings::categoryAttributes
QList< QString > categoryAttributes
Definition: qgsdiagramrenderer.h:421
QgsDiagramInterpolationSettings::classificationAttributeExpression
QString classificationAttributeExpression
Definition: qgsdiagramrenderer.h:672
QgsDiagramSettings
Stores the settings for rendering a single diagram.
Definition: qgsdiagramrenderer.h:381
qgstextdiagram.h
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:521