[SCM] qgis branch, master, updated. a2ee769957385f4e084c5e8b6ba178a8c877d1db
Giuseppe Sucameli
brush.tyler at gmail.com
Tue Mar 20 13:43:05 UTC 2012
The following commit has been merged in the master branch:
commit 39069220b21d2939356f54395a5a69cdd70a67da
Author: Giuseppe Sucameli <brush.tyler at gmail.com>
Date: Thu Mar 8 05:02:44 2012 +0100
[FEATURE] load/save layer style (new symbology) from/to SLD document
This is a huge commit, but it's mostly new functions. It changes only few lines on the existing code.
- add conversion between QgsExpression and OGC Filter Encoding 1.1
- add conversion between QgsRendererV2 and OGC Symbology Encoding 1.1
Work done for Regione Toscana-SIGTA
diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp
index ea0cf06..3071594 100644
--- a/src/app/qgsvectorlayerproperties.cpp
+++ b/src/app/qgsvectorlayerproperties.cpp
@@ -950,14 +950,25 @@ void QgsVectorLayerProperties::on_pbnLoadStyle_clicked()
{
QSettings myQSettings; // where we keep last used filter in persistent state
QString myLastUsedDir = myQSettings.value( "style/lastStyleDir", "." ).toString();
- QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer properties from style file (.qml)" ), myLastUsedDir, tr( "QGIS Layer Style File (*.qml)" ) );
+ QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer properties from style file" ), myLastUsedDir,
+ tr( "QGIS Layer Style File (*.qml)" ) + ";;" + tr( "SLD File (*.sld)" ) );
if ( myFileName.isNull() )
{
return;
}
+ QString myMessage;
bool defaultLoadedFlag = false;
- QString myMessage = layer->loadNamedStyle( myFileName, defaultLoadedFlag );
+
+ if ( myFileName.endsWith( ".sld", Qt::CaseInsensitive ) )
+ {
+ // load from SLD
+ myMessage = layer->loadSldStyle( myFileName, defaultLoadedFlag );
+ }
+ else
+ {
+ myMessage = layer->loadNamedStyle( myFileName, defaultLoadedFlag );
+ }
//reset if the default style was loaded ok only
if ( defaultLoadedFlag )
{
@@ -966,7 +977,7 @@ void QgsVectorLayerProperties::on_pbnLoadStyle_clicked()
else
{
//let the user know what went wrong
- QMessageBox::information( this, tr( "Saved Style" ), myMessage );
+ QMessageBox::information( this, tr( "Load Style" ), myMessage );
}
QFileInfo myFI( myFileName );
@@ -979,7 +990,8 @@ void QgsVectorLayerProperties::on_pbnSaveStyleAs_clicked()
{
QSettings myQSettings; // where we keep last used filter in persistent state
QString myLastUsedDir = myQSettings.value( "style/lastStyleDir", "." ).toString();
- QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save layer properties as style file (.qml)" ), myLastUsedDir, tr( "QGIS Layer Style File (*.qml)" ) );
+ QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save layer properties as style file" ), myLastUsedDir,
+ tr( "QGIS Layer Style File (*.qml)" ) + ";;" + tr( "SLD File (*.sld)" ) );
if ( myOutputFileName.isNull() ) //dialog canceled
{
return;
@@ -987,14 +999,25 @@ void QgsVectorLayerProperties::on_pbnSaveStyleAs_clicked()
apply(); // make sure the qml to save is uptodate
+ QString myMessage;
+ bool defaultLoadedFlag = false;
+
//ensure the user never omitted the extension from the file name
- if ( !myOutputFileName.endsWith( ".qml", Qt::CaseInsensitive ) )
+ if ( myOutputFileName.endsWith( ".sld", Qt::CaseInsensitive ) )
+ {
+ // convert to SLD
+ myMessage = layer->saveSldStyle( myOutputFileName, defaultLoadedFlag );
+ }
+ else
{
- myOutputFileName += ".qml";
+ if ( !myOutputFileName.endsWith( ".qml", Qt::CaseInsensitive ) )
+ {
+ myOutputFileName += ".qml";
+ }
+
+ myMessage = layer->saveNamedStyle( myOutputFileName, defaultLoadedFlag );
}
- bool defaultLoadedFlag = false;
- QString myMessage = layer->saveNamedStyle( myOutputFileName, defaultLoadedFlag );
//reset if the default style was loaded ok only
if ( defaultLoadedFlag )
{
diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp
index 5bb7959..7531f52 100644
--- a/src/core/qgsexpression.cpp
+++ b/src/core/qgsexpression.cpp
@@ -25,6 +25,7 @@
#include "qgsdistancearea.h"
#include "qgsfeature.h"
#include "qgsgeometry.h"
+#include "qgslogger.h"
// from parser
extern QgsExpression::Node* parseExpression( const QString& str, QString& parserErrorMsg );
@@ -109,11 +110,27 @@ const char* QgsExpression::BinaryOperatorText[] =
"||"
};
+const char* QgsExpression::BinaryOgcOperatorText[] =
+{
+ "Or", "And",
+ "PropertyIsEqualTo", "PropertyIsNotEqualTo",
+ "PropertyIsGreaterThanOrEqualTo", "PropertyIsLessThanOrEqualTo",
+ "PropertyIsLessThan", "PropertyIsGreaterThan",
+ "", "PropertyIsLike", "", "", "",
+ "Add", "Sub", "Mul", "Div", "", "",
+ ""
+};
+
const char* QgsExpression::UnaryOperatorText[] =
{
"NOT", "-"
};
+const char* QgsExpression::UnaryOgcOperatorText[] =
+{
+ "Not", ""
+};
+
///////////////////////////////////////////////
// functions
@@ -546,6 +563,49 @@ QString QgsExpression::dump() const
return mRootNode->dump();
}
+void QgsExpression::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ if ( !mRootNode )
+ return;
+
+ mRootNode->toOgcFilter( doc, element );
+}
+
+QgsExpression* QgsExpression::createFromOgcFilter( QDomElement &element )
+{
+ if ( element.isNull() || !element.hasChildNodes() )
+ return NULL;
+
+ QgsExpression *expr = new QgsExpression();
+
+ QDomElement childElem = element.firstChildElement();
+ while ( !childElem.isNull() )
+ {
+ QString errorMsg;
+ QgsExpression::Node *node = QgsExpression::Node::createFromOgcFilter( childElem, errorMsg );
+ if ( !node )
+ {
+ // invalid expression, parser error
+ expr->mParserErrorString = errorMsg;
+ return expr;
+ }
+
+ // use the concat binary operator to append to the root node
+ if ( !expr->mRootNode )
+ {
+ expr->mRootNode = node;
+ }
+ else
+ {
+ expr->mRootNode = new QgsExpression::NodeBinaryOperator( boConcat, expr->mRootNode, node );
+ }
+
+ childElem = childElem.nextSiblingElement();
+ }
+
+ return expr;
+}
+
void QgsExpression::acceptVisitor( QgsExpression::Visitor& v )
{
if ( mRootNode )
@@ -553,6 +613,116 @@ void QgsExpression::acceptVisitor( QgsExpression::Visitor& v )
}
+QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &element, QString &errorMessage )
+{
+ if ( element.isNull() )
+ return NULL;
+
+ // check for unary operators
+ int unaryOpCount = sizeof( UnaryOgcOperatorText ) / sizeof( UnaryOgcOperatorText[0] );
+ for ( int i = 0; i < unaryOpCount; i++ )
+ {
+ QString ogcOperatorName = UnaryOgcOperatorText[ i ];
+ if ( ogcOperatorName.isEmpty() )
+ continue;
+
+ if ( element.localName() == ogcOperatorName )
+ {
+ QgsExpression::Node *node = QgsExpression::NodeUnaryOperator::createFromOgcFilter( element, errorMessage );
+ if ( node )
+ return node;
+
+ return NULL;
+ }
+ }
+
+ // check for binary operators
+ int binaryOpCount = sizeof( BinaryOgcOperatorText ) / sizeof( BinaryOgcOperatorText[0] );
+ for ( int i = 0; i < binaryOpCount; i++ )
+ {
+ QString ogcOperatorName = BinaryOgcOperatorText[ i ];
+ if ( ogcOperatorName.isEmpty() )
+ continue;
+
+ if ( element.localName() == ogcOperatorName )
+ {
+ QgsExpression::Node *node = QgsExpression::NodeBinaryOperator::createFromOgcFilter( element, errorMessage );
+ if ( node )
+ return node;
+
+ return NULL;
+ }
+ }
+
+ // check for other OGC operators, convert them to expressions
+
+ if ( element.localName() == "PropertyIsNull" )
+ {
+ return QgsExpression::NodeBinaryOperator::createFromOgcFilter( element, errorMessage );
+ }
+ else if ( element.localName() == "Literal" )
+ {
+ return QgsExpression::NodeLiteral::createFromOgcFilter( element, errorMessage );
+ }
+ else if ( element.localName() == "Function")
+ {
+ return QgsExpression::NodeFunction::createFromOgcFilter( element, errorMessage );
+ }
+ else if ( element.localName() == "PropertyName")
+ {
+ return QgsExpression::NodeColumnRef::createFromOgcFilter( element, errorMessage );
+ }
+ else if ( element.localName() == "PropertyIsBetween" )
+ {
+ // <ogc:PropertyIsBetween> encode a Range check
+ QgsExpression::Node *operand = 0, *lowerBound = 0, *upperBound = 0;
+
+ QDomElement operandElem = element.firstChildElement( "LowerBoundary" );
+ if ( !operandElem.isNull() )
+ lowerBound = createFromOgcFilter( operandElem, errorMessage );
+
+ operandElem = element.firstChildElement( "UpperBoundary" );
+ if ( !operandElem.isNull() )
+ upperBound = createFromOgcFilter( operandElem, errorMessage );
+
+ // <ogc:expression>
+ operandElem = element.firstChildElement();
+ while ( !operandElem.isNull() )
+ {
+ if ( operandElem.localName() != "LowerBoundary" &&
+ operandElem.localName() != "UpperBoundary" )
+ {
+ operand = createFromOgcFilter( operandElem, errorMessage );
+ break;
+ }
+
+ operandElem = operandElem.nextSiblingElement();
+ }
+
+ if ( !operand || !lowerBound || !upperBound )
+ {
+ if ( operand )
+ delete operand;
+
+ if ( lowerBound )
+ delete lowerBound;
+
+ if ( upperBound )
+ delete upperBound;
+
+ errorMessage = "missing some required sub-elements in ogc:PropertyIsBetween";
+ return NULL;
+ }
+
+ QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( boGE, operand, lowerBound );
+ QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( boLE, operand, upperBound );
+ return new QgsExpression::NodeBinaryOperator( boAnd, geOperator, leOperator );
+ }
+
+ errorMessage += QString( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
+ return NULL;
+}
+
///////////////////////////////////////////////
// nodes
@@ -567,6 +737,14 @@ QString QgsExpression::NodeList::dump() const
return msg;
}
+void QgsExpression::NodeList::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ foreach( Node* n, mList )
+ {
+ n->toOgcFilter( doc, element );
+ }
+}
+
//
QVariant QgsExpression::NodeUnaryOperator::eval( QgsExpression* parent, QgsFeature* f )
@@ -607,6 +785,58 @@ QString QgsExpression::NodeUnaryOperator::dump() const
return QString( "%1 %2" ).arg( UnaryOperatorText[mOp] ).arg( mOperand->dump() );
}
+void QgsExpression::NodeUnaryOperator::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ QDomElement uoElem;
+ switch ( mOp )
+ {
+ case uoMinus:
+ uoElem = doc.createElement( "ogc:Literal" );
+ uoElem.appendChild( doc.createTextNode( "-" ) );
+ break;
+ case uoNot:
+ uoElem = doc.createElement( "ogc:Not" );
+ break;
+
+ default:
+ element.appendChild( doc.createComment( QString( "Unary operator %1 not implemented yet" ).arg( UnaryOperatorText[mOp] ) ) );
+ return;
+ }
+ mOperand->toOgcFilter( doc, uoElem );
+ element.appendChild( uoElem );
+}
+
+QgsExpression::Node* QgsExpression::NodeUnaryOperator::createFromOgcFilter( QDomElement &element, QString &errorMessage )
+{
+ if ( element.isNull() )
+ return NULL;
+
+ int unaryOpCount = sizeof( UnaryOgcOperatorText ) / sizeof( UnaryOgcOperatorText[0] );
+ for ( int i = 0; i < unaryOpCount; i++ )
+ {
+ QString ogcOperatorName = UnaryOgcOperatorText[ i ];
+ if ( ogcOperatorName.isEmpty() )
+ continue;
+
+ if ( element.localName() != ogcOperatorName )
+ continue;
+
+ QDomElement operandElem = element.firstChildElement();
+ QgsExpression::Node* operand = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
+ if ( !operand )
+ {
+ if ( errorMessage.isEmpty() )
+ errorMessage = QString( "invalid operand for '%1' unary operator" ).arg( ogcOperatorName );
+ return NULL;
+ }
+
+ return new QgsExpression::NodeUnaryOperator( ( UnaryOperator ) i, operand );
+ }
+
+ errorMessage = QString( "%1 unary operator not supported." ).arg( element.tagName() );
+ return NULL;
+}
+
//
QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeature* f )
@@ -822,6 +1052,171 @@ QString QgsExpression::NodeBinaryOperator::dump() const
return QString( "%1 %2 %3" ).arg( mOpLeft->dump() ).arg( BinaryOperatorText[mOp] ).arg( mOpRight->dump() );
}
+void QgsExpression::NodeBinaryOperator::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ if ( mOp == boConcat )
+ {
+ // the concat binary operator must only convert its operands
+ mOpLeft->toOgcFilter( doc, element );
+ mOpRight->toOgcFilter( doc, element );
+ return;
+ }
+
+ if ( mOp == boIs || mOp == boIsNot )
+ {
+ // check if one of the operands is NULL
+ QgsExpression::NodeLiteral *opLeftLiteral = dynamic_cast<QgsExpression::NodeLiteral *>( mOpLeft );
+ QgsExpression::NodeLiteral *opRightLiteral = dynamic_cast<QgsExpression::NodeLiteral *>( mOpRight );
+
+ if ( opLeftLiteral && opLeftLiteral->value().isNull() &&
+ opRightLiteral && opRightLiteral->value().isNull() )
+ {
+ // why could anybody find useful to use NULL IS NULL???
+ // BTW avoid issues by converting it to 1 = 1
+ QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqual" );
+
+ QDomElement literalElem = doc.createElement( "ogc:Literal" );
+ literalElem.appendChild( doc.createTextNode( "1" ) );
+ eqElem.appendChild( literalElem );
+
+ literalElem = doc.createElement( "ogc:Literal" );
+ literalElem.appendChild( doc.createTextNode( "1" ) );
+ eqElem.appendChild( literalElem );
+
+ element.appendChild( eqElem );
+ }
+ else if ( ( opLeftLiteral && opLeftLiteral->value().isNull() ) ||
+ ( opRightLiteral && opRightLiteral->value().isNull() ) )
+ {
+ // at least one operand is NULL, use <ogc:PropertyIsNull> element
+ QDomElement isNullElem = doc.createElement( "ogc:PropertyIsNull" );
+ QgsExpression::Node *operand = opLeftLiteral->value().isNull() ? mOpRight : mOpLeft;
+ operand->toOgcFilter( doc, isNullElem );
+
+ if ( mOp == boIsNot )
+ {
+ // append to <ogc:Not> element if IS NOT operator was required
+ QDomElement notOpElem = doc.createElement( "ogc:Not" );
+ notOpElem.appendChild( isNullElem );
+ element.appendChild( notOpElem );
+ }
+ else
+ {
+ element.appendChild( isNullElem );
+ }
+ }
+ else
+ {
+ // both operands are not null, use <ogc:PropertyIsEqual> element
+ QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqual" );
+ mOpLeft->toOgcFilter( doc, eqElem );
+ mOpRight->toOgcFilter( doc, eqElem );
+ element.appendChild( eqElem );
+ }
+ return;
+ }
+
+ if ( mOp == boILike )
+ {
+ // XXX why ogc:PropertyIsLikeType extends ogc:ComparisonOpsType
+ // which has no matchCase attribute? Shouldn't it be better if
+ // would extend BinaryComparisonOpType which has that attribute
+ // and doesn't require to have a ogc:PropertyName as first parameter?
+ QgsExpression ilikeExpr( QString( "upper( %1 ) LIKE upper( %2 )" ).arg( mOpLeft->dump() ).arg( mOpRight->dump() ) );
+ ilikeExpr.toOgcFilter( doc, element );
+ return;
+ }
+
+ QString opText = BinaryOgcOperatorText[mOp];
+ if ( opText.isEmpty() )
+ {
+ // not implemented binary operators
+ // TODO: regex, % (mod), ^ (pow) are not supported yet
+ element.appendChild( doc.createComment( QString( "Binary operator %1 not implemented yet" ).arg( BinaryOperatorText[mOp] ) ) );
+ return;
+ }
+
+ QDomElement boElem = doc.createElement( "ogc:" + opText );
+ if ( mOp == boLike )
+ {
+ // setup wildcards to <ogc:PropertyIsLike>
+ boElem.setAttribute( "wildCard", "%" );
+ boElem.setAttribute( "singleChar", "?" );
+ boElem.setAttribute( "escapeChar", "!" );
+ }
+
+ mOpLeft->toOgcFilter( doc, boElem );
+ mOpRight->toOgcFilter( doc, boElem );
+ element.appendChild( boElem );
+}
+
+QgsExpression::Node* QgsExpression::NodeBinaryOperator::createFromOgcFilter( QDomElement &element, QString &errorMessage )
+{
+ if ( element.isNull() )
+ return NULL;
+
+ QgsExpression::Node* opLeft = 0;
+ QgsExpression::Node* opRight = 0;
+
+ // convert ogc:PropertyIsNull to IS operator with NULL right operand
+ if ( element.localName() == "PropertyIsNull" )
+ {
+ QDomElement operandElem = element.firstChildElement();
+ opLeft = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
+ if ( !opLeft )
+ return NULL;
+
+ opRight = new QgsExpression::NodeLiteral( QVariant() );
+ return new QgsExpression::NodeBinaryOperator( boIs, opLeft, opRight );
+ }
+
+ // the other binary operators
+ int binaryOpCount = sizeof( BinaryOgcOperatorText ) / sizeof( BinaryOgcOperatorText[0] );
+ for ( int i = 0; i < binaryOpCount; i++ )
+ {
+ QString ogcOperatorName = BinaryOgcOperatorText[ i ];
+ if ( ogcOperatorName.isEmpty() )
+ continue;
+
+ if ( element.localName() != ogcOperatorName )
+ continue;
+
+ QDomElement operandElem = element.firstChildElement();
+ opLeft = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
+ if ( !opLeft )
+ {
+ if ( errorMessage.isEmpty() )
+ errorMessage = QString( "invalid left operand for '%1' binary operator" ).arg( ogcOperatorName );
+ break;
+ }
+
+ operandElem = operandElem.nextSiblingElement();
+ opRight = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
+ if ( !opRight )
+ {
+ if ( errorMessage.isEmpty() )
+ errorMessage = QString( "invalid right operand for '%1' binary operator" ).arg( ogcOperatorName );
+ break;
+ }
+
+ return new QgsExpression::NodeBinaryOperator( ( BinaryOperator ) i, opLeft, opRight );
+ }
+
+ if ( !opLeft && !opRight )
+ {
+ errorMessage = QString( "'%1' binary operator not supported." ).arg( element.tagName() );
+ return NULL;
+ }
+
+ if ( opLeft )
+ delete opLeft;
+
+ if ( opRight )
+ delete opRight;
+
+ return NULL;
+}
+
//
QVariant QgsExpression::NodeInOperator::eval( QgsExpression* parent, QgsFeature* f )
@@ -885,6 +1280,31 @@ QString QgsExpression::NodeInOperator::dump() const
return QString( "%1 IN (%2)" ).arg( mNode->dump() ).arg( mList->dump() );
}
+void QgsExpression::NodeInOperator::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ // XXX use a function instead of multiple comparations?
+
+ QDomElement *parent = &element;
+
+ QDomElement orElem;
+ if ( mList->list().size() > 1 )
+ {
+ orElem = doc.createElement( "ogc:Or" );
+ element.appendChild( orElem );
+
+ parent = &orElem;
+ }
+
+ foreach( Node* n, mList->list() )
+ {
+ QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqualTo" );
+ mNode->toOgcFilter( doc, eqElem );
+ n->toOgcFilter( doc, eqElem );
+
+ parent->appendChild( eqElem );
+ }
+}
+
//
QVariant QgsExpression::NodeFunction::eval( QgsExpression* parent, QgsFeature* f )
@@ -935,6 +1355,58 @@ QString QgsExpression::NodeFunction::dump() const
return QString( "%1(%2)" ).arg( fd.mName ).arg( mArgs ? mArgs->dump() : QString() ); // function
}
+void QgsExpression::NodeFunction::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ const FunctionDef& fd = BuiltinFunctions()[mFnIndex];
+ if ( fd.mParams == 0 )
+ return; // TODO: special column
+
+ QDomElement funcElem = doc.createElement( "ogc:Function" );
+ funcElem.setAttribute( "name", fd.mName );
+ mArgs->toOgcFilter( doc, funcElem );
+ element.appendChild( funcElem );
+}
+
+QgsExpression::Node* QgsExpression::NodeFunction::createFromOgcFilter( QDomElement &element, QString &errorMessage )
+{
+ if ( element.isNull() )
+ return NULL;
+
+ if ( element.localName() != "Function" )
+ {
+ errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
+ return NULL;
+ }
+
+ for ( int i = 0; i < BuiltinFunctions().size(); i++ )
+ {
+ QgsExpression::FunctionDef funcDef = BuiltinFunctions()[i];
+
+ if ( element.attribute( "name" ) != funcDef.mName )
+ continue;
+
+ QgsExpression::NodeList *args = new QgsExpression::NodeList();
+
+ QDomElement operandElem = element.firstChildElement();
+ while ( !operandElem.isNull() )
+ {
+ QgsExpression::Node* op = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
+ if ( !op )
+ {
+ delete args;
+ return NULL;
+ }
+ args->append( op );
+
+ operandElem = operandElem.nextSiblingElement();
+ }
+
+ return new QgsExpression::NodeFunction( i, args );
+ }
+
+ return NULL;
+}
+
//
QVariant QgsExpression::NodeLiteral::eval( QgsExpression* , QgsFeature* )
@@ -962,6 +1434,92 @@ QString QgsExpression::NodeLiteral::dump() const
}
}
+void QgsExpression::NodeLiteral::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ QString value;
+ if ( !mValue.isNull() )
+ {
+ switch ( mValue.type() )
+ {
+ case QVariant::Int:
+ value = QString::number( mValue.toInt() );
+ break;
+ case QVariant::Double:
+ value = QString::number( mValue.toDouble() );
+ break;
+ case QVariant::String:
+ value = mValue.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ QDomElement litElem = doc.createElement( "ogc:Literal" );
+ litElem.appendChild( doc.createTextNode( value ) );
+ element.appendChild( litElem );
+}
+
+QgsExpression::Node* QgsExpression::NodeLiteral::createFromOgcFilter( QDomElement &element, QString &errorMessage )
+{
+ if ( element.isNull() )
+ return NULL;
+
+ if ( element.localName() != "Literal" )
+ {
+ errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
+ return NULL;
+ }
+
+ QgsExpression::Node *root = 0;
+
+ // the literal content can have more children (e.g. CDATA section, text, ...)
+ QDomNode childNode = element.firstChild();
+ while ( !childNode.isNull() )
+ {
+ QgsExpression::Node* operand = 0;
+
+ if ( childNode.nodeType() == QDomNode::ElementNode )
+ {
+ // found a element node (e.g. PropertyName), convert it
+ QDomElement operandElem = childNode.toElement();
+ operand = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
+ if ( !operand )
+ {
+ if ( root )
+ delete root;
+
+ errorMessage = QString( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
+ return NULL;
+ }
+ }
+ else
+ {
+ // probably a text/CDATA node, convert its content to string
+ operand = new QgsExpression::NodeLiteral( childNode.nodeValue() );
+ }
+
+ if ( !operand )
+ continue;
+
+ // use the concat operator to merge the ogc:Literal children
+ if ( !root )
+ {
+ root = operand;
+ }
+ else
+ {
+ root = new QgsExpression::NodeBinaryOperator( boConcat, root, operand );
+ }
+
+ childNode = childNode.nextSibling();
+ }
+
+ if ( root )
+ return root;
+
+ return NULL;
+}
+
//
QVariant QgsExpression::NodeColumnRef::eval( QgsExpression* /*parent*/, QgsFeature* f )
@@ -989,6 +1547,27 @@ QString QgsExpression::NodeColumnRef::dump() const
return mName;
}
+void QgsExpression::NodeColumnRef::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ QDomElement propElem = doc.createElement( "ogc:PropertyName" );
+ propElem.appendChild( doc.createTextNode( mName ) );
+ element.appendChild( propElem );
+}
+
+QgsExpression::Node* QgsExpression::NodeColumnRef::createFromOgcFilter( QDomElement &element, QString &errorMessage )
+{
+ if ( element.isNull() )
+ return NULL;
+
+ if ( element.localName() != "PropertyName" )
+ {
+ errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
+ return NULL;
+ }
+
+ return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
+}
+
//
QVariant QgsExpression::NodeCondition::eval( QgsExpression* parent, QgsFeature* f )
@@ -1045,6 +1624,12 @@ QString QgsExpression::NodeCondition::dump() const
return msg;
}
+void QgsExpression::NodeCondition::toOgcFilter( QDomDocument &doc, QDomElement &element ) const
+{
+ // TODO: if(cond) ... [else if (cond2) ...]* [else ...]
+ element.appendChild( doc.createComment( "CASE operator not implemented yet" ) );
+}
+
QStringList QgsExpression::NodeCondition::referencedColumns() const
{
QStringList lst;
diff --git a/src/core/qgsexpression.h b/src/core/qgsexpression.h
index 4f64f82..617ae6d 100644
--- a/src/core/qgsexpression.h
+++ b/src/core/qgsexpression.h
@@ -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 );
@@ -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;
@@ -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;
};
@@ -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 ); }
@@ -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 ); }
@@ -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 ); }
@@ -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 ); }
@@ -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 ); }
@@ -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 ); }
@@ -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 ); }
@@ -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;
diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp
index dbe7f23..3db1cea 100644
--- a/src/core/qgsmaplayer.cpp
+++ b/src/core/qgsmaplayer.cpp
@@ -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()
diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h
index 76f964c..eb16455 100644
--- a/src/core/qgsmaplayer.h
+++ b/src/core/qgsmaplayer.h
@@ -289,6 +289,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
diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp
index cc8a6d8..2c91091 100644
--- a/src/core/qgsvectorlayer.cpp
+++ b/src/core/qgsvectorlayer.cpp
@@ -3393,6 +3393,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 )
{
diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h
index eca5cff..a06c59e 100644
--- a/src/core/qgsvectorlayer.h
+++ b/src/core/qgsvectorlayer.h
@@ -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
diff --git a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
index c355cc0..ea9d014 100644
--- a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
+++ b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
@@ -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 )
@@ -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;
diff --git a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
index d9068ed..706be9a 100644
--- a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
+++ b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
@@ -1,6 +1,7 @@
#ifndef QGSCATEGORIZEDSYMBOLRENDERERV2_H
#define QGSCATEGORIZEDSYMBOLRENDERERV2_H
+#include "qgssymbolv2.h"
#include "qgsrendererv2.h"
#include <QHash>
@@ -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;
@@ -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; }
diff --git a/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp b/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
index 8f7c515..ef029bf 100644
--- a/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
@@ -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 )
@@ -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;
diff --git a/src/core/symbology-ng/qgsellipsesymbollayerv2.h b/src/core/symbology-ng/qgsellipsesymbollayerv2.h
index c234e3b..8552f0b 100644
--- a/src/core/symbology-ng/qgsellipsesymbollayerv2.h
+++ b/src/core/symbology-ng/qgsellipsesymbollayerv2.h
@@ -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;
@@ -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; }
diff --git a/src/core/symbology-ng/qgsfillsymbollayerv2.cpp b/src/core/symbology-ng/qgsfillsymbollayerv2.cpp
index df309fc..a85816d 100644
--- a/src/core/symbology-ng/qgsfillsymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgsfillsymbollayerv2.cpp
@@ -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 )
@@ -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 )
@@ -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() )
@@ -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 ),
@@ -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 )
@@ -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()
{
diff --git a/src/core/symbology-ng/qgsfillsymbollayerv2.h b/src/core/symbology-ng/qgsfillsymbollayerv2.h
index 022d031..2adad7d 100644
--- a/src/core/symbology-ng/qgsfillsymbollayerv2.h
+++ b/src/core/symbology-ng/qgsfillsymbollayerv2.h
@@ -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
@@ -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; }
@@ -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
@@ -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; }
@@ -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;
@@ -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; }
@@ -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 );
@@ -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; }
@@ -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
@@ -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();
diff --git a/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp b/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
index 1f7691d..190c95b 100644
--- a/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
+++ b/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
@@ -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 )
@@ -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;
diff --git a/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h b/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
index de0f4c6..a10da2b 100644
--- a/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
+++ b/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
@@ -1,6 +1,7 @@
#ifndef QGSGRADUATEDSYMBOLRENDERERV2_H
#define QGSGRADUATEDSYMBOLRENDERERV2_H
+#include "qgssymbolv2.h"
#include "qgsrendererv2.h"
class CORE_EXPORT QgsRendererRangeV2
@@ -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;
@@ -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; }
diff --git a/src/core/symbology-ng/qgslinesymbollayerv2.cpp b/src/core/symbology-ng/qgslinesymbollayerv2.cpp
index ff6ef8b..b4ec8e0 100644
--- a/src/core/symbology-ng/qgslinesymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgslinesymbollayerv2.cpp
@@ -3,8 +3,11 @@
#include "qgssymbollayerv2utils.h"
#include "qgsrendercontext.h"
+#include "qgslogger.h"
#include <QPainter>
+#include <QDomDocument>
+#include <QDomElement>
#include <cmath>
@@ -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;
+}
+
+
/////////
@@ -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 );
@@ -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" ) );
+}
diff --git a/src/core/symbology-ng/qgslinesymbollayerv2.h b/src/core/symbology-ng/qgslinesymbollayerv2.h
index 8d99357..d7502c6 100644
--- a/src/core/symbology-ng/qgslinesymbollayerv2.h
+++ b/src/core/symbology-ng/qgslinesymbollayerv2.h
@@ -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
@@ -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; }
@@ -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
@@ -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();
@@ -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
@@ -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;
diff --git a/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp b/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
index 31df9bf..69fa683 100644
--- a/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
@@ -12,6 +12,8 @@
#include <QSvgRenderer>
#include <QFileInfo>
#include <QDir>
+#include <QDomDocument>
+#include <QDomElement>
#include <cmath>
@@ -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;
@@ -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;
@@ -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 );
@@ -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 );
@@ -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()
{
@@ -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;
+}
diff --git a/src/core/symbology-ng/qgsmarkersymbollayerv2.h b/src/core/symbology-ng/qgsmarkersymbollayerv2.h
index 705721c..3cbec0a 100644
--- a/src/core/symbology-ng/qgsmarkersymbollayerv2.h
+++ b/src/core/symbology-ng/qgsmarkersymbollayerv2.h
@@ -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
@@ -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; }
@@ -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();
@@ -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 );
@@ -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
@@ -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; }
diff --git a/src/core/symbology-ng/qgspointdisplacementrenderer.cpp b/src/core/symbology-ng/qgspointdisplacementrenderer.cpp
index a822770..affcddd 100644
--- a/src/core/symbology-ng/qgspointdisplacementrenderer.cpp
+++ b/src/core/symbology-ng/qgspointdisplacementrenderer.cpp
@@ -22,8 +22,10 @@
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgsvectorlayer.h"
+
#include <QDomElement>
#include <QPainter>
+
#include <cmath>
QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString& labelAttributeName )
@@ -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 );
diff --git a/src/core/symbology-ng/qgspointdisplacementrenderer.h b/src/core/symbology-ng/qgspointdisplacementrenderer.h
index a8f9270..856d519 100644
--- a/src/core/symbology-ng/qgspointdisplacementrenderer.h
+++ b/src/core/symbology-ng/qgspointdisplacementrenderer.h
@@ -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 );
diff --git a/src/core/symbology-ng/qgsrendererv2.cpp b/src/core/symbology-ng/qgsrendererv2.cpp
index be5ab2b..36faa5f 100644
--- a/src/core/symbology-ng/qgsrendererv2.cpp
+++ b/src/core/symbology-ng/qgsrendererv2.cpp
@@ -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 );
diff --git a/src/core/symbology-ng/qgsrendererv2.h b/src/core/symbology-ng/qgsrendererv2.h
index 968e8e8..4d9f1ef 100644
--- a/src/core/symbology-ng/qgsrendererv2.h
+++ b/src/core/symbology-ng/qgsrendererv2.h
@@ -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;
@@ -101,6 +102,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 );
@@ -162,4 +185,4 @@ class CORE_EXPORT QgsFeatureRendererV2
};
-#endif
+#endif // QGSRENDERERV2_H
diff --git a/src/core/symbology-ng/qgsrendererv2registry.cpp b/src/core/symbology-ng/qgsrendererv2registry.cpp
index eb4bf3a..2ae673d 100644
--- a/src/core/symbology-ng/qgsrendererv2registry.cpp
+++ b/src/core/symbology-ng/qgsrendererv2registry.cpp
@@ -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 ) );
@@ -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 ) );
diff --git a/src/core/symbology-ng/qgsrendererv2registry.h b/src/core/symbology-ng/qgsrendererv2registry.h
index 535ec4b..ed274c9 100644
--- a/src/core/symbology-ng/qgsrendererv2registry.h
+++ b/src/core/symbology-ng/qgsrendererv2registry.h
@@ -5,6 +5,8 @@
#include <QMap>
#include <QStringList>
+#include "qgis.h"
+
class QgsFeatureRendererV2;
class QDomElement;
class QgsVectorLayer;
@@ -37,6 +39,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;
@@ -49,6 +54,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.
@@ -63,14 +69,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; }
@@ -79,6 +99,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;
};
/**
diff --git a/src/core/symbology-ng/qgsrulebasedrendererv2.cpp b/src/core/symbology-ng/qgsrulebasedrendererv2.cpp
index 665ec9d..fa75d74 100644
--- a/src/core/symbology-ng/qgsrulebasedrendererv2.cpp
+++ b/src/core/symbology-ng/qgsrulebasedrendererv2.cpp
@@ -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();
@@ -377,6 +459,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 );
+}
+
/////////////////////
@@ -497,6 +692,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()
{
@@ -521,7 +721,6 @@ QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc )
return rendererElem;
}
-
QgsLegendSymbologyList QgsRuleBasedRendererV2::legendSymbologyItems( QSize iconSize )
{
QgsLegendSymbologyList lst;
@@ -564,6 +763,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"
diff --git a/src/core/symbology-ng/qgsrulebasedrendererv2.h b/src/core/symbology-ng/qgsrulebasedrendererv2.h
index 6a773ed..c35c013 100644
--- a/src/core/symbology-ng/qgsrulebasedrendererv2.h
+++ b/src/core/symbology-ng/qgsrulebasedrendererv2.h
@@ -18,6 +18,7 @@
#include "qgsfield.h"
#include "qgsfeature.h"
+#include "qgis.h"
#include "qgsrendererv2.h"
@@ -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)
@@ -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
diff --git a/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp b/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
index 7ebc20c..d302a85 100644
--- a/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
+++ b/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
@@ -7,6 +7,7 @@
#include "qgslogger.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
+#include "qgssymbollayerv2.h"
#include <QDomDocument>
#include <QDomElement>
@@ -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;
@@ -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 );
diff --git a/src/core/symbology-ng/qgssinglesymbolrendererv2.h b/src/core/symbology-ng/qgssinglesymbolrendererv2.h
index 49052bc..de83591 100644
--- a/src/core/symbology-ng/qgssinglesymbolrendererv2.h
+++ b/src/core/symbology-ng/qgssinglesymbolrendererv2.h
@@ -1,6 +1,7 @@
#ifndef QGSSINGLESYMBOLRENDERERV2_H
#define QGSSINGLESYMBOLRENDERERV2_H
+#include "qgis.h"
#include "qgsrendererv2.h"
class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2
@@ -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; }
diff --git a/src/core/symbology-ng/qgssymbollayerv2.cpp b/src/core/symbology-ng/qgssymbollayerv2.cpp
index e936ab7..02ee067 100644
--- a/src/core/symbology-ng/qgssymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgssymbollayerv2.cpp
@@ -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 );
+}
diff --git a/src/core/symbology-ng/qgssymbollayerv2.h b/src/core/symbology-ng/qgssymbollayerv2.h
index 0c798b9..400d479 100644
--- a/src/core/symbology-ng/qgssymbollayerv2.h
+++ b/src/core/symbology-ng/qgssymbollayerv2.h
@@ -7,6 +7,8 @@
#include <QMap>
#include <QPointF>
#include <QSet>
+#include <QDomDocument>
+#include <QDomElement>
#include "qgssymbolv2.h"
@@ -17,8 +19,6 @@ class QSize;
class QPolygonF;
class QgsRenderContext;
-class QgsSymbolV2;
-
class CORE_EXPORT QgsSymbolLayerV2
{
@@ -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;
@@ -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 );
diff --git a/src/core/symbology-ng/qgssymbollayerv2registry.cpp b/src/core/symbology-ng/qgssymbollayerv2registry.cpp
index 8c07b39..91eb517 100644
--- a/src/core/symbology-ng/qgssymbollayerv2registry.cpp
+++ b/src/core/symbology-ng/qgssymbollayerv2registry.cpp
@@ -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()
@@ -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;
diff --git a/src/core/symbology-ng/qgssymbollayerv2registry.h b/src/core/symbology-ng/qgssymbollayerv2registry.h
index d3e7a8a..5217186 100644
--- a/src/core/symbology-ng/qgssymbollayerv2registry.h
+++ b/src/core/symbology-ng/qgssymbollayerv2registry.h
@@ -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;
@@ -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.
@@ -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;
};
@@ -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 );
diff --git a/src/core/symbology-ng/qgssymbollayerv2utils.cpp b/src/core/symbology-ng/qgssymbollayerv2utils.cpp
index b8ab5bd..f6da224 100644
--- a/src/core/symbology-ng/qgssymbollayerv2utils.cpp
+++ b/src/core/symbology-ng/qgssymbollayerv2utils.cpp
@@ -5,11 +5,14 @@
#include "qgssymbollayerv2registry.h"
#include "qgssymbolv2.h"
#include "qgsvectorcolorrampv2.h"
+#include "qgsexpression.h"
#include "qgslogger.h"
#include "qgsrendercontext.h"
#include <QColor>
+#include <QFont>
+#include <QDomDocument>
#include <QDomNode>
#include <QDomElement>
#include <QIcon>
@@ -39,6 +42,66 @@ QColor QgsSymbolLayerV2Utils::decodeColor( QString str )
return QColor( red, green, blue, alpha );
}
+QString QgsSymbolLayerV2Utils::encodeSldAlpha( int alpha )
+{
+ return QString::number( alpha / 255.0, 'f', 2 );
+}
+
+int QgsSymbolLayerV2Utils::decodeSldAlpha( QString str )
+{
+ bool ok;
+ double alpha = str.toDouble( &ok );
+ if ( !ok || alpha > 1 )
+ alpha = 255;
+ else if ( alpha < 0 )
+ alpha = 0;
+ return alpha * 255;
+}
+
+QString QgsSymbolLayerV2Utils::encodeSldFontStyle( QFont::Style style )
+{
+ switch ( style )
+ {
+ case QFont::StyleNormal: return "normal";
+ case QFont::StyleItalic: return "italic";
+ case QFont::StyleOblique: return "oblique";
+ default: return "";
+ }
+}
+
+QFont::Style QgsSymbolLayerV2Utils::decodeSldFontStyle( QString str )
+{
+ if ( str == "normal" ) return QFont::StyleNormal;
+ if ( str == "italic" ) return QFont::StyleItalic;
+ if ( str == "oblique" ) return QFont::StyleOblique;
+ return QFont::StyleNormal;
+}
+
+QString QgsSymbolLayerV2Utils::encodeSldFontWeight( int weight )
+{
+ if ( weight == 50 ) return "normal";
+ if ( weight == 75 ) return "bold";
+
+ // QFont::Weight is between 0 and 99
+ // CSS font-weight is between 100 and 900
+ if ( weight < 0 ) return "100";
+ if ( weight > 99 ) return "900";
+ return QString::number( weight * 800 / 99 + 100 );
+}
+
+int QgsSymbolLayerV2Utils::decodeSldFontWeight( QString str )
+{
+ bool ok;
+ int weight = str.toInt( &ok );
+ if ( !ok ) return ( int ) QFont::Normal;
+
+ // CSS font-weight is between 100 and 900
+ // QFont::Weight is between 0 and 99
+ if ( weight > 900 ) return 99;
+ if ( weight < 100 ) return 0;
+ return ( weight - 100 ) * 99 / 800;
+}
+
QString QgsSymbolLayerV2Utils::encodePenStyle( Qt::PenStyle style )
{
switch ( style )
@@ -83,6 +146,25 @@ Qt::PenJoinStyle QgsSymbolLayerV2Utils::decodePenJoinStyle( QString str )
return Qt::BevelJoin;
}
+QString QgsSymbolLayerV2Utils::encodeSldLineJoinStyle( Qt::PenJoinStyle style )
+{
+ switch ( style )
+ {
+ case Qt::BevelJoin: return "bevel";
+ case Qt::MiterJoin: return "mitre";
+ case Qt::RoundJoin: return "round";
+ default: return "";
+ }
+}
+
+Qt::PenJoinStyle QgsSymbolLayerV2Utils::decodeSldLineJoinStyle( QString str )
+{
+ if ( str == "bevel" ) return Qt::BevelJoin;
+ if ( str == "mitre" ) return Qt::MiterJoin;
+ if ( str == "round" ) return Qt::RoundJoin;
+ return Qt::BevelJoin;
+}
+
QString QgsSymbolLayerV2Utils::encodePenCapStyle( Qt::PenCapStyle style )
{
switch ( style )
@@ -102,6 +184,24 @@ Qt::PenCapStyle QgsSymbolLayerV2Utils::decodePenCapStyle( QString str )
return Qt::SquareCap;
}
+QString QgsSymbolLayerV2Utils::encodeSldLineCapStyle( Qt::PenCapStyle style )
+{
+ switch ( style )
+ {
+ case Qt::SquareCap: return "square";
+ case Qt::FlatCap: return "butt";
+ case Qt::RoundCap: return "round";
+ default: return "";
+ }
+}
+
+Qt::PenCapStyle QgsSymbolLayerV2Utils::decodeSldLineCapStyle( QString str )
+{
+ if ( str == "square" ) return Qt::SquareCap;
+ if ( str == "butt" ) return Qt::FlatCap;
+ if ( str == "round" ) return Qt::RoundCap;
+ return Qt::SquareCap;
+}
QString QgsSymbolLayerV2Utils::encodeBrushStyle( Qt::BrushStyle style )
{
@@ -146,6 +246,52 @@ Qt::BrushStyle QgsSymbolLayerV2Utils::decodeBrushStyle( QString str )
return Qt::SolidPattern;
}
+QString QgsSymbolLayerV2Utils::encodeSldBrushStyle( Qt::BrushStyle style )
+{
+ switch( style )
+ {
+ case Qt::CrossPattern: return "cross";
+ case Qt::DiagCrossPattern: return "x";
+
+ /* The following names are taken from the presentation "GeoServer
+ * Cartographic Rendering" by Andrea Aime at the FOSS4G 2010.
+ * (see http://2010.foss4g.org/presentations/3588.pdf)
+ */
+ case Qt::HorPattern: return "horline";
+ case Qt::VerPattern: return "line";
+ case Qt::BDiagPattern: return "slash";
+ case Qt::FDiagPattern: return "backslash";
+
+ /* define the other names following the same pattern used above */
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ return QString( "brush://%1" ).arg( encodeBrushStyle( style ) );
+
+ default:
+ return QString();
+ }
+}
+
+Qt::BrushStyle QgsSymbolLayerV2Utils::decodeSldBrushStyle( QString str )
+{
+ if ( str == "horline" ) return Qt::HorPattern;
+ if ( str == "line" ) return Qt::VerPattern;
+ if ( str == "cross" ) return Qt::CrossPattern;
+ if ( str == "slash" ) return Qt::BDiagPattern;
+ if ( str == "backshash" ) return Qt::FDiagPattern;
+ if ( str == "x" ) return Qt::DiagCrossPattern;
+
+ if ( str.startsWith( "brush://" ) )
+ return decodeBrushStyle( str.mid( 8 ) );
+
+ return Qt::NoBrush;
+}
+
QString QgsSymbolLayerV2Utils::encodePoint( QPointF point )
{
return QString( "%1,%2" ).arg( point.x() ).arg( point.y() );
@@ -187,6 +333,49 @@ QgsSymbolV2::OutputUnit QgsSymbolLayerV2Utils::decodeOutputUnit( QString str )
return QgsSymbolV2::MM;
}
+QString QgsSymbolLayerV2Utils::encodeSldUom( QgsSymbolV2::OutputUnit unit, double *scaleFactor )
+{
+ switch ( unit )
+ {
+ case QgsSymbolV2::MapUnit:
+ if ( scaleFactor )
+ *scaleFactor = 0.001; // from millimeters to meters
+ return "http://www.opengeospatial.org/se/units/metre";
+
+ case QgsSymbolV2::MM:
+ default:
+ // pixel is the SLD default uom. The "standardized rendering pixel
+ // size" is defined to be 0.28mm × 0.28mm (millimeters).
+ if ( scaleFactor )
+ *scaleFactor = 0.28; // from millimeters to pixels
+
+ // http://www.opengeospatial.org/sld/units/pixel
+ return QString();
+ }
+}
+
+QgsSymbolV2::OutputUnit QgsSymbolLayerV2Utils::decodeSldUom( QString str, double *scaleFactor )
+{
+ if ( str == "http://www.opengeospatial.org/se/units/metre" )
+ {
+ if ( scaleFactor )
+ *scaleFactor = 1000.0; // from meters to millimeters
+ return QgsSymbolV2::MapUnit;
+ }
+ else if ( str == "http://www.opengeospatial.org/se/units/foot" )
+ {
+ if ( scaleFactor )
+ *scaleFactor = 304.8; // from feet to meters
+ return QgsSymbolV2::MapUnit;
+ }
+
+ // pixel is the SLD default uom. The "standardized rendering pixel
+ // size" is defined to be 0.28mm x 0.28mm (millimeters).
+ if ( scaleFactor )
+ *scaleFactor = 1/0.00028; // from pixels to millimeters
+ return QgsSymbolV2::MM;
+}
+
QString QgsSymbolLayerV2Utils::encodeRealVector( const QVector<qreal>& v )
{
QString vectorString;
@@ -216,6 +405,35 @@ QVector<qreal> QgsSymbolLayerV2Utils::decodeRealVector( const QString& s )
return resultVector;
}
+QString QgsSymbolLayerV2Utils::encodeSldRealVector( const QVector<qreal>& v )
+{
+ QString vectorString;
+ QVector<qreal>::const_iterator it = v.constBegin();
+ for ( ; it != v.constEnd(); ++it )
+ {
+ if ( it != v.constBegin() )
+ {
+ vectorString.append( " " );
+ }
+ vectorString.append( QString::number( *it ) );
+ }
+ return vectorString;
+}
+
+QVector<qreal> QgsSymbolLayerV2Utils::decodeSldRealVector( const QString& s )
+{
+ QVector<qreal> resultVector;
+
+ QStringList realList = s.split( " " );
+ QStringList::const_iterator it = realList.constBegin();
+ for ( ; it != realList.constEnd(); ++it )
+ {
+ resultVector.append( it->toDouble() );
+ }
+
+ return resultVector;
+}
+
QIcon QgsSymbolLayerV2Utils::symbolPreviewIcon( QgsSymbolV2* symbol, QSize size )
{
return QIcon( symbolPreviewPixmap( symbol, size ) );
@@ -506,6 +724,1412 @@ QDomElement QgsSymbolLayerV2Utils::saveSymbol( QString name, QgsSymbolV2* symbol
}
+bool QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( QDomElement& element,
+ QGis::GeometryType geomType,
+ QgsSymbolLayerV2List &layers )
+{
+ QgsDebugMsg( "Entered." );
+
+ if ( element.isNull() )
+ return false;
+
+ QgsSymbolLayerV2 *l = 0;
+
+ QString symbolizerName = element.localName();
+
+ if ( symbolizerName == "PointSymbolizer" )
+ {
+ // first check for Graphic element, nothing will be rendered if not found
+ QDomElement graphicElem = element.firstChildElement( "Graphic" );
+ if ( graphicElem.isNull() )
+ {
+ QgsDebugMsg( "Graphic element not found in PointSymbolizer" );
+ }
+ else
+ {
+ switch( geomType )
+ {
+ case QGis::Polygon:
+ // polygon layer and point symbolizer: draw poligon centroid
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "CentroidFill", element );
+ if ( l )
+ layers.append( l );
+
+ break;
+
+ case QGis::Point:
+ // point layer and point symbolizer: use markers
+ l = createMarkerLayerFromSld( element );
+ if ( l )
+ layers.append( l );
+
+ break;
+
+ case QGis::Line:
+ // line layer and point symbolizer: draw central point
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
+ if ( l )
+ layers.append( l );
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if ( symbolizerName == "LineSymbolizer" )
+ {
+ // check for Stroke element, nothing will be rendered if not found
+ QDomElement strokeElem = element.firstChildElement( "Stroke" );
+ if ( strokeElem.isNull() )
+ {
+ QgsDebugMsg( "Stroke element not found in LineSymbolizer" );
+ }
+ else
+ {
+ switch( geomType )
+ {
+ case QGis::Polygon:
+ case QGis::Line:
+ // polygon layer and line symbolizer: draw polygon outline
+ // line layer and line symbolizer: draw line
+ l = createLineLayerFromSld( element );
+ if ( l )
+ layers.append( l );
+
+ break;
+
+ case QGis::Point:
+ // point layer and line symbolizer: draw a little line marker
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
+ if ( l )
+ layers.append( l );
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if ( symbolizerName == "PolygonSymbolizer" )
+ {
+ // get Fill and Stroke elements, nothing will be rendered if both are missing
+ QDomElement fillElem = element.firstChildElement( "Fill" );
+ QDomElement strokeElem = element.firstChildElement( "Stroke" );
+ if ( fillElem.isNull() && strokeElem.isNull() )
+ {
+ QgsDebugMsg( "neither Fill nor Stroke element not found in PolygonSymbolizer" );
+ }
+ else
+ {
+ QgsSymbolLayerV2 *l = 0;
+
+ switch( geomType )
+ {
+ case QGis::Polygon:
+ // polygon layer and polygon symbolizer: draw fill
+
+ l = createFillLayerFromSld( element );
+ if ( l )
+ {
+ layers.append( l );
+
+ // SVGFill and SimpleFill symbolLayerV2 supports outline internally,
+ // so don't go forward to create a different symbolLayerV2 for outline
+ if ( l->layerType() == "SimpleFill" || l->layerType() == "SVGFill" )
+ break;
+ }
+
+ // now create polygon outline
+ // polygon layer and polygon symbolizer: draw polygon outline
+ l = createLineLayerFromSld( element );
+ if ( l )
+ layers.append( l );
+
+ break;
+
+ case QGis::Line:
+ // line layer and polygon symbolizer: draw line
+ l = createLineLayerFromSld( element );
+ if ( l )
+ layers.append( l );
+
+ break;
+
+ case QGis::Point:
+ // point layer and polygon symbolizer: draw a square marker
+ convertPolygonSymbolizerToPointMarker( element, layers );
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+QgsSymbolLayerV2* QgsSymbolLayerV2Utils::createFillLayerFromSld( QDomElement &element )
+{
+ QDomElement fillElem = element.firstChildElement( "Fill" );
+ if ( fillElem.isNull() )
+ {
+ QgsDebugMsg( "Fill element not found" );
+ return NULL;
+ }
+
+ QgsSymbolLayerV2 *l = 0;
+
+ if ( needLinePatternFill( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "LinePatternFill", element );
+ else if ( needPointPatternFill( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "PointPatternFill", element );
+ else if ( needSvgFill( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SVGFill", element );
+ else
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleFill", element );
+
+ return l;
+}
+
+QgsSymbolLayerV2* QgsSymbolLayerV2Utils::createLineLayerFromSld( QDomElement &element )
+{
+ QDomElement strokeElem = element.firstChildElement( "Stroke" );
+ if ( strokeElem.isNull() )
+ {
+ QgsDebugMsg( "Stroke element not found" );
+ return NULL;
+ }
+
+ QgsSymbolLayerV2 *l = 0;
+
+ if ( needMarkerLine( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
+ else
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleLine", element );
+
+ return l;
+}
+
+QgsSymbolLayerV2* QgsSymbolLayerV2Utils::createMarkerLayerFromSld( QDomElement &element )
+{
+ QDomElement graphicElem = element.firstChildElement( "Graphic" );
+ if ( graphicElem.isNull() )
+ {
+ QgsDebugMsg( "Graphic element not found" );
+ return NULL;
+ }
+
+ QgsSymbolLayerV2 *l = 0;
+
+ if ( needFontMarker( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "FontMarker", element );
+ else if ( needSvgMarker( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SvgMarker", element );
+ else if ( needEllipseMarker( element ) )
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "EllipseMarker", element );
+ else
+ l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
+
+ return l;
+}
+
+bool QgsSymbolLayerV2Utils::hasExternalGraphic( QDomElement &element )
+{
+ QDomElement graphicElem = element.firstChildElement( "Graphic" );
+ if( graphicElem.isNull() )
+ return false;
+
+ QDomElement externalGraphicElem = graphicElem.firstChildElement( "ExternalGraphic" );
+ if ( externalGraphicElem.isNull() )
+ return false;
+
+ // check for format
+ QDomElement formatElem = externalGraphicElem.firstChildElement( "Format" );
+ if ( formatElem.isNull() )
+ return false;
+
+ QString format = formatElem.firstChild().nodeValue();
+ if ( format != "image/svg+xml" )
+ {
+ QgsDebugMsg( "unsupported External Graphic format found: " + format );
+ return false;
+ }
+
+ // check for a valid content
+ QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( "OnlineResource" );
+ QDomElement inlineContentElem = externalGraphicElem.firstChildElement( "InlineContent" );
+ if ( !onlineResourceElem.isNull() )
+ {
+ return true;
+ }
+ else if ( !inlineContentElem.isNull() )
+ {
+ return false; // not implemented yet
+ }
+ else
+ {
+ return false;
+ }
+
+ return false;
+}
+
+bool QgsSymbolLayerV2Utils::hasWellKnownMark( QDomElement &element )
+{
+ QDomElement graphicElem = element.firstChildElement( "Graphic" );
+ if( graphicElem.isNull() )
+ return false;
+
+ QDomElement markElem = graphicElem.firstChildElement( "Mark" );
+ if ( markElem.isNull() )
+ return false;
+
+ QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
+ if ( wellKnownNameElem.isNull() )
+ return false;
+
+ return true;
+}
+
+
+bool QgsSymbolLayerV2Utils::needFontMarker( QDomElement &element )
+{
+ QDomElement graphicElem = element.firstChildElement( "Graphic" );
+ if( graphicElem.isNull() )
+ return false;
+
+ QDomElement markElem = graphicElem.firstChildElement( "Mark" );
+ if ( markElem.isNull() )
+ return false;
+
+ // check for format
+ QDomElement formatElem = markElem.firstChildElement( "Format" );
+ if ( formatElem.isNull() )
+ return false;
+
+ QString format = formatElem.firstChild().nodeValue();
+ if ( format != "ttf" )
+ {
+ QgsDebugMsg( "unsupported Graphic Mark format found: " + format );
+ return false;
+ }
+
+ // check for a valid content
+ QDomElement onlineResourceElem = markElem.firstChildElement( "OnlineResource" );
+ QDomElement inlineContentElem = markElem.firstChildElement( "InlineContent" );
+ if ( !onlineResourceElem.isNull() )
+ {
+ // mark with ttf format has a markIndex element
+ QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
+ if ( !markIndexElem.isNull() )
+ return true;
+ }
+ else if ( !inlineContentElem.isNull() )
+ {
+ return false; // not implemented yet
+ }
+
+ return false;
+}
+
+bool QgsSymbolLayerV2Utils::needSvgMarker( QDomElement &element )
+{
+ return hasExternalGraphic( element );
+}
+
+bool QgsSymbolLayerV2Utils::needEllipseMarker( QDomElement &element )
+{
+ QDomElement graphicElem = element.firstChildElement( "Graphic" );
+ if( graphicElem.isNull() )
+ return false;
+
+ QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
+ for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
+ {
+ if ( it.key() == "widthHeightFactor" )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool QgsSymbolLayerV2Utils::needMarkerLine( QDomElement &element )
+{
+ QDomElement strokeElem = element.firstChildElement( "Stroke" );
+ if( strokeElem.isNull() )
+ return false;
+
+ QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
+ if ( graphicStrokeElem.isNull() )
+ return false;
+
+ return hasWellKnownMark( graphicStrokeElem );
+}
+
+bool QgsSymbolLayerV2Utils::needLinePatternFill( QDomElement &element ) { Q_UNUSED( element ); return false; }
+bool QgsSymbolLayerV2Utils::needPointPatternFill( QDomElement &element ) { Q_UNUSED( element ); return false; }
+
+bool QgsSymbolLayerV2Utils::needSvgFill( QDomElement &element )
+{
+ QDomElement fillElem = element.firstChildElement( "Fill" );
+ if( fillElem.isNull() )
+ return false;
+
+ QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
+ if ( graphicFillElem.isNull() )
+ return false;
+
+ return hasExternalGraphic( graphicFillElem );
+}
+
+
+bool QgsSymbolLayerV2Utils::convertPolygonSymbolizerToPointMarker( QDomElement &element, QgsSymbolLayerV2List &layerList )
+{
+ QgsDebugMsg( "Entered." );
+
+ /* SE 1.1 says about PolygonSymbolizer:
+ if a point geometry is referenced instead of a polygon,
+ then a small, square, ortho-normal polygon should be
+ constructed for rendering.
+ */
+
+ QgsSymbolLayerV2List layers;
+
+ // retrieve both Fill and Stroke elements
+ QDomElement fillElem = element.firstChildElement( "Fill" );
+ QDomElement strokeElem = element.firstChildElement( "Stroke" );
+
+ // first symbol layer
+ {
+ bool validFill = false, validBorder = false;
+
+ // check for simple fill
+ // Fill element can contain some SvgParameter elements
+ QColor fillColor;
+ Qt::BrushStyle fillStyle;
+
+ if ( fillFromSld( fillElem, fillStyle, fillColor ) )
+ validFill = true;
+
+ // check for simple outline
+ // Stroke element can contain some SvgParameter elements
+ QColor borderColor;
+ Qt::PenStyle borderStyle;
+ double borderWidth = 1.0, dashOffset = 0.0;
+ QVector<qreal> customDashPattern;
+
+ if ( lineFromSld( strokeElem, borderStyle, borderColor, borderWidth,
+ 0, 0, &customDashPattern, &dashOffset ) )
+ validBorder = true;
+
+ if ( validFill || validBorder )
+ {
+ QgsStringMap map;
+ map["name"] = "square";
+ map["color"] = QgsSymbolLayerV2Utils::encodeColor( validFill ? fillColor : Qt::transparent );
+ map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( validBorder ? borderColor : Qt::transparent );
+ map["size"] = QString::number( 6 );
+ map["angle"] = QString::number( 0 );
+ map["offset"] = QgsSymbolLayerV2Utils::encodePoint( QPointF( 0, 0 ) );
+ layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SimpleMarker", map ) );
+ }
+ }
+
+ // second symbol layer
+ {
+ bool validFill = false, validBorder = false;
+
+ // check for graphic fill
+ QString name, format;
+ int markIndex = -1;
+ QColor fillColor, borderColor;
+ double borderWidth = 1, size, angle = 0.0;
+ QPointF anchor, offset;
+
+ // Fill element can contain a GraphicFill element
+ QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
+ if ( !graphicFillElem.isNull() )
+ {
+ // GraphicFill element must contain a Graphic element
+ QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
+ if( !graphicElem.isNull() )
+ {
+ // Graphic element can contains some ExternalGraphic and Mark element
+ // search for the first supported one and use it
+ bool found = false;
+
+ QDomElement graphicChildElem = graphicElem.firstChildElement();
+ while ( !graphicChildElem.isNull() )
+ {
+ if ( graphicChildElem.localName() == "Mark" )
+ {
+ // check for a well known name
+ QDomElement wellKnownNameElem = graphicChildElem.firstChildElement( "WellKnownName" );
+ if ( !wellKnownNameElem.isNull() )
+ {
+ name = wellKnownNameElem.firstChild().nodeValue();
+ found = true;
+ break;
+ }
+ }
+
+ if ( graphicChildElem.localName() == "ExternalGraphic" || graphicChildElem.localName() == "Mark" )
+ {
+ // check for external graphic format
+ QDomElement formatElem = graphicChildElem.firstChildElement( "Format" );
+ if ( formatElem.isNull() )
+ continue;
+
+ format = formatElem.firstChild().nodeValue();
+
+ // TODO: remove this check when more formats will be supported
+ // only SVG external graphics are supported in this moment
+ if ( graphicChildElem.localName() == "ExternalGraphic" && format != "image/svg+xml" )
+ continue;
+
+ // TODO: remove this check when more formats will be supported
+ // only ttf marks are supported in this moment
+ if ( graphicChildElem.localName() == "Mark" && format != "ttf" )
+ continue;
+
+ // check for a valid content
+ QDomElement onlineResourceElem = graphicChildElem.firstChildElement( "OnlineResource" );
+ QDomElement inlineContentElem = graphicChildElem.firstChildElement( "InlineContent" );
+
+ if ( !onlineResourceElem.isNull() )
+ {
+ name = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
+
+ if ( graphicChildElem.localName() == "Mark" && format == "ttf" )
+ {
+ // mark with ttf format may have a name like ttf://fontFamily
+ if ( name.startsWith( "ttf://" ) )
+ name = name.mid( 6 );
+
+ // mark with ttf format has a markIndex element
+ QDomElement markIndexElem = graphicChildElem.firstChildElement( "MarkIndex" );
+ if ( markIndexElem.isNull() )
+ continue;
+
+ bool ok;
+ int v = markIndexElem.firstChild().nodeValue().toInt( &ok );
+ if ( !ok || v < 0 )
+ continue;
+
+ markIndex = v;
+ }
+
+ found = true;
+ break;
+ }
+ else if ( !inlineContentElem.isNull() )
+ continue; // TODO: not implemeneted yet
+ else
+ continue;
+ }
+
+ // if Mark element is present but it doesn't contains neither
+ // WellKnownName nor OnlineResource nor InlineContent,
+ // use the default mark (square)
+ if ( graphicChildElem.localName() == "Mark" )
+ {
+ name = "square";
+ found = true;
+ break;
+ }
+ }
+
+ // if found a valid Mark, check for its Fill and Stroke element
+ if ( found && graphicChildElem.localName() == "Mark" )
+ {
+ // XXX: recursive definition!?! couldn't be dangerous???
+ // to avoid recursion we handle only simple fill and simple stroke
+
+ // check for simple fill
+ // Fill element can contain some SvgParameter elements
+ Qt::BrushStyle markFillStyle;
+
+ QDomElement markFillElem = graphicChildElem.firstChildElement( "Fill" );
+ if ( fillFromSld( markFillElem, markFillStyle, fillColor ) )
+ validFill = true;
+
+ // check for simple outline
+ // Stroke element can contain some SvgParameter elements
+ Qt::PenStyle borderStyle;
+ double borderWidth = 1.0, dashOffset = 0.0;
+ QVector<qreal> customDashPattern;
+
+ QDomElement markStrokeElem = graphicChildElem.firstChildElement( "Stroke" );
+ if ( lineFromSld( markStrokeElem, borderStyle, borderColor, borderWidth,
+ 0, 0, &customDashPattern, &dashOffset ) )
+ validBorder = true;
+ }
+
+ if ( found )
+ {
+ // check for Opacity, Size, Rotation, AnchorPoint, Displacement
+ QDomElement opacityElem = graphicElem.firstChildElement( "Opacity" );
+ if ( !opacityElem.isNull() )
+ fillColor.setAlpha( decodeSldAlpha( opacityElem.firstChild().nodeValue() ) );
+
+ QDomElement sizeElem = graphicElem.firstChildElement( "Size" );
+ if ( !sizeElem.isNull() )
+ {
+ bool ok;
+ double v = sizeElem.firstChild().nodeValue().toDouble( &ok );
+ if ( ok && v > 0 )
+ size = v;
+ }
+
+ QString angleFunc;
+ if ( rotationFromSldElement( graphicElem, angleFunc ) && !angleFunc.isEmpty() )
+ {
+ bool ok;
+ double v = angleFunc.toDouble( &ok );
+ if ( ok )
+ angle = v;
+ }
+
+ displacementFromSldElement( graphicElem, offset );
+ }
+ }
+ }
+
+ if ( validFill || validBorder )
+ {
+ if ( format == "image/svg+xml" )
+ {
+ QgsStringMap map;
+ map["name"] = name;
+ map["fill"] = fillColor.name();
+ map["outline"] = borderColor.name();
+ map["outline-width"] = QString::number( borderWidth );
+ if ( size > 0 )
+ map["size"] = QString::number( size );
+ if ( !doubleNear( angle, 0.0 ) )
+ map["angle"] = QString::number( angle );
+ if ( !offset.isNull() )
+ map["offset"] = QgsSymbolLayerV2Utils::encodePoint( offset );
+ layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SvgMarker", map ) );
+ }
+ else if ( format == "ttf" )
+ {
+ QgsStringMap map;
+ map["font"] = name;
+ map["chr"] = markIndex;
+ map["color"] = QgsSymbolLayerV2Utils::encodeColor( validFill ? fillColor : Qt::transparent );
+ if ( size > 0 )
+ map["size"] = QString::number( size );
+ if ( !doubleNear( angle, 0.0 ) )
+ map["angle"] = QString::number( angle );
+ if ( !offset.isNull() )
+ map["offset"] = QgsSymbolLayerV2Utils::encodePoint( offset );
+ layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "FontMarker", map ) );
+ }
+ }
+ }
+
+ if ( layers.isEmpty() )
+ return false;
+
+ layerList << layers;
+ layers.clear();
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::fillToSld( QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, QColor color )
+{
+ QString patternName;
+ switch ( brushStyle )
+ {
+ case Qt::NoBrush:
+ return;
+
+ case Qt::SolidPattern:
+ if ( color.isValid() )
+ {
+ element.appendChild( createSvgParameterElement( doc, "fill", color.name() ) );
+ if ( color.alpha() < 255 )
+ element.appendChild( createSvgParameterElement( doc, "fill-opacity", encodeSldAlpha( color.alpha() ) ) );
+ }
+ return;
+
+ case Qt::CrossPattern:
+ case Qt::DiagCrossPattern:
+ case Qt::HorPattern:
+ case Qt::VerPattern:
+ case Qt::BDiagPattern:
+ case Qt::FDiagPattern:
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ patternName = encodeSldBrushStyle( brushStyle );
+ break;
+
+ default:
+ element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( brushStyle ) ) );
+ return;
+ }
+
+ QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
+ element.appendChild( graphicFillElem );
+
+ QDomElement graphicElem = doc.createElement( "se:Graphic" );
+ graphicFillElem.appendChild( graphicElem );
+
+ QColor fillColor = patternName.startsWith( "brush://" ) ? color : QColor();
+ QColor borderColor = !patternName.startsWith( "brush://" ) ? color : QColor();
+
+ /* Use WellKnownName tag to handle QT brush styles. */
+ wellKnownMarkerToSld( doc, graphicFillElem, patternName, fillColor, borderColor );
+}
+
+bool QgsSymbolLayerV2Utils::fillFromSld( QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color )
+{
+ QgsDebugMsg( "Entered." );
+
+ brushStyle = Qt::SolidPattern;
+ color = QColor( "#808080" );
+
+ if ( element.isNull() )
+ {
+ brushStyle = Qt::NoBrush;
+ color = QColor();
+ return true;
+ }
+
+ QDomElement graphicFillElem = element.firstChildElement( "GraphicFill" );
+ // if no GraphicFill element is found, it's a solid fill
+ if ( graphicFillElem.isNull() )
+ {
+ QgsStringMap svgParams = getSvgParameterList( element );
+ for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
+ {
+ QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key() ).arg( it.value() ) );
+
+ if ( it.key() == "fill" )
+ color = QColor( it.value() );
+ else if ( it.key() == "fill-opacity" )
+ color.setAlpha( decodeSldAlpha( it.value() ) );
+ }
+ }
+ else // wellKnown marker
+ {
+ QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
+ if ( graphicElem.isNull() )
+ return false; // Graphic is required within GraphicFill
+
+ QString patternName = "square";
+ QColor fillColor, borderColor;
+ double borderWidth, size;
+ if ( !wellKnownMarkerFromSld( graphicFillElem, patternName, fillColor, borderColor, borderWidth, size ) )
+ return false;
+
+ brushStyle = decodeSldBrushStyle( patternName );
+ if ( brushStyle == Qt::NoBrush )
+ return false; // unable to decode brush style
+
+ QColor c = patternName.startsWith( "brush://" ) ? fillColor : borderColor;
+ if ( c.isValid() )
+ color = c;
+ }
+
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::lineToSld( QDomDocument &doc, QDomElement &element,
+ Qt::PenStyle penStyle, QColor color, double width,
+ const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle,
+ const QVector<qreal> *customDashPattern, double dashOffset )
+{
+ QVector<qreal> dashPattern;
+ if ( penStyle == Qt::CustomDashLine && !customDashPattern )
+ {
+ element.appendChild( doc.createComment( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) );
+ penStyle = Qt::DashLine;
+ customDashPattern = &dashPattern;
+ }
+
+ switch ( penStyle )
+ {
+ case Qt::NoPen:
+ return;
+
+ case Qt::SolidLine:
+ break;
+
+ case Qt::DashLine:
+ dashPattern.push_back( 4.0 );
+ dashPattern.push_back( 2.0 );
+ break;
+ case Qt::DotLine:
+ dashPattern.push_back( 1.0 );
+ dashPattern.push_back( 2.0 );
+ break;
+ case Qt::DashDotLine:
+ dashPattern.push_back( 4.0 );
+ dashPattern.push_back( 2.0 );
+ dashPattern.push_back( 1.0 );
+ dashPattern.push_back( 2.0 );
+ break;
+ case Qt::DashDotDotLine:
+ dashPattern.push_back( 4.0 );
+ dashPattern.push_back( 2.0 );
+ dashPattern.push_back( 1.0 );
+ dashPattern.push_back( 2.0 );
+ dashPattern.push_back( 1.0 );
+ dashPattern.push_back( 2.0 );
+ break;
+
+ case Qt::CustomDashLine:
+ Q_ASSERT( customDashPattern );
+ break;
+
+ default:
+ element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) );
+ return;
+ }
+
+ if ( color.isValid() )
+ {
+ element.appendChild( createSvgParameterElement( doc, "stroke", color.name() ) );
+ if ( color.alpha() < 255 )
+ element.appendChild( createSvgParameterElement( doc, "stroke-opacity", encodeSldAlpha( color.alpha() ) ) );
+ }
+ if ( width > 0 )
+ element.appendChild( createSvgParameterElement( doc, "stroke-width", QString::number( width ) ) );
+ if ( penJoinStyle )
+ element.appendChild( createSvgParameterElement( doc, "stroke-linejoin", encodeSldLineJoinStyle( *penJoinStyle ) ) );
+ if ( penCapStyle )
+ element.appendChild( createSvgParameterElement( doc, "stroke-linecap", encodeSldLineCapStyle( *penCapStyle ) ) );
+
+ if ( customDashPattern && customDashPattern->size() > 0 )
+ {
+ element.appendChild( createSvgParameterElement( doc, "stroke-dasharray", encodeSldRealVector( *customDashPattern ) ) );
+ if ( !doubleNear( dashOffset, 0.0 ) )
+ element.appendChild( createSvgParameterElement( doc, "stroke-dashoffset", QString::number( dashOffset ) ) );
+ }
+}
+
+
+bool QgsSymbolLayerV2Utils::lineFromSld( QDomElement &element,
+ Qt::PenStyle &penStyle, QColor &color, double &width,
+ Qt::PenJoinStyle *penJoinStyle, Qt::PenCapStyle *penCapStyle,
+ QVector<qreal> *customDashPattern, double *dashOffset )
+{
+ QgsDebugMsg( "Entered." );
+
+ penStyle = Qt::SolidLine;
+ color = QColor( "#000000" );
+ width = 1;
+ if ( penJoinStyle )
+ *penJoinStyle = Qt::BevelJoin;
+ if ( penCapStyle )
+ *penCapStyle = Qt::SquareCap;
+ if ( customDashPattern )
+ customDashPattern->clear();
+ if ( dashOffset )
+ *dashOffset = 0;
+
+ if ( element.isNull() )
+ {
+ penStyle = Qt::NoPen;
+ color = QColor();
+ return true;
+ }
+
+ QgsStringMap svgParams = getSvgParameterList( element );
+ for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
+ {
+ QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key() ).arg( it.value() ) );
+
+ if ( it.key() == "stroke" )
+ {
+ color = QColor( it.value() );
+ }
+ else if ( it.key() == "stroke-opacity" )
+ {
+ color.setAlpha( decodeSldAlpha( it.value() ) );
+ }
+ else if ( it.key() == "stroke-width" )
+ {
+ bool ok;
+ double w = it.value().toDouble( &ok );
+ if ( ok )
+ width = w;
+ }
+ else if ( it.key() == "stroke-linejoin" && penJoinStyle )
+ {
+ *penJoinStyle = decodeSldLineJoinStyle( it.value() );
+ }
+ else if ( it.key() == "stroke-linecap" && penCapStyle )
+ {
+ *penCapStyle = decodeSldLineCapStyle( it.value() );
+ }
+ else if ( it.key() == "stroke-dasharray" && customDashPattern )
+ {
+ *customDashPattern = decodeSldRealVector( it.value() );
+ if ( customDashPattern->size() > 0 )
+ {
+ // convert the dasharray to one of the QT pen style,
+ // if no match is found then set pen style to CustomDashLine
+ bool dashPatternFound = false;
+
+ if ( customDashPattern->count() == 2 )
+ {
+ if ( customDashPattern->at( 0 ) == 4.0 &&
+ customDashPattern->at( 1 ) == 2.0 )
+ {
+ penStyle = Qt::DashLine;
+ dashPatternFound = true;
+ }
+ else if ( customDashPattern->at( 0 ) == 1.0 &&
+ customDashPattern->at( 1 ) == 2.0 )
+ {
+ penStyle = Qt::DotLine;
+ dashPatternFound = true;
+ }
+ }
+ else if ( customDashPattern->count() == 4 )
+ {
+ if ( customDashPattern->at( 0 ) == 4.0 &&
+ customDashPattern->at( 1 ) == 2.0 &&
+ customDashPattern->at( 2 ) == 1.0 &&
+ customDashPattern->at( 3 ) == 2.0 )
+ {
+ penStyle = Qt::DashDotLine;
+ dashPatternFound = true;
+ }
+ }
+ else if ( customDashPattern->count() == 6 )
+ {
+ if ( customDashPattern->at( 0 ) == 4.0 &&
+ customDashPattern->at( 1 ) == 2.0 &&
+ customDashPattern->at( 2 ) == 1.0 &&
+ customDashPattern->at( 3 ) == 2.0 &&
+ customDashPattern->at( 4 ) == 1.0 &&
+ customDashPattern->at( 5 ) == 2.0 )
+ {
+ penStyle = Qt::DashDotDotLine;
+ dashPatternFound = true;
+ }
+ }
+
+ // default case: set pen style to CustomDashLine
+ if ( !dashPatternFound )
+ {
+ penStyle = Qt::CustomDashLine;
+ }
+ }
+ }
+ else if ( it.key() == "stroke-dashoffset" && dashOffset )
+ {
+ bool ok;
+ double d = it.value().toDouble( &ok );
+ if ( ok )
+ *dashOffset = d;
+ }
+ }
+
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::externalGraphicToSld( QDomDocument &doc, QDomElement &element,
+ QString path, QString mime,
+ QColor color, double size )
+{
+ QDomElement externalGraphicElem = doc.createElement( "se:ExternalGraphic" );
+ element.appendChild( externalGraphicElem );
+
+ createOnlineResourceElement( doc, externalGraphicElem, path, mime );
+
+ //TODO: missing a way to handle svg color. Should use <se:ColorReplacement>
+ Q_UNUSED( color );
+
+ if ( size >= 0 )
+ {
+ QDomElement sizeElem = doc.createElement( "se:Size" );
+ sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
+ element.appendChild( sizeElem );
+ }
+}
+
+bool QgsSymbolLayerV2Utils::externalGraphicFromSld( QDomElement &element,
+ QString &path, QString &mime,
+ QColor &color, double &size )
+{
+ QgsDebugMsg( "Entered." );
+ Q_UNUSED( color );
+
+ QDomElement externalGraphicElem = element.firstChildElement( "ExternalGraphic" );
+ if ( externalGraphicElem.isNull() )
+ return false;
+
+ onlineResourceFromSldElement( externalGraphicElem, path, mime );
+
+ QDomElement sizeElem = element.firstChildElement( "Size" );
+ if ( !sizeElem.isNull() )
+ {
+ bool ok;
+ double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
+ if ( ok )
+ size = s;
+ }
+
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::externalMarkerToSld( QDomDocument &doc, QDomElement &element,
+ QString path, QString format, int *markIndex,
+ QColor color, double size )
+{
+ QDomElement markElem = doc.createElement( "se:Mark" );
+ element.appendChild( markElem );
+
+ createOnlineResourceElement( doc, markElem, path, format );
+
+ if ( markIndex )
+ {
+ QDomElement markIndexElem = doc.createElement( "se:MarkIndex" );
+ markIndexElem.appendChild( doc.createTextNode( QString::number( *markIndex ) ) );
+ markElem.appendChild( markIndexElem );
+ }
+
+ // <Fill>
+ QDomElement fillElem = doc.createElement( "se:Fill" );
+ fillToSld( doc, fillElem, Qt::SolidPattern, color );
+ markElem.appendChild( fillElem );
+
+ // <Size>
+ if ( !doubleNear( size, 0.0 ) && size > 0 )
+ {
+ QDomElement sizeElem = doc.createElement( "se:Size" );
+ sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
+ element.appendChild( sizeElem );
+ }
+}
+
+bool QgsSymbolLayerV2Utils::externalMarkerFromSld( QDomElement &element,
+ QString &path, QString &format, int &markIndex,
+ QColor &color, double &size )
+{
+ QgsDebugMsg( "Entered." );
+
+ color = QColor();
+ markIndex = -1;
+ size = -1;
+
+ QDomElement markElem = element.firstChildElement( "Mark" );
+ if ( markElem.isNull() )
+ return false;
+
+ onlineResourceFromSldElement( markElem, path, format );
+
+ QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
+ if ( !markIndexElem.isNull() )
+ {
+ bool ok;
+ int i = markIndexElem.firstChild().nodeValue().toInt( &ok );
+ if ( ok )
+ markIndex = i;
+ }
+
+ // <Fill>
+ QDomElement fillElem = markElem.firstChildElement( "Fill" );
+ Qt::BrushStyle b = Qt::SolidPattern;
+ fillFromSld( fillElem, b, color );
+ // ignore brush style, solid expected
+
+ // <Size>
+ QDomElement sizeElem = element.firstChildElement( "Size" );
+ if ( !sizeElem.isNull() )
+ {
+ bool ok;
+ double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
+ if ( ok )
+ size = s;
+ }
+
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::wellKnownMarkerToSld( QDomDocument &doc, QDomElement &element,
+ QString name, QColor color, QColor borderColor,
+ double borderWidth, double size )
+{
+ QDomElement markElem = doc.createElement( "se:Mark" );
+ element.appendChild( markElem );
+
+ QDomElement wellKnownNameElem = doc.createElement( "se:WellKnownName" );
+ wellKnownNameElem.appendChild( doc.createTextNode( name ) );
+ markElem.appendChild( wellKnownNameElem );
+
+ // <Fill>
+ if ( color.isValid() )
+ {
+ QDomElement fillElem = doc.createElement( "se:Fill" );
+ fillToSld( doc, fillElem, Qt::SolidPattern, color );
+ markElem.appendChild( fillElem );
+ }
+
+ // <Stroke>
+ if ( borderColor.isValid() )
+ {
+ QDomElement strokeElem = doc.createElement( "se:Stroke" );
+ lineToSld( doc, strokeElem, Qt::SolidLine, borderColor, borderWidth );
+ markElem.appendChild( strokeElem );
+ }
+
+ // <Size>
+ if ( !doubleNear( size, 0.0 ) && size > 0 )
+ {
+ QDomElement sizeElem = doc.createElement( "se:Size" );
+ sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
+ element.appendChild( sizeElem );
+ }
+}
+
+bool QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( QDomElement &element,
+ QString &name, QColor &color, QColor &borderColor,
+ double &borderWidth, double &size )
+{
+ QgsDebugMsg( "Entered." );
+
+ name = "square";
+ color = QColor();
+ borderColor = QColor( "#000000" );
+ borderWidth = 1;
+ size = 6;
+
+ QDomElement markElem = element.firstChildElement( "Mark" );
+ if ( markElem.isNull() )
+ return false;
+
+ QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
+ if ( !wellKnownNameElem.isNull() )
+ {
+ name = wellKnownNameElem.firstChild().nodeValue();
+ QgsDebugMsg( "found Mark with well known name: " + name );
+ }
+
+ // <Fill>
+ QDomElement fillElem = markElem.firstChildElement( "Fill" );
+ Qt::BrushStyle b = Qt::SolidPattern;
+ fillFromSld( fillElem, b, color );
+ // ignore brush style, solid expected
+
+ // <Stroke>
+ QDomElement strokeElem = markElem.firstChildElement( "Stroke" );
+ Qt::PenStyle p = Qt::SolidLine;
+ lineFromSld( strokeElem, p, borderColor, borderWidth );
+ // ignore border style, solid expected
+
+ // <Size>
+ QDomElement sizeElem = element.firstChildElement( "Size" );
+ if ( !sizeElem.isNull() )
+ {
+ bool ok;
+ double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
+ if ( ok )
+ size = s;
+ }
+
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::createRotationElement( QDomDocument &doc, QDomElement &element, QString rotationFunc )
+{
+ if ( !rotationFunc.isEmpty() )
+ {
+ QDomElement rotationElem = doc.createElement( "se:Rotation" );
+ createFunctionElement( doc, rotationElem, rotationFunc );
+ element.appendChild( rotationElem );
+ }
+}
+
+bool QgsSymbolLayerV2Utils::rotationFromSldElement( QDomElement &element, QString &rotationFunc )
+{
+ QDomElement rotationElem = element.firstChildElement( "Rotation" );
+ if ( !rotationElem.isNull() )
+ {
+ functionFromSldElement( rotationElem, rotationFunc );
+ }
+ return true;
+}
+
+
+void QgsSymbolLayerV2Utils::createOpacityElement( QDomDocument &doc, QDomElement &element, QString alphaFunc )
+{
+ if ( !alphaFunc.isEmpty() )
+ {
+ QDomElement opacityElem = doc.createElement( "se:Opacity" );
+ createFunctionElement( doc, opacityElem, alphaFunc );
+ element.appendChild( opacityElem );
+ }
+}
+
+bool QgsSymbolLayerV2Utils::opacityFromSldElement( QDomElement &element, QString &alphaFunc )
+{
+ QDomElement opacityElem = element.firstChildElement( "Opacity" );
+ if ( !opacityElem.isNull() )
+ {
+ functionFromSldElement( opacityElem, alphaFunc );
+ }
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::createDisplacementElement( QDomDocument &doc, QDomElement &element, QPointF offset )
+{
+ if ( offset.isNull() )
+ return;
+
+ QDomElement displacementElem = doc.createElement( "se:Displacement" );
+ element.appendChild( displacementElem );
+
+ QDomElement dispXElem = doc.createElement( "se:DisplacementX" );
+ dispXElem.appendChild( doc.createTextNode( QString::number( offset.x() ) ) );
+
+ QDomElement dispYElem = doc.createElement( "se:DisplacementY" );
+ dispYElem.appendChild( doc.createTextNode( QString::number( offset.y() ) ) );
+
+ displacementElem.appendChild( dispXElem );
+ displacementElem.appendChild( dispYElem );
+}
+
+bool QgsSymbolLayerV2Utils::displacementFromSldElement( QDomElement &element, QPointF &offset )
+{
+ offset = QPointF( 0, 0 );
+
+ QDomElement displacementElem = element.firstChildElement( "Displacement" );
+ if ( displacementElem.isNull() )
+ return true;
+
+ QDomElement dispXElem = element.firstChildElement( "DisplacementX" );
+ if ( !dispXElem.isNull() )
+ {
+ bool ok;
+ double offsetX = dispXElem.firstChild().nodeValue().toDouble( &ok );
+ if ( ok )
+ offset.setX( offsetX );
+ }
+
+ QDomElement dispYElem = element.firstChildElement( "DisplacementY" );
+ if ( !dispYElem.isNull() )
+ {
+ bool ok;
+ double offsetY = dispYElem.firstChild().nodeValue().toDouble( &ok );
+ if ( ok )
+ offset.setY( offsetY );
+ }
+
+ return true;
+}
+
+void QgsSymbolLayerV2Utils::labelTextToSld( QDomDocument &doc, QDomElement &element,
+ QString label, QFont font,
+ QColor color, double size )
+{
+ QDomElement labelElem = doc.createElement( "se:Label" );
+ labelElem.appendChild( doc.createTextNode( label ) );
+ element.appendChild( labelElem );
+
+ QDomElement fontElem = doc.createElement( "se:Font" );
+ element.appendChild( fontElem );
+
+ fontElem.appendChild( createSvgParameterElement( doc, "font-family", font.family() ) );
+#if 0
+ fontElem.appendChild( createSldParameterElement( doc, "font-style", encodeSldFontStyle( font.style() ) ) );
+ fontElem.appendChild( createSldParameterElement( doc, "font-weight", encodeSldFontWeight( font.weight() ) ) );
+#endif
+ fontElem.appendChild( createSvgParameterElement( doc, "font-size", QString::number( size ) ) );
+
+ // <Fill>
+ if ( color.isValid() )
+ {
+ QDomElement fillElem = doc.createElement( "Fill" );
+ fillToSld( doc, fillElem, Qt::SolidPattern, color );
+ element.appendChild( fillElem );
+ }
+}
+
+void QgsSymbolLayerV2Utils::createGeometryElement( QDomDocument &doc, QDomElement &element, QString geomFunc )
+{
+ if ( geomFunc.isEmpty() )
+ return;
+
+ QDomElement geometryElem = doc.createElement( "Geometry" );
+ element.appendChild( geometryElem );
+
+ /* About using a function withing the Geometry tag.
+ *
+ * The SLD specification <= 1.1 is vague:
+ * "In principle, a fixed geometry could be defined using GML or
+ * operators could be defined for computing the geometry from
+ * references or literals. However, using a feature property directly
+ * is by far the most commonly useful method."
+ *
+ * Even if it seems that specs should take care all the possible cases,
+ * looking at the XML schema fragment that encodes the Geometry element,
+ * it has to be a PropertyName element:
+ * <xsd:element name="Geometry">
+ * <xsd:complexType>
+ * <xsd:sequence>
+ * <xsd:element ref="ogc:PropertyName"/>
+ * </xsd:sequence>
+ * </xsd:complexType>
+ * </xsd:element>
+ *
+ * Anyway we will use a ogc:Function to handle geometry transformations
+ * like offset, centroid, ...
+ */
+
+ createFunctionElement( doc, geometryElem, geomFunc );
+}
+
+bool QgsSymbolLayerV2Utils::geometryFromSldElement( QDomElement &element, QString &geomFunc )
+{
+ QDomElement geometryElem = element.firstChildElement( "Geometry" );
+ if ( geometryElem.isNull() )
+ return true;
+
+ return functionFromSldElement( geometryElem, geomFunc );
+}
+
+bool QgsSymbolLayerV2Utils::createFunctionElement( QDomDocument &doc, QDomElement &element, QString function )
+{
+ // let's use QgsExpression to generate the SLD for the function
+ QgsExpression expr( function );
+ if ( expr.hasParserError() )
+ {
+ element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
+ return false;
+ }
+ expr.toOgcFilter( doc, element );
+ return true;
+}
+
+bool QgsSymbolLayerV2Utils::functionFromSldElement( QDomElement &element, QString &function )
+{
+ QgsDebugMsg( "Entered." );
+
+ QgsExpression *expr = QgsExpression::createFromOgcFilter( element );
+ if ( !expr )
+ return false;
+
+ bool valid = expr->hasParserError();
+ if ( !valid )
+ {
+ QgsDebugMsg( "parser error: " + expr->parserErrorString() );
+ }
+ else
+ {
+ function = expr->dump();
+ }
+
+ delete expr;
+ return valid;
+}
+
+void QgsSymbolLayerV2Utils::createOnlineResourceElement( QDomDocument &doc, QDomElement &element,
+ QString path, QString format )
+{
+ QDomElement onlineResourceElem = doc.createElement( "OnlineResource" );
+ onlineResourceElem.setAttribute( "xlink:type", "simple" );
+ onlineResourceElem.setAttribute( "xlink:href", path );
+ element.appendChild( onlineResourceElem );
+
+ QDomElement formatElem = doc.createElement( "Format" );
+ formatElem.appendChild( doc.createTextNode( format ) );
+ element.appendChild( formatElem );
+}
+
+bool QgsSymbolLayerV2Utils::onlineResourceFromSldElement( QDomElement &element, QString &path, QString &format )
+{
+ QgsDebugMsg( "Entered." );
+
+ QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" );
+ if ( onlineResourceElem.isNull() )
+ return false;
+
+ path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
+
+ QDomElement formatElem = element.firstChildElement( "Format" );
+ if ( formatElem.isNull() )
+ return false; // OnlineResource requires a Format sibling element
+
+ format = formatElem.firstChild().nodeValue();
+ return true;
+}
+
+
+QDomElement QgsSymbolLayerV2Utils::createSvgParameterElement( QDomDocument &doc, QString name, QString value )
+{
+ QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
+ nodeElem.setAttribute( "name", name );
+ nodeElem.appendChild( doc.createTextNode( value ) );
+ return nodeElem;
+}
+
+QgsStringMap QgsSymbolLayerV2Utils::getSvgParameterList( QDomElement &element )
+{
+ QgsStringMap params;
+
+ QDomElement paramElem = element.firstChildElement();
+ while( !paramElem.isNull() )
+ {
+ if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" )
+ {
+ QString name = paramElem.attribute( "name" );
+ QString value = paramElem.firstChild().nodeValue();
+
+ if ( !name.isEmpty() && !value.isEmpty() )
+ params[ name ] = value;
+ }
+
+ paramElem = paramElem.nextSiblingElement();
+ }
+
+ return params;
+}
+
+QDomElement QgsSymbolLayerV2Utils::createVendorOptionElement( QDomDocument &doc, QString name, QString value )
+{
+ QDomElement nodeElem = doc.createElement( "VendorOption" );
+ nodeElem.setAttribute( "name", name );
+ nodeElem.appendChild( doc.createTextNode( value ) );
+ return nodeElem;
+}
+
+QgsStringMap QgsSymbolLayerV2Utils::getVendorOptionList( QDomElement &element )
+{
+ QgsStringMap params;
+
+ QDomElement paramElem = element.firstChildElement( "VendorOption" );
+ while( !paramElem.isNull() )
+ {
+ QString name = paramElem.attribute( "name" );
+ QString value = paramElem.firstChild().nodeValue();
+
+ if ( !name.isEmpty() && !value.isEmpty() )
+ params[ name ] = value;
+
+ paramElem = paramElem.nextSiblingElement( "VendorOption" );
+ }
+
+ return params;
+}
+
+
QgsStringMap QgsSymbolLayerV2Utils::parseProperties( QDomElement& element )
{
QgsStringMap props;
@@ -643,7 +2267,7 @@ QDomElement QgsSymbolLayerV2Utils::saveSymbols( QgsSymbolV2Map& symbols, QString
void QgsSymbolLayerV2Utils::clearSymbolMap( QgsSymbolV2Map& symbols )
{
foreach( QString name, symbols.keys() )
- delete symbols.value( name );
+ delete symbols.value( name );
symbols.clear();
}
diff --git a/src/core/symbology-ng/qgssymbollayerv2utils.h b/src/core/symbology-ng/qgssymbollayerv2utils.h
index 4631e90..3f75cd0 100644
--- a/src/core/symbology-ng/qgssymbollayerv2utils.h
+++ b/src/core/symbology-ng/qgssymbollayerv2utils.h
@@ -6,16 +6,17 @@
#include <QMap>
#include <Qt>
#include <QtCore>
+#include <QFont>
+#include <QColor>
#include "qgssymbolv2.h"
+#include "qgis.h"
-class QgsSymbolV2;
class QgsSymbolLayerV2;
class QgsVectorColorRampV2;
typedef QMap<QString, QString> QgsStringMap;
typedef QMap<QString, QgsSymbolV2* > QgsSymbolV2Map;
-class QColor;
class QDomDocument;
class QDomElement;
class QIcon;
@@ -30,6 +31,15 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
static QString encodeColor( QColor color );
static QColor decodeColor( QString str );
+ static QString encodeSldAlpha( int alpha );
+ static int decodeSldAlpha( QString str );
+
+ static QString encodeSldFontStyle( QFont::Style style );
+ static QFont::Style decodeSldFontStyle( QString str );
+
+ static QString encodeSldFontWeight( int weight );
+ static int decodeSldFontWeight( QString str );
+
static QString encodePenStyle( Qt::PenStyle style );
static Qt::PenStyle decodePenStyle( QString str );
@@ -39,18 +49,33 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
static QString encodePenCapStyle( Qt::PenCapStyle style );
static Qt::PenCapStyle decodePenCapStyle( QString str );
+ static QString encodeSldLineJoinStyle( Qt::PenJoinStyle style );
+ static Qt::PenJoinStyle decodeSldLineJoinStyle( QString str );
+
+ static QString encodeSldLineCapStyle( Qt::PenCapStyle style );
+ static Qt::PenCapStyle decodeSldLineCapStyle( QString str );
+
static QString encodeBrushStyle( Qt::BrushStyle style );
static Qt::BrushStyle decodeBrushStyle( QString str );
+ static QString encodeSldBrushStyle( Qt::BrushStyle style );
+ static Qt::BrushStyle decodeSldBrushStyle( QString str );
+
static QString encodePoint( QPointF point );
static QPointF decodePoint( QString str );
static QString encodeRealVector( const QVector<qreal>& v );
static QVector<qreal> decodeRealVector( const QString& s );
+ static QString encodeSldRealVector( const QVector<qreal>& v );
+ static QVector<qreal> decodeSldRealVector( const QString& s );
+
static QString encodeOutputUnit( QgsSymbolV2::OutputUnit unit );
static QgsSymbolV2::OutputUnit decodeOutputUnit( QString str );
+ static QString encodeSldUom( QgsSymbolV2::OutputUnit unit, double *scaleFactor );
+ static QgsSymbolV2::OutputUnit decodeSldUom( QString str, double *scaleFactor );
+
static QIcon symbolPreviewIcon( QgsSymbolV2* symbol, QSize size );
static QIcon symbolLayerPreviewIcon( QgsSymbolLayerV2* layer, QgsSymbolV2::OutputUnit u, QSize size );
static QIcon colorRampPreviewIcon( QgsVectorColorRampV2* ramp, QSize size );
@@ -62,6 +87,87 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
static QgsSymbolLayerV2* loadSymbolLayer( QDomElement& element );
static QDomElement saveSymbol( QString name, QgsSymbolV2* symbol, QDomDocument& doc, QgsSymbolV2Map* subSymbols = NULL );
+ static bool createSymbolLayerV2ListFromSld( QDomElement& element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers );
+
+ static QgsSymbolLayerV2* createFillLayerFromSld( QDomElement &element );
+ static QgsSymbolLayerV2* createLineLayerFromSld( QDomElement &element );
+ static QgsSymbolLayerV2* createMarkerLayerFromSld( QDomElement &element );
+
+ static bool convertPolygonSymbolizerToPointMarker( QDomElement &element, QgsSymbolLayerV2List &layerList );
+ static bool hasExternalGraphic( QDomElement &element );
+ static bool hasWellKnownMark( QDomElement &element );
+
+ static bool needFontMarker( QDomElement &element );
+ static bool needSvgMarker( QDomElement &element );
+ static bool needEllipseMarker( QDomElement &element );
+ static bool needMarkerLine( QDomElement &element );
+ static bool needLinePatternFill( QDomElement &element );
+ static bool needPointPatternFill( QDomElement &element );
+ static bool needSvgFill( QDomElement &element );
+
+ static void fillToSld( QDomDocument &doc, QDomElement &element,
+ Qt::BrushStyle brushStyle, QColor color = QColor() );
+ static bool fillFromSld( QDomElement &element,
+ Qt::BrushStyle &brushStyle, QColor &color );
+
+ static void lineToSld( QDomDocument &doc, QDomElement &element,
+ Qt::PenStyle penStyle, QColor color, double width = -1,
+ const Qt::PenJoinStyle *penJoinStyle = 0, const Qt::PenCapStyle *penCapStyle = 0,
+ const QVector<qreal> *customDashPattern = 0, double dashOffset = 0.0 );
+ static bool lineFromSld( QDomElement &element,
+ Qt::PenStyle &penStyle, QColor &color, double &width,
+ Qt::PenJoinStyle *penJoinStyle = 0, Qt::PenCapStyle *penCapStyle = 0,
+ QVector<qreal> *customDashPattern = 0, double *dashOffset = 0 );
+
+ static void externalGraphicToSld( QDomDocument &doc, QDomElement &element,
+ QString path, QString mime,
+ QColor color, double size = -1 );
+ static bool externalGraphicFromSld( QDomElement &element,
+ QString &path, QString &mime,
+ QColor &color, double &size );
+
+ static void wellKnownMarkerToSld( QDomDocument &doc, QDomElement &element,
+ QString name, QColor color, QColor borderColor = QColor(),
+ double borderWidth = -1, double size = -1 );
+ static bool wellKnownMarkerFromSld( QDomElement &element,
+ QString &name, QColor &color, QColor &borderColor,
+ double &borderWidth, double &size );
+
+ static void externalMarkerToSld( QDomDocument &doc, QDomElement &element,
+ QString path, QString format, int *markIndex = 0,
+ QColor color = QColor(), double size = -1 );
+ static bool externalMarkerFromSld( QDomElement &element,
+ QString &path, QString &format, int &markIndex,
+ QColor &color, double &size );
+
+
+ static void labelTextToSld( QDomDocument &doc, QDomElement &element, QString label,
+ QFont font, QColor color = QColor(), double size = -1 );
+
+ static void createRotationElement( QDomDocument &doc, QDomElement &element, QString rotationFunc );
+ static bool rotationFromSldElement( QDomElement &element, QString &rotationFunc );
+
+ static void createOpacityElement( QDomDocument &doc, QDomElement &element, QString alphaFunc );
+ static bool opacityFromSldElement( QDomElement &element, QString &alphaFunc );
+
+ static void createDisplacementElement( QDomDocument &doc, QDomElement &element, QPointF offset );
+ static bool displacementFromSldElement( QDomElement &element, QPointF &offset );
+
+ static void createOnlineResourceElement( QDomDocument &doc, QDomElement &element, QString path, QString format );
+ static bool onlineResourceFromSldElement( QDomElement &element, QString &path, QString &format );
+
+ static void createGeometryElement( QDomDocument &doc, QDomElement &element, QString geomFunc );
+ static bool geometryFromSldElement( QDomElement &element, QString &geomFunc );
+
+ static bool createFunctionElement( QDomDocument &doc, QDomElement &element, QString function );
+ static bool functionFromSldElement( QDomElement &element, QString &function );
+
+ static QDomElement createSvgParameterElement( QDomDocument &doc, QString name, QString value );
+ static QgsStringMap getSvgParameterList( QDomElement &element );
+
+ static QDomElement createVendorOptionElement( QDomDocument &doc, QString name, QString value );
+ static QgsStringMap getVendorOptionList( QDomElement &element );
+
static QgsStringMap parseProperties( QDomElement& element );
static void saveProperties( QgsStringMap props, QDomDocument& doc, QDomElement& element );
diff --git a/src/core/symbology-ng/qgssymbolv2.cpp b/src/core/symbology-ng/qgssymbolv2.cpp
index b66f528..b8a83a4 100644
--- a/src/core/symbology-ng/qgssymbolv2.cpp
+++ b/src/core/symbology-ng/qgssymbolv2.cpp
@@ -253,6 +253,19 @@ QString QgsSymbolV2::dump()
return s;
}
+void QgsSymbolV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
+{
+ props[ "alpha" ] = QString::number( alpha() );
+ double scaleFactor = 1.0;
+ props[ "uom" ] = QgsSymbolLayerV2Utils::encodeSldUom( outputUnit(), &scaleFactor );
+ props[ "uomScale" ] = scaleFactor != 1 ? QString::number( scaleFactor ) : "";
+
+ for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
+ {
+ ( *it )->toSld( doc, element, props );
+ }
+}
+
QgsSymbolLayerV2List QgsSymbolV2::cloneLayers() const
{
QgsSymbolLayerV2List lst;
diff --git a/src/core/symbology-ng/qgssymbolv2.h b/src/core/symbology-ng/qgssymbolv2.h
index e9e6af1..0b96f4e 100644
--- a/src/core/symbology-ng/qgssymbolv2.h
+++ b/src/core/symbology-ng/qgssymbolv2.h
@@ -12,6 +12,9 @@ class QPainter;
class QSize;
class QPointF;
class QPolygonF;
+
+class QDomDocument;
+class QDomElement;
//class
class QgsFeature;
@@ -89,6 +92,8 @@ class CORE_EXPORT QgsSymbolV2
virtual QgsSymbolV2* clone() const = 0;
+ void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const;
+
OutputUnit outputUnit() const { return mOutputUnit; }
void setOutputUnit( OutputUnit u ) { mOutputUnit = u; }
diff --git a/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp b/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp
index 9959d21..b97fae0 100644
--- a/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp
+++ b/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp
@@ -177,6 +177,18 @@ QgsStringMap QgsVectorFieldSymbolLayer::properties() const
return properties;
}
+void QgsVectorFieldSymbolLayer::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const
+{
+ element.appendChild( doc.createComment( "VectorField not implemented yet..." ) );
+ mLineSymbol->toSld( doc, element, props );
+}
+
+QgsSymbolLayerV2* QgsVectorFieldSymbolLayer::createFromSld( QDomElement &element )
+{
+ Q_UNUSED( element );
+ return NULL;
+}
+
void QgsVectorFieldSymbolLayer::drawPreviewIcon( QgsSymbolV2RenderContext& context, QSize size )
{
if ( mLineSymbol )
diff --git a/src/core/symbology-ng/qgsvectorfieldsymbollayer.h b/src/core/symbology-ng/qgsvectorfieldsymbollayer.h
index 8f55b95..367253b 100644
--- a/src/core/symbology-ng/qgsvectorfieldsymbollayer.h
+++ b/src/core/symbology-ng/qgsvectorfieldsymbollayer.h
@@ -47,6 +47,7 @@ class CORE_EXPORT QgsVectorFieldSymbolLayer: public QgsMarkerSymbolLayerV2
~QgsVectorFieldSymbolLayer();
static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
+ static QgsSymbolLayerV2* createFromSld( QDomElement &element );
QString layerType() const { return "VectorField"; }
@@ -60,6 +61,8 @@ class CORE_EXPORT QgsVectorFieldSymbolLayer: public QgsMarkerSymbolLayerV2
QgsSymbolLayerV2* clone() const;
QgsStringMap properties() const;
+ void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const;
+
void drawPreviewIcon( QgsSymbolV2RenderContext& context, QSize size );
QSet<QString> usedAttributes() const;
--
The Quantum GIS in Debian project
More information about the Pkg-grass-devel
mailing list