QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
178  }
179  else
180  {
181  verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
182  }
183  p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
184  }
185  else //vertical
186  {
187  QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
188  horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
189  horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
190  p->drawLine( intersectionPoint1, intersectionPoint2 );
191  }
192  }
193  }
194 
195  //draw text
196  QFont sFont = scaledFont( s, c );
197  QFontMetricsF fontMetrics( sFont );
198  p->setFont( sFont );
199 
200  QgsExpressionContext expressionContext = c.expressionContext();
201  expressionContext.setFeature( feature );
202  if ( !feature.fields().isEmpty() )
203  expressionContext.setFields( feature.fields() );
204 
205  for ( int i = 0; i < textPositions.size(); ++i )
206  {
207  QgsExpression *expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
208  QString val = expression->evaluate( &expressionContext ).toString();
209 
210  //find out dimensions
211  double textWidth = fontMetrics.width( val );
212  double textHeight = fontMetrics.height();
213 
214  mPen.setColor( s.categoryColors.at( i ) );
215  p->setPen( mPen );
216  QPointF position = textPositions.at( i );
217 
218  // Calculate vertical placement
219  double xOffset = 0;
220 
221  switch ( s.labelPlacementMethod )
222  {
224  xOffset = textHeight / 2.0;
225  break;
226 
228  xOffset = fontMetrics.xHeight();
229  break;
230  }
231  p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
232  }
233 }
234 
235 void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd, QPointF ellipseMid, double r1, double r2, QList<QPointF> &result ) const
236 {
237  result.clear();
238 
239  double rrx = r1 * r1;
240  double rry = r2 * r2;
241  double x21 = lineEnd.x() - lineStart.x();
242  double y21 = lineEnd.y() - lineStart.y();
243  double x10 = lineStart.x() - ellipseMid.x();
244  double y10 = lineStart.y() - ellipseMid.y();
245  double a = x21 * x21 / rrx + y21 * y21 / rry;
246  double b = x21 * x10 / rrx + y21 * y10 / rry;
247  double c = x10 * x10 / rrx + y10 * y10 / rry;
248  double d = b * b - a * ( c - 1 );
249  if ( d > 0 )
250  {
251  double e = std::sqrt( d );
252  double u1 = ( -b - e ) / a;
253  double u2 = ( -b + e ) / a;
254  //work with a tolerance of 0.00001 because of limited numerical precision
255  if ( -0.00001 <= u1 && u1 < 1.00001 )
256  {
257  result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
258  }
259  if ( -0.00001 <= u2 && u2 <= 1.00001 )
260  {
261  result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
262  }
263  }
264 }
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:369
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
Definition: qgsrendercontext.h:57
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:37
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:262
QgsDiagramSettings::Height
@ Height
Definition: qgsdiagramrenderer.h:386
qgsdiagramrenderer.h
QgsDiagramInterpolationSettings
Definition: qgsdiagramrenderer.h:661
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
Definition: qgsattributes.h:57
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:70
QgsFeature
Definition: qgsfeature.h:55
QgsExpression
Definition: qgsexpression.h:113
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:380
qgstextdiagram.h
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:521