Skip to content

Commit a25b025

Browse files
committedSep 1, 2016
Export map level scale based dependencies in most vector symbology
1 parent d477d19 commit a25b025

26 files changed

+2909
-67
lines changed
 

‎doc/api_break.dox

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,15 @@ in code which previously passed a null pointer to QgsVectorFileWriter.</li>
999999
<li>QgsWMSLegendNode has been renamed to QgsWmsLegendNode</li>
10001000
</ul>
10011001

1002+
\subsection qgis_api_break_3_0_QgsRenderer QgsRenderer
1003+
1004+
<ul>
1005+
<li>New virtual method <code>bool writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage, QgsStringMap props = QgsStringMap() )</code> accepts an
1006+
optional property map passing down layer level properties to the SLD encoders. If scale based visibility is enabled, it will contain the
1007+
<code>scaleMinDenom</code> and <code>scaleMaxDenom</code> properties.
1008+
</ul>
1009+
1010+
10021011

10031012
\section qgis_api_break_2_4 QGIS 2.4
10041013

‎python/core/qgsvectorlayer.sip

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ class QgsVectorLayer : QgsMapLayer
709709
*/
710710
bool writeStyle( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const;
711711

712-
bool writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const;
712+
bool writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage, QgsStringMap& props ) const;
713713
bool readSld( const QDomNode& node, QString& errorMessage );
714714

715715
/**
@@ -1660,4 +1660,3 @@ class QgsVectorLayer : QgsMapLayer
16601660

16611661

16621662
};
1663-

‎python/core/symbology-ng/qgssymbollayerutils.sip

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class QgsSymbolLayerUtils
224224
static void createGeometryElement( QDomDocument &doc, QDomElement &element, const QString& geomFunc );
225225
static bool geometryFromSldElement( QDomElement &element, QString &geomFunc );
226226

227+
static bool createExpressionElement( QDomDocument &doc, QDomElement &element, const QString& function );
227228
static bool createFunctionElement( QDomDocument &doc, QDomElement &element, const QString& function );
228229
static bool functionFromSldElement( QDomElement &element, QString &function );
229230

@@ -444,4 +445,34 @@ class QgsSymbolLayerUtils
444445
*/
445446
static QList<double> prettyBreaks( double minimum, double maximum, int classes );
446447

448+
/** Rescales the given size based on the uomScale found in the props, if any is found, otherwise
449+
* returns the value un-modified
450+
* @note added in 3.0
451+
*/
452+
static double rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
453+
454+
/** Rescales the given point based on the uomScale found in the props, if any is found, otherwise
455+
* returns a copy of the original point
456+
* @note added in 3.0
457+
*/
458+
static QPointF rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props ) /PyName=rescalePointUom/;
459+
460+
/** Rescales the given array based on the uomScale found in the props, if any is found, otherwise
461+
* returns a copy of the original point
462+
* @note added in 3.0
463+
*/
464+
static QVector<qreal> rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props ) /PyName=rescaleArrayUom/;
465+
466+
/**
467+
* Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into the SE Rule element
468+
* @note added in 3.0
469+
*/
470+
static void applyScaleDependency( QDomDocument& doc, QDomElement& ruleElem, QgsStringMap& props );
471+
472+
/**
473+
* Merges the local scale limits, if any, with the ones already in the map, if any
474+
* @note added in 3.0
475+
*/
476+
static void mergeScaleDependencies( int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap& props );
477+
447478
};

‎src/core/qgsmaplayer.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1448,7 +1448,13 @@ void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
14481448
return;
14491449
}
14501450

1451-
if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg ) )
1451+
QgsStringMap props;
1452+
if ( hasScaleBasedVisibility() )
1453+
{
1454+
props[ "scaleMinDenom" ] = QString::number( mMinScale );
1455+
props[ "scaleMaxDenom" ] = QString::number( mMaxScale );
1456+
}
1457+
if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg, props ) )
14521458
{
14531459
errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
14541460
return;

‎src/core/qgsvectorlayer.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,8 +1991,12 @@ bool QgsVectorLayer::readSld( const QDomNode& node, QString& errorMessage )
19911991
return true;
19921992
}
19931993

1994-
19951994
bool QgsVectorLayer::writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const
1995+
{
1996+
return writeSld( node, doc, errorMessage, QgsStringMap() );
1997+
}
1998+
1999+
bool QgsVectorLayer::writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage, QgsStringMap props ) const
19962000
{
19972001
Q_UNUSED( errorMessage );
19982002

@@ -2001,9 +2005,15 @@ bool QgsVectorLayer::writeSld( QDomNode& node, QDomDocument& doc, QString& error
20012005
nameNode.appendChild( doc.createTextNode( name() ) );
20022006
node.appendChild( nameNode );
20032007

2008+
QgsStringMap localProps = QgsStringMap( props );
2009+
if ( hasScaleBasedVisibility() )
2010+
{
2011+
QgsSymbolLayerUtils::mergeScaleDependencies( minimumScale(), maximumScale(), localProps );
2012+
}
2013+
20042014
if ( hasGeometryType() )
20052015
{
2006-
node.appendChild( mRenderer->writeSld( doc, name() ) );
2016+
node.appendChild( mRenderer->writeSld( doc, name(), localProps ) );
20072017
}
20082018
return true;
20092019
}

‎src/core/qgsvectorlayer.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,17 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
803803
bool writeStyle( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const override;
804804

805805
bool writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const;
806+
807+
/**
808+
* Writes the symbology of the layer into the document provided in SLD 1.1 format
809+
* @param node the node that will have the style element added to it.
810+
* @param doc the document that will have the QDomNode added.
811+
* @param errorMessage reference to string that will be updated with any error messages
812+
* @param props a open ended set of properties that can drive/inform the SLD encoding
813+
* @return true in case of success
814+
*/
815+
bool writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage, QgsStringMap props = QgsStringMap() ) const;
816+
806817
bool readSld( const QDomNode& node, QString& errorMessage ) override;
807818

808819
/**

‎src/core/symbology-ng/qgscategorizedsymbolrenderer.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QgsStr
141141
mValue.toString().replace( '\'', "''" ) );
142142
QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
143143

144+
// add the mix/max scale denoms if we got any from the callers
145+
QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
146+
144147
mSymbol->toSld( doc, ruleElem, props );
145148
}
146149

@@ -517,9 +520,8 @@ QgsCategorizedSymbolRenderer* QgsCategorizedSymbolRenderer::clone() const
517520
return r;
518521
}
519522

520-
void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element ) const
523+
void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
521524
{
522-
QgsStringMap props;
523525
props[ "attribute" ] = mAttrName;
524526
if ( mRotation.data() )
525527
props[ "angle" ] = mRotation->expression();

‎src/core/symbology-ng/qgscategorizedsymbolrenderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer
9999

100100
virtual QgsCategorizedSymbolRenderer* clone() const override;
101101

102-
virtual void toSld( QDomDocument& doc, QDomElement &element ) const override;
102+
virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override;
103103

104104
//! returns bitwise OR-ed capabilities of the renderer
105105
virtual Capabilities capabilities() override { return SymbolLevels | RotationField | Filter; }

‎src/core/symbology-ng/qgsgraduatedsymbolrenderer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,8 @@ QgsGraduatedSymbolRenderer* QgsGraduatedSymbolRenderer::clone() const
560560
return r;
561561
}
562562

563-
void QgsGraduatedSymbolRenderer::toSld( QDomDocument& doc, QDomElement &element ) const
563+
void QgsGraduatedSymbolRenderer::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const
564564
{
565-
QgsStringMap props;
566565
props[ "attribute" ] = mAttrName;
567566
props[ "method" ] = graduatedMethodStr( mGraduatedMethod );
568567
if ( mRotation.data() )

‎src/core/symbology-ng/qgsgraduatedsymbolrenderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer : public QgsFeatureRenderer
152152

153153
virtual QgsGraduatedSymbolRenderer* clone() const override;
154154

155-
virtual void toSld( QDomDocument& doc, QDomElement &element ) const override;
155+
virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override;
156156

157157
//! returns bitwise OR-ed capabilities of the renderer
158158
virtual Capabilities capabilities() override { return SymbolLevels | RotationField | Filter; }

‎src/core/symbology-ng/qgslinesymbollayer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,7 @@ void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
14501450
if ( !gap.isEmpty() )
14511451
{
14521452
QDomElement gapElem = doc.createElement( "se:Gap" );
1453-
QgsSymbolLayerUtils::createFunctionElement( doc, gapElem, gap );
1453+
QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
14541454
graphicStrokeElem.appendChild( gapElem );
14551455
}
14561456

‎src/core/symbology-ng/qgspointdisplacementrenderer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ QgsPointDisplacementRenderer* QgsPointDisplacementRenderer::clone() const
8787
return r;
8888
}
8989

90-
void QgsPointDisplacementRenderer::toSld( QDomDocument& doc, QDomElement &element ) const
90+
void QgsPointDisplacementRenderer::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const
9191
{
92-
mRenderer->toSld( doc, element );
92+
mRenderer->toSld( doc, element, props );
9393
}
9494

9595

‎src/core/symbology-ng/qgspointdisplacementrenderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRenderer
4646

4747
QgsPointDisplacementRenderer* clone() const override;
4848

49-
virtual void toSld( QDomDocument& doc, QDomElement &element ) const override;
49+
virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override;
5050

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

‎src/core/symbology-ng/qgsrenderer.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,11 @@ QgsFeatureRenderer* QgsFeatureRenderer::loadSld( const QDomNode &node, QgsWkbTyp
340340

341341
QDomElement QgsFeatureRenderer::writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const
342342
{
343-
return writeSld( doc, layer.name() );
343+
QgsStringMap props;
344+
return writeSld( doc, layer.name(), props );
344345
}
345346

346-
QDomElement QgsFeatureRenderer::writeSld( QDomDocument& doc, const QString& styleName ) const
347+
QDomElement QgsFeatureRenderer::writeSld( QDomDocument& doc, const QString& styleName, QgsStringMap props ) const
347348
{
348349
QDomElement userStyleElem = doc.createElement( "UserStyle" );
349350

@@ -352,7 +353,7 @@ QDomElement QgsFeatureRenderer::writeSld( QDomDocument& doc, const QString& styl
352353
userStyleElem.appendChild( nameElem );
353354

354355
QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" );
355-
toSld( doc, featureTypeStyleElem );
356+
toSld( doc, featureTypeStyleElem, props );
356357
userStyleElem.appendChild( featureTypeStyleElem );
357358

358359
return userStyleElem;

‎src/core/symbology-ng/qgsrenderer.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ class CORE_EXPORT QgsFeatureRenderer
263263
Q_DECL_DEPRECATED virtual QDomElement writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const;
264264
//! create the SLD UserStyle element following the SLD v1.1 specs with the given name
265265
//! @note added in 2.8
266-
virtual QDomElement writeSld( QDomDocument& doc, const QString& styleName ) const;
266+
virtual QDomElement writeSld( QDomDocument& doc, const QString& styleName, QgsStringMap props = QgsStringMap() ) const;
267267

268268
/** Create a new renderer according to the information contained in
269269
* the UserStyle element of a SLD style document
@@ -278,8 +278,11 @@ class CORE_EXPORT QgsFeatureRenderer
278278
static QgsFeatureRenderer* loadSld( const QDomNode &node, QgsWkbTypes::GeometryType geomType, QString &errorMessage );
279279

280280
//! used from subclasses to create SLD Rule elements following SLD v1.1 specs
281-
virtual void toSld( QDomDocument& doc, QDomElement &element ) const
282-
{ element.appendChild( doc.createComment( QString( "FeatureRendererV2 %1 not implemented yet" ).arg( type() ) ) ); }
281+
virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const
282+
{
283+
element.appendChild( doc.createComment( QString( "FeatureRendererV2 %1 not implemented yet" ).arg( type() ) ) );
284+
( void ) props; // warning avoidance
285+
}
283286

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

‎src/core/symbology-ng/qgsrulebasedrenderer.cpp

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -348,25 +348,7 @@ void QgsRuleBasedRenderer::Rule::toSld( QDomDocument& doc, QDomElement &element,
348348
props[ "filter" ] += mFilterExp;
349349
}
350350

351-
if ( mScaleMinDenom != 0 )
352-
{
353-
bool ok;
354-
int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
355-
if ( !ok || parentScaleMinDenom <= 0 )
356-
props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
357-
else
358-
props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
359-
}
360-
361-
if ( mScaleMaxDenom != 0 )
362-
{
363-
bool ok;
364-
int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
365-
if ( !ok || parentScaleMaxDenom <= 0 )
366-
props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
367-
else
368-
props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
369-
}
351+
QgsSymbolLayerUtils::mergeScaleDependencies( mScaleMinDenom, mScaleMaxDenom, props );
370352

371353
if ( mSymbol )
372354
{
@@ -402,19 +384,7 @@ void QgsRuleBasedRenderer::Rule::toSld( QDomDocument& doc, QDomElement &element,
402384
QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( "filter", "" ) );
403385
}
404386

405-
if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
406-
{
407-
QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
408-
scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
409-
ruleElem.appendChild( scaleMinDenomElem );
410-
}
411-
412-
if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
413-
{
414-
QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
415-
scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
416-
ruleElem.appendChild( scaleMaxDenomElem );
417-
}
387+
QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
418388

419389
mSymbol->toSld( doc, ruleElem, props );
420390
}
@@ -977,9 +947,9 @@ QgsRuleBasedRenderer* QgsRuleBasedRenderer::clone() const
977947
return r;
978948
}
979949

980-
void QgsRuleBasedRenderer::toSld( QDomDocument& doc, QDomElement &element ) const
950+
void QgsRuleBasedRenderer::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const
981951
{
982-
mRootRule->toSld( doc, element, QgsStringMap() );
952+
mRootRule->toSld( doc, element, props );
983953
}
984954

985955
// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items

‎src/core/symbology-ng/qgsrulebasedrenderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
441441

442442
virtual QgsRuleBasedRenderer* clone() const override;
443443

444-
virtual void toSld( QDomDocument& doc, QDomElement &element ) const override;
444+
virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override;
445445

446446
static QgsFeatureRenderer* createFromSld( QDomElement& element, QgsWkbTypes::GeometryType geomType );
447447

‎src/core/symbology-ng/qgssinglesymbolrenderer.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,8 @@ QgsSingleSymbolRenderer* QgsSingleSymbolRenderer::clone() const
205205
return r;
206206
}
207207

208-
void QgsSingleSymbolRenderer::toSld( QDomDocument& doc, QDomElement &element ) const
208+
void QgsSingleSymbolRenderer::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props ) const
209209
{
210-
QgsStringMap props;
211210
if ( mRotation.data() )
212211
props[ "angle" ] = mRotation->expression();
213212
if ( mSizeScale.data() )
@@ -220,6 +219,8 @@ void QgsSingleSymbolRenderer::toSld( QDomDocument& doc, QDomElement &element ) c
220219
nameElem.appendChild( doc.createTextNode( "Single symbol" ) );
221220
ruleElem.appendChild( nameElem );
222221

222+
QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
223+
223224
if ( mSymbol.data() ) mSymbol->toSld( doc, ruleElem, props );
224225
}
225226

‎src/core/symbology-ng/qgssinglesymbolrenderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class CORE_EXPORT QgsSingleSymbolRenderer : public QgsFeatureRenderer
6161

6262
virtual QgsSingleSymbolRenderer* clone() const override;
6363

64-
virtual void toSld( QDomDocument& doc, QDomElement &element ) const override;
64+
virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override;
6565
static QgsFeatureRenderer* createFromSld( QDomElement& element, QgsWkbTypes::GeometryType geomType );
6666

6767
//! returns bitwise OR-ed capabilities of the renderer

‎src/core/symbology-ng/qgssymbollayerutils.cpp

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,7 +2153,7 @@ void QgsSymbolLayerUtils::createRotationElement( QDomDocument &doc, QDomElement
21532153
if ( !rotationFunc.isEmpty() )
21542154
{
21552155
QDomElement rotationElem = doc.createElement( "se:Rotation" );
2156-
createFunctionElement( doc, rotationElem, rotationFunc );
2156+
createExpressionElement( doc, rotationElem, rotationFunc );
21572157
element.appendChild( rotationElem );
21582158
}
21592159
}
@@ -2174,7 +2174,7 @@ void QgsSymbolLayerUtils::createOpacityElement( QDomDocument &doc, QDomElement &
21742174
if ( !alphaFunc.isEmpty() )
21752175
{
21762176
QDomElement opacityElem = doc.createElement( "se:Opacity" );
2177-
createFunctionElement( doc, opacityElem, alphaFunc );
2177+
createExpressionElement( doc, opacityElem, alphaFunc );
21782178
element.appendChild( opacityElem );
21792179
}
21802180
}
@@ -2378,7 +2378,7 @@ void QgsSymbolLayerUtils::createGeometryElement( QDomDocument &doc, QDomElement
23782378
* like offset, centroid, ...
23792379
*/
23802380

2381-
createFunctionElement( doc, geometryElem, geomFunc );
2381+
createExpressionElement( doc, geometryElem, geomFunc );
23822382
}
23832383

23842384
bool QgsSymbolLayerUtils::geometryFromSldElement( QDomElement &element, QString &geomFunc )
@@ -2390,7 +2390,7 @@ bool QgsSymbolLayerUtils::geometryFromSldElement( QDomElement &element, QString
23902390
return functionFromSldElement( geometryElem, geomFunc );
23912391
}
23922392

2393-
bool QgsSymbolLayerUtils::createFunctionElement( QDomDocument &doc, QDomElement &element, const QString& function )
2393+
bool QgsSymbolLayerUtils::createExpressionElement( QDomDocument &doc, QDomElement &element, const QString& function )
23942394
{
23952395
// let's use QgsExpression to generate the SLD for the function
23962396
QgsExpression expr( function );
@@ -2405,6 +2405,22 @@ bool QgsSymbolLayerUtils::createFunctionElement( QDomDocument &doc, QDomElement
24052405
return true;
24062406
}
24072407

2408+
2409+
bool QgsSymbolLayerUtils::createFunctionElement( QDomDocument &doc, QDomElement &element, const QString& function )
2410+
{
2411+
// let's use QgsExpression to generate the SLD for the function
2412+
QgsExpression expr( function );
2413+
if ( expr.hasParserError() )
2414+
{
2415+
element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
2416+
return false;
2417+
}
2418+
QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter( expr, doc );
2419+
if ( !filterElem.isNull() )
2420+
element.appendChild( filterElem );
2421+
return true;
2422+
}
2423+
24082424
bool QgsSymbolLayerUtils::functionFromSldElement( QDomElement &element, QString &function )
24092425
{
24102426
QDomElement elem = element;
@@ -4000,3 +4016,43 @@ QVector<qreal> QgsSymbolLayerUtils::rescaleUom( const QVector<qreal>& array, Qgs
40004016
}
40014017
return result;
40024018
}
4019+
4020+
void QgsSymbolLayerUtils::applyScaleDependency( QDomDocument& doc, QDomElement& ruleElem, QgsStringMap& props )
4021+
{
4022+
if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
4023+
{
4024+
QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
4025+
scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
4026+
ruleElem.appendChild( scaleMinDenomElem );
4027+
}
4028+
4029+
if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
4030+
{
4031+
QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
4032+
scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
4033+
ruleElem.appendChild( scaleMaxDenomElem );
4034+
}
4035+
}
4036+
4037+
void QgsSymbolLayerUtils::mergeScaleDependencies( int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap& props )
4038+
{
4039+
if ( mScaleMinDenom != 0 )
4040+
{
4041+
bool ok;
4042+
int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
4043+
if ( !ok || parentScaleMinDenom <= 0 )
4044+
props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
4045+
else
4046+
props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
4047+
}
4048+
4049+
if ( mScaleMaxDenom != 0 )
4050+
{
4051+
bool ok;
4052+
int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
4053+
if ( !ok || parentScaleMaxDenom <= 0 )
4054+
props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
4055+
else
4056+
props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
4057+
}
4058+
}

‎src/core/symbology-ng/qgssymbollayerutils.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,14 @@ class CORE_EXPORT QgsSymbolLayerUtils
291291
static void createGeometryElement( QDomDocument &doc, QDomElement &element, const QString& geomFunc );
292292
static bool geometryFromSldElement( QDomElement &element, QString &geomFunc );
293293

294+
/**
295+
* Creates a OGC Expression element based on the provided function expression
296+
* @param doc The document owning the element
297+
* @param element The element parent
298+
* @param function The expression to be encoded
299+
* @return
300+
*/
301+
static bool createExpressionElement( QDomDocument &doc, QDomElement &element, const QString& function );
294302
static bool createFunctionElement( QDomDocument &doc, QDomElement &element, const QString& function );
295303
static bool functionFromSldElement( QDomElement &element, QString &function );
296304

@@ -527,24 +535,32 @@ class CORE_EXPORT QgsSymbolLayerUtils
527535
/** Rescales the given size based on the uomScale found in the props, if any is found, otherwise
528536
* returns the value un-modified
529537
* @note added in 3.0
530-
* @note not available in Python bindings
531538
*/
532539
static double rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
533540

534541
/** Rescales the given point based on the uomScale found in the props, if any is found, otherwise
535542
* returns a copy of the original point
536543
* @note added in 3.0
537-
* @note not available in Python bindings
538544
*/
539545
static QPointF rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
540546

541547
/** Rescales the given array based on the uomScale found in the props, if any is found, otherwise
542548
* returns a copy of the original point
543549
* @note added in 3.0
544-
* @note not available in Python bindings
545550
*/
546551
static QVector<qreal> rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
547552

553+
/**
554+
* Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into the SE Rule element
555+
* @note added in 3.0
556+
*/
557+
static void applyScaleDependency( QDomDocument& doc, QDomElement& ruleElem, QgsStringMap& props );
558+
559+
/**
560+
* Merges the local scale limits, if any, with the ones already in the map, if any
561+
* @note added in 3.0
562+
*/
563+
static void mergeScaleDependencies( int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap& props );
548564

549565
};
550566

‎tests/src/python/test_qgssymbollayer_createsld.py

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
QgsSimpleMarkerSymbolLayer, QgsUnitTypes, QgsSvgMarkerSymbolLayer,
3737
QgsFontMarkerSymbolLayer, QgsEllipseSymbolLayer, QgsSimpleLineSymbolLayer,
3838
QgsMarkerLineSymbolLayer, QgsMarkerSymbol, QgsSimpleFillSymbolLayer, QgsSVGFillSymbolLayer,
39-
QgsLinePatternFillSymbolLayer, QgsPointPatternFillSymbolLayer)
39+
QgsLinePatternFillSymbolLayer, QgsPointPatternFillSymbolLayer, QgsVectorLayer)
4040
from qgis.testing import start_app, unittest
4141
from utilities import unitTestDataPath
4242

@@ -371,6 +371,113 @@ def testPointFillpixels(self):
371371

372372
self.assertStaticSize(root, '2')
373373

374+
def testSingleSymbolNoScaleDependencies(self):
375+
layer = QgsVectorLayer("Point", "addfeat", "memory")
376+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "singleSymbol"))
377+
layer.loadNamedStyle(mFilePath)
378+
379+
dom, root = self.layerToSld(layer)
380+
# print("No dep on single symbol:" + dom.toString())
381+
382+
self.assertScaleDenominator(root, None, None)
383+
384+
def testSingleSymbolScaleDependencies(self):
385+
layer = QgsVectorLayer("Point", "addfeat", "memory")
386+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "singleSymbol"))
387+
layer.loadNamedStyle(mFilePath)
388+
layer.setMinimumScale(1000)
389+
layer.setMaximumScale(500000)
390+
layer.setScaleBasedVisibility(True)
391+
392+
dom, root = self.layerToSld(layer)
393+
# print("Scale dep on single symbol:" + dom.toString())
394+
395+
self.assertScaleDenominator(root, '1000', '500000')
396+
397+
def testCategorizedNoScaleDependencies(self):
398+
layer = QgsVectorLayer("Polygon", "addfeat", "memory")
399+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "categorized"))
400+
layer.loadNamedStyle(mFilePath)
401+
402+
dom, root = self.layerToSld(layer)
403+
# print("Categorized no scale deps:" + dom.toString())
404+
405+
ruleCount = root.elementsByTagName('se:Rule').size()
406+
for i in range(0, ruleCount):
407+
self.assertScaleDenominator(root, None, None, i)
408+
409+
def testCategorizedWithScaleDependencies(self):
410+
layer = QgsVectorLayer("Polygon", "addfeat", "memory")
411+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "categorized"))
412+
layer.loadNamedStyle(mFilePath)
413+
layer.setMinimumScale(1000)
414+
layer.setMaximumScale(500000)
415+
layer.setScaleBasedVisibility(True)
416+
417+
dom, root = self.layerToSld(layer)
418+
# print("Categorized with scale deps:" + dom.toString())
419+
420+
ruleCount = root.elementsByTagName('se:Rule').size()
421+
for i in range(0, ruleCount):
422+
self.assertScaleDenominator(root, '1000', '500000', i)
423+
424+
def testGraduatedNoScaleDependencies(self):
425+
layer = QgsVectorLayer("Polygon", "addfeat", "memory")
426+
427+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "graduated"))
428+
status = layer.loadNamedStyle(mFilePath)
429+
430+
dom, root = self.layerToSld(layer)
431+
# print("Graduated no scale deps:" + dom.toString())
432+
433+
ruleCount = root.elementsByTagName('se:Rule').size()
434+
for i in range(0, ruleCount):
435+
self.assertScaleDenominator(root, None, None, i)
436+
437+
def testRuleBasedNoRootScaleDependencies(self):
438+
layer = QgsVectorLayer("Polygon", "addfeat", "memory")
439+
440+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "ruleBased"))
441+
status = layer.loadNamedStyle(mFilePath)
442+
443+
dom, root = self.layerToSld(layer)
444+
print("Rule based, no root scale deps:" + dom.toString())
445+
446+
ruleCount = root.elementsByTagName('se:Rule').size()
447+
self.assertScaleDenominator(root, '1000', '40000000', 0)
448+
self.assertScaleDenominator(root, None, None, 1)
449+
450+
def testRuleBasedNoRootScaleDependencies(self):
451+
layer = QgsVectorLayer("Polygon", "addfeat", "memory")
452+
453+
mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "ruleBased"))
454+
status = layer.loadNamedStyle(mFilePath)
455+
layer.setMinimumScale(5000)
456+
layer.setMaximumScale(50000000)
457+
layer.setScaleBasedVisibility(True)
458+
459+
dom, root = self.layerToSld(layer)
460+
# print("Rule based, with root scale deps:" + dom.toString())
461+
462+
ruleCount = root.elementsByTagName('se:Rule').size()
463+
self.assertScaleDenominator(root, '5000', '40000000', 0)
464+
self.assertScaleDenominator(root, '5000', '50000000', 1)
465+
466+
def assertScaleDenominator(self, root, expectedMinScale, expectedMaxScale, index=0):
467+
rule = root.elementsByTagName('se:Rule').item(index).toElement()
468+
469+
if expectedMinScale:
470+
minScale = rule.elementsByTagName('se:MinScaleDenominator').item(0)
471+
self.assertEquals(expectedMinScale, minScale.firstChild().nodeValue())
472+
else:
473+
self.assertEquals(0, root.elementsByTagName('se:MinScaleDenominator').size())
474+
475+
if expectedMaxScale:
476+
maxScale = rule.elementsByTagName('se:MaxScaleDenominator').item(0)
477+
self.assertEquals(expectedMaxScale, maxScale.firstChild().nodeValue())
478+
else:
479+
self.assertEquals(0, root.elementsByTagName('se:MaxScaleDenominator').size())
480+
374481
def assertDashPattern(self, root, svgParameterIdx, expectedPattern):
375482
strokeWidth = root.elementsByTagName(
376483
'se:SvgParameter').item(svgParameterIdx)
@@ -401,5 +508,14 @@ def symbolToSld(self, symbolLayer):
401508
symbolLayer.toSld(dom, root, {})
402509
return dom, root
403510

511+
def layerToSld(self, mapLayer):
512+
dom = QDomDocument()
513+
root = dom.createElement("FakeRoot")
514+
dom.appendChild(root)
515+
error = None
516+
mapLayer.writeSld(root, dom, error, {})
517+
return dom, root
518+
519+
404520
if __name__ == '__main__':
405521
unittest.main()

‎tests/testdata/symbol_layer/categorized.qml

Lines changed: 903 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/testdata/symbol_layer/graduated.qml

Lines changed: 585 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/testdata/symbol_layer/ruleBased.qml

Lines changed: 509 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/testdata/symbol_layer/singleSymbol.qml

Lines changed: 615 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.