MS RFC 57: Labeling enhancements: ability to repeat labels along a line/multiline

Authors:Alan Boudreault
Contact:aboudreault at
Last Edited:2009/06/29
Status:Adopted (2009/07/21) and Implemented.
Version:MapServer 6.0


Currently, MapServer draws labels on the longest segment of a linear shape. Even if the shape is a long line or a multiline shape, only one label is drawn. In some cases, the map quality could be highly improved with more labels.

This RFC proposes a mechanism to fix this by adding the ability to add more labels along long lines or multiline shapes. There are two major enhancements that are proposed.

See the Images section to visualize the current and new behaviors.

Enhancement 1: Label all the lines in MultiLine shape

At the moment, if you’ve got a MultiLine shape (i.e. a shapeObj with numlines > 1) then only the longest of the Lines is labelled. This is fine in most cases, but in some cases, we may want each individual Line in the MultiLine to get a label. That’s the first proposed enhancement: make it possible to label all lines in a MultiLine shape.

Enhancement 2: Ability to repeat labels along a line

At the moment, the label is placed at the center of a line in the case of ANGLE FOLLOW, and at the center of the longest segment of a line in the case of ANGLE AUTO|constant. In the case of very long lines (roads), and especially when using metatiles to render a tile cache, we may want to repeat the label at some interval along the line. That’s the second enhancement: make it possible to repeat the label at a given interval along a line.

Technical Solution

The way to control this in the mapfile is to add a LABEL.REPEATDISTANCE parameter. By default this would be turned off and we would keep the current behavior. If REPEATDISTANCE is set to any value > 0 then the labels would be repeated on every line of a multiline shape (enhancement 1 above), and would be repeated multiple times along a given line at an interval of REPEATDISTANCE pixels (item 2 above). In all cases the MINDISTANCE value would still be handled by the label cache so that multiple labels ending up too close to each other for various reasons would be eliminated by the label cache.

To achieve better visual effect, here is an outline of the algorithm that applies when REPEATDISTANCE is set:

  1. Calculate the number of labels candidates (N) that can fit in the length of the line.
  2. Ensure that N is an odd number. If it’s even then subtract one to get an odd number. We want an odd number of label candidates along a given line so that there is always one candidate that falls at the center of the line and which will remain in case of collisions. With an even number of candidates, when collisions happen along a line, the remaining labels are usually shifted one way or the other along the line instead of being centered which does not look as good.
  3. Calculate the offset between labels and the position of candidates along the line.
  4. Insert labels candidates into the label cache in an order that will ensure that the label cache will eliminate the right labels in case of collisions (keep in mind that collisions are still possible since MINDISTANCE remains in effect). We want to give higher priority to the label candidate at the center of the line and lower priority to other candidates as they get further away from the center. Since the label cache order is last in first out (LIFO), we start by inserting the labels candidates at the extremities of the line into the cache end end with the one at the center, giving it the highest priority.

Usage example

This example will repeat the labels every 80 pixels on each line of a multiline shape.


Backwards Compatibility Issues

There is no backwards compatibility issues. By default the LABEL.REPEATDISTANCE parameter is set to 0, so not applied.

Files Impacted

  • mapprimitive.c: Modify the labels placement algorithm
  • mapfile.c, maplexer.l: new LABEL.REPEATDISTANCE mapfile keyword
  • mapdraw.c: Modify the way how msPolylineLabel* calls are handled

Voting History

Adopted on 2009/07/21 with +1 from SteveW, DanielM, SteveL, AssefaY, ThomasB and PericlesN.