Skip to content

Commit

Permalink
Merge pull request #5226 from aaime/rule_labels
Browse files Browse the repository at this point in the history
Also export rule based labelling in SLD. Follows up to ticket #8925
  • Loading branch information
elpaso committed Oct 19, 2017
2 parents e7d7295 + aa594f5 commit a411669
Show file tree
Hide file tree
Showing 8 changed files with 1,648 additions and 274 deletions.
1 change: 1 addition & 0 deletions python/core/qgsrulebasedlabeling.sip
Expand Up @@ -298,6 +298,7 @@ Create the instance from a DOM element with saved configuration
%End
virtual bool requiresAdvancedEffects() const;

virtual void toSld( QDomNode &parent, const QgsStringMap &props ) const;

protected:
};
Expand Down
11 changes: 11 additions & 0 deletions python/core/qgsvectorlayerlabeling.sip
Expand Up @@ -92,6 +92,17 @@ Try to create instance of an implementation based on the XML data
Writes the SE 1.1 TextSymbolizer element based on the current layer labeling settings
%End

protected:

virtual void writeTextSymbolizer( QDomNode &parent, QgsPalLayerSettings &settings, const QgsStringMap &props ) const;
%Docstring
Writes a TextSymbolizer element contents based on the provided labeling settings
writeTextSymbolizer
@param parent the node that will have the text symbolizer element added to it
@param settings the settings getting translated to a TextSymbolizer
@param props a open ended set of properties that can drive/inform the SLD encoding
%End

private:
QgsAbstractVectorLayerLabeling( const QgsAbstractVectorLayerLabeling &rhs );
};
Expand Down
43 changes: 42 additions & 1 deletion src/core/qgsrulebasedlabeling.cpp
Expand Up @@ -13,7 +13,7 @@
* *
***************************************************************************/
#include "qgsrulebasedlabeling.h"

#include "qgssymbollayerutils.h"

QgsRuleBasedLabelProvider::QgsRuleBasedLabelProvider( const QgsRuleBasedLabeling &rules, QgsVectorLayer *layer, bool withFeatureLoop )
: QgsVectorLayerLabelProvider( layer, QString(), withFeatureLoop, nullptr )
Expand Down Expand Up @@ -475,3 +475,44 @@ void QgsRuleBasedLabeling::setSettings( QgsPalLayerSettings *settings, const QSt
return rule->setSettings( settings );
}
}

void QgsRuleBasedLabeling::toSld( QDomNode &parent, const QgsStringMap &props ) const
{
if ( !mRootRule )
{
return;
}

const QgsRuleBasedLabeling::RuleList rules = mRootRule->children();
for ( Rule *rule : rules )
{
QgsPalLayerSettings *settings = rule->settings();

if ( settings->drawLabels )
{
QDomDocument doc = parent.ownerDocument();

QDomElement ruleElement = doc.createElement( QStringLiteral( "se:Rule" ) );
parent.appendChild( ruleElement );

if ( !rule->filterExpression().isEmpty() )
{
QgsSymbolLayerUtils::createFunctionElement( doc, ruleElement, rule->filterExpression() );
}

// scale dependencies, the actual behavior is that the PAL settings min/max and
// the rule min/max get intersected
QgsStringMap localProps = QgsStringMap( props );
QgsSymbolLayerUtils::mergeScaleDependencies( rule->maximumScale(), rule->minimumScale(), localProps );
if ( settings->scaleVisibility )
{
QgsSymbolLayerUtils::mergeScaleDependencies( settings->maximumScale, settings->minimumScale, localProps );
}
QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElement, localProps );

QgsAbstractVectorLayerLabeling::writeTextSymbolizer( ruleElement, *settings, props );
}

}

}
1 change: 1 addition & 0 deletions src/core/qgsrulebasedlabeling.h
Expand Up @@ -372,6 +372,7 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
*/
virtual void setSettings( QgsPalLayerSettings *settings SIP_TRANSFER, const QString &providerId = QString() ) override;
bool requiresAdvancedEffects() const override;
virtual void toSld( QDomNode &parent, const QgsStringMap &props ) const override;

protected:
Rule *mRootRule = nullptr;
Expand Down
551 changes: 280 additions & 271 deletions src/core/qgsvectorlayerlabeling.cpp

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/core/qgsvectorlayerlabeling.h
Expand Up @@ -102,6 +102,17 @@ class CORE_EXPORT QgsAbstractVectorLayerLabeling
parent.appendChild( doc.createComment( QStringLiteral( "SE Export for %1 not implemented yet" ).arg( type() ) ) );
}

protected:

/**
* Writes a TextSymbolizer element contents based on the provided labeling settings
* @brief writeTextSymbolizer
* @param parent the node that will have the text symbolizer element added to it
* @param settings the settings getting translated to a TextSymbolizer
* @param props a open ended set of properties that can drive/inform the SLD encoding
*/
virtual void writeTextSymbolizer( QDomNode &parent, QgsPalLayerSettings &settings, const QgsStringMap &props ) const;

private:
Q_DISABLE_COPY( QgsAbstractVectorLayerLabeling )

Expand Down
42 changes: 40 additions & 2 deletions tests/src/python/test_qgssymbollayer_createsld.py
Expand Up @@ -915,8 +915,9 @@ def testLabelScaleDependencies(self):
self.loadStyleWithCustomProperties(layer, "polygonLabel")
settings = layer.labeling().settings()
settings.scaleVisibility = True
settings.minimumScale = 1000000
settings.maximumScale = 10000000
# Careful: min scale -> large scale denomin
settings.minimumScale = 10000000
settings.maximumScale = 1000000
layer.setLabeling(QgsVectorLayerSimpleLabeling(settings))

dom, root = self.layerToSld(layer)
Expand Down Expand Up @@ -1018,6 +1019,39 @@ def assertLabelBackground(self, backgroundType, expectedMarkName, sizeType, expe
else:
self.assertIsNone(self.assertVendorOption(ts, 'graphic-margin', True))

def testRuleBasedLabels(self):
layer = QgsVectorLayer("Point", "addfeat", "memory")
self.loadStyleWithCustomProperties(layer, "ruleLabel")

dom, root = self.layerToSld(layer)
# print("Rule based labeling: " + dom.toString())

# three rules, one with the point symbol, one with the first rule based label,
# one with the second rule based label
rule1 = self.getRule(root, 0)
self.assertElement(rule1, 'se:PointSymbolizer', 0)

rule2 = self.getRule(root, 1)
self.assertScaleDenominator(root, '100000', '10000000', 1)
tsRule2 = self.assertElement(rule2, 'se:TextSymbolizer', 0)
gt = rule2.elementsByTagName("Filter").item(0).firstChild()
self.assertEqual("ogc:PropertyIsGreaterThan", gt.nodeName())
gtProperty = gt.toElement().firstChild()
self.assertEqual("ogc:PropertyName", gtProperty.nodeName())
self.assertEqual("POP_MAX", gtProperty.toElement().text())
gtValue = gt.childNodes().item(1)
self.assertEqual("1000000", gtValue.toElement().text())

rule3 = self.getRule(root, 2)
tsRule3 = self.assertElement(rule3, 'se:TextSymbolizer', 0)
lt = rule3.elementsByTagName("Filter").item(0).firstChild()
self.assertEqual("ogc:PropertyIsLessThan", lt.nodeName())
ltProperty = lt.toElement().firstChild()
self.assertEqual("ogc:PropertyName", ltProperty.nodeName())
self.assertEqual("POP_MAX", ltProperty.toElement().text())
ltValue = gt.childNodes().item(1)
self.assertEqual("1000000", gtValue.toElement().text())

def updateLinePlacementProperties(self, layer, linePlacement, distance, repeat, maxAngleInternal=25, maxAngleExternal=-25):
settings = layer.labeling().settings()
settings.placement = linePlacement
Expand Down Expand Up @@ -1124,6 +1158,10 @@ def assertElement(self, container, elementName, index, allowMissing=False):
self.assertTrue(node.isElement(), 'Found node but it''s not an element')
return node.toElement()

def getRule(self, root, ruleIndex):
rule = self.assertElement(root, 'se:Rule', ruleIndex)
return rule

def getTextSymbolizer(self, root, ruleIndex, textSymbolizerIndex):
rule = self.assertElement(root, 'se:Rule', ruleIndex)
textSymbolizer = self.assertElement(rule, 'se:TextSymbolizer', textSymbolizerIndex)
Expand Down

0 comments on commit a411669

Please sign in to comment.