|
Quantum GIS API Documentation
master-ce49b66
|
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 }