Quantum GIS API Documentation  master-ce49b66
src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsellipsesymbollayerv2.cpp
00003     ---------------------
00004     begin                : June 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 "qgsellipsesymbollayerv2.h"
00016 #include "qgsexpression.h"
00017 #include "qgsfeature.h"
00018 #include "qgsrendercontext.h"
00019 #include "qgsvectorlayer.h"
00020 #include "qgslogger.h"
00021 
00022 #include <QPainter>
00023 #include <QSet>
00024 #include <QDomDocument>
00025 #include <QDomElement>
00026 
00027 QgsEllipseSymbolLayerV2::QgsEllipseSymbolLayerV2(): mSymbolName( "circle" ), mSymbolWidth( 4 ), mSymbolWidthUnit( QgsSymbolV2::MM ), mSymbolHeight( 3 ),
00028     mSymbolHeightUnit( QgsSymbolV2::MM ), mFillColor( Qt::white ), mOutlineColor( Qt::black ), mOutlineWidth( 0 ), mOutlineWidthUnit( QgsSymbolV2::MM )
00029 {
00030   mPen.setColor( mOutlineColor );
00031   mPen.setWidth( 1.0 );
00032   mPen.setJoinStyle( Qt::MiterJoin );
00033   mBrush.setColor( mFillColor );
00034   mBrush.setStyle( Qt::SolidPattern );
00035   mOffset = QPointF( 0, 0 );
00036 
00037   mAngle = 0;
00038 }
00039 
00040 QgsEllipseSymbolLayerV2::~QgsEllipseSymbolLayerV2()
00041 {
00042 }
00043 
00044 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::create( const QgsStringMap& properties )
00045 {
00046   QgsEllipseSymbolLayerV2* layer = new QgsEllipseSymbolLayerV2();
00047   if ( properties.contains( "symbol_name" ) )
00048   {
00049     layer->setSymbolName( properties[ "symbol_name" ] );
00050   }
00051   if ( properties.contains( "symbol_width" ) )
00052   {
00053     layer->setSymbolWidth( properties["symbol_width"].toDouble() );
00054   }
00055   if ( properties.contains( "symbol_width_unit" ) )
00056   {
00057     layer->setSymbolWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["symbol_width_unit"] ) );
00058   }
00059   if ( properties.contains( "symbol_height" ) )
00060   {
00061     layer->setSymbolHeight( properties["symbol_height"].toDouble() );
00062   }
00063   if ( properties.contains( "symbol_height_unit" ) )
00064   {
00065     layer->setSymbolHeightUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["symbol_height_unit"] ) );
00066   }
00067   if ( properties.contains( "angle" ) )
00068   {
00069     layer->setAngle( properties["angle"].toDouble() );
00070   }
00071   if ( properties.contains( "outline_width" ) )
00072   {
00073     layer->setOutlineWidth( properties["outline_width"].toDouble() );
00074   }
00075   if ( properties.contains( "outline_width_unit" ) )
00076   {
00077     layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
00078   }
00079   if ( properties.contains( "fill_color" ) )
00080   {
00081     layer->setFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["fill_color"] ) );
00082   }
00083   if ( properties.contains( "outline_color" ) )
00084   {
00085     layer->setOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) );
00086   }
00087   if ( properties.contains( "offset" ) )
00088   {
00089     layer->setOffset( QgsSymbolLayerV2Utils::decodePoint( properties["offset"] ) );
00090   }
00091   if ( properties.contains( "offset_unit" ) )
00092   {
00093     layer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
00094   }
00095 
00096   //data defined properties
00097   if ( properties.contains( "width_expression" ) )
00098   {
00099     layer->setDataDefinedProperty( "width", properties["width_expression"] );
00100   }
00101   if ( properties.contains( "height_expression" ) )
00102   {
00103     layer->setDataDefinedProperty( "height", properties["height_expression"] );
00104   }
00105   if ( properties.contains( "rotation_expression" ) )
00106   {
00107     layer->setDataDefinedProperty( "rotation", properties["rotation_expression"] );
00108   }
00109   if ( properties.contains( "outline_width_expression" ) )
00110   {
00111     layer->setDataDefinedProperty( "outline_width", properties[ "outline_width_expression" ] );
00112   }
00113   if ( properties.contains( "fill_color_expression" ) )
00114   {
00115     layer->setDataDefinedProperty( "fill_color", properties["fill_color_expression"] );
00116   }
00117   if ( properties.contains( "outline_color_expression" ) )
00118   {
00119     layer->setDataDefinedProperty( "outline_color", properties["outline_color_expression"] );
00120   }
00121   if ( properties.contains( "symbol_name_expression" ) )
00122   {
00123     layer->setDataDefinedProperty( "symbol_name", properties["symbol_name_expression"] );
00124   }
00125   if ( properties.contains( "offset_expression" ) )
00126   {
00127     layer->setDataDefinedProperty( "offset", properties["offset_expression"] );
00128   }
00129 
00130   //compatibility with old project file format
00131   if ( !properties["width_field"].isEmpty() )
00132   {
00133     layer->setDataDefinedProperty( "width", properties["width_field"] );
00134   }
00135   if ( !properties["height_field"].isEmpty() )
00136   {
00137     layer->setDataDefinedProperty( "height", properties["height_field"] );
00138   }
00139   if ( !properties["rotation_field"].isEmpty() )
00140   {
00141     layer->setDataDefinedProperty( "rotation", properties["rotation_field"] );
00142   }
00143   if ( !properties["outline_width_field"].isEmpty() )
00144   {
00145     layer->setDataDefinedProperty( "outline_width", properties[ "outline_width_field" ] );
00146   }
00147   if ( !properties["fill_color_field"].isEmpty() )
00148   {
00149     layer->setDataDefinedProperty( "fill_color", properties["fill_color_field"] );
00150   }
00151   if ( !properties["outline_color_field"].isEmpty() )
00152   {
00153     layer->setDataDefinedProperty( "outline_color", properties["outline_color_field"] );
00154   }
00155   if ( !properties["symbol_name_field"].isEmpty() )
00156   {
00157     layer->setDataDefinedProperty( "symbol_name", properties["symbol_name_field"] );
00158   }
00159 
00160   return layer;
00161 }
00162 
00163 void QgsEllipseSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context )
00164 {
00165   QgsExpression* outlineWidthExpression = expression( "outline_width" );
00166   QgsExpression* fillColorExpression = expression( "fill_color" );
00167   QgsExpression* outlineColorExpression = expression( "outline_color" );
00168   QgsExpression* widthExpression = expression( "width" );
00169   QgsExpression* heightExpression = expression( "height" );
00170   QgsExpression* symbolNameExpression = expression( "symbol_name" );
00171   QgsExpression* rotationExpression = expression( "rotation" );
00172 
00173   if ( outlineWidthExpression )
00174   {
00175     double width = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
00176     width *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit );
00177     mPen.setWidthF( width );
00178   }
00179   if ( fillColorExpression )
00180   {
00181     QString colorString = fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
00182     mBrush.setColor( QColor( colorString ) );
00183   }
00184   if ( outlineColorExpression )
00185   {
00186     QString colorString = outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
00187     mPen.setColor( QColor( colorString ) );
00188   }
00189   if ( widthExpression || heightExpression || symbolNameExpression )
00190   {
00191     QString symbolName =  mSymbolName;
00192     if ( symbolNameExpression )
00193     {
00194       symbolName = symbolNameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
00195     }
00196     preparePath( symbolName, context, context.feature() );
00197   }
00198 
00199   //offset
00200   double offsetX = 0;
00201   double offsetY = 0;
00202   markerOffset( context, offsetX, offsetY );
00203   QPointF off( offsetX, offsetY );
00204 
00205   QPainter* p = context.renderContext().painter();
00206   if ( !p )
00207   {
00208     return;
00209   }
00210 
00211   //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
00212   double rotation = 0.0;
00213   if ( rotationExpression )
00214   {
00215     rotation = rotationExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
00216   }
00217   else if ( !qgsDoubleNear( mAngle, 0.0 ) )
00218   {
00219     rotation = mAngle;
00220   }
00221   if ( rotation )
00222     off = _rotatedOffset( off, rotation );
00223 
00224   QMatrix transform;
00225   transform.translate( point.x() + off.x(), point.y() + off.y() );
00226   if ( !qgsDoubleNear( rotation, 0.0 ) )
00227   {
00228     transform.rotate( rotation );
00229   }
00230 
00231   p->setPen( mPen );
00232   p->setBrush( mBrush );
00233   p->drawPath( transform.map( mPainterPath ) );
00234 }
00235 
00236 QString QgsEllipseSymbolLayerV2::layerType() const
00237 {
00238   return "EllipseMarker";
00239 }
00240 
00241 void QgsEllipseSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00242 {
00243   if ( !context.feature() || !hasDataDefinedProperty() )
00244   {
00245     preparePath( mSymbolName, context );
00246   }
00247   mPen.setColor( mOutlineColor );
00248   mPen.setWidthF( mOutlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) );
00249   mBrush.setColor( mFillColor );
00250   prepareExpressions( context.layer() );
00251 }
00252 
00253 void QgsEllipseSymbolLayerV2::stopRender( QgsSymbolV2RenderContext & )
00254 {
00255 }
00256 
00257 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::clone() const
00258 {
00259   return QgsEllipseSymbolLayerV2::create( properties() );
00260 }
00261 
00262 void QgsEllipseSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00263 {
00264   QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" );
00265   if ( !props.value( "uom", "" ).isEmpty() )
00266     symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
00267   element.appendChild( symbolizerElem );
00268 
00269   // <Geometry>
00270   QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
00271 
00272   writeSldMarker( doc, symbolizerElem, props );
00273 }
00274 
00275 void QgsEllipseSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00276 {
00277   // <Graphic>
00278   QDomElement graphicElem = doc.createElement( "se:Graphic" );
00279   element.appendChild( graphicElem );
00280 
00281   QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mFillColor, mOutlineColor, mOutlineWidth, mSymbolWidth );
00282 
00283   // store w/h factor in a <VendorOption>
00284   double widthHeightFactor = mSymbolWidth / mSymbolHeight;
00285   QDomElement factorElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
00286   graphicElem.appendChild( factorElem );
00287 
00288   // <Rotation>
00289   const QgsExpression* rotationExpression = dataDefinedProperty( "rotation" );
00290   QString angleFunc = props.value( "angle", "" );
00291   if ( angleFunc.isEmpty() )  // symbol has no angle set
00292   {
00293 
00294     if ( rotationExpression )
00295       angleFunc = rotationExpression->dump();
00296     else if ( !qgsDoubleNear( mAngle, 0.0 ) )
00297       angleFunc = QString::number( mAngle );
00298   }
00299   else if ( rotationExpression )
00300   {
00301     // the symbol has an angle and the symbol layer have a rotation
00302     // property set
00303     angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( rotationExpression->dump() );
00304   }
00305   else if ( !qgsDoubleNear( mAngle, 0.0 ) )
00306   {
00307     // both the symbol and the symbol layer have angle value set
00308     bool ok;
00309     double angle = angleFunc.toDouble( &ok );
00310     if ( !ok )
00311     {
00312       // its a string (probably a property name or a function)
00313       angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mAngle );
00314     }
00315     else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
00316     {
00317       // it's a double value
00318       angleFunc = QString::number( angle + mAngle );
00319     }
00320   }
00321   QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
00322 }
00323 
00324 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::createFromSld( QDomElement &element )
00325 {
00326   QgsDebugMsg( "Entered." );
00327 
00328   QDomElement graphicElem = element.firstChildElement( "Graphic" );
00329   if ( graphicElem.isNull() )
00330     return NULL;
00331 
00332   QString name = "circle";
00333   QColor fillColor, borderColor;
00334   double borderWidth, size;
00335   double widthHeightFactor = 1.0;
00336 
00337   QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
00338   for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
00339   {
00340     if ( it.key() == "widthHeightFactor" )
00341     {
00342       bool ok;
00343       double v = it.value().toDouble( &ok );
00344       if ( ok && !qgsDoubleNear( v, 0.0 ) && v > 0 )
00345         widthHeightFactor = v;
00346     }
00347   }
00348 
00349   if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, borderColor, borderWidth, size ) )
00350     return NULL;
00351 
00352   double angle = 0.0;
00353   QString angleFunc;
00354   if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
00355   {
00356     bool ok;
00357     double d = angleFunc.toDouble( &ok );
00358     if ( ok )
00359       angle = d;
00360   }
00361 
00362   QgsEllipseSymbolLayerV2 *m = new QgsEllipseSymbolLayerV2();
00363   m->setSymbolName( name );
00364   m->setFillColor( fillColor );
00365   m->setOutlineColor( borderColor );
00366   m->setOutlineWidth( borderWidth );
00367   m->setSymbolWidth( size );
00368   m->setSymbolHeight( size / widthHeightFactor );
00369   m->setAngle( angle );
00370   return m;
00371 }
00372 
00373 QgsStringMap QgsEllipseSymbolLayerV2::properties() const
00374 {
00375   QgsStringMap map;
00376   map["symbol_name"] = mSymbolName;
00377   map["symbol_width"] = QString::number( mSymbolWidth );
00378   map["symbol_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSymbolWidthUnit );
00379   map["symbol_height"] = QString::number( mSymbolHeight );
00380   map["symbol_height_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSymbolHeightUnit );
00381   map["angle"] = QString::number( mAngle );
00382   map["outline_width"] = QString::number( mOutlineWidth );
00383   map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
00384   map["fill_color"] = QgsSymbolLayerV2Utils::encodeColor( mFillColor );
00385   map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mOutlineColor );
00386   map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
00387   map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
00388   saveDataDefinedProperties( map );
00389   return map;
00390 }
00391 
00392 bool QgsEllipseSymbolLayerV2::hasDataDefinedProperty() const
00393 {
00394   return ( dataDefinedProperty( "width" ) || dataDefinedProperty( "height" ) || dataDefinedProperty( "rotation" )
00395            || dataDefinedProperty( "outline_width" ) || dataDefinedProperty( "fill_color" ) || dataDefinedProperty( "outline_color" )
00396            || dataDefinedProperty( "symbol_name" ) || dataDefinedProperty( "offset" ) );
00397 }
00398 
00399 void QgsEllipseSymbolLayerV2::preparePath( const QString& symbolName, QgsSymbolV2RenderContext& context, const QgsFeature* f )
00400 {
00401   mPainterPath = QPainterPath();
00402   const QgsRenderContext& ct = context.renderContext();
00403 
00404   double width = 0;
00405 
00406   QgsExpression* widthExpression = expression( "width" );
00407   if ( widthExpression ) //1. priority: data defined setting on symbol layer level
00408   {
00409     width = widthExpression->evaluate( const_cast<QgsFeature*>( f ) ).toDouble();
00410   }
00411   else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
00412   {
00413     width = mSize;
00414   }
00415   else //3. priority: global width setting
00416   {
00417     width = mSymbolWidth;
00418   }
00419   width *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( ct, mSymbolWidthUnit );
00420 
00421   double height = 0;
00422   QgsExpression* heightExpression = expression( "height" );
00423   if ( heightExpression ) //1. priority: data defined setting on symbol layer level
00424   {
00425     height =  heightExpression->evaluate( const_cast<QgsFeature*>( f ) ).toDouble();
00426   }
00427   else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
00428   {
00429     height = mSize;
00430   }
00431   else //3. priority: global height setting
00432   {
00433     height = mSymbolHeight;
00434   }
00435   height *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( ct, mSymbolHeightUnit );
00436 
00437   if ( symbolName == "circle" )
00438   {
00439     mPainterPath.addEllipse( QRectF( -width / 2.0, -height / 2.0, width, height ) );
00440   }
00441   else if ( symbolName == "rectangle" )
00442   {
00443     mPainterPath.addRect( QRectF( -width / 2.0, -height / 2.0, width, height ) );
00444   }
00445   else if ( symbolName == "cross" )
00446   {
00447     mPainterPath.moveTo( 0, -height / 2.0 );
00448     mPainterPath.lineTo( 0, height / 2.0 );
00449     mPainterPath.moveTo( -width / 2.0, 0 );
00450     mPainterPath.lineTo( width / 2.0, 0 );
00451   }
00452   else if ( symbolName == "triangle" )
00453   {
00454     mPainterPath.moveTo( 0, -height / 2.0 );
00455     mPainterPath.lineTo( -width / 2.0, height / 2.0 );
00456     mPainterPath.lineTo( width / 2.0, height / 2.0 );
00457     mPainterPath.lineTo( 0, -height / 2.0 );
00458   }
00459 }
00460 
00461 void QgsEllipseSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit )
00462 {
00463   mSymbolWidthUnit = unit;
00464   mSymbolHeightUnit = unit;
00465   mOutlineWidthUnit = unit;
00466 }
00467 
00468 QgsSymbolV2::OutputUnit QgsEllipseSymbolLayerV2::outputUnit() const
00469 {
00470   QgsSymbolV2::OutputUnit unit = mSymbolWidthUnit;
00471   if ( mSymbolHeightUnit != unit || mOutlineWidthUnit != unit )
00472   {
00473     return QgsSymbolV2::Mixed;
00474   }
00475   return unit;
00476 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines