Quantum GIS API Documentation  1.8
src/core/qgsdiagram.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines