Andrew Trice
Real-World Rich Internet Applications
Tuesday December 23, 2008
More on bitmap-based charting of large data sets
This post is a follow-on to my recent post on visualizing large data sets. In this example, we'll take the concept of bitmap-based charting and plot 150,000 data points, complete with a border and labels instead of just a bitmap containing colored pixels.
I started this example by taking the code from the last example in my previous post, and extending it to include text that adds context to the visualization. First, let's take a look at the example in action.
Click the "Generate Data Set" button to view the data visualization.
You'll notice that the data visualization is almost exactly the same. There have just been labels applied to both the horizontal and vertical axes, and a caption at the top of the chart image. One thing to note here, all of the text in this is actually drawn directly in the bitmapdata used as the image source. These are not text overlays.
It's actually pretty simple how all of this works together. The textField object is an instance of a UITextField component. You can just set the text of the textField instance, adjust the size accordingly so that no text is clipped, and use the BitmapData's draw() method to render the text inside of the BitmapData object. The matrix is used to translate the x/y position of the textField component so that you can control exactly where the text is displayed.
textField.text = "Visualizing " + nf.format(datum.length) + " data points";
textField.width = textField.textWidth + 10;
matrix.identity();
matrix.translate( ( w-textField.width)/2, 5 );
bd.draw( textField, matrix );
Below, you will find the full source code for this sample application. In this example, the data is rendered, and then the grid lines are drawn and text is overlaid as appropriate for the header and axis labels.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
scriptTimeLimit="500" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.formatters.NumberFormatter;
import mx.core.UITextField;
import flash.utils.getTimer;
private var datum : Array = [];
private var textField : UITextField = new UITextField();
private var nf : NumberFormatter = new NumberFormatter();
private function generateData() : void
{
datum = [];
var i : int = 0;
while ( i < 150000 )
{
var o : Object = {};
o.x = .5 + Math.sin(i) * Math.random() * .5;
o.y = .5 + Math.cos(i) * Math.random() * .5;
datum.push( o );
i++;
}
renderData();
}
private function renderData() : void
{
nf.precision = 1;
var i : Number = 0;
var w : int = chart.width;
var h : int = chart.height;
var borderSize : int = 30;
var chartW : int = w - 2*borderSize;
var chartH : int = h - 2*borderSize;
var bd : BitmapData = new BitmapData( w, h, false, 0xFFFFFF );
var color : int;
//render each data point
for each (var o : Object in datum)
{
color = o.y * 0xFFFFFF;
//set pixels in a cross-shape
bd.setPixel( borderSize+(o.x * chartW), borderSize+(chartH-(o.y * chartH)), color );
bd.setPixel( borderSize+(o.x * chartW)+1, borderSize+(chartH-(o.y * chartH)), color );
bd.setPixel( borderSize+(o.x * chartW)-1, borderSize+(chartH-(o.y * chartH)), color );
bd.setPixel( borderSize+(o.x * chartW), borderSize+(chartH-(o.y * chartH))+1, color );
bd.setPixel( borderSize+(o.x * chartW), borderSize+(chartH-(o.y * chartH))-1, color );
}
var segments : int = 10;
var interval : Number = chartW/segments;
var q:Number;
var labelCount : int = 0;
var labelIncrement : Number = .1;
var matrix : Matrix = new Matrix();
matrix.identity();
matrix.translate( borderSize-5, h - borderSize );
//render verical line overlays
for ( i = borderSize; i<=w-(borderSize-2); i += interval )
{
for ( var j : int = borderSize; j < h-borderSize; j ++ )
{
bd.setPixel( i, j, 0 );
}
//add horizontal labels
textField.text = nf.format(labelCount * labelIncrement);
bd.draw( textField, matrix );
matrix.translate( interval, 0 );
labelCount++;
}
//render horizontal line overlays
interval = chartH/segments;
labelCount = 0;
matrix.identity();
matrix.translate( 7, h - (borderSize + 10) );
for ( i = borderSize; i<=h-(borderSize-2); i += interval )
{
for ( j = borderSize; j < w-borderSize; j ++ )
{
bd.setPixel( j, i, 0 );
}
//add verical labels
textField.text = nf.format(labelCount * labelIncrement);
bd.draw( textField, matrix );
matrix.translate( 0, -interval );
labelCount++;
}
//add header
nf.precision = 0;
textField.text = "Visualizing " + nf.format(datum.length) + " data points";
textField.width = textField.textWidth + 10;
matrix.identity();
matrix.translate( ( w-textField.width)/2, 5 );
bd.draw( textField, matrix );
chart.source = new Bitmap( bd );
}
]]>
</mx:Script>
<mx:ApplicationControlBar
dock="true">
<mx:Button
label="Generate Data Set"
click="generateData()" />
</mx:ApplicationControlBar>
<mx:Image
id="chart"
top="10" bottom="10" left="10" right="10"
cacheAsBitmap="true"
resize="renderData()" />
</mx:Application>
You can launch this application in a new window at:
http://www.cynergysystems.com/blogs/blogs/andrew.trice/bitmapcharts/main.html
Or view the full sorce at:
http://www.cynergysystems.com/blogs/blogs/andrew.trice/bitmapcharts/srcview/index.html






