QGIS API Documentation  master-59fd5e0
src/core/diagram/qgstextdiagram.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgstextdiagram.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 "qgstextdiagram.h"
00016 #include "qgsdiagramrendererv2.h"
00017 #include "qgsrendercontext.h"
00018 
00019 #include <QPainter>
00020 
00021 QgsTextDiagram::QgsTextDiagram(): mOrientation( Vertical ), mShape( Circle )
00022 {
00023   mPen.setWidthF( 2.0 );
00024   mPen.setColor( QColor( 0, 0, 0 ) );
00025   mPen.setCapStyle( Qt::FlatCap );
00026   mBrush.setStyle( Qt::SolidPattern );
00027 }
00028 
00029 QgsTextDiagram::~QgsTextDiagram()
00030 {
00031 }
00032 
00033 QSizeF QgsTextDiagram::diagramSize( const QgsAttributes& attributes, const QgsRenderContext& c, const QgsDiagramSettings& s, const QgsDiagramInterpolationSettings& is )
00034 {
00035   Q_UNUSED( c );
00036 
00037   QVariant attrVal = attributes[is.classificationAttribute];
00038 
00039   if ( !attrVal.isValid() )
00040   {
00041     return QSizeF(); //zero size if attribute is missing
00042   }
00043 
00044   double scaledValue = attrVal.toDouble();
00045   double scaledLowerValue = is.lowerValue;
00046   double scaledUpperValue = is.upperValue;
00047   double scaledLowerSizeWidth = is.lowerSize.width();
00048   double scaledLowerSizeHeight = is.lowerSize.height();
00049   double scaledUpperSizeWidth = is.upperSize.width();
00050   double scaledUpperSizeHeight = is.upperSize.height();
00051 
00052   // interpolate the squared value if scale by area
00053   if ( s.scaleByArea )
00054   {
00055     scaledValue = sqrt( scaledValue );
00056     scaledLowerValue = sqrt( scaledLowerValue );
00057     scaledUpperValue = sqrt( scaledUpperValue );
00058     scaledLowerSizeWidth = sqrt( scaledLowerSizeWidth );
00059     scaledLowerSizeHeight = sqrt( scaledLowerSizeHeight );
00060     scaledUpperSizeWidth = sqrt( scaledUpperSizeWidth );
00061     scaledUpperSizeHeight = sqrt( scaledUpperSizeHeight );
00062   }
00063 
00064   //interpolate size
00065   double scaledRatio = ( scaledValue - scaledLowerValue ) / ( scaledUpperValue - scaledLowerValue );
00066 
00067   QSizeF size = QSizeF( is.upperSize.width() * scaledRatio + is.lowerSize.width() * ( 1 - scaledRatio ),
00068                         is.upperSize.height() * scaledRatio + is.lowerSize.height() * ( 1 - scaledRatio ) );
00069 
00070   // Scale, if extension is smaller than the specified minimum
00071   if ( size.width() <= s.minimumSize && size.height() <= s.minimumSize )
00072   {
00073     size.scale( s.minimumSize, s.minimumSize, Qt::KeepAspectRatio );
00074   }
00075 
00076   return size;
00077 }
00078 
00079 QSizeF QgsTextDiagram::diagramSize( const QgsAttributes& attributes, const QgsRenderContext& c, const QgsDiagramSettings& s )
00080 {
00081   Q_UNUSED( c );
00082   Q_UNUSED( attributes );
00083 
00084   return s.size;
00085 }
00086 
00087 void QgsTextDiagram::renderDiagram( const QgsAttributes& att, QgsRenderContext& c, const QgsDiagramSettings& s, const QPointF& position )
00088 {
00089   QPainter* p = c.painter();
00090   if ( !p )
00091   {
00092     return;
00093   }
00094 
00095   double scaleDenominator = c.rendererScale();
00096   if (( s.minScaleDenominator != -1 && scaleDenominator < s.minScaleDenominator )
00097       || ( s.maxScaleDenominator != -1 && scaleDenominator > s.maxScaleDenominator ) )
00098   {
00099     return;
00100   }
00101 
00102   //convert from mm / map units to painter units
00103   QSizeF spu = sizePainterUnits( s.size, s, c );
00104   double w = spu.width();
00105   double h = spu.height();
00106 
00107   double baseX = position.x();
00108   double baseY = position.y() - h;
00109 
00110   QList<QPointF> textPositions; //midpoints for text placement
00111   int nCategories = s.categoryIndices.size();
00112   for ( int i = 0; i < nCategories; ++i )
00113   {
00114     if ( mOrientation == Horizontal )
00115     {
00116       textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0 , baseY + h / 2.0 ) );
00117     }
00118     else //vertical
00119     {
00120       textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
00121     }
00122   }
00123 
00124   mPen.setColor( s.penColor );
00125   setPenWidth( mPen, s, c );
00126   p->setPen( mPen );
00127   mBrush.setColor( s.backgroundColor );
00128   p->setBrush( mBrush );
00129 
00130   //draw shapes and separator lines first
00131   if ( mShape == Circle )
00132   {
00133     p->drawEllipse( baseX, baseY, w, h );
00134 
00135     //draw separator lines
00136     QList<QPointF> intersect; //intersections between shape and separation lines
00137     QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
00138     double r1 = w / 2.0; double r2 = h / 2.0;
00139 
00140     for ( int i = 1; i < nCategories; ++i )
00141     {
00142       if ( mOrientation == Horizontal )
00143       {
00144         lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
00145       }
00146       else //vertical
00147       {
00148         lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
00149       }
00150       if ( intersect.size() > 1 )
00151       {
00152         p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
00153       }
00154     }
00155   }
00156   else if ( mShape == Rectangle )
00157   {
00158     p->drawRect( QRectF( baseX, baseY, w, h ) );
00159     for ( int i = 1; i < nCategories; ++i )
00160     {
00161       if ( mOrientation == Horizontal )
00162       {
00163         p->drawLine( QPointF( baseX + w / nCategories * i , baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
00164       }
00165       else
00166       {
00167         p->drawLine( QPointF( baseX, baseY + h / nCategories * i ) , QPointF( baseX + w,  baseY + h / nCategories * i ) );
00168       }
00169     }
00170   }
00171   else //triangle
00172   {
00173     QPolygonF triangle;
00174     triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
00175     p->drawPolygon( triangle );
00176 
00177     QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
00178     QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
00179     QPointF intersectionPoint1, intersectionPoint2;
00180 
00181     for ( int i = 1; i < nCategories; ++i )
00182     {
00183       if ( mOrientation == Horizontal )
00184       {
00185         QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
00186         if ( baseX + w / nCategories * i < baseX + w / 2.0 )
00187         {
00188           verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
00189         }
00190         else
00191         {
00192           verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
00193         }
00194         p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
00195       }
00196       else //vertical
00197       {
00198         QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
00199         horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
00200         horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
00201         p->drawLine( intersectionPoint1, intersectionPoint2 );
00202       }
00203     }
00204   }
00205 
00206   //draw text
00207   QFont sFont = scaledFont( s, c );
00208   QFontMetricsF fontMetrics( sFont );
00209   p->setFont( sFont );
00210 
00211   for ( int i = 0; i < textPositions.size(); ++i )
00212   {
00213     QString val = att[ s.categoryIndices.at( i )].toString();
00214     //find out dimesions
00215     double textWidth = fontMetrics.width( val );
00216     double textHeight = fontMetrics.height();
00217 
00218     mPen.setColor( s.categoryColors.at( i ) );
00219     p->setPen( mPen );
00220     QPointF position = textPositions.at( i );
00221 
00222     // Calculate vertical placement
00223     double xOffset = 0;
00224 
00225     switch ( s.labelPlacementMethod )
00226     {
00227       case QgsDiagramSettings::Height:
00228         xOffset = textHeight / 2.0;
00229         break;
00230 
00231       case QgsDiagramSettings::XHeight:
00232         xOffset = fontMetrics.xHeight();
00233         break;
00234     }
00235     p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
00236   }
00237 }
00238 
00239 void QgsTextDiagram::lineEllipseIntersection( const QPointF& lineStart, const QPointF& lineEnd, const QPointF& ellipseMid, double r1, double r2, QList<QPointF>& result ) const
00240 {
00241   result.clear();
00242 
00243   double rrx = r1 * r1;
00244   double rry = r2 * r2;
00245   double x21 = lineEnd.x() - lineStart.x();
00246   double y21 = lineEnd.y() - lineStart.y();
00247   double x10 = lineStart.x() - ellipseMid.x();
00248   double y10 = lineStart.y() - ellipseMid.y();
00249   double a = x21 * x21 / rrx + y21 * y21 / rry;
00250   double b = x21 * x10 / rrx + y21 * y10 / rry;
00251   double c = x10 * x10 / rrx + y10 * y10 / rry;
00252   double d = b * b - a * ( c - 1 );
00253   if ( d > 0 )
00254   {
00255     double e = sqrt( d );
00256     double u1 = ( -b - e ) / a;
00257     double u2 = ( -b + e ) / a;
00258     //work with a tolerance of 0.00001 because of limited numerical precision
00259     if ( -0.00001 <= u1 && u1 < 1.00001 )
00260     {
00261       result.push_back( QPointF( lineStart.x() + x21 * u1, lineStart.y() + y21 * u1 ) );
00262     }
00263     if ( -0.00001 <= u2 && u2 <= 1.00001 )
00264     {
00265       result.push_back( QPointF( lineStart.x() + x21 * u2, lineStart.y() + y21 * u2 ) );
00266     }
00267   }
00268 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines