Java tutorial
/* * Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fhcrc.cpl.toolbox.gui.chart; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.renderer.xy.XYBlockRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.LookupPaintScale; import org.jfree.chart.renderer.PaintScale; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.*; import org.jfree.ui.RectangleAnchor; import org.apache.log4j.Logger; import org.fhcrc.cpl.toolbox.Rounder; import org.fhcrc.cpl.toolbox.BrowserController; import java.awt.*; import java.io.IOException; /** * PanelWithChart implementation to make it easy to put out Line Charts * If you want to do anything super-serious with the chart, use * getChart() and getRenderer() */ public class PanelWithHeatMap extends PanelWithChart { static Logger _log = Logger.getLogger(PanelWithHeatMap.class); //this doesn't work -- bug in XYZBlockRenderer // protected XYZToolTipGenerator toolTipGenerator; protected XYDataset dataset; protected NumberAxis xAxis = new NumberAxis("X"); protected NumberAxis yAxis = new NumberAxis("Y"); protected String dataSetName = ""; LookupPaintScale paintScale = null; protected XYBlockRenderer renderer = null; protected double[] xValues = null; protected double[] yValues = null; protected double[][] zValues = null; protected double lowerZBound = 0; protected double upperZBound = 0; public static final int PALETTE_BLUE_RED = 0; public static final int PALETTE_RED = 1; public static final int PALETTE_BLUE = 2; public static final int PALETTE_GRAYSCALE = 3; public static final int PALETTE_WHITE_BLUE = 4; //This palette starts with blue at 0 and scales through the values. Any negative values get the same red color public static final int PALETTE_POSITIVE_WHITE_BLUE_NEGATIVE_BLACK_RED = 5; public static final int DEFAULT_PALETTE = PALETTE_BLUE_RED; protected int palette = DEFAULT_PALETTE; public static final int DISTINCT_PALETTE_VALUES = 100; public PanelWithHeatMap() { super(); init(); } public PanelWithHeatMap(String dataSetName) { super(dataSetName); this.dataSetName = dataSetName; } public PanelWithHeatMap(float[] xValues, float[] yValues, float[][] zValues, String dataSetName) { this(dataSetName); setData(xValues, yValues, zValues); } public PanelWithHeatMap(double[] xValues, double[] yValues, double[][] zValues, String dataSetName) { this(dataSetName); setData(xValues, yValues, zValues); } public PanelWithHeatMap(float[][] zValues, String dataSetName) { this(dataSetName); double[][] zValuesDouble = new double[zValues.length][zValues[0].length]; for (int i = 0; i < zValues.length; i++) for (int j = 0; j < zValues[0].length; j++) { zValuesDouble[i][j] = zValues[i][j]; } setData(zValuesDouble); } public PanelWithHeatMap(double[][] zValues, String dataSetName) { this(dataSetName); setData(zValues); } protected void init() { dataset = new XYSeriesCollection(); renderer = new XYBlockRenderer(); //set all possible series to the default shape // init(chart.getXYPlot()); } public void setAxisLabels(String xLabel, String yLabel) { xAxis.setLabel(xLabel); yAxis.setLabel(yLabel); } /* * Convert floats to doubles */ public void setData(float[] xValues, float[] yValues, float[][] zValues) { double[] xValuesDouble = new double[xValues.length]; for (int i = 0; i < xValues.length; i++) xValuesDouble[i] = xValues[i]; double[] yValuesDouble = new double[yValues.length]; for (int i = 0; i < yValues.length; i++) yValuesDouble[i] = yValues[i]; double[][] zValuesDouble = new double[zValues.length][zValues[0].length]; for (int i = 0; i < zValues.length; i++) for (int j = 0; j < zValues[0].length; j++) { zValuesDouble[i][j] = zValues[i][j]; } setData(xValuesDouble, yValuesDouble, zValuesDouble); } /** * Map out a grid of x and y values for the z values * @param zValues */ public void setData(double[][] zValues) { int width = zValues.length; int height = zValues[0].length; double[] xValues = new double[width]; for (int i = 0; i < width; i++) xValues[i] = i + 1; double[] yValues = new double[height]; for (int i = 0; i < height; i++) yValues[i] = i + 1; _log.debug("Inferring width=" + width + ", height=" + height); if (_log.isDebugEnabled()) { for (int i = 0; i < width; i++) { System.err.println("\n"); for (int j = 0; j < height; j++) { System.err.print(zValues[i][j] + "\t"); } } System.err.println(); } setData(xValues, yValues, zValues); } public String generateHTML(String title) { StringBuffer resultBuf = new StringBuffer("<html><head><title>" + title + "</title><head>\n<table>\n"); resultBuf.append("<tr><td></td>\n"); for (int i = 0; i < yValues.length; i++) { resultBuf.append("<td>" + xAxis.getLabel() + (i + 1) + "</td>"); } resultBuf.append("</tr>\n"); for (int i = 0; i < xValues.length; i++) { resultBuf.append("<tr><td>" + yAxis.getLabel() + (i + 1) + "</td>"); for (int j = 0; j < yValues.length; j++) { double proportionOfMax = ((zValues[j][i] - lowerZBound) / (upperZBound - lowerZBound)); int red = Math.max(0, (int) (proportionOfMax * 256 - 1)); int blue = Math.max(0, (int) ((1 - proportionOfMax) * 256 - 1)); //System.err.println(percentOverlap + ", " + red + ", " + blue + "; " + Integer.toString(red, 16) + ", " + Integer.toString(blue, 16)); String colorAsHex = Integer.toString(red, 16) + "00" + Integer.toString(blue, 16); resultBuf .append("<td bgcolor=\"#" + colorAsHex + "\">" + Rounder.round(zValues[j][i], 2) + "</td>"); } resultBuf.append("</tr>\n"); } resultBuf.append("</table></html>"); return resultBuf.toString(); } public void displayHTML(String title) throws IOException { BrowserController.openTempFileWithContents(generateHTML(title), title, this); } public void displayHTML() throws IOException { displayHTML(getName()); } public void setData(double[] xValues, double[] yValues, double[][] zValues) { this.xValues = xValues; this.yValues = yValues; this.zValues = zValues; double minZValue = Double.MAX_VALUE; double maxZValue = Double.MIN_VALUE; int width = xValues.length; int height = yValues.length; int numCells = width * height; _log.debug("Number of cells in heat map: " + numCells); if (zValues.length != width || zValues[0].length != height) throw new RuntimeException( "PanelWithHeatMap: wrong number of z values for x and y values (" + zValues.length + " vs. " + width + ", " + zValues[0].length + " vs. " + height + ", x/y first, z second)"); DefaultXYZDataset theDataset = new DefaultXYZDataset(); double[][] data = new double[3][numCells]; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { int cellIndex = (j * width) + i; data[0][cellIndex] = xValues[i]; data[1][cellIndex] = yValues[j]; data[2][cellIndex] = zValues[i][j]; //keep track of lowest/highest z values minZValue = Math.min(zValues[i][j], minZValue); maxZValue = Math.max(zValues[i][j], maxZValue); } } lowerZBound = Rounder.round(minZValue, 3); upperZBound = Rounder.round(maxZValue, 3); if (lowerZBound == upperZBound) upperZBound += .0001; _log.debug("low,high values: " + lowerZBound + ", " + upperZBound); theDataset.addSeries("Range: " + lowerZBound + "-" + upperZBound, data); dataset = theDataset; if (renderer == null) { renderer = new XYBlockRenderer(); } if (paintScale == null) { setPaintScale(createPaintScale(palette)); } //This is necessary to get everything to line up renderer.setBlockAnchor(RectangleAnchor.BOTTOM); if (getPlot() != null) { ((XYPlot) getPlot()).setDataset(dataset); ((XYPlot) getPlot()).setRenderer(renderer); invalidate(); return; } XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); JFreeChart chart = new JFreeChart(dataSetName, JFreeChart.DEFAULT_TITLE_FONT, plot, true); // chart.addLegend(new LegendTitle(renderer)); // PaintScaleLegend legend = new PaintScaleLegend(paintScale, xAxis); // chart.addLegend(legend); // LegendItemCollection c1 = new LegendItemCollection(); // // LegendItem item1 = new LegendItem("Label", "Description", // "ToolTip", "URL", true, // new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.red, // true, Color.blue, new BasicStroke(1.2f), true, // new Line2D.Double(1.0, 2.0, 3.0, 4.0), // new BasicStroke(2.1f), Color.green); // LegendItem item2 = new LegendItem("Label", "Description", // "ToolTip", "URL", true, // new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), // true, Color.red, true, Color.blue, new BasicStroke(1.2f), true, // new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), // Color.green); // c1.add(item1); // // chart.getLegend().setSources(new LegendItemSource[]{renderer}); init(chart); } public LookupPaintScale createPaintScale(int palette) { LookupPaintScale result; switch (palette) { case PALETTE_BLUE_RED: result = createPaintScale(lowerZBound, upperZBound, Color.BLUE, Color.RED); break; case PALETTE_RED: result = createPaintScale(lowerZBound, upperZBound, new Color(70, 5, 5), new Color(255, 5, 5)); break; case PALETTE_BLUE: result = createPaintScale(lowerZBound, upperZBound, new Color(5, 5, 70), new Color(5, 5, 255)); break; case PALETTE_GRAYSCALE: result = createPaintScale(lowerZBound, upperZBound, Color.WHITE, new Color(5, 5, 5)); break; case PALETTE_WHITE_BLUE: result = createPaintScale(lowerZBound, upperZBound, Color.WHITE, Color.BLUE); break; case PALETTE_POSITIVE_WHITE_BLUE_NEGATIVE_BLACK_RED: result = new LookupPaintScale(lowerZBound, upperZBound + 0.1, Color.RED); addValuesToPaintScale(result, 0, upperZBound, Color.WHITE, Color.BLUE); addValuesToPaintScale(result, -upperZBound - 0.000001, -0.0000001, Color.BLUE, Color.RED); break; default: result = createPaintScale(lowerZBound, upperZBound, Color.WHITE, new Color(5, 5, 5)); break; } return result; } protected static void addValuesToPaintScale(LookupPaintScale paintScale, double lowerBound, double upperBound, Color lowColor, Color highColor) { int distinctValues = DISTINCT_PALETTE_VALUES; if (upperBound <= lowerBound) upperBound = lowerBound + .0001; double increment = (upperBound - lowerBound) / distinctValues; int redDiff = highColor.getRed() - lowColor.getRed(); int greenDiff = highColor.getGreen() - lowColor.getGreen(); int blueDiff = highColor.getBlue() - lowColor.getBlue(); double redIncrement = (redDiff / distinctValues); double greenIncrement = (greenDiff / distinctValues); double blueIncrement = (blueDiff / distinctValues); _log.debug("Palette: "); for (int i = 0; i < distinctValues; i++) { int r = (int) (lowColor.getRed() + (i * redIncrement)); int g = (int) (lowColor.getGreen() + (i * greenIncrement)); int b = (int) (lowColor.getBlue() + (i * blueIncrement)); Color incrementColor = new Color(r, g, b); double incrementStart = lowerBound + (i * increment); paintScale.add(incrementStart, incrementColor); _log.debug("\t" + incrementStart + "-" + (incrementStart + increment) + ": " + incrementColor); } } /** * Creates a paintscale that has _distinctValues_ even increments between * lowColor and highColor, starting with value lowerBound and ending with upperBound * @param lowerBound * @param upperBound * @param lowColor * @param highColor * @return */ public static LookupPaintScale createPaintScale(double lowerBound, double upperBound, Color lowColor, Color highColor) { //prevent rounding errors that make highest value undefine LookupPaintScale result = new LookupPaintScale(lowerBound, upperBound + 0.01, lowColor); addValuesToPaintScale(result, lowerBound, upperBound, lowColor, highColor); return result; } public void setBlockWidthHeight(float blockWidth, float blockHeight) { renderer.setBlockWidth(blockWidth); renderer.setBlockHeight(blockHeight); repaint(); } public void setPaintScale(LookupPaintScale newPaintScale) { this.paintScale = newPaintScale; renderer.setPaintScale(newPaintScale); repaint(); } public void setPalette(int palette) { this.palette = palette; setPaintScale(createPaintScale(palette)); repaint(); } public XYItemRenderer getRenderer() { return renderer; } public NumberAxis getXAxis() { return xAxis; } public NumberAxis getYAxis() { return yAxis; } public double[] getXValues() { return xValues; } public void setXValues(double[] xValues) { this.xValues = xValues; setData(xValues, yValues, zValues); } public double[] getYValues() { return yValues; } public void setYValues(double[] yValues) { this.yValues = yValues; setData(xValues, yValues, zValues); } public double[][] getZValues() { return zValues; } public void setZValues(double[][] zValues) { this.zValues = zValues; setData(xValues, yValues, zValues); } }