Flex Chart: Column Chart with Multiple Column Item Trend Line

7. April 2009 09:48

I've been doing a lot of work lately with Flex Charts and part of my requirements involve being able to take an unknown amount of data received from a web service and convert that data into a usable experience for my clients.  To make things a little more complicated, there is no way to know how many series of data I am going to need until runtime.  I addressed a way to create this data in another post here where you process unique values of your data into a new data provider which serves as the provider for your chart.  Now that this is completed, I want to be able to draw trend lines for each grouping of out categories that may be incongruent.  In other words, not every category may have a value within each series.

I've been able to find lots of basic charting examples, but few of them address more complicated business requirements.  I realize this is not the only way to achieve the required result, but it rather one way.

An example of this, for your visualization pleasure, could be a circumstance where you are selling fruit.  Each type of fruit is your category(which becomes the grouping along the horizontal axis), and the series is represented by individual fiscal years.  I want all of the years to be visible in my legend by year, but I want all types of fruit to be placed together on the graph so I can see a comparison between them across multiple years. 

 

So let's get started by looking at what I want to see:

 

 

So in this example, we have a company that possibly sells different types of fruit each year.  For the types of fruit that have been sold longer than a single year, we want to be able to visually see a trend over time between like items.  We will never know exactly how many years are going to be represented, so all of the series have to be created at runtime from the data returned.  We also will never know the number of categories and will have to create those groupings at runtime based upon the data.

Now the real question here is that we want to create a trend comparison between items across different series, but keep in mind that the items in each series may not always match up across the categories.  This makes things a little more difficult.



So lets write some code:

We are going to assume that you have already read my previous post on how to group the series into categories and that you now have a arrayCollection or XMLListCollection being used as your chart component data provider.  Inside the chart.series property, you will find a set of objects listed that are the individual series for your chart.  We are going to use these to create our trend line. 

 



In my example, I don't want to be able to see the trending line permanently, but rather want to click a checkbox to turn it on and off.  In order to draw the trend line, I will be using a CartesianDataCanvas placed in my annotation elements. 

<mx:ColumnChart id="chart"  width="100%" height="100%" showDataTips="true" >
        <mx:annotationElements>
                  <mx:CartesianDataCanvas id="cnvOverlay" includeInRanges="true">
                  </mx:CartesianDataCanvas>                           
         </mx:annotationElements>               
</mx:ColumnChart> 

<mx:CheckBox id="chkTrend" label="Show Trending" change="{trendChangeHandler(event)}"/>





In the event handler for the change event on my checkbox, I will do the following:

 

protected function trendChangeHandler(event:Event):void
{   
               
      if(event.currentTarget.selected)
      {
              for each(var item:Object in hAxis.dataProvider)
              {
                  createTrending(String(item));
              }
       }
       else
       {
               cnvOverlay.clear();
        }
 }

Since my horizontal axis is not numerical data but rather a CategoryAxis, I can access each unique value in the axis by looking at it's dataProvider property.  This will show me a collection of objects that I can then use to check my ColumnSeries against.  Here is the heavy lifter:



protected function createTrending(cat:String):void
{
    try
    {
        cnvOverlay.lineStyle(4, 0x0066FF, .5, true, LineScaleMode.NORMAL, CapsStyle.ROUND, JointStyle.MITER, 2);
               
        var ser:Array = chart.series;                   
        var selectedData:Array = [];


        // Iterate over each series to find identical categories
        for (var iIndex:int=0; iIndex<ser.length; iIndex++)
        {
            var items:Array = ser[iIndex].items;
            // Iterate over each item in the series
            for (var jIndex:int=0; jIndex<items.length; jIndex++) {
                if (items[jIndex].item.Tag == cat)
                     {
                         selectedData.push(items[jIndex] as ColumnSeriesItem);
                     }                           
            }                       
         }
          // Start with the first item in the grouping and draw the trending line
          if(selectedData.length > 1)
          {                          
               for(var kIndex:int = 0; kIndex < selectedData.length; kIndex++)
              {
                   var currentItem:ColumnSeriesItem = ColumnSeriesItem(selectedData[kIndex]);
                   var nextItem:ColumnSeriesItem = ColumnSeriesItem(selectedData[kIndex+1]);
                   if(nextItem != null)
                   {
                       var c1:String = new String();
                       c1 =  currentItem.item.Category;
                       var v1:Number = new Number();
                       v1 = currentItem.yValue as Number;
                                  
                       cnvOverlay.moveTo(c1, v1);
                                  
                       var c2:String = new String();
                       c2 = nextItem.item.Category;
                       var v2:Number = new Number();
                       v2 = nextItem.yValue as Number;
                                  
                       cnvOverlay.lineTo(c2, v2);
                  }
                  else
                  {
                      break;
                  }                              
            }
         }
    }
    catch(err:Error)
    {
       throw new Error("Error creating trend line: "+err.message);
       trace(err);
     }
 }

 



Now there is a problem with this code.  Since Im using my category as my xValue, Flex uses the center of the category (not the column) as the xValue.  the result is a vertical line.  Not at all what I want.  I want the trend line to start and end at the center of each column.  What to do, what to do???  Instead of setting the x object point by using the method used in the example from the flex docs here, we have to figure out a different way to handle this.  In the example, there is only one columnItem per horizontal axis value.  We have n-number of columnItems per category.  I'm going to do some more work on this today and see what i can figure out to solve this data space versus pixel space issue within a given category tag.

I found one solution here that I am going to look at today.



Currently rated 4.5 by 2 people

  • Currently 4.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments


Log in