Quantum GIS API Documentation  1.8
src/core/renderer/qgsgraduatedsymbolrenderer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                          qgssinglesymbolrenderer.cpp  -  description
00003                              -------------------
00004     begin                : Oct 2003
00005     copyright            : (C) 2003 by Marco Hugentobler
00006     email                : mhugent@geo.unizh.ch
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgis.h"
00019 #include "qgslogger.h"
00020 #include "qgsfeature.h"
00021 #include "qgsgraduatedsymbolrenderer.h"
00022 #include "qgssymbol.h"
00023 #include "qgssymbologyutils.h"
00024 #include "qgsvectordataprovider.h"
00025 #include "qgsvectorlayer.h"
00026 #include "qgsrendercontext.h"
00027 #include <cmath>
00028 #include <QDomNode>
00029 #include <QDomElement>
00030 #include <QImage>
00031 #include <QPainter>
00032 
00033 
00034 QgsGraduatedSymbolRenderer::QgsGraduatedSymbolRenderer( QGis::GeometryType type, Mode mode )
00035 {
00036   Q_UNUSED( mode );
00037 
00038   mGeometryType = type;
00039 }
00040 
00041 QgsGraduatedSymbolRenderer::QgsGraduatedSymbolRenderer( const QgsGraduatedSymbolRenderer& other )
00042 {
00043   mMode = other.mMode;
00044   mGeometryType = other.mGeometryType;
00045   mClassificationField = other.mClassificationField;
00046   const QList<QgsSymbol*> s = other.symbols();
00047   for ( QList<QgsSymbol*>::const_iterator it = s.begin(); it != s.end(); ++it )
00048   {
00049     addSymbol( new QgsSymbol( **it ) );
00050   }
00051   updateSymbolAttributes();
00052 }
00053 
00054 QgsGraduatedSymbolRenderer& QgsGraduatedSymbolRenderer::operator=( const QgsGraduatedSymbolRenderer & other )
00055 {
00056   if ( this != &other )
00057   {
00058     mMode = other.mMode;
00059     mGeometryType = other.mGeometryType;
00060     mClassificationField = other.mClassificationField;
00061     removeSymbols();
00062     const QList<QgsSymbol*> s = other.symbols();
00063     for ( QList<QgsSymbol*>::const_iterator it = s.begin(); it != s.end(); ++it )
00064     {
00065       addSymbol( new QgsSymbol( **it ) );
00066     }
00067     updateSymbolAttributes();
00068   }
00069 
00070   return *this;
00071 }
00072 
00073 QgsGraduatedSymbolRenderer::~QgsGraduatedSymbolRenderer()
00074 {
00075 
00076 }
00077 
00078 
00079 QgsGraduatedSymbolRenderer::Mode QgsGraduatedSymbolRenderer::mode() const
00080 {
00081   //mode is only really used to be able to reinstate
00082   //the graduated dialog properties properly, so we
00083   //don't do anything else besides accessors and mutators in
00084   //this class
00085   return mMode;
00086 }
00087 
00088 void QgsGraduatedSymbolRenderer::setMode( QgsGraduatedSymbolRenderer::Mode theMode )
00089 {
00090   //mode is only really used to be able to reinstate
00091   //the graduated dialog properties properly, so we
00092   //don't do anything else besides accessors and mutators in
00093   //this class
00094   mMode = theMode;
00095 }
00096 
00097 const QList<QgsSymbol*> QgsGraduatedSymbolRenderer::symbols() const
00098 {
00099   return mSymbols;
00100 }
00101 
00102 void QgsGraduatedSymbolRenderer::removeSymbols()
00103 {
00104   //free the memory first
00105   for ( QList<QgsSymbol*>::iterator it = mSymbols.begin(); it != mSymbols.end(); ++it )
00106   {
00107     delete *it;
00108   }
00109 
00110   //and remove the pointers then
00111   mSymbols.clear();
00112   updateSymbolAttributes();
00113 }
00114 
00115 bool QgsGraduatedSymbolRenderer::willRenderFeature( QgsFeature *f )
00116 {
00117   return ( symbolForFeature( f ) != 0 );
00118 }
00119 
00120 void QgsGraduatedSymbolRenderer::renderFeature( QgsRenderContext &renderContext, QgsFeature & f, QImage* img, bool selected, double opacity )
00121 {
00122   QPainter *p = renderContext.painter();
00123   QgsSymbol* theSymbol = symbolForFeature( &f );
00124   if ( !theSymbol )
00125   {
00126     if ( img && mGeometryType == QGis::Point )
00127     {
00128       img->fill( 0 );
00129     }
00130     else if ( mGeometryType != QGis::Point )
00131     {
00132       p->setPen( Qt::NoPen );
00133       p->setBrush( Qt::NoBrush );
00134     }
00135     return;
00136   }
00137 
00138   //set the qpen and qpainter to the right values
00139   // Point
00140   if ( img && mGeometryType == QGis::Point )
00141   {
00142     double fieldScale = 1.0;
00143     double rotation = 0.0;
00144 
00145     if ( theSymbol->scaleClassificationField() >= 0 )
00146     {
00147       //first find out the value for the scale classification attribute
00148       const QgsAttributeMap& attrs = f.attributeMap();
00149       fieldScale = sqrt( qAbs( attrs[theSymbol->scaleClassificationField()].toDouble() ) );
00150       QgsDebugMsgLevel( QString( "Feature has field scale factor %1" ).arg( fieldScale ), 3 );
00151     }
00152     if ( theSymbol->rotationClassificationField() >= 0 )
00153     {
00154       const QgsAttributeMap& attrs = f.attributeMap();
00155       rotation = attrs[theSymbol->rotationClassificationField()].toDouble();
00156       QgsDebugMsgLevel( QString( "Feature has rotation factor %1" ).arg( rotation ), 3 );
00157     }
00158 
00159     QString oldName;
00160 
00161     if ( theSymbol->symbolField() >= 0 )
00162     {
00163       const QgsAttributeMap& attrs = f.attributeMap();
00164       QString name = attrs[theSymbol->symbolField()].toString();
00165       QgsDebugMsgLevel( QString( "Feature has name %1" ).arg( name ), 3 );
00166       oldName = theSymbol->pointSymbolName();
00167       theSymbol->setNamedPointSymbol( name );
00168     }
00169 
00170     double scale = renderContext.scaleFactor();
00171 
00172     if ( theSymbol->pointSizeUnits() )
00173     {
00174       scale = 1.0 / renderContext.mapToPixel().mapUnitsPerPixel();
00175     }
00176 
00177     *img = theSymbol->getPointSymbolAsImage( scale, selected, mSelectionColor, fieldScale,
00178            rotation, renderContext.rasterScaleFactor(), opacity );
00179 
00180     if ( !oldName.isNull() )
00181     {
00182       theSymbol->setNamedPointSymbol( oldName );
00183     }
00184   }
00185 
00186   // Line, polygon
00187   if ( mGeometryType != QGis::Point )
00188   {
00189     if ( !selected )
00190     {
00191       QPen pen = theSymbol->pen();
00192       pen.setWidthF( renderContext.scaleFactor() * pen.widthF() );
00193       p->setPen( pen );
00194 
00195       if ( mGeometryType == QGis::Polygon )
00196       {
00197         QBrush brush = theSymbol->brush();
00198         scaleBrush( brush, renderContext.rasterScaleFactor() ); //scale brush content for printout
00199         p->setBrush( brush );
00200       }
00201     }
00202     else
00203     {
00204       QPen pen = theSymbol->pen();
00205       pen.setWidthF( renderContext.scaleFactor() * pen.widthF() );
00206 
00207       if ( mGeometryType == QGis::Polygon )
00208       {
00209         QBrush brush = theSymbol->brush();
00210         scaleBrush( brush, renderContext.rasterScaleFactor() ); //scale brush content for printout
00211         brush.setColor( mSelectionColor );
00212         p->setBrush( brush );
00213       }
00214       else //don't draw outlines in selection color for polys otherwise they appear merged
00215       {
00216         pen.setColor( mSelectionColor );
00217       }
00218       p->setPen( pen );
00219     }
00220   }
00221 }
00222 
00223 QgsSymbol *QgsGraduatedSymbolRenderer::symbolForFeature( const QgsFeature* f )
00224 {
00225   //first find out the value for the classification attribute
00226   const QgsAttributeMap& attrs = f->attributeMap();
00227   double value = attrs[mClassificationField].toDouble();
00228 
00229   QList<QgsSymbol*>::iterator it;
00230   //find the first render item which contains the feature
00231   for ( it = mSymbols.begin(); it != mSymbols.end(); ++it )
00232   {
00233     if ( value >= ( *it )->lowerValue().toDouble() && value <= ( *it )->upperValue().toDouble() )
00234     {
00235       break;
00236     }
00237   }
00238 
00239   if ( it == mSymbols.end() )    //only draw features which are covered by a render item
00240   {
00241     return 0;
00242   }
00243   return ( *it );
00244 }
00245 
00246 int QgsGraduatedSymbolRenderer::readXML( const QDomNode& rnode, QgsVectorLayer& vl )
00247 {
00248   mGeometryType = vl.geometryType();
00249   QDomNode modeNode = rnode.namedItem( "mode" );
00250   QString modeValue = modeNode.toElement().text();
00251   QDomNode classnode = rnode.namedItem( "classificationfield" );
00252   QString classificationField = classnode.toElement().text();
00253 
00254   QgsVectorDataProvider* theProvider = vl.dataProvider();
00255   if ( !theProvider )
00256   {
00257     return 1;
00258   }
00259   if ( modeValue == "Empty" )
00260   {
00261     mMode = QgsGraduatedSymbolRenderer::Empty;
00262   }
00263   else if ( modeValue == "Quantile" )
00264   {
00265     mMode = QgsGraduatedSymbolRenderer::Quantile;
00266   }
00267   else //default
00268   {
00269     mMode = QgsGraduatedSymbolRenderer::EqualInterval;
00270   }
00271 
00272   int classificationId = vl.fieldNameIndex( classificationField );
00273   if ( classificationId == -1 )
00274   {
00275     //go on. Because with joins, it might be the joined layer is not loaded yet
00276   }
00277   setClassificationField( classificationId );
00278 
00279   QDomNode symbolnode = rnode.namedItem( "symbol" );
00280   while ( !symbolnode.isNull() )
00281   {
00282     QgsSymbol* sy = new QgsSymbol( mGeometryType );
00283     sy->readXML( symbolnode, &vl );
00284     addSymbol( sy );
00285 
00286     symbolnode = symbolnode.nextSibling();
00287   }
00288   updateSymbolAttributes();
00289   vl.setRenderer( this );
00290   return 0;
00291 }
00292 
00293 QgsAttributeList QgsGraduatedSymbolRenderer::classificationAttributes() const
00294 {
00295   QgsAttributeList list( mSymbolAttributes );
00296   if ( ! list.contains( mClassificationField ) )
00297   {
00298     list.append( mClassificationField );
00299   }
00300   return list;
00301 }
00302 
00303 void QgsGraduatedSymbolRenderer::updateSymbolAttributes()
00304 {
00305   // This function is only called after changing field specifier in the GUI.
00306   // Timing is not so important.
00307 
00308   mSymbolAttributes.clear();
00309 
00310   QList<QgsSymbol*>::iterator it;
00311   for ( it = mSymbols.begin(); it != mSymbols.end(); ++it )
00312   {
00313     int rotationField = ( *it )->rotationClassificationField();
00314     if ( rotationField >= 0 && !mSymbolAttributes.contains( rotationField ) )
00315     {
00316       mSymbolAttributes.append( rotationField );
00317     }
00318     int scaleField = ( *it )->scaleClassificationField();
00319     if ( scaleField >= 0 && !mSymbolAttributes.contains( scaleField ) )
00320     {
00321       mSymbolAttributes.append( scaleField );
00322     }
00323     int symbolField = ( *it )->symbolField();
00324     if ( symbolField >= 0 && !mSymbolAttributes.contains( symbolField ) )
00325     {
00326       mSymbolAttributes.append( symbolField );
00327     }
00328   }
00329 }
00330 
00331 QString QgsGraduatedSymbolRenderer::name() const
00332 {
00333   return "Graduated Symbol";
00334 }
00335 
00336 bool QgsGraduatedSymbolRenderer::writeXML( QDomNode & layer_node, QDomDocument & document, const QgsVectorLayer& vl ) const
00337 {
00338   bool returnval = true;
00339   QDomElement graduatedsymbol = document.createElement( "graduatedsymbol" );
00340   layer_node.appendChild( graduatedsymbol );
00341 
00342   //
00343   // Mode field first ...
00344   //
00345 
00346   QString modeValue = "";
00347   if ( mMode == QgsGraduatedSymbolRenderer::Empty )
00348   {
00349     modeValue = "Empty";
00350   }
00351   else if ( QgsGraduatedSymbolRenderer::Quantile )
00352   {
00353     modeValue = "Quantile";
00354   }
00355   else //default
00356   {
00357     modeValue = "Equal Interval";
00358   }
00359   QDomElement modeElement = document.createElement( "mode" );
00360   QDomText modeText = document.createTextNode( modeValue );
00361   modeElement.appendChild( modeText );
00362   graduatedsymbol.appendChild( modeElement );
00363 
00364 
00365 
00366   //
00367   // classification field now ...
00368   //
00369 
00370   QDomElement classificationfield = document.createElement( "classificationfield" );
00371 
00372   const QgsVectorDataProvider* theProvider = vl.dataProvider();
00373   if ( !theProvider )
00374   {
00375     return false;
00376   }
00377 
00378   QString classificationFieldName;
00379   if ( vl.pendingFields().contains( mClassificationField ) )
00380   {
00381     classificationFieldName = vl.pendingFields()[ mClassificationField ].name();
00382   }
00383 
00384   QDomText classificationfieldtxt = document.createTextNode( classificationFieldName );
00385   classificationfield.appendChild( classificationfieldtxt );
00386   graduatedsymbol.appendChild( classificationfield );
00387   for ( QList<QgsSymbol*>::const_iterator it = mSymbols.begin(); it != mSymbols.end(); ++it )
00388   {
00389     if ( !( *it )->writeXML( graduatedsymbol, document, &vl ) )
00390     {
00391       returnval = false;
00392     }
00393   }
00394   return returnval;
00395 }
00396 
00397 QgsRenderer* QgsGraduatedSymbolRenderer::clone() const
00398 {
00399   QgsGraduatedSymbolRenderer* r = new QgsGraduatedSymbolRenderer( *this );
00400   return r;
00401 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines