Multiple category datasets of different size

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
cme_st
Posts: 31
Joined: Thu Aug 31, 2006 9:02 am
Location: Lappeenranta, Finland

Multiple category datasets of different size

Post by cme_st » Mon Jan 08, 2007 8:04 am

Hi,

I've added the following code to LineChartDemo1.java, creating two datasets, working on a common set of categories. Unfortunately it doesn't work, if there are not all categories in each dataset.

Code: Select all

DefaultCategoryDataset ds = new DefaultCategoryDataset();

ds.addValue(1212, "Classes1", "JDK 1.0");
ds.addValue(2520, "Classes1", "SDK 1.2");
ds.addValue(2842, "Classes1", "SDK 1.3");
            
plot.setDataset(1, ds);
plot.setRenderer(1, new LineAndShapeRenderer());
The result looks like:
Image
but obviously there should be datapoints at JDK1, SDK1.2 and SDK 1.3, not in between.

I don't want to have gaps in the chart that would appear when adding null for the absent categories, so does anybody has an idea how to archieve that?

Chris

cme_st
Posts: 31
Joined: Thu Aug 31, 2006 9:02 am
Location: Lappeenranta, Finland

Post by cme_st » Wed Jan 10, 2007 12:00 pm

Ideas?

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Wed Jan 10, 2007 12:04 pm

This is a bug. I'll look at it, but I suspect the fix may take a bit of work.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

cme_st
Posts: 31
Joined: Thu Aug 31, 2006 9:02 am
Location: Lappeenranta, Finland

Post by cme_st » Fri Jan 12, 2007 10:11 am

Hi,

I managed to produce the following image, which is the expected result of the above code.
Image

It's quite a quick&dirty implementation by subclassing the LineAndShapeRenderer, but maybe the idea is useful, if you consider to fix this bug within the library.

Inherited is the drawItem() method, the code is copied from the base implementation. Inserted/replaced are the lines:

Code: Select all

determineAllCategories();
double x1 = domainAxis.getCategoryMiddle(allCategories.indexOf(
		dataset.getColumnKey(column)), allCategories.size(), 
		dataArea, plot.getDomainAxisEdge());
double x0 = domainAxis.getCategoryMiddle(
		allCategories.indexOf(dataset.getColumnKey(column - 1)), 
		allCategories.size(), dataArea, 
		plot.getDomainAxisEdge());
Where determineAllCategories() is:

Code: Select all

private ArrayList<Comparable> allCategories;
		
protected void determineAllCategories() {
	if (allCategories == null) {
		allCategories = new ArrayList<Comparable>();
		int datasetCount = getPlot().getDatasetCount();
		for (int i = 0; i < datasetCount; i++) {
			CategoryDataset dataset = getPlot().getDataset(i);
			for (int j = 0; j < dataset.getColumnCount(); j++) {
				Comparable col = dataset.getColumnKey(j);
				if (!allCategories.contains(col))
					allCategories.add(col);
			}
		}					
	}
}
The complete code can be found here: (fully runable)

So a (cached) list of all categories is created. This could be provided by the plot later on. The list needs to have the correct order and every name is to be unique. This list is used to paint the items resp. to determine the item's and line's x starting position.

Does this help you at all? Do you think it makes sense?

Chris


Complete code:

Code: Select all

private static class XLineAndShapeRenderer extends LineAndShapeRenderer {
		
// ----8<---- START Custom changes ----8<---------------------------------------
		private ArrayList<Comparable> allCategories;
		
		protected void determineAllCategories() {
			if (allCategories == null) {
				allCategories = new ArrayList<Comparable>();
				int datasetCount = getPlot().getDatasetCount();
				for (int i = 0; i < datasetCount; i++) {
					CategoryDataset dataset = getPlot().getDataset(i);
					for (int j = 0; j < dataset.getColumnCount(); j++) {
						Comparable col = dataset.getColumnKey(j);
						if (!allCategories.contains(col))
						  allCategories.add(col);
					}
				}					
			}
		}
// ----8<---- END Custom changes ----8<-----------------------------------------
		
		@Override
		public void drawItem(Graphics2D g2, CategoryItemRendererState state,
				Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
				ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
				int pass) {
			
// ----8<---- START Custom changes ----8<---------------------------------------
			determineAllCategories();			
// ----8<---- END Custom changes ----8<-----------------------------------------
			
			// do nothing if item is not visible
			if (!getItemVisible(row, column)) {
				return;   
			}
			
			// do nothing if both the line and shape are not visible
			if (!getItemLineVisible(row, column) 
					&& !getItemShapeVisible(row, column)) {
				return;
			}
			
			// nothing is drawn for null...
			Number v = dataset.getValue(row, column);
			if (v == null) {
				return;
			}
			
			PlotOrientation orientation = plot.getOrientation();
			
			// current data point...
// ----8<---- START Custom changes ----8<---------------------------------------
			/*double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), 
					dataArea, plot.getDomainAxisEdge());*/
			double x1 = domainAxis.getCategoryMiddle(allCategories.indexOf(
					dataset.getColumnKey(column)), allCategories.size(), 
					dataArea, plot.getDomainAxisEdge());
// ----8<---- END Custom changes ----8<-----------------------------------------
			
			double value = v.doubleValue();
			double y1 = rangeAxis.valueToJava2D(value, dataArea, 
					plot.getRangeAxisEdge());
			
			if (pass == 0 && getItemLineVisible(row, column)) {
				if (column != 0) {
					Number previousValue = dataset.getValue(row, column - 1);
					if (previousValue != null) {
						// previous data point...
						double previous = previousValue.doubleValue();
// ----8<---- START Custom changes ----8<---------------------------------------
						/*double x0 = domainAxis.getCategoryMiddle(column - 1, 
								getColumnCount(), dataArea, 
								plot.getDomainAxisEdge());*/
						double x0 = domainAxis.getCategoryMiddle(
								allCategories.indexOf(dataset.getColumnKey(column - 1)), 
								allCategories.size(), dataArea, 
								plot.getDomainAxisEdge());						
//----8<---- END Custom changes ----8<-----------------------------------------
						
						double y0 = rangeAxis.valueToJava2D(previous, dataArea, 
								plot.getRangeAxisEdge());
						
						Line2D line = null;
						if (orientation == PlotOrientation.HORIZONTAL) {
							line = new Line2D.Double(y0, x0, y1, x1);
						}
						else if (orientation == PlotOrientation.VERTICAL) {
							line = new Line2D.Double(x0, y0, x1, y1);
						}
						g2.setPaint(getItemPaint(row, column));
						g2.setStroke(getItemStroke(row, column));
						g2.draw(line);
					}
				}
			}
			
			if (pass == 1) {
				Shape shape = getItemShape(row, column);
				if (orientation == PlotOrientation.HORIZONTAL) {
					shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
				}
				else if (orientation == PlotOrientation.VERTICAL) {
					shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
				}
				
				if (getItemShapeVisible(row, column)) {
					if (getItemShapeFilled(row, column)) {
						if (getUseFillPaint()) {
							g2.setPaint(getItemFillPaint(row, column));
						}
						else {
							g2.setPaint(getItemPaint(row, column));   
						}
						g2.fill(shape);
					}
					if (getDrawOutlines()) {
						if (getUseFillPaint()) {
							g2.setPaint(getItemOutlinePaint(row, column));   
						}
						else {
							g2.setPaint(getItemPaint(row, column));
						}
						g2.setStroke(getItemOutlineStroke(row, column));
						g2.draw(shape);
					}
				}
				
				// draw the item label if there is one...
				if (isItemLabelVisible(row, column)) {
					if (orientation == PlotOrientation.HORIZONTAL) {
						drawItemLabel(g2, orientation, dataset, row, column, y1, 
								x1, (value < 0.0));
					}
					else if (orientation == PlotOrientation.VERTICAL) {
						drawItemLabel(g2, orientation, dataset, row, column, x1, 
								y1, (value < 0.0));
					}
				}
				
				// add an item entity, if this information is being collected
				EntityCollection entities = state.getEntityCollection();
				if (entities != null) {
					addItemEntity(entities, dataset, row, column, shape);
				}
			}
		}
		
	}

cme_st
Posts: 31
Joined: Thu Aug 31, 2006 9:02 am
Location: Lappeenranta, Finland

Post by cme_st » Tue Jan 16, 2007 7:57 am

Is the provided code any useful for you?

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Tue Jan 16, 2007 12:40 pm

Yes and no. You have the right idea (looking at all the categories from all the datasets) but the implementation isn't so good. The determineAllCategories() method should only be called once, not every time drawItem() is invoked, and also not by every renderer.

I think the right approach will be to have the CategoryAxis pull in all the categories (from all the datasets plotted against that axis) before the rendering begins, then have each renderer use the category key (rather than index) to request the Java2D coordinates for the category.

I'll try to get this done for 1.0.4.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

cme_st
Posts: 31
Joined: Thu Aug 31, 2006 9:02 am
Location: Lappeenranta, Finland

Post by cme_st » Tue Jan 16, 2007 12:44 pm

david.gilbert wrote:Yes and no. You have the right idea (looking at all the categories from all the datasets) but the implementation isn't so good. The determineAllCategories() method should only be called once, not every time drawItem() is invoked, and also not by every renderer.

I think the right approach will be to have the CategoryAxis pull in all the categories (from all the datasets plotted against that axis) before the rendering begins, then have each renderer use the category key (rather than index) to request the Java2D coordinates for the category.

I'll try to get this done for 1.0.4.
Yeah, as I said, this is just a quick and dirty implementation. But I'm happy that you could use the idea. It would be great, if I found the fix in 1.0.4. Continue your great work!!!

Chris

Tom
Posts: 32
Joined: Mon Dec 11, 2006 2:50 pm

Post by Tom » Thu Feb 15, 2007 9:57 am

Hi David,
I'm replying to this old post again because you said that this bug could be fixed in 1.0.4, but it still doesn't work yet.

Unfortunately, I'm waiting for a solution on that problem. Do you already know when the next release will come out?

Is there any possibility to get an official patch for that issue earlier?

Thanks for your reply

Tom

cme_st
Posts: 31
Joined: Thu Aug 31, 2006 9:02 am
Location: Lappeenranta, Finland

Post by cme_st » Mon Feb 26, 2007 7:25 am

It would still also be helpful for me. :)

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Mon Feb 26, 2007 11:12 am

OK, I'll see what I can do.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

maimuta
Posts: 1
Joined: Fri Oct 26, 2007 3:33 pm
Location: US

Post by maimuta » Fri Oct 26, 2007 3:41 pm

I was experiencing this problem using JFreeChart 1.0.3 with both line charts and bar charts with multiple range axes. I found this post and decided to try to upgrade to 1.0.6 to see if it had been fixed. I am still experiencing the same problem. Can anyone confirm that this is still an issue so that I can know that I have not missed anything? Also, if it is still unresolved, does anyone have an idea of when it will be resolved?

Thanks

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Fri Oct 26, 2007 3:49 pm

Unfortunately this limitation still exists. I'll revisit the problem after I've released JFreechart 1.0.7 (I'm hoping before the end of Oct).
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

Tom
Posts: 32
Joined: Mon Dec 11, 2006 2:50 pm

Post by Tom » Wed Jan 30, 2008 1:32 pm

I just saw that JFreeChart 1.0.9 was released, is it working now?

Tom
Posts: 32
Joined: Mon Dec 11, 2006 2:50 pm

Post by Tom » Fri Feb 01, 2008 10:56 am

Still doesn't work :-(

Locked