src/core/net/sf/basedb/util/plot/HistogramPlot.java

Code
Comments
Other
Rev Date Author Line
2087 17 Mar 06 nicklas 1 /*
2087 17 Mar 06 nicklas 2   $Id$
2087 17 Mar 06 nicklas 3
4889 06 Apr 09 nicklas 4   Copyright (C) 2006 Jari Häkkinen, Nicklas Nordborg
2087 17 Mar 06 nicklas 5
2304 22 May 06 jari 6   This file is part of BASE - BioArray Software Environment.
2304 22 May 06 jari 7   Available at http://base.thep.lu.se/
2087 17 Mar 06 nicklas 8
2087 17 Mar 06 nicklas 9   BASE is free software; you can redistribute it and/or
2087 17 Mar 06 nicklas 10   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
2087 17 Mar 06 nicklas 12   of the License, or (at your option) any later version.
2087 17 Mar 06 nicklas 13
2087 17 Mar 06 nicklas 14   BASE is distributed in the hope that it will be useful,
2087 17 Mar 06 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
2087 17 Mar 06 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2087 17 Mar 06 nicklas 17   GNU General Public License for more details.
2087 17 Mar 06 nicklas 18
2087 17 Mar 06 nicklas 19   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
2087 17 Mar 06 nicklas 21 */
2087 17 Mar 06 nicklas 22 package net.sf.basedb.util.plot;
2087 17 Mar 06 nicklas 23
2137 31 Mar 06 nicklas 24 import net.sf.basedb.core.BaseException;
2087 17 Mar 06 nicklas 25 import net.sf.basedb.core.query.SqlResult;
2131 30 Mar 06 nicklas 26 import net.sf.basedb.core.query.SqlResultIterator;
4128 05 Feb 08 nicklas 27 import net.sf.basedb.core.signal.ThreadSignalHandler;
2087 17 Mar 06 nicklas 28
2087 17 Mar 06 nicklas 29 import org.jfree.chart.JFreeChart;
2087 17 Mar 06 nicklas 30 import org.jfree.chart.axis.NumberAxis;
2137 31 Mar 06 nicklas 31 import org.jfree.chart.plot.DatasetRenderingOrder;
2137 31 Mar 06 nicklas 32 import org.jfree.chart.plot.SeriesRenderingOrder;
2087 17 Mar 06 nicklas 33 import org.jfree.chart.plot.XYPlot;
2087 17 Mar 06 nicklas 34 import org.jfree.chart.renderer.xy.XYBarRenderer;
3015 11 Dec 06 nicklas 35 import org.jfree.chart.renderer.xy.XYItemRenderer;
2137 31 Mar 06 nicklas 36 import org.jfree.chart.renderer.xy.YIntervalRenderer;
2116 27 Mar 06 nicklas 37 import org.jfree.data.DomainOrder;
2087 17 Mar 06 nicklas 38 import org.jfree.data.Range;
2116 27 Mar 06 nicklas 39 import org.jfree.data.general.AbstractSeriesDataset;
2116 27 Mar 06 nicklas 40 import org.jfree.data.xy.IntervalXYDataset;
2087 17 Mar 06 nicklas 41
2137 31 Mar 06 nicklas 42 import java.awt.geom.Rectangle2D;
5384 13 Aug 10 nicklas 43 import java.io.Serializable;
2087 17 Mar 06 nicklas 44 import java.sql.SQLException;
2116 27 Mar 06 nicklas 45 import java.util.ArrayList;
2137 31 Mar 06 nicklas 46 import java.util.Collection;
2137 31 Mar 06 nicklas 47 import java.util.HashMap;
2116 27 Mar 06 nicklas 48 import java.util.List;
2137 31 Mar 06 nicklas 49 import java.util.Map;
2087 17 Mar 06 nicklas 50
3015 11 Dec 06 nicklas 51
2087 17 Mar 06 nicklas 52 /**
2327 24 May 06 nicklas 53   A simple plot utility for generating histogram plots without
2327 24 May 06 nicklas 54   the need to delve deep into the JFreePlot package. Note! This class
2327 24 May 06 nicklas 55   is experimental and it is likely that the implementation will change 
2327 24 May 06 nicklas 56   in the future.
2327 24 May 06 nicklas 57   
2087 17 Mar 06 nicklas 58   @author Nicklas
2087 17 Mar 06 nicklas 59   @version 2.0
2087 17 Mar 06 nicklas 60   @base.modified $Date$
2087 17 Mar 06 nicklas 61 */
2087 17 Mar 06 nicklas 62 public class HistogramPlot
2087 17 Mar 06 nicklas 63 {
2087 17 Mar 06 nicklas 64   private Range domainRange;
2087 17 Mar 06 nicklas 65   private Range rangeRange;
2087 17 Mar 06 nicklas 66   private XYPlot plot;
2087 17 Mar 06 nicklas 67   private HistogramDataset histogram;
2137 31 Mar 06 nicklas 68   private HistogramDataset hilo;
2087 17 Mar 06 nicklas 69   private JFreeChart chart;
2087 17 Mar 06 nicklas 70
2327 24 May 06 nicklas 71   /**
2327 24 May 06 nicklas 72     Create a new HistogramPlot.
2327 24 May 06 nicklas 73     @param nameX The label on the X-axis
2327 24 May 06 nicklas 74     @param nameY The label on the Y-axis
3015 11 Dec 06 nicklas 75     @param renderer The renderer to use or null for the default (XYBarRenderer)
2327 24 May 06 nicklas 76   */
3015 11 Dec 06 nicklas 77   public HistogramPlot(String nameX, String nameY, XYItemRenderer renderer)
2087 17 Mar 06 nicklas 78   {
2087 17 Mar 06 nicklas 79     
2087 17 Mar 06 nicklas 80     histogram = new HistogramDataset();
2137 31 Mar 06 nicklas 81     hilo = new HistogramDataset();
2087 17 Mar 06 nicklas 82     NumberAxis domainAxis = new NumberAxis(nameX);
2087 17 Mar 06 nicklas 83     domainAxis.setAutoRangeIncludesZero(false);
2137 31 Mar 06 nicklas 84     domainRange = new Range(0.0, 0.0);
2137 31 Mar 06 nicklas 85     rangeRange = new Range(0.0, 0.0);
2087 17 Mar 06 nicklas 86     
2116 27 Mar 06 nicklas 87       NumberAxis rangeAxis = new NumberAxis(nameY);
2087 17 Mar 06 nicklas 88     rangeAxis.setAutoRangeIncludesZero(true);
3015 11 Dec 06 nicklas 89     if (renderer == null) renderer = new XYBarRenderer();
3015 11 Dec 06 nicklas 90       plot = new XYPlot(histogram, domainAxis, rangeAxis, renderer);
2137 31 Mar 06 nicklas 91       plot.setDataset(1, hilo);
2137 31 Mar 06 nicklas 92       YIntervalRenderer yRenderer = new YIntervalRenderer();
5033 29 Jul 09 nicklas 93       yRenderer.setBaseShape(new Rectangle2D.Float(-3, -1, 7, 2));
5033 29 Jul 09 nicklas 94       yRenderer.setBaseSeriesVisibleInLegend(false, false);
2137 31 Mar 06 nicklas 95       plot.setRenderer(1, yRenderer, false);
2137 31 Mar 06 nicklas 96       
2137 31 Mar 06 nicklas 97       plot.setSeriesRenderingOrder(SeriesRenderingOrder.FORWARD);
2137 31 Mar 06 nicklas 98       plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
2137 31 Mar 06 nicklas 99       chart = new JFreeChart(plot);
2087 17 Mar 06 nicklas 100     chart.setAntiAlias(false);
2087 17 Mar 06 nicklas 101   }
2087 17 Mar 06 nicklas 102
2327 24 May 06 nicklas 103   /**
2327 24 May 06 nicklas 104     Add data to the plot. The <code>SqlResultIterator</code> should return
2327 24 May 06 nicklas 105     the x value as a float in the first position, ie. <code>data.getFloat(1)</code>
2327 24 May 06 nicklas 106     and, if used, the y value in the second position.
2327 24 May 06 nicklas 107     <p>
2327 24 May 06 nicklas 108     The y value is not used if the yAggregate parameter is {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 109   
2327 24 May 06 nicklas 110     @param data The data to plot
2327 24 May 06 nicklas 111     @param name The name of the data series
2327 24 May 06 nicklas 112     @param binSize The size (= range on x-axis) of each histogram bin
2327 24 May 06 nicklas 113     @param yAggregate What to plot on the y-axis
2327 24 May 06 nicklas 114     @param hiloAggregate Optional parameter specifying if high/low values shold be drawn for each bin
2327 24 May 06 nicklas 115   */
2137 31 Mar 06 nicklas 116   public void addData(SqlResultIterator data, String name, float binSize, YAggregate yAggregate, YAggregate hiloAggregate)
2087 17 Mar 06 nicklas 117     throws SQLException
2087 17 Mar 06 nicklas 118   {
2116 27 Mar 06 nicklas 119     final boolean isCount = yAggregate == YAggregate.COUNT;
2474 31 Jul 06 nicklas 120     float[] xValues = new float[(int)data.getTotalCount()];
2474 31 Jul 06 nicklas 121     float[] yValues = isCount ? null : new float[(int)data.getTotalCount()];
2087 17 Mar 06 nicklas 122     int i = 0;
2116 27 Mar 06 nicklas 123
2087 17 Mar 06 nicklas 124     while (data.hasNext())
2087 17 Mar 06 nicklas 125     {
4128 05 Feb 08 nicklas 126       ThreadSignalHandler.checkInterrupted();
2087 17 Mar 06 nicklas 127       SqlResult r = data.next();
2116 27 Mar 06 nicklas 128       if (!isCount) yValues[i] = r.getFloat(2);
2116 27 Mar 06 nicklas 129       xValues[i++] = r.getFloat(1);
2087 17 Mar 06 nicklas 130     }
2137 31 Mar 06 nicklas 131     HistogramSeries main = new HistogramSeries(name, xValues, yValues, binSize, yAggregate, Float.NaN, Float.NaN);
2137 31 Mar 06 nicklas 132     histogram.addSeries(main);
2137 31 Mar 06 nicklas 133     if (hiloAggregate != null)
2137 31 Mar 06 nicklas 134     {
2137 31 Mar 06 nicklas 135       hilo.addSeries(new HistogramSeries(name, main, hiloAggregate));
2137 31 Mar 06 nicklas 136     }
2137 31 Mar 06 nicklas 137     adjustRanges();
2137 31 Mar 06 nicklas 138   }
2137 31 Mar 06 nicklas 139   
2327 24 May 06 nicklas 140   /**
2327 24 May 06 nicklas 141     Auto-size of the x and y range doesn't seem to work, so we have to do it 
2327 24 May 06 nicklas 142     manually.
2327 24 May 06 nicklas 143   */
2137 31 Mar 06 nicklas 144   private void adjustRanges()
2137 31 Mar 06 nicklas 145   {
2087 17 Mar 06 nicklas 146     domainRange = Range.combine(domainRange, plot.getRenderer().findDomainBounds(histogram));
2137 31 Mar 06 nicklas 147     if (hilo.getSeriesCount() > 0)
2137 31 Mar 06 nicklas 148     {
2137 31 Mar 06 nicklas 149       rangeRange = Range.combine(rangeRange, plot.getRenderer(1).findRangeBounds(hilo));
2137 31 Mar 06 nicklas 150     }
2137 31 Mar 06 nicklas 151     else
2137 31 Mar 06 nicklas 152     {
2137 31 Mar 06 nicklas 153       rangeRange = Range.combine(rangeRange, plot.getRenderer(0).findRangeBounds(histogram));
2137 31 Mar 06 nicklas 154     }
2137 31 Mar 06 nicklas 155     rangeRange = Range.expand(rangeRange, 0, 0.02);
2087 17 Mar 06 nicklas 156     plot.getDomainAxis().setRange(domainRange);
2137 31 Mar 06 nicklas 157     plot.getRangeAxis().setRange(rangeRange);
2087 17 Mar 06 nicklas 158   }
2327 24 May 06 nicklas 159
2327 24 May 06 nicklas 160   /**
2327 24 May 06 nicklas 161     Add data to the plot. The <code>SqlResultIterator</code> should return
2327 24 May 06 nicklas 162     the annotation value in the first position, ie. <code>data.getInt(1)</code>,
2327 24 May 06 nicklas 163     the x value as a float in the second position, and, if used, the y value in the 
2327 24 May 06 nicklas 164     third position.
2327 24 May 06 nicklas 165     <p>
2327 24 May 06 nicklas 166     The y value is not used if the yAggregate parameter is {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 167     
2327 24 May 06 nicklas 168     @param data The data to plot
2327 24 May 06 nicklas 169     @param annotations Annotation information that maps each data spot to 
2327 24 May 06 nicklas 170       an annotation (see {@link ScatterPlot#addData(SqlResultIterator, Collection)}
2327 24 May 06 nicklas 171     @param binSize The size (= range on x-axis) of each histogram bin
2327 24 May 06 nicklas 172     @param yAggregate What to plot on the y-axis
2327 24 May 06 nicklas 173     @param hiloAggregate Optional parameter specifying if high/low values shold be drawn for each bin
2327 24 May 06 nicklas 174    */
2137 31 Mar 06 nicklas 175   public void addData(SqlResultIterator data, Collection<PlotAnnotation> annotations, float binSize, YAggregate yAggregate, YAggregate hiloAggregate)
2137 31 Mar 06 nicklas 176     throws SQLException
2137 31 Mar 06 nicklas 177   {
2137 31 Mar 06 nicklas 178     final boolean isCount = yAggregate == YAggregate.COUNT;
2137 31 Mar 06 nicklas 179     Map<Integer, TempSeries> series = new HashMap<Integer, TempSeries>(annotations.size());
2137 31 Mar 06 nicklas 180     List<TempSeries> tempSeries = new ArrayList<TempSeries>(annotations.size());
2474 31 Jul 06 nicklas 181     int meanCount = (int)(data.getTotalCount() / annotations.size());
2137 31 Mar 06 nicklas 182     for (PlotAnnotation pa : annotations)
2137 31 Mar 06 nicklas 183     {
2137 31 Mar 06 nicklas 184       TempSeries ts = new TempSeries(pa.getName(), meanCount, !isCount);
2137 31 Mar 06 nicklas 185       tempSeries.add(ts);
2327 24 May 06 nicklas 186       for (Integer value : pa.getValues())
2137 31 Mar 06 nicklas 187       {
2327 24 May 06 nicklas 188         if (series.containsKey(value))
2137 31 Mar 06 nicklas 189         {
2327 24 May 06 nicklas 190           throw new BaseException("Value " + value + " has already been mapped to another series");
2137 31 Mar 06 nicklas 191         }
2327 24 May 06 nicklas 192         series.put(value, ts);
2137 31 Mar 06 nicklas 193       }
2137 31 Mar 06 nicklas 194     }
2137 31 Mar 06 nicklas 195     float maxX = Float.MIN_VALUE;
2137 31 Mar 06 nicklas 196     float minX = Float.MAX_VALUE;
2137 31 Mar 06 nicklas 197     while (data.hasNext())
2137 31 Mar 06 nicklas 198     {
4128 05 Feb 08 nicklas 199       ThreadSignalHandler.checkInterrupted();
2137 31 Mar 06 nicklas 200       SqlResult r = data.next();
2137 31 Mar 06 nicklas 201       TempSeries ts = series.get(r.getInt(1));
2137 31 Mar 06 nicklas 202       float x = r.getFloat(2);
2137 31 Mar 06 nicklas 203       if (maxX < x) maxX = x;
2137 31 Mar 06 nicklas 204       if (minX > x) minX = x;
2327 24 May 06 nicklas 205       ts.x.add(x);
2137 31 Mar 06 nicklas 206       if (!isCount) ts.y.add(r.getFloat(3));
2137 31 Mar 06 nicklas 207     }
2137 31 Mar 06 nicklas 208     
2137 31 Mar 06 nicklas 209     for (TempSeries ts : tempSeries)
2137 31 Mar 06 nicklas 210     {
2137 31 Mar 06 nicklas 211       HistogramSeries main = new HistogramSeries(ts.name, ts.getX(), ts.getY(), binSize, yAggregate, minX, maxX);
2137 31 Mar 06 nicklas 212       histogram.addSeries(main);
2137 31 Mar 06 nicklas 213       if (hiloAggregate != null)
2137 31 Mar 06 nicklas 214       {
2137 31 Mar 06 nicklas 215         hilo.addSeries(new HistogramSeries(ts.name, main, hiloAggregate));
2137 31 Mar 06 nicklas 216       }
3015 11 Dec 06 nicklas 217       plot.getRenderer().setSeriesVisibleInLegend(histogram.getSeriesCount()-1, !ts.name.equals(""));
2137 31 Mar 06 nicklas 218     }
2137 31 Mar 06 nicklas 219     adjustRanges();
2137 31 Mar 06 nicklas 220   }
2137 31 Mar 06 nicklas 221   
2327 24 May 06 nicklas 222   /**
2327 24 May 06 nicklas 223      Get the underlying JFreeChar object. Use this method to polish up 
2327 24 May 06 nicklas 224      everything and save the image.
2327 24 May 06 nicklas 225   */
2087 17 Mar 06 nicklas 226   public JFreeChart getChart()
2087 17 Mar 06 nicklas 227   {
2087 17 Mar 06 nicklas 228     return chart;
2087 17 Mar 06 nicklas 229   }
2116 27 Mar 06 nicklas 230   
2327 24 May 06 nicklas 231   /**
2327 24 May 06 nicklas 232     Helper class for storing histogram data that needs to split into
2327 24 May 06 nicklas 233     different annotations. For each annotation we create one TempSeries object
2327 24 May 06 nicklas 234     and fills it with the data for that annotation.
2327 24 May 06 nicklas 235   */
2137 31 Mar 06 nicklas 236   private static class TempSeries
2137 31 Mar 06 nicklas 237   {
2137 31 Mar 06 nicklas 238     final String name;
2137 31 Mar 06 nicklas 239     final List<Float> x;
2137 31 Mar 06 nicklas 240     final List<Float> y;
2137 31 Mar 06 nicklas 241     
2137 31 Mar 06 nicklas 242     TempSeries(String name, int size, boolean hasY)
2137 31 Mar 06 nicklas 243     {
2137 31 Mar 06 nicklas 244       this.name = name;
2137 31 Mar 06 nicklas 245       this.x = new ArrayList<Float>(size);
2137 31 Mar 06 nicklas 246       this.y = hasY ? new ArrayList<Float>(size) : null;
2137 31 Mar 06 nicklas 247     }
2137 31 Mar 06 nicklas 248     
2137 31 Mar 06 nicklas 249     public float[] getX()
2137 31 Mar 06 nicklas 250     {
2137 31 Mar 06 nicklas 251       if (x == null) return null;
2137 31 Mar 06 nicklas 252       float[] xf = new float[x.size()];
2137 31 Mar 06 nicklas 253       int i = 0;
2137 31 Mar 06 nicklas 254       for (Float f : x)
2137 31 Mar 06 nicklas 255       {
2137 31 Mar 06 nicklas 256         xf[i++] = f.floatValue();
2137 31 Mar 06 nicklas 257       }
2137 31 Mar 06 nicklas 258       return xf;
2137 31 Mar 06 nicklas 259     }
2137 31 Mar 06 nicklas 260     
2137 31 Mar 06 nicklas 261     public float[] getY()
2137 31 Mar 06 nicklas 262     {
2137 31 Mar 06 nicklas 263       if (y == null) return null;
2137 31 Mar 06 nicklas 264       float[] yf = new float[y.size()];
2137 31 Mar 06 nicklas 265       int i = 0;
2137 31 Mar 06 nicklas 266       for (Float f : y)
2137 31 Mar 06 nicklas 267       {
2137 31 Mar 06 nicklas 268         yf[i++] = f.floatValue();
2137 31 Mar 06 nicklas 269       }
2137 31 Mar 06 nicklas 270       return yf;
2137 31 Mar 06 nicklas 271     }
2137 31 Mar 06 nicklas 272     
2137 31 Mar 06 nicklas 273   }
2116 27 Mar 06 nicklas 274   
2327 24 May 06 nicklas 275   /**
2327 24 May 06 nicklas 276     An dataset implementation for histogram data that allows
2327 24 May 06 nicklas 277     us to send data to JFreeChart the way we want. 
2327 24 May 06 nicklas 278   */
2116 27 Mar 06 nicklas 279   private static class HistogramDataset
2116 27 Mar 06 nicklas 280     extends AbstractSeriesDataset
2116 27 Mar 06 nicklas 281     implements IntervalXYDataset
2116 27 Mar 06 nicklas 282   {
4084 17 Jan 08 nicklas 283     /**
4084 17 Jan 08 nicklas 284       
4084 17 Jan 08 nicklas 285     */
4084 17 Jan 08 nicklas 286     private static final long serialVersionUID = -515696571729019122L;
2116 27 Mar 06 nicklas 287     private final List<HistogramSeries> all;
2116 27 Mar 06 nicklas 288     
2116 27 Mar 06 nicklas 289     private HistogramDataset()
2116 27 Mar 06 nicklas 290     {
2116 27 Mar 06 nicklas 291       this.all = new ArrayList<HistogramSeries>();
2116 27 Mar 06 nicklas 292     }
2116 27 Mar 06 nicklas 293     /*
2116 27 Mar 06 nicklas 294       From the AbstractSeriesDataset class
2116 27 Mar 06 nicklas 295       -------------------------------------------
2116 27 Mar 06 nicklas 296     */
2116 27 Mar 06 nicklas 297     @Override
2116 27 Mar 06 nicklas 298     public int getSeriesCount()
2116 27 Mar 06 nicklas 299     {
2116 27 Mar 06 nicklas 300       return all.size();
2116 27 Mar 06 nicklas 301     }
6875 20 Apr 15 nicklas 302     @SuppressWarnings("rawtypes")
2116 27 Mar 06 nicklas 303     @Override
2116 27 Mar 06 nicklas 304     public Comparable getSeriesKey(int series)
2116 27 Mar 06 nicklas 305     {
2116 27 Mar 06 nicklas 306       return all.get(series).getName();
2116 27 Mar 06 nicklas 307     }
2116 27 Mar 06 nicklas 308     // -------------------------------------------
2116 27 Mar 06 nicklas 309
2116 27 Mar 06 nicklas 310     /*
2116 27 Mar 06 nicklas 311       From the XYDataset interface
2116 27 Mar 06 nicklas 312       -------------------------------------------
2116 27 Mar 06 nicklas 313     */
6127 14 Sep 12 nicklas 314     @Override
2116 27 Mar 06 nicklas 315     public DomainOrder getDomainOrder()
2116 27 Mar 06 nicklas 316     {
2116 27 Mar 06 nicklas 317       return DomainOrder.ASCENDING;
2116 27 Mar 06 nicklas 318     }
6127 14 Sep 12 nicklas 319     @Override
2116 27 Mar 06 nicklas 320     public int getItemCount(int series)
2116 27 Mar 06 nicklas 321     {
2116 27 Mar 06 nicklas 322       return all.get(series).getItemCount();
2116 27 Mar 06 nicklas 323     }
6127 14 Sep 12 nicklas 324     @Override
2116 27 Mar 06 nicklas 325     public Number getX(int series, int item)
2116 27 Mar 06 nicklas 326     {
2116 27 Mar 06 nicklas 327       return getXValue(series, item);
2116 27 Mar 06 nicklas 328     }
6127 14 Sep 12 nicklas 329     @Override
2116 27 Mar 06 nicklas 330     public double getXValue(int series, int item)
2116 27 Mar 06 nicklas 331     {
2137 31 Mar 06 nicklas 332       HistogramBin bin = all.get(series).getBin(item);
2137 31 Mar 06 nicklas 333       return (bin.getStartX() + bin.getEndX()) / 2;
2116 27 Mar 06 nicklas 334     }
2116 27 Mar 06 nicklas 335
6127 14 Sep 12 nicklas 336     @Override
2116 27 Mar 06 nicklas 337     public Number getY(int series, int item)
2116 27 Mar 06 nicklas 338     {
2116 27 Mar 06 nicklas 339       return getYValue(series, item);
2116 27 Mar 06 nicklas 340     }
6127 14 Sep 12 nicklas 341     @Override
2116 27 Mar 06 nicklas 342     public double getYValue(int series, int item)
2116 27 Mar 06 nicklas 343     {
2137 31 Mar 06 nicklas 344       return all.get(series).getMaxY(item);
2116 27 Mar 06 nicklas 345     }
2116 27 Mar 06 nicklas 346     // -------------------------------------------
2116 27 Mar 06 nicklas 347     /*
2116 27 Mar 06 nicklas 348       From the IntervalXYDataset interface
2116 27 Mar 06 nicklas 349       -------------------------------------------
2116 27 Mar 06 nicklas 350     */
6127 14 Sep 12 nicklas 351     @Override
2116 27 Mar 06 nicklas 352     public Number getEndX(int series, int item)
2116 27 Mar 06 nicklas 353     {
2116 27 Mar 06 nicklas 354       return getEndXValue(series, item);
2116 27 Mar 06 nicklas 355     }
6127 14 Sep 12 nicklas 356     @Override
2116 27 Mar 06 nicklas 357     public double getEndXValue(int series, int item)
2116 27 Mar 06 nicklas 358     {
2116 27 Mar 06 nicklas 359       return all.get(series).getBin(item).getEndX();
2116 27 Mar 06 nicklas 360     }
6127 14 Sep 12 nicklas 361     @Override
2116 27 Mar 06 nicklas 362     public Number getEndY(int series, int item)
2116 27 Mar 06 nicklas 363     {
2116 27 Mar 06 nicklas 364       return getEndYValue(series, item);
2116 27 Mar 06 nicklas 365     }
6127 14 Sep 12 nicklas 366     @Override
2116 27 Mar 06 nicklas 367     public double getEndYValue(int series, int item)
2116 27 Mar 06 nicklas 368     {
2116 27 Mar 06 nicklas 369       return getYValue(series, item);
2116 27 Mar 06 nicklas 370     }
6127 14 Sep 12 nicklas 371     @Override
2116 27 Mar 06 nicklas 372     public Number getStartX(int series, int item)
2116 27 Mar 06 nicklas 373     {
2116 27 Mar 06 nicklas 374       return getStartXValue(series, item);
2116 27 Mar 06 nicklas 375     }
6127 14 Sep 12 nicklas 376     @Override
2116 27 Mar 06 nicklas 377     public double getStartXValue(int series, int item)
2116 27 Mar 06 nicklas 378     {
2116 27 Mar 06 nicklas 379       return all.get(series).getBin(item).getStartX();
2116 27 Mar 06 nicklas 380     }
6127 14 Sep 12 nicklas 381     @Override
2116 27 Mar 06 nicklas 382     public Number getStartY(int series, int item)
2116 27 Mar 06 nicklas 383     {
2116 27 Mar 06 nicklas 384       return getStartYValue(series, item);
2116 27 Mar 06 nicklas 385     }
6127 14 Sep 12 nicklas 386     @Override
2116 27 Mar 06 nicklas 387     public double getStartYValue(int series, int item)
2116 27 Mar 06 nicklas 388     {
2137 31 Mar 06 nicklas 389       return all.get(series).getMinY(item);
2116 27 Mar 06 nicklas 390     }
2116 27 Mar 06 nicklas 391     // -------------------------------------------
2116 27 Mar 06 nicklas 392     
2116 27 Mar 06 nicklas 393     public void addSeries(HistogramSeries s)
2116 27 Mar 06 nicklas 394     {
2116 27 Mar 06 nicklas 395       all.add(s);
2116 27 Mar 06 nicklas 396     }
2116 27 Mar 06 nicklas 397   }
2137 31 Mar 06 nicklas 398
2327 24 May 06 nicklas 399   /**
2327 24 May 06 nicklas 400     Collects information about one histogram series. Multiple series may be
2327 24 May 06 nicklas 401     added to {@link HistogramDataset} which is then added to JFreeChart.
2327 24 May 06 nicklas 402   */
2116 27 Mar 06 nicklas 403   private static class HistogramSeries
5384 13 Aug 10 nicklas 404     implements Serializable
2116 27 Mar 06 nicklas 405   {
5384 13 Aug 10 nicklas 406
5384 13 Aug 10 nicklas 407     private static final long serialVersionUID = 8540300441782223733L;
2116 27 Mar 06 nicklas 408     private final String name;
2116 27 Mar 06 nicklas 409     private final HistogramBin[] bins;
2116 27 Mar 06 nicklas 410     private final YAggregate yAggregate;
2116 27 Mar 06 nicklas 411     
2327 24 May 06 nicklas 412     /**
2327 24 May 06 nicklas 413       Create a new HistogramSeries. 
2327 24 May 06 nicklas 414       @param name The name of the seris
2327 24 May 06 nicklas 415       @param xValues The x values
2327 24 May 06 nicklas 416       @param yValues The y values or null if no y values are used
2327 24 May 06 nicklas 417       @param binSize The bin size on the x axis
2327 24 May 06 nicklas 418       @param yAggregate What to show on the y axis
2327 24 May 06 nicklas 419       @param xMin The minimum x axis value or {@link Float#NaN} to let this method calculate it
2327 24 May 06 nicklas 420       @param xMax The maximum x axis value or {@link Float#NaN} to let this method calculate it
2327 24 May 06 nicklas 421      */
2137 31 Mar 06 nicklas 422     private HistogramSeries(String name, float[] xValues, float[] yValues, float binSize, YAggregate yAggregate, float xMin, float xMax)
2116 27 Mar 06 nicklas 423     {
2116 27 Mar 06 nicklas 424       this.name = name;
2116 27 Mar 06 nicklas 425       this.yAggregate = yAggregate;
2116 27 Mar 06 nicklas 426       
2116 27 Mar 06 nicklas 427       // Find max and min --> number of bins
2137 31 Mar 06 nicklas 428       if (Float.isNaN(xMin) || Float.isNaN(xMax))
2116 27 Mar 06 nicklas 429       {
2137 31 Mar 06 nicklas 430         xMin = Float.MAX_VALUE;
2137 31 Mar 06 nicklas 431         xMax = Float.MIN_VALUE;
2137 31 Mar 06 nicklas 432         for (float x : xValues)
2137 31 Mar 06 nicklas 433         {
2137 31 Mar 06 nicklas 434           if (xMax < x) xMax = x;
2137 31 Mar 06 nicklas 435           if (xMin > x) xMin = x;
2137 31 Mar 06 nicklas 436         }
2116 27 Mar 06 nicklas 437       }
2116 27 Mar 06 nicklas 438       int numBins = 1 + (int)((xMax - xMin) / binSize);
2116 27 Mar 06 nicklas 439       bins = new HistogramBin[numBins];
2116 27 Mar 06 nicklas 440       
2116 27 Mar 06 nicklas 441       // Create the bins
2116 27 Mar 06 nicklas 442       for (int i = 0; i < numBins; ++i)
2116 27 Mar 06 nicklas 443       {
2116 27 Mar 06 nicklas 444         float startX = xMin + i * binSize;
2116 27 Mar 06 nicklas 445         bins[i] = new HistogramBin(startX, startX + binSize);
2116 27 Mar 06 nicklas 446       }
2116 27 Mar 06 nicklas 447       
2116 27 Mar 06 nicklas 448       // Add values to the bins
2116 27 Mar 06 nicklas 449       for (int i = 0; i < xValues.length; ++i)
2116 27 Mar 06 nicklas 450       {
2116 27 Mar 06 nicklas 451         int bin = (int)((xValues[i] - xMin) / binSize);
2116 27 Mar 06 nicklas 452         if (yValues == null)
2116 27 Mar 06 nicklas 453         {
2116 27 Mar 06 nicklas 454           bins[bin].increaseCount();
2116 27 Mar 06 nicklas 455         }
2116 27 Mar 06 nicklas 456         else
2116 27 Mar 06 nicklas 457         {
2116 27 Mar 06 nicklas 458           bins[bin].addMeasurement(yValues[i]);
2116 27 Mar 06 nicklas 459         }
2116 27 Mar 06 nicklas 460       }
2116 27 Mar 06 nicklas 461     }
2116 27 Mar 06 nicklas 462     
2327 24 May 06 nicklas 463     /**
2327 24 May 06 nicklas 464       Create a new HistogramSeries that is using data from another series.
2327 24 May 06 nicklas 465       This is used by for example the high/low series which take it's data from
2327 24 May 06 nicklas 466       the main series.
2327 24 May 06 nicklas 467
2327 24 May 06 nicklas 468       @param name The name of the series
2327 24 May 06 nicklas 469       @param other The other series
2327 24 May 06 nicklas 470       @param yAggregate {@link YAggregate#HILOMAXMIN} or {@link YAggregate#HILOSTDEV}
2327 24 May 06 nicklas 471     */
2137 31 Mar 06 nicklas 472     private HistogramSeries(String name, HistogramSeries other, YAggregate yAggregate)
2137 31 Mar 06 nicklas 473     {
2137 31 Mar 06 nicklas 474       this.name = name;
2137 31 Mar 06 nicklas 475       this.bins = other.bins;
2137 31 Mar 06 nicklas 476       this.yAggregate = yAggregate;
2137 31 Mar 06 nicklas 477     }
2137 31 Mar 06 nicklas 478     
2116 27 Mar 06 nicklas 479     public String getName()
2116 27 Mar 06 nicklas 480     {
2116 27 Mar 06 nicklas 481       return name;
2116 27 Mar 06 nicklas 482     }
2116 27 Mar 06 nicklas 483     
2116 27 Mar 06 nicklas 484     public int getItemCount()
2116 27 Mar 06 nicklas 485     {
2116 27 Mar 06 nicklas 486       return bins.length;
2116 27 Mar 06 nicklas 487     }
2116 27 Mar 06 nicklas 488     
2116 27 Mar 06 nicklas 489     public HistogramBin getBin(int index)
2116 27 Mar 06 nicklas 490     {
2116 27 Mar 06 nicklas 491       return bins[index];
2116 27 Mar 06 nicklas 492     }
2116 27 Mar 06 nicklas 493     
2137 31 Mar 06 nicklas 494     public float getMaxY(int index)
2116 27 Mar 06 nicklas 495     {
2137 31 Mar 06 nicklas 496       return yAggregate.getMaxY(bins[index]);
2116 27 Mar 06 nicklas 497     }
2137 31 Mar 06 nicklas 498     public float getMinY(int index)
2137 31 Mar 06 nicklas 499     {
2137 31 Mar 06 nicklas 500       return yAggregate.getMinY(bins[index]);
2137 31 Mar 06 nicklas 501     }
2116 27 Mar 06 nicklas 502   }
2116 27 Mar 06 nicklas 503   
2327 24 May 06 nicklas 504   /**
2327 24 May 06 nicklas 505     Represents a single bin in a histogram.
2327 24 May 06 nicklas 506   */
2116 27 Mar 06 nicklas 507   private static class HistogramBin
5384 13 Aug 10 nicklas 508     implements Serializable
2116 27 Mar 06 nicklas 509   {
5384 13 Aug 10 nicklas 510
5384 13 Aug 10 nicklas 511     private static final long serialVersionUID = 513573632731346456L;
2116 27 Mar 06 nicklas 512     private final double startX;
2116 27 Mar 06 nicklas 513     private final double endX;
2116 27 Mar 06 nicklas 514     
2116 27 Mar 06 nicklas 515     private int count = 0;
2116 27 Mar 06 nicklas 516     private float sum = 0.0f;
2137 31 Mar 06 nicklas 517     private float squaredSum = 0.0f;
2116 27 Mar 06 nicklas 518     private float min = Float.MAX_VALUE;
2116 27 Mar 06 nicklas 519     private float max = Float.MIN_VALUE;
2116 27 Mar 06 nicklas 520     
2327 24 May 06 nicklas 521     /**
2327 24 May 06 nicklas 522       Create a new HistogramBin.
2327 24 May 06 nicklas 523       @param startX The min x-axis values for this bin
2327 24 May 06 nicklas 524       @param endX The max x-axis value for this bin
2327 24 May 06 nicklas 525      */
2116 27 Mar 06 nicklas 526     private HistogramBin(float startX, float endX)
2116 27 Mar 06 nicklas 527     {
2116 27 Mar 06 nicklas 528       this.startX = startX;
2116 27 Mar 06 nicklas 529       this.endX = endX;
2116 27 Mar 06 nicklas 530     }
2116 27 Mar 06 nicklas 531     
2116 27 Mar 06 nicklas 532     public double getStartX()
2116 27 Mar 06 nicklas 533     {
2116 27 Mar 06 nicklas 534       return startX;
2116 27 Mar 06 nicklas 535     }
2116 27 Mar 06 nicklas 536     
2116 27 Mar 06 nicklas 537     public double getEndX()
2116 27 Mar 06 nicklas 538     {
2116 27 Mar 06 nicklas 539       return endX;
2116 27 Mar 06 nicklas 540     }
2116 27 Mar 06 nicklas 541     
2327 24 May 06 nicklas 542     /**
2327 24 May 06 nicklas 543       Increase the count value in this bin. Used if plotting
2327 24 May 06 nicklas 544       {@link YAggregate#COUNT} values.
2327 24 May 06 nicklas 545     */
2116 27 Mar 06 nicklas 546     public void increaseCount()
2116 27 Mar 06 nicklas 547     {
2116 27 Mar 06 nicklas 548       count++;
2116 27 Mar 06 nicklas 549     }
2116 27 Mar 06 nicklas 550     
2327 24 May 06 nicklas 551     /**
2327 24 May 06 nicklas 552       Add a measurement to this bin. Used if plotting values other than 
2327 24 May 06 nicklas 553       {@link YAggregate#COUNT}. This method increases the count and updates
2327 24 May 06 nicklas 554       the sum, max and min values as required.
2327 24 May 06 nicklas 555       @param y The y value to add
2327 24 May 06 nicklas 556     */
2116 27 Mar 06 nicklas 557     public void addMeasurement(float y)
2116 27 Mar 06 nicklas 558     {
2116 27 Mar 06 nicklas 559       count++;
2116 27 Mar 06 nicklas 560       sum += y;
2137 31 Mar 06 nicklas 561       squaredSum += y * y;
2116 27 Mar 06 nicklas 562       if (y < min) min = y;
2116 27 Mar 06 nicklas 563       if (y > max) max = y;
2116 27 Mar 06 nicklas 564     }
2116 27 Mar 06 nicklas 565     
2327 24 May 06 nicklas 566     /**
2327 24 May 06 nicklas 567        Get the number of values in this bin.
2327 24 May 06 nicklas 568     */
2116 27 Mar 06 nicklas 569     public float getCount()
2116 27 Mar 06 nicklas 570     {
2116 27 Mar 06 nicklas 571       return count;
2116 27 Mar 06 nicklas 572     }
2116 27 Mar 06 nicklas 573     
2327 24 May 06 nicklas 574     /**
2327 24 May 06 nicklas 575        Get the sum of all values in this bin. The sum is not available if 
2327 24 May 06 nicklas 576        plotting the {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 577     */
2116 27 Mar 06 nicklas 578     public float getSum()
2116 27 Mar 06 nicklas 579     {
2116 27 Mar 06 nicklas 580       return sum;
2116 27 Mar 06 nicklas 581     }
2116 27 Mar 06 nicklas 582     
2327 24 May 06 nicklas 583     /**
2327 24 May 06 nicklas 584        Get the mean of all values in this bin. The mean is not available if 
2327 24 May 06 nicklas 585        plotting the {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 586     */
2116 27 Mar 06 nicklas 587     public float getMean()
2116 27 Mar 06 nicklas 588     {
2116 27 Mar 06 nicklas 589       return count == 0 ? 0.0f : sum / count;
2116 27 Mar 06 nicklas 590     }
2116 27 Mar 06 nicklas 591     
2327 24 May 06 nicklas 592     /**
2327 24 May 06 nicklas 593        Get the minumum of all values in this bin. The value is not available if 
2327 24 May 06 nicklas 594        plotting the {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 595     */
2116 27 Mar 06 nicklas 596     public float getMin()
2116 27 Mar 06 nicklas 597     {
2116 27 Mar 06 nicklas 598       return min;
2116 27 Mar 06 nicklas 599     }
2327 24 May 06 nicklas 600     /**
2327 24 May 06 nicklas 601        Get the maximum of all values in this bin. The value is not available if 
2327 24 May 06 nicklas 602        plotting the {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 603     */
2116 27 Mar 06 nicklas 604     public float getMax()
2116 27 Mar 06 nicklas 605     {
2116 27 Mar 06 nicklas 606       return max;
2116 27 Mar 06 nicklas 607     }
2327 24 May 06 nicklas 608     /**
2327 24 May 06 nicklas 609        Get the standard deviation of all values in this bin. The value is not available if 
2327 24 May 06 nicklas 610        plotting the {@link YAggregate#COUNT}.
2327 24 May 06 nicklas 611     */
2137 31 Mar 06 nicklas 612     public float getStdev()
2137 31 Mar 06 nicklas 613     {
2137 31 Mar 06 nicklas 614       return (float)Math.sqrt((squaredSum - sum * sum / count) / (count - 1));
2137 31 Mar 06 nicklas 615     }
2116 27 Mar 06 nicklas 616   }
2116 27 Mar 06 nicklas 617   
2327 24 May 06 nicklas 618   /**
2327 24 May 06 nicklas 619      Defines what to to plot on the y axis in a histogram plot.
2327 24 May 06 nicklas 620   */
2116 27 Mar 06 nicklas 621   public enum YAggregate
2116 27 Mar 06 nicklas 622   {
2327 24 May 06 nicklas 623     /**
2327 24 May 06 nicklas 624       Plot the number of spots falling into each bin.
2327 24 May 06 nicklas 625     */
2116 27 Mar 06 nicklas 626     COUNT
2116 27 Mar 06 nicklas 627     {
6127 14 Sep 12 nicklas 628       @Override
2137 31 Mar 06 nicklas 629       public float getMaxY(HistogramBin bin)
2116 27 Mar 06 nicklas 630       {
2116 27 Mar 06 nicklas 631         return bin.getCount();
2116 27 Mar 06 nicklas 632       }
2116 27 Mar 06 nicklas 633     },
2327 24 May 06 nicklas 634     /**
2327 24 May 06 nicklas 635       Plot the sum of all y-values for spots falling into each bin.
2327 24 May 06 nicklas 636     */
2116 27 Mar 06 nicklas 637     SUM
2116 27 Mar 06 nicklas 638     {
6127 14 Sep 12 nicklas 639       @Override
2137 31 Mar 06 nicklas 640       public float getMaxY(HistogramBin bin)
2116 27 Mar 06 nicklas 641       {
2116 27 Mar 06 nicklas 642         return bin.getSum();
2116 27 Mar 06 nicklas 643       }
2116 27 Mar 06 nicklas 644     },
2327 24 May 06 nicklas 645     /**
2327 24 May 06 nicklas 646       Plot the mean of all y-values for spots falling into each bin.
2327 24 May 06 nicklas 647     */
2116 27 Mar 06 nicklas 648     MEAN
2116 27 Mar 06 nicklas 649     {
6127 14 Sep 12 nicklas 650       @Override
2137 31 Mar 06 nicklas 651       public float getMaxY(HistogramBin bin)
2116 27 Mar 06 nicklas 652       {
2116 27 Mar 06 nicklas 653         return bin.getMean();
2116 27 Mar 06 nicklas 654       }
2116 27 Mar 06 nicklas 655     },
2327 24 May 06 nicklas 656     /**
2327 24 May 06 nicklas 657       Plot the maximum of all y-values for spots falling into each bin.
2327 24 May 06 nicklas 658     */
2116 27 Mar 06 nicklas 659     MAX
2116 27 Mar 06 nicklas 660     {
6127 14 Sep 12 nicklas 661       @Override
2137 31 Mar 06 nicklas 662       public float getMaxY(HistogramBin bin)
2116 27 Mar 06 nicklas 663       {
2116 27 Mar 06 nicklas 664         return bin.getMax();
2116 27 Mar 06 nicklas 665       }
2116 27 Mar 06 nicklas 666     },
2327 24 May 06 nicklas 667     /**
2327 24 May 06 nicklas 668       Plot the minimum of all y-values for spots falling into each bin.
2327 24 May 06 nicklas 669     */
2116 27 Mar 06 nicklas 670     MIN
2116 27 Mar 06 nicklas 671     {
6127 14 Sep 12 nicklas 672       @Override
2137 31 Mar 06 nicklas 673       public float getMaxY(HistogramBin bin)
2116 27 Mar 06 nicklas 674       {
2116 27 Mar 06 nicklas 675         return bin.getMin();
2116 27 Mar 06 nicklas 676       }
2137 31 Mar 06 nicklas 677     },
2327 24 May 06 nicklas 678     /**
2327 24 May 06 nicklas 679       Plot the standard deviation of all y-values for spots falling into each bin.
2327 24 May 06 nicklas 680     */
2137 31 Mar 06 nicklas 681     STDEV
2137 31 Mar 06 nicklas 682     {
6127 14 Sep 12 nicklas 683       @Override
2137 31 Mar 06 nicklas 684       public float getMaxY(HistogramBin bin)
2137 31 Mar 06 nicklas 685       {
2137 31 Mar 06 nicklas 686         return bin.getStdev();
2137 31 Mar 06 nicklas 687       }
6127 14 Sep 12 nicklas 688       @Override
2137 31 Mar 06 nicklas 689       public float getMinY(HistogramBin bin)
2137 31 Mar 06 nicklas 690       {
2137 31 Mar 06 nicklas 691         return bin.getMean() - bin.getStdev();
2137 31 Mar 06 nicklas 692       }
2137 31 Mar 06 nicklas 693     },
2327 24 May 06 nicklas 694     /**
2327 24 May 06 nicklas 695       Plot a high/low bar for each bin with the mean +/- 1 standard deviation.
2327 24 May 06 nicklas 696     */
2137 31 Mar 06 nicklas 697     HILOSTDEV
2137 31 Mar 06 nicklas 698     {
6127 14 Sep 12 nicklas 699       @Override
2137 31 Mar 06 nicklas 700       public float getMaxY(HistogramBin bin)
2137 31 Mar 06 nicklas 701       {
2137 31 Mar 06 nicklas 702         return bin.getMean() + bin.getStdev();
2137 31 Mar 06 nicklas 703       }
6127 14 Sep 12 nicklas 704       @Override
2137 31 Mar 06 nicklas 705       public float getMinY(HistogramBin bin)
2137 31 Mar 06 nicklas 706       {
2137 31 Mar 06 nicklas 707         return bin.getMean() - bin.getStdev();
2137 31 Mar 06 nicklas 708       }
2137 31 Mar 06 nicklas 709       
2137 31 Mar 06 nicklas 710     },
2327 24 May 06 nicklas 711     /**
2327 24 May 06 nicklas 712       Plot a high/low bar for each bin with the maximum and minimum values.
2327 24 May 06 nicklas 713     */
2137 31 Mar 06 nicklas 714     HILOMAXMIN
2137 31 Mar 06 nicklas 715     {
6127 14 Sep 12 nicklas 716       @Override
2137 31 Mar 06 nicklas 717       public float getMaxY(HistogramBin bin)
2137 31 Mar 06 nicklas 718       {
2137 31 Mar 06 nicklas 719         return bin.getMax();
2137 31 Mar 06 nicklas 720       }
6127 14 Sep 12 nicklas 721       @Override
2137 31 Mar 06 nicklas 722       public float getMinY(HistogramBin bin)
2137 31 Mar 06 nicklas 723       {
2137 31 Mar 06 nicklas 724         return bin.getMin();
2137 31 Mar 06 nicklas 725       }
2137 31 Mar 06 nicklas 726     }
2137 31 Mar 06 nicklas 727     ;
2116 27 Mar 06 nicklas 728     
2137 31 Mar 06 nicklas 729     public abstract float getMaxY(HistogramBin bin);
2137 31 Mar 06 nicklas 730     public float getMinY(HistogramBin bin)
2137 31 Mar 06 nicklas 731     {
2137 31 Mar 06 nicklas 732       return 0;
2137 31 Mar 06 nicklas 733     }
2116 27 Mar 06 nicklas 734     
2116 27 Mar 06 nicklas 735   }
2116 27 Mar 06 nicklas 736   
2087 17 Mar 06 nicklas 737 }