Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgsdiagram.cpp 00003 --------------------- 00004 begin : March 2011 00005 copyright : (C) 2011 by Marco Hugentobler 00006 email : marco dot hugentobler at sourcepole dot ch 00007 *************************************************************************** 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 ***************************************************************************/ 00015 #include "qgsdiagram.h" 00016 #include "qgsdiagramrendererv2.h" 00017 #include "qgsrendercontext.h" 00018 00019 #include <QPainter> 00020 00021 void QgsDiagram::setPenWidth( QPen& pen, const QgsDiagramSettings& s, const QgsRenderContext& c ) 00022 { 00023 if ( s.sizeType == QgsDiagramSettings::MM ) 00024 { 00025 pen.setWidthF( s.penWidth * c.scaleFactor() ); 00026 } 00027 else 00028 { 00029 pen.setWidthF( s.penWidth / c.mapToPixel().mapUnitsPerPixel() ); 00030 } 00031 } 00032 00033 QSizeF QgsDiagram::sizePainterUnits( const QSizeF& size, const QgsDiagramSettings& s, const QgsRenderContext& c ) 00034 { 00035 Q_UNUSED( size ); 00036 if ( s.sizeType == QgsDiagramSettings::MM ) 00037 { 00038 return QSizeF( s.size.width() * c.scaleFactor(), s.size.height() * c.scaleFactor() ); 00039 } 00040 else 00041 { 00042 return QSizeF( s.size.width() / c.mapToPixel().mapUnitsPerPixel(), s.size.height() / c.mapToPixel().mapUnitsPerPixel() ); 00043 } 00044 } 00045 00046 QFont QgsDiagram::scaledFont( const QgsDiagramSettings& s, const QgsRenderContext& c ) 00047 { 00048 QFont f = s.font; 00049 if ( s.sizeType == QgsDiagramSettings::MM ) 00050 { 00051 f.setPixelSize( s.font.pointSizeF() * 0.376 * c.scaleFactor() ); 00052 } 00053 else 00054 { 00055 f.setPixelSize( s.font.pointSizeF() / c.mapToPixel().mapUnitsPerPixel() ); 00056 } 00057 00058 return f; 00059 } 00060 00061 QgsTextDiagram::QgsTextDiagram(): mOrientation( Vertical ), mShape( Circle ) 00062 { 00063 mPen.setWidthF( 2.0 ); 00064 mPen.setColor( QColor( 0, 0, 0 ) ); 00065 mPen.setCapStyle( Qt::FlatCap ); 00066 mBrush.setStyle( Qt::SolidPattern ); 00067 } 00068 00069 QgsTextDiagram::~QgsTextDiagram() 00070 { 00071 } 00072 00073 void QgsTextDiagram::renderDiagram( const QgsAttributeMap& att, QgsRenderContext& c, const QgsDiagramSettings& s, const QPointF& position ) 00074 { 00075 QPainter* p = c.painter(); 00076 if ( !p ) 00077 { 00078 return; 00079 } 00080 00081 double scaleDenominator = c.rendererScale(); 00082 if (( s.minScaleDenominator != -1 && scaleDenominator < s.minScaleDenominator ) 00083 || ( s.maxScaleDenominator != -1 && scaleDenominator > s.maxScaleDenominator ) ) 00084 { 00085 return; 00086 } 00087 00088 //convert from mm / map units to painter units 00089 QSizeF spu = sizePainterUnits( s.size, s, c ); 00090 double w = spu.width(); 00091 double h = spu.height(); 00092 00093 double baseX = position.x(); 00094 double baseY = position.y() - h; 00095 00096 QList<QPointF> textPositions; //midpoints for text placement 00097 int nCategories = s.categoryIndices.size(); 00098 for ( int i = 0; i < nCategories; ++i ) 00099 { 00100 if ( mOrientation == Horizontal ) 00101 { 00102 textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0 , baseY + h / 2.0 ) ); 00103 } 00104 else //vertical 00105 { 00106 textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) ); 00107 } 00108 } 00109 00110 mPen.setColor( s.penColor ); 00111 setPenWidth( mPen, s, c ); 00112 p->setPen( mPen ); 00113 mBrush.setColor( s.backgroundColor ); 00114 p->setBrush( mBrush ); 00115 00116 //draw shapes and separator lines first 00117 if ( mShape == Circle ) 00118 { 00119 p->drawEllipse( baseX, baseY, w, h ); 00120 00121 //draw separator lines 00122 QList<QPointF> intersect; //intersections between shape and separation lines 00123 QPointF center( baseX + w / 2.0, baseY + h / 2.0 ); 00124 double r1 = w / 2.0; double r2 = h / 2.0; 00125 00126 for ( int i = 1; i < nCategories; ++i ) 00127 { 00128 if ( mOrientation == Horizontal ) 00129 { 00130 lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect ); 00131 } 00132 else //vertical 00133 { 00134 lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect ); 00135 } 00136 if ( intersect.size() > 1 ) 00137 { 00138 p->drawLine( intersect.at( 0 ), intersect.at( 1 ) ); 00139 } 00140 } 00141 } 00142 else if ( mShape == Rectangle ) 00143 { 00144 p->drawRect( QRectF( baseX, baseY, w, h ) ); 00145 for ( int i = 1; i < nCategories; ++i ) 00146 { 00147 if ( mOrientation == Horizontal ) 00148 { 00149 p->drawLine( QPointF( baseX + w / nCategories * i , baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) ); 00150 } 00151 else 00152 { 00153 p->drawLine( QPointF( baseX, baseY + h / nCategories * i ) , QPointF( baseX + w, baseY + h / nCategories * i ) ); 00154 } 00155 } 00156 } 00157 else //triangle 00158 { 00159 QPolygonF triangle; 00160 triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY ); 00161 p->drawPolygon( triangle ); 00162 00163 QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h ); 00164 QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY ); 00165 QPointF intersectionPoint1, intersectionPoint2; 00166 00167 for ( int i = 1; i < nCategories; ++i ) 00168 { 00169 if ( mOrientation == Horizontal ) 00170 { 00171 QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY ); 00172 if ( baseX + w / nCategories * i < baseX + w / 2.0 ) 00173 { 00174 verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 ); 00175 } 00176 else 00177 { 00178 verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 ); 00179 } 00180 p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 ); 00181 } 00182 else //vertical 00183 { 00184 QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i ); 00185 horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 ); 00186 horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 ); 00187 p->drawLine( intersectionPoint1, intersectionPoint2 ); 00188 } 00189 } 00190 } 00191 00192 //draw text 00193 QFont sFont = scaledFont( s, c ); 00194 QFontMetricsF fontMetrics( sFont ); 00195 p->setFont( sFont ); 00196 00197 for ( int i = 0; i < textPositions.size(); ++i ) 00198 { 00199 QString val = att[ s.categoryIndices.at( i )].toString(); 00200 //find out dimensions 00201 double textHeight = fontMetrics.height(); 00202 double textWidth = fontMetrics.width( val ); 00203 mPen.setColor( s.categoryColors.at( i ) ); 00204 p->setPen( mPen ); 00205 QPointF position = textPositions.at( i ); 00206 p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + textHeight / 2.0 ), val ); 00207 } 00208 } 00209 00210 void QgsTextDiagram::lineEllipseIntersection( const QPointF& lineStart, const QPointF& lineEnd, const QPointF& ellipseMid, double r1, double r2, QList<QPointF>& result ) const 00211 { 00212 result.clear(); 00213 00214 double rrx = r1 * r1; 00215 double rry = r2 * r2; 00216 double x21 = lineEnd.x() - lineStart.x(); 00217 double y21 = lineEnd.y() - lineStart.y(); 00218 double x10 = lineStart.x() - ellipseMid.x(); 00219 double y10 = lineStart.y() - ellipseMid.y(); 00220 double a = x21 * x21 / rrx + y21 * y21 / rry; 00221 double b = x21 * x10 / rrx + y21 * y10 / rry; 00222 double c = x10 * x10 / rrx + y10 * y10 / rry; 00223 double d = b * b - a * ( c - 1 ); 00224 if ( d > 0 ) 00225 { 00226 double e = sqrt( d ); 00227 double u1 = ( -b - e ) / a; 00228 double u2 = ( -b + e ) / a; 00229 //work with a tolerance of 0.00001 because of limited numerical precision 00230 if ( -0.00001 <= u1 && u1 < 1.00001 ) 00231 { 00232 result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) ); 00233 } 00234 if ( -0.00001 <= u2 && u2 <= 1.00001 ) 00235 { 00236 result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) ); 00237 } 00238 } 00239 } 00240 00241 QgsPieDiagram::QgsPieDiagram() 00242 { 00243 mCategoryBrush.setStyle( Qt::SolidPattern ); 00244 mPen.setStyle( Qt::SolidLine ); 00245 } 00246 00247 QgsPieDiagram::~QgsPieDiagram() 00248 { 00249 } 00250 00251 void QgsPieDiagram::renderDiagram( const QgsAttributeMap& att, QgsRenderContext& c, const QgsDiagramSettings& s, const QPointF& position ) 00252 { 00253 QPainter* p = c.painter(); 00254 if ( !p ) 00255 { 00256 return; 00257 } 00258 00259 //get sum of values 00260 QList<double> values; 00261 double currentVal = 0; 00262 double valSum = 0; 00263 00264 QList<int>::const_iterator catIt = s.categoryIndices.constBegin(); 00265 for ( ; catIt != s.categoryIndices.constEnd(); ++catIt ) 00266 { 00267 currentVal = att[*catIt].toDouble(); 00268 values.push_back( currentVal ); 00269 valSum += currentVal; 00270 } 00271 00272 //draw the slices 00273 double totalAngle = 0; 00274 double currentAngle; 00275 00276 //convert from mm / map units to painter units 00277 QSizeF spu = sizePainterUnits( s.size, s, c ); 00278 double w = spu.width(); 00279 double h = spu.height(); 00280 00281 double baseX = position.x(); 00282 double baseY = position.y() - h; 00283 00284 mPen.setColor( s.penColor ); 00285 setPenWidth( mPen, s, c ); 00286 p->setPen( mPen ); 00287 00288 QList<double>::const_iterator valIt = values.constBegin(); 00289 QList< QColor >::const_iterator colIt = s.categoryColors.constBegin(); 00290 for ( ; valIt != values.constEnd(); ++valIt, ++colIt ) 00291 { 00292 currentAngle = *valIt / valSum * 360 * 16; 00293 mCategoryBrush.setColor( *colIt ); 00294 p->setBrush( mCategoryBrush ); 00295 p->drawPie( baseX, baseY, w, h, totalAngle, currentAngle ); 00296 totalAngle += currentAngle; 00297 } 00298 }