This article describes more advanced labeling topics. The labeling process starts with explaining the labeling process.
The labeling process
Figure 1, “Labeling sequence diagram” shows the typical workflow of labeling domain objects in a view.
When the view receives a paint request through the method paintGXYView()
,
it asks the view label placer to compute positions for all layers. The view label placer collects relevant label information
(for example, by querying the layer) and decides which labels get painted and where they are painted.
This information is stored in the layer’s label locations. After this, the view loops over all of its layers and asks each
layer to paint their labels. In its turn the layer queries its stored label locations. For each label, a label painter is
retrieved and asked to paint the label.
Using label painters and label editors programmatically
For some reasons you might want to access a label painter or label editor programmatically, for example to retrieve the bounds
of a label, or to manually paint or edit a label. After setting the domain object on the painter and/or editor using setObject
, the following information is required:
-
the label index of the label to work with, in case your domain object requires multiple labels (see
setLabelIndex
). It is up to the label painter implementation to determine how many labels it allows to paint (seegetLabelCount
). -
the sub label index of the label to work with, in case your domain object requires multiple labels (see
setSubLabelIndex
). It is up to the label painter implementation to determine how many sublabels it allows to paint (seegetSubLabelCount
). -
the location of the label to work with (see
setLabelLocation
). The location instance is used to position the label by the label painter. It is adjusted by the label editor in theeditLabel
method.
If you want to reuse the label location stored in the ILcdGXYEditableLabelsLayer
, as determined, for example, by the view label placer, you can retrieve it from the layer’s label location using ALcdLabelLocations.getLabelLocationSFCT
, as described in Storing and retrieving label locations. To store a label location that has been changed by a label editor use ALcdLabelLocations.putLabelLocation
.
Program: Determining the location of the labels shows how to access an ILcdGXYLabelPainter2
to determine the bounds of a label.
// obtain the location information from the ALcdLabelLocations instance labelLocations.getLabelLocationSFCT(domain_object, labelIndex, subLabelIndex, view, labelLocation); // let the label painter interpret this information ILcdGXYLabelPainter label_painter = layer.getGXYLabelPainter(domain_object); if (label_painter instanceof ILcdGXYLabelPainter2) { ILcdGXYLabelPainter2 label_painter_2 = (ILcdGXYLabelPainter2) label_painter; label_painter_2.setLabelIndex(labelIndex); label_painter_2.setSubLabelIndex(subLabelIndex); label_painter_2.setLabelLocation(labelLocation); label_painter_2.labelBoundsSFCT(graphics, mode, context, rectangleSFCT); // rectangleSFCT now contains the location (in view coordinates) of the label }
Implementing your own label painter
To implement an ILcdGXYLabelPainter2
, you need to support the following major operations:
-
labelBoundsSFCT
: calculates the bounds and rotation of the label. The bounds of the label is stored in the givenRectangle
and the rotation is returned as adouble
. -
paintLabel
: actually paints the label. -
isLabelTouched
: allows interaction with the label. -
labelAnchorPointSFCT
: allows to retrieve an anchor point of the label for other application objects to use, for example as a base position for depending labels (see Labeling labels).
Optionally, you can provide snap targets for other editors to snap to with the supportLabelSnap
and labelSnapTarget
methods.
Implementing your own label editor
The most important method when implementing an ILcdGXYLabelEditor
is editLabel
. This method is responsible for changing the label location in response to the user interaction. The information about the
input is available in the ILcdGXYContext
instance that is passed as a parameter. How this input is interpreted depends on the mode that is passed as parameter.
It is the task of the editor to apply the information that is given in the context and the mode to the TLcdLabelLocation
instance that was previously set on it. The class that asked the editor to apply the changes can then retrieve the TLcdLabelLocation
instance. Thus, it is not the responsibility of the ILcdGXYLabelEditor
to persist the changes, for example in an ALcdLabelLocations
instance, but it is up to the client of the label editor to do this.
Because the label painter provides the visual indication while the user is editing the label, the implementation of the label editor is usually tightly coupled with the implementation of the label painter. This is similar to the situation with the painters and editors used to paint/edit the domain objects as described in Using an ILcdGXYEditor.
Implementing your own placement algorithm
To completely customize the label placement in your application, you can write your own ILcdGXYLabelingAlgorithm
. A labeling algorithm has two major steps:
-
collect relevant label information (see the
collectLabelInfo
method) -
determine label positions (see the
computeLabelPlacements
method)
Because the labeling itself may take place asynchronously, it is important to collect all relevant information that cannot be accessed asynchronously in the collect step.
Because labeling is often about iterating over a number of possible positions and checking if they overlap, a special class
ALcdGXYDiscretePlacementsLabelingAlgorithm
is provided to support this. To implement this algorithm, you need to implement the following:
-
an iterator for the possible labels
-
an iterator for the possible label positions of each label
-
an evaluator that checks if a label position is qualified for placement. Usually an evaluator checks if a label overlaps with the already placed labels using an
ILcdLabelConflictChecker
.
The labels.createalgorithm.*
sample shows how to create a simple ALcdGXYDiscretePlacementsLabelingAlgorithm
that places all labels on the object’s anchor point, avoiding overlapping labels.