Monday, December 22, 2008

JXLayer with JFreeChart Demo Application (Not Applet)

I wanted to run Dave Gilbert's JXLayer demo applet that uses JFreeChart. But my 32-bit Intel Mac won't run applets in Java 1.6. I do have Landon Fuller's SoyLatte implementation of Java 1.6 installed. So I converted the applet into an application and can run it from Eclipse. To run the demo in an application, first download the original zip file from Dave Gilbert's blog. (You may also need to download and rename the jxlayer.jar file if it is not in the zip file's lib directory.) You will replace the file JXLayerAppletDemo1.java with the file JXLayerApplicationDemo.java below. Create a new Java project in Eclipse with the updated source file. In the project properties, add these 3 libraries to the external jars:
jcommon-1.0.14.jar
jfreechart-1.0.11.jar
jxlayer.jar


JXLayerApplicationDemo.java:
/* -----------------------
 * JXLayerAppletDemo1.java
 * -----------------------
 * (C) Copyright 2008, by Object Refinery Limited.
 */

package demo.jxlayer;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Ellipse2D;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

//import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JPanel;

import org.jdesktop.jxlayer.JXLayer;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYDrawableAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.TickUnitSource;
import org.jfree.chart.axis.TickUnits;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.time.Month;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleInsets;

/**
 * An applet that shows a chart with a magnifying glass.
 */
//public class JXLayerAppletDemo1 extends JApplet {
public class JXLayerApplicationDemo extends JPanel {

  /**
   * Instantiate main frame and set visible
   */
  public static void main (String args[]) {
    final JXLayerApplicationDemo app = new JXLayerApplicationDemo();
    JFrame f = new JFrame("JFreeChart Magnifying Glass");
    f.addWindowListener (
        new WindowAdapter() {
          @Override
          public void windowClosing(WindowEvent e) {
            System.exit(0);
          }
        }
    );

    f.add(app, BorderLayout.CENTER);
    f.pack();
    f.setVisible(true);
  }


  /**
   * Constructs the demo application.
   */
  public JXLayerApplicationDemo() {
    super();
    XYDataset dataset = createDataset();
    JFreeChart chart = createChart(dataset);
    ChartPanel chartPanel = new ChartPanel(chart);
    JXLayer layer = new JXLayer(chartPanel);
    MagnifierUI ui1 = new MagnifierUI();
    layer.setUI(ui1);
    chartPanel.setPreferredSize(new java.awt.Dimension(750, 390));
    chartPanel.setPopupMenu(null);
    //setContentPane(layer);
    add(layer);
  }

  /**
   * Creates a sample chart.
   *
   * @param dataset  a dataset for the chart.
   *
   * @return A sample chart.
   */
  private static JFreeChart createChart(XYDataset dataset) {
    JFreeChart chart = ChartFactory.createTimeSeriesChart(
        "JFreeChart and JXLayer",
        null, "$ million", dataset,
        true, true, false);
    XYPlot plot = (XYPlot) chart.getPlot();
    DateAxis xAxis = (DateAxis) plot.getDomainAxis();
    xAxis.setLowerMargin(0.2);
    xAxis.setUpperMargin(0.2);
    xAxis.setStandardTickUnits(createStandardDateTickUnits());

    NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
    yAxis.setLowerMargin(0.2);
    yAxis.setUpperMargin(0.2);

    XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
    renderer.setBaseShapesVisible(true);
    renderer.setBaseLinesVisible(true);
    renderer.setSeriesShape(0, new Ellipse2D.Double(-5.0, -5.0, 10.0, 10.0));
    renderer.setSeriesShape(1, new Ellipse2D.Double(-5.0, -5.0, 10.0, 10.0));
    renderer.setSeriesStroke(0, new BasicStroke(3.0f));
    renderer.setSeriesStroke(1, new BasicStroke(3.0f, BasicStroke.CAP_ROUND,
        BasicStroke.JOIN_ROUND, 5.0f, new float[] {5.0f, 4.0f}, 0.0f));
    renderer.setSeriesFillPaint(0, Color.white);
    renderer.setSeriesFillPaint(1, Color.white);
    renderer.setUseFillPaint(true);

    renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
    renderer.setDefaultEntityRadius(6);

    renderer.addAnnotation(new XYDrawableAnnotation(
        new Month(4, 2005).getFirstMillisecond(), 600, 180, 100, 3.0,
        createPieChart()));
    renderer.addAnnotation(new XYDrawableAnnotation(
        new Month(9, 2007).getFirstMillisecond(), 1250, 120, 100, 2.0,
        createBarChart()));
    renderer.setBaseToolTipGenerator(
        new StandardXYToolTipGenerator("{0} = ({1}, {2})",
            new SimpleDateFormat("yyyy"),
            new DecimalFormat("$#,##0.00")));

    plot.setRenderer(renderer);
    return chart;
  }

  /**
   * Creates a sample dataset.
   *
   * @return A dataset.
   */
  private static XYDataset createDataset() {
    TimeSeries series1 = new TimeSeries("Division A", Year.class);
    series1.add(new Year(2005), 1520);
    series1.add(new Year(2006), 1132);
    series1.add(new Year(2007), 450);
    series1.add(new Year(2008), 620);
    TimeSeries series2 = new TimeSeries("Division B", Year.class);
    series2.add(new Year(2005), 1200);
    series2.add(new Year(2006), 1300);
    series2.add(new Year(2007), 640);
    series2.add(new Year(2008), 520);
    TimeSeriesCollection dataset = new TimeSeriesCollection();
    dataset.addSeries(series1);
    dataset.addSeries(series2);
    return dataset;
  }

  private static JFreeChart createPieChart() {
    DefaultPieDataset dataset = new DefaultPieDataset();
    dataset.setValue("Engineering", 43.2);
    dataset.setValue("Research", 13.2);
    dataset.setValue("Advertising", 20.9);
    PiePlot plot = new PiePlot(dataset);
    plot.setBackgroundPaint(null);
    plot.setOutlinePaint(null);
    plot.setBaseSectionOutlinePaint(Color.white);
    plot.setBaseSectionOutlineStroke(new BasicStroke(2.0f));
    plot.setLabelFont(new Font("Dialog", Font.PLAIN, 18));
    plot.setMaximumLabelWidth(0.25);
    JFreeChart chart = new JFreeChart(plot);
    chart.setBackgroundPaint(null);
    chart.removeLegend();
    chart.setPadding(RectangleInsets.ZERO_INSETS);
    return chart;
  }

  private static JFreeChart createBarChart() {
    DefaultCategoryDataset dataset = new DefaultCategoryDataset();
    dataset.addValue(10.0, "R1", "Q1");
    dataset.addValue(7.0, "R1", "Q2");
    dataset.addValue(8.0, "R1", "Q3");
    dataset.addValue(4.0, "R1", "Q4");
    dataset.addValue(10.6, "R2", "Q1");
    dataset.addValue(6.1, "R2", "Q2");
    dataset.addValue(8.5, "R2", "Q3");
    dataset.addValue(4.3, "R2", "Q4");
    JFreeChart chart = ChartFactory.createBarChart("Sales 2008", null,
        null, dataset, PlotOrientation.VERTICAL, false, false, false);
    chart.setBackgroundPaint(null);
    chart.getPlot().setBackgroundPaint(new Color(200, 200, 255, 60));
    return chart;
  }

  private static TickUnitSource createStandardDateTickUnits() {
    TickUnits units = new TickUnits();
    DateFormat df = new SimpleDateFormat("yyyy");
    units.add(new DateTickUnit(DateTickUnit.YEAR, 1,
        DateTickUnit.YEAR, 1, df));
    units.add(new DateTickUnit(DateTickUnit.YEAR, 2,
        DateTickUnit.YEAR, 1, df));
    units.add(new DateTickUnit(DateTickUnit.YEAR, 5,
        DateTickUnit.YEAR, 5, df));
    return units;
  }

}


Alternatively, the java files can be compiled on the command line. First, navigate to the source directory within the expanded zip archive and type:
javac -classpath .:../lib/jfreechart-1.0.11.jar:../lib/jxlayer.jar:../lib/jcommon-1.0.14.jar demo/jxlayer/*.java

Run the application from the source directory with this command:
java -classpath .:../lib/jfreechart-1.0.11.jar:../lib/jxlayer.jar:../lib/jcommon-1.0.14.jar demo.jxlayer.JXLayerApplicationDemo

The code above gives an example of how to convert an applet to an application. These three steps are necessary:
  • Extend JPanel instead of JApplet.
  • Provide a main method to instantiate the application and place it in a visible JFrame.
  • Call add instead of setContentPane in the constructor.