Grid alignment problem when using multiple axes

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
hfontanez
Posts: 5
Joined: Mon Oct 06, 2014 5:09 pm
antibot: No, of course not.

Grid alignment problem when using multiple axes

Post by hfontanez » Thu Oct 09, 2014 8:36 pm

Version: 1.0.17 (confirmed on 1.0.19)
Java version: 1.6.0_30

Description:
This issue seems to be a bug, but we are not exactly sure. We are trying to display multiple series in a graph that has multiple Y-axes. The problem we are running into is that it appears that the grid lines up only with the primary (Y) axis. I am including pictures of the JFreeChart graph and a picture of the graph we are trying to duplicate. In the snapshot taken from our JFreeChart prototype, we included another potential issue: we are not exactly sure how to snap the major grid lines to the plot area bounds.

The second picture shows what we are trying to emulate with JFreeChart. Notice that each series has a unique scale and that both align perfectly with the plot area grid lines. Although this image is one of two log scales, it is also possible to have a LogAxis and a NumberAxis plotted and we would like the ability for the grid lines to align to both axes. We searched through the forum for a similar issue and could not find anything. We are also including the code used when the first snapshot was taken.

first picture - prototype: Image
second picture - what we're trying to emulate: Image

Code:

Code: Select all

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.RefineryUtilities;
import org.jfree.util.ShapeUtilities;
import org.jfree.util.UnitType;

public class GraphicsPrototype extends ApplicationFrame
{
   private static final long serialVersionUID = 1L;

   class MyDemoPanel extends DemoPanel
   {
      private static final long serialVersionUID = 1L;

      // The dataset
      private XYSeriesCollection dataset;

      // The currently selected point
      private XYItemEntity selectedPoint;

      public MyDemoPanel()
      {
         super(new BorderLayout());

         XYSeries series = new XYSeries("Analog");
         this.dataset = new XYSeriesCollection(series);
         NumberAxis rangeAxis = new NumberAxis("Y");
         rangeAxis.setRange(0.0, 1500.0);
         XYPlot plot = new XYPlot(this.dataset, new NumberAxis("X"), rangeAxis,
               new XYLineAndShapeRenderer());
         plot.getRenderer().setBaseToolTipGenerator(new StandardXYToolTipGenerator());
         plot.setBackgroundPaint(Color.lightGray);
         plot.setDomainGridlinePaint(Color.white);
         plot.setRangeGridlinePaint(Color.white);
         
         for (double k = 0; k < 50; k += 0.1)
         {
            series.add(k, 500.0);
         }
         //Set the marker size to zero to simulate a line.
         plot.getRenderer().setSeriesShape(0, ShapeUtilities.createDiamond(0));

         JFreeChart chart = new JFreeChart("Graphics Prototype", plot);
         addChart(chart);
         LegendTitle legend = (LegendTitle) chart.getSubtitle(0);
         legend.setPosition(RectangleEdge.RIGHT);
         legend.setMargin(new RectangleInsets(UnitType.ABSOLUTE, 0, 4, 0, 4));
         chart.setBorderPaint(Color.black);
         chart.setBorderVisible(true);

         ChartUtilities.applyCurrentTheme(chart);

         ChartPanel chartPanel = new ChartPanel(chart)
         {
            private static final long serialVersionUID = 1L;

            //The original method returns the xyentity at the location only if the marker size is non-zero
            //Override this method to return the xyentity at the location regardless of the marker size.
            public ChartEntity getEntityForPoint(int viewX, int viewY)
            {
               ChartEntity result = null;
               if (getChartRenderingInfo() != null)
               {
                  Insets insets = getInsets();
                  double x = (viewX - insets.left) / getScaleX();
                  double y = (viewY - insets.top) / getScaleY();
                  EntityCollection entityCollection = getChartRenderingInfo()
                        .getEntityCollection();
                  Collection entities = entityCollection.getEntities();
                  if (entities != null)
                  {
                     for (Object object : entities)
                     {
                        ChartEntity entity = (ChartEntity) object;
                        if (entity instanceof XYItemEntity)
                        {
                           Shape area = entity.getArea();
                           Rectangle2D rect = area.getBounds2D();
                           rect = new Rectangle2D.Double(rect.getX() - 1,
                                 rect.getY() - 1, 2, 2);
                           if (rect.contains(x, y))
                           {
                              return entity;
                           }
                        }
                     }
                  }
               }
               return result;
            }

            @Override
            public void mousePressed(MouseEvent e)
            {
               int x = e.getX(); // initialized point whenever mouse is pressed
               int y = e.getY();
               ChartEntity entity = getEntityForPoint(x, y);
               if (entity instanceof XYItemEntity)
               {
                  selectedPoint = (XYItemEntity) entity;
                  setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
               }
            }

            @Override
            public void mouseReleased(MouseEvent e)
            {
               setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
               int x = e.getX();
               int y = e.getY();
               if (selectedPoint != null)
               {
                  XYPlot plot = (XYPlot) getChart().getPlot();

                  for (int i = 0, n = plot.getDatasetCount(); i < n; i++)
                  {
                     XYDataset dataset = plot.getDataset(i);
                     if (dataset != null
                           && dataset.equals(selectedPoint.getDataset()))
                     {

                        // the following translation takes account of the fact
                        // that the chart image may have been scaled up or 
                        // down to fit the panel...
                        Point2D p = this
                              .translateScreenToJava2D(new Point(x, y));

                        // now convert the Java2D coordinate to axis
                        // coordinates...
                        ChartRenderingInfo info = this.getChartRenderingInfo();
                        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
                        double yy = plot.getRangeAxis().java2DToValue(p.getY(),
                              dataArea, plot.getRangeAxisEdge());
                        XYSeriesCollection collection = (XYSeriesCollection) dataset;
                        XYSeries series = collection.getSeries(selectedPoint
                              .getSeriesIndex());
                        int count = series.getItemCount();
                        for (int k = 0; k < count; k++)
                        {
                           series.updateByIndex(k, yy);
                        }
                        break;
                     }
                  }
               }
               //Finished drag, set the selected point to null
               selectedPoint = null;
            }

            @Override
            public void mouseDragged(MouseEvent e)
            {
               int x = e.getX();
               int y = e.getY();
               if (selectedPoint != null)
               {
                  XYPlot plot = (XYPlot) getChart().getPlot();

                  for (int i = 0, n = plot.getDatasetCount(); i < n; i++)
                  {
                     XYDataset dataset = plot.getDataset(i);
                     if (dataset != null
                           && dataset.equals(selectedPoint.getDataset()))
                     {

                        // the following translation takes account of the fact
                        // that the chart image may have been scaled up or 
                        // down to fit the panel...
                        Point2D p = this
                              .translateScreenToJava2D(new Point(x, y));

                        // now convert the Java2D coordinate to axis
                        // coordinates...
                        ChartRenderingInfo info = this.getChartRenderingInfo();
                        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
                        //Get the logical value
                        double yy = plot.getRangeAxis().java2DToValue(p.getY(),
                              dataArea, plot.getRangeAxisEdge());

                        XYSeriesCollection collection = (XYSeriesCollection) dataset;
                        XYSeries series = collection.getSeries(selectedPoint
                              .getSeriesIndex());
                        int count = series.getItemCount();
                        //Since this is a line running parallel to x axis, update the y value of all the points to the new value.
                        for (int k = 0; k < count; k++)
                        {
                           series.updateByIndex(k, yy);
                        }
                        break;
                     }
                  }
               }
            }
         };
         add(chartPanel, BorderLayout.CENTER);
         chartPanel.setPreferredSize(new java.awt.Dimension(500, 470));
         chartPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
      }

   }

   public GraphicsPrototype(String title)
   {
      super(title);
      setContentPane(createDemoPanel());
   }

   public JPanel createDemoPanel()
   {
      return new MyDemoPanel();
   }

   public static void main(String[] args)
   {
      GraphicsPrototype demo = new GraphicsPrototype(
            "JFreeChart: GraphicsPrototype.java");
      demo.pack();
      RefineryUtilities.centerFrameOnScreen(demo);
      demo.setVisible(true);
   }
}

hfontanez
Posts: 5
Joined: Mon Oct 06, 2014 5:09 pm
antibot: No, of course not.

Re: Grid alignment problem when using multiple axes

Post by hfontanez » Fri Oct 10, 2014 1:48 pm

I forgot to include these empty classes:

Code: Select all

import mycharts.GraphicsDemo.Phase;

import org.jfree.data.time.TimeSeries;

public class ForecastAnalogTimeSeries extends TimeSeries
{

	public ForecastAnalogTimeSeries(Phase phase)
	{
		super(phase);
	}
}
and:

Code: Select all

import org.jfree.data.time.TimeSeries;

public class HistoryDigitalTimeSeries extends TimeSeries
{
	public HistoryDigitalTimeSeries(String phase)
	{
		super(phase);
	}
}

Locked