Building vertical bar charts

Now that we have built a bar chart using Flex, we are ready to do the same in pure ActionScript. This bar chart version will allow you to expand it in multiple ways and will remove the weight that the Flex framework adds to the file size. Now a bit about bar charts; Bar charts are good when you don't have too much data (more than 20 bars starts to make a big chart), or when you've averaged it. It is a quick way to compare data visually.

Getting ready

All we will need for this is to start a new project in FlashDevelop. Also, it would help to read about preparing data and about axes in the Adding labels and axes recipe in Chapter 1, Getting Started with Graph Drawing.

How to do it...

This section will refer a lot to the code provided with the book; check the Bar Chart folder within code files for Chapter 3. You will notice that we divided all the elements in the charts into their own classes.

How to do it...
  1. It all starts in the Main.as file, where we create the data that we will use to display in the chart after that we just create the chart and add it to the display list.
    var data:Vector.<BarData> = new Vector.<BarData>();
    data.push(new BarData("January", 60));
    data.push(new BarData("February", 100));
    data.push(new BarData("March", 30));
    
    var chart:BarChart = new BarChart(data, 400, 410);
    chart.x = 30;
    chart.y = 30;
    addChild(chart);
  2. From here you can look into the BarData class, which it is just two variables, a string and a number that represents the data that we are going to show.
  3. We now need to create a class for all the elements that comprise a bar chart. They are: the bars, the vertical axis, and the horizontal axis. Now this recipe is building a vertical bar chart so the vertical axis is the one that will have numerical marks and the horizontal axis will have labels on the marks.
  4. First the Bar class: This class will only draw a rectangle with the height representing the data for a certain label. The following is its constructor:
    public function Bar(width:int, height:int) {
      graphics.beginFill(0xfca25a);
      graphics.drawRect(-width/2, 0, width, -height);
      graphics.endFill();
    }
  5. The horizontal axis will take the x coordinate of the created bars and will place a label under it. Notice how it uses functions created in Chapter 1, Getting Started with Graph Drawing:
    public function HorizontalAxis(listOfMark:Vector.<Number>, data:Vector.<BarData>, width:Number) {
      drawAxisLine(new Point(0, 0), new Point(width, 0));
    
      for (var i:int = 0; i < listOfMark.length; i++) {
        drawAxisLine(new Point(listOfMark[i], -3), new Point(listOfMark[i], 3));
    
        var textField:TextField = new TextField();
        textField.text = data[i].label;
        textField.width = textField.textWidth + 5;
        textField.height = textField.textHeight + 3;
        textField.x = listOfMark[i] - textField.width / 2;
        textField.y = 5;
        addChild(textField);
      }
    }
  6. Now the vertical axis will make 10 marks at regular interval and will add a label with the associated value in it:
    for (var i:int = 0; i < _numberOfMarks; i++) {
      drawAxisLine(new Point( -3, (i + 1) * -heightOfAxis / _numberOfMarks ), new Point(3, (i + 1) * -heightOfAxis / _numberOfMarks));
      var textField:TextField = new TextField();
      textField.text = String(((i + 1) / (_numberOfMarks)) * maximumValue );
      textField.width = textField.textWidth + 5;
      textField.height = textField.textHeight + 3;
      textField.x = -textField.width - 3;
      textField.y = (i + 1) * -heightOfAxis / _numberOfMarks - textField.height / 2;
      addChild(textField);
    }
  7. Finally, the BarChart class will take the three classes we just created and put it all together. By iterating through all the data, it will find the maximum value, so that we know what range of values to put on the vertical axis.
    var i:int;
    var maximumValue:Number = data[0].data;
    for (i = 1; i < data.length; i++) {
      if (data[i].data > maximumValue) {
        maximumValue = data[i].data;
      }
    } 
  8. After that we create each bar, notice that we also keep the position of each bar to give it to the horizontal axis thereafter:
    var listOfMarks:Vector.<Number> = new Vector.<Number>();
    var bar:Bar;
    for (i = 0; i < data.length; i++) {
      bar = new Bar(_barWidth, data[i].data * scaleHeight);
      bar.x = MARGIN + _barSpacing + _barWidth / 2 + i * (_barWidth + _barSpacing);
      listOfMarks.push(bar.x - MARGIN);
      bar.y = height - MARGIN;
      addChild(bar);
    }
  9. Now all we have left to do is create the axes and then we are done; this is done really easily as shown in the following code:
    _horizontalAxis = new HorizontalAxis(listOfMarks, data, width - MARGIN);
    _horizontalAxis.x = MARGIN;
    _horizontalAxis.y = height - MARGIN;
    addChild(_horizontalAxis);
    
    _verticalAxis = new VerticalAxis(height - MARGIN, maximumValue);
    _verticalAxis.x = MARGIN;
    _verticalAxis.y = height -MARGIN;
    addChild(_verticalAxis);

How it works...

So we divided all the elements into their own classes because this will permit us to extend and modify them more easily in the future.

So let's begin where it all starts, the data. Well, our BarChart class accepts a vector of BarData as an argument. We did this so that you could add as many bars as you want and the chart would still work. Be aware that if you add many bars, you might have to give more width to the chart so that it can accommodate them.

You can see in the code, that the width of the bar of determined by the width of the graph divided by the number bars. We decided that 85 percent of that value would be given to the bars and 15 percent would be given to the space between the bars. Those values are arbitrary and you can play with them to give different styles to the chart.

Also the other important step is to determine what our data range is. We do so by finding what the maximum value is. For simplicity, we assume that the values will start at 0, but the validity of a chart is always relative to the data, so if there are negative values it wouldn't work, but you could always fix this. So when we found our maximum value, we can decide for a scale for the rest of the values. You can use the following formula for it:

var scaleHeight:Number = (height - 10) / maximumValue;

Here, height is the height of the chart and 10 is just a margin we leave to the graph to place the labels. After that, if we multiply that scale by the value of the data, it will give us the height of each bar and there you have it, a completed bar chart.

There's more...

We created a very simple version of a bar chart but there are numerous things we could do to improve it. Styling, interactivity, and the possibility of accommodating a wider range of data are just some examples.

Styling

This basic chart could use a little bit of styling. By modifying the color of the bars, the font of the labels, and by adding a drop shadow to the bars, it could be greatly enhanced. You could also make all of them dynamic so that you could specify them when you create a new chart.

Interactivity

It would be really good to show the values for the bars when you move the mouse over them. Right now you can kind of get an idea of which one is the biggest bar but that is all. If this feature is implemented, you can get the exact value.

Accommodating a wider data range

As we explained earlier, we didn't account for all the data range. Values could be very different; some could be negative, some could be very small (between 0 and 1), or you would want to set the minimum and maximum value of the vertical axes. The good thing here is that you can modify the code to better fit your data.

See also

The next recipe will iterate on this one giving you a more complex chart. Also, you could check the previous recipe to learn how to do this using the Flex framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.109.30