QGIS API Documentation  2.14.0-Essen
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 
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  QVector<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;
150  double r2 = h / 2.0;
151 
152  for ( int i = 1; i < nCategories; ++i )
153  {
154  if ( mOrientation == Horizontal )
155  {
156  lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
157  }
158  else //vertical
159  {
160  lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
161  }
162  if ( intersect.size() > 1 )
163  {
164  p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
165  }
166  }
167  }
168  else if ( mShape == Rectangle )
169  {
170  p->drawRect( QRectF( baseX, baseY, w, h ) );
171  for ( int i = 1; i < nCategories; ++i )
172  {
173  if ( mOrientation == Horizontal )
174  {
175  p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
176  }
177  else
178  {
179  p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
180  }
181  }
182  }
183  else //triangle
184  {
185  QPolygonF triangle;
186  triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
187  p->drawPolygon( triangle );
188 
189  QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
190  QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
191  QPointF intersectionPoint1, intersectionPoint2;
192 
193  for ( int i = 1; i < nCategories; ++i )
194  {
195  if ( mOrientation == Horizontal )
196  {
197  QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
198  if ( baseX + w / nCategories * i < baseX + w / 2.0 )
199  {
200  verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
201  }
202  else
203  {
204  verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
205  }
206  p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
207  }
208  else //vertical
209  {
210  QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
211  horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
212  horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
213  p->drawLine( intersectionPoint1, intersectionPoint2 );
214  }
215  }
216  }
217 
218  //draw text
219  QFont sFont = scaledFont( s, c );
220  QFontMetricsF fontMetrics( sFont );
221  p->setFont( sFont );
222 
223  QgsExpressionContext expressionContext = c.expressionContext();
224  expressionContext.setFeature( feature );
225  if ( feature.fields() )
226  expressionContext.setFields( *feature.fields() );
227 
228  for ( int i = 0; i < textPositions.size(); ++i )
229  {
230  QgsExpression* expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
231  QString val = expression->evaluate( &expressionContext ).toString();
232 
233  //find out dimesions
234  double textWidth = fontMetrics.width( val );
235  double textHeight = fontMetrics.height();
236 
237  mPen.setColor( s.categoryColors.at( i ) );
238  p->setPen( mPen );
239  QPointF position = textPositions.at( i );
240 
241  // Calculate vertical placement
242  double xOffset = 0;
243 
244  switch ( s.labelPlacementMethod )
245  {
247  xOffset = textHeight / 2.0;
248  break;
249 
251  xOffset = fontMetrics.xHeight();
252  break;
253  }
254  p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
255  }
256 }
257 
258 void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd, QPointF ellipseMid, double r1, double r2, QList<QPointF>& result ) const
259 {
260  result.clear();
261 
262  double rrx = r1 * r1;
263  double rry = r2 * r2;
264  double x21 = lineEnd.x() - lineStart.x();
265  double y21 = lineEnd.y() - lineStart.y();
266  double x10 = lineStart.x() - ellipseMid.x();
267  double y10 = lineStart.y() - ellipseMid.y();
268  double a = x21 * x21 / rrx + y21 * y21 / rry;
269  double b = x21 * x10 / rrx + y21 * y10 / rry;
270  double c = x10 * x10 / rrx + y10 * y10 / rry;
271  double d = b * b - a * ( c - 1 );
272  if ( d > 0 )
273  {
274  double e = sqrt( d );
275  double u1 = ( -b - e ) / a;
276  double u2 = ( -b + e ) / a;
277  //work with a tolerance of 0.00001 because of limited numerical precision
278  if ( -0.00001 <= u1 && u1 < 1.00001 )
279  {
280  result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
281  }
282  if ( -0.00001 <= u2 && u2 <= 1.00001 )
283  {
284  result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
285  }
286  }
287 }
QSizeF sizePainterUnits(QSizeF size, const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:84
Class for parsing and evaluation of expressions (formerly called "search strings").
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: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
IntersectType intersect(const QLineF &line, QPointF *intersectionPoint) const
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
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:188
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.
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.
void push_back(const T &value)
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:115
int size() const
QList< QColor > categoryColors
void setColor(const QColor &color)
QString toString() const
qreal width() const
LabelPlacementMethod labelPlacementMethod
qreal height() const