QGIS API Documentation  2.12.0-Lyon
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  if ( !attrVal.isValid() )
58  {
59  return QSizeF(); //zero size if attribute is missing
60  }
61 
62  double scaledValue = attrVal.toDouble();
63  double scaledLowerValue = is.lowerValue;
64  double scaledUpperValue = is.upperValue;
65  double scaledLowerSizeWidth = is.lowerSize.width();
66  double scaledLowerSizeHeight = is.lowerSize.height();
67  double scaledUpperSizeWidth = is.upperSize.width();
68  double scaledUpperSizeHeight = is.upperSize.height();
69 
70  // interpolate the squared value if scale by area
71  if ( s.scaleByArea )
72  {
73  scaledValue = sqrt( scaledValue );
74  scaledLowerValue = sqrt( scaledLowerValue );
75  scaledUpperValue = sqrt( scaledUpperValue );
76  scaledLowerSizeWidth = sqrt( scaledLowerSizeWidth );
77  scaledLowerSizeHeight = sqrt( scaledLowerSizeHeight );
78  scaledUpperSizeWidth = sqrt( scaledUpperSizeWidth );
79  scaledUpperSizeHeight = sqrt( scaledUpperSizeHeight );
80  }
81 
82  //interpolate size
83  double scaledRatio = ( scaledValue - scaledLowerValue ) / ( scaledUpperValue - scaledLowerValue );
84 
85  QSizeF size = QSizeF( is.upperSize.width() * scaledRatio + is.lowerSize.width() * ( 1 - scaledRatio ),
86  is.upperSize.height() * scaledRatio + is.lowerSize.height() * ( 1 - scaledRatio ) );
87 
88  // Scale, if extension is smaller than the specified minimum
89  if ( size.width() <= s.minimumSize && size.height() <= s.minimumSize )
90  {
91  size.scale( s.minimumSize, s.minimumSize, Qt::KeepAspectRatio );
92  }
93 
94  return size;
95 }
96 
98 {
99  Q_UNUSED( c );
100  Q_UNUSED( attributes );
101 
102  return s.size;
103 }
104 
105 void QgsTextDiagram::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QgsDiagramSettings& s, const QPointF& position )
106 {
107  QPainter* p = c.painter();
108  if ( !p )
109  {
110  return;
111  }
112 
113  //convert from mm / map units to painter units
114  QSizeF spu = sizePainterUnits( s.size, s, c );
115  double w = spu.width();
116  double h = spu.height();
117 
118  double baseX = position.x();
119  double baseY = position.y() - h;
120 
121  QList<QPointF> textPositions; //midpoints for text placement
122  int nCategories = s.categoryAttributes.size();
123  for ( int i = 0; i < nCategories; ++i )
124  {
125  if ( mOrientation == Horizontal )
126  {
127  textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
128  }
129  else //vertical
130  {
131  textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
132  }
133  }
134 
135  mPen.setColor( s.penColor );
136  setPenWidth( mPen, s, c );
137  p->setPen( mPen );
138  mBrush.setColor( s.backgroundColor );
139  p->setBrush( mBrush );
140 
141  //draw shapes and separator lines first
142  if ( mShape == Circle )
143  {
144  p->drawEllipse( baseX, baseY, w, h );
145 
146  //draw separator lines
147  QList<QPointF> intersect; //intersections between shape and separation lines
148  QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
149  double r1 = w / 2.0; double r2 = h / 2.0;
150 
151  for ( int i = 1; i < nCategories; ++i )
152  {
153  if ( mOrientation == Horizontal )
154  {
155  lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
156  }
157  else //vertical
158  {
159  lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
160  }
161  if ( intersect.size() > 1 )
162  {
163  p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
164  }
165  }
166  }
167  else if ( mShape == Rectangle )
168  {
169  p->drawRect( QRectF( baseX, baseY, w, h ) );
170  for ( int i = 1; i < nCategories; ++i )
171  {
172  if ( mOrientation == Horizontal )
173  {
174  p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
175  }
176  else
177  {
178  p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
179  }
180  }
181  }
182  else //triangle
183  {
184  QPolygonF triangle;
185  triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
186  p->drawPolygon( triangle );
187 
188  QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
189  QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
190  QPointF intersectionPoint1, intersectionPoint2;
191 
192  for ( int i = 1; i < nCategories; ++i )
193  {
194  if ( mOrientation == Horizontal )
195  {
196  QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
197  if ( baseX + w / nCategories * i < baseX + w / 2.0 )
198  {
199  verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
200  }
201  else
202  {
203  verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
204  }
205  p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
206  }
207  else //vertical
208  {
209  QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
210  horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
211  horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
212  p->drawLine( intersectionPoint1, intersectionPoint2 );
213  }
214  }
215  }
216 
217  //draw text
218  QFont sFont = scaledFont( s, c );
219  QFontMetricsF fontMetrics( sFont );
220  p->setFont( sFont );
221 
222  QgsExpressionContext expressionContext = c.expressionContext();
223  expressionContext.setFeature( feature );
224  if ( feature.fields() )
225  expressionContext.setFields( *feature.fields() );
226 
227  for ( int i = 0; i < textPositions.size(); ++i )
228  {
229  QgsExpression* expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
230  QString val = expression->evaluate( &expressionContext ).toString();
231 
232  //find out dimesions
233  double textWidth = fontMetrics.width( val );
234  double textHeight = fontMetrics.height();
235 
236  mPen.setColor( s.categoryColors.at( i ) );
237  p->setPen( mPen );
238  QPointF position = textPositions.at( i );
239 
240  // Calculate vertical placement
241  double xOffset = 0;
242 
243  switch ( s.labelPlacementMethod )
244  {
246  xOffset = textHeight / 2.0;
247  break;
248 
250  xOffset = fontMetrics.xHeight();
251  break;
252  }
253  p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
254  }
255 }
256 
257 void QgsTextDiagram::lineEllipseIntersection( const QPointF& lineStart, const QPointF& lineEnd, const QPointF& ellipseMid, double r1, double r2, QList<QPointF>& result ) const
258 {
259  result.clear();
260 
261  double rrx = r1 * r1;
262  double rry = r2 * r2;
263  double x21 = lineEnd.x() - lineStart.x();
264  double y21 = lineEnd.y() - lineStart.y();
265  double x10 = lineStart.x() - ellipseMid.x();
266  double y10 = lineStart.y() - ellipseMid.y();
267  double a = x21 * x21 / rrx + y21 * y21 / rry;
268  double b = x21 * x10 / rrx + y21 * y10 / rry;
269  double c = x10 * x10 / rrx + y10 * y10 / rry;
270  double d = b * b - a * ( c - 1 );
271  if ( d > 0 )
272  {
273  double e = sqrt( d );
274  double u1 = ( -b - e ) / a;
275  double u2 = ( -b + e ) / a;
276  //work with a tolerance of 0.00001 because of limited numerical precision
277  if ( -0.00001 <= u1 && u1 < 1.00001 )
278  {
279  result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
280  }
281  if ( -0.00001 <= u2 && u2 <= 1.00001 )
282  {
283  result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
284  }
285  }
286 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
void clear()
double minimumSize
Scale diagrams smaller than mMinimumSize to mMinimumSize.
void scale(qreal width, qreal height, Qt::AspectRatioMode mode)
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:176
void drawLine(const QLineF &line)
virtual QgsDiagram * clone() const override
Returns an instance that is equivalent to this one.
void setStyle(Qt::BrushStyle style)
qreal width(const QString &text) const
QSizeF sizePainterUnits(const QSizeF &size, const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:84
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
IntersectType intersect(const QLineF &line, QPointF *intersectionPoint) const
QgsAttributes attributes() const
Returns the feature's attributes.
Definition: qgsfeature.cpp:92
void setPen(const QColor &color)
void drawEllipse(const QRectF &rectangle)
const QgsFields * fields() const
Returns the field map associated with the feature.
Definition: qgsfeature.cpp:164
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)
void setColor(const QColor &color)
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Base class for all diagram types.
Definition: qgsdiagram.h:35
QgsExpressionContext & expressionContext()
Gets the expression context.
const T & at(int i) const
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:108
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.
bool isValid() const
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:109
QList< QColor > categoryColors
void setColor(const QColor &color)
QString toString() const
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, const QPointF &position) override
Draws the diagram at the given position (in pixel coordinates)
qreal width() const
LabelPlacementMethod labelPlacementMethod
qreal height() const