PDA

View Full Version : Creating an AxisRenderer object in ActionScript


ChristianD
05-19-2008, 06:46 AM
In the source below I'm dynamically Creating a Chart in ActionScript :p and I'm dynamically adding series to the chart. This works fine :), but the data in each series represents various units of measure (Data Ranges) so the chart is less useful for seeing the change in each series when using one Linear axis. Thus my problem :confused:, for each series that's graphed, I'd like to give each series it's own Linear/Vertical axis so that each graph is shown properly. It's easy to define AxisRenderers in mxml for each Verticle Axis when you have a static set of data and then assign each dataset to the left or right side of the graph with the "placement" variable on the AxisRenderer object, however I need to accomplish the same task using Actionscript since I will have a dynamic number of series that will get added to the graph. Below is my best attempt to logically accomplish this task but it doesn't accomplish the task of creating a new Vertical axis for each series that gets added to the chart.:confused: Does anyone have some source that demonstrates how to accomplish this task. I'd like to alternate which side each series is shown on (left/right) but I'd just settle for getting a separate Linear Axis/Vertical Axis/YAxis for each series at this point. The Adobe article Using Multiple Axes demonstrates the task I'm trying to accomplish(Using multiple axes (http://livedocs.adobe.com/flex/3/html/charts_types_12.html#227567)).

I'm very appreciative of any help that I can get trying to solve this problem!!!!!!!!

Source:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApp()">

<mx:Script>
<![CDATA[
import mx.charts.LinearAxis;
import mx.charts.AxisRenderer;
import mx.charts.CategoryAxis;
import mx.collections.ArrayCollection;

import mx.charts.series.LineSeries;
import mx.charts.DateTimeAxis;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.charts.HitData;
import mx.events.ToolTipEvent;
import mx.managers.ToolTipManager;

[Bindable]
private var PointData:ArrayCollection;
[Bindable]
private var Points1AC:ArrayCollection;
[Bindable]
private var Points2AC:ArrayCollection;

public function initApp():void
{
Points1XML.send();
}

// ************************************************** ***//
// Event handler function to manage returned
// RealTime Historian data.
// ************************************************** ***//
private function LoadPoints1(event:ResultEvent):void
{
Points1AC = event.result.HistorianData.Point.PointData;

TrendData2(Points1AC);
}

// ************************************************** ***//
// Event handler function to manage returned
// RealTime Historian data.
// ************************************************** ***//
private function TrendData2(PointsAC:ArrayCollection):void
{
var NameList:ArrayCollection = new ArrayCollection();

// var PointNamesAC:ArrayCollection = new ArrayCollection();

var TempObjectA:Object = PointsAC[0];

NameList.addItem(TempObjectA.PointName);

for each(var Point:Object in PointsAC)
{
// Create an Array of unique Point names.
for each(var TempName:String in NameList)
{
if (NameList.contains(Point.PointName) == false)
{
NameList.addItem(Point.PointName);
}

}

}

// Iterate over names and create a new series
// for each one.
for each (var Name:String in NameList)
{
var TempAC:ArrayCollection = new ArrayCollection;

for each(var PointB:Object in PointsAC)
{
if (PointB.PointName == Name)
{
TempAC.addItem(PointB);
}
}

// Create the new series and set its properties.
var localSeries:LineSeries = new LineSeries();
localSeries.dataProvider = TempAC;
localSeries.yField = "Value";
localSeries.xField = "DateTime";

// Set values that show up in dataTips and Legend.
localSeries.displayName = Name;

// Back up the current series on the chart.
var currentSeries:Array = myChart.series;

// Add the new series to the current Array of series.
currentSeries.push(localSeries);

// Add the new Array of series to the chart.
myChart.series = currentSeries;

//----

// Create a horizontal axis.
var hAxis:CategoryAxis = new CategoryAxis;

hAxis.dataProvider = TempAC;

hAxis.categoryField="DateTime";

myChart.horizontalAxis = hAxis;

//----
// Create a verticl axis for each series
var vAxis:LinearAxis = new LinearAxis;

var VAxisRenderer:AxisRenderer = new AxisRenderer();
VAxisRenderer.axis = vAxis;

myChart.verticalAxis = vAxis;

VAxisRenderer.placement = "right";
}
}

// ************************************************** ***//
// This Function alerts users of any errors encountered
// when calling the HTTPServices to retrieve data
// ************************************************** ***//
private function showFault(event:FaultEvent):void
{
/* trace("The application has encountered an error!!!")
trace(event.fault.faultCode+":"+event.fault.faultString); // Display any resulting errors encounterd calling the HTTPService */
Alert.show(event.fault.faultString, 'Alert Box', mx.controls.Alert.OK);
}

]]>
</mx:Script>

<mx:HTTPService id="Points1XML"
url="Points1.xml"
result="LoadPoints1(event)"
fault="showFault(event)">
</mx:HTTPService>

<mx:Panel title="Line Chart with Variable Number of Series">
<mx:LineChart id="myChart" showDataTips="true">
</mx:LineChart>
<mx:Legend dataProvider="{myChart}"/>
</mx:Panel>
</mx:Application>

Bellow is a sample of the dynamic data that is retrieved from the HTTP Service that gets called:

<?xml version="1.0" encoding="UTF-8" ?>
<HistorianData xmlns:Point="http://www.w3.org/2001/XMLSchema-Instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Point>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEFREQA</PointName>
<Total>239.946</Total>
<AverageValue>59.9865</AverageValue>
<MinValue>59.985</MinValue>
<MaxValue>59.988</MaxValue>
<DateTime>05/16/2008 09:39:32</DateTime>
<Value>59.988</Value>
<Quality />
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEFREQA</PointName>
<Total>239.946</Total>
<AverageValue>59.9865</AverageValue>
<MinValue>59.985</MinValue>
<MaxValue>59.988</MaxValue>
<DateTime>05/16/2008 09:39:52</DateTime>
<Value>59.988</Value>
<Quality>#</Quality>
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEFREQA</PointName>
<Total>239.946</Total>
<AverageValue>59.9865</AverageValue>
<MinValue>59.985</MinValue>
<MaxValue>59.988</MaxValue>
<DateTime>05/16/2008 09:39:56</DateTime>
<Value>59.985</Value>
<Quality />
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEFREQA</PointName>
<Total>239.946</Total>
<AverageValue>59.9865</AverageValue>
<MinValue>59.985</MinValue>
<MaxValue>59.988</MaxValue>
<DateTime>05/16/2008 09:39:58</DateTime>
<Value>59.985</Value>
<Quality>#</Quality>
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEVAL1</PointName>
<Total>-250.02393</Total>
<AverageValue>-62.50598</AverageValue>
<MinValue>-69.44717</MinValue>
<MaxValue>-55.5648</MaxValue>
<DateTime>05/16/2008 09:39:32</DateTime>
<Value>-55.5648</Value>
<Quality />
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEVAL1</PointName>
<Total>-250.02393</Total>
<AverageValue>-62.50598</AverageValue>
<MinValue>-69.44717</MinValue>
<MaxValue>-55.5648</MaxValue>
<DateTime>05/16/2008 09:39:52</DateTime>
<Value>-55.5648</Value>
<Quality>#</Quality>
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEVAL1</PointName>
<Total>-250.02393</Total>
<AverageValue>-62.50598</AverageValue>
<MinValue>-69.44717</MinValue>
<MaxValue>-55.5648</MaxValue>
<DateTime>05/16/2008 09:39:56</DateTime>
<Value>-69.44717</Value>
<Quality />
</PointData>
<PointData>
<PointName>EMSTST1.UNIVSERV.ACEVAL1</PointName>
<Total>-250.02393</Total>
<AverageValue>-62.50598</AverageValue>
<MinValue>-69.44717</MinValue>
<MaxValue>-55.5648</MaxValue>
<DateTime>05/16/2008 09:39:58</DateTime>
<Value>-69.44717</Value>
<Quality>#</Quality>
</PointData>
</Point>
</HistorianData>

ChristianD
05-19-2008, 07:41 AM
Okay, I've figured out how to add multiples axes by pushing multiple AxisRenderers onto the verticalAxisRenderers array(myChart.verticalAxisRenderers.push(VAxisRend erer)). :D However, now I have encountered a new problem. :( if I set maximum and minimum values for the LinearAxis attached to the AxisRenderer only the last series added to the chart is displayed/ If I remove the maximum and minimum values both charts are shown, but only the last series is actually mapped to an axis. Anyone have any ideas. I'd also love to get rid of the default Vertical axis that doesn't need to be drawn, but I'd settle for just getting each series to map to the individual axes at the moment.

Source:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApp()">

<mx:Script>
<![CDATA[
import mx.charts.LinearAxis;
import mx.charts.AxisRenderer;
import mx.charts.CategoryAxis;
import mx.collections.ArrayCollection;

import mx.charts.series.LineSeries;
import mx.charts.DateTimeAxis;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.charts.HitData;
import mx.events.ToolTipEvent;
import mx.managers.ToolTipManager;

[Bindable]
private var PointData:ArrayCollection;
[Bindable]
private var Points1AC:ArrayCollection;
[Bindable]
private var Points2AC:ArrayCollection;

public function initApp():void
{
Points1XML.send();
}

// ************************************************** ***//
// Event handler function to manage returned
// RealTime Historian data.
// ************************************************** ***//
private function LoadPoints1(event:ResultEvent):void
{
Points1AC = event.result.HistorianData.Point.PointData;

TrendData2(Points1AC);
}

// ************************************************** ***//
// Event handler function to manage returned
// RealTime Historian data.
// ************************************************** ***//
private function TrendData2(PointsAC:ArrayCollection):void
{
var NameList:ArrayCollection = new ArrayCollection();

// var PointNamesAC:ArrayCollection = new ArrayCollection();

var TempObjectA:Object = PointsAC[0];

NameList.addItem(TempObjectA.PointName);

for each(var Point:Object in PointsAC)
{
// Create an Array of unique Point names.
for each(var TempName:String in NameList)
{
if (NameList.contains(Point.PointName) == false)
{
NameList.addItem(Point.PointName);
}

}

}

var x = 0;

// Iterate over names and create a new series
// for each one.
for each (var Name:String in NameList)
{
var TempAC:ArrayCollection = new ArrayCollection;

for each(var PointB:Object in PointsAC)
{
if (PointB.PointName == Name)
{
TempAC.addItem(PointB);
}
}

// Create a horizontal axis.
var hAxis:CategoryAxis = new CategoryAxis;

hAxis.dataProvider = TempAC;

hAxis.categoryField="DateTime";

myChart.horizontalAxis = hAxis;

//----
// Create a verticl axis for each series
var vAxis:LinearAxis = new LinearAxis;
//vAxis.maximum = TempAC[x].MaxValue;
//vAxis.minimum = TempAC[x].MinValue;

var VAxisRenderer:AxisRenderer = new AxisRenderer();
VAxisRenderer.axis = vAxis;
if (x/2 == 0){
VAxisRenderer.placement = "right";
}
else{
VAxisRenderer.placement = "left";
}

myChart.verticalAxisRenderers.push(VAxisRenderer);

myChart.verticalAxis = vAxis;

// Create the new series and set its properties.
var localSeries:LineSeries = new LineSeries();
localSeries.dataProvider = TempAC;
localSeries.yField = "Value";
localSeries.xField = "DateTime";

// Set values that show up in dataTips and Legend.
localSeries.displayName = Name;

// Back up the current series on the chart.
var currentSeries:Array = myChart.series;

// Add the new series to the current Array of series.
currentSeries.push(localSeries);

// Add the new Array of series to the chart.
myChart.series = currentSeries;

//----
x++;
}
}

// ************************************************** ***//
// This Function alerts users of any errors encountered
// when calling the HTTPServices to retrieve data
// ************************************************** ***//
private function showFault(event:FaultEvent):void
{
/* trace("The application has encountered an error!!!")
trace(event.fault.faultCode+":"+event.fault.faultString); // Display any resulting errors encounterd calling the HTTPService */
Alert.show(event.fault.faultString, 'Alert Box', mx.controls.Alert.OK);
}

]]>
</mx:Script>

<mx:HTTPService id="Points1XML"
url="Points1.xml"
result="LoadPoints1(event)"
fault="showFault(event)">
</mx:HTTPService>

<mx:Panel title="Line Chart with Variable Number of Series">
<mx:LineChart id="myChart" showDataTips="true">
</mx:LineChart>
<mx:Legend dataProvider="{myChart}"/>
</mx:Panel>
</mx:Application>

tsj4
05-19-2008, 06:16 PM
I usually use a for loop to loop through all the values for the measure which will be assigned to that axis. On every loop I have a compare function to see if the next value is larger then the last greatest value and if so then replace that value. Once the loop has went through the length of the dataprovider then I set the max value of the vertical axis.

Hope that helps.

tsj4
05-19-2008, 06:18 PM
also in your code I don't see any reference to any secondVerticalAxis

ChristianD
05-19-2008, 08:15 PM
:p Okay ladies and gents, bellow is the source to dynamically add AxisRenderers to a Flex Chart in Actionscript. I've also removed the default Vertical axis that get's created with one simple line (myChart.verticalAxisRenderer.visible = false) ;). This verticalAxisRenderer property has been deprecated in the Flex 3 API, so this solution may not exist for much longer when trying to remove the default Verticle axis that gets created. This task proved far more confusing than it should have been and I still haven't added any of the style information that you should add to make it clear which axis corresponds to each series on the chart. Never the less, the code does work and since I've found very few articles on the web or on Adobe's website that come remotely close to helping developers figure this out, I hope it comes in handy for some of you. ;)

Source shown bellow:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApp()">

<mx:Script>
<![CDATA[
import mx.charts.LinearAxis;
import mx.charts.AxisRenderer;
import mx.charts.CategoryAxis;
import mx.collections.ArrayCollection;

import mx.charts.series.LineSeries;
import mx.charts.DateTimeAxis;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.charts.HitData;

[Bindable]
private var PointData:ArrayCollection;
[Bindable]
private var Points1AC:ArrayCollection;
[Bindable]
private var Points2AC:ArrayCollection;

public function initApp():void
{
Points1XML.send();
}

// ************************************************** ***//
// Event handler function to manage returned
// RealTime Historian data.
// ************************************************** ***//
private function LoadPoints1(event:ResultEvent):void
{
Points1AC = event.result.HistorianData.Point.PointData;

TrendData2(Points1AC);
}

// ************************************************** ***//
// Event handler function to manage returned
// RealTime Historian data.
// ************************************************** ***//
private function TrendData2(PointsAC:ArrayCollection):void
{
var NameList:ArrayCollection = new ArrayCollection();

var TempObjectA:Object = PointsAC[0];

// Store the first name found in the collection in the array
NameList.addItem(TempObjectA.PointName);

for each(var Point:Object in PointsAC)
{
// Create an Array of unique names.
for each(var TempName:String in NameList)
{
if (NameList.contains(Point.PointName) == false)
{
NameList.addItem(Point.PointName);
}

}

}

var x:int = 0;

// Iterate over names and create a new series
// for each one.
for each (var Name:String in NameList)
{
var TempAC:ArrayCollection = new ArrayCollection;

for each(var PointB:Object in PointsAC)
{
if (PointB.PointName == Name)
{
TempAC.addItem(PointB);
}
}

// Create a horizontal axis.
var hAxis:CategoryAxis = new CategoryAxis;

hAxis.dataProvider = TempAC;

hAxis.categoryField="DateTime";

myChart.horizontalAxis = hAxis;

//----

// Create a Vertical/Linear axis for each series
var vAxis:LinearAxis = new LinearAxis;

// Determine the Max/Min limits of your new Linear axis
vAxis.maximum = TempAC[x].MaxValue;
vAxis.minimum = TempAC[x].MinValue;

// Create an axis renderer to define how you want each
// series show on the chart
var VAxisRenderer:AxisRenderer = new AxisRenderer();
VAxisRenderer.axis = vAxis;

// Alternate which side of the graph the AxisRenderer appears on.
// The "x" variable is a dirty hack so feel free to implement a soultion
// that looks more elegant.
if (x/2 == 0){
VAxisRenderer.placement = "left";
}
else{
VAxisRenderer.placement = "Right";
}

// Hide the default Verticle axis that gets created before adding a series.
myChart.verticalAxisRenderer.visible = false;

// Push the new Renderer onto the charts Renderer Array
// for each series you add to the chart
myChart.verticalAxisRenderers.push(VAxisRenderer);

// Create the new series and set its properties.
var localSeries:LineSeries = new LineSeries();
localSeries.dataProvider = TempAC;
localSeries.yField = "Value";
localSeries.xField = "DateTime";

// Set values that show up in dataTips and Legend.
localSeries.displayName = Name;

localSeries.verticalAxis = vAxis;

// Back up the current series on the chart.
var currentSeries:Array = myChart.series;

// Add the new series to the current Array of series.
currentSeries.push(localSeries);

// Add the new Array of series to the chart.
myChart.series = currentSeries;

//----
x++;
}
}

// ************************************************** ***//
// This Function alerts users of any errors encountered
// when calling the HTTPServices to retrieve data
// ************************************************** ***//
private function showFault(event:FaultEvent):void
{
/* trace("The application has encountered an error!!!")
trace(event.fault.faultCode+":"+event.fault.faultString); // Display any resulting errors encounterd calling the HTTPService */
Alert.show(event.fault.faultString, 'Alert Box', mx.controls.Alert.OK);
}

]]>
</mx:Script>

<mx:HTTPService id="Points1XML"
url="Points3.xml"
result="LoadPoints1(event)"
fault="showFault(event)">
</mx:HTTPService>

<mx:Panel title="Line Chart with Variable Number of Series">
<mx:LineChart id="myChart" showDataTips="true">
</mx:LineChart>
<mx:Legend dataProvider="{myChart}"/>
</mx:Panel>
</mx:Application>

ChristianD
05-19-2008, 08:23 PM
also in your code I don't see any reference to any secondVerticalAxis

The second verticle axis code has been depricated in the Flex3 API. Trust me when I say, that was my first line of thinking, but I wanted a solution that wouldn't potentially go away in a few months.

As for the Min and Max limits, I'm fortunate that my data collection contains the limits already for each series, so I didn't have to loop through the entire series to discover the min and max values. If you have a large enough data set you can take a major performance hit looping through the collections to determine each series limits, so I'm happy the service creater took the liberty to include the information in each object returned.

Just asking, but does anyone offhand know how to quickly add the style information to each renderer. I'm sure it's probably just callying a style method and handing it a color code, but it should would be benifitial if somone could point me in the right direction.

ChristianD
05-19-2008, 09:22 PM
also in your code I don't see any reference to any secondVerticalAxis

In your Creating Charts in Actionscript post ()Creating Charts in Actionscript (http://www.actionscript.org/forums/showthread.php3?t=158392&highlight=Create+chart+actionscript&page=2) you set the AxisRenderer label rotation angle using the hAxisRenderer.setStyle("labelRotation", 45) method. Are you familiar with how to retrieve the color code that Flex uses to color the lines shown on a Line chart. I'm pretty sure i need to use the getStyle() method (http://127.0.0.1:64430/help/nftopic/com.adobe.flexbuilder.help/html/styles_08.html) but does anyone have any ideas about which object contains the color code for the line chart that gets rendered on the chart. I can't find a default color on the series object even though that's where i would expect it to exist and I haven't found a color property on the chart just yet either.