Posts in Category "Charts"

Date range selection for Flex charts

Charting applications that have user interaction are great. They can take advantage of the built-in charting effects, and also take advantage of users’ needs to look at data differently, and drill down into data. One of the most interesting ways to experience time-based data is by using a date range selector.

There are some examples of date range selectors out there, such as the fantastic one used by Google finance. I wanted to do something simpler so I wrote this example, which is loosely based on Ely’s Custom Annotation example in his Chart Sampler.

Try it out. Just click and drag a range for the chart:

This app consists of a main application file (DateSelectionExample.mxml) and a single ActionScript class file, RangeSelector.as.

The RangeSelector extends the ChartElement class. As a result, you add it to your chart as an annotationElement:

<mx:annotationElements>
<custom:RangeSelector/>
</mx:annotationElements>

The meat of the RangeSelector class is in the updateDisplayList() method. Here, it does several things:

  1. Draws a transparent rectangle over the entire chart. This rectangle allows the mouse events to be handled in the RangeSelector rather than be passed to the chart.
  2. Draws the selected region if the mouse is being held down and dragged over the chart.
  3. Transforms the screen coordinates of the selected region into data coordinates that the chart “understands”.
  4. Positions the region’s labels as it expands and contracts due to mouse movement.
  5. Resets the selection region and hide the labels when the mouse is not drawing a new area.

In the constructor, the RangeSelector added labels that show the current date as you drag a selection out and in:

_labelLeft = new Label();
_labelRight = new Label();
addChild(_labelLeft);
addChild(_labelRight);

The startTracking() and endTracking() methods keep track of the mouse when it is clicked and dragged. This rectangular region is what defines the new dates to show on the chart.

I used the parentApplication property to access objects and properties on the main application. It’s not the cleanest or most portable code, but it works, and you could easily write a class that handles the access methods.

The width of the selection area is determined by where the user drags the mouse. But to get the height, I wanted to use the entire height of the chart. To do this, I use the parentApplication property to reach up to the parent app and get the height of the chart itself:

g.drawRect(c[0].x,0,rectWidth, parentApplication.myChart.height);

Later, in the commitProperties() method, the RangeSelector extracts the date information (day, date, and year) to build the label’s text:

leftDate = new Date(dLeft);
rightDate = new Date(dRight);
_labelLeft.text = (leftDate.getMonth()+1) + "/" + leftDate.getDate() + "/" + leftDate.getFullYear();
_labelRight.text = (rightDate.getMonth()+1) + "/" + rightDate.getDate() + "/" + rightDate.getFullYear();

Note that the Date class’s month property is zero-based, so I added 1 to make sure I had the right month in the label.

Finally, when you finish dragging, the RangeSelector modifies the chart’s data in the parent application:

parentApplication.minDate = new Date(dLeft);
parentApplication.maxDate = new Date(dRight);

The minDate and maxDate properties in the parent application are bound to the chart’s DateTimeAxis. By changing these values, the chart’s axis changes. As a result, the data shown on the chart changes.

<mx:horizontalAxis>
<mx:DateTimeAxis id="hAxis"
dataUnits="days"
minimum="{minDate}"
maximum="{maxDate}"/>
</mx:horizontalAxis>

When the chart data changes, the RangeSelector also adjusts the size of the plot points. In this case, I adjust the size of the plot points at the same ratio as the size of the selection is to the chart area. For example, if you drew a selection area half the size of the chart, the plot points increase 50% in size. The smaller the selection, the greater the increase in size of the plot points.

var plotPointRatio:Number = rectWidth/parentApplication.myChart.width;
var curRadius:int = parentApplication.series1.getStyle("radius");
parentApplication.series1.setStyle("radius", curRadius *(1 - plotPointRatio + 1));
parentApplication.series2.setStyle("radius", curRadius *(1 - plotPointRatio + 1));
parentApplication.series3.setStyle("radius", curRadius *(1 - plotPointRatio + 1));

In the main app, when you hit reset, the radii of the plot points are reset to their defaults, and the minDate and maxDate properties are reset. The chart’s axis returns to its original range:

minDate = null;
maxDate = null;
...
series1.setStyle("radius", 5);
series2.setStyle("radius", 5);
series3.setStyle("radius", 5);

You can download the source here:

DateSelectionExample.mxml
RangeSelector.as

Changing chart series color at run time with a ColorPicker

A recent thread in the Flex support forums resulted in a couple of new charting examples. Specifically, a user was trying to use a ColorPicker component to change the color of a series in a chart at run time. Turns out, it was a little tricky, so I figured I should share the examples.

The first example shows a LineChart. You can click on an item in the chart, which brings up a ColorPicker. You select a color and Flex applies that color to the selected series.

The meat of the example is in the itemClick handler. This is where you get a reference to the LineSeries by accessing the LineSeriesItem’s element property. Here’s the code:

private function selectItemHandler(e:ChartItemEvent):void {
var hitData:HitData = e.hitData;
csi = LineSeriesItem(hitData.chartItem);
el = ChartElement(csi.element);

cp = new ColorPicker();
myChart.addChild(cp);
cp.addEventListener(ColorPickerEvent.CHANGE, changeItemColor);
cp.x = e.localX;
cp.y = e.localY;
cp.open();
}

The changeItemColor() method is pretty straightforward. For a LineChart’s series, you define a new Stroke and then apply that Stroke to the series’ lineStroke style. You then remove the ColorPicker from the display list so it doesn’t pollute the chart’s appearance. Here’s the code for that:

private function changeItemColor(e:ColorPickerEvent):void {
var s1:Stroke = new Stroke(cp.selectedColor, 4, 1);
el.setStyle("lineStroke", s1);
myChart.removeChild(cp);
}

Here’s a running example:

To change the color of a series like a ColumnSeries or a BarSeries, you have to apply the style a little bit differently. In this case, you define a SolidColor and then apply that to the series’ fill property.

var c:SolidColor = new SolidColor(cp.selectedColor);
el.setStyle("fill", c);

Here’s the running example:

The final example is a little different in that it applies the color to a single item in the series and not the entire series. I thought it was interesting, so I figure I’d share it.

Instead of applying a style to the series, you set the fill property on the ColumnSeriesItem:

var c:SolidColor = new SolidColor(cp.selectedColor);
csi.fill = c;

In addition, to get it to work requires a little workaround: You have to trigger a call to the series’ updateDisplayList(). To do that, you just set a property on the itemRenderer, like this:

csi.itemRenderer.height = csi.itemRenderer.height;

Here’s the running example:

Here is a ZIP file of the 3 running examples and the source code:
Download file (985KB)

You might notice that these examples also use the live data that I blogged about recently.

“Live” data for charting examples

This is not a major announcement, but it’s got a touch of cool factor and represents an improvement to about 200 doc examples. The charting examples in Flex documentation have been updated to use real-time data served up from a MySQL database. They connect to the database via an HTTPService tag, and then use the results as the data provider for the charts.

The server that hosts the data is located at:
aspexamples.adobe.com

The following list provides links to the new example data. The link in parentheses provides the XML output of the request. The best way to view the source data as it is used by Flex is to click the XML link, and then select View > Source in your browser:

To use these URLs as data sources in your apps, you can do something like the following:

<mx:HTTPService
id="srv"
url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"
/>

Be sure to call the service’s send() method when your app is finished loading (in the creationComplete event handler):

creationComplete="srv.send()"

And then set the value of the chart’s dataProvider property to something like this:

dataProvider="{srv.lastResult.data.result}"

The updated examples won’t appear in the Flex documentation until the next time it is generated for the public. This will likely be for the Flex 4 (“Gumbo”) beta. But the data is available now for your testing.

Customizing the layout of legend items

This question came up in a recent forum posting:

You can make the LegendItems in a Legend display horizontally in a long line or vertically in a long list, but how do I customize the layout of these LegendItems?

The answer took me a little while to figure out, so I’ll post it here so others can benefit from it… Yes, the answer is also posted as a response in the forums, but I can put running examples here so you can see what I mean.

First, an app that shows the problem. The ColumnChart control in the following app has 7 separate series, each with a separate entry in the legend. If you lay out the LegendItems vertically, you get a lot of extra white space to the right of the legend, below the chart, as the following example shows:

If you lay the LegendItems out horizontally, you get scroll bars at the bottom of the application, as the following example shows:

There’s no way (that I could figure out) to eliminate those scroll bars without clipping the LegendItems.

One solution, which can be adapted to any number of layout techniques, is to build the list of LegendItems in ActionScript and add them to a dynamic grid layout. I did this in a single loop that takes into account the preferred number of items per row:

for (var j:int = 0; j < numRows; j++) {
var gr:GridRow = new GridRow();
myGrid.addChild(gr);
for (var k:int = 0; k < rowSize; k++) {
if (z < myChart.series.length) {
var gi:GridItem = new GridItem();
gr.addChild(gi);
var li:LegendItem = new LegendItem();
// Apply the current series' displayName to the LegendItem's label.
...
// Get the current series' fill.
...
// Apply the current series' fill to the corresponding LegendItem.
...
// Add the LegendItem to the GridItem.
gi.addChild(li);
// Increment any time a LegendItem is added.
z++;
}
}

Before you do any of this, you have to remove all the existing GridRows and GridItems. You do this with a call to the removeAllChildren() method, like this:

myGrid.removeAllChildren();

There are a couple of gotchas in this approach: you have to be sure to grab the right labels so that your dynamic LegendItems match the series’ display names that show up in the chart. You do this by accessing the chart’s series array’s displayName property, as the following example shows:

li.label = myChart.series[z].displayName;

You also have to get the color (actually, it’s the “fill” property) from the series, and apply it to the corresponding LegendItem’s fill, as the following example shows:

var sc:SolidColor = myChart.series[z].getStyle("fill");
li.setStyle("fill", sc);

Here’s a running example that lays out the LegendItems in the selected number of rows. I also took the opportunity to apply some styles to make the LegendItems a little more attractive. They are of uniform width and their background colors are the same as the marker color, as the following example shows:

li.setStyle("textIndent", 5);
li.setStyle("labelPlacement", "left");
li.setStyle("fontSize", 9);
gi.setStyle("backgroundAlpha", "1");
gi.setStyle("backgroundColor", sc.color);
gi.width = 80;

Here’s the running example:

Here’s the source code:
Download ZIP (9K)

Extracting data visualization source code

All users have access to the Flex 3 SDK source code. You can view the ActionScript source files for classes like Button and DataGrid. The source code is in the frameworks\projects\framework\src directory of your Flex installation. The source code for the data visualization projects does not come pre-installed, however. If you have a Flex Builder Professional license key, you can extract the data visualization source code, which include the charting controls, the AdvancedDataGrid classes, the OLAP classes, and the automation classes. To view your Flex Builder license key, select Manage Flex Licenses from the Help menu.

To extract the data visualization source code, you use the DMV-source.jar file. This file is located in the SDK’s lib directory. If you are using Flex Builder, the lib directory is located at {Flex Builder 3}\sdks\3.0.0\lib.

The syntax to extract the data visualization source code is as follows:

> java -jar DMV-source.jar {license-file-location} {output-location}

The first argument is the location of the license.properties file. This file must contain a valid Flex Builder 3 Professional license key. The second parameter is the location that you want the data visualization source code to be extracted to.

If you installed Flex Builder 3 Professional, then the license.properties file is located at the following locations, depending on your operating system:

  • Windows: C:\Documents and Settings\All Users\Application Data\Adobe\Flex\
  • Mac OS: /Library/Application Support/Adobe/Flex/
  • Linux: ~/.adobe/Flex/

The following example extracts the data visualization source code to the c:\temp\dataviz_source directory on Windows:

java -jar c:\Flex Builder 3\sdks\3.0.0\lib\DMV-source.jar
"C:\Documents and Settings\All Users\Application Data\Adobe\Flex\"
c:\temp\dataviz_source

If you have a valid Flex Builder 3 Professional license key but did not install Flex Builder, then you can create a license.properties file anywhere, and point to it when you use the DMV-source.jar file. The syntax of the license.properties file is as follows:

flexbuilder3={license_key}

Using an HTML page as a data provider

Here’s a new example in the charting doc. It shows how someone might use some or all of an HTML page they load via an HTTPService tag as a data provider for their charting component. It includes logic to extract just the data from the target page.

The benefit to this is that if you have a wiki or blog that you can edit via a browser, you can build and update a data page without having to hit a database. The code extracts specially-tagged data from the HTML page, so even if it’s part of a larger block of HTML, you can still extract just the data.

One thing to note: The target domain in this example works because it has a crossdomain.xml file. If you want to load a page from your own target domain, you must be sure that your target domain has a crossdomain.xml file in place, and that that policy file lets the domain on which the Flex app is running request data from it.

Here’s the running example:

Here’s the data page that drives this chart:
http://examples.adobe.com/flex3/exchangingdata/html/dataPage.html

You can download the source MXML file here:
HTMLDataProvider.zip (1KB)

The exact location of the data page is passed into the application as a flashVars property. To change the data page, you only have to edit the HTML wrapper for the SWF file.

In the HTML wrapper, we pass the target HTML page as a param in the <object> tag:

<param name="flashVars" value="dataPage=http://examples.adobe.com/flex3/exchangingdata/html/dataPage.html" />

And again as an attribute of the <embed> tag:

flashVars="dataPage=http://www.shallowpondestates.com/misc/dataPage.html"

Note that you can view the source of this blog entry page to see the complete object/embed tags.

In the Flex app, the data page is read from the Application’s application.parameters property:

dataPage = Application.application.parameters.dataPage;

To initially get the page’s contents into the Flex app, we use an HTTPService object:

<mx:HTTPService id="serviceInput"
resultFormat="text"
url="{dataPage}"
/>

To get the HTML in a single string we get the lastResult property of the HTTPService in the ResultEvent handler:

var htmlBlob:String = serviceInput.lastResult.toString();

And to extract just the portion of the data page that we need (the part between the “data starts here” and “data ends here” comments), we use the slice() method:

htmlBlob = htmlBlob.slice( htmlBlob.indexOf("&lt;!-- data starts here --&gt;", 0) + 25, htmlBlob.length );
htmlBlob = htmlBlob.slice( 0, htmlBlob.indexOf("&lt;!-- data ends here --&gt;", 0) );

We then do a couple more passes on the remaining HTML string to replace HTML entities with angle brackets, so that we will end up with well-formed XML:

var tagPattern:RegExp = /<[^]*>/g;
htmlBlob = htmlBlob.replace(tagPattern, "");

var ltPattern:RegExp = /&lt;/g;
htmlBlob = htmlBlob.replace(ltPattern, "<");

var gtPattern:RegExp = /&gt;/g;
htmlBlob = htmlBlob.replace(gtPattern, ">");

At this point, we just need to pass the XML to the XMLList constructor so that it can be used as a data provider in the chart:

chartData = new XMLList(htmlBlob);

That’s it. If you compile and deploy this app, you can use the provided data page, or create your own. If your data page is on the same domain as the application, then you do not need a crossdomain.xml file.

Flex 3 New Charting Features

The Flex 3 beta went out before the charting documentation included the new features. So here is a PDF with all the charting chapters, including the new feature documentation.

Download file (1 MB)

The entire set of charting chapters is included here in one PDF because the organization has changed so much. I added a new chapter and consolidated some sections from other chapters.

New sections:
CHAPTER: Displaying Data and Labels
“Using data labels”
“Using per-item fills”
“Omitting days on a DateTimeAxis” (aka, work-week axis)

————————————————–
CHAPTER: Formatting Charts
“Positioning the axes”

————————————————–
CHAPTER: Using Events and Effects in Charts
“Selecting chart items”
“Drawing on chart controls”
“Drilling down into data” (new since 2.0.1, but previously blogged about here)
————————————————–

The following sections existed before, but have been completely rewritten based on feature changes:

CHAPTER: Chart Types
“Using multiple axes”
“Using multiple data series”

————————————————–

As always, feedback is welcome.

Chart data drill down examples

One thing the chart documentation lacks is examples of drilling down into data. This is critical for making charts that are interesting and fun to interact with. This blog entry includes two examples of drilling down into data.


Example #1

Here’s an example that uses the Event object to get a reference to the clicked ColumnSeries. It then drills down into the single ColumnSeries by replacing the chart’s series Array with the single ColumnSeries in the chart. When you click a column again, the chart returns to its original configuration with all ColumnSeries.


Example #2

The following example is similar to the previous example in that it drills down into the assets of a fictional person’s net worth. In this case, though, it shows the value of the asset classes for the clicked-on month in the drill-down view rather than the change over time of a particular asset class.

This example uses the HitData object’s item property to access the values of the current data provider. By building an Array of objects with the newly-discovered data, the chart is able to drill down into the series without making calls to any external services.


More Info

You can download the documentation
and the source code for these examples.

Of course, these examples are nearly as exciting as what is going on over at Quietly Scheming, but hopefully they will get you started.