Bug in StackedXYAreaRenderer2 with TimeTableXYDataset???

A free public discussion forum for the JFreeChart class library.

Bug in StackedXYAreaRenderer2 with TimeTableXYDataset???

Postby wherisat » Tue Jun 03, 2008 10:38 pm

Hello,

Versions bug seen in:
1.0.9
SVN Revision 1041 (downloaded on 6/3/08 5pm EST)

Similar BUT different posts:
http://www.jfree.org/phpBB2/viewtopic.php?t=24763&highlight=timetablexydataset+bug

Data for screen shot below
Code: Select all
   
    Minute aX1 = new Minute(0, 0, 1, 1, 1970);
    Minute aX2 = new Minute(59, 23, 1, 1, 1970);
    Minute bX1 = new Minute(0, 0, 1, 1, 1970);
    Minute bX2 = new Minute(40, 18, 1, 1, 1970);

    dataset.add(aX1, 0.1, "A");                                 
    dataset.add(aX2, 0.1, "A");
    dataset.add(bX1, 0.1, "B");
    dataset.add(bX2, 0.1, "B");


Screenshot
Image
http://img141.imageshack.us/my.php?image=jfreechartbugdh4.png

Discussion
There are two time periods, B ends before A. But as shown in the screenshot, when rendered, they both end at the same time. Also, something very strange is happening in the dataset. If you use the code below to print up the dataset right after the last dataset.add call, you'll see the output below:

Code: Select all
for (int s = 0; s < dataset.getSeriesCount(); s++)
{
System.out.println("SERIES: " + dataset.getSeriesKey(s));
for (int i = 0; i < dataset.getItemCount(s); i++)
{
    System.out.println("X: " + dataset.getStartX(s, i) + ", " + dataset.getX(s, i) + ", " + dataset.getEndX(s, i));
    System.out.println("Y: " + dataset.getStartY(s, i) + ", " + dataset.getY(s, i) + ", " + dataset.getEndY(s, i));
  }
}
SERIES: A
X: 1.8E7, 1.8E7, 1.8059999E7
Y: 0.1, 0.1, 0.1
X: 8.52E7, 8.52E7, 8.5259999E7
Y: null, null, null
X: 1.0434E8, 1.0434E8, 1.04399999E8
Y: 0.1, 0.1, 0.1
SERIES: B
X: 1.8E7, 1.8E7, 1.8059999E7
Y: 0.1, 0.1, 0.1
X: 8.52E7, 8.52E7, 8.5259999E7
Y: 0.1, 0.1, 0.1
X: 1.0434E8, 1.0434E8, 1.04399999E8
Y: null, null, null



Minimum Working Example (yes, I use Netbeans)
Code: Select all
package yoy;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.labels.XYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2;
import org.jfree.chart.urls.StandardXYURLGenerator;
import org.jfree.chart.urls.XYURLGenerator;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.xy.TableXYDataset;

public class JFreeChartIssue extends javax.swing.JFrame
{
    private final JFreeChart chart;
    private final ChartPanel chartPanel;
    private final TimeTableXYDataset dataset = new TimeTableXYDataset();

    /** Creates new form JFreeChartIssue */
    public JFreeChartIssue()
    {
        initComponents();

        chart = createStackedTimeDomainAreaChart(
                "TITLE",
                "Time of Day",
                "Probability",
                dataset,
                PlotOrientation.VERTICAL,
                true,
                true,
                false);
        dataset.setDomainIsPointsInTime(false);
        chartPanel = new ChartPanel(chart);
        add(chartPanel, java.awt.BorderLayout.CENTER);
        pack();
    }

    public static JFreeChart createStackedTimeDomainAreaChart(String title,
            String xAxisLabel,
            String yAxisLabel,
            TableXYDataset dataset,
            PlotOrientation orientation,
            boolean legend,
            boolean tooltips,
            boolean urls)
    {

        if (orientation == null)
        {
            throw new IllegalArgumentException("Null 'orientation' argument.");
        }
        DateAxis xAxis = new DateAxis(xAxisLabel);
        NumberAxis yAxis = new NumberAxis(yAxisLabel);
        XYToolTipGenerator toolTipGenerator = null;
        if (tooltips)
        {
            toolTipGenerator = new StandardXYToolTipGenerator();
        }

        XYURLGenerator urlGenerator = null;
        if (urls)
        {
            urlGenerator = new StandardXYURLGenerator();
        }
        StackedXYAreaRenderer2 renderer = new StackedXYAreaRenderer2(
                toolTipGenerator, urlGenerator);
        renderer.setOutline(true);
        XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
        plot.setOrientation(orientation);

        plot.setRangeAxis(yAxis);  // forces recalculation of the axis range

        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
                plot, legend);
        return chart;

    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                         
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        pack();
    }// </editor-fold>                       

    final static Minute aX1 = new Minute(0, 0, 1, 1, 1970);
    final static Minute aX2 = new Minute(59, 23, 1, 1, 1970);
    final static Minute bX1 = new Minute(0, 0, 1, 1, 1970);
    final static Minute bX2 = new Minute(40, 18, 1, 1, 1970);
   
private void formWindowOpened(java.awt.event.WindowEvent evt) {                                 

    dataset.add(aX1, 0.1, "A");                                 
    dataset.add(aX2, 0.1, "A");
    dataset.add(bX1, 0.1, "B");
    dataset.add(bX2, 0.1, "B");
    chartPanel.revalidate();
}

    /**
     * @param args the command line arguments
     */
    public static void main(String args[])
    {
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new JFreeChartIssue().setVisible(true);
            }

        });
    }

    // Variables declaration - do not modify                     
    // End of variables declaration                   
}
wherisat
 
Posts: 10
Joined: Fri Mar 16, 2007 8:15 pm

Re: Bug in StackedXYAreaRenderer2 with TimeTableXYDataset???

Postby paradoxoff » Wed Jun 04, 2008 6:21 am

See the API doc:
API doc wrote:public interface TableXYDataset
extends XYDataset

A dataset containing one or more data series containing (x, y) data items, where all series in the dataset share the same set of x-values. This is a restricted form of the XYDataset interface (which allows independent x-values between series). This is used primarily by the StackedXYAreaRenderer.

You have 3 x-values in your dataset, and it is expected that these are valid for both series A and B. Since you do not have a real y-value for the second x-value in series A and the third x-value in series B you are getting null values when you request the y-values. The second (null) value in series A is apparently interpreted as zero (which seems to be a reasonable behaviour). The only real question for me is why the third (null) y-value of series B is not interpreted as zero as well but is simply interpreted as "no change compared to the last data item".
paradoxoff
 
Posts: 1191
Joined: Sat Feb 17, 2007 1:51 pm

Thanks

Postby wherisat » Wed Jun 04, 2008 3:09 pm

I missed the javadoc on that interface, thanks for the redirect. Maybe a exception or at least a logger.warning when all series in the dataset don't share the same set of x-values is appropriate?

Artificially adding the same x-values for each series fixed the issue.
wherisat
 
Posts: 10
Joined: Fri Mar 16, 2007 8:15 pm

Postby david.gilbert » Wed Jun 04, 2008 8:15 pm

I updated the Javadocs to make this a little clearer. I don't want the code to generate any exception because I think it is reasonable for calling code to rely on this behaviour (that is, it is a feature not a bug).
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Ask your company to buy the JFreeChart Developer Guide
:idea: Check out other products sold by my company Object Refinery Limited
david.gilbert
JFreeChart Project Leader
 
Posts: 11341
Joined: Fri Mar 14, 2003 10:29 am

Ok...is this a bug?

Postby wherisat » Thu Jun 05, 2008 3:55 pm

I've been artificially adding x-coordinates to satisfy the same-x points for all series requirement. However, I've run into another issue. The following data yields the chart below, regardless of setting setRoundXCoordinates to true or false. Briefly browsing the javadocs doesn't seem to explain this.

Here is the new data:

Code: Select all
final static Minute x1 = new Minute(0, 0, 1, 1, 1970);
final static Millisecond x2pre = new Millisecond(999, 59, 39, 18, 1, 1, 1970);
final static Minute x2 = new Minute(40, 18, 1, 1, 1970);
final static Millisecond x2post = new Millisecond(1, new Second(0, x2));
final static Minute x3 = new Minute(59, 23, 1, 1, 1970);

//A Goes from x1 to x2
dataset.add(x1, 0.1, "A");
dataset.add(x2pre, 0.1, "A");
dataset.add(x2, 0.5, "A");
dataset.add(x2post, 0.0, "A");
dataset.add(x3, 0.0, "A");

//B goes from x2 to x3
dataset.add(x1, 0.0, "B");
dataset.add(x2pre, 0.0, "B");
dataset.add(x2, 0.5, "B");
dataset.add(x2post, 0.5, "B");
dataset.add(x3, 0.5, "B");


And the resultant image:
Image
http://img149.imageshack.us/img149/9492/jfreechartbuglb2.th.png

Discussion
The issue here is that the blue series, B, has a value of 0.0 until just before the X2 marker, but in the graph it has a interpolated value (I'm guessing, between 0.0 and 0.5). Doesn't make sense. I may have to go to stacked bars for what I'm doing, but that would be far uglier. Any help is appreciated.
wherisat
 
Posts: 10
Joined: Fri Mar 16, 2007 8:15 pm

Postby david.gilbert » Thu Jun 05, 2008 4:21 pm

From a quick look, the problem seems to be triggered by mixing Millisecond and Minute periods in a single dataset - the millisecond data items are being dropped. It will be something to do with the way that Comparable is implemented, and very likely a bug, but I don't have the time to investigate further right now.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Ask your company to buy the JFreeChart Developer Guide
:idea: Check out other products sold by my company Object Refinery Limited
david.gilbert
JFreeChart Project Leader
 
Posts: 11341
Joined: Fri Mar 14, 2003 10:29 am

Exactly right

Postby wherisat » Thu Jun 05, 2008 5:02 pm

You're right - changing everything to Millisecond yields the appropriate graph. Thanks!

I don't think that Millisecond.compareTo is implemented for non-Millisecond instances, here is the current code:

Code: Select all
       
// CASE 2 : Comparing to another TimePeriod object
// -----------------------------------------------
else if (obj instanceof RegularTimePeriod) {
   // more difficult case - evaluate later...
   result = 0;
}


Here is my naive attempt:

Code: Select all
else if (obj instanceof RegularTimePeriod)
{
// more difficult case - evaluate later...
RegularTimePeriod rtp = (RegularTimePeriod) obj;
final long thisVal = this.getFirstMillisecond();
final long anotherVal = rtp.getFirstMillisecond();
result = (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
}
wherisat
 
Posts: 10
Joined: Fri Mar 16, 2007 8:15 pm


Return to JFreeChart - General

Who is online

Users browsing this forum: Bing [Bot], Yahoo [Bot] and 13 guests