Annotating Flex charts with a DateTimeAxis

Flex charts seem like they have everything imaginable already built in, but it's hard to tell since there are so few examples of customized charting. I set out this week to add annotations to my chart, assuming that there would probably already be something built in to make it quick and easy.

Turns out that each chart has an annotation layer built in, that sits on top of the chart itself. All you have to do is pass in an array of objects that you want displayed and the chart takes care of it.

The trouble comes when trying to get coordinates for these annotations to match them up with a specific data point in the chart. A method called dataToLocal() is the answer in most cases. Passing in the value of the point you want coordinates for returns a Point object that can be used to determine where to place the annotation.

The tricky part comes if you're not using a regular axis. I tried this with a DateTimeAxis as my horizontal axis, only to be returned a number as my x coordinate that didn't match up to anywhere near my line, no matter what the data was.

So how to get a true x value on a DateTimeAxis? You write your own transform instead of using the default. I started with using the first and last values in the dataProvider to determine the end points of the chart but soon found the need to switch to using the baseline and maximum axis values to get more precise measurements. This is what I came up with:

I also found that on initially populating the chart with data, the baseline and maximum numbers are off slightly. It's only a tiny bit but it's enough to throw a graph with a lot of points off by up to a few pixels. So instead of invalidating the displayList and drawing my annotations on the next frame after the chart was populated, I had to use callLater. This extra second gave the chart time to correct the baseline and maximum values and the x and y coordinates were exactly where they should be.

Comments

8 Responses to “Annotating Flex charts with a DateTimeAxis”


  1. 1 Bruce

    You have a working example?

  2. 2 lauren

    I can put together a quick example when I get a chance and post it up on the SVN. Check back for a link.

  3. 3 lauren

    Take a look at this quick example here that shows a chart with a DateTimeAxis. There is also a DataGrid that lists all the annotations in the chart and their corresponding times and values.

  4. 4 Vikram

    I think you can use the dataTransform object to retreive the correct co-ordinates for a point.

  5. 5 Jeremy Mitchell

    So much for the example link. :(

    if i do this (i hardcoded a dataprovider position):

    var xPosition:Number = (dataProvider[5].date.time-myDateTimeAxis.baseline)
    / (myDateTimeAxis.maximum.time-myDateTimeAxis.baseline)
    * AxisRenderer(myChart.horizontalAxisRenderer).length;

    error! horizontalAxisRenderer is null

    so, i try this

    var xPosition:Number = (dataProvider[5].date.time-myDateTimeAxis.baseline)
    / (myDateTimeAxis.maximum.time-myDateTimeAxis.baseline)
    * AxisRenderer(myChart.horizontalAxisRenderers[0]).length;

    and then xPosition = 16665205464000

    i have a hard time believing that’s right. :(

  6. 6 lauren

    @Jeremy Mitchell,
    we are in the process of changing over our svn. all links will be updated within a day or two, please check back soon.

  7. 7 lauren

    The example link from above has now moved to:
    https://code.9mmedia.com/svn/public/DateTimeAnnotations/trunk/

  8. 8 Brian Bonner

    I had pulled out part of my code to do the date location information based on your post, when I realized it wasn’t placing the annotations in the correct position because of when it was getting the data. I think if a callLater is used when dataToLocal is called they get in the right place. In fact, I found that the dataToLocal has the benefit of actually placing it in the right spot based on the actual day of when the event occurred. I did have to subtract 1/2 the width of the icon I was placing to get it centered.

    var point:Point = colSeries.dataToLocal(eventInstance.eventDate, 0);
    eventDisplayColumn.x=point.x – iconWidth / 2;
    eventDisplayColumn.y=0;

    Here’s the call later update:

    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
    super.updateDisplayList(unscaledWidth, unscaledHeight);

    if(redrawEvents) {
    //remove this callLater to see the chart baseline and max values incorrect.
    callLater(refreshEvents);
    redrawEvents = false;
    }
    }

Leave a Reply