Labeling¶
Controlling Label Placement¶
Controlling where the WMS server places labels with SLD is bit complex. The SLD specification only defines the most basic way of controlling placement explicitly says that defining more control is “a real can of worms”. Geoserver fully supports the SLD specification plus adds a few extra parameters so you can make pretty maps.
Basic SLD Placement¶
The SLD specification indicates two types of LabelPlacement:
- for Point Geometries (“PointPlacement”)
- for Linear (line) geometries (“LinePlacement”)
Note
Relative to Where?
- See below for the actual algorithm details, but:
- Polygons are intersected with the viewport and the centroid is used.
- Lines are intersected with the viewport and the middle of the line is used.
Code¶
<xsd:element name="PointPlacement">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="sld:AnchorPoint" minOccurs="0"/>
<xsd:element ref="sld:Displacement" minOccurs="0"/>
<xsd:element ref="sld:Rotation" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
<xsd:element name="LinePlacement">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="sld:PerpendicularOffset" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
PointPlacement¶
When you use a <PointPlacement> element, the geometry you are labeling will be reduced to a single point (usually the “middle” of the geometry - see algorithm below for details). You can control where the label is relative to this point using the options:
| Option | Meaning (Name) |
| AnchorPoint | This is relative to the LABEL. Using this you can do things such as center the label on top of the point, have the label to the left of the point, or have the label centered under the point. |
| Displacement | This is in PIXELS and lets you fine-tune the location of the label. |
| Rotation | This is the clockwise rotation of the label in degrees. |
The best way to understand these is with examples:
AnchorPoint¶
The anchor point determines where the label is placed relative to the label point. These measurements are relative to the bounding box of the label. The (x,y) location inside the label’s bounding box (specified by the AnchorPoint) is placed at the label point.
The anchor point is defined relative to the label’s bounding box. The bottom left is (0,0), the top left is (1,1), and the middle is (0.5,0.5).
<PointPlacement>
<AnchorPoint>
<AnchorPointX>
0.5
</AnchorPointX>
<AnchorPointY>
0.5
</AnchorPointY>
</AnchorPoint>
</PointPlacement>
By changing the values, you can control where the label is placed.
(x=0,y=0.5) DEFAULT - place the label to the right of the label point
(x=0.5,y=0.5) - place the centre of the label at the label point
(x=1,y=0.5) - place the label to the left of the label point
(x=0.5,y=0) - place the label centered above the label point
Displacement¶
Displacement allows fine control of the placement of the label. The displacement values are in pixels and simply move the location of the label on the resulting image.
<PointPlacement>
<Displacement>
<DisplacementX>
10
</DisplacementX>
<DisplacementY>
0
</DisplacementY>
</Displacement>
</PointPlacement>
displacement of x=10 pixels, compare with anchor point (x=0,y=0.5) above
displacement of y=-10 pixels, compare with anchor point (x=0.5,y=1.0) not shown
Rotation¶
Rotation is simple - it rotates the label clockwise the number of degrees you specify. See the examples below for how it interacts with AnchorPoints and displacements.
<Rotation>
45
</Rotation>
simple 45 degrees rotation
45 degrees rotation with anchor point (x=0.5,y=0.5)
45 degrees with 40 pixel X displacement
45 degrees rotation with 40 pixel Y displacement with anchor point (x=0.5,y=0.5)
LinePlacement¶
When you are labeling a line (i.e. a road or river), you can specify a <LinePlacement> element. This tells the labeling system two things: (a) that you want Geoserver to determine the best rotation and placement for the label (b) a minor option to control how the label is placed relative to the line.
The line placement option is very simple - it only allows you to move a label up-and-down from a line.
<xs:elementname="LinePlacement">
<xs:complexType>
<xs:sequence>
<xs:element ref="sld:PerpendicularOffset" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
...
<xs:element name="PerpendicularOffset" type="sld:ParameterValueType"/>
This is very similiar to the DisplacementY option (see above).
<LabelPlacement>
<LinePlacement>
<PerpendicularOffset>
10
</PerpendicularOffset>
</LinePlacement>
</LabelPlacement>
PerpendicularOffset=0
PerpendicularOffset=10 pixels
Composing labels from multiple attributes¶
The <Label> element in TextSymbolizer is said to be mixed, that is, its content can be a mixture of plain text and OGC Expressions. The mix gets interepreted as a concatenation, this means you can leverage it to get complex labels out of multiple attributes.
For example, if you want both a state name and its abbreviation to appear in a label, you can do the following:
<Label>
<ogc:PropertyName>STATE_NAME</ogc:PropertyName> (<ogc:PropertyName>STATE_ABBR</ogc:PropertyName>)
</Label>
and you’ll get a label such as Texas (TX).
If you need to add extra white space or newline, you’ll stumble into an xml oddity. The whitespace handling in the Label element is following a XML mandated rule called “collapse”, in which all leading and trailing whitespaces have to be removed, whilst all whitespaces (and newlines) in the middle of the xml element are collapsed into a single whitespace.
So, what if you need to insert a newline or a sequence of two or more spaces between your property names? Enter CDATA. CDATA is a special XML section that has to be returned to the interpreter as-is, without following any whitespace handling rule. So, for example, if you wanted to have the state abbreviation sitting on the next line you’d use the following:
<Label>
<ogc:PropertyName>STATE_NAME</ogc:PropertyName><![CDATA[
]]>(<ogc:PropertyName>STATE_ABBR</ogc:PropertyName>)
</Label>
Geoserver Specific Enhanced Options¶
The following options are all extensions of the SLD specification. Using these options gives much more control over how the map looks, since the SLD standard isn’t expressive enough to handle all the options one might want. In time we hope to have them be an official part of the specification.
Priority Labeling (<Priority>)¶
GeoServer has extended the standard SLD to also include priority labeling. This allows you to control which labels are rendered in preference to other labels.
For example, lets assume you have a data set like this:
City Name | population
------------+------------
Yonkers | 197,818
Jersey City | 237,681
Newark | 280,123
New York | 8,107,916
Most people don’t know where “Yonkers” city is, but do know where “New York” city is. On our map, we want to give “New York” priority so its more likely to be labeled when it’s in conflict (overlapping) “Yonkers”.
Note
Standard SLD Behavior
If you do not have a <Priority> tag in your SLD then you get the default SLD labeling behavior. This basically means that if there’s a conflict between two labels, there is no ‘dispute’ mechanism and its random which label will be displayed.
In our TextSymbolizer we can put an Expression to retreive or calculate the priority for each feature:
<Priority>
<PropertyName>population</PropertyName>
</Priority>
Location of the cities (see population data above)
New York is labeled in preference to the less populated cities. Without priority labeling, “Yonkers” could be labeled in preference to New York, making a difficult to interpret map.
Notice that larger cities are more readily named than smaller cities.
Grouping Geometries (<VendorOption name=”group”>)¶
Sometimes you will have a set of related features that you only want a single label for. The grouping option groups all features with the same label text, then finds a representative geometry for the group.
Roads data is an obvious example - you only want a single label for all of “main street”, not a label for every piece of “main street.”
When the grouping option is off (default), grouping is not performed and each geometry is labeled (space permitting).
With the grouping option on, all the geometries with the same label are grouped together and the label position is determined from ALL the geometries.
| Geometry | Representative Geometry |
| Point Set | first point inside the view rectangle is used. |
| Line Set | lines are (a) networked together (b) clipped to the view rectangle (c) middle of the longest network path is used. |
| Polygon Set | polygons are (a) clipped to the view rectangle (b) the centroid of the largest polygon is used. |
<VendorOption name="group">yes</VendorOption>
Warning
Watch out - you could group together two sets of features by accident. For example, you could create a single group for “Paris” which contains features for Paris (France) and Paris (Texas).
Overlapping and Separating Labels (<VendorOption name=”spaceAround”>)¶
By default geoserver will not put labels “on top of each other”. By using the spaceAround option you can allow labels to overlap and you can also add extra space around a label.
<VendorOption name="spaceAround">10</VendorOption>
Default behavior (“0”) - the bounding box of a label cannot overlap the bounding box of another label.
With a negative spaceAround value, overlapping is allowed.
With a spaceAround value of 10 for all TextSymbolizers, each label will be 20 pixels apart from each other (see below).
NOTE: the value you specify (an integer in pixels) actually provides twice the space that you might expect. This is because you can specify a spaceAround for one label as 5, and for another label (in another TextSymbolizer) as 3. The distance between them will be 8. For two labels in the first symbolizer (“5”) they will each be 5 pixels apart from each other, for a total of 10 pixels!
Note
Interaction with different values in different TextSymbolizers
You can have multiple TextSymbolizers in your SLD file, each with a different spaceAround option. This will normally do what you would think if all your spaceAround options are >=0. If you have negative values (‘allow overlap’) then these labels can overlap labels that you’ve said should not be overlapping. If you dont like this behavior, its not too difficult to change - feel free to submit a patch!
followLine¶
The followLine option forces a label to follow the curve of the line. To use this option place the following in your <TextSymbolizer>.
<VendorOption name="followLine">true</VendorOption>
It is required to use <LinePlacement> along with this option to ensure that all labels are correctly following the lines:
<LabelPlacement>
<LinePlacement/>
</LabelPlacement>
maxDisplacement¶
The maxDisplacement option controls the displacement of the label along a line. Normally GeoServer would label a line at its center point only, provided the location is not busy with another label, and not label it at all otherwise. When set, the labeller will search for another location within maxDisplacement pixels from the pre-computed label point.
When used in conjunction with repeat, the value for maxDisplacement should always be lower than the value for repeat.
<VendorOption name="maxDisplacement">10</VendorOption>
repeat¶
The repeat option determines how often GeoServer labels a line. Normally GeoServer would label each line only once, regardless of their length. Specify a positive value to make it draw the label every repeat pixels.
<VendorOption name="repeat">100</VendorOption>
labelAllGroup¶
The labelAllGroup option makes sure that all of the segments in a line group are labeled instead of just the longest one.
<VendorOption name="labelAllGroup">true</VendorOption>
maxAngleDelta¶
Designed to use used in conjuection with followLine, the maxAngleDelta option sets the maximum angle, in degrees, between two subsequent characters in a curved label. Large angles create either visually disconnected words or overlapping characters. It is advised not to use angles larger than 30.
<VendorOption name="maxAngleDelta">15</VendorOption>
autoWrap¶
The autoWrap option wraps labels when they exceed the given value, given in pixels. Make sure to give a dimension wide enough to accommodate the longest word other wise this option will split words over multiple lines.
<VendorOption name="autoWrap">50</VendorOption>
forceLeftToRight¶
The labeller always tries to draw labels so that they can be read, meaning the label does not always follow the line orientation, but sometimes it’s flipped 180° instead to allow for normal reading. This may get in the way if the label is a directional arrow, and you’re trying to show one way directions (assuming the geometry is oriented along the one way, and that you have a flag to discern one ways from streets with both circulations).
The following setting disables label flipping, making the label always follow the natural orientation of the line being labelled:
<VendorOption name="forceLeftToRigth">false</VendorOption>
conflictResolution¶
By default labels are subjected to conflict resolution, meaning the renderer will not allow any label to overlap with a label that has been drawn already. Setting this parameter to false pull the label out of the conflict resolution game, meaning the label will be drawn even if it overlaps with other labels, and other labels drawn after it won’t mind overlapping with it.
<VendorOption name="conflictResolution">false</VendorOption>
Goodness of Fit¶
Geoserver will remove labels if they are a particularly bad fit for the geometry they are labeling.
| Geometry | Goodness of Fit Algorithm |
| Point | Always returns 1.0 since the label is at the point |
| Line | Always returns 1.0 since the label is always placed on the line. |
| Polygon | The label is sampled approximately at every letter. The distance from these points to the polygon is determined and each sample votes based on how close it is to the polygon. (see LabelCacheDefault#goodnessOfFit()) |
The default value is 0.5, but it can be modified using:
<VendorOption name="goodnessOfFit">0.3</VendorOption>
Polygon alignment¶
GeoServer normally tries to place horizontal labels within a polygon, and give up in case the label position is busy or if the label does not fit enough in the polygon. This options allows GeoServer to try alternate rotations for the labels.
<VendorOption name="polygonAlign">mbr</VendorOption>
| Option | Description |
| manual | The default value, only the rotation manually specified in the <Rotation> tag will be used |
| ortho | If the label does not fit horizontally and the polygon is taller than wider the vertical alignement will also be tried |
| mbr | If the label does not fit horizontally the minimum bounding rectangle will be computed and a label aligned to it will be tried out as well |