Monday, August 25, 2008

Flex Charts with Google Charts and Eastwood Charts

Flex provides a rich, sophisticated set of dynamic charts, but a license fee is required (via purchase of FlexBuilder Professional) to use these Flex charts for most realistic production situations. There are several other charting options including Open Flash Chart (runs as its own SWF and reads JSON data files to generate charts), amCharts (not open source and uses ActionScript 2 so cannot be used directly in Flex), Fusion Charts Free (sends SWF and data files from server to browser), Google Charts, and JFreeChart/Eastwood Charts. In this blog entry, I'll briefly look at using Google Charts and Eastwood Charts 1.1.0 with Flex because I believe these are the easiest to use in a Flex application.

Because the Eastwood Chart Servlet implements the most of the APIs of Google Charts, my example will be able, in most cases, to call either charting provider based on user selection. There are some extensions in Eastwood beyond the Google Chart API and the Google Chart API has some features still not implemented in Eastwood, but most of the common functionality is commonly implemented in both charting providers.

The concept behind Google Chart API (and hence applies to Eastwood Chart Servlet as well) involves providing data for chart generation to the chart provider via HTTP URL parameters and getting an image (PNG) as a response.

While URLs constructed to request chart generation from Google Charts and Eastwood Charts are almost always the same (because Eastwood is implemented to the Google Charts API), there are differences between the two implementations of these APIs. Using Google Charts is really easy because no work needs to be done on the server-side. All the developer needs to do is construct the appropriate URL conforming to the Google Charts API and invoke that to get the returned image. With Eastwood Charts, the developer needs to install the Eastwood-provided WAR file on his or her favorite Java EE container. It is important to note that this is all that must be done. The WAR files does not need to be tweaked or manually unzipped, but simply needs to be deployed by dropping it in the appropriate container directory or else through the server's administration tool.

The Eastwood Developer Manual that is included with the Eastwood download contains a list of several bullets outlining some advantages of the Eastwood Charts implementation of the Google Charts API. Most of these advantages stem ability to run the charting server on one's own network. Advantages of running the charting provider on one's own machine is include the ability to keep potentially sensitive data within one's own environment and the ability to run the chart generation in environments in which Internet connectivity is not available.

The following MXML code is a simple but complete example that accesses both Google Charts and Eastwood Charts via the same URL. I intentionally included 3D bar chart examples to demonstrate an extension provided by Eastwood Charts, but not by the Google Charts. While I don't show it here, Google Charts similarly provide some features not currently supported in Eastwood Charts (such as Maps).

Here is the source code for the Flex application taking advantage of both Google Charts and Eastwood Charts:

MXML Code for Application Using Eastwood Charts and Google Charts

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="900" height="900">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;

/**
* Generate chart using Eastwood Chart Servlet or Google Charts using
* submitted values.
*/
private function generateChart(event:FlexEvent):void
{
chartDisplayPanel.visible = true;
chartDisplayPanel.title = chartType.selectedValue.toString();
const url:String = buildUrl(chartType.selection.id);
chartUrlText.text = url;
generatedChart.source = url;
}

/**
* Determine maximum value entered for balances.
*
* @return Maximum balance from provided input balances.
*/
private function findMaximumDataValue():Number
{
const maximumBalance:Number =
Math.max(
Number(savingsInput.text),
Number(checkingInput.text),
Number(moneyMarketInput.text),
Number(taxableStocksInput.text),
Number(taxableBondsInput.text),
Number(iraInput.text),
Number(pensionInput.text) );
return maximumBalance;
}

/**
* Return the appropriate chart provider URL domain.
*
* @return URL domain for chart provider.
*/
private function findChartProvider():String
{
var chartProviderUrl:String;
if ( chartProvider.selection.id == "google" )
{
chartProviderUrl = "http://chart.apis.google.com";
}
else
{
chartProviderUrl = "http://localhost:8080/eastwood-1.1.0";
}
return chartProviderUrl;
}

/**
* Construct labels portion of chart generation URL.
*
* @return Labels portion of chart generation URL.
*/
private function constructLabels():String
{
const labelsStr:String =
"Savings|Checking|Money Market|Taxable Stocks|"
+ "Taxable Bonds|IRA|Pension";
return labelsStr;
}

/**
* Construct URL for accessing Eastwood-generated or Google-generated chart.
*/
private function buildUrl(chartType:String):String
{
const maximumBalance:Number = findMaximumDataValue();
const chartProviderDomainUrl:String = findChartProvider();
const urlString:String =
chartProviderDomainUrl + "/chart?cht="
+ chartType // type of chart
+ "&chs=400x400" // chart size: width x height
+ "&chd=t:" // build up the data over next several lines
+ savingsInput.text + ","
+ checkingInput.text + ","
+ moneyMarketInput.text + ","
+ taxableStocksInput.text + ","
+ taxableBondsInput.text + ","
+ iraInput.text + ","
+ pensionInput.text
+ "&chds=0," + Math.round(maximumBalance) // data scaling
+ "&chl=" + constructLabels();
return urlString;
}
]]>
</mx:Script>

<mx:HBox id="mainHorizontalBox" width="95%">
<mx:Panel id="inputPanel" title="Flex and Eastwood Charts">
<mx:Form>
<mx:FormHeading label="Investments Distribution" />
<mx:FormItem label="Savings ($)">
<mx:TextInput id="savingsInput" />
</mx:FormItem>
<mx:FormItem label="Checking ($)">
<mx:TextInput id="checkingInput" />
</mx:FormItem>
<mx:FormItem label="Money Market ($)">
<mx:TextInput id="moneyMarketInput" />
</mx:FormItem>
<mx:FormItem label="Taxable Stocks ($)">
<mx:TextInput id="taxableStocksInput" />
</mx:FormItem>
<mx:FormItem label="Taxable Bonds ($)">
<mx:TextInput id="taxableBondsInput" />
</mx:FormItem>
<mx:FormItem label="IRA ($)">
<mx:TextInput id="iraInput" />
</mx:FormItem>
<mx:FormItem label="Pension ($)">
<mx:TextInput id="pensionInput" />
</mx:FormItem>
<mx:FormItem label="Chart Type" id="chartTypeFormItem">
<mx:RadioButtonGroup id="chartType" />
<mx:RadioButton groupName="chartType" id="p"
label="Pie"
value="Investments Pie Chart" />
<mx:RadioButton groupName="chartType" id="p3"
label="3D Pie"
value="Investments 3D Pie Chart" />
<mx:RadioButton groupName="chartType" id="bvs"
label="Vertical Bar"
value="Investments Vertical Bar" />
<mx:RadioButton groupName="chartType" id="bvs3"
label="Vertical 3D Bar"
value="Investments Vertical 3D Bar"
selected="true" />
<mx:RadioButton groupName="chartType" id="bhs"
label="Horizontal Bar"
value="Investments Horizontal Bar" />
<mx:RadioButton groupName="chartType" id="bhs3"
label="Horizontal 3D Bar"
value="Investments Horizontal 3D Bar" />
</mx:FormItem>
<mx:FormItem label="Chart Provider"
id="chartProviderFormItem">
<mx:RadioButtonGroup id="chartProvider" />
<mx:RadioButton groupName="chartProvider" id="google"
label="Google Charts API"
value="Google Charts API" />
<mx:RadioButton groupName="chartProvider" id="eastwood"
label="Eastwood Chart Servlet"
value="Eastwood Chart Servlet"
selected="true" />
</mx:FormItem>
<mx:Button id="submitButton" label="Generate Chart"
buttonDown="generateChart(event);" />
</mx:Form>
</mx:Panel>
<mx:Panel id="chartDisplayPanel" height="{inputPanel.height}"
visible="false">
<mx:Image id="generatedChart" />
</mx:Panel>
</mx:HBox>
<mx:Panel id="summaryPanel" width="{mainHorizontalBox.width}"
title="URL Being Used for Chart Generation">
<mx:TextArea id="chartUrlText" width="{summaryPanel.width}"
height="75" />
</mx:Panel>

</mx:Application>


The above example provides a radio button group for the user to select which chart provider (Google Charts or Eastwood Charts) should be used. Another radio button group allows the user to select which chart type should be generated. The text fields allow for dynamic data values to be supplied that are used in the chart generation. The sample code takes these input values, the user's selection for chart type, and the user's selection for chart provider and builds a URL from these data points that meets the Google Charts API.

The following screen snapshots show charts generated from both Google Charts and Eastwood Charts (click on the images to see larger versions).

Eastwood Charts-Generated 3D Pie Chart



Google Charts-Generated 3D Pie Chart



Eastwood Charts-Generated Vertical Bar Chart



Google Charts-Generated Vertical Bar Chart



As you can see in the screen snapshots, there are some slight differences in the implementations of Eastwood Charts and Google Charts. However, they are remarkably similar and it is pretty convenient to be able to have the same code call either implementation to generate these charts. There are many more types of charts available than those shown in this example. Examples of these types of charts can be found in the Google Charts API page and in the Eastwood Chart Servlet samples page.

1 comment:

Andrew Westberg said...

I heard that Flex Charting would be free in the next version of FlexBuilder.