Follow the arrows

Here’s a neat trick for those working with road maps that want to indicate traffic direction by way of appropriately pointed arrows. With text symbolizers using font characters, this is actually a snap, provided your data includes information about direction.

The New York City streets data set has an attribute field called trafdir which specifies the flow of traffic on one-way streets:

W – (“with”) One-way in the direction of ascending street numbers
A – (“against”) One-way in the opposite direction of ascending street numbers

With this in mind, all that is needed is an SLD with two rules, each rule drawing an arrow pointing in appropriate direction specified by the trafdir attribute. Since arrows are included in most fonts, this can be accomplished using text symbolizers. And since the text label is oriented relative to the lines themselves (due to labels that can follow curves) the arrows will automatically be properly aligned with the road.

Here is one of the SLD rules. The other rule is identical, except for the attribute value and the specific character used.

<Rule>
  <ogc:Filter>
    <ogc:PropertyIsEqualTo>
      <ogc:PropertyName>trafdir</ogc:PropertyName>
      <ogc:Literal>W</ogc:Literal>
    </ogc:PropertyIsEqualTo>
  </ogc:Filter>
  <TextSymbolizer>
    <Label>
      <ogc:Literal>&#x2192;</ogc:Literal>
    </Label>
    <Font>
      <CssParameter name="font-family">Lucida Sans</CssParameter>
      <CssParameter name="font-size">18</CssParameter>
    </Font>
    <Fill>
      <CssParameter name="fill">#a4bdc5</CssParameter>
    </Fill>
  </TextSymbolizer>
</Rule>

 

This trick was figured out by Jordan Anderson, author of Ride The City, who goes on to say:

“Probably the most challenging thing was getting the correct balance between street name labels and arrow labels (which are on two different layers). I became intimately familiar with all the new labeling options and made use of spaceAround, maxDisplacement, and Repeat to get something close to the right balance.”

Looks pretty good to me. Thanks Jordan!

5 Comments

  1. Christoph
    Posted 2009/04/06 at 2:29 am | Permalink

    There is another alternative to get quite cool looking direction arrows. I use it to get the oneway streets from Openstreetmap labeled. The trick is to use some kinds of dasharray strokes to make it look like arrows. Try out the following rules (one for oneway streets in forward direction and one for backward direction). You have to adapt the filter to your data and have fun!

    oneway
    yes

    oneway
    true

    oneway
    1

    10000

    #6c70d5
    1
    bevel
    0 12 10 152

    #6c70d5
    2
    bevel
    0 12 9 153

    #6c70d5
    3
    bevel
    0 18 2 154

    #6c70d5
    4
    bevel
    0 18 1 155

    oneway
    -1

    10000

    #6c70d5
    1
    bevel
    0 12 10 152

    #6c70d5
    2
    bevel
    0 13 9 152

    #6c70d5
    3
    bevel
    0 14 2 158

    #6c70d5
    4
    bevel
    0 15 1 158

  2. Christoph
    Posted 2009/04/06 at 2:40 am | Permalink

    I’m sorry, this was my first post. I try another one with xml quoted. Why there is no button for testing comments?

    <Rule>
    <ogc:Filter>
    <ogc:Or>
    <ogc:PropertyIsEqualTo>
    <ogc:PropertyName>oneway</ogc:PropertyName>
    <ogc:Literal>yes</ogc:Literal>
    </ogc:PropertyIsEqualTo>
    <ogc:PropertyIsEqualTo>
    <ogc:PropertyName>oneway</ogc:PropertyName>
    <ogc:Literal>true</ogc:Literal>
    </ogc:PropertyIsEqualTo>
    <ogc:PropertyIsEqualTo>
    <ogc:PropertyName>oneway</ogc:PropertyName>
    <ogc:Literal>1</ogc:Literal>
    </ogc:PropertyIsEqualTo>
    </ogc:Or>
    </ogc:Filter>
    <MaxScaleDenominator>10000</MaxScaleDenominator>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>1</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 12 10 152</CssParameter>
    </Stroke>
    </LineSymbolizer>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>2</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 12 9 153</CssParameter>
    </Stroke>
    </LineSymbolizer>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>3</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 18 2 154</CssParameter>
    </Stroke>
    </LineSymbolizer>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>4</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 18 1 155</CssParameter>
    </Stroke>
    </LineSymbolizer>
    </Rule>
    <Rule>
    <ogc:Filter>
    <ogc:PropertyIsEqualTo>
    <ogc:PropertyName>oneway</ogc:PropertyName>
    <ogc:Literal>-1</ogc:Literal>
    </ogc:PropertyIsEqualTo>
    </ogc:Filter>
    <MaxScaleDenominator>10000</MaxScaleDenominator>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>1</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 12 10 152</CssParameter>
    </Stroke>
    </LineSymbolizer>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>2</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 13 9 152</CssParameter>
    </Stroke>
    </LineSymbolizer>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>3</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 14 2 158</CssParameter>
    </Stroke>
    </LineSymbolizer>
    <LineSymbolizer>
    <Stroke>
    <CssParameter name=”stroke”>#6c70d5</CssParameter>
    <CssParameter name=”stroke-width”>4</CssParameter>
    <CssParameter name=”stroke-linejoin”>bevel</CssParameter>
    <CssParameter name=”stroke-dasharray”>0 15 1 158</CssParameter>
    </Stroke>
    </LineSymbolizer>
    </Rule>

  3. Mike Pumphrey
    Posted 2009/04/06 at 10:58 am | Permalink

    Hey Christoph. Thanks for the code. Do you have a link to a screenshot or some sample output using this SLD? I’m sure everyone would like to see it.

    (And I’ll look into getting some comment previewing set up.)

  4. Posted 2009/04/07 at 9:36 am | Permalink

    Very helpful! I just posted a question about drawing arrows on the uDig mailing list, and received a link to this post.

    I tried the second approach from Christoph’s comments in uDig. There is a screenshot in my blog:

    http://hwellmann.blogspot.com/2009/04/arrows-with-styled-layer-descriptor.html

  5. Posted 2009/05/11 at 1:28 pm | Permalink

    I found that I needed two other parameters in the SLD to get this to work for me. I needed this between Font and Fill:

    <LabelPlacement>
    <LinePlacement>
    <PerpendicularOffset>0</PerpendicularOffset>
    </LinePlacement>
    </LabelPlacement>

    And this after Fill:

    <VendorOption name=”followLine”>true</VendorOption>
    <VendorOption name=”forceLeftToRight”>false</VendorOption>

Download GeoServer