skip to main content
Programmer's documentation > Using graph layout algorithms > Automatic label placement > Defining your own labeling model
 
Defining your own labeling model
Describes how to develop a custom label layout algorithm if you need one.
*The need for a custom label layout algorithm
*Discusses when you may need a custom label layout algorithm.
*The IlvLabelingModel Class
*Describes the methods in the labeling model class and gives a class diagram.
*The IlvLabelingModelWithRotation Interface
*Describes the methods in the rotation interface for the labeling model.
*Subclassing the default labeling model
*Describes the default labeling model and how to subclass it if necessary.
*Creating a new labeling model
*Explains when and how to create a new labeling model.
The need for a custom label layout algorithm
NOTE Before reading this section, you should be familiar with the IlvLabelingModel class (see Labels and obstacles in Java).
It is sometimes necessary to add label layout features to an existing application.
If the application uses a diagram component with styling, using the Annealing Label Layout is a straightforward process. You use the internal labeling model of the label layout renderer, but you do not need to worry about the details.
If your application is not based on styling, but already uses the class IlvManagerLayer to manipulate and display labels and obstacles, you can use and adapt the default labeling model ( IlvDefaultLabelingModel).
Even if the application uses data structures that are independent of the Rogue Wave® JViews data structures, it is possible to apply a supplied label layout algorithm.
If you need to define your own labeling model, create a subclass of IlvLabelingModel. If the layout is to support rotated labels, your subclass must additionally implement the interface IlvLabelingModelWithRotation.
The IlvLabelingModel Class
The methods defined in the IlvLabelingModel class can be divided into several categories: those that provide information on the structure of the labels and obstacles, on their geometry, on their overlap penalty, and notification of changes in the manager.
They are described in the following sections:
*Information on the structure of labels and obstacles
*Information on the geometry of labels and obstacles
*Overlap calculation
*Notification of changes
*Storing and retrieving object properties
Class diagram for IlvLabelingModel
Information on the structure of labels and obstacles
The following methods of the IlvLabelingModel class allow the layout algorithms to retrieve information on the labels:
Decides whether an object is a label.
Enumerates all existing labels.
Returns the number of labels that exist.
The following methods allow the layout algorithms to retrieve information on the obstacles in a similar way:
 
boolean isObstacle(Object obj)
 
boolean isPolylineObstacle(Object obj)
 
Enumeration getObstacles()
 
int getObstaclesCount()
For optimization purposes, the labeling model distinguishes between normal obstacles and polyline obstacles. While normal obstacles cover the major part of their bounding box, polyline obstacles have a line width and range over intermediate points; thus they cover only a small part of their bounding box.
Since a polyline obstacle is an obstacle, both isObstacle and isPolylineObstacle return true for a polyline obstacle.
Information on the geometry of labels and obstacles
For labels and obstacles, the label layout can retrieve the bounding box with the method:
 
IlvRect boundingBox(Object labelOrObstacle)
For the special polyline obstacles, the label layout can retrieve the precise shape of the polyline with the methods:
 
float getPolylineWidth(Object polylineObstacle)
 
IlvPoint[] getPolylinePoints(Object polylineObstacle)
The following method moves a label to the new position.
 
void moveLabel(Object label, float x, float y, boolean redraw)
Overlap calculation
A good label layout avoids overlaps. Thus, the calculation of overlap values is an important step of the algorithm. The speed of the layout algorithm depends crucially on the speed of the overlap calculation. The labeling model provides the following methods for overlap calculations:
Calculates the overlap between two labels.
Calculates the overlap between a label and a normal obstacle.
Calculates the overlap between a label and a polyline obstacle.
These methods return a positive penalty value that indicates how much the objects overlap.
*The returned value is 0 if the objects do not overlap.
*A smaller overlap value designates less overlap than a larger overlap value. The actual number is arbitrary and depends on the implementer of the labeling model. For example, if all objects are rectangles, then it could be the size of the overlapping area of the rectangles.
Typically the overlap value is calculated before the label is moved. It is calculated for a speculative label position, not for the real label position. Hence, the speculative bounding box of the label is passed as an argument. Similarly, the bounding box (or polyline shape) of the obstacle is passed as an argument as well. The meaning of the returned value is the overlap penalty if the label were placed at its passed bounding box, and the obstacle were placed at its passed bounding box.
If the overlap methods return a positive nonzero penalty only when the speculative bounding boxes overlap, then the following method can return true:
 
boolean isBoundingBoxDependent()
This method exists mainly for quadtree optimization purposes.
Notification of changes
The following methods of the IlvLabelingModel class allow a layout algorithm to be notified of changes in the data structures:
 
void addLabelingModelListener(LabelingModelListener listener)
 
void removeLabelingModelListener(LabelingModelListener listener)
 
void fireLabelingModelEvent(Object obstacleOrLabel, int eventType, boolean
adjusting)
 
void fireLabelingModelEvent(LabelingModelEvent event)
A “change” can be a structural change (that is, a label or obstacle was added or removed) or a geometrical change (that is, a label or obstacle was moved or reshaped). The event type is typically a bitwise-Or of the bit masks defined in the class LabelingModelEvent. For instance, when a label was removed, an event with type (STRUCTURE_CHANGED | LABEL_REMOVED) is fired and the removed label is stored in the event. The labeling model event listener mechanism provides a means to keep the layout algorithms informed of these changes. When the layout algorithm is restarted on the same graph, it is able to detect whether the data structures have changed since the last time the layout was successfully performed. If necessary the layout can be performed again. If there was no change, the layout algorithm can avoid unnecessary work by not performing the layout.
The labeling model event listener is defined by the LabelingModelListener interface. To receive the events (that is, instances of the LabelingModelEvent class), a class must implement the LabelingModelListener interface and must register itself using the addLabelingModelListener method of the IlvLabelingModel class.
NOTE The label layout algorithms register themselves as listeners to the labeling model (via the functionality in IlvLabelLayout ). Therefore, there is usually no need to manipulate these listeners directly.
Storing and retrieving object properties
The following methods of the IlvLabelingModel class allow a layout algorithm to store data objects for each label or obstacle:
 
void setProperty(Object labelOrObstacle, String key, Object value)
 
Object getProperty(Object labelOrObstacle, String key)
 
void setProperty(String key, Object value)
 
Object getProperty(String key)
The layout algorithm may need to associate a set of properties with the labels or obstacles, or global properties. Properties are a set of key-value pairs, where the key is a String object and the value can be any kind of information value.
The IlvLabelingModelWithRotation Interface
The methods of the IlvLabelingModel class are designed to support upright rectangular labels. If rotated rectangular labels occur in your application, the labeling model must additionally implement the interface IlvLabelingModelWithRotation.
In IlvLabelingModelWithRotation, all labels are considered rectangles that can be rotated around the center of the label. Hence, the method boundingBox of the labeling model returns the bounding box of the unrotated label, not the bounding box of the rotated label.
The IlvLabelingModelWithRotation interface assumes that the rotation of the label depends on the position of the label. For example, if the label is placed close to link segments, the label should be rotated according to the link segments.
Rotations
You can retrieve the rotation of the label with the method:
 
double getRotation(Object label, IlvRect positionRectangle)
You can set the rotation of the label with the method:
 
void setRotation(Object label, double angle)
The angles are in degrees.
The method setRotation(Object label, double angle) is called by the Annealing Label Layout at the end of layout to inform the label about the final rotation calculated by the layout. If the label is attached to a link and always follows the rotation of the link automatically, it will not be necessary to inform the label about the final rotation. Therefore, setRotation( Object label, double angle ) can be empty, but getRotation( Object label, IlvRect positionRectangle ) must return the rotation of the link segment when the label is placed at positionRectangle.
Overlap calculations
The IlvLabelingModelWithRotation interface offers methods for calculating the overlaps of rotated labels. These methods have as additional parameter an angle for the label parameters:
Methods for Calculating Overlaps of Rotated Labels
 
   double getLabelOverlap(Object label1, IlvRect rect1,
       double angle1, Object label2, IlvRect rect2,
       double angle2, float minDist);
 
   double getObstacleOverlap(
       Object label, IlvRect labelRect, double angle,
       Object obstacle, IlvRect obstacleBBox, float minDist);
 
   double getPolylineObstacleOverlap(
       Object label, IlvRect labelRect, double angle,
       Object polylineObstacle, IlvPoint[] pts,
       float lineWidth, float minDist);
For each label parameter, an unrotated rectangle ( labelRect ) is passed, which defines the speculative position of the label and a rotation angle.
The meaning of the returned value is the overlap penalty if the label were placed at labelRect and rotated by the angle and if the obstacle were placed at obstacleBBox. The penalty 0 means that the objects do not overlap.
Subclassing the default labeling model
The default labeling model IlvDefaultLabelingModel is a subclass of IlvLabelingModel. It has certain properties that may not be suitable for your application:
*Only objects of type IlvLabel and IlvZoomableLabel are considered labels.
*Only objects of type IlvLine, IlvPolyline, and IlvLinkImage are considered polyline obstacles.
*All other objects are considered rectangular obstacles.
It is easy to change these properties by subclassing IlvDefaultLabelingModel. For instance, if you use only labels of class MySpecialLabel, you can override the method isDefaultLabelClass:
 
public boolean isDefaultLabelClass(Object obj)
{
    return (obj instanceof MySpecialLabel);
}
If you want some objects to be completely ignored, make sure they are not considered as labels or obstacles. To avoid considering objects of class IgnorableGraphic as obstacles, you can override the method isObstacle:
 
public boolean isDefaultObstacleClass(Object obj)
{
    return super.isDefaultObstacleClass(obj) &&
           !(obj instanceof IgnorableGraphic);
}
NOTE Instead of overriding isDefaultLabelClass and isDefaultObstacleClass, you can also specify which objects are labels and obstacles by using the methods setLabel and setObstacle, as illustrated in Labels and obstacles in Java.
Some obstacles do not have a rectangular shape. For simplicity and speed, the overlap value is based on the bounding box of normal obstacles. Hence the default labeling model may compute overlaps in situations where in fact there are no overlaps, because the bounding box is usually a little larger than the area that is really covered by the obstacle. You can correct this by overriding the method getOverlapValue by implementing a more precise (but also more complex) overlap test:
 
public double getObstacleOverlap(
    Object label, IlvRect labelBBox,
    Object obstacle, IlvRect obstacleBBox,
    float minDist)
  {
    ... complex calculation of the overlap considering the precise shape of
... the obstacle. If the label is closer than minDist to the obstacle,
... it should be considered as overlap
    ...
    if (hasOverlap)
        return a value proportional to the overlap;
    else
        return 0.0;
   }
Since the default labeling model IlvDefaultLabelingModel implements the IlvLabelingModelWithRotation interface, you can similarly override all overlap methods that have a rotation angle parameter for the labels. See The IlvLabelingModelWithRotation Interface.
Creating a new labeling model
The default labeling model is suitable only if the underlying data structure is an IlvManager. The case may arise when an application uses its own classes and when, for some reason, you do not want to replace these classes with Rogue Wave® JViews classes such as IlvManager and IlvLabel. Here, you cannot use the default labeling model. To enable the label layout algorithms to work with these data structures, you must write a custom labeling model (that is, a subclass of IlvLabelingModel ).
The custom labeling model must implement all the abstract methods of the IlvLabelingModel class. The nonabstract methods of this class have a default implementation that is functional. However, they may not be optimal because they do not take advantage of the characteristics of the underlying graph implementation. In this case, they can be overridden as well. The efficiency of the layout algorithm depends directly on the efficiency of the implementation of the labeling model and the underlying data structure.
The following minimum set of methods must be implemented:
 
abstract Enumeration getLabels()
 
abstract boolean isLabel(Object obj)
 
abstract void moveLabel(Object label, float x, float y, boolean redraw)
 
abstract double getLabelOverlap(Object label1, IlvRect bbox1, Object label2,
IlvRect bbox2, float minDist)
 
abstract Enumeration getObstacles()
 
abstract boolean isObstacle(Object obj)
 
abstract double getObstacleOverlap(Object label, IlvRect labelBBox, Object
obstacle, IlvRect obstacleBBox, float minDist)
 
abstract IlvRect boundingBox(Object labelOrObstacle)
These methods are described in The IlvLabelingModel Class.
If the label layout algorithm is to support rotated labels, the new labeling model must additionally implement the interface IlvLabelingModelWithRotation.

Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.