The Annealing Label Layout is extensible. The point label
descriptor and the polyline label descriptor are designed to cover
the majority of the cases. In rare situations, you may want to
implement your own label descriptor by subclassing the base class IlvAnnealingLabelDescriptor. This section
describes the necessary steps.
A label descriptor basically specifies the
path where the top-left corner of a label can be placed. For
simplification, it considers the path rolled out such that the path
has only one dimension. If the path is known, the precise label
position can be specified by just one value: the path location. The
Annealing Label Layout proposes different path locations during the
layout; however, it does not know what the path looks like. The
task of the label descriptor is to translate the path location into
concrete (x, y) coordinates of the label.
As an example, we want to create a label
descriptor that can place labels precisely at a triangular
obstacle. We could use the point label descriptor as an
approximation, but it does not place the labels precisely at a
triangular shape.
In the following figure, the upper diagram shows the path around
the triangle (the dashed red and blue line). Below, you can see the
same path rolled out in one dimension. The Annealing Label Layout
may ask the label descriptor to place the label at position 1 to 8.
For the Annealing Label Layout, these positions are just numbers
between 0 and
maxPathLocation
. The task of the label descriptor is to translate these numbers
into the correct positions as shown in the upper part of the
figure.
The base class IlvAnnealingLabelDescriptor. has two
protected data members:
actPathLocation
is the current path location of the label.maxPathLocation
is the maximum value of the path location.
To create a new label descriptor, you need to implement a method
that initializes the path constructor at the beginning of layout.
You should calculate the maximum path location
maxPathLocation
and initialize the
actPathLocation
here. The method is called only once during layout:
void initialize(IlvLabelingModel labelingModel)
In the previous figure, the maximum path
location for an equilateral triangle is:
3 * sidelength + 2 * labelwidth + 2 * labelheight
At each iteration step, the layout calls the method
setPosition
and provides an actual value for the path location. The method
setPosition
should store the value into
actPathLocation
and translate the path location into appropriate (x, y)
coordinates. Then it should call the predefined method
updatePosition(x, y)
with these coordinates:
public void setPosition(double pathLocation, float distFromPath)
{
float x, y;
// make sure the position is between 0 and max
while (pathLocation > maxPathLocation)
pathLocation -= maxPathLocation;
while (pathLocation < 0)
pathLocation += maxPathLocation;
// store the actual position
actPathLocation = pathLocation;
// translate the path location into (x, y)
if (pathLocation < labelwidth + sidelength) {
x = (float)pathLocation;
y = triangleBottom;
} else if (pathLocation < labelwidth + labelheight + sidelength) {
x = labelwidth + sidelength;
y = triangleBottom - (float)pathLocation + labelwidth + sidelength;
} else if ... (other cases) ...
...
// finally, update the internal data structures
updatePosition(x, y);
}
The label may have a preferred position at
the triangle. The Annealing Layout checks a location close to the
preferred position from time to time. You should implement the
following method to return the preferred path location:
double getPreferredPathLocation()
Furthermore, you should implement a strategy
on how to come close to the preferred location. Towards the end of
layout, the algorithm calls the method:
setTowardsPreferredPosition(pathLocation, dist, i, maxI)
to perform a sequence of steps that shift
the label from the current position closer to the preferred
position.
with
i
from
1
to
maxI
. Implement the method so that at each step you calculate a path
location closer to the preferred location. When
i
is
maxI
, it should be exactly at the preferred location. You can call
setPosition
to move the label to the preferred (x, y) position. For instance:
public void setTowardsPreferredPosition( double pathLocation, float dist, int i, int maxI) { double offset = pathLocation - getPreferredPathLocation(); double newLocation = pathLocation - i * offset / maxI; setPosition(newLocation, dist); }
These methods take the
distance
parameter in addition to the path location. This is the distance
from the path. If the label must always be on the path, you can
assume this distance is 0. Set it to a different value only if your
label descriptor allows the label to have a variable offset from
the path.