Skip to content

Commit 2f268d4

Browse files
aaimerldhont
authored andcommittedSep 20, 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 5c4fd97 commit 2f268d4

File tree

8 files changed

+505
-33
lines changed

8 files changed

+505
-33
lines changed
 

‎src/core/qgsmaplayer.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,6 @@ void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg )
14621462
// Create the root element
14631463
QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
14641464
root.setAttribute( "version", "1.1.0" );
1465-
root.setAttribute( "units", "mm" ); // default qgsmaprenderer is Millimeters
14661465
root.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" );
14671466
root.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
14681467
root.setAttribute( "xmlns:se", "http://www.opengis.net/se" );

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

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

412-
QgsSymbolLayerV2Utils::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 = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
417-
graphicElem.appendChild( factorElem );
412+
double outlineWidth = QgsSymbolLayerV2Utils::rescaleUom( mOutlineWidth, mOutlineWidthUnit, props );
413+
double symbolWidth = QgsSymbolLayerV2Utils::rescaleUom( mSymbolWidth, mSymbolWidthUnit, props );
414+
QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, outlineWidth, symbolWidth );
418415

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

457464
QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::createFromSld( QDomElement &element )

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

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

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

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

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

20892092
if ( !mSvgFilePath.isEmpty() )
20902093
{
2091-
QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, mPatternWidth );
2094+
double partternWidth = QgsSymbolLayerV2Utils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2095+
QgsSymbolLayerV2Utils::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-
QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
2106+
double svgOutlineWidth = QgsSymbolLayerV2Utils::rescaleUom( mSvgOutlineWidth, mSvgOutlineWidthUnit, props );
2107+
QgsSymbolLayerV2Utils::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-
QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, mDistance );
2895+
lineWidth = QgsSymbolLayerV2Utils::rescaleUom( lineWidth, mLineWidthUnit, props );
2896+
double distance = QgsSymbolLayerV2Utils::rescaleUom( mDistance, mDistanceUnit, props );
2897+
QgsSymbolLayerV2Utils::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 = QgsSymbolLayerV2Utils::rescaleUom( lineOffset, mOffsetUnit, props );
29082916
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
29092917
}
29102918

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

30693082
QgsSymbolV2::OutputUnit 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 = QgsSymbolLayerV2Utils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
3322+
double dx = QgsSymbolLayerV2Utils::rescaleUom( mDistanceX, mDistanceXUnit, props );
3323+
double dy = QgsSymbolLayerV2Utils::rescaleUom( mDistanceY, mDistanceYUnit, props );
3324+
QString dist = QgsSymbolLayerV2Utils::encodePoint( QPointF( dx, dy ) );
33103325
QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
33113326
symbolizerElem.appendChild( distanceElem );
33123327

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

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

412412
Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
413-
QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
414-
&mPenJoinStyle, &mPenCapStyle, &mCustomDashVector );
413+
double width = QgsSymbolLayerV2Utils::rescaleUom( mWidth, mWidthUnit, props );
414+
QVector<qreal> customDashVector = QgsSymbolLayerV2Utils::rescaleUom( mCustomDashVector, mCustomDashPatternUnit, props );
415+
QgsSymbolLayerV2Utils::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 = QgsSymbolLayerV2Utils::rescaleUom( mOffset, mOffsetUnit, props );
423+
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
421424
symbolizerElem.appendChild( perpOffsetElem );
422425
}
423426
}
@@ -1413,7 +1416,8 @@ void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element,
14131416
symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
14141417
break;
14151418
default:
1416-
gap = qgsDoubleToString( mInterval );
1419+
double interval = QgsSymbolLayerV2Utils::rescaleUom( mInterval, mIntervalUnit, props );
1420+
gap = qgsDoubleToString( interval );
14171421
break;
14181422
}
14191423

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

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

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

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

11281130
// <Rotation>
11291131
QString angleFunc;
@@ -1140,7 +1142,8 @@ void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElemen
11401142
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
11411143

11421144
// <Displacement>
1143-
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
1145+
QPointF offset = QgsSymbolLayerV2Utils::rescaleUom( mOffset, mOffsetUnit, props );
1146+
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, offset );
11441147
}
11451148

11461149
QString QgsSimpleMarkerSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
@@ -2225,7 +2228,8 @@ void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &
22252228
QDomElement graphicElem = doc.createElement( "se:Graphic" );
22262229
element.appendChild( graphicElem );
22272230

2228-
QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mColor, mSize );
2231+
double size = QgsSymbolLayerV2Utils::rescaleUom( mSize, mSizeUnit, props );
2232+
QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mColor, size );
22292233

22302234
// <Rotation>
22312235
QString angleFunc;
@@ -2243,7 +2247,8 @@ void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &
22432247
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
22442248

22452249
// <Displacement>
2246-
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
2250+
QPointF offset = QgsSymbolLayerV2Utils::rescaleUom( mOffset, mOffsetUnit, props );
2251+
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, offset );
22472252
}
22482253

22492254
QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element )
@@ -2848,6 +2853,8 @@ void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement
28482853
QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
28492854
int markIndex = mChr.unicode();
28502855
QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );
2856+
double size = QgsSymbolLayerV2Utils::rescaleUom( mSize, mSizeUnit, props );
2857+
QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, size );
28512858

28522859
// <Rotation>
28532860
QString angleFunc;
@@ -2864,7 +2871,8 @@ void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement
28642871
QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
28652872

28662873
// <Displacement>
2867-
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset );
2874+
QPointF offset = QgsSymbolLayerV2Utils::rescaleUom( mOffset, mOffsetUnit, props );
2875+
QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, offset );
28682876
}
28692877

28702878
QRectF QgsFontMarkerSymbolLayerV2::bounds( QPointF point, QgsSymbolV2RenderContext& context )

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

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ QString QgsSymbolLayerV2Utils::encodeSldUom( QgsSymbolV2::OutputUnit unit, doubl
455455
// pixel is the SLD default uom. The "standardized rendering pixel
456456
// size" is defined to be 0.28mm × 0.28mm (millimeters).
457457
if ( scaleFactor )
458-
*scaleFactor = 0.28; // from millimeters to pixels
458+
*scaleFactor = 1 / 0.28; // from millimeters to pixels
459459

460460
// http://www.opengeospatial.org/sld/units/pixel
461461
return QString();
@@ -4081,3 +4081,80 @@ QList<double> QgsSymbolLayerV2Utils::prettyBreaks( double minimum, double maximu
40814081

40824082
return breaks;
40834083
}
4084+
4085+
double QgsSymbolLayerV2Utils::rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
4086+
{
4087+
double scale = 1;
4088+
bool roundToUnit = false;
4089+
if ( unit == QgsUnitTypes::RenderUnknownUnit )
4090+
{
4091+
if ( props.contains( "uomScale" ) )
4092+
{
4093+
bool ok;
4094+
scale = props.value( "uomScale" ).toDouble( &ok );
4095+
if ( !ok )
4096+
{
4097+
return size;
4098+
}
4099+
}
4100+
}
4101+
else
4102+
{
4103+
if ( props.value( "uom" ) == "http://www.opengeospatial.org/se/units/metre" )
4104+
{
4105+
switch ( unit )
4106+
{
4107+
case QgsUnitTypes::RenderMillimeters:
4108+
scale = 0.001;
4109+
break;
4110+
case QgsUnitTypes::RenderPixels:
4111+
scale = 0.00028;
4112+
roundToUnit = true;
4113+
break;
4114+
default:
4115+
scale = 1;
4116+
}
4117+
}
4118+
else
4119+
{
4120+
// target is pixels
4121+
switch ( unit )
4122+
{
4123+
case QgsUnitTypes::RenderMillimeters:
4124+
scale = 1 / 0.28;
4125+
roundToUnit = true;
4126+
break;
4127+
// we don't have a good case for map units, as pixel values won't change based on zoom
4128+
default:
4129+
scale = 1;
4130+
}
4131+
}
4132+
4133+
}
4134+
double rescaled = size * scale;
4135+
// round to unit if the result is pixels to avoid a weird looking SLD (people often think
4136+
// of pixels as integers, even if SLD allows for float values in there
4137+
if ( roundToUnit )
4138+
{
4139+
rescaled = qRound( rescaled );
4140+
}
4141+
return rescaled;
4142+
}
4143+
4144+
QPointF QgsSymbolLayerV2Utils::rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
4145+
{
4146+
double x = rescaleUom( point.x(), unit, props );
4147+
double y = rescaleUom( point.y(), unit, props );
4148+
return QPointF( x, y );
4149+
}
4150+
4151+
QVector<qreal> QgsSymbolLayerV2Utils::rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
4152+
{
4153+
QVector<qreal> result;
4154+
QVector<qreal>::const_iterator it = array.constBegin();
4155+
for ( ; it != array.constEnd(); ++it )
4156+
{
4157+
result.append( rescaleUom( *it, unit, props ) );
4158+
}
4159+
return result;
4160+
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,28 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
479479
*/
480480
static QList<double> prettyBreaks( double minimum, double maximum, int classes );
481481

482+
/** Rescales the given size based on the uomScale found in the props, if any is found, otherwise
483+
* returns the value un-modified
484+
* @note added in 3.0
485+
* @note not available in Python bindings
486+
*/
487+
static double rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
488+
489+
/** Rescales the given point based on the uomScale found in the props, if any is found, otherwise
490+
* returns a copy of the original point
491+
* @note added in 3.0
492+
* @note not available in Python bindings
493+
*/
494+
static QPointF rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
495+
496+
/** Rescales the given array based on the uomScale found in the props, if any is found, otherwise
497+
* returns a copy of the original point
498+
* @note added in 3.0
499+
* @note not available in Python bindings
500+
*/
501+
static QVector<qreal> rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
502+
503+
482504
};
483505

484506
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.