src/core/symbology/qgssymbol.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           QgsSymbol.cpp  -  description
00003                              -------------------
00004     begin                : Sun Aug 11 2002
00005     copyright            : (C) 2002 by Gary E.Sherman
00006     email                : sherman at mrcc dot com
00007        Romans 3:23=>Romans 6:23=>Romans 5:8=>Romans 10:9,10=>Romans 12
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 /* $Id: qgssymbol.cpp 9908 2008-12-29 12:05:05Z jef $ */
00019 #include <cmath>
00020 
00021 #include "qgis.h"
00022 #include "qgssymbol.h"
00023 #include "qgslogger.h"
00024 #include "qgssymbologyutils.h"
00025 #include "qgsmarkercatalogue.h"
00026 #include "qgsapplication.h"
00027 #include "qgsvectorlayer.h"
00028 
00029 #include <QPainter>
00030 #include <QDomNode>
00031 #include <QDomDocument>
00032 #include <QImage>
00033 #include <QDir>
00034 #include <QFileInfo>
00035 //#include <QString>
00036 #include "qgslogger.h"
00037 //do we have to include qstring?
00038 
00039 QgsSymbol::QgsSymbol( QGis::GeometryType t, QString lvalue, QString uvalue, QString label ) :
00040     mLowerValue( lvalue ),
00041     mUpperValue( uvalue ),
00042     mLabel( label ),
00043     mType( t ),
00044     mPointSymbolName( "hard:circle" ),
00045     mPointSize( DEFAULT_POINT_SIZE ),
00046     mPointSymbolImage( 1, 1, QImage::Format_ARGB32_Premultiplied ),
00047     mWidthScale( -1.0 ),
00048     mCacheUpToDate( false ),
00049     mCacheUpToDate2( false ),
00050     mRotationClassificationField( -1 ),
00051     mScaleClassificationField( -1 )
00052 {
00053   mPen.setWidthF( DEFAULT_LINE_WIDTH );
00054 }
00055 
00056 
00057 QgsSymbol::QgsSymbol( QGis::GeometryType t, QString lvalue, QString uvalue, QString label, QColor c ) :
00058     mLowerValue( lvalue ),
00059     mUpperValue( uvalue ),
00060     mLabel( label ),
00061     mType( t ),
00062     mPen( c ),
00063     mBrush( c ),
00064     mPointSymbolName( "hard:circle" ),
00065     mPointSize( DEFAULT_POINT_SIZE ),
00066     mPointSymbolImage( 1, 1, QImage::Format_ARGB32_Premultiplied ),
00067     mWidthScale( -1.0 ),
00068     mCacheUpToDate( false ),
00069     mCacheUpToDate2( false ),
00070     mRotationClassificationField( -1 ),
00071     mScaleClassificationField( -1 )
00072 {
00073   mPen.setWidthF( DEFAULT_LINE_WIDTH );
00074 }
00075 
00076 QgsSymbol::QgsSymbol()
00077     : mPointSymbolName( "hard:circle" ),
00078     mPointSize( DEFAULT_POINT_SIZE ),
00079     mPointSymbolImage( 1, 1, QImage::Format_ARGB32_Premultiplied ),
00080     mWidthScale( -1.0 ),
00081     mCacheUpToDate( false ),
00082     mCacheUpToDate2( false ),
00083     mRotationClassificationField( -1 ),
00084     mScaleClassificationField( -1 )
00085 {
00086   mPen.setWidthF( DEFAULT_LINE_WIDTH );
00087 }
00088 
00089 
00090 QgsSymbol::QgsSymbol( QColor c )
00091     : mPen( c ),
00092     mBrush( c ),
00093     mPointSymbolName( "hard:circle" ),
00094     mPointSize( DEFAULT_POINT_SIZE ),
00095     mPointSymbolImage( 1, 1, QImage::Format_ARGB32_Premultiplied ),
00096     mWidthScale( -1.0 ),
00097     mCacheUpToDate( false ),
00098     mCacheUpToDate2( false ),
00099     mRotationClassificationField( -1 ),
00100     mScaleClassificationField( -1 )
00101 {
00102   mPen.setWidthF( DEFAULT_LINE_WIDTH );
00103 }
00104 
00105 QgsSymbol::QgsSymbol( const QgsSymbol& s )
00106 {
00107   if ( this != &s )
00108   {
00109     mLowerValue = s.mLowerValue;
00110     mUpperValue = s.mUpperValue;
00111     mLabel = s.mLabel;
00112     mType = s.mType;
00113     mPen = s.mPen;
00114     mBrush = s.mBrush;
00115     mTextureFilePath = s.mTextureFilePath;
00116     mPointSymbolName = s.mPointSymbolName;
00117     mPointSize = s.mPointSize;
00118     mPointSymbolImage = s.mPointSymbolImage;
00119     mPointSymbolImageSelected = s.mPointSymbolImageSelected;
00120     mWidthScale = s.mWidthScale;
00121     mPointSymbolImage2 = s.mPointSymbolImage2;
00122     mPointSymbolImageSelected2 = s.mPointSymbolImageSelected2;
00123     mCacheUpToDate = s.mCacheUpToDate;
00124     mCacheUpToDate2 = s.mCacheUpToDate2;
00125     mSelectionColor = s.mSelectionColor;
00126     mSelectionColor2 = s.mSelectionColor2;
00127     mRotationClassificationField = s.mRotationClassificationField;
00128     mScaleClassificationField = s.mScaleClassificationField;
00129   }
00130 }
00131 
00132 QgsSymbol::~QgsSymbol()
00133 {
00134 }
00135 
00136 
00137 QColor QgsSymbol::color() const
00138 {
00139   return mPen.color();
00140 }
00141 
00142 void QgsSymbol::setColor( QColor c )
00143 {
00144   mPen.setColor( c );
00145   mCacheUpToDate = mCacheUpToDate2 = false;
00146 }
00147 
00148 QColor QgsSymbol::fillColor() const
00149 {
00150   return mBrush.color();
00151 }
00152 
00153 void QgsSymbol::setFillColor( QColor c )
00154 {
00155   mBrush.setColor( c );
00156   mCacheUpToDate = mCacheUpToDate2 = false;
00157 }
00158 
00159 double QgsSymbol::lineWidth() const
00160 {
00161   return mPen.widthF();
00162 }
00163 
00164 void QgsSymbol::setLineWidth( double w )
00165 {
00166   mPen.setWidthF( w );
00167   mCacheUpToDate = mCacheUpToDate2 = false;
00168 }
00169 
00170 void QgsSymbol::setLineStyle( Qt::PenStyle s )
00171 {
00172   mPen.setStyle( s );
00173   mCacheUpToDate = mCacheUpToDate2 = false;
00174 }
00175 
00176 void QgsSymbol::setFillStyle( Qt::BrushStyle s )
00177 {
00178   mBrush.setStyle( s );
00179   mCacheUpToDate = mCacheUpToDate2 = false;
00180 }
00181 
00182 QString QgsSymbol::customTexture() const
00183 {
00184   return mTextureFilePath;
00185 }
00186 
00187 void QgsSymbol::setCustomTexture( QString path )
00188 {
00189   mTextureFilePath = path;
00190   mBrush.setTextureImage( QImage( path ) );
00191   mCacheUpToDate = mCacheUpToDate2 = false;
00192 }
00193 
00194 //should we set the path independently of setting the texture?
00195 
00196 void QgsSymbol::setNamedPointSymbol( QString name )
00197 {
00198   // do some sanity checking for svgs...
00199   QString myTempName = name;
00200   myTempName.replace( "svg:", "" );
00201   QFile myFile( myTempName );
00202   if ( !myFile.exists() )
00203   {
00204     QgsDebugMsg( "\n\n\n *** Svg Symbol not found on fs ***" );
00205     QgsDebugMsg( "Name: " + name );
00206     //see if we can resolve the problem...
00207     //by using the qgis svg dir from this local machine
00208     //one day when user specified svg are allowed we need
00209     //to adjust this logic probably...
00210     QString svgPath = QgsApplication::svgPath();
00211     QgsDebugMsg( "SvgPath: " + svgPath );
00212     QFileInfo myInfo( myTempName );
00213     QString myFileName = myInfo.fileName(); // foo.svg
00214     QString myLowestDir = myInfo.dir().dirName();
00215     QString myLocalPath = svgPath + QDir::separator() +
00216                           myLowestDir + QDir::separator() +
00217                           myFileName;
00218     QgsDebugMsg( "Alternative svg path: " + myLocalPath );
00219     if ( QFile( myLocalPath ).exists() )
00220     {
00221       name = "svg:" + myLocalPath;
00222       QgsDebugMsg( "Svg found in alternative path" );
00223     }
00224     else
00225     {
00226       //couldnt find the file, no happy ending :-(
00227       QgsDebugMsg( "Computed alternate path but no svg there either" );
00228     }
00229   }
00230   mPointSymbolName = name;
00231   mCacheUpToDate = mCacheUpToDate2 = false;
00232 }
00233 
00234 QString QgsSymbol::pointSymbolName() const
00235 {
00236   return mPointSymbolName;
00237 }
00238 
00239 void QgsSymbol::setPointSize( double s )
00240 {
00241   if ( s < MINIMUM_POINT_SIZE )
00242     mPointSize = MINIMUM_POINT_SIZE;
00243   else
00244     mPointSize = s;
00245 
00246   mCacheUpToDate = mCacheUpToDate2 = false;
00247 }
00248 
00249 double QgsSymbol::pointSize() const
00250 {
00251   return mPointSize;
00252 }
00253 
00254 
00255 QImage QgsSymbol::getLineSymbolAsImage()
00256 {
00257   //Note by Tim: dont use premultiplied - it causes
00258   //artifacts on the output icon!
00259   QImage img( 15, 15, QImage::Format_ARGB32 );//QImage::Format_ARGB32_Premultiplied);
00260   //0 = fully transparent
00261   img.fill( QColor( 255, 255, 255, 0 ).rgba() );
00262   QPainter p( &img );
00263   p.setRenderHints( QPainter::Antialiasing );
00264   p.setPen( mPen );
00265 
00266 
00267   QPainterPath myPath;
00268   myPath.moveTo( 0, 0 );
00269   myPath.cubicTo( 15, 0, 5, 7, 15, 15 );
00270   p.drawPath( myPath );
00271   //p.drawLine(0, 0, 15, 15);
00272   return img; //this is ok because of qts sharing mechanism
00273 }
00274 
00275 QImage QgsSymbol::getPolygonSymbolAsImage()
00276 {
00277   //Note by Tim: dont use premultiplied - it causes
00278   //artifacts on the output icon!
00279   QImage img( 15, 15, QImage::Format_ARGB32 ); //, QImage::Format_ARGB32_Premultiplied);
00280   //0 = fully transparent
00281   img.fill( QColor( 255, 255, 255, 0 ).rgba() );
00282   QPainter p( &img );
00283   p.setRenderHints( QPainter::Antialiasing );
00284   p.setPen( mPen );
00285   p.setBrush( mBrush );
00286   QPolygon myPolygon;
00287   //leave a little white space around so
00288   //dont draw at 0,0,15,15
00289   myPolygon << QPoint( 2, 2 )
00290   << QPoint( 1, 5 )
00291   << QPoint( 1, 10 )
00292   << QPoint( 2, 12 )
00293   << QPoint( 5, 13 )
00294   << QPoint( 6, 13 )
00295   << QPoint( 8, 12 )
00296   << QPoint( 8, 12 )
00297   << QPoint( 10, 12 )
00298   << QPoint( 12, 13 )
00299   << QPoint( 13, 11 )
00300   << QPoint( 12, 8 )
00301   << QPoint( 11, 6 )
00302   << QPoint( 12, 5 )
00303   << QPoint( 13, 2 )
00304   << QPoint( 11, 1 )
00305   << QPoint( 10, 1 )
00306   << QPoint( 8, 2 )
00307   << QPoint( 6, 4 )
00308   << QPoint( 4, 2 )
00309   ;
00310   p.drawPolygon( myPolygon );
00311   //p.drawRect(1, 1, 14, 14);
00312   return img; //this is ok because of qts sharing mechanism
00313 }
00314 
00315 QImage QgsSymbol::getCachedPointSymbolAsImage( double widthScale,
00316     bool selected, QColor selectionColor )
00317 {
00318   if ( !mCacheUpToDate2
00319        || ( selected && mSelectionColor != selectionColor ) )
00320   {
00321     if ( selected )
00322     {
00323       cache2( widthScale, selectionColor );
00324     }
00325     else
00326     {
00327       cache2( widthScale, mSelectionColor );
00328     }
00329   }
00330 
00331   if ( selected )
00332   {
00333     return mPointSymbolImageSelected2;
00334   }
00335   else
00336   {
00337     return mPointSymbolImage2;
00338   }
00339 }
00340 
00341 QImage QgsSymbol::getPointSymbolAsImage( double widthScale, bool selected, QColor selectionColor, double scale,
00342     double rotation, double rasterScaleFactor )
00343 {
00344   if ( 1.0 == ( scale * rasterScaleFactor ) && 0 == rotation )
00345   {
00346     if ( mWidthScale < 0 || widthScale == mWidthScale )
00347     {
00348       // If scale is 1.0 and rotation 0.0, use cached image.
00349       return getCachedPointSymbolAsImage( widthScale, selected, selectionColor );
00350     }
00351   }
00352 
00353   QImage preRotateImage;
00354   QPen pen = mPen;
00355   double newWidth = mPen.widthF() * widthScale * rasterScaleFactor;
00356   pen.setWidthF( newWidth );
00357 
00358   if ( selected )
00359   {
00360     pen.setColor( selectionColor );
00361     QBrush brush = mBrush;
00362     preRotateImage = QgsMarkerCatalogue::instance()->imageMarker(
00363                        mPointSymbolName, ( float )( mPointSize * scale * widthScale *
00364                                                     rasterScaleFactor ),
00365                        pen, mBrush );
00366   }
00367   else
00368   {
00369     preRotateImage = QgsMarkerCatalogue::instance()->imageMarker(
00370                        mPointSymbolName, ( float )( mPointSize * scale * widthScale *
00371                                                     rasterScaleFactor ),
00372                        pen, mBrush );
00373   }
00374 
00375   QMatrix rotationMatrix;
00376   rotationMatrix = rotationMatrix.rotate( rotation );
00377 
00378   return preRotateImage.transformed( rotationMatrix, Qt::SmoothTransformation );
00379 }
00380 
00381 
00382 void QgsSymbol::cache( QColor selectionColor )
00383 {
00384   QPen pen = mPen;
00385   pen.setColor( selectionColor );
00386   QBrush brush = mBrush;
00387   // For symbols that have a different coloured border, the line
00388   // below causes the fill colour to be wrong for the print
00389   // composer. Not sure why...
00390   // brush.setColor ( selectionColor );
00391 
00392   mPointSymbolImage = QgsMarkerCatalogue::instance()->imageMarker( mPointSymbolName, mPointSize,
00393                       mPen, mBrush );
00394 
00395   mPointSymbolImageSelected = QgsMarkerCatalogue::instance()->imageMarker(
00396                                 mPointSymbolName, mPointSize, pen, brush );
00397 
00398   mSelectionColor = selectionColor;
00399   mCacheUpToDate = true;
00400 }
00401 
00402 void QgsSymbol::cache2( double widthScale, QColor selectionColor )
00403 {
00404 // QgsDebugMsg(QString("widthScale = %1").arg(widthScale));
00405 
00406   QPen pen = mPen;
00407   pen.setWidthF( widthScale * pen.widthF() );
00408 
00409   mPointSymbolImage2 = QgsMarkerCatalogue::instance()->imageMarker( mPointSymbolName, mPointSize * widthScale,
00410                        pen, mBrush, false );
00411 
00412   QBrush brush = mBrush;
00413   brush.setColor( selectionColor );
00414   pen.setColor( selectionColor );
00415 
00416   mPointSymbolImageSelected2 = QgsMarkerCatalogue::instance()->imageMarker(
00417                                  mPointSymbolName, mPointSize * widthScale, pen, brush,  false );
00418 
00419   mSelectionColor2 = selectionColor;
00420 
00421   mWidthScale = widthScale;
00422   mCacheUpToDate2 = true;
00423 }
00424 
00425 void QgsSymbol::appendField( QDomElement &symbol, QDomDocument &document, const QgsVectorLayer &vl, QString name, int idx ) const
00426 {
00427   appendText( symbol, document, name, vl.pendingFields().contains( idx ) ? vl.pendingFields()[idx].name() : "" );
00428 }
00429 
00430 void QgsSymbol::appendText( QDomElement &symbol, QDomDocument &document, QString name, QString value ) const
00431 {
00432   QDomElement node = document.createElement( name );
00433   QDomText txt = document.createTextNode( value );
00434   symbol.appendChild( node );
00435   node.appendChild( txt );
00436 }
00437 
00438 bool QgsSymbol::writeXML( QDomNode & item, QDomDocument & document, const QgsVectorLayer *vl ) const
00439 {
00440   bool returnval = false;
00441   returnval = true; // no error checking yet
00442   QDomElement symbol = document.createElement( "symbol" );
00443   item.appendChild( symbol );
00444 
00445   appendText( symbol, document, "lowervalue", mLowerValue );
00446   appendText( symbol, document, "uppervalue", mUpperValue );
00447   appendText( symbol, document, "label", mLabel );
00448   appendText( symbol, document, "pointsymbol", pointSymbolName() );
00449   appendText( symbol, document, "pointsize", QString::number( pointSize() ) );
00450 
00451   if ( vl )
00452   {
00453     appendField( symbol, document, *vl, "rotationclassificationfieldname", mRotationClassificationField );
00454     appendField( symbol, document, *vl, "scaleclassificationfieldname", mScaleClassificationField );
00455   }
00456 
00457   QDomElement outlinecolor = document.createElement( "outlinecolor" );
00458   outlinecolor.setAttribute( "red", QString::number( mPen.color().red() ) );
00459   outlinecolor.setAttribute( "green", QString::number( mPen.color().green() ) );
00460   outlinecolor.setAttribute( "blue", QString::number( mPen.color().blue() ) );
00461   symbol.appendChild( outlinecolor );
00462 
00463   appendText( symbol, document, "outlinestyle", QgsSymbologyUtils::penStyle2QString( mPen.style() ) );
00464   appendText( symbol, document, "outlinewidth", QString::number( mPen.widthF() ) );
00465 
00466   QDomElement fillcolor = document.createElement( "fillcolor" );
00467   fillcolor.setAttribute( "red", QString::number( mBrush.color().red() ) );
00468   fillcolor.setAttribute( "green", QString::number( mBrush.color().green() ) );
00469   fillcolor.setAttribute( "blue", QString::number( mBrush.color().blue() ) );
00470   symbol.appendChild( fillcolor );
00471 
00472   appendText( symbol, document, "fillpattern", QgsSymbologyUtils::brushStyle2QString( mBrush.style() ) );
00473   appendText( symbol, document, "texturepath", mTextureFilePath );
00474 
00475   return returnval;
00476 }
00477 
00478 int QgsSymbol::readFieldName( QDomNode &synode, QString name, const QgsVectorLayer &vl )
00479 {
00480   QDomNode node = synode.namedItem( name + "name" );
00481 
00482   if ( !node.isNull() )
00483   {
00484     const QgsFieldMap &fields = vl.pendingFields();
00485     QString name = node.toElement().text();
00486 
00487     for ( QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); it++ )
00488       if ( it->name() == name )
00489         return it.key();
00490 
00491     return -1;
00492   }
00493 
00494   node = synode.namedItem( name );
00495 
00496   return node.isNull() ? -1 : node.toElement().text().toInt();
00497 }
00498 
00499 bool QgsSymbol::readXML( QDomNode &synode, const QgsVectorLayer *vl )
00500 {
00501   // Legacy project file formats didn't have support for pointsymbol nor
00502   // pointsize Dom elements.  Therefore we should check whether these
00503   // actually exist.
00504 
00505   QDomNode lvalnode = synode.namedItem( "lowervalue" );
00506   if ( ! lvalnode.isNull() )
00507   {
00508     QDomElement lvalelement = lvalnode.toElement();
00509     mLowerValue = lvalelement.text();
00510   }
00511 
00512   QDomNode uvalnode = synode.namedItem( "uppervalue" );
00513   if ( ! uvalnode.isNull() )
00514   {
00515     QDomElement uvalelement = uvalnode.toElement();
00516     mUpperValue = uvalelement.text();
00517   }
00518 
00519   QDomNode labelnode = synode.namedItem( "label" );
00520   if ( ! labelnode.isNull() )
00521   {
00522     QDomElement labelelement = labelnode.toElement();
00523     mLabel = labelelement.text();
00524   }
00525 
00526   QDomNode psymbnode = synode.namedItem( "pointsymbol" );
00527 
00528   if ( ! psymbnode.isNull() )
00529   {
00530     QDomElement psymbelement = psymbnode.toElement();
00531     setNamedPointSymbol( psymbelement.text() );
00532   }
00533 
00534   QDomNode psizenode = synode.namedItem( "pointsize" );
00535 
00536   if ( !  psizenode.isNull() )
00537   {
00538     QDomElement psizeelement = psizenode.toElement();
00539     setPointSize( psizeelement.text().toFloat() );
00540   }
00541 
00542   if ( vl )
00543   {
00544     mRotationClassificationField = readFieldName( synode, "rotationclassificationfield", *vl );
00545     mScaleClassificationField = readFieldName( synode, "scaleclassificationfield", *vl );
00546   }
00547   else
00548   {
00549     mRotationClassificationField = -1;
00550     mScaleClassificationField = -1;
00551   }
00552 
00553   QDomNode outlcnode = synode.namedItem( "outlinecolor" );
00554   QDomElement oulcelement = outlcnode.toElement();
00555   int red = oulcelement.attribute( "red" ).toInt();
00556   int green = oulcelement.attribute( "green" ).toInt();
00557   int blue = oulcelement.attribute( "blue" ).toInt();
00558   setColor( QColor( red, green, blue ) );
00559 
00560   QDomNode outlstnode = synode.namedItem( "outlinestyle" );
00561   QDomElement outlstelement = outlstnode.toElement();
00562   setLineStyle( QgsSymbologyUtils::qString2PenStyle( outlstelement.text() ) );
00563 
00564   QDomNode outlwnode = synode.namedItem( "outlinewidth" );
00565   QDomElement outlwelement = outlwnode.toElement();
00566   setLineWidth( outlwelement.text().toDouble() );
00567 
00568   QDomNode fillcnode = synode.namedItem( "fillcolor" );
00569   QDomElement fillcelement = fillcnode.toElement();
00570   red = fillcelement.attribute( "red" ).toInt();
00571   green = fillcelement.attribute( "green" ).toInt();
00572   blue = fillcelement.attribute( "blue" ).toInt();
00573   setFillColor( QColor( red, green, blue ) );
00574 
00575   QDomNode texturepathnode = synode.namedItem( "texturepath" );
00576   QDomElement texturepathelement = texturepathnode.toElement();
00577   setCustomTexture( texturepathelement.text() );
00578 
00579   //run this after setting the custom texture path, so we override the brush if it isn't the custom pattern brush.
00580   QDomNode fillpnode = synode.namedItem( "fillpattern" );
00581   QDomElement fillpelement = fillpnode.toElement();
00582   setFillStyle( QgsSymbologyUtils::qString2BrushStyle( fillpelement.text() ) );
00583 
00584   return true;
00585 }
00586 
00587 int QgsSymbol::rotationClassificationField() const
00588 {
00589   return mRotationClassificationField;
00590 }
00591 
00592 void QgsSymbol::setRotationClassificationField( int field )
00593 {
00594   mRotationClassificationField = field;
00595 }
00596 
00597 int QgsSymbol::scaleClassificationField() const
00598 {
00599   return mScaleClassificationField;
00600 }
00601 
00602 void QgsSymbol::setScaleClassificationField( int field )
00603 {
00604   mScaleClassificationField = field;
00605 }

Generated on Fri Jan 9 16:51:13 2009 for Quantum GIS API Documentation by  doxygen 1.5.1