package flare.vis.legend {
import flare.animate.Transitioner;
import flare.display.RectSprite;
import flare.display.TextSprite;
import flare.scale.Scale;
import flare.scale.ScaleType;
import flare.util.Displays;
import flare.util.Orientation;
import flare.util.Vectors;
import flare.util.palette.ColorPalette;
import flare.util.palette.Palette;
import flare.util.palette.ShapePalette;
import flare.util.palette.SizePalette;
import flare.vis.data.ScaleBinding;
import flash.display.Sprite;
import flash.geom.Rectangle;
import flash.text.TextFormat;
/**
* A legend describing the visual encoding of a data property. Legends
* support both discrete legends that list individual items and
* range legends the convey a continuous range of values. Discrete
* legends consist of a collection of LegendItem instances
* stored in the items sprite. Range legends consist of
* a single LegendRange instance stored in the
* items sprite.
*
*
There are multiple ways to generate a legend. To build a legend
* based on an existing visual encoding, use the static
* fromScale constructor. This method takes a data scale
* and one more or more palettes (e.g., color, shape, or size palettes)
* and uses them to generate an appropriate legend. If the data scale
* is a quantitative scale and only a color palette is provided, a
* continuous range legend will be generated. Otherwise, a discrete
* legend will be created.
*
*
Legends can also be created from a collection of independent
* values using the static fromValues constructor. This
* method takes an Object Vector or Array of legend item descriptions
* and uses them to generate a legend. For example, consider this code:
This example will create a legend with the described values.
* See the documentation for the buildFromValues method
* for more details.
*/
public class Legend extends Sprite
{
/** @private The layout bounds for this legend instance. */
protected var _bounds:Rectangle = null;
/** @private Sprite defining the border of the legend. */
protected var _border:RectSprite;
/** @private Sprite containing the legend items. */
protected var _items:Sprite;
/** @private TextSprite containing the legend title.*/
protected var _title:TextSprite;
/** @private Scale instance used to define the legend mapping. */
protected var _scale:Scale;
/** @private Flag for if this legend is discrete or continuous. */
protected var _discrete:Boolean = true;
/** @private The default color to use for legend items. */
protected var _defaultColor:uint = 0xff888888;
/** @private The color palette used to encode values (may be null). */
protected var _colors:ColorPalette;
/** @private The shape palette used to encode values (may be null). */
protected var _shapes:ShapePalette;
/** @private The size palette used to encode values (may be null). */
protected var _sizes:SizePalette;
/** @private Flag indicating the desired orientation of this legend. */
protected var _orient:String = null;
/** @private Margins within legend items. */
protected var _margin:Number = 4;
/** @private Spacing between legend items. */
protected var _spacing:Number = 0;
/** @private Base icon size */
protected var _baseIconSize:Number = 12;
/** @private TextFormat (font, size, style) of legend item labels. */
protected var _labelTextFormat:TextFormat = new TextFormat("Arial",12,0);
/** @private Label text mode. */
protected var _labelTextMode:int = TextSprite.BITMAP;
/** @private The calculated internal width of the legend. */
protected var _iw:Number;
/** @private The calculated internal height of the legend. */
protected var _ih:Number;
// -- Properties ------------------------------------------------------
/** The layout bounds for this legend instance. */
public function get bounds():Rectangle { return _bounds; }
public function set bounds(b:Rectangle):void { _bounds = b; }
/** Sprite defining the border of the legend. */
public function get border():RectSprite { return _border; }
/** Sprite containing the legend items. */
public function get items():Sprite { return _items; }
/** TextSprite containing the legend title.*/
public function get title():TextSprite { return _title; }
/** Flag indicating if this legend is discrete or continuous. */
public function get discrete():Boolean { return _discrete; }
/** Scale instance used to define the legend mapping. */
public function get scale():Scale { return _scale; }
public function set scale(s:Scale):void { _scale = s; }
/** The legend range, if this legend is continuous. This
* value is null if the legend is discrete. */
public function get range():LegendRange {
return _discrete ? null : LegendRange(_items.getChildAt(0));
}
/** The default color to use for legend items. */
public function get defaultColor():uint { return _defaultColor; }
public function set defaultColor(c:uint):void { _defaultColor = c; }
/** The color palette used to encode values (may be null). */
public function get colorPalette():ColorPalette { return _colors; }
public function set colorPalette(cp:ColorPalette):void { _colors = cp; }
/** The shape palette used to encode values (may be null). */
public function get shapePalette():ShapePalette { return _shapes; }
public function set shapePalette(sp:ShapePalette):void { _shapes = sp; }
/** The size palette used to encode values (may be null). */
public function get sizePalette():SizePalette { return _sizes; }
public function set sizePalette(sp:SizePalette):void { _sizes = sp; }
/** The desired orientation of this legend. */
public function get orientation():String { return _orient; }
public function set orientation(o:String):void { _orient = o; }
/** Margins within legend items. */
public function get margin():Number { return _margin; }
public function set margin(m:Number):void { _margin = m; }
/** Spacing between legend items. */
public function get spacing():Number { return _spacing; }
public function set spacing(s:Number):void { _spacing = s; }
/** Base icon size, corresponding to a size factor of 1. */
public function get baseIconSize():Number { return _baseIconSize; }
public function set baseIconSize(s:Number):void {
if (_baseIconSize != s && _discrete) {
for (var i:uint=0; i<_items.numChildren; ++i) {
var li:LegendItem = LegendItem(_items.getChildAt(i));
li.iconSize *= (s / _baseIconSize);
li.maxIconSize *= (s / _baseIconSize);
}
}
_baseIconSize = s;
}
/** TextFormat (font, size, style) of legend item labels. */
public function get labelTextFormat():TextFormat { return _labelTextFormat; }
public function set labelTextFormat(f:TextFormat):void {
_labelTextFormat = f; updateItems();
}
/** Label text mode. */
public function get labelTextMode():int { return _labelTextMode; }
public function set labelTextMode(mode:int):void {
_labelTextMode = mode; updateItems();
}
// -- Initialization --------------------------------------------------
/**
* Creates a new Legend for the given data field.
* @param dataField the data field to describe with the legend
* @param vis the visualization corresponding to this legend
* @param scale the scale value used to map the data field to visual
* variables
*/
public function Legend(title:String, scale:Scale=null,
colors:ColorPalette=null, shapes:ShapePalette=null,
sizes:SizePalette=null)
{
this.scale = scale;
addChild(_border = new RectSprite(0,0,0,0,13,13));
addChild(_title = new TextSprite());
addChild(_items = new Sprite());
_border.lineColor = 0;
_border.fillColor = 0;
_colors = colors;
_shapes = shapes;
_sizes = sizes;
_title.textField.defaultTextFormat =
new TextFormat("Arial",12,null,true);
if (title != null)
_title.text = title;
else
_title.visible = false;
if (scale != null) {
buildFromScale();
update();
}
}
/**
* Update the legend, recomputing layout of items.
* @param t a transitioner for value updates
* @return the input transitioner
*/
public function update(t:Transitioner=null):Transitioner
{
if (_scale is ScaleBinding && ScaleBinding(_scale).updateBinding())
buildFromScale();
updateItems();
layout(t);
return t;
}
/**
* Builds the contents of this legend from the current scale values.
* This method will remove all items from the legend and rebuild the
* legend using the current scale and palette settings.
*/
public function buildFromScale():void
{
// first, remove all items
while (_items.numChildren > 0) {
_items.removeChildAt(_items.numChildren-1);
}
// determine legend type
var type:String = _scale.scaleType;
if (ScaleType.isQuantitative(type) && !_sizes && !_shapes) {
// build continuous legend
_discrete = false;
if (!_orient) _orient = Orientation.LEFT_TO_RIGHT;
_items.addChild(new LegendRange(_title.text,
_scale, _colors, _orient));
} else {
// build discrete legend
_discrete = true;
if (!_orient) _orient = Orientation.TOP_TO_BOTTOM;
var numVals:int = ScaleType.isQuantitative(type) ? 5 : -1;
var maxSize:Number = Number.MIN_VALUE;
var vals:Vector.