QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsdiagramrendererv2.h"
17 #include "qgsrendercontext.h"
18 #include "qgsexpression.h"
19 
20 #include <QPainter>
21 
22 QgsTextDiagram::QgsTextDiagram(): mOrientation( Vertical ), mShape( Circle )
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 }
33 
35 {
36  return new QgsTextDiagram( *this );
37 }
38 
40 {
41  QgsExpressionContext expressionContext = c.expressionContext();
42  expressionContext.setFeature( feature );
43  if ( feature.fields() )
44  expressionContext.setFields( *feature.fields() );
45 
46  QVariant attrVal;
48  {
49  QgsExpression* expression = getExpression( is.classificationAttributeExpression, expressionContext );
50  attrVal = expression->evaluate( &expressionContext );
51  }
52  else
53  {
54  attrVal = feature.attributes().at( is.classificationAttribute );
55  }
56 
57  bool ok = false;
58  double val = attrVal.toDouble( &ok );
59  if ( !ok )
60  {
61  return QSizeF(); //zero size if attribute is missing
62  }
63 
64  return sizeForValue( val, s, is );
65 }
66 
67 double QgsTextDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const
68 {
69  QSizeF size = sizeForValue( value, s, is );
70  return qMax( size.width(), size.height() );
71 }
72 
74 {
75  Q_UNUSED( c );
76  Q_UNUSED( attributes );
77 
78  return s.size;
79 }
80 
82 {
83  QPainter* p = c.painter();
84  if ( !p )
85  {
86  return;
87  }
88 
89  //convert from mm / map units to painter units
90  QSizeF spu = sizePainterUnits( s.size, s, c );
91  double w = spu.width();
92  double h = spu.height();
93 
94  double baseX = position.x();
95  double baseY = position.y() - h;
96 
97  QVector<QPointF> textPositions; //midpoints for text placement
98  int nCategories = s.categoryAttributes.size();
99  for ( int i = 0; i < nCategories; ++i )
100  {
101  if ( mOrientation == Horizontal )
102  {
103  textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
104  }
105  else //vertical
106  {
107  textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
108  }
109  }
110 
111  mPen.setColor( s.penColor );
112  setPenWidth( mPen, s, c );
113  p->setPen( mPen );
114  mBrush.setColor( s.backgroundColor );
115  p->setBrush( mBrush );
116 
117  //draw shapes and separator lines first
118  if ( mShape == Circle )
119  {
120  p->drawEllipse( baseX, baseY, w, h );
121 
122  //draw separator lines
123  QList<QPointF> intersect; //intersections between shape and separation lines
124  QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
125  double r1 = w / 2.0;
126  double r2 = h / 2.0;
127 
128  for ( int i = 1; i < nCategories; ++i )
129  {
130  if ( mOrientation == Horizontal )
131  {
132  lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
133  }
134  else //vertical
135  {
136  lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
137  }
138  if ( intersect.size() > 1 )
139  {
140  p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
141  }
142  }
143  }
144  else if ( mShape == Rectangle )
145  {
146  p->drawRect( QRectF( baseX, baseY, w, h ) );
147  for ( int i = 1; i < nCategories; ++i )
148  {
149  if ( mOrientation == Horizontal )
150  {
151  p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
152  }
153  else
154  {
155  p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
156  }
157  }
158  }
159  else //triangle
160  {
161  QPolygonF triangle;
162  triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
163  p->drawPolygon( triangle );
164 
165  QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
166  QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
167  QPointF intersectionPoint1, intersectionPoint2;
168 
169  for ( int i = 1; i < nCategories; ++i )
170  {
171  if ( mOrientation == Horizontal )
172  {
173  QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
174  if ( baseX + w / nCategories * i < baseX + w / 2.0 )
175  {
176  verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
177  }
178  else
179  {
180  verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
181  }
182  p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
183  }
184  else //vertical
185  {
186  QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
187  horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
188  horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
189  p->drawLine( intersectionPoint1, intersectionPoint2 );
190  }
191  }
192  }
193 
194  //draw text
195  QFont sFont = scaledFont( s, c );
196  QFontMetricsF fontMetrics( sFont );
197  p->setFont( sFont );
198 
199  QgsExpressionContext expressionContext = c.expressionContext();
200  expressionContext.setFeature( feature );
201  if ( feature.fields() )
202  expressionContext.setFields( *feature.fields() );
203 
204  for ( int i = 0; i < textPositions.size(); ++i )
205  {
206  QgsExpression* expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
207  QString val = expression->evaluate( &expressionContext ).toString();
208 
209  //find out dimesions
210  double textWidth = fontMetrics.width( val );
211  double textHeight = fontMetrics.height();
212 
213  mPen.setColor( s.categoryColors.at( i ) );
214  p->setPen( mPen );
215  QPointF position = textPositions.at( i );
216 
217  // Calculate vertical placement
218  double xOffset = 0;
219 
220  switch ( s.labelPlacementMethod )
221  {
223  xOffset = textHeight / 2.0;
224  break;
225 
227  xOffset = fontMetrics.xHeight();
228  break;
229  }
230  p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
231  }
232 }
233 
234 void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd, QPointF ellipseMid, double r1, double r2, QList<QPointF>& result ) const
235 {
236  result.clear();
237 
238  double rrx = r1 * r1;
239  double rry = r2 * r2;
240  double x21 = lineEnd.x() - lineStart.x();
241  double y21 = lineEnd.y() - lineStart.y();
242  double x10 = lineStart.x() - ellipseMid.x();
243  double y10 = lineStart.y() - ellipseMid.y();
244  double a = x21 * x21 / rrx + y21 * y21 / rry;
245  double b = x21 * x10 / rrx + y21 * y10 / rry;
246  double c = x10 * x10 / rrx + y10 * y10 / rry;
247  double d = b * b - a * ( c - 1 );
248  if ( d > 0 )
249  {
250  double e = sqrt( d );
251  double u1 = ( -b - e ) / a;
252  double u2 = ( -b + e ) / a;
253  //work with a tolerance of 0.00001 because of limited numerical precision
254  if ( -0.00001 <= u1 && u1 < 1.00001 )
255  {
256  result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
257  }
258  if ( -0.00001 <= u2 && u2 <= 1.00001 )
259  {
260  result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
261  }
262  }
263 }
QSizeF sizePainterUnits(QSizeF size, const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:77
Class for parsing and evaluation of expressions (formerly called "search strings").
void clear()
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
void push_back(const T &value)
QList< QString > categoryAttributes
Q_DECL_DEPRECATED QgsExpression * getExpression(const QString &expression, const QgsFields *fields)
Definition: qgsdiagram.cpp:47
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
const T & at(int i) const
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
void drawLine(const QLineF &line)
void setStyle(Qt::BrushStyle style)
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position) override
Draws the diagram at the given position (in pixel coordinates)
qreal width(const QString &text) const
int size() const
void setCapStyle(Qt::PenCapStyle style)
void drawRect(const QRectF &rectangle)
void setFont(const QFont &font)
qreal x() const
qreal y() const
qreal xHeight() const
const QgsFields * fields() const
Returns the field map associated with the feature.
Definition: qgsfeature.cpp:188
IntersectType intersect(const QLineF &line, QPointF *intersectionPoint) const
void setPen(const QColor &color)
void drawEllipse(const QRectF &rectangle)
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setWidthF(qreal width)
void setBrush(const QBrush &brush)
void drawText(const QPointF &position, const QString &text)
virtual QgsTextDiagram * clone() const override
Returns an instance that is equivalent to this one.
void setColor(const QColor &color)
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
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:103
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
const T & at(int i) const
Additional diagram settings for interpolated size rendering.
Contains information about the context of a rendering operation.
QFont scaledFont(const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:87
QPainter * painter()
QSizeF diagramSize(const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s) override
Returns the size in map units the diagram will use to render.
void push_back(const T &value)
double toDouble(bool *ok) const
int classificationAttribute
Index of the classification attribute.
qreal height() const
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:71
A vector of attributes.
Definition: qgsfeature.h:115
int size() const
QList< QColor > categoryColors
void setColor(const QColor &color)
QString toString() const
qreal width() const
Stores the settings for rendering a single diagram.
LabelPlacementMethod labelPlacementMethod
qreal height() const