Chart X and/or Y-axis

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
paololim
Posts: 11
Joined: Tue Jul 17, 2007 3:08 pm
Location: Hopewell Junction, NY

Chart X and/or Y-axis

Post by paololim » Tue Jul 31, 2007 3:12 pm

For some of the charts I am creating, when the values become very small (i.e. xxxE-9, xxxE-12) the scaling on the charts vanishes. Boxes/lines/etc are plotted correctly in the picture, the axis labels vanish. Is there some way in jfreechart to make the domain/number axis and/or the renderer handle really small values properly on the axes? Or is there something I am doing wrong?

The relevant code here is (I think):

Code: Select all

        
        CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.setBackgroundPaint(Color.lightGray);
        plot.setRangeGridlinesVisible(true);
        plot.setBackgroundPaint(new Color(0, 0, 0, 0));
        
        CategoryAxis domainAxis = plot.getDomainAxis();
        domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
        domainAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 10));
        domainAxis.setMaximumCategoryLabelWidthRatio(20.0f);

        NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
        yAxis.setAutoRange(true);
        yAxis.setAutoRangeIncludesZero(false);
Last edited by paololim on Tue Jul 31, 2007 6:21 pm, edited 1 time in total.

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 Jul 31, 2007 4:02 pm

I need to fix the way small numbers are handled by default. The workaround is to replace the tick unit source for each axis:

Code: Select all

        NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis();
        domainAxis.setStandardTickUnits(new StandardTickUnitSource());
        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        rangeAxis.setStandardTickUnits(new StandardTickUnitSource());
David Gilbert
JFreeChart Project Leader

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

Priya Shivapura
Posts: 59
Joined: Fri Feb 23, 2007 7:41 am

Post by Priya Shivapura » Tue Jul 31, 2007 4:14 pm

You would also need to add the following line along with what David has mentioned

Code: Select all

rangeAxis.setAutoRangeMinimumSize(...)
This will prevent the items being squished to the middle of the chart.

Regards,
Priya

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 Jul 31, 2007 4:23 pm

Thanks Priya, I hadn't spotted that. I'll add a note to the documentation.
David Gilbert
JFreeChart Project Leader

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

RichardWest
Posts: 844
Joined: Fri Oct 13, 2006 9:29 pm
Location: Sunnyvale, CA

Post by RichardWest » Tue Jul 31, 2007 5:33 pm

You could also experiment with the DivNumberFormat class that DavidThi808 has posted in Thousands, millions, billions, & gazillions?. For this application, the magnitude would be 1E-12. If I and/or he can get the axis up and running, that would further address the problem.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

paololim
Posts: 11
Joined: Tue Jul 17, 2007 3:08 pm
Location: Hopewell Junction, NY

Post by paololim » Wed Aug 08, 2007 8:58 pm

hi everyone.. thank you for your replies. they really helped! thanks.

Clandestino
Posts: 6
Joined: Tue Jul 25, 2006 2:49 pm

Post by Clandestino » Thu Aug 16, 2007 10:43 am

Hi,

I have the exact same problem. In my program, the chart displays values from a calculation. I re-use the same dataset. When the "calculate" button is pressed, the dataset is cleared, and when the calculation is done the dataset is updated with fresh data.

I have made sure that davids and Priyas tricks are performed whenever the dataset changes.

Code: Select all

        NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis();
        domainAxis.setStandardTickUnits(new StandardTickUnitSource());
        domainAxis.setAutoRangeMinimumSize(1e-20); 

        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        rangeAxis.setStandardTickUnits(new StandardTickUnitSource());
        rangeAxis.setAutoRangeMinimumSize(1e-20); 
What did I do wrong? What auto-range minimum size should I use?

Clandestino
Posts: 6
Joined: Tue Jul 25, 2006 2:49 pm

Post by Clandestino » Thu Aug 30, 2007 5:27 pm

Hello again.

I have beaten my head bloody against this problem. The fix above helps me get the first plot right, but I need to be able to update the dataset when new calculation data is available.

Below is a sample program that illustrates what happens. Please excuse the messy code, it was difficult to distill the vital parts into a working program. When the plot is cleared (by giving it an empty dataset) the domain axis labels disappear. When the new data is injected they do not reappear.

If any of you wizards would try to run the problem and give me a solution (a hack, anything) I would be eternally greatful.

Code: Select all

import java.awt.EventQueue;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.StandardTickUnitSource;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.DefaultXYDataset;

public class TestAxisProblem {
	private static XYPlot plot;

	public static void main( String[] args ) {
                // The user has created a plot
		EventQueue.invokeLater( new Runnable() { public void run() { launchGUI(); } });
		try { Thread.sleep( 2000 ); } catch( Exception e ) {}

                // The user clicks the "calculate" button and data is cleared from memory.
		EventQueue.invokeLater( new Runnable() { public void run() { clearPlot(); } } );
		try { Thread.sleep( 2000 ); } catch( Exception e ) {}

                // When the calculation is finished, the plot is updated 
		EventQueue.invokeLater( new Runnable() { public void run() { updatePlotAgain(); } } );
	}
	
	private static void fixAxis() {
		plot.getRangeAxis().setAutoRangeMinimumSize(1e-50);
		plot.getRangeAxis().setStandardTickUnits(new StandardTickUnitSource());
		plot.getDomainAxis().setAutoRangeMinimumSize(1e-50);
		plot.getDomainAxis().setStandardTickUnits(new StandardTickUnitSource());
	}

	private static void launchGUI() {
		DefaultXYDataset firstDataset = new DefaultXYDataset();
		firstDataset.addSeries("Test", new double[][]{{0,5e-30},{100,2e-30}});
		
		JFreeChart chart = ChartFactory.createXYLineChart("Fjuff", "x", "y", firstDataset, PlotOrientation.HORIZONTAL, false, false, false);
		plot = (XYPlot) chart.getPlot();

		fixAxis();
		
		ChartPanel panel = new ChartPanel(chart);
		JFrame frame = new JFrame();
		frame.setContentPane(panel);
		frame.pack();
		frame.setVisible( true );
	}

	private static void clearPlot() {
		DefaultXYDataset emptyDataset = new DefaultXYDataset();
		
		plot.setDataset(emptyDataset);
		
		fixAxis();
	} 
	
	private static void updatePlotAgain() {
		DefaultXYDataset secondDataset = new DefaultXYDataset();
		secondDataset.addSeries("Test", new double[][]{{0,1e-19},{100,1e-20}});
		
		plot.setDataset(secondDataset);
		
		fixAxis();
	} 
}
Regards
Erik

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 » Thu Aug 30, 2007 5:48 pm

Hi Erik,

I've run your test code and it reproduces the problem for me so it shouldn't take too long to trace the bug...I hope!
David Gilbert
JFreeChart Project Leader

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

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 Aug 31, 2007 2:22 pm

I can see the problem (weakness / rounding errors in the auto tick unit selection mechanism), but I need some more time to experiment with better solutions.
David Gilbert
JFreeChart Project Leader

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

Clandestino
Posts: 6
Joined: Tue Jul 25, 2006 2:49 pm

Post by Clandestino » Fri Aug 31, 2007 3:06 pm

Yeah, I saw that too. Glad to hear that you are working on it! Any solution is ok :D

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 Aug 31, 2007 3:13 pm

A quick and dirty fix (if you need something right away):

Code: Select all

Index: /home/dgilbert/workspace/jfreechart-1.0.x-catstats/source/org/jfree/chart/axis/NumberAxis.java
===================================================================
--- /home/dgilbert/workspace/jfreechart-1.0.x-catstats/source/org/jfree/chart/axis/NumberAxis.java	(revision 162)
+++ /home/dgilbert/workspace/jfreechart-1.0.x-catstats/source/org/jfree/chart/axis/NumberAxis.java	(working copy)
@@ -1061,17 +1061,17 @@
      * @param dataArea  the area defined by the axes.

      * @param edge  the axis location.

      */

-   protected void selectHorizontalAutoTickUnit(Graphics2D g2,

-                                               Rectangle2D dataArea,

-                                               RectangleEdge edge) {

+    protected void selectHorizontalAutoTickUnit(Graphics2D g2,

+            Rectangle2D dataArea, RectangleEdge edge) {

 

-        double tickLabelWidth = estimateMaximumTickLabelWidth(

-            g2, getTickUnit()

-        );

+        double approxUnit = getRange().getLength() / 10.0;

+        NumberTickUnit unit0 = new NumberTickUnit(approxUnit);

+        double tickLabelWidth = estimateMaximumTickLabelWidth(g2, 

+                unit0);

 

-        // start with the current tick unit...

+        // start with the guess of 10 ticks...

         TickUnitSource tickUnits = getStandardTickUnits();

-        TickUnit unit1 = tickUnits.getCeilingTickUnit(getTickUnit());

+        TickUnit unit1 = tickUnits.getCeilingTickUnit(unit0);

         double unit1Width = lengthToJava2D(unit1.getSize(), dataArea, edge);

 

         // then extrapolate...

@@ -1105,9 +1105,11 @@
 

         double tickLabelHeight = estimateMaximumTickLabelHeight(g2);

 

-        // start with the current tick unit...

+        // start with a guess of 10 ticks along the axis...

+        double approxUnit = getRange().getLength() / 10.0;

+        NumberTickUnit unit0 = new NumberTickUnit(approxUnit);

         TickUnitSource tickUnits = getStandardTickUnits();

-        TickUnit unit1 = tickUnits.getCeilingTickUnit(getTickUnit());

+        TickUnit unit1 = tickUnits.getCeilingTickUnit(unit0);

         double unitHeight = lengthToJava2D(unit1.getSize(), dataArea, edge);

 

         // then extrapolate...

@@ -1113,8 +1115,8 @@
         // then extrapolate...

         double guess = (tickLabelHeight / unitHeight) * unit1.getSize();

         

-        NumberTickUnit unit2 

-            = (NumberTickUnit) tickUnits.getCeilingTickUnit(guess);

+        NumberTickUnit unit2 = (NumberTickUnit) tickUnits.getCeilingTickUnit(

+                guess);

         double unit2Height = lengthToJava2D(unit2.getSize(), dataArea, edge);

 

         tickLabelHeight = estimateMaximumTickLabelHeight(g2);
...but I'd like to work on something cleaner before committing anything to source control.
David Gilbert
JFreeChart Project Leader

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

Clandestino
Posts: 6
Joined: Tue Jul 25, 2006 2:49 pm

Post by Clandestino » Mon Sep 03, 2007 8:57 am

Thanks David.

Do you think you could post the full method(s) instead of the CVS diff?

Best regards,
Erik

Priya Shivapura
Posts: 59
Joined: Fri Feb 23, 2007 7:41 am

Post by Priya Shivapura » Tue Sep 04, 2007 4:14 pm

Hello David,

I was wondering if the issue related to tick generation for a dataset with large values is related to the problem described in this thread.

Here's the link to a question I had posted a few days back.

http://www.jfree.org/phpBB2/viewtopic.p ... 9fd6caab94



Regards,
Priya

Locked