Quantum GIS API Documentation  1.8
src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsmarkersymbollayerv2.cpp
00003     ---------------------
00004     begin                : November 2009
00005     copyright            : (C) 2009 by Martin Dobias
00006     email                : wonder.sk at gmail.com
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 
00016 #include "qgsmarkersymbollayerv2.h"
00017 #include "qgssymbollayerv2utils.h"
00018 
00019 #include "qgsrendercontext.h"
00020 #include "qgsapplication.h"
00021 #include "qgslogger.h"
00022 #include "qgsproject.h"
00023 #include "qgssvgcache.h"
00024 
00025 #include <QPainter>
00026 #include <QSvgRenderer>
00027 #include <QFileInfo>
00028 #include <QDir>
00029 #include <QDomDocument>
00030 #include <QDomElement>
00031 
00032 #include <cmath>
00033 
00034 // MSVC compiler doesn't have defined M_PI in math.h
00035 #ifndef M_PI
00036 #define M_PI          3.14159265358979323846
00037 #endif
00038 
00039 #define DEG2RAD(x)    ((x)*M_PI/180)
00040 
00041 
00042 static QPointF _rotatedOffset( const QPointF& offset, double angle )
00043 {
00044   angle = DEG2RAD( angle );
00045   double c = cos( angle ), s = sin( angle );
00046   return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c );
00047 }
00048 
00050 
00051 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle )
00052 {
00053   mName = name;
00054   mColor = color;
00055   mBorderColor = borderColor;
00056   mSize = size;
00057   mAngle = angle;
00058   mOffset = QPointF( 0, 0 );
00059 }
00060 
00061 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::create( const QgsStringMap& props )
00062 {
00063   QString name = DEFAULT_SIMPLEMARKER_NAME;
00064   QColor color = DEFAULT_SIMPLEMARKER_COLOR;
00065   QColor borderColor = DEFAULT_SIMPLEMARKER_BORDERCOLOR;
00066   double size = DEFAULT_SIMPLEMARKER_SIZE;
00067   double angle = DEFAULT_SIMPLEMARKER_ANGLE;
00068 
00069   if ( props.contains( "name" ) )
00070     name = props["name"];
00071   if ( props.contains( "color" ) )
00072     color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
00073   if ( props.contains( "color_border" ) )
00074     borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
00075   if ( props.contains( "size" ) )
00076     size = props["size"].toDouble();
00077   if ( props.contains( "angle" ) )
00078     angle = props["angle"].toDouble();
00079 
00080   QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle );
00081   if ( props.contains( "offset" ) )
00082     m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
00083   return m;
00084 }
00085 
00086 
00087 QString QgsSimpleMarkerSymbolLayerV2::layerType() const
00088 {
00089   return "SimpleMarker";
00090 }
00091 
00092 void QgsSimpleMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00093 {
00094   QColor brushColor = mColor;
00095   QColor penColor = mBorderColor;
00096   if ( context.alpha() < 1 )
00097   {
00098     penColor.setAlphaF( context.alpha() );
00099     brushColor.setAlphaF( context.alpha() );
00100   }
00101   mBrush = QBrush( brushColor );
00102   mPen = QPen( penColor );
00103   mPen.setWidthF( context.outputLineWidth( mPen.widthF() ) );
00104 
00105   QColor selBrushColor = context.selectionColor();
00106   QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor;
00107   if ( context.alpha() < 1 )
00108   {
00109     selBrushColor.setAlphaF( context.alpha() );
00110     selPenColor.setAlphaF( context.alpha() );
00111   }
00112   mSelBrush = QBrush( selBrushColor );
00113   mSelPen = QPen( selPenColor );
00114   mSelPen.setWidthF( context.outputLineWidth( mPen.widthF() ) );
00115 
00116   bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation;
00117   bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale;
00118 
00119   // use caching only when:
00120   // - the size and rotation is not data-defined
00121   // - drawing to screen (not printer)
00122   mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput();
00123 
00124   // use either QPolygonF or QPainterPath for drawing
00125   // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes
00126   if ( !prepareShape() ) // drawing as a polygon
00127   {
00128     if ( preparePath() ) // drawing as a painter path
00129     {
00130       // some markers can't be drawn as a polygon (circle, cross)
00131       // For these set the selected border color to the selected color
00132 
00133       if ( mName != "circle" )
00134         mSelPen.setColor( selBrushColor );
00135     }
00136     else
00137     {
00138       QgsDebugMsg( "unknown symbol" );
00139       return;
00140     }
00141   }
00142 
00143   QMatrix transform;
00144 
00145   // scale the shape (if the size is not going to be modified)
00146   if ( !hasDataDefinedSize )
00147   {
00148     double scaledSize = context.outputLineWidth( mSize );
00149     if ( mUsingCache )
00150       scaledSize *= context.renderContext().rasterScaleFactor();
00151     double half = scaledSize / 2.0;
00152     transform.scale( half, half );
00153   }
00154 
00155   // rotate if the rotation is not going to be changed during the rendering
00156   if ( !hasDataDefinedRotation && mAngle != 0 )
00157   {
00158     transform.rotate( mAngle );
00159   }
00160 
00161   if ( !mPolygon.isEmpty() )
00162     mPolygon = transform.map( mPolygon );
00163   else
00164     mPath = transform.map( mPath );
00165 
00166   if ( mUsingCache )
00167   {
00168     prepareCache( context );
00169   }
00170   else
00171   {
00172     mCache = QImage();
00173     mSelCache = QImage();
00174   }
00175 }
00176 
00177 
00178 void QgsSimpleMarkerSymbolLayerV2::prepareCache( QgsSymbolV2RenderContext& context )
00179 {
00180   double scaledSize = context.outputPixelSize( mSize );
00181 
00182   // calculate necessary image size for the cache
00183   double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen
00184   int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; //  make image width, height odd; account for pen width
00185 
00186   double center = (( double ) imageSize / 2 ) + 0.5; // add 1/2 pixel for proper rounding when the figure's coordinates are added
00187 
00188   mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
00189   mCache.fill( 0 );
00190 
00191   QPainter p;
00192   p.begin( &mCache );
00193   p.setRenderHint( QPainter::Antialiasing );
00194   p.setBrush( mBrush );
00195   p.setPen( mPen );
00196   p.translate( QPointF( center, center ) );
00197   drawMarker( &p, context );
00198   p.end();
00199 
00200   // Construct the selected version of the Cache
00201 
00202   QColor selColor = context.selectionColor();
00203 
00204   mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
00205   mSelCache.fill( 0 );
00206 
00207   p.begin( &mSelCache );
00208   p.setRenderHint( QPainter::Antialiasing );
00209   p.setBrush( mSelBrush );
00210   p.setPen( mSelPen );
00211   p.translate( QPointF( center, center ) );
00212   drawMarker( &p, context );
00213   p.end();
00214 
00215   // Check that the selected version is different.  If not, then re-render,
00216   // filling the background with the selection color and using the normal
00217   // colors for the symbol .. could be ugly!
00218 
00219   if ( mSelCache == mCache )
00220   {
00221     p.begin( &mSelCache );
00222     p.setRenderHint( QPainter::Antialiasing );
00223     p.fillRect( 0, 0, imageSize, imageSize, selColor );
00224     p.setBrush( mBrush );
00225     p.setPen( mPen );
00226     p.translate( QPointF( center, center ) );
00227     drawMarker( &p, context );
00228     p.end();
00229   }
00230 }
00231 
00232 void QgsSimpleMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00233 {
00234   Q_UNUSED( context );
00235 }
00236 
00237 bool QgsSimpleMarkerSymbolLayerV2::prepareShape()
00238 {
00239   mPolygon.clear();
00240 
00241   if ( mName == "square" || mName == "rectangle" )
00242   {
00243     mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
00244     return true;
00245   }
00246   else if ( mName == "diamond" )
00247   {
00248     mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
00249     << QPointF( 1, 0 ) << QPointF( 0, -1 );
00250     return true;
00251   }
00252   else if ( mName == "pentagon" )
00253   {
00254     mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) )
00255     << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) )
00256     << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) )
00257     << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) )
00258     << QPointF( 0, -1 );
00259     return true;
00260   }
00261   else if ( mName == "triangle" )
00262   {
00263     mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 );
00264     return true;
00265   }
00266   else if ( mName == "equilateral_triangle" )
00267   {
00268     mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) )
00269     << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) )
00270     << QPointF( 0, -1 );
00271     return true;
00272   }
00273   else if ( mName == "star" )
00274   {
00275     double sixth = 1.0 / 3;
00276 
00277     mPolygon << QPointF( 0, -1 )
00278     << QPointF( -sixth, -sixth )
00279     << QPointF( -1, -sixth )
00280     << QPointF( -sixth, 0 )
00281     << QPointF( -1, 1 )
00282     << QPointF( 0, + sixth )
00283     << QPointF( 1, 1 )
00284     << QPointF( + sixth, 0 )
00285     << QPointF( 1, -sixth )
00286     << QPointF( + sixth, -sixth );
00287     return true;
00288   }
00289   else if ( mName == "regular_star" )
00290   {
00291     double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
00292 
00293     mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) )  // 324
00294     << QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) )    // 288
00295     << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) )   // 252
00296     << QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) )   // 216
00297     << QPointF( 0, inner_r )         // 180
00298     << QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) )   // 144
00299     << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) )   // 108
00300     << QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) )    //  72
00301     << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) )   //  36
00302     << QPointF( 0, -1 );          //   0
00303     return true;
00304   }
00305   else if ( mName == "arrow" )
00306   {
00307     mPolygon
00308     << QPointF( 0, -1 )
00309     << QPointF( 0.5,  -0.5 )
00310     << QPointF( 0.25, -0.25 )
00311     << QPointF( 0.25,  1 )
00312     << QPointF( -0.25,  1 )
00313     << QPointF( -0.25, -0.5 )
00314     << QPointF( -0.5,  -0.5 );
00315     return true;
00316   }
00317   else if ( mName == "filled_arrowhead" )
00318   {
00319     mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
00320     return true;
00321   }
00322 
00323   return false;
00324 }
00325 
00326 bool QgsSimpleMarkerSymbolLayerV2::preparePath()
00327 {
00328   mPath = QPainterPath();
00329 
00330   if ( mName == "circle" )
00331   {
00332     mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
00333     return true;
00334   }
00335   else if ( mName == "cross" )
00336   {
00337     mPath.moveTo( -1, 0 );
00338     mPath.lineTo( 1, 0 ); // horizontal
00339     mPath.moveTo( 0, -1 );
00340     mPath.lineTo( 0, 1 ); // vertical
00341     return true;
00342   }
00343   else if ( mName == "x" || mName == "cross2" )
00344   {
00345     mPath.moveTo( -1, -1 );
00346     mPath.lineTo( 1, 1 );
00347     mPath.moveTo( 1, -1 );
00348     mPath.lineTo( -1, 1 );
00349     return true;
00350   }
00351   else if ( mName == "line" )
00352   {
00353     mPath.moveTo( 0, -1 );
00354     mPath.lineTo( 0, 1 ); // vertical line
00355     return true;
00356   }
00357   else if ( mName == "arrowhead" )
00358   {
00359     mPath.moveTo( 0, 0 );
00360     mPath.lineTo( -1, -1 );
00361     mPath.moveTo( 0, 0 );
00362     mPath.lineTo( -1, 1 );
00363     return true;
00364   }
00365 
00366   return false;
00367 }
00368 
00369 void QgsSimpleMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context )
00370 {
00371   QgsRenderContext& rc = context.renderContext();
00372   QPainter* p = rc.painter();
00373   if ( !p )
00374   {
00375     return;
00376   }
00377 
00378   QPointF off( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
00379   if ( mAngle )
00380     off = _rotatedOffset( off, mAngle );
00381 
00382   if ( mUsingCache )
00383   {
00384     // we will use cached image
00385     QImage &img = context.selected() ? mSelCache : mCache;
00386     double s = img.width() / context.renderContext().rasterScaleFactor();
00387     p->drawImage( QRectF( point.x() - s / 2.0 + off.x(),
00388                           point.y() - s / 2.0 + off.y(),
00389                           s, s ), img );
00390   }
00391   else
00392   {
00393     QMatrix transform;
00394 
00395     bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation;
00396     bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale;
00397 
00398     // move to the desired position
00399     transform.translate( point.x() + off.x(), point.y() + off.y() );
00400 
00401     // resize if necessary
00402     if ( hasDataDefinedSize )
00403     {
00404       double scaledSize = context.outputLineWidth( mSize );
00405       double half = scaledSize / 2.0;
00406       transform.scale( half, half );
00407     }
00408 
00409     // rotate if necessary
00410     if ( mAngle != 0 && hasDataDefinedRotation )
00411     {
00412       transform.rotate( mAngle );
00413     }
00414 
00415     p->setBrush( context.selected() ? mSelBrush : mBrush );
00416     p->setPen( context.selected() ? mSelPen : mPen );
00417 
00418     if ( !mPolygon.isEmpty() )
00419       p->drawPolygon( transform.map( mPolygon ) );
00420     else
00421       p->drawPath( transform.map( mPath ) );
00422   }
00423 }
00424 
00425 
00426 QgsStringMap QgsSimpleMarkerSymbolLayerV2::properties() const
00427 {
00428   QgsStringMap map;
00429   map["name"] = mName;
00430   map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
00431   map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
00432   map["size"] = QString::number( mSize );
00433   map["angle"] = QString::number( mAngle );
00434   map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
00435   return map;
00436 }
00437 
00438 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::clone() const
00439 {
00440   QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( mName, mColor, mBorderColor, mSize, mAngle );
00441   m->setOffset( mOffset );
00442   return m;
00443 }
00444 
00445 void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00446 {
00447   // <Graphic>
00448   QDomElement graphicElem = doc.createElement( "se:Graphic" );
00449   element.appendChild( graphicElem );
00450 
00451   QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mName, mColor, mBorderColor, -1, mSize );
00452 
00453   // <Rotation>
00454   QString angleFunc;
00455   bool ok;
00456   double angle = props.value( "angle", "0" ).toDouble( &ok );
00457   if ( !ok )
00458   {
00459     angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
00460   }
00461   else if ( angle + mAngle != 0 )
00462   {
00463     angleFunc = QString::number( angle + mAngle );
00464   }
00465   QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
00466 
00467   // <Displacement>
00468   QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
00469 }
00470 
00471 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::createFromSld( QDomElement &element )
00472 {
00473   QgsDebugMsg( "Entered." );
00474 
00475   QDomElement graphicElem = element.firstChildElement( "Graphic" );
00476   if ( graphicElem.isNull() )
00477     return NULL;
00478 
00479   QString name = "square";
00480   QColor color, borderColor;
00481   double borderWidth, size;
00482 
00483   if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderWidth, size ) )
00484     return NULL;
00485 
00486   double angle = 0.0;
00487   QString angleFunc;
00488   if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
00489   {
00490     bool ok;
00491     double d = angleFunc.toDouble( &ok );
00492     if ( ok )
00493       angle = d;
00494   }
00495 
00496   QPointF offset;
00497   QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset );
00498 
00499   QgsMarkerSymbolLayerV2 *m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size );
00500   m->setAngle( angle );
00501   m->setOffset( offset );
00502   return m;
00503 }
00504 
00505 void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderContext& context )
00506 {
00507   Q_UNUSED( context );
00508 
00509   if ( mPolygon.count() != 0 )
00510   {
00511     p->drawPolygon( mPolygon );
00512   }
00513   else
00514   {
00515     p->drawPath( mPath );
00516   }
00517 }
00518 
00519 
00521 
00522 
00523 QgsSvgMarkerSymbolLayerV2::QgsSvgMarkerSymbolLayerV2( QString name, double size, double angle )
00524 {
00525   mPath = symbolNameToPath( name );
00526   mSize = size;
00527   mAngle = angle;
00528   mOffset = QPointF( 0, 0 );
00529   mOutlineWidth = 1.0;
00530   mFillColor = QColor( Qt::black );
00531   mOutlineColor = QColor( Qt::black );
00532 }
00533 
00534 
00535 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::create( const QgsStringMap& props )
00536 {
00537   QString name = DEFAULT_SVGMARKER_NAME;
00538   double size = DEFAULT_SVGMARKER_SIZE;
00539   double angle = DEFAULT_SVGMARKER_ANGLE;
00540 
00541   if ( props.contains( "name" ) )
00542     name = props["name"];
00543   if ( props.contains( "size" ) )
00544     size = props["size"].toDouble();
00545   if ( props.contains( "angle" ) )
00546     angle = props["angle"].toDouble();
00547 
00548   QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle );
00549 
00550   //we only check the svg default parameters if necessary, since it could be expensive
00551   if ( !props.contains( "fill" ) && !props.contains( "outline" ) && !props.contains( "outline-width" ) )
00552   {
00553     QColor fillColor, outlineColor;
00554     double outlineWidth;
00555     bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
00556     QgsSvgCache::instance()->containsParams( name, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
00557     if ( hasFillParam )
00558     {
00559       m->setFillColor( fillColor );
00560     }
00561     if ( hasOutlineParam )
00562     {
00563       m->setOutlineColor( outlineColor );
00564     }
00565     if ( hasOutlineWidthParam )
00566     {
00567       m->setOutlineWidth( outlineWidth );
00568     }
00569   }
00570 
00571   if ( props.contains( "offset" ) )
00572     m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
00573   if ( props.contains( "fill" ) )
00574     m->setFillColor( QColor( props["fill"] ) );
00575   if ( props.contains( "outline" ) )
00576     m->setOutlineColor( QColor( props["outline"] ) );
00577   if ( props.contains( "outline-width" ) )
00578     m->setOutlineWidth( props["outline-width"].toDouble() );
00579   return m;
00580 }
00581 
00582 void QgsSvgMarkerSymbolLayerV2::setPath( QString path )
00583 {
00584   mPath = path;
00585   QColor fillColor, outlineColor;
00586   double outlineWidth;
00587   bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
00588   QgsSvgCache::instance()->containsParams( path, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
00589   if ( hasFillParam )
00590   {
00591     setFillColor( fillColor );
00592   }
00593   if ( hasOutlineParam )
00594   {
00595     setOutlineColor( outlineColor );
00596   }
00597   if ( hasOutlineWidthParam )
00598   {
00599     setOutlineWidth( outlineWidth );
00600   }
00601 }
00602 
00603 
00604 QString QgsSvgMarkerSymbolLayerV2::layerType() const
00605 {
00606   return "SvgMarker";
00607 }
00608 
00609 void QgsSvgMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00610 {
00611   mOrigSize = mSize; // save in case the size would be data defined
00612   Q_UNUSED( context );
00613 }
00614 
00615 void QgsSvgMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00616 {
00617   Q_UNUSED( context );
00618 }
00619 
00620 void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context )
00621 {
00622   QPainter* p = context.renderContext().painter();
00623   if ( !p )
00624   {
00625     return;
00626   }
00627 
00628   p->save();
00629   QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
00630   if ( mAngle )
00631     outputOffset = _rotatedOffset( outputOffset, mAngle );
00632   p->translate( point + outputOffset );
00633 
00634   int size = ( int )( context.outputLineWidth( mSize ) );
00635   if ( size < 1 ) //don't render symbols with size below one pixel
00636   {
00637     return;
00638   }
00639 
00640   bool rotated = !doubleNear( mAngle, 0 );
00641   bool drawOnScreen = doubleNear( context.renderContext().rasterScaleFactor(), 1.0, 0.1 );
00642   if ( rotated )
00643     p->rotate( mAngle );
00644 
00645   if ( drawOnScreen && !rotated )
00646   {
00647     const QImage& img = QgsSvgCache::instance()->svgAsImage( mPath, size, mFillColor, mOutlineColor, mOutlineWidth,
00648                         context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() );
00649     //consider transparency
00650     if ( !doubleNear( context.alpha(), 1.0 ) )
00651     {
00652       QImage transparentImage = img.copy();
00653       QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
00654       p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
00655     }
00656     else
00657     {
00658       p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
00659     }
00660   }
00661   else
00662   {
00663     p->setOpacity( context.alpha( ) );
00664     const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( mPath, size, mFillColor, mOutlineColor, mOutlineWidth,
00665                           context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() );
00666     p->drawPicture( 0, 0, pct );
00667   }
00668 
00669   if ( context.selected() )
00670   {
00671     QPen pen( context.selectionColor() );
00672     pen.setWidth( context.outputLineWidth( 1.0 ) );
00673     p->setPen( pen );
00674     p->setBrush( Qt::NoBrush );
00675     double sizePixel = context.outputLineWidth( mSize );
00676     p->drawRect( QRectF( -sizePixel / 2.0, -sizePixel / 2.0, sizePixel, sizePixel ) );
00677   }
00678 
00679   p->restore();
00680 }
00681 
00682 
00683 QgsStringMap QgsSvgMarkerSymbolLayerV2::properties() const
00684 {
00685   QgsStringMap map;
00686   map["name"] = symbolPathToName( mPath );
00687   map["size"] = QString::number( mSize );
00688   map["angle"] = QString::number( mAngle );
00689   map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
00690   map["fill"] = mFillColor.name();
00691   map["outline"] = mOutlineColor.name();
00692   map["outline-width"] = QString::number( mOutlineWidth );
00693   return map;
00694 }
00695 
00696 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const
00697 {
00698   QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( mPath, mSize, mAngle );
00699   m->setFillColor( mFillColor );
00700   m->setOutlineColor( mOutlineColor );
00701   m->setOutlineWidth( mOutlineWidth );
00702   m->setOffset( mOffset );
00703   return m;
00704 }
00705 
00706 void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00707 {
00708   // <Graphic>
00709   QDomElement graphicElem = doc.createElement( "se:Graphic" );
00710   element.appendChild( graphicElem );
00711 
00712   QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize );
00713 
00714   // <Rotation>
00715   QString angleFunc;
00716   bool ok;
00717   double angle = props.value( "angle", "0" ).toDouble( &ok );
00718   if ( !ok )
00719   {
00720     angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
00721   }
00722   else if ( angle + mAngle != 0 )
00723   {
00724     angleFunc = QString::number( angle + mAngle );
00725   }
00726 
00727   QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
00728 
00729   // <Displacement>
00730   QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
00731 }
00732 
00733 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element )
00734 {
00735   QgsDebugMsg( "Entered." );
00736 
00737   QDomElement graphicElem = element.firstChildElement( "Graphic" );
00738   if ( graphicElem.isNull() )
00739     return NULL;
00740 
00741   QString path, mimeType;
00742   QColor fillColor;
00743   double size;
00744 
00745   if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
00746     return NULL;
00747 
00748   if ( mimeType != "image/svg+xml" )
00749     return NULL;
00750 
00751   double angle = 0.0;
00752   QString angleFunc;
00753   if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
00754   {
00755     bool ok;
00756     double d = angleFunc.toDouble( &ok );
00757     if ( ok )
00758       angle = d;
00759   }
00760 
00761   QPointF offset;
00762   QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset );
00763 
00764   QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( path, size );
00765   m->setFillColor( fillColor );
00766   //m->setOutlineColor( outlineColor );
00767   //m->setOutlineWidth( outlineWidth );
00768   m->setAngle( angle );
00769   m->setOffset( offset );
00770   return m;
00771 }
00772 
00773 
00774 QStringList QgsSvgMarkerSymbolLayerV2::listSvgFiles()
00775 {
00776   // copied from QgsMarkerCatalogue - TODO: unify
00777   QStringList list;
00778   QStringList svgPaths = QgsApplication::svgPaths();
00779 
00780   for ( int i = 0; i < svgPaths.size(); i++ )
00781   {
00782     QDir dir( svgPaths[i] );
00783     foreach( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
00784     {
00785       svgPaths.insert( i + 1, dir.path() + "/" + item );
00786     }
00787 
00788     foreach( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
00789     {
00790       // TODO test if it is correct SVG
00791       list.append( dir.path() + "/" + item );
00792     }
00793   }
00794   return list;
00795 }
00796 
00797 QString QgsSvgMarkerSymbolLayerV2::symbolNameToPath( QString name )
00798 {
00799   // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
00800 
00801   // we might have a full path...
00802   if ( QFile( name ).exists() )
00803     return QFileInfo( name ).canonicalFilePath();
00804 
00805   // SVG symbol not found - probably a relative path was used
00806 
00807   QStringList svgPaths = QgsApplication::svgPaths();
00808   for ( int i = 0; i < svgPaths.size(); i++ )
00809   {
00810     QgsDebugMsg( "SvgPath: " + svgPaths[i] );
00811     QFileInfo myInfo( name );
00812     QString myFileName = myInfo.fileName(); // foo.svg
00813     QString myLowestDir = myInfo.dir().dirName();
00814     QString myLocalPath = svgPaths[i] + "/" + myLowestDir + "/" + myFileName;
00815 
00816     QgsDebugMsg( "Alternative svg path: " + myLocalPath );
00817     if ( QFile( myLocalPath ).exists() )
00818     {
00819       QgsDebugMsg( "Svg found in alternative path" );
00820       return QFileInfo( myLocalPath ).canonicalFilePath();
00821     }
00822     else if ( myInfo.isRelative() )
00823     {
00824       QFileInfo pfi( QgsProject::instance()->fileName() );
00825       QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
00826       if ( pfi.exists() && QFile( alternatePath ).exists() )
00827       {
00828         QgsDebugMsg( "Svg found in alternative path" );
00829         return QFileInfo( alternatePath ).canonicalFilePath();
00830       }
00831       else
00832       {
00833         QgsDebugMsg( "Svg not found in project path" );
00834       }
00835     }
00836     else
00837     {
00838       //couldnt find the file, no happy ending :-(
00839       QgsDebugMsg( "Computed alternate path but no svg there either" );
00840     }
00841   }
00842   return QString();
00843 }
00844 
00845 QString QgsSvgMarkerSymbolLayerV2::symbolPathToName( QString path )
00846 {
00847   // copied from QgsSymbol::writeXML
00848 
00849   QFileInfo fi( path );
00850   if ( !fi.exists() )
00851     return path;
00852 
00853   path = fi.canonicalFilePath();
00854 
00855   QStringList svgPaths = QgsApplication::svgPaths();
00856 
00857   for ( int i = 0; i < svgPaths.size(); i++ )
00858   {
00859     QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
00860 
00861     if ( !dir.isEmpty() && path.startsWith( dir ) )
00862     {
00863       path = path.mid( dir.size() );
00864       break;
00865     }
00866   }
00867 
00868   return path;
00869 }
00870 
00871 
00873 
00874 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle )
00875 {
00876   mFontFamily = fontFamily;
00877   mChr = chr;
00878   mColor = color;
00879   mAngle = angle;
00880   mSize = pointSize;
00881   mOffset = QPointF( 0, 0 );
00882 }
00883 
00884 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::create( const QgsStringMap& props )
00885 {
00886   QString fontFamily = DEFAULT_FONTMARKER_FONT;
00887   QChar chr = DEFAULT_FONTMARKER_CHR;
00888   double pointSize = DEFAULT_FONTMARKER_SIZE;
00889   QColor color = DEFAULT_FONTMARKER_COLOR;
00890   double angle = DEFAULT_FONTMARKER_ANGLE;
00891 
00892   if ( props.contains( "font" ) )
00893     fontFamily = props["font"];
00894   if ( props.contains( "chr" ) && props["chr"].length() > 0 )
00895     chr = props["chr"].at( 0 );
00896   if ( props.contains( "size" ) )
00897     pointSize = props["size"].toDouble();
00898   if ( props.contains( "color" ) )
00899     color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
00900   if ( props.contains( "angle" ) )
00901     angle = props["angle"].toDouble();
00902 
00903   QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle );
00904   if ( props.contains( "offset" ) )
00905     m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
00906   return m;
00907 }
00908 
00909 QString QgsFontMarkerSymbolLayerV2::layerType() const
00910 {
00911   return "FontMarker";
00912 }
00913 
00914 void QgsFontMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00915 {
00916   mFont = QFont( mFontFamily );
00917   mFont.setPixelSize( context.outputLineWidth( mSize ) );
00918   QFontMetrics fm( mFont );
00919   mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 );
00920 
00921   mOrigSize = mSize; // save in case the size would be data defined
00922 }
00923 
00924 void QgsFontMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00925 {
00926   Q_UNUSED( context );
00927 }
00928 
00929 void QgsFontMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context )
00930 {
00931   QPainter* p = context.renderContext().painter();
00932   QColor penColor = context.selected() ? context.selectionColor() : mColor;
00933   penColor.setAlphaF( context.alpha() );
00934   p->setPen( penColor );
00935   p->setFont( mFont );
00936 
00937 
00938   p->save();
00939   QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) );
00940   if ( mAngle )
00941     outputOffset = _rotatedOffset( outputOffset, mAngle );
00942   p->translate( point + outputOffset );
00943 
00944   if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
00945   {
00946     double s = mSize / mOrigSize;
00947     p->scale( s, s );
00948   }
00949 
00950   if ( mAngle != 0 )
00951     p->rotate( mAngle );
00952 
00953   p->drawText( -mChrOffset, mChr );
00954   p->restore();
00955 }
00956 
00957 QgsStringMap QgsFontMarkerSymbolLayerV2::properties() const
00958 {
00959   QgsStringMap props;
00960   props["font"] = mFontFamily;
00961   props["chr"] = mChr;
00962   props["size"] = QString::number( mSize );
00963   props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
00964   props["angle"] = QString::number( mAngle );
00965   props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
00966   return props;
00967 }
00968 
00969 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::clone() const
00970 {
00971   QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( mFontFamily, mChr, mSize, mColor, mAngle );
00972   m->setOffset( mOffset );
00973   return m;
00974 }
00975 
00976 void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00977 {
00978   // <Graphic>
00979   QDomElement graphicElem = doc.createElement( "se:Graphic" );
00980   element.appendChild( graphicElem );
00981 
00982   QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
00983   int markIndex = mChr.unicode();
00984   QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );
00985 
00986   // <Rotation>
00987   QString angleFunc;
00988   bool ok;
00989   double angle = props.value( "angle", "0" ).toDouble( &ok );
00990   if ( !ok )
00991   {
00992     angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
00993   }
00994   else if ( angle + mAngle != 0 )
00995   {
00996     angleFunc = QString::number( angle + mAngle );
00997   }
00998   QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
00999 
01000   // <Displacement>
01001   QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
01002 }
01003 
01004 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::createFromSld( QDomElement &element )
01005 {
01006   QgsDebugMsg( "Entered." );
01007 
01008   QDomElement graphicElem = element.firstChildElement( "Graphic" );
01009   if ( graphicElem.isNull() )
01010     return NULL;
01011 
01012   QString name, format;
01013   QColor color;
01014   double size;
01015   int chr;
01016 
01017   if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
01018     return NULL;
01019 
01020   if ( !name.startsWith( "ttf://" ) || format != "ttf" )
01021     return NULL;
01022 
01023   QString fontFamily = name.mid( 6 );
01024 
01025   double angle = 0.0;
01026   QString angleFunc;
01027   if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
01028   {
01029     bool ok;
01030     double d = angleFunc.toDouble( &ok );
01031     if ( ok )
01032       angle = d;
01033   }
01034 
01035   QPointF offset;
01036   QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset );
01037 
01038   QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color );
01039   m->setAngle( angle );
01040   m->setOffset( offset );
01041   return m;
01042 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines