This article explains the support that LuciadLightspeed offers for labeling the representations of your domain objects. A label can be anything: a single word, a multi-line text string, an icon, or even an interactive panel. Painting labels is similar to painting domain objects, with the following differences:
Labels are usually painted on top of all regular object representations.
The labeling API explicitly models multiple labels for an object’s representation.
Labels have more advanced positioning options. This allows decluttering of labels to avoid overlap.
The following sections describe a typical labeling scenario, how to paint labels, how to work with label locations, how to interact with labels, and some more advanced labeling topics.
The following requirements are exemplary for a typical labeling scenario:
You want to place a single label next to your domain object’s representation.
The label is text-based, its text comes from a certain property of the object.
To prevent cluttering, overlapping labels should be dropped.
To determine the number of labels, the label content, and the rendering of the label, you need to configure an
ILcdGXYLabelPainterProvider on the layer so that it returns an
ILcdGXYLabelPainter2 for all domain objects. All the main implementations of
ILcdGXYLabelPainter2 that LuciadLightspeed provides also implement the
ILcdGXYLabelPainterProvider interface, and return themselves as label painter for every object. This is convenient when all objects of a layer need the
same label painter.
Choosing a label painter details the LuciadLightspeed implementations of the label painters and label painter providers.
In the sample scenario, a
TLcdGXYDataObjectLabelPainter is used: a text-based label painter which you can configure to paint one of the properties of the domain objects.
The following snippet shows how to configure this label painter on the layer.
TLcdGXYLayer layer = TLcdGXYLayer.create(aModel); //Activate labeling and configure the styling of those labels TLcdGXYDataObjectLabelPainter labelPainter = new TLcdGXYDataObjectLabelPainter(); labelPainter.setExpressions("identifier"); labelPainter.setForeground(Color.WHITE); labelPainter.setHaloEnabled(true); labelPainter.setHaloColor(Color.BLACK); layer.setGXYLabelPainterProvider(labelPainter); layer.setLabeled(true);
By default, the layer paints all labels for all objects on the location suggested by the label painter. To add decluttering
behavior or more advanced placement behavior, you need to set up and configure an
In the sample scenario, we use a
TLcdGXYLocationListLabelingAlgorithm. This algorithm places as many labels as possible without introducing overlapping labels. This snippet shows you how:
TLcdGXYLocationListLabelingAlgorithm labelingAlgorithm = new TLcdGXYLocationListLabelingAlgorithm(); layer.setGXYLabelingAlgorithmProvider(l -> labelingAlgorithm);
LuciadLightspeed provides the interface
ILcdGXYLabelPainter2 for painting labels. A label painter determines the content and visualization of your labels. It typically paints the label
at a position near the domain object.
For more information on how to influence where and when labels are placed, refer to Working with label locations.
Choosing a label painter describes LuciadLightspeed’s default implementations. One particular implementation,
TLcdGXYStampLabelPainter, allows you to customize the painting of labels by providing a label stamp as explained in Customizing labels using label stamps. If you want complete control of the painting of labels, you can provide your own implementation of
ILcdGXYLabelPainter2 as explained in Implementing your own label painter. Finally, Adding a halo to labels explains how to add a halo effect to your labels.
As described in A typical labeling scenario, you need to install an
ILcdGXYLabelPainter2 on your
ILcdGXYLayer to add labels for your domain objects. LuciadLightspeed provides several
ILcdGXYLabelPainter2 implementations in the packages
TLcdGXYLabelPainteris the default label painter for text-based labels and allows full configuration of the appearance of the label (the font, the color, whether or not the text should be in a frame, and more). Refer to the API reference to see the complete list of properties which you can modify to customize the appearance.
The text for the label is retrieved by using the
toStringmethod of the domain object that is labeled, but this can be overridden by overwriting the
retrieveLabelsmethod in a subclass. There is an extension of this class:
TLcdGXYDataObjectLabelPainterwhich takes the text from the properties of a domain object that implement
TLcdGXYStampLabelPainterdelegates the actual painting of the label to a label stamp. This allows you to easily customize the graphical representation of a label without having to worry about its location. This stamp could, for example, paint the label as simple text, or could use a complex AWT/Swing component to paint the label. Refer to Customizing labels using label stamps for more information on label stamps.
This painter also support interactive labels, which are described in Working with interactive labels.
TLcdGXYLabelPainterAdapterwraps around a regular painter so it can be used as a label painter. Painting many domain objects usually results in a cluttered view, making selection more difficult and making it difficult to distinguish one object from another. Painting object representations as a label has two advantages. Firstly, it provides the possibility to paint labels at a certain offset which reduces cluttering as shown in Figure 1, “Offset icons can reduce clutter and improve readability”.Figure 1. Offset icons can reduce clutter and improve readability
Secondly, it provides the possibility to position the labels using the label placers as described in Using label placers. Refer to the
labels.offset.*sample on how to use the
TLcdGXYLabelPainterAdapter, and Labels as domain object representations and Labeling labels for more information on using labels to represent the domain object.
TLcdGXYCompositeLabelPainterallows you to combine different label painters for the different labels of an object. For example, it can hold a
TLcdGXYLabelPainterAdapterto render an icon representation of the domain object and a
TLcdGXYLabelPainterto render a text label attached to the icon.
Splitting up the label painter in the
TLcdGXYStampLabelPainter and an
ALcdGXYLabelStamp separates the information on where to draw the label from how to draw the label. The
TLcdGXYStampLabelPainter takes care of determining the label location and then passes this position to the
In order to calculate the label location, the dimensions of the label must be known in advance. As this is related to how
the labels are drawn, the
ALcdGXYLabelStamp needs to provide this information. Therefore the
ALcdGXYLabelStamp class contains the following three methods:
dimensionSFCTdetermines the size of the label. This needs to be independent of the location, because the size is needed to determine the location.
paintpaints the label on the specified
java/awtGraphics instance. The Graphics is already translated and rotated, so the stamp can start painting at (0,0). Nevertheless, location and rotation information is provided, in case the stamp needs it. The
ALcdGXYLabelStampshould make sure not to paint outside of the bounds that it has returned, as the labeling algorithms typically rely on the bounds to declutter the labels.
isToucheddetermines if the label is present at the specified location. For example, when the label is circular, the bounds of that label, which are rectangular by definition, cover a larger area than is actually used by the label. This method then returns
falsefor points that fall outside of the circle and
truefor those within the circle.
You can enable halos on other label painters using the class
TLcdGXYHaloLabelPainter. This class wraps around an existing
ILcdGXYLabelPainter2, adding a halo to anything drawn by the wrapped painter. The methods to configure the label halo functionality are the same
as those mentioned in Painting halos around objects. Make sure to disable the image cache or to call the method
TLcdGXYHaloLabelPainter when the label is modified.
It is possible to complement a label painter with an
ILcdGXYLabelPainter2 determines the content and appearance of the labels,
ILcdGXYViewLabelPlacer computes if and where the label is positioned.
A label placer takes multiple labels of a group of domain objects into account. The group can be as large as all the objects visible in the view, including objects of different layers. This allows more advanced placing of labels, to improve readability and avoid overlap.
Using label placers explains the label placer implementations and how they use an algorithm to compute the label positions. Using label algorithms details the label algorithm implementations. Customizing label locations and label dependencies explains where the label locations are stored and how you can customize them.
Because a label placer operates on the labels of all layers, it is configured on the view by calling the
setGXYViewLabelPlacer method of the
ILcdGXYView implementation. LuciadLightspeed offers two label placer implementations (see the
com.luciad.view.gxy.labeling package), which you can configure both with a placement algorithm that encapsulates the actual positioning logic:
TLcdGXYLabelPlaceris the default label placer implementation. It allows setting an obstacle provider to prevent the placing of labels at certain positions.
TLcdGXYAsynchronousLabelPlaceroffers the same functionality, but positions the labels in its own thread. Use this implementation in combination with asynchronously painted layers as described in the the asynchronous painting documentation.
The view’s label placer uses one or more
ILcdGXYLabelingAlgorithms to determine which labels are placed and at which locations.
The easiest way to use these algorithms is to configure them directly on your layer using
LuciadLightspeed offers the following labeling algorithms (see the package
TLcdGXYLocationListLabelingAlgorithmis a default decluttering algorithm, iterating over a number of customizable positions relative to the domain object’s anchor point.
TLcdGXYOnPathLabelingAlgorithmis a decluttering algorithm that places labels along the border of the domain object.
TLcdGXYCompositeLabelingAlgorithmallows you to specify a label placement algorithm per layer, per object, or even per label. The composite algorithm partitions the labels into groups that are passed on to the relevant delegate algorithms. For more information on how to use composite algorithms, refer to the
TLcdGXYCompositeDiscretePlacementsLabelingAlgorithmis similar to
TLcdGXYCompositeLabelingAlgorithmbut allows interleaving the algorithms during the placement step. This means that you can, for example, first place a line label, then a point label, then another line label, and so on. For more information on how to use and configure algorithms, refer to the
One way to customize these algorithms is to wrap them, for example to further remove unwanted labels.
To illustrate this, the
labels.createalgorithm.* sample shows some possible algorithm wrappers.
If you want even more control of label placement, you can consider implementing your own labeling algorithm as
described in Implementing your own placement algorithm.
getLabelLocation method of an
ALcdLabelLocations instance you can retrieve the location information for a given label. This information needs to be interpreted by the label
ALcdLabelLocations instance not only stores the label locations, but also which labels are painted. For example, in the case that a certain
labeling algorithm (see Using label algorithms) decides that there is not enough place on the screen to paint all labels. In this case you can use the method
applyOnPaintedLabelLocations of the
ALcdLabelLocations instance to go over all labels that are painted, for example to determine which labels are visible at a certain location
on the screen.
All the information regarding the location of a label is contained in a
TLcdLabelLocation instance. This instance can specify the location of the label in several ways: as a location index, as a position relative
to the labeled domain object, or as an absolute position on the view. The actual placing of the label depends on how the location
getLocationIndexmethod returns a value smaller than zero, the label is freely placed and its position is specified by the
getLocationYmethods. This type of placement is used by most labeling algorithms. How these methods are interpreted depends on the label edit mode:
If the label edit mode indicates that the
TLcdLabelLocationcontains absolute information, the result of the methods is interpreted as absolute coordinates on the view.
If the label edit mode indicates that the
TLcdLabelLocationcontains a relative position, the result of the methods is interpreted as the horizontal and vertical offset with respect to a certain point of the domain object. What that point exactly is depends on the implementation of
ILcdGXYLabelPainter2. It can, for example, be the anchor point of the domain object as specified by the
anchorPointSFCTmethod of the
ILcdGXYPainterof the domain object.
getLocationIndexmethod of the
TLcdLabelLocationinstance returns a value larger than or equal to zero, the label location is placed on a discrete location. What the actual position is, depends on how the
ILcdGXYLabelPainter2interprets this location index.
It is possible to subclass
TLcdLabelLocation so it can contain more information. Refer to the API reference for more information.
TLcdLabelLocation allows declaring a label as being a domain object representation instead of a label of the domain object representation with
This information is used by the
TLcdGXYEditControllerModel2 to influence editing behavior: moving the label moves the entire domain object. It can also be used to influence the paint
order of labels.
The main advantage of painting domain objects as labels is that they can reduce clutter because they can take part in the
label placement process.
labels.offset.* sample has an icon representation of its domain objects. These icon labels are modeled as body labels.
The sample shows how to make a layer paint these body labels along with the regular domain object representations, and the
other labels along with the other labels of the view.
TLcdLabelLocation allows declaring a dependency on another label with the method
ILcdGXYLabelPainter2 implementations can use this information to paint the label relative to this parent label, instead of the domain object.
ILcdGXYViewLabelPlacer implementations can use this information to make sure that the parent labels are positioned first. Hence, you can use this
to label a label which is especially useful when you are painting the object representation as a label.
TLcdLabelLocations allows setting a dependency provider so that the label locations automatically have their parent labels configured.
labels.offset.* sample shows how to use dependencies to paint labels relative to an icon representation of the object, which is in turn modeled
as a label.
Just like with representations of domain objects, it is possible to interact with labels: users can select them and perform
certain operations on them. Graphically editing labels explains how to graphically edit labels so users can, for example, move them around. If you want more complex interaction,
you can use interactive labels, which allow you to represent your label with any
java.awt.Component. This is explained in Working with interactive labels.
Similar to graphically editing domain objects with an
ILcdGXYEditor, you can edit the labels of a domain with an
ILcdGXYLabelEditor. The following sections provide more details on the main implementations of
ILcdGXYLabelEditor and how to install and obtain an
ILcdGXYLabelEditor, allowing users to move labels around. When asked to edit the location of a label, it modifies the
TLcdLabelLocation by setting the location index to
-1 which indicates free placement. It also adjusts the location information so that the offset, specified in the given
ILcdGXYContext, is added to the label position.
ILcdGXYLabelEditor instances are obtained with the
getGXYLabelEditor method of the
ILcdGXYEditableLabelsLayer interface. This interface is an extension of
ILcdGXYLayer which provides methods to support editable labels.
TLcdGXYEditController2 retrieves an
ILcdGXYLabelEditor using this interface to offer mouse-based editing.
ILcdGXYEditableLabelsLayer and delegates the retrieval of a label editor to an
ILcdGXYLabelEditorProvider. All the main implementations of
ILcdGXYLabelEditor provided in LuciadLightspeed also implement this
ILcdGXYLabelEditorProvider interface and return themselves as label editor for every object.
Program: Installing an
ILcdGXYLabelEditor on an
ILcdGXYEditableLabelsLayer shows how to install a label editor on a layer.
An interactive label is a label with which the user can interact to modify properties related to the domain object. You can
configure interactive labels using an
ALcdGXYInteractiveLabelProvider. To display and edit interactive labels you can use a
Interactive labels are only shown when the user moves the mouse cursor over a regular label. This is done for performance
reasons. In case the
ALcdGXYInteractiveLabelProvider indicates that it can provide an interactive label for a certain label, it is asked to provide the interactive label. The
interactive label is then presented to the user.
The most important method when implementing an
ALcdGXYInteractiveLabelProvider is the
provideInteractiveLabel method. This method returns the
java.awt.Component with which the user can interact. This component can be any AWT/Swing component; it can be a single text field or a
JPanel containing check boxes, combo boxes, and more. As there can only be one interactive label from the same provider at the same
time, the implementation of this method can always return the same instance of the AWT/Swing component.
ALcdGXYInteractiveLabelProvider provides the following methods:
canProvideInteractiveLabelmethod is used to determine if the
ALcdGXYInteractiveLabelProvidercan provide an interactive label for the given object and indices. This allows fine-grained control over which labels can be interactive and which not. This method is always called before the
ALcdGXYInteractiveLabelProvideris asked to actually provide an interactive label.
canStopInteractionis called to ask if the interactive label can be stopped. This is useful for validation purposes and similar cases. Consider, for example, a text field in the interactive label that contains invalid text. In this case the interactive label should not stop. Otherwise, the user cannot correct the text. If the method returns
stopInteractionmethod will not be called.
ALcdGXYInteractiveLabelProviderthat the last provided interactive label should apply all outstanding changes, and prepare the label to be removed from the user interface. It returns a
booleanto indicate if the provider was successful in stopping the interactive label. If the method returns
false, the label will not be removed from the user interface and the user can keep interacting with the label. Consider, for example, an interactive label with a text field that contains invalid text. When the label is asked to stop the interaction, it can return
falseand change the text field.
cancelInteractionmethod tells the
ALcdGXYInteractiveLabelProviderto prepare the interactive label to be removed from the user interface without applying any changes. This request cannot be denied, consequently this method does not return a boolean.
As mentioned before, there can only be one interactive label from the same provider at the same time. As a result, the methods
ALcdGXYInteractiveLabelProvider are always called in a certain order. There are never two calls to
provideInteractiveLabel before the previous interaction is stopped or canceled. This allows you to attach all the necessary listeners to the domain
object and the AWT/Swing components in the
provideInteractiveLabel method, and to remove these listeners in the implementation of the
TLcdGXYInteractiveLabelsController uses a mouse listener to show interactive labels. When the mouse hovers over a label, it provides an interactive label for
that label. For touch input, where no hover exists, the interactive label is put in place at the first touch.
It is also possible to bypass the automatic behavior. When the
setProvideInteractiveLabelOnMouseOver method of
TLcdGXYInteractiveLabelsController is set to
false, no interactive labels are shown automatically. You can then ask the
ALcdGXYInteractiveLabelProvider to provide an interactive label with the
provideInteractiveLabel method. To stop or cancel the label interaction, you can use the
cancelInteraction methods respectively as this no longer happens automatically either. Bypassing the automatic behavior allows you, for example,
to implement a scenario in which a user cycles through the labels by pressing a certain key on the keyboard, making each label
interactive one after the other.
In order to move the interactive labels, the
ALcdGXYInteractiveLabelProvider can dispatch mouse or touch events that happen on the interactive label to the
ILcdGXYView. The active
ILcdGXYController can then receive the events and use those, for example, to move the label with the
ILcdGXYLabelEditor. Which events are dispatched to the view and which events are dispatched to the interactive label is decided by the
dispatchAWTEvent method of the
ALcdGXYInteractiveLabelProvider. By default this method dispatches the event to the view when it happened on a
JPanel. It dispatches all other events to the originating component itself. As a result, when the user clicks and drags on a
JLabel, the controller of the view can handle these events and can, for example, move the label. Clicking on, for example, a JSlider
would as expected drag the knob. You can override this method to customize which events are dispatched to the interactive
label and which are dispatched to the
dispatchMouseEvent is used strictly for mouse events, a more general method is available to dispatch AWT events:
dispatchAWTEvent. Currently this method is called with touch events and mouse events. Mouse events are by default passed to the
dispatchMouseEvent method by the
By default the
TLcdGXYInteractiveLabelsController uses the view itself as the
java.awt.Container that is used to add the interactive label to the user interface. You can customize this by overriding
addComponentToGXYView and related methods. Override this method, for example, if the
ILcdGXYView implementation used in your application does not extend from