From 59d8b18ed8c53ab6fe21e7be306f66d8dc7d1a35 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sun, 20 Nov 2016 15:41:51 +0100 Subject: [PATCH] Porting Raymond Nijssen FOSS4G 2016 labeling work onto latest version of master, with basic support for testing. fixes #8925 --- src/core/qgsvectorlayer.cpp | 30 +- src/core/qgsvectorlayerlabeling.cpp | 49 +++ src/core/qgsvectorlayerlabeling.h | 16 +- .../python/test_qgssymbollayer_createsld.py | 54 ++++ tests/testdata/symbol_layer/simpleLabel.qml | 285 ++++++++++++++++++ 5 files changed, 424 insertions(+), 10 deletions(-) create mode 100644 tests/testdata/symbol_layer/simpleLabel.qml diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 09ac98c6172..2eb8e49e246 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2205,20 +2205,34 @@ bool QgsVectorLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &error { Q_UNUSED( errorMessage ); - // store the Name element - QDomElement nameNode = doc.createElement( QStringLiteral( "se:Name" ) ); - nameNode.appendChild( doc.createTextNode( name() ) ); - node.appendChild( nameNode ); - QgsStringMap localProps = QgsStringMap( props ); if ( hasScaleBasedVisibility() ) { QgsSymbolLayerUtils::mergeScaleDependencies( maximumScale(), minimumScale(), localProps ); } - if ( isSpatial() ) - { - node.appendChild( mRenderer->writeSld( doc, name(), localProps ) ); + if ( isSpatial() ) { + // store the Name element + QDomElement nameNode = doc.createElement( "se:Name" ); + nameNode.appendChild( doc.createTextNode( name() ) ); + node.appendChild( nameNode ); + + QDomElement userStyleElem = doc.createElement( "UserStyle" ); + node.appendChild( userStyleElem ); + + QDomElement nameElem = doc.createElement( "se:Name" ); + nameElem.appendChild( doc.createTextNode( name() ) ); + + userStyleElem.appendChild( nameElem ); + + QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" ); + userStyleElem.appendChild( featureTypeStyleElem ); + + mRenderer->toSld( doc, featureTypeStyleElem, localProps ); + if ( mLabeling != nullptr ) + { + mLabeling->toSld( featureTypeStyleElem, localProps ); + } } return true; } diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index 7a0cea27ec6..252fe1ca6cd 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -17,6 +17,8 @@ #include "qgspallabeling.h" #include "qgsrulebasedlabeling.h" #include "qgsvectorlayer.h" +#include "qgssymbollayerutils.h" +#include "qgis.h" QgsAbstractVectorLayerLabeling *QgsAbstractVectorLayerLabeling::create( const QDomElement &element, const QgsReadWriteContext &context ) @@ -88,3 +90,50 @@ QgsVectorLayerSimpleLabeling *QgsVectorLayerSimpleLabeling::create( const QDomEl return new QgsVectorLayerSimpleLabeling( QgsPalLayerSettings() ); } + +void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap &props ) const +{ + + if ( mSettings->drawLabels ) + { + QDomDocument doc = parent.ownerDocument(); + + QDomElement ruleElement = doc.createElement( QStringLiteral( "se:Rule" ) ); + parent.appendChild( ruleElement ); + + QDomElement textSymbolizerElement = doc.createElement( QStringLiteral( "se:TextSymbolizer" ) ); + ruleElement.appendChild( textSymbolizerElement ); + + // label + QDomElement labelElement = doc.createElement( QStringLiteral( "se:Label" ) ); + textSymbolizerElement.appendChild( labelElement ); + + if ( mSettings->isExpression ) + { + labelElement.appendChild( doc.createComment( QStringLiteral( "SE Export for %1 not implemented yet" ).arg( mSettings->getLabelExpression()->dump() ) ) ); + labelElement.appendChild( doc.createTextNode( "Placeholder" ) ); + } + else + { + QDomElement propertyNameElement = doc.createElement( QStringLiteral( "ogc:PropertyName" ) ); + propertyNameElement.appendChild( doc.createTextNode( mSettings->fieldName ) ); + labelElement.appendChild( propertyNameElement ); + } + + // font + QDomElement fontElement = doc.createElement( QStringLiteral( "se:Font" ) ); + textSymbolizerElement.appendChild( fontElement ); + QgsTextFormat format = mSettings->format(); + fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-family" ), format.font().family() ) ); + double fontSize = QgsSymbolLayerUtils::rescaleUom( format.size(), format.sizeUnit(), props ); + fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-size" ), QString::number( fontSize ) ) ); + + + // fill + QDomElement fillElement = doc.createElement( QStringLiteral( "se:Fill" ) ); + textSymbolizerElement.appendChild( fillElement ); + fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill" ), format.color().name() ) ); + } + + +} diff --git a/src/core/qgsvectorlayerlabeling.h b/src/core/qgsvectorlayerlabeling.h index 0324dd4783a..2b9669cf6ef 100644 --- a/src/core/qgsvectorlayerlabeling.h +++ b/src/core/qgsvectorlayerlabeling.h @@ -19,9 +19,9 @@ #include #include +#include -#include "qgis_core.h" -#include "qgis_sip.h" +#include "qgis.h" class QDomDocument; class QDomElement; @@ -76,6 +76,17 @@ class CORE_EXPORT QgsAbstractVectorLayerLabeling //! Try to create instance of an implementation based on the XML data static QgsAbstractVectorLayerLabeling *create( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; + /** + * Writes the SE 1.1 TextSymbolizer element based on the current layer labelling settings + */ + virtual void toSld( QDomNode& parent, const QgsStringMap& props ) const + { + Q_UNUSED( parent ) + Q_UNUSED( props ) + QDomDocument doc = parent.ownerDocument(); + parent.appendChild( doc.createComment( QStringLiteral( "SE Export for %1 not implemented yet" ).arg( type() ) ) ); + } + private: Q_DISABLE_COPY( QgsAbstractVectorLayerLabeling ) @@ -105,6 +116,7 @@ class CORE_EXPORT QgsVectorLayerSimpleLabeling : public QgsAbstractVectorLayerLa virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const override; virtual QgsPalLayerSettings settings( const QString &providerId = QString() ) const override; bool requiresAdvancedEffects() const override; + virtual void toSld( QDomNode& parent, const QgsStringMap& props ) const override; //! Create the instance from a DOM element with saved configuration static QgsVectorLayerSimpleLabeling *create( const QDomElement &element, const QgsReadWriteContext &context ); diff --git a/tests/src/python/test_qgssymbollayer_createsld.py b/tests/src/python/test_qgssymbollayer_createsld.py index cd452cb4116..16e13149974 100644 --- a/tests/src/python/test_qgssymbollayer_createsld.py +++ b/tests/src/python/test_qgssymbollayer_createsld.py @@ -546,6 +546,60 @@ class TestQgsSymbolLayerCreateSld(unittest.TestCase): ltValue = lt.childNodes().item(1) self.assertEquals(max, ltValue.toElement().text()) + def testSimpleLabelling(self): + layer = QgsVectorLayer("Point", "addfeat", "memory") + self.loadStyleWithCustomProperties(layer, "simpleLabel") + + dom, root = self.layerToSld(layer) + # print("Simple label text symbolizer" + dom.toString()) + + ts = self.getTextSymbolizer(root, 1, 0) + self.assertPropertyName(ts, 'se:Label', 'NAME') + font = self.assertElement(ts, 'se:Font', 0) + self.assertEquals('Liberation Mono', self.assertSvgParameter(font, 'font-family').text()) + self.assertEquals('9', self.assertSvgParameter(font, 'font-size').text()) + + fill = self.assertElement(ts, 'se:Fill', 0) + self.assertEquals('#000000', self.assertSvgParameter(fill, "fill").text()) + + def loadStyleWithCustomProperties(self, layer, qmlFileName): + # load the style, only vector symbology + path = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), qmlFileName)) + + # labelling is in custom properties, they need to be loaded separately + status = layer.loadNamedStyle(path) + doc = QDomDocument() + file = QFile(path) + file.open(QIODevice.ReadOnly) + doc.setContent(file, True) + file.close() + flag = layer.readCustomProperties(doc.documentElement()) + + def assertElement(self, container, elementName, index): + list = container.elementsByTagName(elementName) + self.assertTrue(list.size() > index, 'Expected to find at least ' + str(index + 1) + ' ' + elementName + ' in ' + container.nodeName() + ' but found ' + str(list.size())) + node = list.item(index) + self.assertTrue(node.isElement(), 'Found node but it''s not an element') + return node.toElement() + + def getTextSymbolizer(self, root, ruleIndex, textSymbolizerIndex): + rule = self.assertElement(root, 'se:Rule', ruleIndex) + textSymbolizer = self.assertElement(rule, 'se:TextSymbolizer', textSymbolizerIndex) + return textSymbolizer + + def assertPropertyName(self, root, containerProperty, expectedAttributeName): + container = root.elementsByTagName(containerProperty).item(0).toElement() + property = container.elementsByTagName("ogc:PropertyName").item(0).toElement() + self.assertEqual(expectedAttributeName, property.text()) + + def assertSvgParameter(self, container, expectedName): + list = container.elementsByTagName("se:SvgParameter") + for i in range(0, list.size()): + item = list.item(i) + if item.isElement and item.isElement() and item.toElement().attribute('name') == expectedName: + return item.toElement() + self.fail('Could not find a se:SvgParameter named ' + expectedName + ' in ' + container.nodeName()) + def assertScaleDenominator(self, root, expectedMinScale, expectedMaxScale, index=0): rule = root.elementsByTagName('se:Rule').item(index).toElement() diff --git a/tests/testdata/symbol_layer/simpleLabel.qml b/tests/testdata/symbol_layer/simpleLabel.qml new file mode 100644 index 00000000000..bf4ee3c8fdd --- /dev/null +++ b/tests/testdata/symbol_layer/simpleLabel.qml @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + NAME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + 0 +