When mapping a road network, the display order is important as we don’t want the main roads to be cut by secondary roads.

The display order depends on many factors. This article explains the behavior of MapServer when rendering features so that you can create clean maps with all features displayed in the right order. Note that some of the rules explained here only apply to LINE type LAYER objects.

1) Read first - drawn first

The first thing to consider when trying to display features in a specific order is the order in which the source data is read by MapServer. In a LAYER, the first feature encountered by MapServer is drawn first, then the next one will be drawn on top of it and so on. This is regardless of the CLASS rendering it and its position, which means that the source data should be sorted. For example, in PostGIS, the data can be sorted according to a ‘z-index’ attribute (z_order in OpenStreetMap) and the length of each line.


DATA "geometry from (select osm_id, geometry, name, type
      from osm_roads
      where type in ('secondary', 'tertiary', 'residential')
      order by z_order asc, st_length(geometry) asc)
      as foo using unique osm_id using srid=900913"
 

When mapping ‘shapefiles’, the sortshp tool might be very useful.

NOTE: This rule applies to any type of layer

2) Number of STYLE objects inside the CLASS

The number of STYLE objects found inside a CLASS affects the display order. Inside a given LAYER, the first STYLE of each CLASS is processed and rendered, then MapServer proceeds to the second STYLE of each CLASS and so on.

3) Outline first

When the first STYLE object of a given CLASS contains a OUTLINEWIDTH, the outlines of each class are drawn before the feature’s interior. This has been true since MapServer 5.4 and it is documented in the RFC-49.

Incorrect mapfile example:

CLASS  # secondary roads
  EXPRESSION /secondary/
  STYLE
    WIDTH 12
    COLOR 255 0 0 # red
    OUTLINEWIDTH 2
    OUTLINECOLOR 153 111 57 # brown
  END
  STYLE
    WIDTH 11
    COLOR 223 197 124 # orange
  END
END

CLASS # tertiary roads
  EXPRESSION /tertiary/
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 193 188 157 # gray
    COLOR 255 253 139 # yellow
  END
END

CLASS # residential roads
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 103 181 157  # green
  END
  STYLE
    WIDTH 11
    COLOR  238 225 226  # pink
  END
END

Considering that the source data has been sorted in that order: residential, tertiary, secondary, the display order is the following:

  1. Residential roads’ outline(green)
  2. Tertiary roads’ outline (gray)
  3. Secondary roads’ outline (brown)
  4. Residential roads’ first style (outline only)
  5. Tertiary roads’ only style (yellow)
  6. Secondary roads’ first style (red)
  7. Residential roads’ second style (pink)
  8. Secondary roads’ second style (orange)

The problems:

  1. The red outline of the secondary roads crosses the yellow lines. Note that the red outline is not an actual outline but a STYLE with a COLOR tag and a WIDTH tag greater than that of the second STYLE.
  2. The pink lines’ interior are drawn over the yellow lines instead of being drawn below.
  3. The pink lines’ interior are drawn over the red lines.
Correct mapfile example:

CLASS  # secondary roads
  EXPRESSION /secondary/
  STYLE
    WIDTH 12
    COLOR 255 0 0 # red
    OUTLINEWIDTH 2
    OUTLINECOLOR 153 111 57 # brown
  END
  STYLE
    WIDTH 11
    COLOR 223 197 124 # orange
  END
END

CLASS # tertiary roads
  EXPRESSION /tertiary/
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 193 188 157 # gray
  END
  STYLE
    WIDTH 11
    COLOR 255 253 139 # yellow
  END
END

CLASS # residential roads
  STYLE
    WIDTH 11
    OUTLINEWIDTH 1
    OUTLINECOLOR 103 181 157  # green
  END
  STYLE
    WIDTH 11
    COLOR  238 225 226  # pink
  END
END

NOTE: This article was originally posted in French on Simon Mercier’s blog