You added multiple labels to an object in a Lightspeed view, but only one of these labels appears. What is going on ?
What happens?
Say that you want to add a label at the start and end of a line. In such a case, it is easy to end up with the following code:
public class MultipleLabelsStyler extends ALspLabelStyler {
@Override
public void style(Collection<?> aObjects, ALspLabelStyleCollector aStyleCollector, TLspContext aContext) {
for (Object object : aObjects) {
// Calculate start and end points...
...
// Add labels for the start and end point
aStyleCollector.object(object).geometry(startPoint).styles(labelStyles1).submit();
aStyleCollector.object(object).geometry(endPoint).styles(labelStyles2).submit();
}
}
}
Why does this happen?
Each label needs a unique identifier. This is reflected in the TLspLabelID
class. A label is identified by its:
-
Layer
-
Paint representation. In 99% of the cases, the paint representation is LABEL.
-
Domain object
-
Sub-label ID
When these 4 fields are the same for two labels, the labels are considered identical. This is exactly what happens in the
styler of the example above. Because ALspLabelStyleCollector.label()
is not called explicitly, LuciadLightspeed assigns a default sub-label ID to the label. This happens for both labels, so
they end up with the same default sub-label ID. The layer, paint representation, and domain object identifiers are the same
as well, so there is no way for LuciadLightspeed to know that they represent distinct labels.
How can you fix it?
In short, you can fix this problem by assigning a unique identifier to each label of the same object. For the example above, such an approach results in the following code:
public class MultipleLabelsStyler extends ALspLabelStyler {
@Override
public void style(Collection<?> aObjects, ALspLabelStyleCollector aStyleCollector, TLspContext aContext) {
for (Object object : aObjects) {
// Calculate start and end points...
...
// Add labels for the start and end point, using different sub-label IDs for each label
aStyleCollector.object(object).label("start").geometry(startPoint).styles(labelStyles1).submit();
aStyleCollector.object(object).label("end").geometry(endPoint).styles(labelStyles2).submit();
}
}
}
One object label gets the start
sub-label ID while the other gets the end
sub-label ID. As you can see in the code, you only need to assign distinct sub-label IDs if an object has multiple labels.
This means that a sub-label ID can be used repeatedly for multiple objects.
What makes up a good sub-label id?
Good sub-label IDs fulfill a number of conditions:
-
They must uniquely identify each of of the labels for the same object,paint representation, and layer.
Not doing so makes labels disappear. -
They must be immutable and must properly implement the
equals()
andhashCode()
methods.
Not doing so causes memory leaks and flickering labels. -
They must be equal for the same label in subsequent
ALspLabelStyler.style()
calls.
Not doing so causes flickering labels. -
They must be suitable for use as a cache key, meaning that they should not hold references to domain objects,layers,the view, and so on.
Not doing so causes memory leaks.
Good candidates for sub-label IDs are String
or Integer
objects, but you can use more complex objects as well. In many cases, it is useful to add information to the sub-label ID
about the object receiving the labels. For example, when you are adding labels to grid lines, the sub-label ID could look
as follows:
public class GridLineSublabelID {
private final double fCoordinate;
private final boolean fHorizontal;
public GridLineSublabelID(double aCoordinate, boolean aHorizontal) {
fCoordinate = aCoordinate;
fHorizontal = aHorizontal;
}
public double getCoordinate() {
return fCoordinate;
}
public boolean isHorizontal() {
return fHorizontal;
}
@Override
public boolean equals(Object o) {
...
}
@Override
public int hashCode() {
...
}
}
The coordinate and orientation information in this sub-label ID can be used to determine the label text or the labeling algorithm for grid line labels, for example.