# Source code for svir.utilities.sldadapter

    # -*- coding: utf-8 -*-
    #/***************************************************************************
    # Irmt
    #                                 A QGIS plugin
    # OpenQuake Integrated Risk Modelling Toolkit
    #                              -------------------
    #        begin                : 2015-03-27
    #        copyright            : (C) 2015 by GEM Foundation
    #        email                : devops@openquake.org
    # ***************************************************************************/
    #
    # OpenQuake is free software: you can redistribute it and/or modify it
    # under the terms of the GNU Affero General Public License as published
    # by the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # OpenQuake is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU Affero General Public License
    # along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

    # Part of this code was taken and re-adapted from
    # https://github.com/boundlessgeo/suite-qgis-plugin/blob/master/src/opengeo/qgis/sldadapter.py
    # methods to convert the SLD produced by GeoServer (1.0) to the SLD produced
    # by QGIS (1.1), and also the other way round. This is a quick and dirty
    # solution until both programs support the same specification

    import re
    import os
    from qgis.PyQt.QtCore import Qt
    from qgis.PyQt.QtXml import QDomDocument
    from qgis.core import (
        QgsSingleBandGrayRenderer, QgsExpression, QgsOgcUtils,
        QgsSingleBandPseudoColorRenderer, QgsSymbolLayerUtils, QgsSymbol,
        QgsUnitTypes)

    SIZE_FACTOR = 4
    RASTER_SLD_TEMPLATE = (
        '<?xml version="1.0"?>'
        '<sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld"'
        ' xmlns:sld="http://www.opengis.net/sld"'
        ' xmlns:ogc="http://www.opengis.net/ogc"'
        ' xmlns:gml="http://www.opengis.''net/gml" version="1.0.0">'
        '<sld:NamedLayer>'
        '<sld:Name>STYLE_NAME</sld:Name>'
        '<sld:UserStyle>'
        '<sld:Name>STYLE_NAME</sld:Name>'
        '<sld:Title/>'
        '<sld:FeatureTypeStyle>'
        # '<sld:Name>name</sld:Name>'
        '<sld:Rule>'
        # '<sld:Name>Single symbol</sld:Name>'
        '<RasterSymbolizer>'
        'SYMBOLIZER_CODE'
        '</RasterSymbolizer>'
        '</sld:Rule>'
        '</sld:FeatureTypeStyle>'
        '</sld:UserStyle>'
        '</sld:NamedLayer>'
        '</sld:StyledLayerDescriptor>')

    def adaptQgsToGs(sld, layer):
        sld = sld.replace("se:SvgParameter", "CssParameter")
        sld = sld.replace("1.1.", "1.0.")
        sld = sld.replace("\t", "")
        sld = sld.replace("\n", "")
        sld = re.sub("\s\s+", " ", sld)
        sld = re.sub("<ogc:Filter>[ ]*?<ogc:Filter>", "<ogc:Filter>", sld)
        sld = re.sub("</ogc:Filter>[ ]*?</ogc:Filter>", "</ogc:Filter>", sld)
        if layer.hasScaleBasedVisibility():
            s = ("<MinScaleDenominator>" + str(layer.minimumScale()) +
                 "</MinScaleDenominator><MaxScaleDenominator>" +
                 str(layer.maximumScale()) + "</MaxScaleDenominator>")
            sld = sld.replace("<se:Rule>", "<se:Rule>" + s)
        labeling = layer.customProperty("labeling/enabled")
        labeling = str(labeling).lower() == str(True).lower()
        if labeling:
            s = getLabelingAsSld(layer)
            sld = sld.replace("<se:Rule>", "<se:Rule>" + s)
        sld = sld.replace("se:", "sld:")
        sizes = re.findall("<sld:Size>.*?</sld:Size>", sld)
        for size in sizes:
            newsize = "<sld:Size>%f</sld:Size>" % (
                float(size[10:-11]) * SIZE_FACTOR)
            sld = sld.replace(size, newsize)
        return sld

    def getLabelingAsSld(layer):
        try:
            s = "<TextSymbolizer><Label>"
            s += ("<ogc:PropertyName>" + layer.customProperty("labeling/fieldName")
                  + "</ogc:PropertyName>")
            s += "</Label>"
            r = int(layer.customProperty("labeling/textColorR"))
            g = int(layer.customProperty("labeling/textColorG"))
            b = int(layer.customProperty("labeling/textColorB"))
            rgb = '#%02x%02x%02x' % (r, g, b)
            s += ('<Fill><CssParameter name="fill">' + rgb
                  + "</CssParameter></Fill>")
            s += "<Font>"
            s += ('<CssParameter name="font-family">'
                  + layer.customProperty("labeling/fontFamily")
                  + '</CssParameter>')
            s += ('<CssParameter name="font-size">'
                  + str(layer.customProperty("labeling/fontSize"))
                  + '</CssParameter>')
            if bool(layer.customProperty("labeling/fontItalic")):
                s += '<CssParameter name="font-style">italic</CssParameter>'
            if bool(layer.customProperty("labeling/fontBold")):
                s += '<CssParameter name="font-weight">bold</CssParameter>'
            s += "</Font>"
            s += "<LabelPlacement>"
            s += ("<PointPlacement>"
                  "<AnchorPoint>"
                  "<AnchorPointX>0.5</AnchorPointX>"
                  "<AnchorPointY>0.5</AnchorPointY>"
                  "</AnchorPoint>")
            s += "<Displacement>"
            s += ("<DisplacementX>"
                  + str(layer.customProperty("labeling/xOffset"))
                  + "0</DisplacementX>")
            s += ("<DisplacementY>"
                  + str(layer.customProperty("labeling/yOffset"))
                  + "0</DisplacementY>")
            s += "</Displacement>"
            s += ("<Rotation>"
                  + str(layer.customProperty("labeling/angleOffset"))
                  + "</Rotation>")
            s += "</PointPlacement></LabelPlacement>"
            s += "</TextSymbolizer>"
            return s
        except Exception:
            return ""

    def adaptGsToQgs(sld):
        # TODO
        return sld

    def getGsCompatibleSld(layer, style_name):
        sld = getStyleAsSld(layer, style_name)
        if sld is not None:
            return adaptQgsToGs(sld, layer)
        else:
            return None
    # QDomElement QgsFeatureRenderer::writeSld( QDomDocument& doc, const QString& styleName ) const
    #   492 {
    #   493   QDomElement userStyleElem = doc.createElement( "UserStyle" );
    #   494
    #   495   QDomElement nameElem = doc.createElement( "se:Name" );
    #   496   nameElem.appendChild( doc.createTextNode( styleName ) );
    #   497   userStyleElem.appendChild( nameElem );
    #   498
    #   499   QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" );
    #   500   toSld( doc, featureTypeStyleElem );
    #   501   userStyleElem.appendChild( featureTypeStyleElem );
    #   502
    #   503   return userStyleElem;
    #   504 }

     # void QgsRuleBasedRenderer::Rule::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props )
     #  300 {
     #  301   // do not convert this rule if there are no symbols
     #  302   if ( symbols().isEmpty() )
     #  303     return;
     #  304
     #  305   if ( !mFilterExp.isEmpty() )
     #  306   {
     #  307     if ( !props.value( "filter", "" ).isEmpty() )
     #  308       props[ "filter" ] += " AND ";
     #  309     props[ "filter" ] += mFilterExp;
     #  310   }
     #  311
     #  312   if ( mScaleMinDenom != 0 )
     #  313   {
     #  314     bool ok;
     #  315     int parentScaleMinDenom = props.value( "minimumScale", "0" ).toInt( &ok );
     #  316     if ( !ok || parentScaleMinDenom <= 0 )
     #  317       props[ "minimumScale" ] = QString::number( mScaleMinDenom );
     #  318     else
     #  319       props[ "minimumScale" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
     #  320   }
     #  321
     #  322   if ( mScaleMaxDenom != 0 )
     #  323   {
     #  324     bool ok;
     #  325     int parentScaleMaxDenom = props.value( "maximumScale", "0" ).toInt( &ok );
     #  326     if ( !ok || parentScaleMaxDenom <= 0 )
     #  327       props[ "maximumScale" ] = QString::number( mScaleMaxDenom );
     #  328     else
     #  329       props[ "maximumScale" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
     #  330   }
     #  331
     #  332   if ( mSymbol )
     #  333   {
     #  334     QDomElement ruleElem = doc.createElement( "se:Rule" );
     #  335     element.appendChild( ruleElem );
     #  336
     #  337     //XXX: <se:Name> is the rule identifier, but our the Rule objects
     #  338     // have no properties could be used as identifier. Use the label.
     #  339     QDomElement nameElem = doc.createElement( "se:Name" );
     #  340     nameElem.appendChild( doc.createTextNode( mLabel ) );
     #  341     ruleElem.appendChild( nameElem );
     #  342
     #  343     if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
     #  344     {
     #  345       QDomElement descrElem = doc.createElement( "se:Description" );
     #  346       if ( !mLabel.isEmpty() )
     #  347       {
     #  348         QDomElement titleElem = doc.createElement( "se:Title" );
     #  349         titleElem.appendChild( doc.createTextNode( mLabel ) );
     #  350         descrElem.appendChild( titleElem );
     #  351       }
     #  352       if ( !mDescription.isEmpty() )
     #  353       {
     #  354         QDomElement abstractElem = doc.createElement( "se:Abstract" );
     #  355         abstractElem.appendChild( doc.createTextNode( mDescription ) );
     #  356         descrElem.appendChild( abstractElem );
     #  357       }
     #  358       ruleElem.appendChild( descrElem );
     #  359     }
     #  360
     #  361     if ( !props.value( "filter", "" ).isEmpty() )
     #  362     {
     #  363       QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( "filter", "" ) );
     #  364     }
     #  365
     #  366     if ( !props.value( "minimumScale", "" ).isEmpty() )
     #  367     {
     #  368       QDomElement minimumScaleElem = doc.createElement( "se:MinScaleDenominator" );
     #  369       minimumScaleElem.appendChild( doc.createTextNode( props.value( "minimumScale", "" ) ) );
     #  370       ruleElem.appendChild( minimumScaleElem );
     #  371     }
     #  372
     #  373     if ( !props.value( "maximumScale", "" ).isEmpty() )
     #  374     {
     #  375       QDomElement maximumScaleElem = doc.createElement( "se:MaxScaleDenominator" );
     #  376       maximumScaleElem.appendChild( doc.createTextNode( props.value( "maximumScale", "" ) ) );
     #  377       ruleElem.appendChild( maximumScaleElem );
     #  378     }
     #  379
     #  380     mSymbol->toSld( doc, ruleElem, props );
     #  381   }
     #  382
     #  383   // loop into childern rule list
     #  384   for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
     #  385   {
     #  386     ( *it )->toSld( doc, element, props );
     #  387   }
     #  388 }

    def getStyleAsSld(layer, styleName):
        if layer.type() == layer.VectorLayer:
            document = QDomDocument()
            header = document.createProcessingInstruction("xml", "version=\"1.0\"")
            document.appendChild(header)

            root = document.createElementNS(
                "http://www.opengis.net/sld", "StyledLayerDescriptor")
            root.setAttribute("version", "1.0.0")
            root.setAttribute("xmlns:ogc", "http://www.opengis.net/ogc")
            root.setAttribute("xmlns:sld", "http://www.opengis.net/sld")
            # root.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink" )
            # root.setAttribute(
            #     "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" )
            document.appendChild(root)

            namedLayerNode = document.createElement("sld:NamedLayer")
            root.appendChild(namedLayerNode)

            nameNode = document.createElement("sld:Name")
            featureTypeStyleElem = document.createElement("sld:FeatureTypeStyle")
            namedLayerNode.appendChild(nameNode)
            nameNode.appendChild(document.createTextNode(styleName))
            userNode = document.createElement("sld:UserStyle")
            namedLayerNode.appendChild(userNode)

            nameElem = document.createElement("sld:Name")
            nameElem.appendChild(document.createTextNode(styleName))
            userNode.appendChild(nameElem)
            titleElem = document.createElement("sld:Title")
            titleElem.appendChild(document.createTextNode(styleName))
            userNode.appendChild(titleElem)

            rule = layer.renderer().rootRule()
            props = {}  # QgsStringMap() can be see as a python dictionary
            rule_to_sld(rule, document, featureTypeStyleElem, props)
            userNode.appendChild(featureTypeStyleElem)

            return str(document.toString(4))
        elif layer.type() == layer.RasterLayer:
            renderer = layer.renderer()
            if isinstance(renderer, QgsSingleBandGrayRenderer):
                symbolizerCode = "<Opacity>%d</Opacity>" % renderer.opacity()
                symbolizerCode += (
                    "<ChannelSelection><GrayChannel><SourceChannelName>"
                    + str(renderer.grayBand())
                    + "</SourceChannelName></GrayChannel></ChannelSelection>")
                sld = RASTER_SLD_TEMPLATE.replace(
                    "SYMBOLIZER_CODE", symbolizerCode).replace("STYLE_NAME",
                                                               layer.name())
                return sld
            elif isinstance(renderer, QgsSingleBandPseudoColorRenderer):
                symbolizerCode = "<ColorMap>"
                # band = renderer.usesBands()[0]
                items = \
                    renderer.shader().rasterShaderFunction().colorRampItemList()
                for item in items:
                    color = item.color
                    rgb = '#%02x%02x%02x' % (
                        color.red(), color.green(), color.blue())
                    symbolizerCode += ('<ColorMapEntry color="' + rgb
                                       + '" quantity="'
                                       + str(item.value) + '" />')
                symbolizerCode += "</ColorMap>"
                sld = RASTER_SLD_TEMPLATE.replace(
                    "SYMBOLIZER_CODE", symbolizerCode).replace("STYLE_NAME",
                                                               layer.name())
                return sld
            else:
                # we use some default styles in case we have an
                # unsupported renderer
                sldpath = os.path.join(
                    os.path.dirname(__file__), "..", "resources")
                if layer.bandCount() == 1:
                    sldfile = os.path.join(sldpath, "grayscale.sld")
                else:
                    sldfile = os.path.join(sldpath, "rgb.sld")
                with open(sldfile, 'r', newline='') as f:
                    sld = f.read()
                return sld
        else:
            return None

    def rule_to_sld(rule, document, element, props):
        if (hasattr(rule, 'symbols') and rule.symbols()  # working before QGIS 2.12
                or hasattr(rule, 'symbols2') and rule.symbols2()):  # working after
                                                                    # QGIS 2.12
            if rule.filterExpression():
                if filter in props:
                    props['filter'] += " AND "
                else:
                    props['filter'] = ""
                props['filter'] += rule.filterExpression()
            minimumScale = rule.minimumScale()
            if minimumScale != 0:
                ok = True
                try:
                    parentScaleMinDenom = int(props.get('minimumScale', '0'))
                except ValueError:
                    ok = False
                if not ok or parentScaleMinDenom <= 0:
                    props['minimumScale'] = str(minimumScale)
                else:
                    props['minimumScale'] = str(
                        max(parentScaleMinDenom, minimumScale))
            maximumScale = rule.maximumScale()
            if maximumScale != 0:
                ok = True
                try:
                    parentScaleMaxDenom = int(props.get('maximumScale', '0'))
                except ValueError:
                    ok = False
                if not ok or parentScaleMaxDenom <= 0:
                    props['maximumScale'] = str(maximumScale)
                else:
                    props['maximumScale'] = str(
                        min(parentScaleMaxDenom, maximumScale))

            if rule.symbol():
                ruleNode = document.createElement("sld:Rule")
                element.appendChild(ruleNode)
    #  337     //XXX: <se:Name> is the rule identifier, but our the Rule objects
    #  338     // have no properties could be used as identifier. Use the label.
                nameNode = document.createElement("sld:Name")
                nameNode.appendChild(document.createTextNode(rule.label()))
                ruleNode.appendChild(nameNode)
                if rule.label() or rule.description():
                    descrNode = document.createElement("sld:Description")
                    if rule.label():
                        titleNode = document.createElement("sld:Title")
                        titleNode.appendChild(
                            document.createTextNode(rule.label()))
                        # descrNode.appendChild(titleNode)
                        ruleNode.appendChild(titleNode)
                    if rule.description():
                        abstractNode = document.createElement("sld:Abstract")
                        abstractNode.appendChild(
                            document.createTextNode(rule.description()))
                        # descrNode.appendChild(abstractNode)
                        ruleNode.appendChild(abstractNode)
                    # ruleNode.appendChild(descrNode)
                if 'filter' in props:
                    createFunctionElement(
                        document, ruleNode, props.get('filter', ''))
                if props.get('minimumScale', ''):
                    sminNode = document.createElement("sld:MinScaleDenominator")
                    sminNode.appendChild(
                        document.createTextNode(props['minimumScale']))
                    descrNode.appendChild(sminNode)
                if props.get('maximumScale', ''):
                    smaxNode = document.createElement("sld:MaxScaleDenominator")
                    smaxNode.appendChild(
                        document.createTextNode(props['maximumScale']))
                    descrNode.appendChild(smaxNode)
                symbolv2_to_sld(rule.symbol(), document, ruleNode, props)
    #  379
    #  380     mSymbol->toSld( doc, ruleElem, props );
    #  381   }
    #  382
            # loop into childern rule list
            for child in rule.children():
                rule_to_sld(child, document, element, props)
    #  383   // loop into childern rule list
    #  384   for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
    #  385   {
    #  386     ( *it )->toSld( doc, element, props );
    #  387   }
    #  388 }

    def symbolv2_to_sld(symbol, document, element, props):
        props['alpha'] = str(symbol.opacity())
        scaleFactor = 1.0
        (scaleFactor, props['uom']) = encodeSldUom(symbol.outputUnit(),
                                                   scaleFactor)
        props['uomScale'] = str(scaleFactor) if scaleFactor != 1 else ''
        for symbolLayer in symbol.symbolLayers():
            symbolLayer_to_sld(symbolLayer, document, element, props)

    def encodeSldUom(outputUnit, scaleFactor):
        if outputUnit == QgsUnitTypes.RenderMapUnits:
            if scaleFactor:
                scaleFactor = 0.001  # from millimeters to meters
            return (scaleFactor, "http://www.opengeospatial.org/se/units/metre")
        else:
            # pixel is the SLD default uom. The "standardized rendering pixel
            # size" is defined to be 0.28mm x -1.28mm (millimeters).
            if scaleFactor:
                scaleFactor = 0.28  # from millimeters to pixels
            # http://www.opengeospatial.org/sld/units/pixel
            return (scaleFactor, 'http://www.opengeospatial.org/se/units/pixel')

    def symbolLayer_to_sld(symbolLayer, document, element, props):
        if (not hasattr(symbolLayer, 'brushStyle')
                or symbolLayer.brushStyle() == Qt.NoBrush
                and symbolLayer.strokeStyle() == Qt.NoPen):
            return
        symbolizerElem = document.createElement('sld:PolygonSymbolizer')
        if 'uom' in props:
            symbolizerElem.setAttribute('uom', props['uom'])
        element.appendChild(symbolizerElem)
        # geometry
        createGeometryElement(document, symbolizerElem, props.get('geom', ''))
        if symbolLayer.brushStyle() != Qt.NoBrush:
            # fill
            fillElem = document.createElement('sld:Fill')
            symbolizerElem.appendChild(fillElem)
            fill_to_sld(symbolLayer, document, fillElem,
                        symbolLayer.brushStyle(), symbolLayer.color())
        if symbolLayer.strokeStyle() != Qt.NoPen:
            # stroke
            strokeElem = document.createElement('sld:Stroke')
            symbolizerElem.appendChild(strokeElem)
            # TODO: Add dash style and other parameters
            line_to_sld(document, strokeElem, symbolLayer.dxfPenStyle(),
                        symbolLayer.strokeColor(), symbolLayer.strokeWidth(),
                        symbolLayer.penJoinStyle(),  # penCapStyle,
                        symbolLayer.dxfCustomDashPattern()[0],
                        symbolLayer.offset())
        # Displacement
        create_displacement_element(document, element, symbolLayer.offset())

    def create_displacement_element(document, element, offset):
        if not offset:
            return
        displacementElem = document.createElement('sld:Displacement')
        element.appendChild(displacementElem)
        dispXElem = document.createElement('sld:DisplacementX')
        dispXElem.appendChild(document.createTextNode(str(offset.x())))
        dispYElem = document.createElement('sld:DisplacementY')
        dispYElem.appendChild(document.createTextNode(str(offset.y())))
        displacementElem.appendChild(dispXElem)
        displacementElem.appendChild(dispYElem)

    def line_to_sld(document, element, penStyle, color, width,
                    penJoinStyle, customDashPattern, dashOffset):
        dashPattern = []
        if penStyle == Qt.CustomDashLine and not customDashPattern:
            element.appendChild(document.createComment(
                "WARNING: Custom dash pattern required but not provided."
                " Using default dash pattern."))
            penStyle = Qt.DashLine
        if penStyle == Qt.NoPen:
            return
        if penStyle != Qt.SolidLine:
            if penStyle == Qt.DashLine:
                dashPattern.append(4.0)
                dashPattern.append(2.0)
            elif penStyle == Qt.DotLine:
                dashPattern.append(1.0)
                dashPattern.append(2.0)
            elif penStyle == Qt.DashDotLine:
                dashPattern.append(4.0)
                dashPattern.append(2.0)
                dashPattern.append(1.0)
                dashPattern.append(2.0)
            elif penStyle == Qt.DashDotDotLine:
                dashPattern.append(4.0)
                dashPattern.append(2.0)
                dashPattern.append(1.0)
                dashPattern.append(2.0)
                dashPattern.append(1.0)
                dashPattern.append(2.0)
            else:
                element.appendChild(document.createComment(
                    'BrushStyle %s not supported yet' % penStyle))
        if color.isValid():
            element.appendChild(createCssParameterElement(
                document, "stroke", color.name()))
            if color.alpha() < 255:
                element.appendChild(createCssParameterElement(
                    document, "stroke-opacity",
                    QgsSymbolLayerUtils.encodeSldAlpha(color.alpha())))
        if width > 0:
            element.appendChild(createCssParameterElement(
                document, "stroke-width", str(width)))
        if penJoinStyle:
            element.appendChild(createCssParameterElement(
                document, "stroke-linejoin",
                QgsSymbolLayerUtils.encodeSldLineJoinStyle(penJoinStyle)))
        # TODO: manage penCapStyle
        if len(dashPattern) > 0:
            element.appendChild(createCssParameterElement(
                document, "stroke-dasharray",
                QgsSymbolLayerUtils.encodeSldRealVector(dashPattern)))  # " ".join(dashPattern)))
            # FIXME: what's dashOffset? qgsDoubleNear expects a singular number,
            # not a tuple
            # if qgsDoubleNear(dashOffset, 0.0):
            # if abs(dashOffset - 0.0) > 1e-8:
            #     element.appendChild(createCssParameterElement(
            #         document, "stroke-dashoffset", str(dashOffset)))

     # 1763 void QgsSymbolLayerUtils::lineToSld( QDomDocument &doc, QDomElement &element,
     # 1764                                        Qt::PenStyle penStyle, QColor color, double width,
     # 1765                                        const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle,
     # 1766                                        const QVector<qreal> *customDashPattern, double dashOffset )
     # 1767 {
     # 1768   QVector<qreal> dashPattern;
     # 1769   const QVector<qreal> *pattern = &dashPattern;
     # 1770
     # 1771   if ( penStyle == Qt::CustomDashLine && !customDashPattern )
     # 1772   {
     # 1773     element.appendChild( doc.createComment( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) );
     # 1774     penStyle = Qt::DashLine;
     # 1775   }
     # 1776
     # 1777   switch ( penStyle )
     # 1778   {
     # 1779     case Qt::NoPen:
     # 1780       return;
     # 1781
     # 1782     case Qt::SolidLine:
     # 1783       break;
     # 1784
     # 1785     case Qt::DashLine:
     # 1786       dashPattern.push_back( 4.0 );
     # 1787       dashPattern.push_back( 2.0 );
     # 1788       break;
     # 1789     case Qt::DotLine:
     # 1790       dashPattern.push_back( 1.0 );
     # 1791       dashPattern.push_back( 2.0 );
     # 1792       break;
     # 1793     case Qt::DashDotLine:
     # 1794       dashPattern.push_back( 4.0 );
     # 1795       dashPattern.push_back( 2.0 );
     # 1796       dashPattern.push_back( 1.0 );
     # 1797       dashPattern.push_back( 2.0 );
     # 1798       break;
     # 1799     case Qt::DashDotDotLine:
     # 1800       dashPattern.push_back( 4.0 );
     # 1801       dashPattern.push_back( 2.0 );
     # 1802       dashPattern.push_back( 1.0 );
     # 1803       dashPattern.push_back( 2.0 );
     # 1804       dashPattern.push_back( 1.0 );
     # 1805       dashPattern.push_back( 2.0 );
     # 1806       break;
     # 1807
     # 1808     case Qt::CustomDashLine:
     # 1809       Q_ASSERT( customDashPattern );
     # 1810       pattern = customDashPattern;
     # 1811       break;
     # 1812
     # 1813     default:
     # 1814       element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) );
     # 1815       return;
     # 1816   }
     # 1817
     # 1818   if ( color.isValid() )
     # 1819   {
     # 1820     element.appendChild( createSvgParameterElement( doc, "stroke", color.name() ) );
     # 1821     if ( color.alpha() < 255 )
     # 1822       element.appendChild( createSvgParameterElement( doc, "stroke-opacity", encodeSldAlpha( color.alpha() ) ) );
     # 1823   }
     # 1824   if ( width > 0 )
     # 1825     element.appendChild( createSvgParameterElement( doc, "stroke-width", QString::number( width ) ) );
     # 1826   if ( penJoinStyle )
     # 1827     element.appendChild( createSvgParameterElement( doc, "stroke-linejoin", encodeSldLineJoinStyle( *penJoinStyle ) ) );
     # 1828   if ( penCapStyle )
     # 1829     element.appendChild( createSvgParameterElement( doc, "stroke-linecap", encodeSldLineCapStyle( *penCapStyle ) ) );
     # 1830
     # 1831   if ( pattern->size() > 0 )
     # 1832   {
     # 1833     element.appendChild( createSvgParameterElement( doc, "stroke-dasharray", encodeSldRealVector( *pattern ) ) );
     # 1834     if ( !qgsDoubleNear( dashOffset, 0.0 ) )
     # 1835       element.appendChild( createSvgParameterElement( doc, "stroke-dashoffset", QString::number( dashOffset ) ) );
     # 1836   }
     # 1837 }

      # 448 QString QgsSymbolLayerUtils::encodeSldRealVector( const QVector<qreal>& v )
      # 449 {
      # 450   QString vectorString;
      # 451   QVector<qreal>::const_iterator it = v.constBegin();
      # 452   for ( ; it != v.constEnd(); ++it )
      # 453   {
      # 454     if ( it != v.constBegin() )
      # 455     {
      # 456       vectorString.append( " " );
      # 457     }
      # 458     vectorString.append( QString::number( *it ) );
      # 459   }
      # 460   return vectorString;
      # 461 }

    def createCssParameterElement(document, name, value):
        nodeElem = document.createElement('sld:CssParameter')
        nodeElem.setAttribute('name', name)
        nodeElem.appendChild(document.createTextNode(value))
        return nodeElem
     # 2515 QDomElement QgsSymbolLayerUtils::createSvgParameterElement( QDomDocument &doc, QString name, QString value )
     # 2516 {
     # 2517   QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
     # 2518   nodeElem.setAttribute( "name", name );
     # 2519   nodeElem.appendChild( doc.createTextNode( value ) );
     # 2520   return nodeElem;
     # 2521 }

    def fill_to_sld(symbolLayer, document, element, brushStyle, color):
        patternName = ''
        if brushStyle == Qt.NoBrush:
            return
        elif brushStyle == Qt.SolidPattern:
            if color.isValid():
                cssElement = createCssParameterElement(document,
                                                       'fill',
                                                       color.name())
                element.appendChild(cssElement)
                if color.alpha() < 255:
                    cssElement = createCssParameterElement(
                        document, 'fill-opacity',
                        QgsSymbolLayerUtils.encodeSldAlpha(color.alpha()))
                    element.appendChild()
            return
        elif brushStyle in (Qt.CrossPattern,
                            Qt.CrossPattern,
                            Qt.DiagCrossPattern,
                            Qt.HorPattern,
                            Qt.VerPattern,
                            Qt.BDiagPattern,
                            Qt.FDiagPattern,
                            Qt.Dense1Pattern,
                            Qt.Dense2Pattern,
                            Qt.Dense3Pattern,
                            Qt.Dense4Pattern,
                            Qt.Dense5Pattern,
                            Qt.Dense6Pattern,
                            Qt.Dense7Pattern,
                            ):
            patternName = QgsSymbolLayerUtils.encodeSldBrushStyle(brushStyle)
            return
        else:
            element.appendChild(document.createComment('Brush not supported'))
        graphicFillElem = document.createElement('sld:GraphicFill')
        element.appendChild(graphicFillElem)
        graphicElem = document.createElement('sld:Graphic')
        element.appendChild(graphicElem)
        fillColor = color if patternName.startswith('brush://') else Qt.QColor()
        strokeColor = \
            color if not patternName.startswith('brush://') else Qt.QColor()
        # Use WellKnownName tag to handle QT brush styles.
        QgsSymbolLayerUtils.wellKnownMarkerToSld(
            document, graphicElem, patternName, fillColor,
            strokeColor, Qt.SolidLine, -1, -1)

    def createGeometryElement(document, element, geomFunc):
        if not geomFunc:
            return
        geometryElem = document.createElement('Geometry')
        element.appendChild(geometryElem)
        createFunctionElement(document, geometryElem, geomFunc)
    # void QgsSymbolLayerUtils::createGeometryElement( QDomDocument &doc, QDomElement &element, QString geomFunc )
    #  2388 {
    #  2389   if ( geomFunc.isEmpty() )
    #  2390     return;
    #  2391
    #  2392   QDomElement geometryElem = doc.createElement( "Geometry" );
    #  2393   element.appendChild( geometryElem );
    #  2394
    #  2395   /* About using a function withing the Geometry tag.
    #  2396    *
    #  2397    * The SLD specification <= 1.1 is vague:
    #  2398    * "In principle, a fixed geometry could be defined using GML or
    #  2399    * operators could be defined for computing the geometry from
    #  2400    * references or literals. However, using a feature property directly
    #  2401    * is by far the most commonly useful method."
    #  2402    *
    #  2403    * Even if it seems that specs should take care all the possible cases,
    #  2404    * looking at the XML schema fragment that encodes the Geometry element,
    #  2405    * it has to be a PropertyName element:
    #  2406    *   <xsd:element name="Geometry">
    #  2407    *       <xsd:complexType>
    #  2408    *           <xsd:sequence>
    #  2409    *               <xsd:element ref="ogc:PropertyName"/>
    #  2410    *           </xsd:sequence>
    #  2411    *       </xsd:complexType>
    #  2412    *   </xsd:element>
    #  2413    *
    #  2414    * Anyway we will use a ogc:Function to handle geometry transformations
    #  2415    * like offset, centroid, ...
    #  2416    */
    #  2417
    #  2418   createFunctionElement( doc, geometryElem, geomFunc );
    #  2419 }

    def createFunctionElement(document, element, function):
        expr = QgsExpression(function)
        if expr.hasParserError():
            element.appendChild(document.createComment("Parser Error"))
            return False
        filterElem = QgsOgcUtils.expressionToOgcFilter(expr, document)
        if filterElem:
            element.appendChild(filterElem)  # NOTE: in qgis2 filterElem was tuple
        return True

    # bool QgsSymbolLayerUtils::createFunctionElement( QDomDocument &doc, QDomElement &element, QString function )
    #  2431 {
    #  2432   // let's use QgsExpression to generate the SLD for the function
    #  2433   QgsExpression expr( function );
    #  2434   if ( expr.hasParserError() )
    #  2435   {
    #  2436     element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
    #  2437     return false;
    #  2438   }
    #  2439   QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter( expr, doc );
    #  2440   if ( !filterElem.isNull() )
    #  2441     element.appendChild( filterElem );
    #  2442   return true;
    #  2443 }

    def getGeomTypeFromSld(sld):
        if "PointSymbolizer" in sld:
            return "Point"
        elif "LineSymbolizer" in sld:
            return "LineString"
        else:
            return "Polygon"