Skip to content

Commit a0adcc2

Browse files
committedAug 23, 2016
Emit valid SLD when on screen mm are used
SLD does not have a notion of on screen mm, rescale them to pixels to get an equivalent, more valid and more widely usable, output
1 parent ee87b0d commit a0adcc2

File tree

8 files changed

+504
-34
lines changed

8 files changed

+504
-34
lines changed
 

‎src/core/qgsmaplayer.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1429,7 +1429,6 @@ void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
14291429
// Create the root element
14301430
QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
14311431
root.setAttribute( "version", "1.1.0" );
1432-
root.setAttribute( "units", "mm" ); // default qgsmaprenderer is Millimeters
14331432
root.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" );
14341433
root.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
14351434
root.setAttribute( "xmlns:se", "http://www.opengis.net/se" );

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,12 +409,9 @@ void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &elem
409409
QDomElement graphicElem = doc.createElement( "se:Graphic" );
410410
element.appendChild( graphicElem );
411411

412-
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, mOutlineWidth, mSymbolWidth );
413-
414-
// store w/h factor in a <VendorOption>
415-
double widthHeightFactor = mSymbolWidth / mSymbolHeight;
416-
QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
417-
graphicElem.appendChild( factorElem );
412+
double outlineWidth = QgsSymbolLayerUtils::rescaleUom( mOutlineWidth, mOutlineWidthUnit, props );
413+
double symbolWidth = QgsSymbolLayerUtils::rescaleUom( mSymbolWidth, mSymbolWidthUnit, props );
414+
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, outlineWidth, symbolWidth );
418415

419416
// <Rotation>
420417
QgsDataDefined* ddRotation = getDataDefinedProperty( QgsSymbolLayer::EXPR_ROTATION );
@@ -452,6 +449,15 @@ void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &elem
452449
}
453450
}
454451
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
452+
453+
// <Displacement>
454+
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
455+
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
456+
457+
// store w/h factor in a <VendorOption>
458+
double widthHeightFactor = mSymbolWidth / mSymbolHeight;
459+
QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
460+
graphicElem.appendChild( factorElem );
455461
}
456462

457463
QgsSymbolLayer* QgsEllipseSymbolLayer::createFromSld( QDomElement &element )

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,11 +352,13 @@ void QgsSimpleFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
352352
// <Stroke>
353353
QDomElement strokeElem = doc.createElement( "se:Stroke" );
354354
symbolizerElem.appendChild( strokeElem );
355-
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, mBorderStyle, mBorderColor, mBorderWidth, &mPenJoinStyle );
355+
double borderWidth = QgsSymbolLayerUtils::rescaleUom( mBorderWidth, mBorderWidthUnit, props );
356+
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, mBorderStyle, borderWidth, borderWidth, &mPenJoinStyle );
356357
}
357358

358359
// <se:Displacement>
359-
QgsSymbolLayerUtils::createDisplacementElement( doc, symbolizerElem, mOffset );
360+
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
361+
QgsSymbolLayerUtils::createDisplacementElement( doc, symbolizerElem, offset );
360362
}
361363

362364
QString QgsSimpleFillSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
@@ -1778,6 +1780,7 @@ void QgsSVGFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
17781780
mPatternWidthUnit = unit;
17791781
mSvgOutlineWidthUnit = unit;
17801782
mOutlineWidthUnit = unit;
1783+
mOutline->setOutputUnit( unit );
17811784
}
17821785

17831786
QgsUnitTypes::RenderUnit QgsSVGFillSymbolLayer::outputUnit() const
@@ -2088,7 +2091,8 @@ void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, cons
20882091

20892092
if ( !mSvgFilePath.isEmpty() )
20902093
{
2091-
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, mPatternWidth );
2094+
double partternWidth = QgsSymbolLayerUtils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2095+
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, partternWidth );
20922096
}
20932097
else
20942098
{
@@ -2099,7 +2103,8 @@ void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, cons
20992103

21002104
if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
21012105
{
2102-
QgsSymbolLayerUtils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
2106+
double svgOutlineWidth = QgsSymbolLayerUtils::rescaleUom( mSvgOutlineWidth, mSvgOutlineWidthUnit, props );
2107+
QgsSymbolLayerUtils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, svgOutlineWidth );
21032108
}
21042109

21052110
// <Rotation>
@@ -2887,7 +2892,9 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme
28872892
//line properties must be inside the graphic definition
28882893
QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
28892894
double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2890-
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, mDistance );
2895+
lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
2896+
double distance = QgsSymbolLayerUtils::rescaleUom( mDistance, mDistanceUnit, props );
2897+
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
28912898

28922899
// <Rotation>
28932900
QString angleFunc;
@@ -2905,6 +2912,7 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme
29052912

29062913
// <se:Displacement>
29072914
QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
2915+
lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
29082916
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
29092917
}
29102918

@@ -3064,6 +3072,11 @@ void QgsPointPatternFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit uni
30643072
mDistanceYUnit = unit;
30653073
mDisplacementXUnit = unit;
30663074
mDisplacementYUnit = unit;
3075+
if ( mMarkerSymbol )
3076+
{
3077+
mMarkerSymbol->setOutputUnit( unit );
3078+
}
3079+
30673080
}
30683081

30693082
QgsUnitTypes::RenderUnit QgsPointPatternFillSymbolLayer::outputUnit() const
@@ -3306,7 +3319,9 @@ void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &elem
33063319
fillElem.appendChild( graphicFillElem );
33073320

33083321
// store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3309-
QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
3322+
double dx = QgsSymbolLayerUtils::rescaleUom( mDistanceX, mDistanceXUnit, props );
3323+
double dy = QgsSymbolLayerUtils::rescaleUom( mDistanceY, mDistanceYUnit, props );
3324+
QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
33103325
QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "distance", dist );
33113326
symbolizerElem.appendChild( distanceElem );
33123327

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,14 +410,17 @@ void QgsSimpleLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
410410
symbolizerElem.appendChild( strokeElem );
411411

412412
Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
413-
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
414-
&mPenJoinStyle, &mPenCapStyle, &mCustomDashVector );
413+
double width = QgsSymbolLayerUtils::rescaleUom( mWidth, mWidthUnit, props );
414+
QVector<qreal> customDashVector = QgsSymbolLayerUtils::rescaleUom( mCustomDashVector, mCustomDashPatternUnit, props );
415+
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, width,
416+
&mPenJoinStyle, &mPenCapStyle, &customDashVector );
415417

416418
// <se:PerpendicularOffset>
417419
if ( !qgsDoubleNear( mOffset, 0.0 ) )
418420
{
419421
QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
420-
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( mOffset ) ) );
422+
double offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
423+
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
421424
symbolizerElem.appendChild( perpOffsetElem );
422425
}
423426
}
@@ -1413,7 +1416,8 @@ void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
14131416
symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, "placement", "points" ) );
14141417
break;
14151418
default:
1416-
gap = qgsDoubleToString( mInterval );
1419+
double interval = QgsSymbolLayerUtils::rescaleUom( mInterval, mIntervalUnit, props );
1420+
gap = qgsDoubleToString( interval );
14171421
break;
14181422
}
14191423

@@ -1453,7 +1457,8 @@ void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
14531457
if ( !qgsDoubleNear( mOffset, 0.0 ) )
14541458
{
14551459
QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1456-
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( mOffset ) ) );
1460+
double offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1461+
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
14571462
symbolizerElem.appendChild( perpOffsetElem );
14581463
}
14591464
}
@@ -1554,6 +1559,7 @@ double QgsMarkerLineSymbolLayer::width() const
15541559
void QgsMarkerLineSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
15551560
{
15561561
QgsLineSymbolLayer::setOutputUnit( unit );
1562+
mMarker->setOutputUnit( unit );
15571563
mIntervalUnit = unit;
15581564
mOffsetUnit = unit;
15591565
mOffsetAlongLineUnit = unit;

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,9 @@ void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
11221122
QDomElement graphicElem = doc.createElement( "se:Graphic" );
11231123
element.appendChild( graphicElem );
11241124

1125-
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mBorderColor, mOutlineStyle, mOutlineWidth, mSize );
1125+
double outlineWidth = QgsSymbolLayerUtils::rescaleUom( mOutlineWidth, mOutlineWidthUnit, props );
1126+
double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1127+
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mBorderColor, mOutlineStyle, outlineWidth, size );
11261128

11271129
// <Rotation>
11281130
QString angleFunc;
@@ -1139,7 +1141,8 @@ void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
11391141
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
11401142

11411143
// <Displacement>
1142-
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset );
1144+
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1145+
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
11431146
}
11441147

11451148
QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
@@ -2223,7 +2226,8 @@ void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &el
22232226
QDomElement graphicElem = doc.createElement( "se:Graphic" );
22242227
element.appendChild( graphicElem );
22252228

2226-
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mColor, mSize );
2229+
double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2230+
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mColor, size );
22272231

22282232
// <Rotation>
22292233
QString angleFunc;
@@ -2241,7 +2245,8 @@ void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &el
22412245
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
22422246

22432247
// <Displacement>
2244-
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset );
2248+
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2249+
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
22452250
}
22462251

22472252
QgsSymbolLayer* QgsSvgMarkerSymbolLayer::createFromSld( QDomElement &element )
@@ -2844,7 +2849,8 @@ void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &e
28442849

28452850
QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
28462851
int markIndex = mChr.unicode();
2847-
QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );
2852+
double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2853+
QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, size );
28482854

28492855
// <Rotation>
28502856
QString angleFunc;
@@ -2861,7 +2867,8 @@ void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &e
28612867
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
28622868

28632869
// <Displacement>
2864-
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset );
2870+
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2871+
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
28652872
}
28662873

28672874
QRectF QgsFontMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext& context )

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

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ QString QgsSymbolLayerUtils::encodeSldUom( QgsUnitTypes::RenderUnit unit, double
421421
// pixel is the SLD default uom. The "standardized rendering pixel
422422
// size" is defined to be 0.28mm × 0.28mm (millimeters).
423423
if ( scaleFactor )
424-
*scaleFactor = 0.28; // from millimeters to pixels
424+
*scaleFactor = 1 / 0.28; // from millimeters to pixels
425425

426426
// http://www.opengeospatial.org/sld/units/pixel
427427
return QString();
@@ -3923,3 +3923,80 @@ QList<double> QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum,
39233923

39243924
return breaks;
39253925
}
3926+
3927+
double QgsSymbolLayerUtils::rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
3928+
{
3929+
double scale = 1;
3930+
bool roundToUnit = false;
3931+
if ( unit == QgsUnitTypes::RenderUnknownUnit )
3932+
{
3933+
if ( props.contains( "uomScale" ) )
3934+
{
3935+
bool ok;
3936+
scale = props.value( "uomScale" ).toDouble( &ok );
3937+
if ( !ok )
3938+
{
3939+
return size;
3940+
}
3941+
}
3942+
}
3943+
else
3944+
{
3945+
if ( props.value( "uom" ) == "http://www.opengeospatial.org/se/units/metre" )
3946+
{
3947+
switch ( unit )
3948+
{
3949+
case QgsUnitTypes::RenderMillimeters:
3950+
scale = 0.001;
3951+
break;
3952+
case QgsUnitTypes::RenderPixels:
3953+
scale = 0.00028;
3954+
roundToUnit = true;
3955+
break;
3956+
default:
3957+
scale = 1;
3958+
}
3959+
}
3960+
else
3961+
{
3962+
// target is pixels
3963+
switch ( unit )
3964+
{
3965+
case QgsUnitTypes::RenderMillimeters:
3966+
scale = 1 / 0.28;
3967+
roundToUnit = true;
3968+
break;
3969+
// we don't have a good case for map units, as pixel values won't change based on zoom
3970+
default:
3971+
scale = 1;
3972+
}
3973+
}
3974+
3975+
}
3976+
double rescaled = size * scale;
3977+
// round to unit if the result is pixels to avoid a weird looking SLD (people often think
3978+
// of pixels as integers, even if SLD allows for float values in there
3979+
if ( roundToUnit )
3980+
{
3981+
rescaled = qRound( rescaled );
3982+
}
3983+
return rescaled;
3984+
}
3985+
3986+
QPointF QgsSymbolLayerUtils::rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
3987+
{
3988+
double x = rescaleUom( point.x(), unit, props );
3989+
double y = rescaleUom( point.y(), unit, props );
3990+
return QPointF( x, y );
3991+
}
3992+
3993+
QVector<qreal> QgsSymbolLayerUtils::rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
3994+
{
3995+
QVector<qreal> result;
3996+
QVector<qreal>::const_iterator it = array.constBegin();
3997+
for ( ; it != array.constEnd(); ++it )
3998+
{
3999+
result.append( rescaleUom( *it, unit, props ) );
4000+
}
4001+
return result;
4002+
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,28 @@ class CORE_EXPORT QgsSymbolLayerUtils
500500
*/
501501
static QList<double> prettyBreaks( double minimum, double maximum, int classes );
502502

503+
/** Rescales the given size based on the uomScale found in the props, if any is found, otherwise
504+
* returns the value un-modified
505+
* @note added in 3.0
506+
* @note not available in Python bindings
507+
*/
508+
static double rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
509+
510+
/** Rescales the given point based on the uomScale found in the props, if any is found, otherwise
511+
* returns a copy of the original point
512+
* @note added in 3.0
513+
* @note not available in Python bindings
514+
*/
515+
static QPointF rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
516+
517+
/** Rescales the given array based on the uomScale found in the props, if any is found, otherwise
518+
* returns a copy of the original point
519+
* @note added in 3.0
520+
* @note not available in Python bindings
521+
*/
522+
static QVector<qreal> rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
523+
524+
503525
};
504526

505527
class QPolygonF;

‎tests/src/python/test_qgssymbollayer_createsld.py

Lines changed: 347 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@
2828
import os
2929

3030
from qgis.PyQt.QtCore import pyqtWrapperType, Qt, QDir, QFile, QIODevice, QPointF
31-
from qgis.PyQt.QtXml import (QDomDocument, QDomElement)
31+
from qgis.PyQt.QtXml import (
32+
QDomDocument, QDomElement, QDomNode, QDomNamedNodeMap)
3233
from qgis.PyQt.QtGui import QColor
3334

34-
from qgis.core import QgsSimpleMarkerSymbolLayer
35+
from qgis.core import (
36+
QgsSimpleMarkerSymbolLayer, QgsUnitTypes, QgsSvgMarkerSymbolLayer,
37+
QgsFontMarkerSymbolLayer, QgsEllipseSymbolLayer, QgsSimpleLineSymbolLayer,
38+
QgsMarkerLineSymbolLayer, QgsMarkerSymbol, QgsSimpleFillSymbolLayer, QgsSVGFillSymbolLayer,
39+
QgsLinePatternFillSymbolLayer, QgsPointPatternFillSymbolLayer)
3540
from qgis.testing import start_app, unittest
3641
from utilities import unitTestDataPath
3742

@@ -46,22 +51,355 @@ class TestQgsSymbolLayerCreateSld(unittest.TestCase):
4651
This class tests the creation of SLD from QGis layers
4752
"""
4853

49-
def testSimpleMarkerSymbolLayer(self):
54+
def testSimpleMarkerRotation(self):
5055
symbol = QgsSimpleMarkerSymbolLayer(
5156
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
5257
symbol.setAngle(50)
53-
dom = QDomDocument()
54-
root = dom.createElement("FakeRoot")
55-
dom.appendChild(root)
56-
symbol.toSld(dom, root, {})
57-
# print "This is the dom: " + dom.toString()
58+
dom, root = self.symbolToSld(symbol)
59+
# print( "Simple marker rotation: " + root.ownerDocument().toString())
5860

61+
self.assertStaticRotation(root, '50')
62+
63+
def assertStaticRotation(self, root, expectedValue):
5964
# Check the rotation element is a literal, not a
6065
rotation = root.elementsByTagName('se:Rotation').item(0)
6166
literal = rotation.firstChild()
6267
self.assertEquals("ogc:Literal", literal.nodeName())
63-
self.assertEquals('50', literal.firstChild().nodeValue())
68+
self.assertEquals(expectedValue, literal.firstChild().nodeValue())
69+
70+
def assertStaticDisplacement(self, root, expectedDispX, expectedDispY):
71+
displacement = root.elementsByTagName('se:Displacement').item(0)
72+
self.assertIsNotNone(displacement)
73+
dx = displacement.firstChild()
74+
self.assertIsNotNone(dx)
75+
self.assertEquals("se:DisplacementX", dx.nodeName())
76+
self.assertSldNumber(expectedDispX, dx.firstChild().nodeValue())
77+
dy = displacement.lastChild()
78+
self.assertIsNotNone(dy)
79+
self.assertEquals("se:DisplacementY", dy.nodeName())
80+
self.assertSldNumber(expectedDispY, dy.firstChild().nodeValue())
81+
82+
def assertSldNumber(self, expected, stringValue):
83+
value = float(stringValue)
84+
self.assertFloatEquals(expected, value, 0.01)
85+
86+
def assertFloatEquals(self, expected, actual, tol):
87+
self.assertLess(abs(expected - actual), tol)
88+
89+
def testSimpleMarkerUnitDefault(self):
90+
symbol = QgsSimpleMarkerSymbolLayer(
91+
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
92+
symbol.setOutlineWidth(3)
93+
symbol.setOffset(QPointF(5, 10))
94+
dom, root = self.symbolToSld(symbol)
95+
# print("Simple marker unit mm: " + root.ownerDocument().toString())
96+
97+
# Check the size has been rescaled to pixels
98+
self.assertStaticSize(root, '36')
99+
100+
# Check the same happened to the outline width
101+
self.assertStrokeWidth(root, 2, 11)
102+
self.assertStaticDisplacement(root, 18, 36)
103+
104+
def assertStrokeWidth(self, root, svgParameterIdx, expectedWidth):
105+
strokeWidth = root.elementsByTagName(
106+
'se:SvgParameter').item(svgParameterIdx)
107+
svgParameterName = strokeWidth.attributes().namedItem('name')
108+
self.assertEquals("stroke-width", svgParameterName.nodeValue())
109+
self.assertSldNumber(
110+
expectedWidth, strokeWidth.firstChild().nodeValue())
111+
112+
def testSimpleMarkerUnitPixels(self):
113+
symbol = QgsSimpleMarkerSymbolLayer(
114+
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
115+
symbol.setOutlineWidth(3)
116+
symbol.setOffset(QPointF(5, 10))
117+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
118+
dom, root = self.symbolToSld(symbol)
119+
# print("Marker unit mm: " + root.ownerDocument().toString())
120+
121+
# Check the size has not been rescaled
122+
self.assertStaticSize(root, '10')
123+
124+
# Check the same happened to the outline width
125+
self.assertStrokeWidth(root, 2, 3)
126+
self.assertStaticDisplacement(root, 5, 10)
127+
128+
def testSvgMarkerUnitDefault(self):
129+
symbol = QgsSvgMarkerSymbolLayer('symbols/star.svg', 10, 90)
130+
symbol.setOffset(QPointF(5, 10))
131+
132+
dom, root = self.symbolToSld(symbol)
133+
# print("Svg marker mm: " + dom.toString())
134+
135+
# Check the size has been rescaled
136+
self.assertStaticSize(root, '36')
137+
138+
# Check rotation for good measure
139+
self.assertStaticRotation(root, '90')
140+
self.assertStaticDisplacement(root, 18, 36)
141+
142+
def testSvgMarkerUnitPixels(self):
143+
symbol = QgsSvgMarkerSymbolLayer('symbols/star.svg', 10, 0)
144+
symbol.setOffset(QPointF(5, 10))
145+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
146+
dom, root = self.symbolToSld(symbol)
147+
# print("Svg marker unit px: " + dom.toString())
148+
149+
# Check the size has not been rescaled
150+
self.assertStaticSize(root, '10')
151+
self.assertStaticDisplacement(root, 5, 10)
152+
153+
def testFontMarkerUnitDefault(self):
154+
symbol = QgsFontMarkerSymbolLayer('sans', ',', 10, QColor('black'), 45)
155+
symbol.setOffset(QPointF(5, 10))
156+
dom, root = self.symbolToSld(symbol)
157+
# print "Font marker unit mm: " + dom.toString()
158+
159+
# Check the size has been rescaled
160+
self.assertStaticSize(root, '36')
161+
self.assertStaticRotation(root, '45')
162+
self.assertStaticDisplacement(root, 18, 36)
163+
164+
def testFontMarkerUnitPixel(self):
165+
symbol = QgsFontMarkerSymbolLayer('sans', ',', 10, QColor('black'), 45)
166+
symbol.setOffset(QPointF(5, 10))
167+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
168+
dom, root = self.symbolToSld(symbol)
169+
# print ("Font marker unit mm: " + dom.toString())
170+
171+
# Check the size has been rescaled
172+
self.assertStaticSize(root, '10')
173+
self.assertStaticRotation(root, '45')
174+
self.assertStaticDisplacement(root, 5, 10)
175+
176+
def createEllipseSymbolLayer(self):
177+
# No way to build it programmatically...
178+
mTestName = 'QgsEllipseSymbolLayer'
179+
mFilePath = QDir.toNativeSeparators(
180+
'%s/symbol_layer/%s.sld' % (unitTestDataPath(), mTestName))
181+
182+
mDoc = QDomDocument(mTestName)
183+
mFile = QFile(mFilePath)
184+
mFile.open(QIODevice.ReadOnly)
185+
mDoc.setContent(mFile, True)
186+
mFile.close()
187+
mSymbolLayer = QgsEllipseSymbolLayer.createFromSld(
188+
mDoc.elementsByTagName('PointSymbolizer').item(0).toElement())
189+
return mSymbolLayer
190+
191+
def testEllipseMarkerUnitDefault(self):
192+
symbol = self.createEllipseSymbolLayer()
193+
symbol.setOffset(QPointF(5, 10))
194+
symbol.setOutputUnit(QgsUnitTypes.RenderMillimeters)
195+
dom, root = self.symbolToSld(symbol)
196+
# print ("Ellipse marker unit mm: " + dom.toString())
197+
198+
# Check the size has been rescaled
199+
self.assertStaticSize(root, '25')
200+
# Check also the stroke width
201+
self.assertStrokeWidth(root, 2, 4)
202+
self.assertStaticDisplacement(root, 18, 36)
203+
204+
def testEllipseMarkerUnitPixel(self):
205+
symbol = self.createEllipseSymbolLayer()
206+
symbol.setOffset(QPointF(5, 10))
207+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
208+
dom, root = self.symbolToSld(symbol)
209+
# print ("Ellipse marker unit mm: " + dom.toString())
210+
211+
# Check the size has been rescaled
212+
self.assertStaticSize(root, '7')
213+
# Check also the stroke width
214+
self.assertStrokeWidth(root, 2, 1)
215+
self.assertStaticDisplacement(root, 5, 10)
216+
217+
def testSimpleLineUnitDefault(self):
218+
symbol = QgsSimpleLineSymbolLayer(QColor("black"), 1)
219+
symbol.setCustomDashVector([10, 10])
220+
symbol.setUseCustomDashPattern(True)
221+
symbol.setOffset(5)
222+
dom, root = self.symbolToSld(symbol)
223+
224+
# print ("Simple line px: \n" + dom.toString())
225+
226+
self.assertStrokeWidth(root, 1, 4)
227+
self.assertDashPattern(root, 4, '36 36')
228+
self.assertStaticPerpendicularOffset(root, '18')
229+
230+
def testSimpleLineUnitPixel(self):
231+
symbol = QgsSimpleLineSymbolLayer(QColor("black"), 1)
232+
symbol.setCustomDashVector([10, 10])
233+
symbol.setUseCustomDashPattern(True)
234+
symbol.setOffset(5)
235+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
236+
dom, root = self.symbolToSld(symbol)
237+
238+
# print ("Simple line px: \n" + dom.toString())
239+
240+
self.assertStrokeWidth(root, 1, 1)
241+
self.assertDashPattern(root, 4, '10 10')
242+
self.assertStaticPerpendicularOffset(root, '5')
64243

244+
def testMarkLineUnitDefault(self):
245+
symbol = QgsMarkerLineSymbolLayer()
246+
symbol.setSubSymbol(
247+
QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '3'}))
248+
symbol.setInterval(5)
249+
symbol.setOffset(5)
250+
dom, root = self.symbolToSld(symbol)
251+
252+
# print ("Mark line mm: \n" + dom.toString())
253+
254+
# size of the mark
255+
self.assertStaticSize(root, '11')
256+
# gap and offset
257+
self.assertStaticGap(root, '18')
258+
self.assertStaticPerpendicularOffset(root, '18')
259+
260+
def testMarkLineUnitPixels(self):
261+
symbol = QgsMarkerLineSymbolLayer()
262+
symbol.setSubSymbol(
263+
QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '3'}))
264+
symbol.setInterval(5)
265+
symbol.setOffset(5)
266+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
267+
dom, root = self.symbolToSld(symbol)
268+
269+
# print ("Mark line px: \n" + dom.toString())
270+
271+
# size of the mark
272+
self.assertStaticSize(root, '3')
273+
# gap and offset
274+
self.assertStaticGap(root, '5')
275+
self.assertStaticPerpendicularOffset(root, '5')
276+
277+
def testSimpleFillDefault(self):
278+
symbol = QgsSimpleFillSymbolLayer(
279+
QColor('red'), Qt.SolidPattern, QColor('green'), Qt.SolidLine, 5)
280+
symbol.setOffset(QPointF(5, 10))
281+
282+
dom, root = self.symbolToSld(symbol)
283+
284+
# print ("Simple fill mm: \n" + dom.toString())
285+
286+
self.assertStrokeWidth(root, 2, 18)
287+
self.assertStaticDisplacement(root, 18, 36)
288+
289+
def testSimpleFillPixels(self):
290+
symbol = QgsSimpleFillSymbolLayer(
291+
QColor('red'), Qt.SolidPattern, QColor('green'), Qt.SolidLine, 5)
292+
symbol.setOffset(QPointF(5, 10))
293+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
294+
295+
dom, root = self.symbolToSld(symbol)
296+
# print ( "Simple fill px: \n" + dom.toString())
297+
298+
self.assertStrokeWidth(root, 2, 5)
299+
self.assertStaticDisplacement(root, 5, 10)
300+
301+
def testSvgFillDefault(self):
302+
symbol = QgsSVGFillSymbolLayer('test/star.svg', 10, 45)
303+
symbol.setSvgOutlineWidth(3)
304+
305+
dom, root = self.symbolToSld(symbol)
306+
# print ("Svg fill mm: \n" + dom.toString())
307+
308+
self.assertStaticRotation(root, '45')
309+
self.assertStaticSize(root, '36')
310+
# width of the svg outline
311+
self.assertStrokeWidth(root, 1, 11)
312+
# width of the polygon outline
313+
self.assertStrokeWidth(root, 3, 1)
314+
315+
def testSvgFillPixel(self):
316+
symbol = QgsSVGFillSymbolLayer('test/star.svg', 10, 45)
317+
symbol.setSvgOutlineWidth(3)
318+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
319+
320+
dom, root = self.symbolToSld(symbol)
321+
# print ("Svg fill px: \n" + dom.toString())
322+
323+
self.assertStaticRotation(root, '45')
324+
self.assertStaticSize(root, '10')
325+
# width of the svg outline
326+
self.assertStrokeWidth(root, 1, 3)
327+
# width of the polygon outline
328+
self.assertStrokeWidth(root, 3, 0.26)
329+
330+
def testLineFillDefault(self):
331+
symbol = QgsLinePatternFillSymbolLayer()
332+
symbol.setLineAngle(45)
333+
symbol.setLineWidth(1)
334+
symbol.setOffset(5)
335+
336+
dom, root = self.symbolToSld(symbol)
337+
# print ("Line fill mm: \n" + dom.toString())
338+
339+
self.assertStaticRotation(root, '45')
340+
self.assertStrokeWidth(root, 1, 4)
341+
self.assertStaticSize(root, '18')
342+
self.assertStaticDisplacement(root, 15, 9)
343+
344+
def testLineFillPixels(self):
345+
symbol = QgsLinePatternFillSymbolLayer()
346+
symbol.setLineAngle(45)
347+
symbol.setLineWidth(1)
348+
symbol.setOffset(5)
349+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
350+
351+
dom, root = self.symbolToSld(symbol)
352+
# print ("Line fill px: \n" + dom.toString())
353+
354+
self.assertStaticRotation(root, '45')
355+
self.assertStrokeWidth(root, 1, 1)
356+
self.assertStaticSize(root, '5')
357+
self.assertStaticDisplacement(root, 4.25, 2.63)
358+
359+
def testPointFillDefault(self):
360+
symbol = QgsPointPatternFillSymbolLayer()
361+
dom, root = self.symbolToSld(symbol)
362+
# print ("Point fill mm: \n" + dom.toString())
363+
364+
self.assertStaticSize(root, '7')
365+
366+
def testPointFillpixels(self):
367+
symbol = QgsPointPatternFillSymbolLayer()
368+
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
369+
dom, root = self.symbolToSld(symbol)
370+
# print ("Point fill px: \n" + dom.toString())
371+
372+
self.assertStaticSize(root, '2')
373+
374+
def assertDashPattern(self, root, svgParameterIdx, expectedPattern):
375+
strokeWidth = root.elementsByTagName(
376+
'se:SvgParameter').item(svgParameterIdx)
377+
svgParameterName = strokeWidth.attributes().namedItem('name')
378+
self.assertEquals("stroke-dasharray", svgParameterName.nodeValue())
379+
self.assertEquals(
380+
expectedPattern, strokeWidth.firstChild().nodeValue())
381+
382+
def assertStaticGap(self, root, expectedValue):
383+
# Check the rotation element is a literal, not a
384+
rotation = root.elementsByTagName('se:Gap').item(0)
385+
literal = rotation.firstChild()
386+
self.assertEquals("ogc:Literal", literal.nodeName())
387+
self.assertEquals(expectedValue, literal.firstChild().nodeValue())
388+
389+
def assertStaticSize(self, root, expectedValue):
390+
size = root.elementsByTagName('se:Size').item(0)
391+
self.assertEquals(expectedValue, size.firstChild().nodeValue())
392+
393+
def assertStaticPerpendicularOffset(self, root, expectedValue):
394+
offset = root.elementsByTagName('se:PerpendicularOffset').item(0)
395+
self.assertEquals(expectedValue, offset.firstChild().nodeValue())
396+
397+
def symbolToSld(self, symbolLayer):
398+
dom = QDomDocument()
399+
root = dom.createElement("FakeRoot")
400+
dom.appendChild(root)
401+
symbolLayer.toSld(dom, root, {})
402+
return dom, root
65403

66404
if __name__ == '__main__':
67405
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.