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