wiki:MapGuideRfc14

Version 59 (modified by waltweltonlair, 18 years ago) ( diff )

--

MapGuide RFC 14 - Cartographic Stylization Engine

This page contains an change request (RFC) for the MapGuide Open Source project. More MapGuide RFCs can be found on the RFCs page.

Status

RFC Template Version(1.0)
Submission DateFeb 2/2007
Last ModifiedWalt Welton-Lair Timestamp
AuthorMany
RFC StatusAdopted
Implementation Statuspending
Proposed Milestone1.2
Assigned PSC guide(s)Tom Fukushima
Voting HistoryFeb 27, 2007
+1Tom, Jason, Bruce, Andy, Paul, Bob, Haris
+0
-0
-1

Overview

This document describes a proposal for adding a cartographic styling engine to MapGuide. The main goal is to be able to handle point, line, and fill styles as sophisticated as AutoCAD as well as being able to represent all symbols described in

http://ngmdb.usgs.gov/fgdc_gds/geolsymstd/fgdc-geolsym-allnocharts.pdf

We are aware that this is a high standard we want to live up to and it is currently beyond our ability to fully test against this standard. At this point though we still believe that we can create all symbols with the proposed solution represented by these two major requirements. Note: the FGDC standard goes beyond line styles, fill styles, and point styles – we are ignoring anything else in that document.

Motivation

Most GIS applications are in need of very sophisticated symbolization. In many countries published maps must adhere to high standards that are often even written into law. This RFC introduces a high quality symbolization engine for MapGuide.

The new engine will also support using point symbols as labels. This addresses a long standing MapGuide request for being able to create maps that use highway symbolization as part of labeling.

Proposed Solution

The proposal is to create one engine that will satisfy the requirements of all three symbolizations (point, line, and area). This engine will use as input a new XML resource, parameters, and geometry, and will use the rendering interface of MapGuide to perform high quality stylization from those inputs. The resource format and the engine will ultimately be able to handle all three symbolization types.

To implement all of this we will create a new type-style element for the MapGuide LayerDefinition schema. This type-style can reference the new symbolization resources.

Symbols themselves will be represented as a new resource type, and will be stored in the MapGuide resource repository. Groups of symbols can then be easily stored in folders in the repository to represent symbol libraries. The proposed design includes the ability to define both simple and compound symbols. A simple symbol consists of a collection of path, image, and text elements that define the graphics, and information on how the symbol is used in the context of points, lines, and areas. A compound symbol contains a collection of simple symbols, either inlined or using references to existing symbols.

The details on how the symbolization works are best gathered from the XML schema. Please refer to appendix A for the schema.

Here are some examples to make it more clear how the symbol engine will work.

Example 1: interstate highway symbol label

This is a symbol containing an image (could also be paths) and a variable size text string.

<SimpleSymbolDefinition>
  <Name>US_Interstate</Name>
  <Graphics>
    <Image>
      <Reference>
        <ResourceId>'Library%3A%2F%2FHighwaySymbols%2FImageLibrary.SymbolDefinition'</ResourceId>
        <LibraryItemName>'InterstateImage'</LibraryItemName>
      </Reference>
      <SizeX>10.8</SizeX>
      <SizeY>11.2</SizeY>
      <PositionX>0</PositionX>
      <PositionY>0.8</PositionY>
    </Image>
    <Text>
      <String>%INTERSTATE_NUMBER%</String>
      <FontName>'Arial'</FontName>
      <Height>6</Height>
      <PositionX>0</PositionX>
      <PositionY>0</PositionY>
      <HorizontalAlignment>'Center'</HorizontalAlignment>
      <VerticalAlignment>'Halfline'</VerticalAlignment>
    </Text>
  </Graphics>
  <LineUsage>
    <AngleControl>'FromAngle'</AngleControl>
    <VertexControl>'OverlapNoWrap'</VertexControl>
    <Angle>0</Angle>
    <StartOffset>30</StartOffset>
    <EndOffset>30</EndOffset>
    <Repeat>100</Repeat>
  </LineUsage>
  <ParameterDefinition>
    <Parameter>
      <Identifier>%INTERSTATE_NUMBER%</Identifier>
      <DefaultValue>0</DefaultValue>
      <DisplayName>Interstate Number</DisplayName>
      <Description>The interstate number</Description>
    </Parameter>
  </ParameterDefinition>
</SimpleSymbolDefinition>

The symbol graphics are defined such that the center of the text label is the symbol origin. The image is therefore shifted upwards slightly to compensate for this. The text string is also parametrized. Its value is obtained from an expression in the symbol instance. In this case the expression would be set to the interstate number attribute. One limitation of this symbol definition is that it doesn't resize if the number of characters in the text string changes. This limitation is removed in example 6 below.

Example 2: line style with simple dashing

The repeating dash pattern is “long dash / dot / short dash / dot”. The long dash is 10 mm long and the short dash is 5 mm long. Each gap is 2.5 mm. The overall period of the dashing is 25 mm. The symbol definition XML would be:

<SimpleSymbolDefinition>
  <Name>LongDash-Dot-ShortDash-Dot</Name>
  <Graphics>
    <Path>
      <Geometry>
        M  0.0,0 L 10.0,0
        M 12.5,0 L 12.5,0
        M 15.0,0 L 20.0,0
        M 22.5,0 L 22.5,0
      </Geometry>
      <LineColor>ff00000</LineColor>
    </Path>
  </Graphics>
  <LineUsage>
    <VertexControl>'OverlapWrap'</VertexControl>
    <Repeat>25</Repeat>
  </LineUsage>
  <ParameterDefinition/>
</SimpleSymbolDefinition>

In the path geometry all the formatting elements (line color, line cap, line join, …) are left out – they take on their default values. For the LineUsage the default vertex control is OverlapNoWrap, and here we set it to OverlapWrap. OverlapWrap means it’s ok to draw the symbol if it overlaps a vertex, but make it bend around the vertex. For simple line styles consisting of centerline dashing, OverlapWrap is the standard behavior we’re used to.

Example 3: line style with a decoration and thickness

The repeating pattern is just a cross. It repeats with period 5 mm.

<SimpleSymbolDefinition>
  <Name>ThickRail</Name>
  <Description>This is a railroad symbol</Description>
  <Graphics>
    <Path>
      <Geometry>
        M 0.0, 0.0 L 5.0,0.0
        M 2.5,-2.5 L 2.5,2.5
      </Geometry>
      <LineWeight>%LINE_WEIGHT%</LineWeight>
      <LineCap>'Round'</LineCap>
      <LineJoin>'Round'</LineJoin>
    </Path>
  </Graphics>
  <LineUsage>
    <VertexControl>'OverlapWrap'</VertexControl>
    <Repeat>5</Repeat>
  </LineUsage>
  <ParameterDefinition>
    <Parameter>
      <Identifier>%LINE_WEIGHT%</Identifier>
      <DefaultValue>1</DefaultValue>
      <DisplayName>Line Weight</DisplayName>
    </Parameter>
  </ParameterDefinition>
</SimpleSymbolDefinition>

This time the line weight for the path has been parametrized. Its default value is 1 mm, as specified in the ParameterDefinition element, and this can be overridden at the symbol instance level. We’ve also changed the path’s line cap and line join styles to round.

Note that when the line weight is modified the spacing between the vertical ticks does *not* change. The path geometry is fixed. If you want the symbol to get bigger / smaller to account for the line weight then this would be done using the symbol instance scales. For example, setting the scales to “1 + %LINE_WEIGHT% / 5” would ensure that there is always 5 mm of empty horizontal space between adjacent vertical ticks.

Example 4: line style with start / end symbols

This line style includes text along the centerline, and start / end symbols. In order to store all three components of the line style together we need to make this a compound symbol definition.

<CompoundSymbolDefinition>
  <Name>GasWithStartEndSymbols</Name>
  <SimpleSymbol>
    <SimpleSymbolDefinition>
      <Name>GasStyle</Name>
      <Graphics>
        <Path>
          <Geometry>M 0,0 H 5 M 15,0 H 5</Geometry>
          <LineWeight>1</LineWeight>
          <LineCap>'Round'</LineCap>
          <LineJoin>'Round'</LineJoin>
        </Path>
        <Text>
          <String>'GAS'</String>
          <FontName>'Arial'</FontName>
          <Height>3.5</Height>
          <PositionX>10</PositionX>
          <PositionY>0</PositionY>
          <HorizontalAlignment>'Center'</HorizontalAlignment>
          <VerticalAlignment>'Halfline'</VerticalAlignment>
        </Text>
      </Graphics>
      <LineUsage>
        <VertexControl>'OverlapWrap'</VertexControl>
        <Repeat>20</Repeat>
      </LineUsage>
      <ParameterDefinition/>
    </SimpleSymbolDefinition>
  </SimpleSymbol>
  <SimpleSymbol>
    <SimpleSymbolDefinition>
      <Name>BlueSquare</Name>
      <Graphics>
        <Path>
          <Geometry>M -0.5,-0.5 H 1, V 1, H -1, Z</Geometry>
          <FillColor>c00000ff</FillColor>
        </Path>
      </Graphics>
      <LineUsage>
        <VertexControl>'OverlapNoWrap'</VertexControl>
        <StartOffset>0</StartOffset>
        <Repeat>0</Repeat>
      </LineUsage>
      <ParameterDefinition/>
    </SimpleSymbolDefinition>
  </SimpleSymbol>
  <SimpleSymbol>
    <SimpleSymbolDefinition>
      <Name>RedTriangle</Name>
      <Graphics>
        <Path>
          <Geometry>M -0.5,-0.5 H 1, L 0,1, Z</Geometry>
          <FillColor>c0ff0000</FillColor>
        </Path>
      </Graphics>
      <LineUsage>
        <VertexControl>'OverlapNoWrap'</VertexControl>
        <EndOffset>0</EndOffset>
        <Repeat>0</Repeat>
      </LineUsage>
      <ParameterDefinition/>
    </SimpleSymbolDefinition>
  </SimpleSymbol>
</CompoundSymbolDefinition>

In this case the order of the symbols in the compound determines the draw order. Here, the start and end symbols are drawn on top of the dashing. For the start symbol the StartOffset and Repeat are set to 0. This ensures the symbol is only drawn once at the start of the feature. Likewise, for the end symbol we set the EndOffset and Repeat to 0. We also want OverlapNoWrap for the VertexControl. The symbol can overlap a vertex, but we simply want to “stamp” it – no bending around vertices.

One other note: the default AngleControl behavior is FromGeometry. This means the angle of the start / end symbol will be computed from the geometry, as shown here.

If you wanted the start / end symbol angles to be a fixed value you would change AngleControl to FromAngle and specify the Angle, e.g.

    <SimpleSymbolDefinition>
      <Name>BlueSquare</Name>
      ...
      <LineUsage>
        <AngleControl>'FromAngle'</AngleControl>
        <VertexControl>'OverlapNoWrap'</VertexControl>
        <Angle>0</Angle>
        <StartOffset>0</Repeat>
        <Repeat>0</Repeat>
      </LineUsage>
      ...
    </SimpleSymbolDefinition>

Example 5: line style with a special cross-tick offset requirement

This line style is one that the current MapGuide stylization code can’t handle. The problem is that the cross tick decoration needs to have a different start offset than the other decorations. Our new stylization schema handles this easily though. We have to create a compound symbol definition since we have two symbol components with different distributions.

<CompoundSymbolDefinition>
  <Name>MTYP1500a</Name>
  <SimpleSymbol>
    <SimpleSymbolDefinition>
      <Name>UpperLowerDashing</Name>
      <Graphics>
        <Path>
          <Geometry>
            M 0,-2.5 h 8 m 2,0 h 8 m 2,0 h 2
            M 0,2.5 h 8 m 2,0 h 8 m 2,0 h 2
          </Geometry>
        </Path>
      </Graphics>
      <LineUsage>
        <VertexControl>'OverlapWrap'</VertexControl>
        <Repeat>24</Repeat>
      </LineUsage>
      <ParameterDefinition/>
    </SimpleSymbolDefinition>
  </SimpleSymbol>
  <SimpleSymbol>
    <SimpleSymbolDefinition>
      <Name>OffsetCrossTick</Name>
      <Graphics>
        <Path>
          <Geometry>M 0.0,-2.5 v 5</Geometry>
        </Path>
      </Graphics>
      <LineUsage>
        <VertexControl>'OverlapWrap'</VertexControl>
        <StartOffset>28</StartOffset>
        <Repeat>24</Repeat>
      </LineUsage>
      <ParameterDefinition/>
    </SimpleSymbolDefinition>
  </SimpleSymbol>
</CompoundSymbolDefinition>

The upper / lower dashing has zero start offset, whereas the cross tick has a 28 mm offset. This horizontally positions the first cross tick at the center of the first long dash in the second cycle of upper / lower dashing.

Example 6: auto-resizing interstate highway symbol

The highway in example 1 was designed to display 1 or 2 digit interstate numbers. In the case of a 3 digit number you would end up with something like:

To fix this we can update the symbol to auto-resize based on the text string:

<SimpleSymbolDefinition>
  <Name>US_Interstate</Name>
  <Graphics>
    <Image>
      <ResizeControl>'AdjustToResizeBox'</ResizeControl>
      <Reference>
        <ResourceId>'Library%3A%2F%2FHighwaySymbols%2FImageLibrary.SymbolDefinition'</ResourceId>
        <LibraryItemName>'InterstateImage'</LibraryItemName>
      </Reference>
      <SizeX>10.8</SizeX>
      <SizeY>11.2</SizeY>
      <PositionX>0</PositionX>
      <PositionY>0.8</PositionY>
    </Image>
    <Text>
      <ResizeControl>'AddToResizeBox'</ResizeControl>
      <String>%INTERSTATE_NUMBER%</String>
      <FontName>'Arial'</FontName>
      <Height>6</Height>
      <PositionX>0</PositionX>
      <PositionY>0</PositionY>
      <HorizontalAlignment>'Center'</HorizontalAlignment>
      <VerticalAlignment>'Halfline'</VerticalAlignment>
    </Text>
  </Graphics>
  <ResizeBox>
    <PositionX>0</PositionX>
    <PositionY>0</PositionY>
    <SizeX>6</SizeX>
    <SizeY>5</SizeY>
    <GrowControl>'GrowInX'</GrowControl>
  </ResizeBox>
  <LineUsage>
    <AngleControl>'FromAngle'</AngleControl>
    <VertexControl>'OverlapNoWrap'</VertexControl>
    <Angle>0</Angle>
    <StartOffset>30</StartOffset>
    <EndOffset>30</EndOffset>
    <Repeat>100</Repeat>
  </LineUsage>
  <ParameterDefinition>
    <Parameter>
      <Identifier>%INTERSTATE_NUMBER%</Identifier>
      <DefaultValue>0</DefaultValue>
      <DisplayName>Interstate Number</DisplayName>
      <Description>The interstate number</Description>
    </Parameter>
  </ParameterDefinition>
</SimpleSymbolDefinition>

All we've done is include a ResizeBox and specify the ResizeControl for each graphic element. The ResizeBox is positioned at the same location as the Text element, and has an initial size of 6 mm by 5 mm. This box fits nicely in the Image element - as long as the text does not extend outside of this box then no resizing is needed. The GrowControl setting for the resize box is GrowInX, which means the box is only allowed to grow horizontally.

The Text element's ResizeControl is set to AddToResizeBox. This means the graphical extent of the element is added to the resize box, and the box will grow, if necessary, to include that extent. In the case of 1 or 2 digit interstate numbers no growth occurs, but in the case of a 3 digit number the box will grow horizontally to fit the text.

The Image element's ResizeControl is set to AdjustToResizeBox. This means the element will be resized and repositioned if the resize box grows in size. For example, if the resize box width grows by 10% then the element will be scaled horizontally by a factor of 1.1. The scaling is done about the center of the resize box.

Here's what the updated symbol looks like when displaying a 3 digit interstate number.

Theming and Localization of Symbols

Most properties in the SymbolDefinition can be parametrized. This means that an expression can be provided that references attributes in the layer feature source for which the SymbolDefinition is used. An example of a parametrized value would be:

<LineWeight>%1%</LineWeight>

This means that the parameter named 1 can be set to an expression in the LayerDefinition, based on style choices made by the map author. The SymbolDefinition must also declare this parameter in its ParameterDefinition element. Here it describes what that parameter does and also provides a default value for the parameter. Here is a sample SymbolDefinition with this ParameterDefinition element:

<SimpleSymbolDefinition>
  <Name>ThickRail</Name>
  <Description>This is a railroad symbol</Description>
  <Graphics>
    <Path>
      <Geometry>
        M 0.0, 0.0 L 5.0,0.0
        M 2.5,-2.5 L 2.5,2.5
      </Geometry>
      <LineWeight>%LINE_WEIGHT%</LineWeight>
      <LineCap>'Round'</LineCap>
      <LineJoin>'Round'</LineJoin>
    </Path>
  </Graphics>
  <LineUsage>
    <VertexControl>'OverlapWrap'</VertexControl>
    <Repeat>5</Repeat>
  </LineUsage>
  <ParameterDefinition>
    <Parameter>
      <Identifier>%LINE_WEIGHT%</Identifier>
      <DefaultValue>1</DefaultValue>
      <DisplayName>Line Weight</DisplayName>
      <Description>The line weight for the symbol</Description>
      <ValueList>
        <Value>1</Value>
        <Value>2</Value>
        <Value>3</Value>
        <Value>4</Value>
      </ValueList>
    </Parameter>
  </ParameterDefinition>
</SimpleSymbolDefinition>

For translation of this symbol into other languages, our solution is to create a translated parameter section and attach that to the SymbolDefinition resource in the MapGuide repository as Resource Data. The name of the attached resource will contain the locale of the translation. For example, if the original symbol is called Symbol.xml, the German translated section would be called Symbol.xml.de. Here is what it would look like:

<SimpleSymbolDefinition>
  <Name>DickeEisenBahnShiene</Name>
  <Description>Das ist ein Eisenbahnschienen Symbol</Description>
  <ParameterDefinition>
    <Parameter>
      <Identifier>%LINE_WEIGHT%</Identifier>
      <DefaultValue>1</DefaultValue>
      <DisplayName>Linien Dicke<DisplayName>
      <Description>Das ist nur ein Beispiel.</Description>
      <ValueList>
        <Value>1</Value>
        <Value>2</Value>
        <Value>3</Value>
        <Value>4</Value>
      </ValueList>
    </Parameter>
  </ParameterDefinition>
</SimpleSymbolDefinition>

Implications

The new symbol definitions will have an impact on the LayerDefinition XML schema. To preserve backwards compatibility, we propose to add a new CompositeTypeStyle element that will be the only LDF element that references new symbol definitions. The CompositeTypeStyle inherits FeatureTypeStyle alongside Area, Line and PointTypeStyle. Layers that have a CompositeTypeStyle will use a new code path for stylization based on the new SymbolDefinitions. Stylization of layers that have one of the old FeatureTypeStyles will not change. One will therefore be able to mix the two kinds of layers within the same map.

Just like the other FeatureTypeStyles, the CompositeTypeStyle will hold on to a symbolization object (CompositeSymbolization) which defines the way features draw. The CompositeSymbolization will reference one or more SymbolDefinitions and also optionally define theming expressions for properties of the SymbolDefinition which are marked as parametrized. Those allow for the symbol definition to take up styles based on attributes of the specific feature class that the CompositeSymbolization is associated with.

See Appendix A for a link to the schema for the proposed LayerDefinition additions.

Test Plan

Unit tests will be written to verify the new cartographic styling engine. In addition we will also create a sample symbol library to use for testing.

Funding/Resources

Autodesk will provide resources to implement the core symbolization engine and detailed implementation for point symbols and label symbols. If time allows we will also implement some portion of the line symbolization. In the future we plan to finish the symbolization engine to also include area symbolization. Later stages may include some resourcing help from DM Solutions. Exact timing for the line and area symbolizations has yet to be determined, but we would like to complete it for the 1.3 release in the fall.

Addendum, March 21, 2007

The following schema changes have been approved and made:

  • removed invalid enumerations from SymbolInstance::CheckExclusionRegion element
    • it should be a plain xs:string
  • changed SymbolInstance::SizeContext to be a SizeContextType
    • this is for consistency
  • made LineUsage::VertexAngleLimit an xs:string type
    • the type was missing in the schema
  • made SymbolInstance::DrawLast an xs:string type
    • the type was missing in the schema
  • changed all enumerated type properties to plain strings so they support expressions
    • PointUsage::AngleControl
    • LineUsage::AngleControl
    • LineUsage::UnitsControl
    • LineUsage::VertexControl
    • AreaUsage::OriginControl
    • AreaUsage::AngleControl
    • AreaUsage::ClippingControl
    • Path::LineCap
    • Path::LineJoin
    • GraphicBaseType::ResizeControl
    • ResizeBox::GrowControl
  • added a VertexJoin property on LineUsage
    • all graphic elements in the simple symbol use this value to determine their wrapping behavior at vertices
  • renamed Parameter::ValueList to AllowedValues
  • renamed SimpleSymbol::SimpleSymbolReference to SymbolReference
    • it's already clear enough that this is a SimpleSymbolReference because this is part of SimpleSymbol
  • switched order of elements in AreaUsage
    • switched AngleControl and OriginControl
    • put Angle before OriginX and OriginY
    • makes AreaUsage more consistent with LineUsage and PointUsage
  • changed Graphics collection to allow 0 to unbounded elements
    • currently it requires at least one element
    • users want to save their symbols while editing them
    • they may temporarily not contain any graphic elements
  • same change for the CompoundSymbolDefinition::SimpleSymbol collection
    • allow 0 to unbounded symbols
  • same change for the CompositeSymbolization::SymbolInstance collection
    • allow 0 to unbounded symbol instances
  • reworked SymbolDefinition element
    • created separate SimpleSymbolDefinition and CompoundSymbolDefinition elements which are of the corresponding types
    • updated SymbolInstance to have either a SymbolReference, SimpleSymbolDefinition, or CompoundSymbolDefinition
    • deleted SymbolDefinition and SymbolDefinitionType
  • moved CompositeSymbolization::ParameterOverrides into SymbolInstance
    • this lets someone use the same SymbolDefinition multiple times in a symbolization, but specify different overrides for each instance
  • documented the default value for all optional elements
  • for properties supporting expressions that must evaluate to certain string values, documented what those values are

Addendum, March 23, 2007

The following additional schema changes have been made:

  • added a HeightScalable property on Text
    • if set to false then any scale defined on the symbol instance is not applied to the font height
  • added a SizeScalable property on Image
    • if set to false then any scale defined on the symbol instance is not applied to the image size

Addendum, April 1, 2007

Updated schemas per ticket #21.

Addendum, April 3, 2007

Updated SymbolDefinition schema per ticket #27.

Appendix A

Link to the symbolization schema (xsd)

Link to the updated MapGuide LayerDefinition schema:

Link to autogenerated html documentation for the schemas (including only the relevant subset for LayerDefinition):

Attachments (11)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.