Skip to content
Permalink
Browse files

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
  • Loading branch information
aaime committed Aug 23, 2016
1 parent ee87b0d commit a0adcc22e76a23cd42cceca7e3024aa45f4d89a5
@@ -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" );
@@ -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 )
@@ -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 );

@@ -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;
@@ -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 )
@@ -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;
}
@@ -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;

0 comments on commit a0adcc2

Please sign in to comment.
You can’t perform that action at this time.