connectedpixel.com

actionscript, web development

Big Data Scrolling Flex Charts

Submitted by joelmay on 27 February, 2008 - 9:59am.

If your TextArea has a lot of text, you get a scroll bar. If your datagrid has too many rows or columns, you get scroll bars. If your Image is too large, you can put it inside a container with scrollbars. Charts, however, do not have scrollbars. Charts with too much data look like this:

I suppose it doesn't look too bad, but it is impossible to read the x-axis column labels. And of course, the dataset could be even bigger and the chart would be more unreadable.

You could put the chart inside a container with scroll bars, but that has a problem: the vertical axis scrolls out of view:


I'm facing this problem now, so I created a component called 'ScrollableAxisRenderer'. It's derived from AxisRenderer and used in place of an AxisRenderer. Here's what it gives you:


The scrollbar bits are skinnable. This example also has a zoom slider.

I suppose the zoom would be better if the bottom gutter didn't jump around. But I don't really need it right now, so I'm not going to worry about it. I just threw it in for the heck of it.

BarCharts are also supported.

How it works:

The ScrollableAxisRenderer contains another custom component: ChartDataScrollBar. When linked with the chart, it grabs the chart's dataprovider, wraps it in a SubRangeArrayColection and sets the chart's data provider to that. SubRangeArrayCollection implements IList and reveals only a subset of the inner collection; i.e. it acts as a filter. In psuedo-code:


The scrollbar's scrollPosition is bound to the SubRangeArrayCollection's startColumn property. Moving the scrollbar changes the data subset that the chart sees.

Note: While it would be easier to set the filterFunction property on the original collection, that would make the data unshareable. For example, if 2 charts were using that same collection as a dataprovider, using the filterFunction property for the first chart would break the second.

The next hurdle is getting the vertical range to stay fixed (in the case of a ColumnChart). As I scrolled the data, the axis values would jump around like crazy as the chart recalculated the optimal vertical range. I found that simply setting the min and max on the axis to themselves made them explicitly set. They were no longer recalculated as the data changed, and they did not jump around anymore.

Pseudo-code

I'm sure it has bugs in it. If you find anything, please let me know. If you have any suggestions about how to code it better, I would appreciate it. The updateProperties(), measure(), etc. are a bit voodoo-ish to me. When something should be updated in updateproperties() vs. updateDisplayList() is sometimes ambiguous.

The scrollbar skinning for this thing has a nasty hack. The ChartDataScrollBar wraps the Flex ScrollBar component which has a hard-coded minimum width of 16. I tried using that width here, but it looked ugly on top of the chart's axis. It really needs to be the same width as the axis, which is about 10 pixels. I can't override the 16-pixel value because the number is private (or internal or whatever). Therefore, I made the scrollbar skins 16 pixels wide, but half the width is invisible (alpha = 0). If you skin this thing, do the same thing. If you scale-9 the thumb, only include the ends of the thumb, not the width. If you change the axis stroke width, you'll have to fix the skin.

Again, if anyone has a better solution, please share it.

Here's the source.

Ely (not verified) Says:

Love it.

27 February, 2008 - 12:46pm

Joel -- this is a great modification, and something that a _lot_ of people ask for often. If you're up for it, you should make it more widely available...submit it to one of the open source flex component libraries (I would suggest submitting it to the SDK, but I don't think charts are part of the open source codebase).

Anyway, great enhancement.
Ely Greenfield
Flex SDK.

joelmay Says:

I'd be glad to open source it

27 February, 2008 - 1:53pm

Ely,

I'll try to figure out where to submit it. I'm not familiar with the process of submitting code to an open source library. If you know precisely where I should submit this, please let me know. Or feel free to set it up yourself if you know where you want to put it. My feelings will not be hurt.

I might shoot you an email for advice on how to do this. I'll dig in over the weekend.

If anyone else can tell me precisely how to do this, or point me to the right place, please speak up.

Thanks,
Joel

Tyler (not verified) Says:

Awesome

27 February, 2008 - 1:31pm

This is so valuable. Great solution for a common problem.

Lordy (not verified) Says:

This is an amazing

27 February, 2008 - 1:54pm

This is an amazing component, please submit it to one of open source libraries like Flexlib or something. Any component that enhances data visualization, gets a big thumbs up from me.

joelmay Says:

FlexLib looks like a good

27 February, 2008 - 3:14pm

Lordy,

FlexLib looks like a good place to put this.

Actually, I didn't even know about it until now. It's a great resource. It has some really useful components.

Thanks,
Joel

Alex Frid (not verified) Says:

Cool

6 March, 2008 - 12:35am

Beautiful, especially the internal graph scroll !, really impressive work.

Varun Shetty (not verified) Says:

Amazing Stuff

15 March, 2008 - 6:05pm

you got some great stuff happenin.. love the way you have modified the graph.. and there are so many other things on this site to learn from ... u shud sure write often.. :)

nick (not verified) Says:

scroll bug?

25 March, 2008 - 9:51am

Hey Joel, great component, I'm having some problems with using the firstIndexOffset property though. I'm basically trying to do something like this:

private function scrollHandler(event:MouseEvent):void{
scrollAxisRenderer.firstIndexOffset += event.delta;
}

But any changes to the firstIndexOffset don't seem to be reflected in the graph. Is this a bug or am I just not doing it right? Thanks.

-Nick

joelmay Says:

Thanks for finding the problem

25 March, 2008 - 1:31pm

Nick,
I'll look into it and post something in the next couple days. Thanks for trying it out and finding the problem.
Joel

Benjie (not verified) Says:

Hi Joel, great work on this

28 March, 2008 - 6:05am

Hi Joel, great work on this - just what I was looking to implement!

I have experienced some issues when using an ArrayCollection as the chart's dataProvider, i.e. the innerDP does not populate at 'innerDP = _chart.dataProvider as IList;' (ChartDataScrollBar).

It works fine with an XMLList however like in your example - maybe I'm doing something wrong?

joelmay Says:

Thanks Benjie

28 March, 2008 - 3:38pm

I'll look into it this weekend. I have to actually use this component for a client project this weekend. I'll be exercising it more and make sure I fix this ArrayCollection issue.

Benjie (not verified) Says:

Hi Joel, I've managed to get

31 March, 2008 - 3:13am

Hi Joel, I've managed to get it working in code now...you just need to explicitally state that the horizontal property of the ScrollAxisRenderer to either true or false.

e.g

scroll.axis = catAxis;
scroll.placement = "bottom";
scroll.maxVisibleColumns = 8;
scroll.horizontal = true;

Hope this helps :-)

joelmay Says:

Thanks Benjie, thank you,

31 March, 2008 - 10:06am

Thanks Benjie, thank you, thank you.
I'm buried at the moment, and that helps huge.

Dean (not verified) Says:

Hi Benjie, I'm also trying

27 June, 2008 - 5:01pm

Hi Benjie,

I'm also trying to use an ArrayCollection as a dataprovider, but can't get it to work. I've set horizontal="true" for the component as you discovered, but it still doesn't work.

Can you provide any tips or other code mods that worked for you?

Thanks!
-Dean

sachin (not verified) Says:

could you please let me know

31 March, 2008 - 6:09am

could you please let me know what change is need to show lables on left hand side of barchart while keeping scrollbar on right hand side.
Please let me know as earky as possible

joelmay Says:

Sachin, I honestly didn't

31 March, 2008 - 10:21am

Sachin,
I honestly didn't think of that. It should be able to handle that. But the way I implemented it, the scrollbar connected to the axis renderer. Of course, the labels are also. Hmm. Before I submit it to flexlib, I'll try to address this issue. But in the meantime, if you look at the code, there is another component buried in there called 'ChartDataScrollBar'. You can put that anywhere you want, even on top of the other axis. You connect it to the chart, and it should work.

It's just a bit more work this way. ScrollableAxisRenderer is easier to use.

Unfortunately, I'm am under the gun right now, so I can't give you sample code using ChartDataScrollBar. Maybe in a few days.

Anonymous (not verified) Says:

Hi, I ahve done this

1 April, 2008 - 10:55pm

Hi,
I ahve done this .I have replaced following line of code:
var bInverted:Boolean = this.placement == "right";

in calcVertScrollBarRect() method of Scrollable Axis Renderer class with the following line:

var bInverted:Boolean = this.placement == "left";

and i n addition to this I have removed placement="right" property from VerticalAxisRenderer.

Thanks alot for creating such a wonder full sample

joelmay Says:

Thanks for figuring this

3 April, 2008 - 11:25am

Thanks for figuring this out. When I get to cleaning the code up so I can submit it to flexlib, I'll incorporate your idea.

Putting the scrollbar on the side opposite the axis labels is actually a good idea. The scrollbar tends to visually separate the labels from the graph.

Marcel (not verified) Says:

Got it working with AreaChart

7 April, 2008 - 3:36am

I really love this piece of work!

I got it working with AreaChart as well. I noticed that if you want a smooth update of your scrollbar using a DateTimeAxis with irregular dates, you need to add
_scrollBar.invalidateDisplayList();
_scrollBar.invalidateProperties();
_scrollBar.invalidateSize();
in ChartDataScrollBar just after the updateScrollBar line 444. Not sure if you need all these invalidate functions, but it works better now.

BUT, one thing I can't seem to figure out is that when you update your dataProvider, it just shows you all your datapoints and the whole zoom and scroll stuff does not work. It seems to have lost its connection with the original chart or something. Any ideas?

(but thanks for the great work, it made a big impression here...!)

joelmay Says:

Thanks Marcel

8 April, 2008 - 9:01am

I'll add those invalidates.

As far as things goofing up when you update your dataProvider, I don't know at the moment. I'm buried with work right now. I'll take a look as soon as I can. I suspect that the dataProvider setter needs more stuff. Something needs to be reset. If you figure it out, please post here.

Thanks,
Joel

Benjie (not verified) Says:

That sorts out the problem I

15 April, 2008 - 4:05am

That sorts out the problem I was having with the redraw of the scrollbar after using the slider. The scrollbar would default to a mininum length of half the axis whereas with this code it eliminates that issue.

Thanks Marcel :)

bobby (not verified) Says:

great work

9 April, 2008 - 10:32pm

great stuff... nice work.. thanks for sharing...

would love to get it working with my dynamically populated ArrayCollection...

will keep trying to work it out

(it seems to randomly populate it but only very occassionally)

joelmay Says:

Sorry it's not working

11 April, 2008 - 1:54pm

Sorry it's not working right. Fixing the problems with this thing is high on my list, but unfortunately, I have work that is higher on my list.

If you figure out the problem, please post it here.
Thanks,
Joel

bobby (not verified) Says:

will do

17 April, 2008 - 12:27am

Hi

No problems... if I figure it out will post the solution.

I know the problem too much to do and never enough time!!

thanks for you work

Bobby

Prabhu Anonymous (not verified) Says:

Need Help

21 April, 2008 - 5:20am

Hi

I Want same scroll bars in line chart and Area chart.
Its not work in Line Chart & Area Chart.

prabahar Says:

Same

23 April, 2008 - 11:33pm

i Want line chart and Area Chart

joelmay Says:

You're right

30 April, 2008 - 3:28pm

It should support those charts. I'll double check when I get back to this and make sure that they get supported.
Joel

Anonymous (not verified) Says:

Line charts please

19 May, 2008 - 8:16am

So do you have it in line charts? Tf you do That would be awesome

Anonymous (not verified) Says:

firstIndexOffset fix

30 April, 2008 - 11:23am

Hey Joel,

I figured out what the problem was, I think you're missing a call to invalidateDisplayList() in the "firstIndexOffset" set function of ChartDataScrollBar.as. This was preventing the chart from reflecting the changes made. Hope this helps.

-Nick

joelmay Says:

Thanks Nick

30 April, 2008 - 3:29pm

I'll put that in. Maybe this weekend I'll get back to this and clean it up.
Joel

McUsher (not verified) Says:

pseudo-code

23 May, 2008 - 1:53am

Just for the record:
As you mentioned yourself
"axis.minimum = axis.minimum;
axis.maximum = axis.maximum;"

I am not 100% sure, but i think it sounds more reasonable using:
axis.assignedMinimum = axis.computedMinimum

...though i assume does the precise same thing in the bytecode

Sunil Bannur (not verified) Says:

Very Cool

26 May, 2008 - 12:16am

This is a cool extension of AxisRenderer, a request that I come across very often.

Sunil Bannur
Engineer - Flex Data Visualization
Adobe

Mat (not verified) Says:

Problem with arraycollection changing dataprovider

2 June, 2008 - 2:01am

Hi,

I try to use this renderer in one of my charts but i've got problem when i change chart dataprovider (arraycollection), the scrollbar doesnt update...ant idea ?

Thank's for your work.

Flex addict (not verified) Says:

I've got the same problem

17 June, 2008 - 1:44am

I've got the same problem too. I try to fix this problem but I failed... Is there other people with the same problem?

Viv (not verified) Says:

Same here

3 July, 2008 - 1:06am

Yes I am also having the same problem...

The Webmaster (not verified) Says:

Great Information

16 June, 2008 - 11:40am

This is really helpful for me and my website
You rock man
Thanks for this :)