MS RFC 124:改进MapServer中的SLD支持

最后更新

2020-04-05

作者

Jérome Boué

联系

jbo-ads@laposte.net

状态

草稿

版本

(针对MapServer 8.0)

概述

目前在MapServer中实现SLD/SE有很多缺点。本RFC建议解决其中一些问题,以获得更好的支持。到目前为止,已查明下列问题:

  1. 算术表达式 :SLD的MapServer实现不处理<SvgParameter>中的<Add>、<Sub>、<Mul>、<Div>操作或<Literal>或<PropertyName>元素,也不处理希望保存算术表达式的其他元素。

  2. WMS GetStyles请求 :WMS GetStyles请求仅基于映射文件配置返回SLD文件,即使请求中存在SLD或SLD_BODY参数。

  3. 样式分层 :当多个样式规则应用于一个要素时,只考虑第一个规则,而不是通过遵循 画家模型 如符号编码规范所述。

  4. 线符号中的标记或外部图形 :SLD的MapServer实现不处理<linesymboler>元素中的<Mark>或<ExternalGraphic>元素。

  5. 规则中的线性符号和多进制符号化器 :SLD的MapServer实现不能在同一个<Rule>元素中同时处理<linesymboler>和<PolygonSymbolizer>(这比在同一个<PolygonSymbolizer>元素中同时处理<Fill>和<Stroke>更具表现力)。

本RFC的下一节将描述每个问题和建议的解决方案。

建议的解决方案

一。文字常量、变量和算术表达式

目前,MapServer只支持<CssParameter>或<SvgParameter>标记中的原始常量,例如:

<SvgParameter name="stroke">#0000ff</SvgParameter>
<SvgParameter name="stroke-width">2.0</SvgParameter>

目标是支持<Literal>和<PropertyName>以及算术表达式,例如:

<!-- Literal, PropertyName -->
<SvgParameter name="stroke"> <Literal>#0000ff</Literal> </SvgParameter>
<SvgParameter name="stroke-width"> <PropertyName>LINE_WIDTH</PropertyName> </SvgParameter>

<!-- Arithmetic expression -->
<SvgParameter name="stroke-width">
  <Mul>
    <PropertyName>CATEGORY</PropertyName>
    <Literal>3</Literal>
  </Mul>
</SvgParameter>

此RFC建议将此语法扩展到以下标记:

父标记层次结构

目标标记

<PolygonSymbolizer><Fill>

<SvgParameter name=“fill”>
<SvgParameter name=“fill opacity”>
<PolygonSymbolizer>Stroke>
<linesymboler><Stroke>

<SvgParameter name=“stroke”>
<SvgParameter name=“笔划宽度”>
<SvgParameter name=“笔划不透明度”>
<pointsymboler><Graphic>


<大小>
<不透明度>
<旋转>
<pointsymboler><Graphic><Displacement>

<位移x>
<位移>
<pointsymboler><Graphic><Mark><Fill>
<SvgParameter name=“fill”>
<pointsymboler><Graphic><Mark><Stroke>

<SvgParameter name=“stroke”>
<SvgParameter name=“笔划宽度”>
<textSymboler>
<旋转>
<textSymboler><Font>符号
<SvgParameter name=“font size”>
<textSymboler><Fill>
<SvgParameter name=“fill”>
<textSymboler><Halo><Fill>
<SvgParameter name=“fill”>

向样式和标注属性添加算术表达式支持涉及到创建名为 exprBindings[] ,in styleObjlabelObj 数据结构。此字段数组存储从SLD语法转换为MapFile语法的表达式字符串。它的处理方式与现有的 bindings[] 字段数组,用于存储属性名称。

此外,还创建了一个新函数来解析SLD算术表达式并执行语法转换: msSLDParseOgcExpression()

副产品的一个效果是,在MapFile样式中添加对算术表达式的支持将是毫不费力的。

2。WMS公司 GetStyles 请求

WMS GetStyles 请求提供基于映射文件内容的SLD文档。当前此请求不考虑通过的SLD文档 SLDSLD_BODY URL参数。此外,这种转换缺少输入映射文件中的许多基本样式特性。

该RFC提出了集成输入SLD文档和增强从映射文件的转换。生成的SLD文档 GetStyles 请求将是请求中指定的MapFile和SLD的组合,尽可能接近应用的实际样式。

增强涉及以下项目:

描述

MapFile

SLD(突出显示的行显示增强功能)

SYMBOL (<pointsymboler>):

同时生成颜色和轮廓颜色,包括不透明度(可以在输入SLD中指定)。

ANGLEOFFSET 和全球 OPACITY 生成。

LAYER
  TYPE POINT
  STYLE
    COLOR 0 0 255
    OUTLINECOLOR 255 0 0
    OPACITY 50
    SYMBOL "star"
    SIZE 20
    ANGLE 180
    OFFSET 2 2
  END
END
<se:PointSymbolizer>
  <se:Graphic>
    <se:Mark>
      <se:WellKnownName>star</se:WellKnownName>
      <se:Stroke>
        <se:SvgParameter name="stroke">#ff0000</se:SvgParameter>
        <se:SvgParameter name="stroke-opacity">1</se:SvgParameter>
        <se:SvgParameter name="stroke-width">1</se:SvgParameter>
      </se:Stroke>
      <se:Fill>
        <se:SvgParameter name="fill">#0000ff</se:SvgParameter>
        <se:SvgParameter name="fill-opacity">0.10</se:SvgParameter>
      </se:Fill>
    </se:Mark>
    <se:Size>20</se:Size>
    <se:Rotation>180</se:Rotation>
    <se:Displacement>
      <se:DisplacementX>2</se:DisplacementX>
      <se:DisplacementY>2</se:DisplacementY>
    </se:Displacement>
    <se:Opacity>0.5</se:Opacity>
  </se:Graphic>
</se:PointSymbolizer>

POLYGON (<PolygonSymbolizer>):

将生成轮廓不透明度。

LAYER
  TYPE POLYGON
  CLASS
    STYLE
     WIDTH 1
      OPACITY 50
      COLOR 0 255 255
      OUTLINECOLOR 255 0 255
    END
  END
END
<se:PolygonSymbolizer>
  <se:Fill>
    <se:SvgParameter name="fill">#00ffff</se:SvgParameter>
    <se:SvgParameter name="fill-opacity">0.50</se:SvgParameter>
  </se:Fill>
  <se:Stroke>
    <se:SvgParameter name="stroke">#ff00ff</se:SvgParameter>
    <se:SvgParameter name="stroke-width">1.00</se:SvgParameter>
    <se:SvgParameter name="stroke-opacity">0.50</se:SvgParameter>
  </se:Stroke>
</se:PolygonSymbolizer>

LABEL (<textSymboler>):

轮廓颜色,也就是光晕,是生成的。

LAYER
  CLASS
    LABEL
      TEXT "[name]"
      COLOR 255 255 255
      OUTLINECOLOR 0 255 0
      OUTLINEWIDTH 2
      TYPE TRUETYPE
      FONT vera-bold
      SIZE 12
      OFFSET 2 20
      ANGLE 15
    END
  END
END
<se:TextSymbolizer>
  <se:Label>
    <se:PropertyName>name</se:PropertyName>
  </se:Label>
  <se:Font>
    <se:SvgParameter name="font-family">vera</se:SvgParameter>
    <se:SvgParameter name="font-weight">bold</se:SvgParameter>
    <se:SvgParameter name="font-size">12</se:SvgParameter>
  </se:Font>
  <se:Fill>
    <se:SvgParameter name="fill">#ffffff</se:SvgParameter>
  </se:Fill>
  <se:LabelPlacement>
    <se:PointPlacement>
      <se:Displacement>
        <se:DisplacementX>2</se:DisplacementX>
        <se:DisplacementY>20</se:DisplacementY>
      </se:Displacement>
    </se:PointPlacement>
    <se:AnchorPoint>
      <se:AnchorPointX>0.5</se:AnchorPointX>
      <se:AnchorPointY>0.5</se:AnchorPointY>
    </se:AnchorPoint>
    <se:Rotation>15</se:Rotation>
  </se:LabelPlacement>
  <se:Halo>
    <se:Radius>2</se:Radius>
    <se:Fill>
      <se:SvgParameter name="fill">#00ff00</se:SvgParameter>
    </se:Fill>
  </se:Halo>
</se:TextSymbolizer>

三。样式分层

MapServer设计为仅使用图层内的第一个适用类来渲染要素(请参见 CLASS description in LAYER page )。

另一方面,SLD/SE定义了一个名为“ 画家模型 其中,所有适用的规则(类的SLD等效项)都用于呈现要素(请参见 OpenGIS Symbology Encoding Implementation Specification ,p.7)。

下面是两个渲染的比较示例。同样的样式表用MapFile和SLD编写,并应用于法国地区的草图。样式表规范是:

  • 类1:多边形填充绿色

  • 第2类:3到5条边的多边形用红色填充

  • 类3:边正好为4的多边形填充蓝色

MapFile与SLD样式呈现策略的示例

Mapfile

SLD

只有第一个适用的类用于呈现

所有适用的类都用于呈现,每个类都在前面的类的基础上

../../_images/stylefirst.png ../../_images/stylepainter.png

为了在各自的上下文中处理这两种呈现模式,一个名为 rendermode 添加到 layerObj 数据结构。这是用在 msDrawVectorLayer() 中的函数 mapdraw.c 文件。此字段的两个可能值为:

MS_FIRST_CLASS

默认渲染模式,在 Mapfile 中定义的层上设置。在此模式下, msDrawVectorLayer() 获取每个形状的第一个适用类,并仅使用该类进行渲染。

MS_PAINTERS_MODEL

SLD渲染模式,在SLD文档中定义的层上设置。在此模式下, msDrawVectorLayer() 循环访问每个形状的所有适用类,并逐个使用它们进行呈现。此外,由于 msDrawShape() 更改形状坐标值,并且由于可能会多次绘制形状,因此必须在调用 msDrawShape() 并在此之后进行修复。

对于算术表达式,在映射文件中提供渲染模式的选择,例如,通过添加 RENDERMODE 参数在 LAYER 部分,从技术上来说并不难。

四。线符号中的标记或外部图形

当前在MapServer中实现SLD/SE时,无法在行(<linesymboler>标记)上呈现符号(<Mark>或<ExternalGraphic>标记)。作为一个例子,让我们画一条蓝色的线来表示多瑙河,上面有改变的圆圈(由<Mark>标记实现)和船(由<ExternalGraphic>标记实现)。下面,第一张图片由映射文件生成,第二张图片由SLD文档生成。

MapFile

(正确渲染)

../../_images/danube-mapfile.png

SLD

(渲染不正确)

../../_images/danube-sld-bug.png

源代码检查显示:

  • 必须根据授权模式验证<ExternalGraphic>参数(请参见 mapogcsld.c L2140-L2145 )。这涉及到设置 "sld_external_graphic" 中的属性 VALIDATION 映射文件的块 WEB 部分,例如:

    WEB
      VALIDATION
        "sld_external_graphic" "^.*/sld/data/.*"
      END
      [...]
    END
    
  • 在没有指定的情况下,<Mark>的当前实现在呈现符号时失败:始终将<Stroke>颜色分配给 OUTLINECOLOR 变量(请参见 mapogcsld.c L1697-L1703 )。当<Fill>标记不存在时,应将其分配给 COLOR 变量。

建议的解决方案包含一个文档更新(在 SLD 文档)第一个问题和第二个问题的错误修复。

5个。规则中的线性符号和多进制符号化器

当前在MapServer中实现SLD/SE并不期望在同一个<Rule>中混合<linesymboler>和<PolygonSymbolizer>,因此在这种情况下会产生不正确的呈现。作为一个例子,让我们通过使用两个SLD变体绘制一个蓝色多边形和一个黄色轮廓。

  • 第一个是一个单独的<PolygonSymbolizer>包含轮廓的<Stroke>部分和内部区域的<Fill>部分。

  • 第二个是一个<linesymboler>包含轮廓的<Stroke>部分,然后是一个<PolygonSymbolizer>包含内部区域的<Fill>部分。

规则中线性符号和多进制符号化器错误表示的说明

<Stroke>和<Fill>内部<PolygonSymbolizer>

<linesymboler>和<PolygonSymbolizer>内部<Rule>

<FeatureTypeStyle>
  <Rule>
    <PolygonSymbolizer>

      <Stroke>
        <SvgParameter name="stroke">
          <Literal>#FFFF00</Literal>
        </SvgParameter>
      </Stroke>



      <Fill>
        <SvgParameter name="fill">
          <Literal>#0000FF</Literal>
        </SvgParameter>
      </Fill>

    </PolygonSymbolizer>
  </Rule>
</FeatureTypeStyle>
<FeatureTypeStyle>
  <Rule>

    <LineSymbolizer>
      <Stroke>
        <SvgParameter name="stroke">
          <Literal>#FFFF00</Literal>
        </SvgParameter>
      </Stroke>
    </LineSymbolizer>

    <PolygonSymbolizer>
      <Fill>
        <SvgParameter name="fill">
          <Literal>#0000FF</Literal>
        </SvgParameter>
      </Fill>
    </PolygonSymbolizer>

  </Rule>
</FeatureTypeStyle>
../../_images/stylesinsymb.png

正确渲染

../../_images/symbinrule.png

渲染不正确

A source code inspection 显示考虑了所有SLD/SE标记。建议的解决方案包含一个简单的错误修复,其目的是 mapObj 两种情况的数据结构都是相同的。

在当前的实现中 STYLE 为每个<Stroke>或<Fill>标记生成。但在第一种情况下,<Fill>标记在<Stroke>标记之前处理,而在第二种情况下,<linesymboler>标记(仅包含<Stroke>标记)在<PolygonSymbolizer>标记之前处理,能够同时包含<Stroke>和<Fill>标记。

此外,将<LineSymbolizer><Stroke>颜色分配给 COLOR 变量,而将<PolygonSymbolizer><Stroke>轮廓颜色分配给 OUTLINECOLOR 变量。

提出的解决方案一方面是在标记之前处理<PolygonSymbolizer>标记,另一方面是移动 COLOR 价值到 OUTLINECOLOR 对于由<linesymboler>定义但适用于多边形层的样式。

测试

这个 msautotest/sld 将创建套件,并使用集中于这些新功能或改进功能的测试用例进行填充。

此外, msautotest/{{gdal,misc,query,renderers,wxs}} 测试套件将在开发期间运行,以确保不会发生回归。

文档

MapServer SLD 文档页面将根据本RFC中描述的建议功能进行更新。

向后兼容性问题

不希望出现兼容性问题。目标是在不改变任何其他现有行为的情况下提供更好的SLD支持。

受影响的文件

  • mapcopy.c

  • mapdraw.c

  • mapfile.c

  • maplayer.c

  • mapogcsld.c

  • mapogcsld.h

  • mapserver.h

  • maputil.c

  • mapwms.c

信用

多亏了法国国防部的资助。

投票历史

由PSC成员SethG、MikeS、JukkaR、TomK、jefm、evener、SteveL、DanielM、JeromeB的+1收养。