first commit
This commit is contained in:
372
com/yahoo/astra/fl/charts/PieChart.as
Executable file
372
com/yahoo/astra/fl/charts/PieChart.as
Executable file
@ -0,0 +1,372 @@
|
||||
/*
|
||||
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
|
||||
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
|
||||
*/
|
||||
package com.yahoo.astra.fl.charts
|
||||
{
|
||||
import com.yahoo.astra.fl.charts.axes.IAxis;
|
||||
import com.yahoo.astra.fl.charts.axes.IRadialAxisRenderer;
|
||||
import com.yahoo.astra.fl.charts.axes.NumericAxis;
|
||||
import com.yahoo.astra.fl.charts.axes.RadialAxisRenderer;
|
||||
import com.yahoo.astra.fl.charts.series.ISeries;
|
||||
import com.yahoo.astra.fl.charts.series.PieSeries;
|
||||
import com.yahoo.astra.utils.GeomUtil;
|
||||
import com.yahoo.astra.utils.NumberUtil;
|
||||
|
||||
import fl.core.InvalidationType;
|
||||
import fl.core.UIComponent;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.geom.Point;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
|
||||
/**
|
||||
* An Array containing the default colors for each series. These colors are
|
||||
* used for markers in most cases, but they may apply to lines, fills, or
|
||||
* other graphical items.
|
||||
*
|
||||
* <p>Important: In the PieChart, a series uses multiple colors. The <code>seriesColors</code>
|
||||
* style is designed to work with multiple series where the index in the Array
|
||||
* corresponds to the series index. As a result, to set the colors on a PieChart,
|
||||
* an Array of color values should appear at each index in the outer Array.</p>
|
||||
*
|
||||
* @default [ [ 0xfcaf3e, 0x73d216, 0x729fcf, 0xfce94f, 0xad7fa8, 0x3465a4 ], [ 0x3465a4, 0xad7fa8, 0xfce94f, 0x729fcf, 0x73d216, 0xfcaf3e ] ]
|
||||
*/
|
||||
[Style(name="seriesColors", type="Array")]
|
||||
|
||||
/**
|
||||
* A chart that displays its data points with pie-like wedges.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class PieChart extends Chart implements ICategoryChart
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Class Variables
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var defaultStyles:Object =
|
||||
{
|
||||
seriesColors: [],
|
||||
seriesMarkerSkins: []
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The chart styles that correspond to styles on each series.
|
||||
*/
|
||||
private static const PIE_SERIES_STYLES:Object =
|
||||
{
|
||||
markerSkins: "seriesMarkerSkins",
|
||||
fillColors: "seriesColors"
|
||||
};
|
||||
|
||||
//--------------------------------------
|
||||
// Class Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @copy fl.core.UIComponent#getStyleDefinition()
|
||||
*/
|
||||
public static function getStyleDefinition():Object
|
||||
{
|
||||
return mergeStyles(defaultStyles, Chart.getStyleDefinition());
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function PieChart()
|
||||
{
|
||||
super();
|
||||
this.defaultSeriesType = PieSeries;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Displays a message in live preview mode when there is no dataProvider.
|
||||
*/
|
||||
protected var livePreviewMessage:TextField;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the dataField property.
|
||||
*/
|
||||
private var _dataField:String;
|
||||
|
||||
/**
|
||||
* The field used to access data for this series.
|
||||
*/
|
||||
public function get dataField():String
|
||||
{
|
||||
return this._dataField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set dataField(value:String):void
|
||||
{
|
||||
if(this._dataField != value)
|
||||
{
|
||||
this._dataField = value;
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the categoryField property.
|
||||
*/
|
||||
private var _categoryField:String;
|
||||
|
||||
/**
|
||||
* The field used to access categories for this series.
|
||||
*/
|
||||
public function get categoryField():String
|
||||
{
|
||||
return this._categoryField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set categoryField(value:String):void
|
||||
{
|
||||
if(this._categoryField != value)
|
||||
{
|
||||
this._categoryField = value;
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Flag indicating that the category names have been set manually,
|
||||
* and they should not be auto-generated by the chart.
|
||||
*/
|
||||
private var _categoryNamesSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the categoryNames property.
|
||||
*/
|
||||
private var _categoryNames:Array;
|
||||
|
||||
[Inspectable]
|
||||
/**
|
||||
* The names of the categories used by each series.
|
||||
*/
|
||||
public function get categoryNames():Array
|
||||
{
|
||||
return this._categoryNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set categoryNames(value:Array):void
|
||||
{
|
||||
if(this._categoryNames != value)
|
||||
{
|
||||
this._categoryNames = value;
|
||||
this._categoryNamesSetByUser = value != null;
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Determines the data field used by the series. Either the chart's
|
||||
* default data field or a data field explicitly defined for the series.
|
||||
*/
|
||||
public function seriesToDataField(series:PieSeries):String
|
||||
{
|
||||
var field:String = this.dataField;
|
||||
if(series.dataField)
|
||||
{
|
||||
field = series.dataField;
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the category field used by the series. Either the chart's
|
||||
* default category field or a category field explicitly defined for the
|
||||
* series.
|
||||
*/
|
||||
public function seriesToCategoryField(series:PieSeries):String
|
||||
{
|
||||
var field:String = this.categoryField;
|
||||
if(series.categoryField)
|
||||
{
|
||||
field = series.categoryField;
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function configUI():void
|
||||
{
|
||||
super.configUI();
|
||||
if(this.isLivePreview)
|
||||
{
|
||||
//special case for live previews with no data.
|
||||
this.livePreviewMessage = new TextField();
|
||||
this.livePreviewMessage.autoSize = TextFieldAutoSize.LEFT;
|
||||
this.livePreviewMessage.defaultTextFormat = this.getStyleValue("textFormat") as TextFormat;
|
||||
this.livePreviewMessage.text = "No live preview data";
|
||||
this.addChild(this.livePreviewMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function draw():void
|
||||
{
|
||||
if(this.isLivePreview)
|
||||
{
|
||||
//special case for live previews with no data.
|
||||
this.livePreviewMessage.visible = !this.dataProvider || this.dataProvider.length == 0;
|
||||
this.livePreviewMessage.x = (this.width - this.livePreviewMessage.width) / 2;
|
||||
this.livePreviewMessage.y = (this.height - this.livePreviewMessage.height) / 2;
|
||||
}
|
||||
|
||||
this.generateCategories(this.series);
|
||||
|
||||
var stylesInvalid:Boolean = this.isInvalid(InvalidationType.STYLES);
|
||||
|
||||
super.draw();
|
||||
|
||||
var contentPadding:Number = this.getStyleValue("contentPadding") as Number;
|
||||
var seriesWidth:Number = this.width - 2 * contentPadding;
|
||||
var seriesHeight:Number = this.height - 2 * contentPadding;
|
||||
this.drawSeries(contentPadding, seriesWidth, seriesHeight);
|
||||
this.updateLegend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Positions and sizes each series.
|
||||
*/
|
||||
protected function drawSeries(contentPadding:Number, width:Number, height:Number):void
|
||||
{
|
||||
this.content.x = this.content.y = contentPadding;
|
||||
|
||||
var seriesCount:int = this.series.length;
|
||||
for(var i:int = 0; i < seriesCount; i++)
|
||||
{
|
||||
var series:UIComponent = this.series[i] as UIComponent;
|
||||
this.copyStylesToSeries(ISeries(series), PIE_SERIES_STYLES);
|
||||
|
||||
PieSeries(series).categoryNames = this.categoryNames;
|
||||
|
||||
//if a pie chart contains more than one series, each additional series should be
|
||||
//a little bit smaller so that they can all be visible to the viewer.
|
||||
series.width = width - i * width / seriesCount;
|
||||
series.height = height - i * height / seriesCount;
|
||||
|
||||
//reposition the series based on the calculated dimensions
|
||||
series.x = (width - series.width) / 2;
|
||||
series.y = (height - series.height) / 2;
|
||||
series.drawNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function defaultDataTipFunction(item:Object, index:int, series:ISeries):String
|
||||
{
|
||||
//get the series display name first
|
||||
var text:String = super.defaultDataTipFunction(item, index, series);
|
||||
if(text.length > 0) text += "\n";
|
||||
|
||||
var pieSeries:PieSeries = PieSeries(series);
|
||||
|
||||
var category:String = pieSeries.itemToCategory(item, index);
|
||||
text += category + "\n";
|
||||
|
||||
var data:Number = pieSeries.itemToData(item);
|
||||
text += data + "\n";
|
||||
|
||||
var percentage:Number = pieSeries.itemToPercentage(item);
|
||||
text += (percentage < 0.01 ? "< 0.01" : NumberUtil.roundToPrecision(percentage, 2)) + "%";
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Private Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Analyzes the data to determine the category names.
|
||||
*/
|
||||
private function generateCategories(data:Array):void
|
||||
{
|
||||
if(this._categoryNamesSetByUser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//auto-detect the category labels
|
||||
var maxSeriesLength:int = 0;
|
||||
var seriesCount:int = data.length;
|
||||
var uniqueCategoryValues:Array = [];
|
||||
for(var i:int = 0; i < seriesCount; i++)
|
||||
{
|
||||
var series:PieSeries = data[i] as PieSeries;
|
||||
var seriesLength:int = series.length;
|
||||
maxSeriesLength = Math.max(seriesLength, maxSeriesLength);
|
||||
|
||||
// determine the field for this axis
|
||||
var currentCategoryField:String = this.seriesToCategoryField(series);
|
||||
|
||||
for(var j:int = 0; j < seriesLength; j++)
|
||||
{
|
||||
var item:Object = series.dataProvider[j];
|
||||
var category:String = j.toString();
|
||||
if(item.hasOwnProperty(currentCategoryField))
|
||||
{
|
||||
category = item[currentCategoryField];
|
||||
}
|
||||
if(uniqueCategoryValues.indexOf(category) < 0)
|
||||
{
|
||||
uniqueCategoryValues.push(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._categoryNames = uniqueCategoryValues;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user