Branch data Line data Source code
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 : :
22 : 0 : QgsTextDiagram::QgsTextDiagram()
23 : 0 : {
24 : 0 : mPen.setWidthF( 2.0 );
25 : 0 : mPen.setColor( QColor( 0, 0, 0 ) );
26 : 0 : mPen.setCapStyle( Qt::FlatCap );
27 : 0 : mBrush.setStyle( Qt::SolidPattern );
28 : 0 : }
29 : :
30 : 0 : QgsTextDiagram *QgsTextDiagram::clone() const
31 : : {
32 : 0 : return new QgsTextDiagram( *this );
33 : 0 : }
34 : :
35 : 0 : QSizeF QgsTextDiagram::diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is )
36 : : {
37 : 0 : QgsExpressionContext expressionContext = c.expressionContext();
38 : 0 : expressionContext.setFeature( feature );
39 : 0 : if ( !feature.fields().isEmpty() )
40 : 0 : expressionContext.setFields( feature.fields() );
41 : :
42 : 0 : QVariant attrVal;
43 : 0 : if ( is.classificationAttributeIsExpression )
44 : : {
45 : 0 : QgsExpression *expression = getExpression( is.classificationAttributeExpression, expressionContext );
46 : 0 : attrVal = expression->evaluate( &expressionContext );
47 : 0 : }
48 : : else
49 : : {
50 : 0 : attrVal = feature.attribute( is.classificationField );
51 : : }
52 : :
53 : 0 : bool ok = false;
54 : 0 : double val = attrVal.toDouble( &ok );
55 : 0 : if ( !ok )
56 : : {
57 : 0 : return QSizeF(); //zero size if attribute is missing
58 : : }
59 : :
60 : 0 : return sizeForValue( val, s, is );
61 : 0 : }
62 : :
63 : 0 : double QgsTextDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const
64 : : {
65 : 0 : QSizeF size = sizeForValue( value, s, is );
66 : 0 : return std::max( size.width(), size.height() );
67 : 0 : }
68 : :
69 : 0 : QString QgsTextDiagram::diagramName() const
70 : : {
71 : 0 : return DIAGRAM_NAME_TEXT;
72 : : }
73 : :
74 : 0 : QSizeF QgsTextDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s )
75 : : {
76 : 0 : Q_UNUSED( c )
77 : 0 : Q_UNUSED( attributes )
78 : :
79 : 0 : return s.size;
80 : : }
81 : :
82 : 0 : void QgsTextDiagram::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position )
83 : : {
84 : 0 : QPainter *p = c.painter();
85 : 0 : if ( !p )
86 : : {
87 : 0 : return;
88 : : }
89 : :
90 : : //convert from mm / map units to painter units
91 : 0 : QSizeF spu = sizePainterUnits( s.size, s, c );
92 : 0 : double w = spu.width();
93 : 0 : double h = spu.height();
94 : :
95 : 0 : double baseX = position.x();
96 : 0 : double baseY = position.y() - h;
97 : :
98 : 0 : QVector<QPointF> textPositions; //midpoints for text placement
99 : 0 : int nCategories = s.categoryAttributes.size();
100 : 0 : for ( int i = 0; i < nCategories; ++i )
101 : : {
102 : 0 : if ( mOrientation == Horizontal )
103 : : {
104 : 0 : textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
105 : 0 : }
106 : : else //vertical
107 : : {
108 : 0 : textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
109 : : }
110 : 0 : }
111 : :
112 : 0 : mPen.setColor( s.penColor );
113 : 0 : setPenWidth( mPen, s, c );
114 : 0 : p->setPen( mPen );
115 : 0 : mBrush.setColor( s.backgroundColor );
116 : 0 : p->setBrush( mBrush );
117 : :
118 : : //draw shapes and separator lines first
119 : 0 : if ( mShape == Circle )
120 : : {
121 : 0 : p->drawEllipse( baseX, baseY, w, h );
122 : :
123 : : //draw separator lines
124 : 0 : QList<QPointF> intersect; //intersections between shape and separation lines
125 : 0 : QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
126 : 0 : double r1 = w / 2.0;
127 : 0 : double r2 = h / 2.0;
128 : :
129 : 0 : for ( int i = 1; i < nCategories; ++i )
130 : : {
131 : 0 : if ( mOrientation == Horizontal )
132 : : {
133 : 0 : lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
134 : 0 : }
135 : : else //vertical
136 : : {
137 : 0 : lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
138 : : }
139 : 0 : if ( intersect.size() > 1 )
140 : : {
141 : 0 : p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
142 : 0 : }
143 : 0 : }
144 : 0 : }
145 : 0 : else if ( mShape == Rectangle )
146 : : {
147 : 0 : p->drawRect( QRectF( baseX, baseY, w, h ) );
148 : 0 : for ( int i = 1; i < nCategories; ++i )
149 : : {
150 : 0 : if ( mOrientation == Horizontal )
151 : : {
152 : 0 : p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
153 : 0 : }
154 : : else
155 : : {
156 : 0 : p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
157 : : }
158 : 0 : }
159 : 0 : }
160 : : else //triangle
161 : : {
162 : 0 : QPolygonF triangle;
163 : 0 : triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
164 : 0 : p->drawPolygon( triangle );
165 : :
166 : 0 : QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
167 : 0 : QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
168 : 0 : QPointF intersectionPoint1, intersectionPoint2;
169 : :
170 : 0 : for ( int i = 1; i < nCategories; ++i )
171 : : {
172 : 0 : if ( mOrientation == Horizontal )
173 : : {
174 : 0 : QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
175 : 0 : if ( baseX + w / nCategories * i < baseX + w / 2.0 )
176 : : {
177 : : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
178 : : verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
179 : : #else
180 : 0 : verticalLine.intersects( triangleEdgeLeft, &intersectionPoint1 );
181 : : #endif
182 : 0 : }
183 : : else
184 : : {
185 : : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
186 : : verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
187 : : #else
188 : 0 : verticalLine.intersects( triangleEdgeRight, &intersectionPoint1 );
189 : : #endif
190 : : }
191 : 0 : p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
192 : 0 : }
193 : : else //vertical
194 : : {
195 : 0 : QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
196 : : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
197 : : horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
198 : : horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
199 : : #else
200 : 0 : horizontalLine.intersects( triangleEdgeLeft, &intersectionPoint1 );
201 : 0 : horizontalLine.intersects( triangleEdgeRight, &intersectionPoint2 );
202 : : #endif
203 : 0 : p->drawLine( intersectionPoint1, intersectionPoint2 );
204 : : }
205 : 0 : }
206 : 0 : }
207 : :
208 : : //draw text
209 : 0 : QFont sFont = scaledFont( s, c );
210 : 0 : QFontMetricsF fontMetrics( sFont );
211 : 0 : p->setFont( sFont );
212 : :
213 : 0 : QgsExpressionContext expressionContext = c.expressionContext();
214 : 0 : expressionContext.setFeature( feature );
215 : 0 : if ( !feature.fields().isEmpty() )
216 : 0 : expressionContext.setFields( feature.fields() );
217 : :
218 : 0 : for ( int i = 0; i < textPositions.size(); ++i )
219 : : {
220 : 0 : QgsExpression *expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
221 : 0 : QString val = expression->evaluate( &expressionContext ).toString();
222 : :
223 : : //find out dimensions
224 : 0 : double textWidth = fontMetrics.horizontalAdvance( val );
225 : 0 : double textHeight = fontMetrics.height();
226 : :
227 : 0 : mPen.setColor( s.categoryColors.at( i ) );
228 : 0 : p->setPen( mPen );
229 : 0 : QPointF position = textPositions.at( i );
230 : :
231 : : // Calculate vertical placement
232 : 0 : double xOffset = 0;
233 : :
234 : 0 : switch ( s.labelPlacementMethod )
235 : : {
236 : : case QgsDiagramSettings::Height:
237 : 0 : xOffset = textHeight / 2.0;
238 : 0 : break;
239 : :
240 : : case QgsDiagramSettings::XHeight:
241 : 0 : xOffset = fontMetrics.xHeight();
242 : 0 : break;
243 : : }
244 : 0 : p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
245 : 0 : }
246 : 0 : }
247 : :
248 : 0 : void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd, QPointF ellipseMid, double r1, double r2, QList<QPointF> &result ) const
249 : : {
250 : 0 : result.clear();
251 : :
252 : 0 : double rrx = r1 * r1;
253 : 0 : double rry = r2 * r2;
254 : 0 : double x21 = lineEnd.x() - lineStart.x();
255 : 0 : double y21 = lineEnd.y() - lineStart.y();
256 : 0 : double x10 = lineStart.x() - ellipseMid.x();
257 : 0 : double y10 = lineStart.y() - ellipseMid.y();
258 : 0 : double a = x21 * x21 / rrx + y21 * y21 / rry;
259 : 0 : double b = x21 * x10 / rrx + y21 * y10 / rry;
260 : 0 : double c = x10 * x10 / rrx + y10 * y10 / rry;
261 : 0 : double d = b * b - a * ( c - 1 );
262 : 0 : if ( d > 0 )
263 : : {
264 : 0 : double e = std::sqrt( d );
265 : 0 : double u1 = ( -b - e ) / a;
266 : 0 : double u2 = ( -b + e ) / a;
267 : : //work with a tolerance of 0.00001 because of limited numerical precision
268 : 0 : if ( -0.00001 <= u1 && u1 < 1.00001 )
269 : : {
270 : 0 : result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
271 : 0 : }
272 : 0 : if ( -0.00001 <= u2 && u2 <= 1.00001 )
273 : : {
274 : 0 : result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
275 : 0 : }
276 : 0 : }
277 : 0 : }
|