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 // Create the root element
QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" ); QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
root.setAttribute( "version", "1.1.0" ); 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( "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:ogc", "http://www.opengis.net/ogc" );
root.setAttribute( "xmlns:se", "http://www.opengis.net/se" ); 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" ); QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem ); element.appendChild( graphicElem );
QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, mOutlineWidth, mSymbolWidth ); double outlineWidth = QgsSymbolLayerUtils::rescaleUom( mOutlineWidth, mOutlineWidthUnit, props );
double symbolWidth = QgsSymbolLayerUtils::rescaleUom( mSymbolWidth, mSymbolWidthUnit, props );
// store w/h factor in a <VendorOption> QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mOutlineColor, mOutlineStyle, outlineWidth, symbolWidth );
double widthHeightFactor = mSymbolWidth / mSymbolHeight;
QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
graphicElem.appendChild( factorElem );
// <Rotation> // <Rotation>
QgsDataDefined* ddRotation = getDataDefinedProperty( QgsSymbolLayer::EXPR_ROTATION ); QgsDataDefined* ddRotation = getDataDefinedProperty( QgsSymbolLayer::EXPR_ROTATION );
@ -452,6 +449,15 @@ void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &elem
} }
} }
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc ); 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 ) QgsSymbolLayer* QgsEllipseSymbolLayer::createFromSld( QDomElement &element )

View File

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

View File

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

View File

@ -1122,7 +1122,9 @@ void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
QDomElement graphicElem = doc.createElement( "se:Graphic" ); QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem ); 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> // <Rotation>
QString angleFunc; QString angleFunc;
@ -1139,7 +1141,8 @@ void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc ); QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement> // <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 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" ); QDomElement graphicElem = doc.createElement( "se:Graphic" );
element.appendChild( graphicElem ); 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> // <Rotation>
QString angleFunc; QString angleFunc;
@ -2241,7 +2245,8 @@ void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &el
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc ); QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement> // <Displacement>
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, mOffset ); QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
} }
QgsSymbolLayer* QgsSvgMarkerSymbolLayer::createFromSld( QDomElement &element ) QgsSymbolLayer* QgsSvgMarkerSymbolLayer::createFromSld( QDomElement &element )
@ -2844,7 +2849,8 @@ void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &e
QString fontPath = QString( "ttf://%1" ).arg( mFontFamily ); QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
int markIndex = mChr.unicode(); 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> // <Rotation>
QString angleFunc; QString angleFunc;
@ -2861,7 +2867,8 @@ void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &e
QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc ); QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
// <Displacement> // <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 ) 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 // pixel is the SLD default uom. The "standardized rendering pixel
// size" is defined to be 0.28mm × 0.28mm (millimeters). // size" is defined to be 0.28mm × 0.28mm (millimeters).
if ( scaleFactor ) if ( scaleFactor )
*scaleFactor = 0.28; // from millimeters to pixels *scaleFactor = 1 / 0.28; // from millimeters to pixels
// http://www.opengeospatial.org/sld/units/pixel // http://www.opengeospatial.org/sld/units/pixel
return QString(); return QString();
@ -3923,3 +3923,80 @@ QList<double> QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum,
return breaks; 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 ); 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; class QPolygonF;

View File

@ -28,10 +28,15 @@ import qgis # NOQA
import os import os
from qgis.PyQt.QtCore import pyqtWrapperType, Qt, QDir, QFile, QIODevice, QPointF 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.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 qgis.testing import start_app, unittest
from utilities import unitTestDataPath from utilities import unitTestDataPath
@ -46,22 +51,355 @@ class TestQgsSymbolLayerCreateSld(unittest.TestCase):
This class tests the creation of SLD from QGis layers This class tests the creation of SLD from QGis layers
""" """
def testSimpleMarkerSymbolLayer(self): def testSimpleMarkerRotation(self):
symbol = QgsSimpleMarkerSymbolLayer( symbol = QgsSimpleMarkerSymbolLayer(
'star', QColor(255, 0, 0), QColor(0, 255, 0), 10) 'star', QColor(255, 0, 0), QColor(0, 255, 0), 10)
symbol.setAngle(50) symbol.setAngle(50)
dom = QDomDocument() dom, root = self.symbolToSld(symbol)
root = dom.createElement("FakeRoot") # print( "Simple marker rotation: " + root.ownerDocument().toString())
dom.appendChild(root)
symbol.toSld(dom, root, {})
# print "This is the dom: " + dom.toString()
self.assertStaticRotation(root, '50')
def assertStaticRotation(self, root, expectedValue):
# Check the rotation element is a literal, not a # Check the rotation element is a literal, not a
rotation = root.elementsByTagName('se:Rotation').item(0) rotation = root.elementsByTagName('se:Rotation').item(0)
literal = rotation.firstChild() literal = rotation.firstChild()
self.assertEquals("ogc:Literal", literal.nodeName()) 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__': if __name__ == '__main__':
unittest.main() unittest.main()