Introduction
This article follows up on the article on adding and styling features.
Adding labels to features
You can attach icon and text labels to features with the FeatureCanvas::drawLabel
FeatureCanvas::drawLabel
FeatureCanvas::drawLabel
method. Drawing labels is similar to drawing icons or text, with a few distinctions. The most prominent distinctions are:
-
Labels are decluttered
-
Labels have more flexible placement options
-
Labels are always painted on top
The following sections explain this in more detail.
Label decluttering
LuciadCPillar automatically declutters labels to make sure that labels don’t overlap. Labels have more than one possible location. Depending on the view extents and the space occupied by other labels, LuciadCPillar selects the best of those positions. If there are no good positions, the label is dropped.
Labels are decluttered according to these principles:
-
Labels can never overlap with other labels.
-
If a label has a position that lies completely inside the map, it has precedence over a position that lies partially outside the map. If no better position is available for the other label, the label is still painted partially outside the map though.
-
If no valid label position is available, the label isn’t painted. This can happen when all possible positions overlap with another label, for example.
-
If the anchor of the label is invisible, the label isn’t painted.
You can influence the label decluttering behavior by assigning priorities
priorities
priorities
to a label. Labels with a higher priority are placed on the map before labels with a lower priority. In practice, this means
that high-priority labels have a better chance of finding a valid location than low-priority labels. Note that label priorities
work across layers.
Label positioning
You can attach labels to a feature in many ways:
-
Point-based: you attach the label to a Point anchor. The label has
multiple possible locations
multiple possible locations
multiple possible locations
around that point.
std::vector<RelativePosition> possiblePositions = {RelativePosition::east(offset),
RelativePosition::west(offset),
RelativePosition::north(offset),
RelativePosition::south(offset),
RelativePosition::northEast(furtherOffset, furtherOffset),
RelativePosition::northWest(furtherOffset, furtherOffset),
RelativePosition::southEast(furtherOffset, furtherOffset),
RelativePosition::southWest(furtherOffset, furtherOffset)};
canvas.drawLabel().anchor(*geometry).pointPositions(possiblePositions).text(labelTextBlock).pinStyle(pinStyle).priority(cityStyle->priority).submit();
var possiblePositions = new List<RelativePosition>
{
RelativePosition.East(offset),
RelativePosition.West(offset),
RelativePosition.North(offset),
RelativePosition.South(offset),
RelativePosition.NorthEast(furtherOffset, furtherOffset),
RelativePosition.NorthWest(furtherOffset, furtherOffset),
RelativePosition.SouthEast(furtherOffset, furtherOffset),
RelativePosition.SouthWest(furtherOffset, furtherOffset)
};
canvas.DrawLabel()
.Anchor(geometry)
.PointPositions(possiblePositions)
.Text(labelTextBlock)
.PinStyle(pinStyle)
.Priority(cityStyle.Priority)
.Submit();
val possiblePositions = listOf(
RelativePosition.east(offset),
RelativePosition.west(offset),
RelativePosition.north(offset),
RelativePosition.south(offset),
RelativePosition.northEast(furtherOffset, furtherOffset),
RelativePosition.northWest(furtherOffset, furtherOffset),
RelativePosition.southEast(furtherOffset, furtherOffset),
RelativePosition.southWest(furtherOffset, furtherOffset)
)
canvas.drawLabel().anchor(geometry).pointPositions(possiblePositions).text(labelTextBlock)
.pinStyle(pinStyle).priority(cityStyle.priority).submit()
canvas.drawLabel()
.anchor(*geometry) //
.inPath()
.text(name)
.textStyle(std::move(textStyle))
.priority(Priority::fallback())
.queryable(false)
.submit();
canvas.DrawLabel()
.Anchor(geometry)
.InPath()
.Text(name).TextStyle(textStyle)
.Priority(Priority.Fallback)
.Queryable(false)
.Submit();
canvas.drawLabel()
.anchor(geometry)
.inPath()
.text(name)
.textStyle(textStyle)
.priority(Priority.Fallback)
.queryable(false)
.submit()
canvas.drawLabel()
.text(*name) //
.textStyle(textStyle)
.anchor(*geometry)
.onPath(PathLabelPosition::Above, 0.0)
.priority(Priority::low())
.submit();
canvas.DrawLabel()
.Text(name).TextStyle(textStyle)
.OnPath(PathLabelPosition.Above, 0.0)
.Anchor(geometry)
.Priority(Priority.Low)
.Submit();
canvas.drawLabel()
.text(name)
.textStyle(textStyle)
.anchor(geometry)
.onPath(PathLabelPosition.Above, 0.0)
.priority(Priority.Low)
.submit()
Label content
The label content can be text-based, or it can be an icon.
-
Single-line text: You can specify a single string as label content using the
text(std::string)
method.
-
Multi-line text: You can do this by specifying multiple strings as label content using the
text(std::vector<std::string>)
method.
Visualizing label pins
You can attach a pin
attach a pin
attach a pin
to a label.
A pin is a line that connects the label with its anchor.
Label painting
LuciadCPillar paints labels slightly differently than it paints regular icons or text:
-
It doesn’t drape the labels. This means that it always paints the labels in view space.
-
It paints labels on top of everything else.
-
You can
enable and disable
enable and disable
enable and disable
all the labels of a layer.
Labels in map query results
Labels are part of the results returned by queryFeatures
queryFeatures
queryFeatures
. The query
result doesn’t include a touch point for labels, though. Because labels are overlaid on the map and not painted in the map
reference like a Feature’s geometry, feature queries don’t compute the exact position on the map for labels.
The MapQueryFeaturesResult
MapQueryFeaturesResult
MapQueryFeaturesResult
entries for labels have the overlay
flag enabled.