585 changes: 585 additions & 0 deletions src/core/qgsexpression.cpp

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ class CORE_EXPORT QgsExpression
static const char* BinaryOperatorText[];
static const char* UnaryOperatorText[];

static const char* BinaryOgcOperatorText[];
static const char* UnaryOgcOperatorText[];

typedef QVariant( *FcnEval )( const QVariantList& values, QgsFeature* f, QgsExpression* parent );

Expand Down Expand Up @@ -226,6 +228,9 @@ class CORE_EXPORT QgsExpression

virtual QString dump() const = 0;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const { Q_UNUSED( doc ); Q_UNUSED( element ); }
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const = 0;
virtual bool needsGeometry() const = 0;

Expand All @@ -243,6 +248,8 @@ class CORE_EXPORT QgsExpression
QList<Node*> list() { return mList; }

virtual QString dump() const;
virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;

protected:
QList<Node*> mList;
};
Expand All @@ -259,6 +266,10 @@ class CORE_EXPORT QgsExpression
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return mOperand->referencedColumns(); }
virtual bool needsGeometry() const { return mOperand->needsGeometry(); }
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand All @@ -281,6 +292,10 @@ class CORE_EXPORT QgsExpression
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); }
virtual bool needsGeometry() const { return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); }
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand Down Expand Up @@ -308,6 +323,9 @@ class CORE_EXPORT QgsExpression
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;

virtual QStringList referencedColumns() const { QStringList lst( mNode->referencedColumns() ); foreach( Node* n, mList->list() ) lst.append( n->referencedColumns() ); return lst; }
virtual bool needsGeometry() const { bool needs = false; foreach( Node* n, mList->list() ) needs |= n->needsGeometry(); return needs; }
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand All @@ -331,6 +349,10 @@ class CORE_EXPORT QgsExpression
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { QStringList lst; if ( !mArgs ) return lst; foreach( Node* n, mArgs->list() ) lst.append( n->referencedColumns() ); return lst; }
virtual bool needsGeometry() const { bool needs = BuiltinFunctions()[mFnIndex].mUsesGeometry; if ( mArgs ) { foreach( Node* n, mArgs->list() ) needs |= n->needsGeometry(); } return needs; }
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand All @@ -351,6 +373,10 @@ class CORE_EXPORT QgsExpression
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return QStringList(); }
virtual bool needsGeometry() const { return false; }
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand All @@ -369,6 +395,10 @@ class CORE_EXPORT QgsExpression
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return QStringList( mName ); }
virtual bool needsGeometry() const { return false; }
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand Down Expand Up @@ -399,6 +429,9 @@ class CORE_EXPORT QgsExpression
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;

virtual QStringList referencedColumns() const;
virtual bool needsGeometry() const;
virtual void accept( Visitor& v ) { v.visit( this ); }
Expand Down Expand Up @@ -428,7 +461,13 @@ class CORE_EXPORT QgsExpression
/** entry function for the visitor pattern */
void acceptVisitor( Visitor& v );

// convert from/to OGC Filter
void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression* createFromOgcFilter( QDomElement &element );

protected:
// internally used to create an empty expression
QgsExpression() : mRootNode( NULL ), mRowNumber( 0 ), mCalc( NULL ) {}

QString mExpression;
Node* mRootNode;
Expand Down
139 changes: 139 additions & 0 deletions src/core/qgsmaplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,146 @@ QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag
return myErrorMessage;
}

QString QgsMapLayer::saveSldStyle( const QString theURI, bool & theResultFlag )
{
QDomDocument myDocument = QDomDocument();

QDomNode header = myDocument.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" );
myDocument.appendChild( header );

// Create the root element
QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
root.setAttribute( "version", "1.1.0" );
root.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" );
root.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
root.setAttribute( "xmlns:se", "http://www.opengis.net/se" );
root.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
root.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
myDocument.appendChild( root );

// Create the NamedLayer element
QDomElement namedLayerNode = myDocument.createElement( "NamedLayer" );
root.appendChild( namedLayerNode );

QString errorMsg;
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
if ( !vlayer )
{
theResultFlag = false;
return tr( "Could not save symbology because:\n%1" ).arg( "Non-vector layers not supported yet" );
}

if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg ) )
{
theResultFlag = false;
return tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
}

// check if the uri is a file or ends with .sld,
// which indicates that it should become one
QString filename;
if ( vlayer->providerType() == "ogr" )
{
QStringList theURIParts = theURI.split( "|" );
filename = theURIParts[0];
}
else if ( vlayer->providerType() == "delimitedtext" )
{
filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
}
else
{
filename = theURI;
}

QFileInfo myFileInfo( filename );
if ( myFileInfo.exists() || filename.endsWith( ".sld", Qt::CaseInsensitive ) )
{
QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
if ( !myDirInfo.isWritable() )
{
return tr( "The directory containing your dataset needs to be writable!" );
}

// now construct the file name for our .sld style file
QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";

QFile myFile( myFileName );
if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
{
QTextStream myFileStream( &myFile );
// save as utf-8 with 2 spaces for indents
myDocument.save( myFileStream, 2 );
myFile.close();
theResultFlag = true;
return tr( "Created default style file as %1" ).arg( myFileName );
}
}

theResultFlag = false;
return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
}

QString QgsMapLayer::loadSldStyle( const QString theURI, bool &theResultFlag )
{
QgsDebugMsg( "Entered." );

theResultFlag = false;

QDomDocument myDocument;

// location of problem associated with errorMsg
int line, column;
QString myErrorMessage;

QFile myFile( theURI );
if ( myFile.open( QFile::ReadOnly ) )
{
// read file
theResultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
if ( !theResultFlag )
myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
myFile.close();
}
else
{
myErrorMessage = tr( "Unable to open file %1" ).arg( theURI );
}

if ( !theResultFlag )
{
return myErrorMessage;
}

// check for root SLD element
QDomElement myRoot = myDocument.firstChildElement( "StyledLayerDescriptor" );
if ( myRoot.isNull() )
{
myErrorMessage = QString( "Error: StyledLayerDescriptor element not found in %1" ).arg( theURI );
theResultFlag = false;
return myErrorMessage;
}

// now get the style node out and pass it over to the layer
// to deserialise...
QDomElement namedLayerElem = myRoot.firstChildElement( "NamedLayer" );
if ( namedLayerElem.isNull() )
{
myErrorMessage = QString( "Info: NamedLayer element not found." );
theResultFlag = false;
return myErrorMessage;
}

QString errorMsg;
theResultFlag = readSld( namedLayerElem, errorMsg );
if ( !theResultFlag )
{
myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
return myErrorMessage;
}

return "";
}


QUndoStack* QgsMapLayer::undoStack()
Expand Down
7 changes: 7 additions & 0 deletions src/core/qgsmaplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,13 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
virtual QString saveNamedStyle( const QString theURI, bool & theResultFlag );

virtual QString saveSldStyle( const QString theURI, bool & theResultFlag );
virtual QString loadSldStyle( const QString theURI, bool &theResultFlag );

virtual bool readSld( const QDomNode& node, QString& errorMessage )
{ Q_UNUSED( node ); errorMessage = QString( "Layer type %1 not supported" ).arg( type() ); return false; }


/** Read the symbology for the current layer from the Dom node supplied.
* @param node node that will contain the symbology definition for this layer.
* @param errorMessage reference to string that will be updated with any error messages
Expand Down
47 changes: 47 additions & 0 deletions src/core/qgsvectorlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3391,6 +3391,53 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
return true;
}

bool QgsVectorLayer::readSld( const QDomNode& node, QString& errorMessage )
{
// get the Name element
QDomElement nameElem = node.firstChildElement( "Name" );
if ( nameElem.isNull() )
{
errorMessage = "Warning: Name element not found within NamedLayer while it's required.";
}

if ( hasGeometryType() )
{
setUsingRendererV2( true );

QgsFeatureRendererV2* r = QgsFeatureRendererV2::loadSld( node, geometryType(), errorMessage );
if ( !r )
return false;

setRendererV2( r );
}
return true;
}


bool QgsVectorLayer::writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const
{
Q_UNUSED( errorMessage );

// store the Name element
QDomElement nameNode = doc.createElement( "se:Name" );
nameNode.appendChild( doc.createTextNode( name() ) );
node.appendChild( nameNode );

if ( hasGeometryType() )
{
if ( mUsingRendererV2 )
{
node.appendChild( mRendererV2->writeSld( doc, *this ) );
}
else
{
node.appendChild( doc.createComment( "Old Renderer not supported yet" ) );
return false;
}
}
return true;
}


bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, QgsGeometry* geom )
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgsvectorlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
*/
bool writeSymbology( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const;

bool writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const;
bool readSld( const QDomNode& node, QString& errorMessage );

/**
* Number of features in the layer. This is necessary if features are
Expand Down
48 changes: 48 additions & 0 deletions src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,37 @@ QString QgsRendererCategoryV2::dump()
return QString( "%1::%2::%3\n" ).arg( mValue.toString() ).arg( mLabel ).arg( mSymbol->dump() );
}

void QgsRendererCategoryV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
if ( !mSymbol || props.value( "attribute", "" ).isEmpty() )
return;

QDomElement ruleElem = doc.createElement( "se:Rule" );
element.appendChild( ruleElem );

QString valueStr = QString( "value: %1" ).arg( mValue.toString() );

QDomElement nameElem = doc.createElement( "se:Name" );
nameElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : valueStr ) );
ruleElem.appendChild( nameElem );

QString descrName = props.value( "version", "1.1" ) < "1.1" ? "Abstract" : "se:Description";
QString descrValue = QString( "Categorized symbol rendererV2 - %1" ).arg( valueStr );

QDomElement descrElem = doc.createElement( descrName );
descrElem.appendChild( doc.createTextNode( descrValue ) );
ruleElem.appendChild( descrElem );

// create the ogc:Filter for the range
QDomElement filterElem = doc.createElement( "ogc:Filter" );

QString filterFunc = QString( "%1 = '%2'" )
.arg( props[ "attribute" ] ).arg( mValue.toString().replace( "'", "''" ) );
QgsSymbolLayerV2Utils::createFunctionElement( doc, filterElem, filterFunc );

mSymbol->toSld( doc, ruleElem, props );
}

///////////////////

QgsCategorizedSymbolRendererV2::QgsCategorizedSymbolRendererV2( QString attrName, QgsCategoryList categories )
Expand Down Expand Up @@ -331,6 +362,23 @@ QgsFeatureRendererV2* QgsCategorizedSymbolRendererV2::clone()
return r;
}

void QgsCategorizedSymbolRendererV2::toSld( QDomDocument &doc, QDomElement &element ) const
{
QgsStringMap props;
props[ "attribute" ] = mAttrName;
if ( !mRotationField.isEmpty() )
props[ "angle" ] = QString( mRotationField ).append( "\"" ).prepend( "\"" );
if ( !mSizeScaleField.isEmpty() )
props[ "scale" ] = QString( mSizeScaleField ).append( "\"" ).prepend( "\"" );

// create a Rule for each range
for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); it++ )
{
QgsStringMap catProps( props );
it->toSld( doc, element, catProps );
}
}

QgsSymbolV2List QgsCategorizedSymbolRendererV2::symbols()
{
QgsSymbolV2List lst;
Expand Down
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef QGSCATEGORIZEDSYMBOLRENDERERV2_H
#define QGSCATEGORIZEDSYMBOLRENDERERV2_H

#include "qgssymbolv2.h"
#include "qgsrendererv2.h"

#include <QHash>
Expand Down Expand Up @@ -32,6 +33,8 @@ class CORE_EXPORT QgsRendererCategoryV2
// debugging
QString dump();

void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const;

protected:
QVariant mValue;
QgsSymbolV2* mSymbol;
Expand Down Expand Up @@ -60,6 +63,8 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2

virtual QgsFeatureRendererV2* clone();

virtual void toSld( QDomDocument& doc, QDomElement &element ) const;

//! returns bitwise OR-ed capabilities of the renderer
//! \note added in 2.0
virtual int capabilities() { return SymbolLevels | RotationField; }
Expand Down
113 changes: 113 additions & 0 deletions src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
#include "qgsfeature.h"
#include "qgsrendercontext.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"

#include <QPainter>
#include <QSet>
#include <QDomDocument>
#include <QDomElement>

QgsEllipseSymbolLayerV2::QgsEllipseSymbolLayerV2(): mSymbolName( "circle" ), mSymbolWidth( 4 ), mSymbolHeight( 3 ),
mFillColor( Qt::black ), mOutlineColor( Qt::white ), mOutlineWidth( 0 )
Expand Down Expand Up @@ -187,6 +191,115 @@ QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::clone() const
return QgsEllipseSymbolLayerV2::create( properties() );
}

void QgsEllipseSymbolLayerV2::toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

writeSldMarker( doc, symbolizerElem, props );
}

void QgsEllipseSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
// <Graphic>
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );

QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mFillColor, mOutlineColor, mOutlineWidth, mSymbolWidth );

// store w/h factor in a <VendorOption>
double widthHeightFactor = mSymbolWidth / mSymbolHeight;
QDomElement factorElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
graphicElem.appendChild( factorElem );

// <Rotation>
QString angleFunc = props.value( "angle", "" );
if ( angleFunc.isEmpty() ) // symbol has no angle set
{
if ( !mRotationField.isEmpty() )
angleFunc = mRotationField;
else if ( !doubleNear( mAngle, 0.0 ) )
angleFunc = QString::number( mAngle );
}
else if ( !mRotationField.isEmpty() )
{
// the symbol has an angle and the symbol layer have a rotation
// property set
angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mRotationField );
}
else if ( !doubleNear( mAngle, 0.0 ) )
{
// both the symbol and the symbol layer have angle value set
bool ok;
double angle = angleFunc.toDouble( &ok );
if ( !ok )
{
// its a string (probably a property name or a function)
angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mAngle );
}
else if ( !doubleNear( angle + mAngle, 0.0 ) )
{
// it's a double value
angleFunc = QString::number( angle + mAngle );
}
}
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
}

QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QDomElement graphicElem = element.firstChildElement( "Graphic" );
if( graphicElem.isNull() )
return NULL;

QString name = "circle";
QColor color, borderColor;
double borderWidth, size;
double widthHeightFactor = 1.0;

QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
{
if ( it.key() == "widthHeightFactor" )
{
bool ok;
double v = it.value().toDouble( &ok );
if ( ok && !doubleNear( v, 0.0 ) && v > 0 )
widthHeightFactor = v;
}
}

if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderWidth, size ) )
return NULL;

double angle = 0.0;
QString angleFunc;
if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
{
bool ok;
double d = angleFunc.toDouble( &ok );
if ( ok )
angle = d;
}

QgsEllipseSymbolLayerV2 *m = new QgsEllipseSymbolLayerV2();
m->setSymbolName( name );
m->setColor( color );
m->setOutlineColor( borderColor );
m->setOutlineWidth( borderWidth );
m->setSymbolWidth( size );
m->setSymbolHeight( size / widthHeightFactor );
m->setAngle( angle );
return m;
}

QgsStringMap QgsEllipseSymbolLayerV2::properties() const
{
QgsStringMap map;
Expand Down
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgsellipsesymbollayerv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class CORE_EXPORT QgsEllipseSymbolLayerV2: public QgsMarkerSymbolLayerV2
~QgsEllipseSymbolLayerV2();

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

void renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context );
QString layerType() const;
Expand All @@ -20,6 +21,9 @@ class CORE_EXPORT QgsEllipseSymbolLayerV2: public QgsMarkerSymbolLayerV2
QgsSymbolLayerV2* clone() const;
QgsStringMap properties() const;

void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const;
void writeSldMarker( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const;

void setSymbolName( const QString& name ) { mSymbolName = name; }
QString symbolName() const { return mSymbolName; }

Expand Down
291 changes: 291 additions & 0 deletions src/core/symbology-ng/qgsfillsymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
#include "qgsrendercontext.h"
#include "qgsproject.h"
#include "qgssvgcache.h"
#include "qgslogger.h"

#include <QPainter>
#include <QFile>
#include <QSvgRenderer>
#include <QDomDocument>
#include <QDomElement>

QgsSimpleFillSymbolLayerV2::QgsSimpleFillSymbolLayerV2( QColor color, Qt::BrushStyle style, QColor borderColor, Qt::PenStyle borderStyle, double borderWidth )
: mBrushStyle( style ), mBorderColor( borderColor ), mBorderStyle( borderStyle ), mBorderWidth( borderWidth )
Expand Down Expand Up @@ -117,6 +120,63 @@ QgsSymbolLayerV2* QgsSimpleFillSymbolLayerV2::clone() const
return sl;
}

void QgsSimpleFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
if ( mBrushStyle == Qt::NoBrush && mBorderStyle == Qt::NoPen )
return;

QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

if ( mBrushStyle != Qt::NoBrush )
{
// <Fill>
QDomElement fillElem = doc.createElement( "se:Fill" );
symbolizerElem.appendChild( fillElem );
QgsSymbolLayerV2Utils::fillToSld( doc, fillElem, mBrushStyle, mColor );
}

if ( mBorderStyle != Qt::NoPen )
{
// <Stroke>
QDomElement strokeElem = doc.createElement( "se:Stroke" );
symbolizerElem.appendChild( strokeElem );
QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, mBorderStyle, mBorderColor, mBorderWidth );
}

// <se:Displacement>
QgsSymbolLayerV2Utils::createDisplacementElement( doc, symbolizerElem, mOffset );
}

QgsSymbolLayerV2* QgsSimpleFillSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QColor color, borderColor;
Qt::BrushStyle fillStyle;
Qt::PenStyle borderStyle;
double borderWidth;

QDomElement fillElem = element.firstChildElement( "Fill" );
QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );

QDomElement strokeElem = element.firstChildElement( "Stroke" );
QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );

QPointF offset;
QgsSymbolLayerV2Utils::displacementFromSldElement( element, offset );

QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
sl->setOffset( offset );
return sl;
}


//QgsImageFillSymbolLayer

QgsImageFillSymbolLayer::QgsImageFillSymbolLayer(): mOutlineWidth( 0.0 ), mOutline( 0 )
Expand Down Expand Up @@ -380,6 +440,123 @@ QgsSymbolLayerV2* QgsSVGFillSymbolLayer::clone() const
return clonedLayer;
}

void QgsSVGFillSymbolLayer::toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

QDomElement fillElem = doc.createElement( "se:Fill" );
symbolizerElem.appendChild( fillElem );

QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
fillElem.appendChild( graphicFillElem );

QDomElement graphicElem = doc.createElement( "se:Graphic" );
graphicFillElem.appendChild( graphicElem );

if ( !mSvgFilePath.isEmpty() )
{
QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mSvgFillColor, mPatternWidth );
}
else
{
// TODO: create svg from data
// <se:InlineContent>
symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
}

if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
{
QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
}

// <Rotation>
QString angleFunc;
bool ok;
double angle = props.value( "angle", "0" ).toDouble( &ok );
if ( !ok )
{
angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
}
else if ( angle + mAngle != 0 )
{
angleFunc = QString::number( angle + mAngle );
}
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );

if ( mOutline )
{
// the outline sub symbol should be stored within the Stroke element,
// but it will be stored in a separated LineSymbolizer because it could
// have more than one layer
mOutline->toSld( doc, element, props );
}
}

QgsSymbolLayerV2* QgsSVGFillSymbolLayer::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QString path, mimeType;
QColor fillColor, borderColor;
Qt::PenStyle penStyle;
double size, borderWidth;

QDomElement fillElem = element.firstChildElement( "Fill" );
if( fillElem.isNull() )
return NULL;

QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
if( graphicFillElem.isNull() )
return NULL;

QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
if( graphicElem.isNull() )
return NULL;

if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
return NULL;

if ( mimeType != "image/svg+xml" )
return NULL;

QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );

double angle = 0.0;
QString angleFunc;
if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
{
bool ok;
double d = angleFunc.toDouble( &ok );
if ( ok )
angle = d;
}

QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
sl->setSvgFillColor( fillColor );
sl->setSvgOutlineColor( borderColor );
sl->setSvgOutlineWidth( borderWidth );

// try to get the outline
QDomElement strokeElem = element.firstChildElement( "Stroke" );
if ( !strokeElem.isNull() )
{
QgsSymbolLayerV2 *l = QgsSymbolLayerV2Utils::createLineLayerFromSld( strokeElem );
if ( l )
{
QgsSymbolLayerV2List layers;
layers.append( l );
sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
}
}

return sl;
}

void QgsSVGFillSymbolLayer::storeViewBox()
{
if ( !mSvgData.isEmpty() )
Expand Down Expand Up @@ -630,6 +807,52 @@ QgsSymbolLayerV2* QgsLinePatternFillSymbolLayer::clone() const
return clonedLayer;
}

void QgsLinePatternFillSymbolLayer::toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

QDomElement fillElem = doc.createElement( "se:Fill" );
symbolizerElem.appendChild( fillElem );

QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
fillElem.appendChild( graphicFillElem );

QDomElement graphicElem = doc.createElement( "se:Graphic" );
graphicFillElem.appendChild( graphicElem );

QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), mColor, mLineWidth, mDistance );

// <Rotation>
QString angleFunc;
bool ok;
double angle = props.value( "angle", "0" ).toDouble( &ok );
if ( !ok )
{
angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
}
else if ( angle + mLineAngle != 0 )
{
angleFunc = QString::number( angle + mLineAngle );
}
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );

// <se:Displacement>
QPointF lineOffset( qSin( mLineAngle ) * mOffset, qCos( mLineAngle ) * mOffset );
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
}

QgsSymbolLayerV2* QgsLinePatternFillSymbolLayer::createFromSld( QDomElement &element )
{
Q_UNUSED( element );
return NULL;
}

////////////////////////

QgsPointPatternFillSymbolLayer::QgsPointPatternFillSymbolLayer(): QgsImageFillSymbolLayer(), mMarkerSymbol( 0 ), mDistanceX( 15 ),
Expand Down Expand Up @@ -765,6 +988,49 @@ QgsSymbolLayerV2* QgsPointPatternFillSymbolLayer::clone() const
return clonedLayer;
}

void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
{
QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

QDomElement fillElem = doc.createElement( "se:Fill" );
symbolizerElem.appendChild( fillElem );

QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
fillElem.appendChild( graphicFillElem );

// store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
QString dist = QgsSymbolLayerV2Utils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
symbolizerElem.appendChild( distanceElem );

QgsSymbolLayerV2 *layer = mMarkerSymbol->symbolLayer( i );
QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
if ( !markerLayer )
{
QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
graphicFillElem.appendChild( doc.createComment( errorMsg ) );
}
else
{
markerLayer->writeSldMarker( doc, graphicFillElem, props );
}
}
}

QgsSymbolLayerV2* QgsPointPatternFillSymbolLayer::createFromSld( QDomElement &element )
{
Q_UNUSED( element );
return NULL;
}

bool QgsPointPatternFillSymbolLayer::setSubSymbol( QgsSymbolV2* symbol )
{
if ( !symbol )
Expand Down Expand Up @@ -859,6 +1125,31 @@ QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2::clone() const
return x;
}

void QgsCentroidFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
// SLD 1.0 specs says: "if a line, polygon, or raster geometry is
// used with PointSymbolizer, then the semantic is to use the centroid
// of the geometry, or any similar representative point.
mMarker->toSld( doc, element, props );
}

QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QgsSymbolLayerV2 *l = QgsSymbolLayerV2Utils::createMarkerLayerFromSld( element );
if ( !l )
return NULL;

QgsSymbolLayerV2List layers;
layers.append( l );
QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );

QgsCentroidFillSymbolLayerV2* x = new QgsCentroidFillSymbolLayerV2();
x->setSubSymbol( marker );
return x;
}


QgsSymbolV2* QgsCentroidFillSymbolLayerV2::subSymbol()
{
Expand Down
16 changes: 16 additions & 0 deletions src/core/symbology-ng/qgsfillsymbollayerv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CORE_EXPORT QgsSimpleFillSymbolLayerV2 : public QgsFillSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -40,6 +41,8 @@ class CORE_EXPORT QgsSimpleFillSymbolLayerV2 : public QgsFillSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

Qt::BrushStyle brushStyle() const { return mBrushStyle; }
void setBrushStyle( Qt::BrushStyle style ) { mBrushStyle = style; }

Expand Down Expand Up @@ -97,6 +100,7 @@ class CORE_EXPORT QgsSVGFillSymbolLayer: public QgsImageFillSymbolLayer
~QgsSVGFillSymbolLayer();

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -109,6 +113,8 @@ class CORE_EXPORT QgsSVGFillSymbolLayer: public QgsImageFillSymbolLayer

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

//getters and setters
void setSvgFilePath( const QString& svgPath );
QString svgFilePath() const { return mSvgFilePath; }
Expand Down Expand Up @@ -151,6 +157,7 @@ class CORE_EXPORT QgsLinePatternFillSymbolLayer: public QgsImageFillSymbolLayer
~QgsLinePatternFillSymbolLayer();

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

QString layerType() const;

Expand All @@ -162,6 +169,8 @@ class CORE_EXPORT QgsLinePatternFillSymbolLayer: public QgsImageFillSymbolLayer

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

//getters and setters
void setLineAngle( double a ) { mLineAngle = a; }
double lineAngle() const { return mLineAngle; }
Expand Down Expand Up @@ -193,6 +202,8 @@ class CORE_EXPORT QgsPointPatternFillSymbolLayer: public QgsImageFillSymbolLayer
~QgsPointPatternFillSymbolLayer();

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

QString layerType() const;

void startRender( QgsSymbolV2RenderContext& context );
Expand All @@ -203,6 +214,8 @@ class CORE_EXPORT QgsPointPatternFillSymbolLayer: public QgsImageFillSymbolLayer

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

//getters and setters
double distanceX() const { return mDistanceX; }
void setDistanceX( double d ) { mDistanceX = d; }
Expand Down Expand Up @@ -236,6 +249,7 @@ class CORE_EXPORT QgsCentroidFillSymbolLayerV2 : public QgsFillSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -251,6 +265,8 @@ class CORE_EXPORT QgsCentroidFillSymbolLayerV2 : public QgsFillSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

void setColor( const QColor& color );

QgsSymbolV2* subSymbol();
Expand Down
49 changes: 49 additions & 0 deletions src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,38 @@ QString QgsRendererRangeV2::dump()
return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol->dump() );
}

void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
if ( !mSymbol || props.value( "attribute", "" ).isEmpty() )
return;

QDomElement ruleElem = doc.createElement( "se:Rule" );
element.appendChild( ruleElem );

QString valueStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );

QDomElement nameElem = doc.createElement( "se:Name" );
nameElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : valueStr ) );
ruleElem.appendChild( nameElem );

QString descrName = props.value( "version", "1.1" ) < "1.1" ? "Abstract" : "se:Description";
QString descrValue = QString( "Graduated symbol rendererV2 - %1" ).arg( valueStr );

QDomElement descrElem = doc.createElement( descrName );
descrElem.appendChild( doc.createTextNode( descrValue ) );
ruleElem.appendChild( descrElem );

// create the ogc:Filter for the range
QDomElement filterElem = doc.createElement( "ogc:Filter" );

QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
.arg( props[ "attribute" ] )
.arg( mLowerValue ).arg( mUpperValue );
QgsSymbolLayerV2Utils::createFunctionElement( doc, filterElem, filterFunc );

mSymbol->toSld( doc, ruleElem, props );
}

///////////

QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( QString attrName, QgsRangeList ranges )
Expand Down Expand Up @@ -302,6 +334,23 @@ QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::clone()
return r;
}

void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
{
QgsStringMap props;
props[ "attribute" ] = mAttrName;
if ( !mRotationField.isEmpty() )
props[ "angle" ] = QString( mRotationField ).append( "\"" ).prepend( "\"" );
if ( !mSizeScaleField.isEmpty() )
props[ "scale" ] = QString( mSizeScaleField ).append( "\"" ).prepend( "\"" );

// create a Rule for each range
for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); it++ )
{
QgsStringMap catProps( props );
it->toSld( doc, element, catProps );
}
}

QgsSymbolV2List QgsGraduatedSymbolRendererV2::symbols()
{
QgsSymbolV2List lst;
Expand Down
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef QGSGRADUATEDSYMBOLRENDERERV2_H
#define QGSGRADUATEDSYMBOLRENDERERV2_H

#include "qgssymbolv2.h"
#include "qgsrendererv2.h"

class CORE_EXPORT QgsRendererRangeV2
Expand All @@ -25,6 +26,8 @@ class CORE_EXPORT QgsRendererRangeV2
// debugging
QString dump();

void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const;

protected:
double mLowerValue, mUpperValue;
QgsSymbolV2* mSymbol;
Expand Down Expand Up @@ -55,6 +58,8 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2

virtual QgsFeatureRendererV2* clone();

virtual void toSld( QDomDocument& doc, QDomElement &element ) const;

//! returns bitwise OR-ed capabilities of the renderer
//! \note added in 2.0
virtual int capabilities() { return SymbolLevels | RotationField; }
Expand Down
253 changes: 253 additions & 0 deletions src/core/symbology-ng/qgslinesymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
#include "qgssymbollayerv2utils.h"

#include "qgsrendercontext.h"
#include "qgslogger.h"

#include <QPainter>
#include <QDomDocument>
#include <QDomElement>

#include <cmath>

Expand Down Expand Up @@ -148,6 +151,77 @@ QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::clone() const
return l;
}

void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
if ( mPenStyle == Qt::NoPen )
return;

QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

// <Stroke>
QDomElement strokeElem = doc.createElement( "se:Stroke" );
symbolizerElem.appendChild( strokeElem );

Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
&mPenJoinStyle, &mPenCapStyle, &mCustomDashVector );

// <se:PerpendicularOffset>
if ( mOffset != 0 )
{
QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
symbolizerElem.appendChild( perpOffsetElem );
}
}

QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QDomElement strokeElem = element.firstChildElement( "Stroke" );
if ( strokeElem.isNull() )
return NULL;

Qt::PenStyle penStyle;
QColor color;
double width;
Qt::PenJoinStyle penJoinStyle;
Qt::PenCapStyle penCapStyle;
QVector<qreal> customDashVector;

if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
color, width,
&penJoinStyle, &penCapStyle,
&customDashVector ) )
return NULL;

double offset = 0.0;
QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
if ( !perpOffsetElem.isNull() )
{
bool ok;
double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
if ( ok )
offset = d;
}

QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
l->setOffset( offset );
l->setPenJoinStyle( penJoinStyle );
l->setPenCapStyle( penCapStyle );
l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
l->setCustomDashVector( customDashVector );
return l;
}



/////////

Expand Down Expand Up @@ -574,6 +648,154 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::clone() const
return x;
}

void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
{
QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

QString gap;
switch( mPlacement )
{
case FirstVertex:
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
break;
case LastVertex:
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
break;
case CentralPoint:
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
break;
case Vertex:
// no way to get line/polygon's vertices, use a VendorOption
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
break;
default:
gap = QString::number( mInterval );
break;
}

if( !mRotateMarker )
{
// markers in LineSymbolizer must be drawn following the line orientation,
// use a VendorOption when no marker rotation
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
}

// <Stroke>
QDomElement strokeElem = doc.createElement( "se:Stroke" );
symbolizerElem.appendChild( strokeElem );

// <GraphicStroke>
QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
strokeElem.appendChild( graphicStrokeElem );

QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
if ( !markerLayer )
{
graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( markerLayer->layerType() ) ) );
}
else
{
markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
}

if ( !gap.isEmpty() )
{
QDomElement gapElem = doc.createElement( "se:Gap" );
QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap );
graphicStrokeElem.appendChild( gapElem );
}

if ( !doubleNear( mOffset, 0.0 ) )
{
QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
symbolizerElem.appendChild( perpOffsetElem );
}
}
}

QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QDomElement strokeElem = element.firstChildElement( "Stroke" );
if ( strokeElem.isNull() )
return NULL;

QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
if ( graphicStrokeElem.isNull() )
return NULL;

// retrieve vendor options
bool rotateMarker = true;
Placement placement = Interval;

QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
{
if ( it.key() == "placement" )
{
if ( it.value() == "points" ) placement = Vertex;
else if ( it.value() == "firstPoint" ) placement = FirstVertex;
else if ( it.value() == "lastPoint" ) placement = LastVertex;
else if ( it.value() == "centralPoint" ) placement = CentralPoint;
}
else if ( it.value() == "rotateMarker" )
{
rotateMarker = it.value() == "0";
}
}

QgsMarkerSymbolV2 *marker = 0;

QgsSymbolLayerV2 *l = QgsSymbolLayerV2Utils::createMarkerLayerFromSld( graphicStrokeElem );
if ( l )
{
QgsSymbolLayerV2List layers;
layers.append( l );
marker = new QgsMarkerSymbolV2( layers );
}

double interval = 0.0;
QDomElement gapElem = element.firstChildElement( "Gap" );
if ( !gapElem.isNull() )
{
bool ok;
double d = gapElem.firstChild().nodeValue().toDouble( &ok );
if ( ok )
interval = d;
}

double offset = 0.0;
QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
if ( !perpOffsetElem.isNull() )
{
bool ok;
double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
if ( ok )
offset = d;
}

QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
x->setPlacement( placement );
if ( !doubleNear( interval, 0.0 ) && interval > 0 )
x->setInterval( interval );
if ( marker )
x->setSubSymbol( marker );
if ( !doubleNear( offset, 0.0 ) )
x->setOffset( offset );
return x;
}

void QgsMarkerLineSymbolLayerV2::setWidth( double width )
{
mMarker->setSize( width );
Expand Down Expand Up @@ -683,3 +905,34 @@ QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::clone() const
{
return new QgsLineDecorationSymbolLayerV2( mColor, mWidth );
}

void QgsLineDecorationSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom" , "" ) );

// <Stroke>
QDomElement strokeElem = doc.createElement( "se:Stroke" );
symbolizerElem.appendChild( strokeElem );

// <GraphicStroke>
QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
strokeElem.appendChild( graphicStrokeElem );

// <Graphic>
QDomElement graphicElem = doc.createElement( "se:Graphic" );
graphicStrokeElem.appendChild( graphicElem );

// <Mark>
QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "arrowhead", QColor(), mColor, mWidth, mWidth*8 );

// <Rotation>
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, props.value( "angle", "" ) );

// use <VendorOption> to draw the decoration at end of the line
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
}
9 changes: 9 additions & 0 deletions src/core/symbology-ng/qgslinesymbollayerv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class CORE_EXPORT QgsSimpleLineSymbolLayerV2 : public QgsLineSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -39,6 +40,8 @@ class CORE_EXPORT QgsSimpleLineSymbolLayerV2 : public QgsLineSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

// new stuff

Qt::PenStyle penStyle() const { return mPenStyle; }
Expand Down Expand Up @@ -97,6 +100,7 @@ class CORE_EXPORT QgsMarkerLineSymbolLayerV2 : public QgsLineSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -112,6 +116,8 @@ class CORE_EXPORT QgsMarkerLineSymbolLayerV2 : public QgsLineSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

void setColor( const QColor& color );

QgsSymbolV2* subSymbol();
Expand Down Expand Up @@ -163,6 +169,7 @@ class CORE_EXPORT QgsLineDecorationSymbolLayerV2 : public QgsLineSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -178,6 +185,8 @@ class CORE_EXPORT QgsLineDecorationSymbolLayerV2 : public QgsLineSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

protected:
QPen mPen;
QPen mSelPen;
Expand Down
203 changes: 201 additions & 2 deletions src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <QSvgRenderer>
#include <QFileInfo>
#include <QDir>
#include <QDomDocument>
#include <QDomElement>

#include <cmath>

Expand All @@ -34,6 +36,8 @@ static QPointF _rotatedOffset( const QPointF& offset, double angle )

QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle )
{
if ( name == "rectangle" )
name = "square";
mName = name;
mColor = color;
mBorderColor = borderColor;
Expand Down Expand Up @@ -222,7 +226,7 @@ bool QgsSimpleMarkerSymbolLayerV2::prepareShape()
{
mPolygon.clear();

if ( mName == "rectangle" )
if ( mName == "square" )
{
mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
return true;
Expand Down Expand Up @@ -324,7 +328,7 @@ bool QgsSimpleMarkerSymbolLayerV2::preparePath()
mPath.lineTo( 0, 1 ); // vertical
return true;
}
else if ( mName == "cross2" )
else if ( mName == "x" || mName == "cross2" )
{
mPath.moveTo( -1, -1 );
mPath.lineTo( 1, 1 );
Expand Down Expand Up @@ -426,6 +430,66 @@ QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::clone() const
return m;
}

void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
// <Graphic>
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );

QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mName, mColor, mBorderColor, -1, mSize );

// <Rotation>
QString angleFunc;
bool ok;
double angle = props.value( "angle", "0" ).toDouble( &ok );
if ( !ok )
{
angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
}
else if ( angle + mAngle != 0 )
{
angleFunc = QString::number( angle + mAngle );
}
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );

// <Displacement>
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
}

QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QDomElement graphicElem = element.firstChildElement( "Graphic" );
if( graphicElem.isNull() )
return NULL;

QString name = "square";
QColor color, borderColor;
double borderWidth, size;

if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderWidth, size ) )
return NULL;

double angle = 0.0;
QString angleFunc;
if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
{
bool ok;
double d = angleFunc.toDouble( &ok );
if ( ok )
angle = d;
}

QPointF offset;
QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset );

QgsMarkerSymbolLayerV2 *m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size );
m->setAngle( angle );
m->setOffset( offset );
return m;
}

void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderContext& context )
{
Q_UNUSED( context );
Expand Down Expand Up @@ -627,6 +691,73 @@ QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const
return m;
}

void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
// <Graphic>
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );

QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize );

// <Rotation>
QString angleFunc;
bool ok;
double angle = props.value( "angle", "0" ).toDouble( &ok );
if ( !ok )
{
angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
}
else if ( angle + mAngle != 0 )
{
angleFunc = QString::number( angle + mAngle );
}

QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );

// <Displacement>
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
}

QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QDomElement graphicElem = element.firstChildElement( "Graphic" );
if( graphicElem.isNull() )
return NULL;

QString path, mimeType;
QColor fillColor;
double size;

if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
return NULL;

if ( mimeType != "image/svg+xml" )
return NULL;

double angle = 0.0;
QString angleFunc;
if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
{
bool ok;
double d = angleFunc.toDouble( &ok );
if ( ok )
angle = d;
}

QPointF offset;
QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset );

QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( path, size );
m->setFillColor( fillColor );
//m->setOutlineColor( outlineColor );
//m->setOutlineWidth( outlineWidth );
m->setAngle( angle );
m->setOffset( offset );
return m;
}


QStringList QgsSvgMarkerSymbolLayerV2::listSvgFiles()
{
Expand Down Expand Up @@ -829,3 +960,71 @@ QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::clone() const
m->setOffset( mOffset );
return m;
}

void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
// <Graphic>
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );

QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
int markIndex = mChr.unicode();
QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );

// <Rotation>
QString angleFunc;
bool ok;
double angle = props.value( "angle", "0" ).toDouble( &ok );
if ( !ok )
{
angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
}
else if ( angle + mAngle != 0 )
{
angleFunc = QString::number( angle + mAngle );
}
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );

// <Displacement>
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
}

QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::createFromSld( QDomElement &element )
{
QgsDebugMsg( "Entered." );

QDomElement graphicElem = element.firstChildElement( "Graphic" );
if( graphicElem.isNull() )
return NULL;

QString name, format;
QColor color;
double size;
int chr;

if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
return NULL;

if ( !name.startsWith( "ttf://" ) || format != "ttf" )
return NULL;

QString fontFamily = name.mid( 6 );

double angle = 0.0;
QString angleFunc;
if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
{
bool ok;
double d = angleFunc.toDouble( &ok );
if ( ok )
angle = d;
}

QPointF offset;
QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset );

QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color );
m->setAngle( angle );
m->setOffset( offset );
return m;
}
9 changes: 9 additions & 0 deletions src/core/symbology-ng/qgsmarkersymbollayerv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class CORE_EXPORT QgsSimpleMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -43,6 +44,8 @@ class CORE_EXPORT QgsSimpleMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

QString name() const { return mName; }
void setName( QString name ) { mName = name; }

Expand Down Expand Up @@ -87,6 +90,7 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

//! Return a list of all available svg files
static QStringList listSvgFiles();
Expand All @@ -111,6 +115,8 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

QString path() const { return mPath; }
void setPath( QString path );

Expand Down Expand Up @@ -161,6 +167,7 @@ class CORE_EXPORT QgsFontMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
// static stuff

static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
static QgsSymbolLayerV2* createFromSld( QDomElement &element );

// implemented from base classes

Expand All @@ -176,6 +183,8 @@ class CORE_EXPORT QgsFontMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2

QgsSymbolLayerV2* clone() const;

void writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

// new methods

QString fontFamily() const { return mFontFamily; }
Expand Down
8 changes: 8 additions & 0 deletions src/core/symbology-ng/qgspointdisplacementrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgsvectorlayer.h"

#include <QDomElement>
#include <QPainter>

#include <cmath>

QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString& labelAttributeName )
Expand Down Expand Up @@ -66,6 +68,12 @@ QgsFeatureRendererV2* QgsPointDisplacementRenderer::clone()
return r;
}

void QgsPointDisplacementRenderer::toSld( QDomDocument& doc, QDomElement &element ) const
{
mRenderer->toSld( doc, element );
}


bool QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
{
Q_UNUSED( drawVertexMarker );
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology-ng/qgspointdisplacementrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2

QgsFeatureRendererV2* clone();

virtual void toSld( QDomDocument& doc, QDomElement &element ) const;

/**Reimplemented from QgsFeatureRendererV2*/
bool renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false );

Expand Down
104 changes: 104 additions & 0 deletions src/core/symbology-ng/qgsrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,110 @@ QDomElement QgsFeatureRendererV2::save( QDomDocument& doc )
return doc.createElement( RENDERER_TAG_NAME );
}

QgsFeatureRendererV2* QgsFeatureRendererV2::loadSld( const QDomNode &node, QGis::GeometryType geomType, QString &errorMessage )
{
QDomElement element = node.toElement();
if ( element.isNull() )
return NULL;

// get the UserStyle element
QDomElement userStyleElem = element.firstChildElement( "UserStyle" );
if ( userStyleElem.isNull() )
{
// UserStyle element not found, nothing will be rendered
errorMessage = "Info: UserStyle element not found.";
return NULL;
}

// get the FeatureTypeStyle element
QDomElement featTypeStyleElem = userStyleElem.firstChildElement( "FeatureTypeStyle" );
if ( featTypeStyleElem.isNull() )
{
errorMessage = "Info: FeatureTypeStyle element not found.";
return NULL;
}

// use the RuleRenderer when more rules are present or the rule
// has filters or min/max scale denominators set,
// otherwise use the SingleSymbol renderer
bool needRuleRenderer = false;
int ruleCount = 0;

QDomElement ruleElem = featTypeStyleElem.firstChildElement( "Rule" );
while( !ruleElem.isNull() )
{
ruleCount++;

// more rules present, use the RuleRenderer
if ( ruleCount > 1 )
{
QgsDebugMsg( "more Rule elements found: need a RuleRenderer" );
needRuleRenderer = true;
break;
}

QDomElement ruleChildElem = ruleElem.firstChildElement();
while( !ruleChildElem.isNull() )
{
// rule has filter or min/max scale denominator, use the RuleRenderer
if ( ruleChildElem.localName() == "Filter" ||
ruleChildElem.localName() == "MinScaleDenominator" ||
ruleChildElem.localName() == "MaxScaleDenominator" )
{
QgsDebugMsg( "Filter or Min/MaxScaleDenominator element found: need a RuleRenderer" );
needRuleRenderer = true;
break;
}

ruleChildElem = ruleChildElem.nextSiblingElement();
}

if ( needRuleRenderer )
{
break;
}

ruleElem = ruleElem.nextSiblingElement( "Rule" );
}

QString rendererType;
if ( needRuleRenderer )
{
rendererType = "RuleRenderer";
}
else
{
rendererType = "singleSymbol";
}
QgsDebugMsg( QString( "Instantiating a '%1' renderer..." ).arg( rendererType ) );

// create the renderer and return it
QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( rendererType );
if ( m == NULL )
{
errorMessage = QString( "Error: Unable to get metadata for '%1' renderer." ).arg( rendererType );
return NULL;
}

QgsFeatureRendererV2* r = m->createRendererFromSld( featTypeStyleElem, geomType );
return r;
}

QDomElement QgsFeatureRendererV2::writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const
{
QDomElement userStyleElem = doc.createElement( "UserStyle" );

QDomElement nameElem = doc.createElement( "se:Name" );
nameElem.appendChild( doc.createTextNode( layer.name() ) );
userStyleElem.appendChild( nameElem );

QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" );
toSld( doc, featureTypeStyleElem );
userStyleElem.appendChild( featureTypeStyleElem );

return userStyleElem;
}

QgsLegendSymbologyList QgsFeatureRendererV2::legendSymbologyItems( QSize iconSize )
{
Q_UNUSED( iconSize );
Expand Down
31 changes: 27 additions & 4 deletions src/core/symbology-ng/qgsrendererv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@
#include <QVariant>
#include <QPair>
#include <QPixmap>

class QDomDocument;
class QDomElement;
#include <QDomDocument>
#include <QDomElement>

class QgsSymbolV2;
class QgsRenderContext;
class QgsFeature;
class QgsVectorLayer;

typedef QMap<QString, QString> QgsStringMap;

typedef QList<QgsSymbolV2*> QgsSymbolV2List;
typedef QMap<QString, QgsSymbolV2* > QgsSymbolV2Map;

Expand Down Expand Up @@ -100,6 +101,28 @@ class CORE_EXPORT QgsFeatureRendererV2
//! store renderer info to XML element
virtual QDomElement save( QDomDocument& doc );

//! create the SLD UserStyle element following the SLD v1.1 specs
//! @note added in 1.9
virtual QDomElement writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const;

/** create a new renderer according to the information contained in
* the UserStyle element of a SLD style document
* @param node the node in the SLD document whose the UserStyle element
* is a child
* @param geomType the geometry type of the features, used to convert
* Symbolizer elements
* @param errorMessage it will contain the error message if something
* went wrong
* @return the renderer
* @note added in 1.9
*/
static QgsFeatureRendererV2* loadSld( const QDomNode &node, QGis::GeometryType geomType, QString &errorMessage );

//! used from subclasses to create SLD Rule elements following SLD v1.1 specs
//! @note added in 1.9
virtual void toSld( QDomDocument& doc, QDomElement &element ) const
{ element.appendChild( doc.createComment( QString( "FeatureRendererV2 %1 not implemented yet" ).arg( type() ) ) ); }

//! return a list of symbology items for the legend
virtual QgsLegendSymbologyList legendSymbologyItems( QSize iconSize );

Expand Down Expand Up @@ -161,4 +184,4 @@ class CORE_EXPORT QgsFeatureRendererV2
};


#endif
#endif // QGSRENDERERV2_H
8 changes: 6 additions & 2 deletions src/core/symbology-ng/qgsrendererv2registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ QgsRendererV2Registry::QgsRendererV2Registry()
// add default renderers
addRenderer( new QgsRendererV2Metadata( "singleSymbol",
QObject::tr( "Single Symbol" ),
QgsSingleSymbolRendererV2::create ) );
QgsSingleSymbolRendererV2::create,
QgsSingleSymbolRendererV2::createFromSld ) );

addRenderer( new QgsRendererV2Metadata( "categorizedSymbol",
QObject::tr( "Categorized" ),
QgsCategorizedSymbolRendererV2::create ) );
Expand All @@ -24,7 +26,9 @@ QgsRendererV2Registry::QgsRendererV2Registry()

addRenderer( new QgsRendererV2Metadata( "RuleRenderer",
QObject::tr( "Rule-based" ),
QgsRuleBasedRendererV2::create ) );
QgsRuleBasedRendererV2::create,
QgsRuleBasedRendererV2::createFromSld ) );

addRenderer( new QgsRendererV2Metadata( "pointDisplacement",
QObject::tr( "Point displacement" ),
QgsPointDisplacementRenderer::create ) );
Expand Down
24 changes: 23 additions & 1 deletion src/core/symbology-ng/qgsrendererv2registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <QStringList>
#include <QDomElement>

#include "qgis.h"

class QgsFeatureRendererV2;
class QgsVectorLayer;
class QgsStyleV2;
Expand Down Expand Up @@ -36,6 +38,9 @@ class CORE_EXPORT QgsRendererV2AbstractMetadata
virtual QgsRendererV2Widget* createRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
{ Q_UNUSED( layer ); Q_UNUSED( style ); Q_UNUSED( renderer ); return NULL; }

virtual QgsFeatureRendererV2* createRendererFromSld( QDomElement& elem, QGis::GeometryType geomType )
{ Q_UNUSED( elem ); Q_UNUSED( geomType ); return NULL; }

protected:
//! name used within QGIS for identification (the same what renderer's type() returns)
QString mName;
Expand All @@ -48,6 +53,7 @@ class CORE_EXPORT QgsRendererV2AbstractMetadata

typedef QgsFeatureRendererV2*( *QgsRendererV2CreateFunc )( QDomElement& );
typedef QgsRendererV2Widget*( *QgsRendererV2WidgetFunc )( QgsVectorLayer*, QgsStyleV2*, QgsFeatureRendererV2* );
typedef QgsFeatureRendererV2*( *QgsRendererV2CreateFromSldFunc )( QDomElement&, QGis::GeometryType geomType );

/**
Convenience metadata class that uses static functions to create renderer and its widget.
Expand All @@ -62,14 +68,28 @@ class CORE_EXPORT QgsRendererV2Metadata : public QgsRendererV2AbstractMetadata
QgsRendererV2CreateFunc pfCreate,
QIcon icon = QIcon(),
QgsRendererV2WidgetFunc pfWidget = NULL )
: QgsRendererV2AbstractMetadata( name, visibleName, icon ), mCreateFunc( pfCreate ), mWidgetFunc( pfWidget ) {}
: QgsRendererV2AbstractMetadata( name, visibleName, icon ),
mCreateFunc( pfCreate ), mWidgetFunc( pfWidget ), mCreateFromSldFunc( NULL ) {}

QgsRendererV2Metadata( QString name,
QString visibleName,
QgsRendererV2CreateFunc pfCreate,
QgsRendererV2CreateFromSldFunc pfCreateFromSld,
QIcon icon = QIcon(),
QgsRendererV2WidgetFunc pfWidget = NULL )
: QgsRendererV2AbstractMetadata( name, visibleName, icon ),
mCreateFunc( pfCreate ), mWidgetFunc( pfWidget ), mCreateFromSldFunc( pfCreateFromSld ) {}

virtual QgsFeatureRendererV2* createRenderer( QDomElement& elem ) { return mCreateFunc ? mCreateFunc( elem ) : NULL; }
virtual QgsRendererV2Widget* createRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
{ return mWidgetFunc ? mWidgetFunc( layer, style, renderer ) : NULL; }
virtual QgsFeatureRendererV2* createRendererFromSld( QDomElement& elem, QGis::GeometryType geomType )
{ return mCreateFromSldFunc ? mCreateFromSldFunc( elem, geomType ) : NULL; }


QgsRendererV2CreateFunc createFunction() const { return mCreateFunc; }
QgsRendererV2WidgetFunc widgetFunction() const { return mWidgetFunc; }
QgsRendererV2CreateFromSldFunc createFromSldFunction() const { return mCreateFromSldFunc; }

void setWidgetFunction( QgsRendererV2WidgetFunc f ) { mWidgetFunc = f; }

Expand All @@ -78,6 +98,8 @@ class CORE_EXPORT QgsRendererV2Metadata : public QgsRendererV2AbstractMetadata
QgsRendererV2CreateFunc mCreateFunc;
//! pointer to function that creates a widget for configuration of renderer's params
QgsRendererV2WidgetFunc mWidgetFunc;
//! pointer to function that creates an instance of the renderer from SLD
QgsRendererV2CreateFromSldFunc mCreateFromSldFunc;
};

/**
Expand Down
231 changes: 230 additions & 1 deletion src/core/symbology-ng/qgsrulebasedrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,88 @@ QDomElement QgsRuleBasedRendererV2::Rule::save( QDomDocument& doc, QgsSymbolV2Ma
return ruleElem;
}

void QgsRuleBasedRendererV2::Rule::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props )
{
// do not convert this rule if there are no symbols
if ( symbols().isEmpty() )
return;

if ( !mFilterExp.isEmpty() )
{
if ( !props.value( "filter", "" ).isEmpty() )
props[ "filter" ] += " AND ";
props[ "filter" ] += mFilterExp;
}

if ( mScaleMinDenom != 0 )
{
bool ok;
int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
if ( !ok || parentScaleMinDenom <= 0 )
props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
else
props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
}

if ( mScaleMaxDenom != 0 )
{
bool ok;
int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
if ( !ok || parentScaleMaxDenom <= 0 )
props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
else
props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
}

if ( mSymbol )
{
QDomElement ruleElem = doc.createElement( "se:Rule" );
element.appendChild( ruleElem );

QDomElement nameElem = doc.createElement( "se:Name" );
nameElem.appendChild( doc.createTextNode( mLabel ) );
ruleElem.appendChild( nameElem );

if ( !mDescription.isEmpty() )
{
QDomElement descrElem = doc.createElement( "se:Description" );
QDomElement abstractElem = doc.createElement( "se:Abstract" );
abstractElem.appendChild( doc.createTextNode( mDescription ) );
descrElem.appendChild( abstractElem );
ruleElem.appendChild( descrElem );
}

if ( !props.value( "filter", "" ).isEmpty() )
{
QDomElement filterElem = doc.createElement( "ogc:Filter" );
QgsSymbolLayerV2Utils::createFunctionElement( doc, filterElem, props.value( "filter", "" ) );
ruleElem.appendChild( filterElem );
}

if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
{
QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
ruleElem.appendChild( scaleMinDenomElem );
}

if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
{
QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
ruleElem.appendChild( scaleMaxDenomElem );
}

mSymbol->toSld( doc, ruleElem, props );
}

// loop into childern rule list
for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
{
( *it )->toSld( doc, element, props );
}
}

bool QgsRuleBasedRendererV2::Rule::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
{
mActiveChildren.clear();
Expand Down Expand Up @@ -373,6 +455,119 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::create( QDomElement&
return rule;
}

QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::createFromSld( QDomElement& ruleElem, QGis::GeometryType geomType )
{
if ( ruleElem.localName() != "Rule" )
{
QgsDebugMsg( QString( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
return NULL;
}

QString label, description, filterExp;
int scaleMinDenom = 0, scaleMaxDenom = 0;
QgsSymbolLayerV2List layers;

// retrieve the Rule element child nodes
QDomElement childElem = ruleElem.firstChildElement();
while( !childElem.isNull() )
{
if ( childElem.localName() == "Name" )
{
label = childElem.firstChild().nodeValue();
}
else if ( childElem.localName() == "Description" )
{
// <se:Description> can contains a title and an abstract
// prefer Abstract if available
QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
QDomElement titleElem = childElem.firstChildElement( "Title" );
if ( !abstractElem.isNull() )
{
description = abstractElem.firstChild().nodeValue();
}
else if ( !titleElem.isNull() && description.isEmpty() )
{
description = titleElem.firstChild().nodeValue();
}
}
else if ( childElem.localName() == "Abstract" )
{
// <sld:Abstract>
description = childElem.firstChild().nodeValue();
}
else if ( childElem.localName() == "Title" )
{
// <sld:Title>
if ( description.isEmpty() )
description = childElem.firstChild().nodeValue();
}
else if ( childElem.localName() == "Filter" )
{
QgsExpression *filter = QgsExpression::createFromOgcFilter( childElem );
if ( filter )
{
if ( filter->hasParserError() )
{
QgsDebugMsg( "parser error: " + filter->parserErrorString() );
}
else
{
filterExp = filter->dump();
}
delete filter;
}
}
else if ( childElem.localName() == "MinScaleDenominator" )
{
bool ok;
int v = childElem.firstChild().nodeValue().toInt( &ok );
if ( ok )
scaleMinDenom = v;
}
else if ( childElem.localName() == "MaxScaleDenominator" )
{
bool ok;
int v = childElem.firstChild().nodeValue().toInt( &ok );
if ( ok )
scaleMaxDenom = v;
}
else if ( childElem.localName().endsWith( "Symbolizer" ) )
{
// create symbol layers for this symbolizer
QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
}

childElem = childElem.nextSiblingElement();
}

// now create the symbol
QgsSymbolV2 *symbol = 0;
if ( layers.size() > 0 )
{
switch( geomType )
{
case QGis::Line:
symbol = new QgsLineSymbolV2( layers );
break;

case QGis::Polygon:
symbol = new QgsFillSymbolV2( layers );
break;

case QGis::Point:
symbol = new QgsMarkerSymbolV2( layers );
break;

default:
QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
return NULL;
}
}

// and then create and return the new rule
return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
}


/////////////////////

Expand Down Expand Up @@ -493,6 +688,11 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2::clone()
return r;
}

void QgsRuleBasedRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
{
mRootRule->toSld( doc, element, QgsStringMap() );
}

// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
QgsSymbolV2List QgsRuleBasedRendererV2::symbols()
{
Expand All @@ -517,7 +717,6 @@ QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc )
return rendererElem;
}


QgsLegendSymbologyList QgsRuleBasedRendererV2::legendSymbologyItems( QSize iconSize )
{
QgsLegendSymbologyList lst;
Expand Down Expand Up @@ -560,6 +759,36 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2::create( QDomElement& element )
return r;
}

QgsFeatureRendererV2* QgsRuleBasedRendererV2::createFromSld( QDomElement& element, QGis::GeometryType geomType )
{
// retrieve child rules
Rule* root = 0;

QDomElement ruleElem = element.firstChildElement( "Rule" );
while ( !ruleElem.isNull() )
{
Rule *child = Rule::createFromSld( ruleElem, geomType );
if ( child )
{
// create the root rule if not done before
if ( !root )
root = new Rule( 0 );

root->appendChild( child );
}

ruleElem = ruleElem.nextSiblingElement( "Rule" );
}

if ( !root )
{
// no valid rules was found
return NULL;
}

// create and return the new renderer
return new QgsRuleBasedRendererV2( root );
}

#include "qgscategorizedsymbolrendererv2.h"
#include "qgsgraduatedsymbolrendererv2.h"
Expand Down
8 changes: 8 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "qgsfield.h"
#include "qgsfeature.h"
#include "qgis.h"

#include "qgsrendererv2.h"

Expand Down Expand Up @@ -116,6 +117,9 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
//! clone this rule, return new instance
Rule* clone() const;

void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props );
static Rule* createFromSld( QDomElement& element, QGis::GeometryType geomType );

QDomElement save( QDomDocument& doc, QgsSymbolV2Map& symbolMap );

//! prepare the rule for rendering and its children (build active children array)
Expand Down Expand Up @@ -197,6 +201,10 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2

virtual QgsFeatureRendererV2* clone();

virtual void toSld( QDomDocument& doc, QDomElement &element ) const;

static QgsFeatureRendererV2* createFromSld( QDomElement& element, QGis::GeometryType geomType );

virtual QgsSymbolV2List symbols();

//! store renderer info to XML element
Expand Down
83 changes: 83 additions & 0 deletions src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "qgslogger.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgssymbollayerv2.h"

#include <QDomDocument>
#include <QDomElement>
Expand Down Expand Up @@ -171,6 +172,24 @@ QgsFeatureRendererV2* QgsSingleSymbolRendererV2::clone()
return r;
}

void QgsSingleSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
{
QgsStringMap props;
if ( !mRotationField.isEmpty() )
props[ "angle" ] = QString( mRotationField ).append( "\"" ).prepend( "\"" );
if ( !mSizeScaleField.isEmpty() )
props[ "scale" ] = QString( mSizeScaleField ).append( "\"" ).prepend( "\"" );

QDomElement ruleElem = doc.createElement( "se:Rule" );
element.appendChild( ruleElem );

QDomElement nameElem = doc.createElement( "se:Name" );
nameElem.appendChild( doc.createTextNode( "Single symbol" ) );
ruleElem.appendChild( nameElem );

mSymbol->toSld( doc, ruleElem, props );
}

QgsSymbolV2List QgsSingleSymbolRendererV2::symbols()
{
QgsSymbolV2List lst;
Expand Down Expand Up @@ -206,6 +225,70 @@ QgsFeatureRendererV2* QgsSingleSymbolRendererV2::create( QDomElement& element )
return r;
}

QgsFeatureRendererV2* QgsSingleSymbolRendererV2::createFromSld( QDomElement& element, QGis::GeometryType geomType )
{
// XXX this renderer can handle only one Rule!

// get the first Rule element
QDomElement ruleElem = element.firstChildElement( "Rule" );
if ( ruleElem.isNull() )
{
QgsDebugMsg( "no Rule elements found!" );
return NULL;
}

QString label, description;
QgsSymbolLayerV2List layers;

// retrieve the Rule element child nodes
QDomElement childElem = ruleElem.firstChildElement();
while( !childElem.isNull() )
{
if ( childElem.localName() == "Name" )
{
label = childElem.firstChild().nodeValue();
}
else if ( childElem.localName() == "Description" || childElem.localName() == "Abstract" )
{
description = childElem.firstChild().nodeValue();
}
else if ( childElem.localName().endsWith( "Symbolizer" ) )
{
// create symbol layers for this symbolizer
QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
}

childElem = childElem.nextSiblingElement();
}

// now create the symbol
QgsSymbolV2 *symbol = 0;
if ( layers.size() > 0 )
{
switch( geomType )
{
case QGis::Line:
symbol = new QgsLineSymbolV2( layers );
break;

case QGis::Polygon:
symbol = new QgsFillSymbolV2( layers );
break;

case QGis::Point:
symbol = new QgsMarkerSymbolV2( layers );
break;

default:
QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
return NULL;
}
}

// and finally return the new renderer
return new QgsSingleSymbolRendererV2( symbol );
}

QDomElement QgsSingleSymbolRendererV2::save( QDomDocument& doc )
{
QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
Expand Down
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgssinglesymbolrendererv2.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef QGSSINGLESYMBOLRENDERERV2_H
#define QGSSINGLESYMBOLRENDERERV2_H

#include "qgis.h"
#include "qgsrendererv2.h"

class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2
Expand Down Expand Up @@ -36,6 +37,9 @@ class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2

virtual QgsFeatureRendererV2* clone();

virtual void toSld( QDomDocument& doc, QDomElement &element ) const;
static QgsFeatureRendererV2* createFromSld( QDomElement& element, QGis::GeometryType geomType );

//! returns bitwise OR-ed capabilities of the renderer
//! \note added in 2.0
virtual int capabilities() { return SymbolLevels | RotationField; }
Expand Down
13 changes: 13 additions & 0 deletions src/core/symbology-ng/qgssymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,16 @@ void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points,
p->drawPath( path );
}
}

void QgsMarkerSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{
QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" );
if ( !props.value( "uom", "" ).isEmpty() )
symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
element.appendChild( symbolizerElem );

// <Geometry>
QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );

writeSldMarker( doc, symbolizerElem, props );
}
12 changes: 10 additions & 2 deletions src/core/symbology-ng/qgssymbollayerv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <QMap>
#include <QPointF>
#include <QSet>
#include <QDomDocument>
#include <QDomElement>

#include "qgssymbolv2.h"

Expand All @@ -17,8 +19,6 @@ class QSize;
class QPolygonF;

class QgsRenderContext;
class QgsSymbolV2;


class CORE_EXPORT QgsSymbolLayerV2
{
Expand All @@ -37,6 +37,9 @@ class CORE_EXPORT QgsSymbolLayerV2

virtual QgsSymbolLayerV2* clone() const = 0;

virtual void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{ Q_UNUSED( props ); element.appendChild( doc.createComment( QString( "SymbolLayerV2 %1 not implemented yet" ).arg( layerType() ) ) ); }

virtual QgsStringMap properties() const = 0;

virtual void drawPreviewIcon( QgsSymbolV2RenderContext& context, QSize size ) = 0;
Expand Down Expand Up @@ -91,6 +94,11 @@ class CORE_EXPORT QgsMarkerSymbolLayerV2 : public QgsSymbolLayerV2
void setOffset( QPointF offset ) { mOffset = offset; }
QPointF offset() { return mOffset; }

virtual void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;

virtual void writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
{ Q_UNUSED( props ); element.appendChild( doc.createComment( QString( "QgsMarkerSymbolLayerV2 %1 not implemented yet" ).arg( layerType() ) ) ); }

protected:
QgsMarkerSymbolLayerV2( bool locked = false );

Expand Down
30 changes: 19 additions & 11 deletions src/core/symbology-ng/qgssymbollayerv2registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,33 @@ QgsSymbolLayerV2Registry::QgsSymbolLayerV2Registry()
{
// init registry with known symbol layers
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SimpleLine", QObject::tr( "Simple line" ), QgsSymbolV2::Line,
QgsSimpleLineSymbolLayerV2::create ) );
QgsSimpleLineSymbolLayerV2::create, QgsSimpleLineSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "MarkerLine", QObject::tr( "Marker line" ), QgsSymbolV2::Line,
QgsMarkerLineSymbolLayerV2::create ) );
QgsMarkerLineSymbolLayerV2::create, QgsMarkerLineSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "LineDecoration", QObject::tr( "Line decoration" ), QgsSymbolV2::Line,
QgsLineDecorationSymbolLayerV2::create ) );

addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SimpleMarker", QObject::tr( "Simple marker" ), QgsSymbolV2::Marker,
QgsSimpleMarkerSymbolLayerV2::create ) );
QgsSimpleMarkerSymbolLayerV2::create, QgsSimpleMarkerSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SvgMarker", QObject::tr( "SVG marker" ), QgsSymbolV2::Marker,
QgsSvgMarkerSymbolLayerV2::create ) );
QgsSvgMarkerSymbolLayerV2::create, QgsSvgMarkerSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "FontMarker", QObject::tr( "Font marker" ), QgsSymbolV2::Marker,
QgsFontMarkerSymbolLayerV2::create ) );
QgsFontMarkerSymbolLayerV2::create, QgsFontMarkerSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "EllipseMarker", QObject::tr( "Ellipse marker" ), QgsSymbolV2::Marker,
QgsEllipseSymbolLayerV2::create ) );
QgsEllipseSymbolLayerV2::create, QgsEllipseSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "VectorField", QObject::tr( "Vector Field marker" ), QgsSymbolV2::Marker,
QgsVectorFieldSymbolLayer::create ) );

addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SimpleFill", QObject::tr( "Simple fill" ), QgsSymbolV2::Fill,
QgsSimpleFillSymbolLayerV2::create ) );
QgsSimpleFillSymbolLayerV2::create, QgsSimpleFillSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SVGFill", QObject::tr( "SVG fill" ), QgsSymbolV2::Fill,
QgsSVGFillSymbolLayer::create ) );
QgsSVGFillSymbolLayer::create, QgsSVGFillSymbolLayer::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "CentroidFill", QObject::tr( "Centroid fill" ), QgsSymbolV2::Fill,
QgsCentroidFillSymbolLayerV2::create ) );
QgsCentroidFillSymbolLayerV2::create, QgsCentroidFillSymbolLayerV2::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "LinePatternFill", QObject::tr( "Line pattern fill" ), QgsSymbolV2::Fill,
QgsLinePatternFillSymbolLayer::create ) );
QgsLinePatternFillSymbolLayer::create, QgsLinePatternFillSymbolLayer::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "PointPatternFill", QObject::tr( "Point pattern fill" ), QgsSymbolV2::Fill,
QgsPointPatternFillSymbolLayer::create ) );
QgsPointPatternFillSymbolLayer::create, QgsPointPatternFillSymbolLayer::createFromSld ) );
}

QgsSymbolLayerV2Registry::~QgsSymbolLayerV2Registry()
Expand Down Expand Up @@ -101,6 +101,14 @@ QgsSymbolLayerV2* QgsSymbolLayerV2Registry::createSymbolLayer( QString name, con
return mMetadata[name]->createSymbolLayer( properties );
}

QgsSymbolLayerV2* QgsSymbolLayerV2Registry::createSymbolLayerFromSld( QString name, QDomElement& element ) const
{
if ( !mMetadata.contains( name ) )
return NULL;

return mMetadata[name]->createSymbolLayerFromSld( element );
}

QStringList QgsSymbolLayerV2Registry::symbolLayersForType( QgsSymbolV2::SymbolType type )
{
QStringList lst;
Expand Down
21 changes: 20 additions & 1 deletion src/core/symbology-ng/qgssymbollayerv2registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class CORE_EXPORT QgsSymbolLayerV2AbstractMetadata
virtual QgsSymbolLayerV2* createSymbolLayer( const QgsStringMap& map ) = 0;
/** create widget for symbol layer of this type. Can return NULL if there's no GUI */
virtual QgsSymbolLayerV2Widget* createSymbolLayerWidget( const QgsVectorLayer * ) { return NULL; }
/** create a symbol layer of this type given the map of properties. */
virtual QgsSymbolLayerV2* createSymbolLayerFromSld( QDomElement & ) { return NULL; }


protected:
QString mName;
Expand All @@ -36,6 +39,7 @@ class CORE_EXPORT QgsSymbolLayerV2AbstractMetadata

typedef QgsSymbolLayerV2*( *QgsSymbolLayerV2CreateFunc )( const QgsStringMap& );
typedef QgsSymbolLayerV2Widget*( *QgsSymbolLayerV2WidgetFunc )( const QgsVectorLayer* );
typedef QgsSymbolLayerV2*( *QgsSymbolLayerV2CreateFromSldFunc )( QDomElement& );

/**
Convenience metadata class that uses static functions to create symbol layer and its widget.
Expand All @@ -47,19 +51,31 @@ class CORE_EXPORT QgsSymbolLayerV2Metadata : public QgsSymbolLayerV2AbstractMeta
QgsSymbolV2::SymbolType type,
QgsSymbolLayerV2CreateFunc pfCreate,
QgsSymbolLayerV2WidgetFunc pfWidget = NULL )
: QgsSymbolLayerV2AbstractMetadata( name, visibleName, type ), mCreateFunc( pfCreate ), mWidgetFunc( pfWidget ) {}
: QgsSymbolLayerV2AbstractMetadata( name, visibleName, type ),
mCreateFunc( pfCreate ), mWidgetFunc( pfWidget ), mCreateFromSldFunc( NULL ) {}

QgsSymbolLayerV2Metadata( QString name, QString visibleName,
QgsSymbolV2::SymbolType type,
QgsSymbolLayerV2CreateFunc pfCreate,
QgsSymbolLayerV2CreateFromSldFunc pfCreateFromSld,
QgsSymbolLayerV2WidgetFunc pfWidget = NULL )
: QgsSymbolLayerV2AbstractMetadata( name, visibleName, type ),
mCreateFunc( pfCreate ), mWidgetFunc ( pfWidget ), mCreateFromSldFunc( pfCreateFromSld ) {}

QgsSymbolLayerV2CreateFunc createFunction() const { return mCreateFunc; }
QgsSymbolLayerV2WidgetFunc widgetFunction() const { return mWidgetFunc; }
QgsSymbolLayerV2CreateFromSldFunc createFromSldFunction() const { return mCreateFromSldFunc; }

void setWidgetFunction( QgsSymbolLayerV2WidgetFunc f ) { mWidgetFunc = f; }

virtual QgsSymbolLayerV2* createSymbolLayer( const QgsStringMap& map ) { return mCreateFunc ? mCreateFunc( map ) : NULL; }
virtual QgsSymbolLayerV2Widget* createSymbolLayerWidget( const QgsVectorLayer* vl ) { return mWidgetFunc ? mWidgetFunc( vl ) : NULL; }
virtual QgsSymbolLayerV2* createSymbolLayerFromSld( QDomElement& elem ) { return mCreateFromSldFunc ? mCreateFromSldFunc( elem ) : NULL; }

protected:
QgsSymbolLayerV2CreateFunc mCreateFunc;
QgsSymbolLayerV2WidgetFunc mWidgetFunc;
QgsSymbolLayerV2CreateFromSldFunc mCreateFromSldFunc;
};


Expand All @@ -83,6 +99,9 @@ class CORE_EXPORT QgsSymbolLayerV2Registry
//! create a new instance of symbol layer given symbol layer name and properties
QgsSymbolLayerV2* createSymbolLayer( QString name, const QgsStringMap& properties = QgsStringMap() ) const;

//! create a new instance of symbol layer given symbol layer name and SLD
QgsSymbolLayerV2* createSymbolLayerFromSld( QString name, QDomElement &element ) const;

//! return a list of available symbol layers for a specified symbol type
QStringList symbolLayersForType( QgsSymbolV2::SymbolType type );

Expand Down
Loading