Variation on GroupedStackedBarChart

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
opeongo
Posts: 10
Joined: Thu Oct 18, 2007 2:46 pm

Variation on GroupedStackedBarChart

Post by opeongo » Mon Jan 23, 2017 6:47 pm

I would like to make a Grouped Stacked Bar Chart, but I have a bit of a variation from the the way the GroupedStackedBarRenderer works.

The GroupedStackedBarRenderer works by grouping series data up, but that is not what I need to do.

In my case, I have a number of categories that I would like to group together. These categories have a hierarchy to them. For example, the category labels are like "Jan 04/Product 1", "Jan 04/Product 2", "Jan 04/Product 3", "Feb 04/Product 1", etc. In fact, the stacked bar chart already looks pretty good, but the problem is that the category labels do not fit along the domain axis because there just is not enough space. The axis labeling would look a lot better if I could spread the labels out on to multiple rows based on the the grouping hierarchy. This would mean having "Jan 04" displayed once, centered below the first three bars, and then the bars labeled "Product 1", "Product 2", "Product 3", and so on.

I had a look through through the API and did not see anything that was close to implementing this type of format. The SubCategoryAxis goes with the GroupedStackBarRenderer, and so does not fit my needs. The ExtendedCategoryAxis doesn't apply either. I think this means trying to write another Axis class (HierarchicalCategoryAxis?) to do the job. I would be willing to put the effort in to doing this, but I have a feeling like it wont be easy. I cannot see an easy of doing this by subclassing the CategoryAxis class. It looks like a complete rewrite would be required.

So, I'm looking on some feedback here about how to do this, to get some suggestions from the experienced crowd. Has this been done before? Should I even try? What should I know before starting?

Thanks!

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Variation on GroupedStackedBarChart

Post by paradoxoff » Mon Jan 23, 2017 9:12 pm

I don´t think that it would be too hard to implement this feature.With "this feature", I mean the option to fill a "second line" with additional category labels where each label is not assigned to a single category, but spans several categories.
You will need to override the drawCategoryLabels of CategoryAxis. In your overriden method, first call the method of the superclass. The logic for drawing the "spanning" labels would be very similar to that for drawing the ordinary category labels. Replacing lines like

Code: Select all

x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
with

Code: Select all

x0 = getCategoryStart(startCategoryIndex, ticks.size(), dataArea, edge);
x1 = getCategoryEnd(endCategoryIndex, ticks.size(), dataArea, edge);
where startCategoryIndex and endCategoryIndex are the indices of the first and last category that should be spanned by the current label, might be all that you need to do.
You will also have to implement a way to store the label (an instance of String) and the start and end category index for each "subLabel" (actually, it is more like a "superlabel"). A combination of an ArrayList<String> and an ArrayLIst<int[2]> or a custom class holding one string and two integers should do the job, but I will leave the final decision to you.

opeongo
Posts: 10
Joined: Thu Oct 18, 2007 2:46 pm

Re: Variation on GroupedStackedBarChart

Post by opeongo » Mon Jan 23, 2017 9:35 pm

I had a look in to this, and there were two questions that I had.

The first I think is rather simple, and that is how to tell the chart/plot that there will be more space required in the domain axis to draw the extra lines. It looks like I just override reserveSpace() and add the extra requirement.

The second is a bit more difficult I think. That is that I want to split the existing category labels in to two (or more) parts. I think that I can 'tell' the new Axis class to do this by passing it a regex that has capture groups. For my example the pattern would look something like "(.*)/(.*)". The first capture group would be the outer hierarchy, the second the inner category, etc. My class can use this to split out the hierarchy and draw the group label centered using the method that you suggest. The problem is when I call super.drawCategoryLabels(), how would it know not to draw the entire label? Can I rewrite the category labels so that only a part of the string is draw in the superclass? It looks like the dataset keys are immutable. Will I also have to override the CategoryDataset?

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Variation on GroupedStackedBarChart

Post by paradoxoff » Tue Jan 24, 2017 1:19 pm

1st question: JFreeChart stores the space required to draw the axes in an AxisState object. Please check the source code of CategoryAxis. The draw method returns a state object (an instance of AxisState) that encapsulates a "cursor", which is simply a double value. In the draw-method, the AxisState is initialized using the cursor parameter from the draw-method. The state object is then handed over to the drawCategoryLabels and drawLabel methods, where the cursor is moved further based on the space requirements for the category labels/axis label and based on the position of the axis. You can easily hand the AxisState over to a further drawXYZLabels methods and move the cursor as needed. There is no need to calculate and reserve AxisSpace explicitly.

2nd question: In order to control the "textual content" of the category tick labels, you can override the createLabel method of the CategoryAxis class. In the original version, the label strings are simply created by calling toString() on the category keys. In your overriden version, you could apply your regex to the return value of the toString() method and only use a fraction of the string to create the TextBlock that is part of the CategoryTick.

opeongo
Posts: 10
Joined: Thu Oct 18, 2007 2:46 pm

Re: Variation on GroupedStackedBarChart

Post by opeongo » Tue Jan 24, 2017 8:48 pm

Ok, that was relatively easy to implement. I now have a HierarchicalCategoryAxis class.

I made a change to the design, and I now pass a regex to the constructor that specifies how to split the hierarchical levels using the String.split() function. This might be a bit less flexible, but regexes can be troublesome (especially when quoting special characters) and hopefully this syntax will reduce specification errors.

So here is what I did:

1) I started with the SubCategoryAxis class and modified it.
1) Overrode the configure() method to scan the categories and determine the hierarchy.
2) Overrode reserveSpace() to add extra space for the number of extra lines of labels required to be drawn.
3) Overrode createLabel() to only show the last part of the hierarchy when called by the superclass.
4) Overrode draw() to first call the superclass to draw the last part of the label (drawCategoryLabels()), and second to draw the rest of the hierarchy.

There were a few fiddly bits to get the spacing right (such as to defer adding categoryLabelPositionOffset until after the final label), but otherwise no problems.

How should I go about making a contribution of this class? I had a look at the patch manager at sourceforge, and it doesn't look active. What are people doing these days?

Thanks for all the help, that sped up the process a lot I'm sure!

Locked