public class TLcdGXYContinuousLabelingAlgorithm extends Object implements ILcdGXYLabelingAlgorithm
ILcdGXYLabelingAlgorithm
that offers continuously decluttered
labels, ideally suited for labeling moving objects (e.g., tracks).
When the objects move, the labels move along and avoid overlap in a continuous fashion by moving the labels around gently. The locations of the labels are not limited to a fixed number of possible locations, instead a label can be put everywhere on the map. By default there is no hard constraint on the distance between the label and the object. The labels do tend to move to their desired label location though.
Using this class, the desired location of the labels can be specified for all labels at once or for every label individually.
Which labels are removed in the labeling process is defined by the
label coverage property. Which labels should
be dropped first is influenced by the order in which labels are retrieved from the label infos
object, see TLcdCollectedLabelInfoList.getLabels()
. The default
is to never drop any labels.
setPadding(double)
to add space between labels.setReuseLocationsScaleRatioInterval(com.luciad.util.ILcdInterval)
to keep the relative location after zooming in the view.setClampOnScreenEdges(boolean)
to specify if labels can be partially outside the view.setLabelMovementBehavior(com.luciad.realtime.gxy.labeling.TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior)
to specify when labels should be moved.setMinDistance(float)
to ensure labels are never too close to their object.setMaxDistance(float)
to ensure labels are never too far from their object.setDisallowedAngle(float)
to specify where a label can be positioned around its object.
This algorithm uses the obstacles and placements from the given ILcdLabelConflictChecker
to avoid placing labels at these locations.
For smooth visual results, the layer/view using this painter should be refreshed regularly.
This can for example be done by using the TLcdSimulator
, or by
refreshing the map using a timer, e.g. for a gxy layer/view :
Timer timer = new Timer( 50, new ActionListener() {
public void actionPerformed( ActionEvent e ) {
ILcdGXYView view = ...;
ILcdGXYLayer track_layer = ...;
view.invalidateGXYLayer( track_layer, true, this, "Invalidating track layer" );
}
}
} );
timer.setRepeats( true );
timer.start();
Using such a timer, it might be important with respect to performance to set
the correct number of background layers (see ILcdGXYView.getNumberOfCachedBackgroundLayers()
).
The TLcdSimulator
does this automatically (see TLcdSimulator.isManageNumberOfCachedBackgroundLayers()
).
Notes:
ILcdGXYLabelPainter
of the layer implements
ILcdGXYLabelPainter2
as it can only declutter labels if they can be
placed freely, not when placement is limited to a fixed number of locations.ILcdGXYLabelPainter2
need to be independent of the location
of the label for this algorithm to work well. Therefore the option setConsiderPinForBounds
of TLcdGXYStampLabelPainter
is not supported in combination with this algorithm, as the
pin varies in size depending on the label location.Modifier and Type | Class and Description |
---|---|
static class |
TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior
Indicates when labels should be moved.
|
Constructor and Description |
---|
TLcdGXYContinuousLabelingAlgorithm() |
Modifier and Type | Method and Description |
---|---|
TLcdGXYContinuousLabelingAlgorithm |
clone()
Makes
Object.clone() public. |
TLcdCollectedLabelInfoList |
collectLabelInfo(List<TLcdLabelIdentifier> aLabelsToCollect,
Graphics aGraphics,
ILcdGXYView aGXYView)
This method returns a
TLcdCollectedLabelInfoList object based
on the given list of TLcdLabelIdentifier s. |
List<TLcdLabelPlacement> |
computeLabelPlacements(TLcdCollectedLabelInfoList aLabelInfoList,
ILcdLabelConflictChecker aLabelConflictChecker,
ILcdGXYView aView)
This method computes a list of
TLcdLabelPlacement s using the given
TLcdCollectedLabelInfoList . |
Point |
getDesiredRelativeLocation()
Returns the desired relative location.
|
float |
getDisallowedAngle()
Returns the size of the disallowed range.
|
TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior |
getLabelMovementBehavior()
Indicates when labels are moved.
|
ILcdGXYMultiLabelPriorityProvider |
getLabelPriorityProvider()
Returns the label placing priority provider.
|
ILcdCollectedLabelInfoDependencyProvider |
getMasterSlaveDependencyProvider()
Returns the dependency provider that defines the master-slave dependency between labels.
|
int |
getMaxDeclutterTime()
Returns the maximum declutter time.
|
float |
getMaxDistance()
Returns the configured maximum allowed distance.
|
double |
getMaxLabelCoverage()
Returns the maximum label coverage ratio.
|
float |
getMinDistance()
Returns the configured minimum allowed distance.
|
protected double |
getOrientation(Object aDomainObject)
Retrieves a domain object's orientation.
|
double |
getPadding()
Returns the padding around the labels.
|
ILcdInterval |
getReuseLocationsScaleRatioInterval()
Returns the scale ratio interval in which label locations are reused.
|
boolean |
isClampOnScreenEdges()
Indicates whether labels are clamped against screen edges.
|
protected void |
retrieveDesiredLabelLocation(Graphics aGraphics,
ILcdGXYContext aGXYContext,
Object aObject,
int aLabelIndex,
int aSubLabelIndex,
Point aRelativeLocationSFCT)
Retrieves the desired label location for the given object, label and sub
label index.
|
void |
setClampOnScreenEdges(boolean aEnabled)
Enable or disable clamping labels against screen edges.
|
void |
setDesiredRelativeLocation(Point aDesiredRelativeLocation)
Sets the desired relative label location: the location the labels tend to move to.
|
void |
setDisallowedAngle(float aArcAngle)
Set a range, relative to the object's heading, where labels cannot be placed.
|
void |
setLabelMovementBehavior(TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior aBehavior)
Indicate when labels should be moved.
|
void |
setLabelPriorityProvider(ILcdGXYMultiLabelPriorityProvider aLabelPriorityProvider)
Sets the label placing priority provider, specifying the priority for individual
labels.
|
void |
setMasterSlaveDependencyProvider(ILcdCollectedLabelInfoDependencyProvider aDependencyProvider)
Sets the dependency provider that defines the master-slave dependency between labels.
|
void |
setMaxDeclutterTime(int aMaxDeclutterTime)
Sets the maximum time (in ms) that should be spend in decluttering the
labels.
|
void |
setMaxDistance(float aMaximumDistance)
Set the maximum allowed distance of a label to its anchor point.
|
void |
setMaxLabelCoverage(double aMaximumLabelCoverage)
Sets the maximum label coverage ratio.
|
void |
setMinDistance(float aMinimumDistance)
Set the minimum allowed distance of a label to its anchor point.
|
void |
setPadding(double aPadding)
Sets the padding around the labels.
|
void |
setReuseLocationsScaleRatioInterval(ILcdInterval aReuseLocationsScaleRatioInterval)
Sets the scale ratio interval in which the label locations are reused.
|
public void setLabelMovementBehavior(TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior aBehavior)
MINIMAL_MOVEMENT
to avoid moving labels as much as possibleREDUCED_MOVEMENT
to avoid moving labels when they are near other labels, but keep them close to their objectOPTIMAL_SPREAD
to ensure an optimal spread of labels
The default is OPTIMAL_SPREAD
.
aBehavior
- The desired behaviorpublic TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior getLabelMovementBehavior()
setLabelMovementBehavior(com.luciad.realtime.gxy.labeling.TLcdGXYContinuousLabelingAlgorithm.LabelMovementBehavior)
public void setMinDistance(float aMinimumDistance)
If set, a label cannot move closer to its anchor point than this distance. The distance is calculated from the point of the label closest to the anchor.
By default, the minimum distance is disabled (0
).
aMinimumDistance
- the minimum distance, in pixels.public float getMinDistance()
setMaxDistance(float)
public void setMaxDistance(float aMaximumDistance)
If set, a label cannot move further from its anchor point than this distance. The distance is calculated from the point of the label closest to the anchor.
By default, the maximum distance is disabled (Float#POSITIVE_INFINITY
).
aMaximumDistance
- the maximum distance, in pixels.public float getMaxDistance()
setMaxDistance(float)
public void setDisallowedAngle(float aArcAngle)
If set, a label cannot move inside the disallowed area. A label's arc is calculated between its center of the label, the anchor point and the orientation.
For example, to ensure a label is never "in front of" its object, use setDisallowedAngle(120)
.
Domain objects must implement ILcdOriented
to use this feature, or you can override getOrientation(Object)
to use a custom implementation.
By default, the disallowed range is disabled (0 degrees).
aArcAngle
- The size of the range in which labels should not be positioned, in degrees, spread evenly around the object's orientation.public float getDisallowedAngle()
setDisallowedAngle(float)
public void setClampOnScreenEdges(boolean aEnabled)
If enabled, labels for visible objects are always fully visible. If disabled, labels can be partially outside the view.
By default, this is enabled.
aEnabled
- true
if it should be enabled, false
otherwise.public boolean isClampOnScreenEdges()
true
if enabled, false
otherwise.setClampOnScreenEdges(boolean)
public ILcdInterval getReuseLocationsScaleRatioInterval()
setReuseLocationsScaleRatioInterval(com.luciad.util.ILcdInterval)
public void setReuseLocationsScaleRatioInterval(ILcdInterval aReuseLocationsScaleRatioInterval)
ALcdLabelLocations
).
If large changes in
scale
occur (e.g., zooming
in/out), trying to reuse those locations is no longer useful as visual
contact with the labels is lost anyway. Using this interval, one can
specify for which scale changes the label locations should be reused,
using a lower and an upper ratio.
For example setting this value to new TLcdInterval(0.8, 1.2)
means that the label locations are reused if new scale (e.g., after
zooming) divided by the old scale (e.g., before zooming) is within the
interval [0.8, 1.2].
If this interval is set to null
, label locations are never
reused.aReuseLocationsScaleRatioInterval
- The scale ratio interval.public double getPadding()
setPadding(double)
public void setPadding(double aPadding)
Sets the padding around the labels. This padding is added to the bounds of the labels, enlarging it during placement.
In practice this means that the bigger the padding, the more space between the labels.
By default, this value is 0.0
.
aPadding
- the padding, in pixels.IllegalArgumentException
- when the given padding is smaller than 0
.getPadding()
public Point getDesiredRelativeLocation()
setDesiredRelativeLocation(java.awt.Point)
public void setDesiredRelativeLocation(Point aDesiredRelativeLocation)
new Point( 10, 10 )
, all labels (that is, their anchors)
tend to move to an offset of (10, 10) to the object they belong to.
See also retrieveDesiredLabelLocation(java.awt.Graphics, com.luciad.view.gxy.ILcdGXYContext, java.lang.Object, int, int, java.awt.Point)
.aDesiredRelativeLocation
- The desired location relative to the anchor point of the object.ILcdGXYPainter.anchorPointSFCT(java.awt.Graphics, int, com.luciad.view.gxy.ILcdGXYContext, java.awt.Point)
,
ILcdGXYLabelPainter2.labelAnchorPointSFCT(java.awt.Graphics, int, com.luciad.view.gxy.ILcdGXYContext, java.awt.Point)
protected void retrieveDesiredLabelLocation(Graphics aGraphics, ILcdGXYContext aGXYContext, Object aObject, int aLabelIndex, int aSubLabelIndex, Point aRelativeLocationSFCT)
getDesiredRelativeLocation
.
Override this method to specify the desired location differently for every
label. This for example can be used to avoid that labels would like to be
at the location where a track is heading to.aGraphics
- The graphics involved in the paint operation.aGXYContext
- The context containing the layer, the view and the transformations.aObject
- The object.aLabelIndex
- The label index, 0 if no ILcdGXYMultiLabelPainter is used.aSubLabelIndex
- The sub label index, 0 if no ILcdGXYMultiLabelPainter is used.aRelativeLocationSFCT
- The location this method adapts as a side effect, relative to the anchor point
of the object.ILcdGXYPainter.anchorPointSFCT(java.awt.Graphics, int, com.luciad.view.gxy.ILcdGXYContext, java.awt.Point)
,
ILcdGXYLabelPainter2.labelAnchorPointSFCT(java.awt.Graphics, int, com.luciad.view.gxy.ILcdGXYContext, java.awt.Point)
public double getMaxLabelCoverage()
setMaxLabelCoverage(double)
public void setMaxLabelCoverage(double aMaximumLabelCoverage)
It is defined as the surface used by the labels (approximated by their bounds)
divided by the total surface of the view, so typical values are in the range of ]0,1].
To for example start dropping labels if more than 30% of the view would be covered
by labels, set this property to 0.3. A value of for example 2 means the labels can
use the available screen space twice (so possibly a lot of overlap). Set it to
Double.MAX_VALUE
to never drop any labels (the default value).
aMaximumLabelCoverage
- The maximum label coverage, must be larger than or equal to 0.public int getMaxDeclutterTime()
setMaxDeclutterTime(int)
public void setMaxDeclutterTime(int aMaxDeclutterTime)
aMaxDeclutterTime
- The maximum time that can be spend in decluttering, in ms.public ILcdCollectedLabelInfoDependencyProvider getMasterSlaveDependencyProvider()
setMasterSlaveDependencyProvider(com.luciad.view.labeling.algorithm.ILcdCollectedLabelInfoDependencyProvider)
public void setMasterSlaveDependencyProvider(ILcdCollectedLabelInfoDependencyProvider aDependencyProvider)
The master-slave dependency is defined as follows. The slave always follows the master label. If the master label is translated, the slave label is translated by the same amount. If the master label is sticky, so is the slave label, and vice-versa.
aDependencyProvider
- the dependency provider that defines the master-slave dependency
between labels. Should never be null
.public ILcdGXYMultiLabelPriorityProvider getLabelPriorityProvider()
setLabelPriorityProvider(com.luciad.view.gxy.ILcdGXYMultiLabelPriorityProvider)
public void setLabelPriorityProvider(ILcdGXYMultiLabelPriorityProvider aLabelPriorityProvider)
When no label priority provider is set, a default label priority provider is used
which returns Integer.MAX_VALUE
for all labels.
aLabelPriorityProvider
- The priority provider to set.public TLcdGXYContinuousLabelingAlgorithm clone()
ILcdCloneable
Makes Object.clone()
public.
java.lang.Object
, it can be implemented like this:
public Object clone() {
try {
return super.clone();
} catch ( CloneNotSupportedException e ) {
// Cannot happen: extends from Object and implements Cloneable (see also Object.clone)
throw new RuntimeException( e );
}
}
clone
in interface ILcdCloneable
clone
in class Object
Object.clone()
public TLcdCollectedLabelInfoList collectLabelInfo(List<TLcdLabelIdentifier> aLabelsToCollect, Graphics aGraphics, ILcdGXYView aGXYView)
ILcdGXYLabelingAlgorithm
TLcdCollectedLabelInfoList
object based
on the given list of TLcdLabelIdentifier
s. The returned
TLcdCollectedLabelInfoList
should contain a TLcdCollectedLabelInfo
object for each label that should be placed.
Each TLcdCollectedLabelInfo
should also contain all information needed by
computeLabelPlacements
to place the labels. In order to enable
correct asynchronous label placing, all calls to the layer and the (label) painters should be
done in this method. The results should then be stored in the info objects.
It is possible that the returned TLcdCollectedLabelInfoList
doesn't contain a
TLcdCollectedLabelInfo
object for every given TLcdLabelIdentifier
.
In that case the missing labels are not placed.
collectLabelInfo
in interface ILcdGXYLabelingAlgorithm
aLabelsToCollect
- the labels for which a TLcdCollectedLabelInfoList
should
be created.aGraphics
- the graphics.aGXYView
- the view.TLcdCollectedLabelInfoList
which contains all the information needed to
place the labels.protected double getOrientation(Object aDomainObject)
setDisallowedAngle(float)
).
The result must be expressed as an azimuth, in degrees, clockwise starting from 12 o'clock.
Return Double.NaN
if no orientation can be calculated or if it is not applicable.
The default implementation returns an orientation if the object is ILcdOriented
, or Double.NaN
otherwise.
aDomainObject
- the domain objectILcdOriented
,
setDisallowedAngle(float)
public List<TLcdLabelPlacement> computeLabelPlacements(TLcdCollectedLabelInfoList aLabelInfoList, ILcdLabelConflictChecker aLabelConflictChecker, ILcdGXYView aView)
ILcdGXYLabelingAlgorithm
TLcdLabelPlacement
s using the given
TLcdCollectedLabelInfoList
. The returned label placements
should contain a valid TLcdLabelLocation
and label bounds. They should also point
to their corresponding TLcdCollectedLabelInfo
, which in turn should point to its
corresponding TLcdCollectedLabeledObjectInfo
.
The given TLcdCollectedLabelInfoList
should always be an object created or returned
by collectLabelInfo
.
The returned List
of TLcdLabelPlacement
s contains label placements
that are either visible or invisible. When a placement is present in the list, it means that
the location should be stored. When a placement in the list is marked as visible, it means that
its location should be marked as to-be-painted.
The labels should by preference be placed in the order in which the labels are returned by
TLcdCollectedLabelInfoList.getLabels()
.
computeLabelPlacements
in interface ILcdGXYLabelingAlgorithm
aLabelInfoList
- the info object that contains all information needed to place the labels.aLabelConflictChecker
- the bounds conflict checker that can be used to detect conflicts between labels.aView
- the view.