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
This commit is contained in:
Andrea Aime 2016-08-23 07:19:06 +02:00
parent ee87b0dbc9
commit a0adcc22e7
8 changed files with 504 additions and 34 deletions

View File

@ -1429,7 +1429,6 @@ void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
// Create the root element
QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
root.setAttribute( "version", "1.1.0" );
root.setAttribute( "units", "mm" ); // default qgsmaprenderer is Millimeters
root.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" );
root.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
root.setAttribute( "xmlns:se", "http://www.opengis.net/se" );

View File

@ -409,12 +409,9 @@ void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &elem
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, mOutlineWidth, mSymbolWidth );
// store w/h factor in a <VendorOption>
double widthHeightFactor = mSymbolWidth / mSymbolHeight;
QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
graphicElem.appendChild( factorElem );
double outlineWidth = QgsSymbolLayerUtils::rescaleUom( mOutlineWidth, mOutlineWidthUnit, props );
double symbolWidth = QgsSymbolLayerUtils::rescaleUom( mSymbolWidth, mSymbolWidthUnit, props );
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, outlineWidth, symbolWidth );
// <Rotation>
QgsDataDefined* ddRotation = getDataDefinedProperty( QgsSymbolLayer::EXPR_ROTATION );
@ -452,6 +449,15 @@ void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &elem
}
}
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement>
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
// store w/h factor in a <VendorOption>
double widthHeightFactor = mSymbolWidth / mSymbolHeight;
QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
graphicElem.appendChild( factorElem );
}
QgsSymbolLayer* QgsEllipseSymbolLayer::createFromSld( QDomElement &element )

View File

@ -352,11 +352,13 @@ void QgsSimpleFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
// <Stroke>
QDomElement strokeElem = doc.createElement( "se:Stroke" );
symbolizerElem.appendChild( strokeElem );
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, mBorderStyle, mBorderColor, mBorderWidth, &mPenJoinStyle );
double borderWidth = QgsSymbolLayerUtils::rescaleUom( mBorderWidth, mBorderWidthUnit, props );
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, mBorderStyle, borderWidth, borderWidth, &mPenJoinStyle );
}
// <se:Displacement>
QgsSymbolLayerUtils::createDisplacementElement( doc, symbolizerElem, mOffset );
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, symbolizerElem, offset );
}
QString QgsSimpleFillSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
@ -1778,6 +1780,7 @@ void QgsSVGFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
mPatternWidthUnit = unit;
mSvgOutlineWidthUnit = unit;
mOutlineWidthUnit = unit;
mOutline->setOutputUnit( unit );
}
QgsUnitTypes::RenderUnit QgsSVGFillSymbolLayer::outputUnit() const
@ -2088,7 +2091,8 @@ void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, cons
if ( !mSvgFilePath.isEmpty() )
{
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, mPatternWidth );
double partternWidth = QgsSymbolLayerUtils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, partternWidth );
}
else
{
@ -2099,7 +2103,8 @@ void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, cons
if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
{
QgsSymbolLayerUtils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
double svgOutlineWidth = QgsSymbolLayerUtils::rescaleUom( mSvgOutlineWidth, mSvgOutlineWidthUnit, props );
QgsSymbolLayerUtils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, svgOutlineWidth );
}
// <Rotation>
@ -2887,7 +2892,9 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme
//line properties must be inside the graphic definition
QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, mDistance );
lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
double distance = QgsSymbolLayerUtils::rescaleUom( mDistance, mDistanceUnit, props );
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
// <Rotation>
QString angleFunc;
@ -2905,6 +2912,7 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme
// <se:Displacement>
QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
}
@ -3064,6 +3072,11 @@ void QgsPointPatternFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit uni
mDistanceYUnit = unit;
mDisplacementXUnit = unit;
mDisplacementYUnit = unit;
if ( mMarkerSymbol )
{
mMarkerSymbol->setOutputUnit( unit );
}
}
QgsUnitTypes::RenderUnit QgsPointPatternFillSymbolLayer::outputUnit() const
@ -3306,7 +3319,9 @@ void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &elem
fillElem.appendChild( graphicFillElem );
// store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
double dx = QgsSymbolLayerUtils::rescaleUom( mDistanceX, mDistanceXUnit, props );
double dy = QgsSymbolLayerUtils::rescaleUom( mDistanceY, mDistanceYUnit, props );
QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "distance", dist );
symbolizerElem.appendChild( distanceElem );

View File

@ -410,14 +410,17 @@ void QgsSimpleLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
symbolizerElem.appendChild( strokeElem );
Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
&mPenJoinStyle, &mPenCapStyle, &mCustomDashVector );
double width = QgsSymbolLayerUtils::rescaleUom( mWidth, mWidthUnit, props );
QVector<qreal> customDashVector = QgsSymbolLayerUtils::rescaleUom( mCustomDashVector, mCustomDashPatternUnit, props );
QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, width,
&mPenJoinStyle, &mPenCapStyle, &customDashVector );
// <se:PerpendicularOffset>
if ( !qgsDoubleNear( mOffset, 0.0 ) )
{
QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( mOffset ) ) );
double offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
symbolizerElem.appendChild( perpOffsetElem );
}
}
@ -1413,7 +1416,8 @@ void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, "placement", "points" ) );
break;
default:
gap = qgsDoubleToString( mInterval );
double interval = QgsSymbolLayerUtils::rescaleUom( mInterval, mIntervalUnit, props );
gap = qgsDoubleToString( interval );
break;
}
@ -1453,7 +1457,8 @@ void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
if ( !qgsDoubleNear( mOffset, 0.0 ) )
{
QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( mOffset ) ) );
double offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
symbolizerElem.appendChild( perpOffsetElem );
}
}
@ -1554,6 +1559,7 @@ double QgsMarkerLineSymbolLayer::width() const
void QgsMarkerLineSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
{
QgsLineSymbolLayer::setOutputUnit( unit );
mMarker->setOutputUnit( unit );
mIntervalUnit = unit;
mOffsetUnit = unit;
mOffsetAlongLineUnit = unit;

View File

@ -1122,7 +1122,9 @@ void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mBorderColor, mOutlineStyle, mOutlineWidth, mSize );
double outlineWidth = QgsSymbolLayerUtils::rescaleUom( mOutlineWidth, mOutlineWidthUnit, props );
double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mBorderColor, mOutlineStyle, outlineWidth, size );
// <Rotation>
QString angleFunc;
@ -1139,7 +1141,8 @@ void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement>
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset );
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
}
QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
@ -2223,7 +2226,8 @@ void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &el
QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem );
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mColor, mSize );
double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mColor, size );
// <Rotation>
QString angleFunc;
@ -2241,7 +2245,8 @@ void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &el
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement>
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset );
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
}
QgsSymbolLayer* QgsSvgMarkerSymbolLayer::createFromSld( QDomElement &element )
@ -2844,7 +2849,8 @@ void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &e
QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
int markIndex = mChr.unicode();
QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );
double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, size );
// <Rotation>
QString angleFunc;
@ -2861,7 +2867,8 @@ void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &e
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement>
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset );
QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
}
QRectF QgsFontMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext& context )

View File

@ -421,7 +421,7 @@ QString QgsSymbolLayerUtils::encodeSldUom( QgsUnitTypes::RenderUnit unit, double
// pixel is the SLD default uom. The "standardized rendering pixel
// size" is defined to be 0.28mm × 0.28mm (millimeters).
if ( scaleFactor )
*scaleFactor = 0.28; // from millimeters to pixels
*scaleFactor = 1 / 0.28; // from millimeters to pixels
// http://www.opengeospatial.org/sld/units/pixel
return QString();
@ -3923,3 +3923,80 @@ QList<double> QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum,
return breaks;
}
double QgsSymbolLayerUtils::rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
{
double scale = 1;
bool roundToUnit = false;
if ( unit == QgsUnitTypes::RenderUnknownUnit )
{
if ( props.contains( "uomScale" ) )
{
bool ok;
scale = props.value( "uomScale" ).toDouble( &ok );
if ( !ok )
{
return size;
}
}
}
else
{
if ( props.value( "uom" ) == "http://www.opengeospatial.org/se/units/metre" )
{
switch ( unit )
{
case QgsUnitTypes::RenderMillimeters:
scale = 0.001;
break;
case QgsUnitTypes::RenderPixels:
scale = 0.00028;
roundToUnit = true;
break;
default:
scale = 1;
}
}
else
{
// target is pixels
switch ( unit )
{
case QgsUnitTypes::RenderMillimeters:
scale = 1 / 0.28;
roundToUnit = true;
break;
// we don't have a good case for map units, as pixel values won't change based on zoom
default:
scale = 1;
}
}
}
double rescaled = size * scale;
// round to unit if the result is pixels to avoid a weird looking SLD (people often think
// of pixels as integers, even if SLD allows for float values in there
if ( roundToUnit )
{
rescaled = qRound( rescaled );
}
return rescaled;
}
QPointF QgsSymbolLayerUtils::rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
{
double x = rescaleUom( point.x(), unit, props );
double y = rescaleUom( point.y(), unit, props );
return QPointF( x, y );
}
QVector<qreal> QgsSymbolLayerUtils::rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props )
{
QVector<qreal> result;
QVector<qreal>::const_iterator it = array.constBegin();
for ( ; it != array.constEnd(); ++it )
{
result.append( rescaleUom( *it, unit, props ) );
}
return result;
}

View File

@ -500,6 +500,28 @@ class CORE_EXPORT QgsSymbolLayerUtils
*/
static QList<double> prettyBreaks( double minimum, double maximum, int classes );
/** Rescales the given size based on the uomScale found in the props, if any is found, otherwise
* returns the value un-modified
* @note added in 3.0
* @note not available in Python bindings
*/
static double rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
/** Rescales the given point based on the uomScale found in the props, if any is found, otherwise
* returns a copy of the original point
* @note added in 3.0
* @note not available in Python bindings
*/
static QPointF rescaleUom( const QPointF& point, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
/** Rescales the given array based on the uomScale found in the props, if any is found, otherwise
* returns a copy of the original point
* @note added in 3.0
* @note not available in Python bindings
*/
static QVector<qreal> rescaleUom( const QVector<qreal>& array, QgsUnitTypes::RenderUnit unit, const QgsStringMap& props );
};
class QPolygonF;

View File

@ -28,10 +28,15 @@ import qgis # NOQA
import os
from qgis.PyQt.QtCore import pyqtWrapperType, Qt, QDir, QFile, QIODevice, QPointF
from qgis.PyQt.QtXml import (QDomDocument, QDomElement)
from qgis.PyQt.QtXml import (
QDomDocument, QDomElement, QDomNode, QDomNamedNodeMap)
from qgis.PyQt.QtGui import QColor
from qgis.core import QgsSimpleMarkerSymbolLayer
from qgis.core import (
QgsSimpleMarkerSymbolLayer, QgsUnitTypes, QgsSvgMarkerSymbolLayer,
QgsFontMarkerSymbolLayer, QgsEllipseSymbolLayer, QgsSimpleLineSymbolLayer,
QgsMarkerLineSymbolLayer, QgsMarkerSymbol, QgsSimpleFillSymbolLayer, QgsSVGFillSymbolLayer,
QgsLinePatternFillSymbolLayer, QgsPointPatternFillSymbolLayer)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
@ -46,22 +51,355 @@ class TestQgsSymbolLayerCreateSld(unittest.TestCase):
This class tests the creation of SLD from QGis layers
"""
def testSimpleMarkerSymbolLayer(self):
def testSimpleMarkerRotation(self):
symbol = QgsSimpleMarkerSymbolLayer(
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
symbol.setAngle(50)
dom = QDomDocument()
root = dom.createElement("FakeRoot")
dom.appendChild(root)
symbol.toSld(dom, root, {})
# print "This is the dom: " + dom.toString()
dom, root = self.symbolToSld(symbol)
# print( "Simple marker rotation: " + root.ownerDocument().toString())
self.assertStaticRotation(root, '50')
def assertStaticRotation(self, root, expectedValue):
# Check the rotation element is a literal, not a
rotation = root.elementsByTagName('se:Rotation').item(0)
literal = rotation.firstChild()
self.assertEquals("ogc:Literal", literal.nodeName())
self.assertEquals('50', literal.firstChild().nodeValue())
self.assertEquals(expectedValue, literal.firstChild().nodeValue())
def assertStaticDisplacement(self, root, expectedDispX, expectedDispY):
displacement = root.elementsByTagName('se:Displacement').item(0)
self.assertIsNotNone(displacement)
dx = displacement.firstChild()
self.assertIsNotNone(dx)
self.assertEquals("se:DisplacementX", dx.nodeName())
self.assertSldNumber(expectedDispX, dx.firstChild().nodeValue())
dy = displacement.lastChild()
self.assertIsNotNone(dy)
self.assertEquals("se:DisplacementY", dy.nodeName())
self.assertSldNumber(expectedDispY, dy.firstChild().nodeValue())
def assertSldNumber(self, expected, stringValue):
value = float(stringValue)
self.assertFloatEquals(expected, value, 0.01)
def assertFloatEquals(self, expected, actual, tol):
self.assertLess(abs(expected - actual), tol)
def testSimpleMarkerUnitDefault(self):
symbol = QgsSimpleMarkerSymbolLayer(
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
symbol.setOutlineWidth(3)
symbol.setOffset(QPointF(5, 10))
dom, root = self.symbolToSld(symbol)
# print("Simple marker unit mm: " + root.ownerDocument().toString())
# Check the size has been rescaled to pixels
self.assertStaticSize(root, '36')
# Check the same happened to the outline width
self.assertStrokeWidth(root, 2, 11)
self.assertStaticDisplacement(root, 18, 36)
def assertStrokeWidth(self, root, svgParameterIdx, expectedWidth):
strokeWidth = root.elementsByTagName(
'se:SvgParameter').item(svgParameterIdx)
svgParameterName = strokeWidth.attributes().namedItem('name')
self.assertEquals("stroke-width", svgParameterName.nodeValue())
self.assertSldNumber(
expectedWidth, strokeWidth.firstChild().nodeValue())
def testSimpleMarkerUnitPixels(self):
symbol = QgsSimpleMarkerSymbolLayer(
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
symbol.setOutlineWidth(3)
symbol.setOffset(QPointF(5, 10))
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print("Marker unit mm: " + root.ownerDocument().toString())
# Check the size has not been rescaled
self.assertStaticSize(root, '10')
# Check the same happened to the outline width
self.assertStrokeWidth(root, 2, 3)
self.assertStaticDisplacement(root, 5, 10)
def testSvgMarkerUnitDefault(self):
symbol = QgsSvgMarkerSymbolLayer('symbols/star.svg', 10, 90)
symbol.setOffset(QPointF(5, 10))
dom, root = self.symbolToSld(symbol)
# print("Svg marker mm: " + dom.toString())
# Check the size has been rescaled
self.assertStaticSize(root, '36')
# Check rotation for good measure
self.assertStaticRotation(root, '90')
self.assertStaticDisplacement(root, 18, 36)
def testSvgMarkerUnitPixels(self):
symbol = QgsSvgMarkerSymbolLayer('symbols/star.svg', 10, 0)
symbol.setOffset(QPointF(5, 10))
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print("Svg marker unit px: " + dom.toString())
# Check the size has not been rescaled
self.assertStaticSize(root, '10')
self.assertStaticDisplacement(root, 5, 10)
def testFontMarkerUnitDefault(self):
symbol = QgsFontMarkerSymbolLayer('sans', ',', 10, QColor('black'), 45)
symbol.setOffset(QPointF(5, 10))
dom, root = self.symbolToSld(symbol)
# print "Font marker unit mm: " + dom.toString()
# Check the size has been rescaled
self.assertStaticSize(root, '36')
self.assertStaticRotation(root, '45')
self.assertStaticDisplacement(root, 18, 36)
def testFontMarkerUnitPixel(self):
symbol = QgsFontMarkerSymbolLayer('sans', ',', 10, QColor('black'), 45)
symbol.setOffset(QPointF(5, 10))
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Font marker unit mm: " + dom.toString())
# Check the size has been rescaled
self.assertStaticSize(root, '10')
self.assertStaticRotation(root, '45')
self.assertStaticDisplacement(root, 5, 10)
def createEllipseSymbolLayer(self):
# No way to build it programmatically...
mTestName = 'QgsEllipseSymbolLayer'
mFilePath = QDir.toNativeSeparators(
'%s/symbol_layer/%s.sld' % (unitTestDataPath(), mTestName))
mDoc = QDomDocument(mTestName)
mFile = QFile(mFilePath)
mFile.open(QIODevice.ReadOnly)
mDoc.setContent(mFile, True)
mFile.close()
mSymbolLayer = QgsEllipseSymbolLayer.createFromSld(
mDoc.elementsByTagName('PointSymbolizer').item(0).toElement())
return mSymbolLayer
def testEllipseMarkerUnitDefault(self):
symbol = self.createEllipseSymbolLayer()
symbol.setOffset(QPointF(5, 10))
symbol.setOutputUnit(QgsUnitTypes.RenderMillimeters)
dom, root = self.symbolToSld(symbol)
# print ("Ellipse marker unit mm: " + dom.toString())
# Check the size has been rescaled
self.assertStaticSize(root, '25')
# Check also the stroke width
self.assertStrokeWidth(root, 2, 4)
self.assertStaticDisplacement(root, 18, 36)
def testEllipseMarkerUnitPixel(self):
symbol = self.createEllipseSymbolLayer()
symbol.setOffset(QPointF(5, 10))
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Ellipse marker unit mm: " + dom.toString())
# Check the size has been rescaled
self.assertStaticSize(root, '7')
# Check also the stroke width
self.assertStrokeWidth(root, 2, 1)
self.assertStaticDisplacement(root, 5, 10)
def testSimpleLineUnitDefault(self):
symbol = QgsSimpleLineSymbolLayer(QColor("black"), 1)
symbol.setCustomDashVector([10, 10])
symbol.setUseCustomDashPattern(True)
symbol.setOffset(5)
dom, root = self.symbolToSld(symbol)
# print ("Simple line px: \n" + dom.toString())
self.assertStrokeWidth(root, 1, 4)
self.assertDashPattern(root, 4, '36 36')
self.assertStaticPerpendicularOffset(root, '18')
def testSimpleLineUnitPixel(self):
symbol = QgsSimpleLineSymbolLayer(QColor("black"), 1)
symbol.setCustomDashVector([10, 10])
symbol.setUseCustomDashPattern(True)
symbol.setOffset(5)
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Simple line px: \n" + dom.toString())
self.assertStrokeWidth(root, 1, 1)
self.assertDashPattern(root, 4, '10 10')
self.assertStaticPerpendicularOffset(root, '5')
def testMarkLineUnitDefault(self):
symbol = QgsMarkerLineSymbolLayer()
symbol.setSubSymbol(
QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '3'}))
symbol.setInterval(5)
symbol.setOffset(5)
dom, root = self.symbolToSld(symbol)
# print ("Mark line mm: \n" + dom.toString())
# size of the mark
self.assertStaticSize(root, '11')
# gap and offset
self.assertStaticGap(root, '18')
self.assertStaticPerpendicularOffset(root, '18')
def testMarkLineUnitPixels(self):
symbol = QgsMarkerLineSymbolLayer()
symbol.setSubSymbol(
QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '3'}))
symbol.setInterval(5)
symbol.setOffset(5)
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Mark line px: \n" + dom.toString())
# size of the mark
self.assertStaticSize(root, '3')
# gap and offset
self.assertStaticGap(root, '5')
self.assertStaticPerpendicularOffset(root, '5')
def testSimpleFillDefault(self):
symbol = QgsSimpleFillSymbolLayer(
QColor('red'), Qt.SolidPattern, QColor('green'), Qt.SolidLine, 5)
symbol.setOffset(QPointF(5, 10))
dom, root = self.symbolToSld(symbol)
# print ("Simple fill mm: \n" + dom.toString())
self.assertStrokeWidth(root, 2, 18)
self.assertStaticDisplacement(root, 18, 36)
def testSimpleFillPixels(self):
symbol = QgsSimpleFillSymbolLayer(
QColor('red'), Qt.SolidPattern, QColor('green'), Qt.SolidLine, 5)
symbol.setOffset(QPointF(5, 10))
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ( "Simple fill px: \n" + dom.toString())
self.assertStrokeWidth(root, 2, 5)
self.assertStaticDisplacement(root, 5, 10)
def testSvgFillDefault(self):
symbol = QgsSVGFillSymbolLayer('test/star.svg', 10, 45)
symbol.setSvgOutlineWidth(3)
dom, root = self.symbolToSld(symbol)
# print ("Svg fill mm: \n" + dom.toString())
self.assertStaticRotation(root, '45')
self.assertStaticSize(root, '36')
# width of the svg outline
self.assertStrokeWidth(root, 1, 11)
# width of the polygon outline
self.assertStrokeWidth(root, 3, 1)
def testSvgFillPixel(self):
symbol = QgsSVGFillSymbolLayer('test/star.svg', 10, 45)
symbol.setSvgOutlineWidth(3)
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Svg fill px: \n" + dom.toString())
self.assertStaticRotation(root, '45')
self.assertStaticSize(root, '10')
# width of the svg outline
self.assertStrokeWidth(root, 1, 3)
# width of the polygon outline
self.assertStrokeWidth(root, 3, 0.26)
def testLineFillDefault(self):
symbol = QgsLinePatternFillSymbolLayer()
symbol.setLineAngle(45)
symbol.setLineWidth(1)
symbol.setOffset(5)
dom, root = self.symbolToSld(symbol)
# print ("Line fill mm: \n" + dom.toString())
self.assertStaticRotation(root, '45')
self.assertStrokeWidth(root, 1, 4)
self.assertStaticSize(root, '18')
self.assertStaticDisplacement(root, 15, 9)
def testLineFillPixels(self):
symbol = QgsLinePatternFillSymbolLayer()
symbol.setLineAngle(45)
symbol.setLineWidth(1)
symbol.setOffset(5)
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Line fill px: \n" + dom.toString())
self.assertStaticRotation(root, '45')
self.assertStrokeWidth(root, 1, 1)
self.assertStaticSize(root, '5')
self.assertStaticDisplacement(root, 4.25, 2.63)
def testPointFillDefault(self):
symbol = QgsPointPatternFillSymbolLayer()
dom, root = self.symbolToSld(symbol)
# print ("Point fill mm: \n" + dom.toString())
self.assertStaticSize(root, '7')
def testPointFillpixels(self):
symbol = QgsPointPatternFillSymbolLayer()
symbol.setOutputUnit(QgsUnitTypes.RenderPixels)
dom, root = self.symbolToSld(symbol)
# print ("Point fill px: \n" + dom.toString())
self.assertStaticSize(root, '2')
def assertDashPattern(self, root, svgParameterIdx, expectedPattern):
strokeWidth = root.elementsByTagName(
'se:SvgParameter').item(svgParameterIdx)
svgParameterName = strokeWidth.attributes().namedItem('name')
self.assertEquals("stroke-dasharray", svgParameterName.nodeValue())
self.assertEquals(
expectedPattern, strokeWidth.firstChild().nodeValue())
def assertStaticGap(self, root, expectedValue):
# Check the rotation element is a literal, not a
rotation = root.elementsByTagName('se:Gap').item(0)
literal = rotation.firstChild()
self.assertEquals("ogc:Literal", literal.nodeName())
self.assertEquals(expectedValue, literal.firstChild().nodeValue())
def assertStaticSize(self, root, expectedValue):
size = root.elementsByTagName('se:Size').item(0)
self.assertEquals(expectedValue, size.firstChild().nodeValue())
def assertStaticPerpendicularOffset(self, root, expectedValue):
offset = root.elementsByTagName('se:PerpendicularOffset').item(0)
self.assertEquals(expectedValue, offset.firstChild().nodeValue())
def symbolToSld(self, symbolLayer):
dom = QDomDocument()
root = dom.createElement("FakeRoot")
dom.appendChild(root)
symbolLayer.toSld(dom, root, {})
return dom, root
if __name__ == '__main__':
unittest.main()