private function adjustAxes(pLengthDict:Dictionary, vPosDict:Dictionary, vNegDict:Dictionary, clearance:Number):void {
var axis:LinearAxis
var axisLength:Number
var compChartMax:Number;
var key:Object;
var maxNegValue:Number;
var maxPosValue:Number;
var obj:Object
var percentPositive:Number;
var refChartMax:Number;
var referenceAxis:LinearAxis;
var referencePercentPositive:Number;
for (key in vPosDict){
axis = key as LinearAxis;
axisLength = pLengthDict[key];
maxPosValue = vPosDict[key];
maxNegValue = vNegDict[key];
obj = calculateMaxMin(maxPosValue, maxNegValue, axisLength, clearance);
if(maxPosValue > 0){
axis.maximum = obj.max;
}
else {
axis.maximum = 0;
}
if(maxNegValue > 0){
axis.minimum = obj.min;
}
else {
axis.minimum = 0;
}
percentPositive = axis.maximum / (axis.maximum - axis.minimum);
if(isNaN(referencePercentPositive)){
referenceAxis = axis;
referencePercentPositive = percentPositive;
}
else if(Math.abs(percentPositive - 0.5) < Math.abs(referencePercentPositive - 0.5)){
referenceAxis = axis;
referencePercentPositive = percentPositive;
}
}
//If there is more than one axis, Flex doesn't line up the zero
//value of each axis, so the data looks weird. This forces the
//zero values to line up.
for (key in vPosDict){
axis = key as LinearAxis;
axisLength = pLengthDict[key];
if(axis == referenceAxis){
continue;
}
if(axis.baseAtZero){
//we need to match the positive/negative ratio of the
//reference chart so that the zero values will line up
percentPositive = axis.maximum / (axis.maximum - axis.minimum);
if(Math.abs(percentPositive - referencePercentPositive) >= 0.5){
//The difference in axes is great. Make them both 50/50
//positive/negative.
compChartMax = Math.max(axis.maximum, Math.abs(axis.minimum));
obj = calculateMaxMin(compChartMax, compChartMax, axisLength, clearance);
axis.maximum = obj.max;
axis.minimum = obj.min;
refChartMax = Math.max(referenceAxis.maximum, Math.abs(referenceAxis.minimum));
obj = calculateMaxMin(refChartMax, refChartMax, pLengthDict[referenceAxis], clearance);
referenceAxis.maximum = obj.max;
referenceAxis.minimum = obj.min;
referencePercentPositive = 0.5;
}
else if(percentPositive < referencePercentPositive){
axis.maximum = -axis.minimum * referencePercentPositive / (1 - referencePercentPositive);
}
else if(percentPositive > referencePercentPositive){
axis.minimum = axis.maximum - (axis.maximum / referencePercentPositive);
}
}//end if(axis.baseAtZero)
}//end iteration over axes
}
private function calculateMaxMin(maxPosValue:Number, maxNegValue:Number,
axisLength:Number, clearance:Number):Object {
var useableAxisLength:Number = axisLength - clearance * 2;
var unitsPerPixel:Number = Math.ceil((maxPosValue + maxNegValue)/useableAxisLength);
var retObj:Object = new Object();
retObj.max = clearance * unitsPerPixel + maxPosValue;
retObj.min = -1 * (clearance * unitsPerPixel + maxNegValue);
return retObj;
}
public function padAxes():void {
if(theChart.series == null){
return;
}
var clearance:Number;
var i:int;
var items:Array;
var j:int;
var label:Label
var labelContainer:Sprite;
// key for all Dictionary objects is an IAxis object, referencing a
//particular axis ( primary or secondary )
//holds maximum positive value (NOT pixels) of data associated with a
//particular axis
var vPosDict:Dictionary = new Dictionary();
//holds the absolute value of the minimum negative value (NOT pixels) of
//data associated with a particular axis
var vNegDict:Dictionary = new Dictionary();
//holds the length, in pixels, of a particular axis
var pLengthDict:Dictionary = new Dictionary();
if(theChart is BarChart){
var labelWidth:Number = 0;
for(i = 0; i < theChart.series.length; i++){
if(! theChart.series is BarSeries){
continue;
}
for(j = 0; j < theChart.horizontalAxisRenderers.length; j++){
if(theChart.horizontalAxisRenderers[j].axis == theChart.series[i].horizontalAxis){
pLengthDict[theChart.series[i].horizontalAxis] = theChart.horizontalAxisRenderers[j].length;
break;
}
}
labelContainer = theChart.series[i].labelContainer;
items = theChart.series[i].items;
if(items == null || labelContainer == null || items.length != labelContainer.numChildren){
continue;
}
if(isNaN(vPosDict[theChart.series[i].horizontalAxis])){
vPosDict[theChart.series[i].horizontalAxis] = 0;
}
if(isNaN(vNegDict[theChart.series[i].horizontalAxis])){
vNegDict[theChart.series[i].horizontalAxis] = 0;
}
var bsi:BarSeriesItem;
var tmpLabel:Label = new Label();
for(j = 0; j < items.length; j++){
bsi = items[j];
label = labelContainer.getChildAt(j) as Label;
//we use a textField to get an immediate measure of the width
var textField:TextField = new TextField();
textField.text = label.text;
labelWidth = Math.max(labelWidth, textField.textWidth);
var xValue:Number = new Number(bsi.xValue);
if(xValue < 0){
vNegDict[theChart.series[i].horizontalAxis] = Math.max(vNegDict[theChart.series[i].horizontalAxis], Math.abs(xValue));
}
else {
vPosDict[theChart.series[i].horizontalAxis] = Math.max(vPosDict[theChart.series[i].horizontalAxis], xValue);
}
}//loop through bar series items
}//loop through bar series
clearance = theChart.computedGutters.left + theChart.getStyle("paddingLeft") + labelWidth;
adjustAxes(pLengthDict, vPosDict, vNegDict, clearance);
//Flex draws a new line for zero, but still keeps the old baseline.
//This will remove the old baseline.
theChart.backgroundElements[0].setStyle("verticalShowOrigin", false);
}
else if(theChart is ColumnChart){
var labelHeight:Number = 0;
for(i = 0; i < theChart.series.length; i++){
if(! theChart.series is ColumnSeries){
continue;
}
for(j = 0; j < theChart.verticalAxisRenderers.length; j++){
if(theChart.verticalAxisRenderers[j].axis == theChart.series[i].verticalAxis){
pLengthDict[theChart.series[i].verticalAxis] = theChart.verticalAxisRenderers[j].length;
break;
}
}
labelContainer = theChart.series[i].labelContainer;
items = theChart.series[i].items;
if(items == null || labelContainer == null || items.length != labelContainer.numChildren){
continue;
}
if(isNaN(vPosDict[theChart.series[i].verticalAxis])){
vPosDict[theChart.series[i].verticalAxis] = 0;
}
if(isNaN(vNegDict[theChart.series[i].verticalAxis])){
vNegDict[theChart.series[i].verticalAxis] = 0;
}
var csi:ColumnSeriesItem;
for(j = 0; j < items.length; j++){
csi = items[j];
label = labelContainer.getChildAt(j) as Label;
labelHeight = Math.max(labelHeight, label.height);
var yValue:Number = new Number(csi.yValue);
if(yValue < 0){
vNegDict[theChart.series[i].verticalAxis] = Math.max(vNegDict[theChart.series[i].verticalAxis], Math.abs(yValue));
}
else {
vPosDict[theChart.series[i].verticalAxis] = Math.max(vPosDict[theChart.series[i].verticalAxis], yValue);
}
}//loop through column series items
}//loop through column series
clearance = theChart.computedGutters.top + theChart.getStyle("paddingTop") + labelHeight;
adjustAxes(pLengthDict, vPosDict, vNegDict, clearance);
//Flex draws a new line for zero, but still keeps the old baseline.
//This will remove the old baseline.
theChart.backgroundElements[0].setStyle("horizontalShowOrigin", false);
}//end if ColumnChart
}
Saturday, February 6, 2010
Column Labels Redux
My work life is a series of Roseanne Roseannadanna moments. ("Well, Jane, it just goes to show you, it's always something.") I discovered that my code to force labels to render outside columns will not work when there are negative values. This code, however, should work:
Subscribe to:
Post Comments (Atom)

Nice work. Wish I had found this before I implemented my own version.
ReplyDelete