Archive for July, 2009

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

We need help with a Flex and AIR documentation usability study

Hi all,
Our friends on the Flash Platform documentation team are looking for usability study participants in a Flex/AIR study. They are offering Amazon gift certificates as an incentive.

For more information, see http://blogs.adobe.com/actionscriptdocs/

Regards,
-Randy