00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00036 #include "qgslogger.h"
00037
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
00195
00196 void QgsSymbol::setNamedPointSymbol( QString name )
00197 {
00198
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
00207
00208
00209
00210 QString svgPath = QgsApplication::svgPath();
00211 QgsDebugMsg( "SvgPath: " + svgPath );
00212 QFileInfo myInfo( myTempName );
00213 QString myFileName = myInfo.fileName();
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
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
00258
00259 QImage img( 15, 15, QImage::Format_ARGB32 );
00260
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
00272 return img;
00273 }
00274
00275 QImage QgsSymbol::getPolygonSymbolAsImage()
00276 {
00277
00278
00279 QImage img( 15, 15, QImage::Format_ARGB32 );
00280
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
00288
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
00312 return img;
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
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
00388
00389
00390
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
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;
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
00502
00503
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
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 }