WMS Labeling#
The tiling of rendered vector maps often results in issues with truncated or repeated labels. Some of these issues can be reduced with a proper configuration of MapProxy, but some require changes to the configuration of the source WMS server.
This document describes settings for MapProxy and MapServer, but the problems and solutions are also valid for other WMS servers. Refer to their documentations on how to configure these settings.
The Problem#
MapProxy always uses small tiles for caching. MapProxy does not pass through incoming requests to the source WMS [1], but it always requests images/tiles that are aligned to the internal grid. MapProxy combines, scales and reprojects these tiles for WMS requests and for tiled requests (TMS/KML) the tiles are combined by the client (OpenLayers, etc).
When tiles are combined, the text labels at the boundaries need to be present at both tiles and need to be placed at the exact same (geographic) location.
There are three common problems here.
No placement outside the BBOX#
WMS servers do not draw features that are outside of the map bounds. For example, a city label that extends into the neighboring map tile will not be drawn in that other tile, because the geographic feature of the city (a single point) is only present in one tile.
Dynamic label placement#
WMS servers can adjust the position of labels so that more labels can fit on a map. For example, a city label is not always displayed at the same geographic location, but moved around to fit in the requested map or to make space for other labels.
Repeated labels#
WMS servers render labels for polygon areas in each request. Labels for large areas will appear multiple times, once in each tile.
MapProxy Options#
There are two options that help with these issues.
Meta Tiles#
You can use meta tiles to reduce the labeling issues. A meta tile is a collection of multiple tiles. Instead of requesting each tile with a single request, MapProxy requests a single image that covers the area of multiple tiles and then splits that response into the actual tiles.
The following image demonstrates that:
The thin lines represent the tiles. The WMS request (inner box) consists of 20 tiles and without metatiling each tile results in a request to the WMS source. With a meta tile size of 4x4, only two larger requests to the source WMS are required (thick black box).
Because you are requesting less images, you have less boundaries where labeling issues can appear. In this case it reduces the number of tile/image boundaries from 31 to only one.
But, it only reduces the problem and does not solve it. Nonetheless, it should be used because it also reduces the load on the source WMS server.
You can configure the meta tile size in the globals.cache
section and for each cache
. It defaults to [4, 4]
.
globals:
cache:
meta_size: [6, 6]
caches:
mycache:
sources: [...]
grids: [...]
meta_size: [8, 8]
This does also work for tiles services. When a client like OpenLayers requests the 20 tiles from the example above in parallel, MapProxy will still requests the two meta tiles. Locking ensures that each meta tile will be requested only once.
Meta Buffer#
In addition to meta tiles, MapProxy implements a meta buffer. The meta buffer adds extra space at the edges of the requested area. With this buffer, you can solve the first issue: no placement outside the BBOX.
You can combine meta tiling and meta buffer. MapProxy then extends the whole meta tile with the configured buffer.
A meta buffer of 100 will add 100 pixels at each edge of the request. With a meta size of 4x4 and a tile size of 256x256, the requested image is extended from 1024x1024 to 1224x1224. The BBOX is also extended to match the new geographical extent.
To solve the first issue, the value should be at least half of your longest labels: If you have text labels that are up to 200 pixels wide, than you should use a meta buffer of around 120 pixels.
You can configure the size of the meta buffer in the globals.cache
section and for each cache
. It defaults to 80
.
globals:
cache:
meta_buffer: 100
caches:
mycache:
sources: [...]
grids: [...]
meta_buffer: 150
WMS Server Options#
You can reduce some of the labeling issues with meta tiling, and solve the first issue with the meta buffer. The issues with dynamic and repeated labeling requires some changes to your WMS server.
In general, you need to disable the dynamic position of labels and you need to allow the rendering of partial labels.
MapServer Options#
MapServer has lots of settings that affect the rendering. The two most important settings are
PROCESSING "LABEL_NO_CLIP=ON"
from theLAYER
configuration.With this option the labels are fixed to the whole feature and not only the part of the feature that is visible in the current map request. Default is off.
and
PARTIALS
from theLABEL
configuration.If this option is true, then labels are rendered beyond the boundaries of the map request. Default is true.
PARTIAL FALSE
#
The easiest option to solve all issues is PARTIAL FALSE
with a meta buffer of 0. This prevents any label from truncation, but it comes with a large downside: Since no labels are rendered at the boundaries of the meta tiles, you will have areas with no labels at all. These areas form a noticeable grid pattern on your maps.
The following images demonstrates a WMS request with a meta tile boundary in the center.
You can improve that with the right set of configuration options for each type of geometry.
Points#
As described above, you can use a meta buffer to prevent missing labels. You need to set PARTIALS TRUE
(which is the default), and configure a large enough meta buffer. The labels need to be placed at the same position with each request. You can configure that with the POSITION
options. The default is auto
and you should set this to an explicit value, cc
or uc
for example.
example.map
:
LABEL
[...]
POSITION cc
PARTIALS TRUE
END
mapproxy.yaml
:
caches:
mycache:
meta_buffer: 150
[...]
Polygons#
Meta tiling reduces the number of repeated labels, but they can still appear at the border of meta tiles.
You can use the PROCESSING "LABEL_NO_CLIP=ON"
option to fix this problem.
With this option, MapServer places the label always at a fixed position, even if that position is outside the current map request.
If the LABEL_NO_CLIP
option is used, PARTIALS
should be TRUE
. Otherwise label would not be rendered if they overlap the map boundary. This options also requires a meta buffer.
example.map
:
LAYER
TYPE POLYGON
PROCESSING "LABEL_NO_CLIP=ON"
[...]
LABEL
[...]
POSITION cc
PARTIALS TRUE
END
END
mapproxy.yaml
:
caches:
mycache:
meta_buffer: 150
[...]
Lines#
By default, labels are repeated on longer line strings. Where these labels are repeated depends on the current view of that line. That placement might differ in two neighboring image requests for long lines.
Most of the time, the labels will match at the boundaries of the meta tiles, when you use PARTIALS TRUE
and a meta buffer. But, you might notice truncated labels on long line strings. In practice these issues are rare, though.
example.map
:
LAYER
TYPE LINE
[...]
LABEL
[...]
PARTIALS TRUE
END
END
mapproxy.yaml
:
caches:
mycache:
meta_buffer: 150
[...]
You can disable repeated labels with PROCESSING LABEL_NO_CLIP="ON"
, if don’t want to have any truncated labels. Like with polygons, you need set PARTIALS TRUE
and use a meta buffer. The downside of this is that each lines will only have one label in the center of that line.
example.map
:
LAYER
TYPE LINE
PROCESSING "LABEL_NO_CLIP=ON"
[...]
LABEL
[...]
PARTIALS TRUE
END
END
mapproxy.yaml
:
caches:
mycache:
meta_buffer: 150
[...]
There is a third option. If you want repeated labels but don’t want any truncated labels, you can set PARTIALS FALSE
. Remember that you will get the same grid pattern as mentioned above, but it might not be noted if you mix this layer with other point and polygon layers where PARTIALS
is enabled.
You need to compensate the meta buffer when you use PARTIALS FALSE
in combination with other layers that require a meta buffer. You need to set the option LABELCACHE_MAP_EDGE_BUFFER
to the negative value of your meta buffer.
WEB
[...]
METADATA
LABELCACHE_MAP_EDGE_BUFFER "-100"
END
END
LAYER
TYPE LINE
[...]
LABEL
[...]
PARTIALS FALSE
END
END
mapproxy.yaml
:
caches:
mycache:
meta_buffer: 100
[...]
Other WMS Servers#
The most important step for all WMS servers is to disable to dynamic placement of labels. Look into the documentation how to do this for you WMS server.
If you want to contribute to this document then join our mailing list or use our issue tracker.