QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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  Q_UNUSED( c );
42 
43  QVariant attrVal;
45  {
47  attrVal = expression->evaluate( feature );
48  }
49  else
50  {
51  attrVal = feature.attributes()[is.classificationAttribute];
52  }
53 
54  if ( !attrVal.isValid() )
55  {
56  return QSizeF(); //zero size if attribute is missing
57  }
58 
59  double scaledValue = attrVal.toDouble();
60  double scaledLowerValue = is.lowerValue;
61  double scaledUpperValue = is.upperValue;
62  double scaledLowerSizeWidth = is.lowerSize.width();
63  double scaledLowerSizeHeight = is.lowerSize.height();
64  double scaledUpperSizeWidth = is.upperSize.width();
65  double scaledUpperSizeHeight = is.upperSize.height();
66 
67  // interpolate the squared value if scale by area
68  if ( s.scaleByArea )
69  {
70  scaledValue = sqrt( scaledValue );
71  scaledLowerValue = sqrt( scaledLowerValue );
72  scaledUpperValue = sqrt( scaledUpperValue );
73  scaledLowerSizeWidth = sqrt( scaledLowerSizeWidth );
74  scaledLowerSizeHeight = sqrt( scaledLowerSizeHeight );
75  scaledUpperSizeWidth = sqrt( scaledUpperSizeWidth );
76  scaledUpperSizeHeight = sqrt( scaledUpperSizeHeight );
77  }
78 
79  //interpolate size
80  double scaledRatio = ( scaledValue - scaledLowerValue ) / ( scaledUpperValue - scaledLowerValue );
81 
82  QSizeF size = QSizeF( is.upperSize.width() * scaledRatio + is.lowerSize.width() * ( 1 - scaledRatio ),
83  is.upperSize.height() * scaledRatio + is.lowerSize.height() * ( 1 - scaledRatio ) );
84 
85  // Scale, if extension is smaller than the specified minimum
86  if ( size.width() <= s.minimumSize && size.height() <= s.minimumSize )
87  {
88  size.scale( s.minimumSize, s.minimumSize, Qt::KeepAspectRatio );
89  }
90 
91  return size;
92 }
93 
94 QSizeF QgsTextDiagram::diagramSize( const QgsAttributes& attributes, const QgsRenderContext& c, const QgsDiagramSettings& s )
95 {
96  Q_UNUSED( c );
97  Q_UNUSED( attributes );
98 
99  return s.size;
100 }
101 
102 void QgsTextDiagram::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QgsDiagramSettings& s, const QPointF& position )
103 {
104  Q_UNUSED( feature );
105 
106  QPainter* p = c.painter();
107  if ( !p )
108  {
109  return;
110  }
111 
112  double scaleDenominator = c.rendererScale();
113  if (( s.minScaleDenominator != -1 && scaleDenominator < s.minScaleDenominator )
114  || ( s.maxScaleDenominator != -1 && scaleDenominator > s.maxScaleDenominator ) )
115  {
116  return;
117  }
118 
119  //convert from mm / map units to painter units
120  QSizeF spu = sizePainterUnits( s.size, s, c );
121  double w = spu.width();
122  double h = spu.height();
123 
124  double baseX = position.x();
125  double baseY = position.y() - h;
126 
127  QList<QPointF> textPositions; //midpoints for text placement
128  int nCategories = s.categoryAttributes.size();
129  for ( int i = 0; i < nCategories; ++i )
130  {
131  if ( mOrientation == Horizontal )
132  {
133  textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
134  }
135  else //vertical
136  {
137  textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
138  }
139  }
140 
141  mPen.setColor( s.penColor );
142  setPenWidth( mPen, s, c );
143  p->setPen( mPen );
144  mBrush.setColor( s.backgroundColor );
145  p->setBrush( mBrush );
146 
147  //draw shapes and separator lines first
148  if ( mShape == Circle )
149  {
150  p->drawEllipse( baseX, baseY, w, h );
151 
152  //draw separator lines
153  QList<QPointF> intersect; //intersections between shape and separation lines
154  QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
155  double r1 = w / 2.0; double r2 = h / 2.0;
156 
157  for ( int i = 1; i < nCategories; ++i )
158  {
159  if ( mOrientation == Horizontal )
160  {
161  lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
162  }
163  else //vertical
164  {
165  lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
166  }
167  if ( intersect.size() > 1 )
168  {
169  p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
170  }
171  }
172  }
173  else if ( mShape == Rectangle )
174  {
175  p->drawRect( QRectF( baseX, baseY, w, h ) );
176  for ( int i = 1; i < nCategories; ++i )
177  {
178  if ( mOrientation == Horizontal )
179  {
180  p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
181  }
182  else
183  {
184  p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
185  }
186  }
187  }
188  else //triangle
189  {
190  QPolygonF triangle;
191  triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
192  p->drawPolygon( triangle );
193 
194  QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
195  QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
196  QPointF intersectionPoint1, intersectionPoint2;
197 
198  for ( int i = 1; i < nCategories; ++i )
199  {
200  if ( mOrientation == Horizontal )
201  {
202  QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
203  if ( baseX + w / nCategories * i < baseX + w / 2.0 )
204  {
205  verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
206  }
207  else
208  {
209  verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
210  }
211  p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
212  }
213  else //vertical
214  {
215  QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
216  horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
217  horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
218  p->drawLine( intersectionPoint1, intersectionPoint2 );
219  }
220  }
221  }
222 
223  //draw text
224  QFont sFont = scaledFont( s, c );
225  QFontMetricsF fontMetrics( sFont );
226  p->setFont( sFont );
227 
228  for ( int i = 0; i < textPositions.size(); ++i )
229  {
230  QgsExpression* expression = getExpression( s.categoryAttributes.at( i ), feature.fields() );
231  QString val = expression->evaluate( feature ).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( const QPointF& lineStart, const QPointF& lineEnd, const 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 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
double minimumSize
Scale diagrams smaller than mMinimumSize to mMinimumSize.
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
QList< QString > categoryAttributes
QgsExpression * getExpression(const QString &expression, const QgsFields *fields)
Definition: qgsdiagram.cpp:47
double rendererScale() const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
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:71
const QgsFields * fields() const
Get associated field map.
Definition: qgsfeature.h:231
const QgsAttributes & attributes() const
Definition: qgsfeature.h:142
QSizeF diagramSize(const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s)
Returns the size in map units the diagram will use to render.
Base class for all diagram types.
Definition: qgsdiagram.h:34
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:95
QPainter * painter()
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:100
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, const QPointF &position)
Draws the diagram at the given position (in pixel coordinates)
int classificationAttribute
Index of the classification attribute.
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:58
double size
Definition: qgssvgcache.cpp:77
virtual QgsDiagram * clone() const
Returns an instance that is equivalent to this one.
QList< QColor > categoryColors
LabelPlacementMethod labelPlacementMethod