    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jfreechart/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * --------------------------
    028 * DefaultContourDataset.java
    029 * --------------------------
    030 * (C) Copyright 2002-2008, by David M. O'Donnell and Contributors.
    031 *
    032 * Original Author:  David M. O'Donnell;
    033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034 *
    035 * Changes (from 23-Jan-2003)
    036 * --------------------------
    037 * 23-Jan-2003 : Added standard header (DG);
    038 * 20-May-2003 : removed member vars numX and numY, which were never used (TM);
    039 * 06-May-2004 : Now extends AbstractXYZDataset (DG);
    040 * 15-Jul-2004 : Switched getX() with getXValue(), getY() with getYValue() and
    041 *               getZ() with getZValue() methods (DG);
    042 * ------------- JFREECHART 1.0.x --------------------------------------------
    043 * 31-Jan-2007 : Deprecated (DG);
    044 *
    045 */
    046
    047package org.jfree.data.contour;
    048
    049import java.util.Arrays;
    050import java.util.Date;
    051import java.util.Vector;
    052
    053import org.jfree.chart.plot.XYPlot;
    054import org.jfree.chart.renderer.xy.XYBlockRenderer;
    055import org.jfree.data.Range;
    056import org.jfree.data.xy.AbstractXYZDataset;
    057import org.jfree.data.xy.XYDataset;
    058
    059/**
    060 * A convenience class that provides a default implementation of the
    061 * {@link ContourDataset} interface.
    062 *
    063 * @deprecated This class is no longer supported (as of version 1.0.4).  If
    064 *     you are creating contour plots, please try to use {@link XYPlot} and
    065 *     {@link XYBlockRenderer}.
    066 */
    067public class DefaultContourDataset extends AbstractXYZDataset
    068                                   implements ContourDataset {
    069
    070    /** The series name (this dataset supports only one series). */
    071    protected Comparable seriesKey = null;
    072
    073    /** Storage for the x values. */
    074    protected Number[] xValues = null;
    075
    076    /** Storage for the y values. */
    077    protected Number[] yValues = null;
    078
    079    /** Storage for the z values. */
    080    protected Number[] zValues = null;
    081
    082    /** The index for the start of each column in the data. */
    083    protected int[] xIndex = null;
    084
    085    /** Flags that track whether x, y and z are dates. */
    086    boolean[] dateAxis = new boolean[3];
    087
    088    /**
    089     * Creates a new dataset, initially empty.
    090     */
    091    public DefaultContourDataset() {
    092        super();
    093    }
    094
    095    /**
    096     * Constructs a new dataset with the given data.
    097     *
    098     * @param seriesKey  the series key.
    099     * @param xData  the x values.
    100     * @param yData  the y values.
    101     * @param zData  the z values.
    102     */
    103    public DefaultContourDataset(Comparable seriesKey,
    104                                 Object[] xData,
    105                                 Object[] yData,
    106                                 Object[] zData) {
    107
    108        this.seriesKey = seriesKey;
    109        initialize(xData, yData, zData);
    110    }
    111
    112    /**
    113     * Initialises the dataset.
    114     *
    115     * @param xData  the x values.
    116     * @param yData  the y values.
    117     * @param zData  the z values.
    118     */
    119    public void initialize(Object[] xData,
    120                           Object[] yData,
    121                           Object[] zData) {
    122
    123        this.xValues = new Double[xData.length];
    124        this.yValues = new Double[yData.length];
    125        this.zValues = new Double[zData.length];
    126
    127        // We organise the data with the following assumption:
    128        // 1) the data are sorted by x then y
    129        // 2) that the data will be represented by a rectangle formed by
    130        //    using x[i+1], x, y[j+1], and y.
    131        // 3) we march along the y-axis at the same value of x until a new
    132        //    value x is found at which point we will flag the index
    133        //    where x[i+1]<>x[i]
    134
    135        Vector tmpVector = new Vector(); //create a temporary vector
    136        double x = 1.123452e31; // set x to some arbitary value (used below)
    137        for (int k = 0; k < this.xValues.length; k++) {
    138            if (xData[k] != null) {
    139                Number xNumber;
    140                if (xData[k] instanceof Number) {
    141                    xNumber = (Number) xData[k];
    142                }
    143                else if (xData[k] instanceof Date) {
    144                    this.dateAxis[0] = true;
    145                    Date xDate = (Date) xData[k];
    146                    xNumber = new Long(xDate.getTime()); //store data as Long
    147                }
    148                else {
    149                    xNumber = new Integer(0);
    150                }
    151                this.xValues[k] = new Double(xNumber.doubleValue());
    152                    // store Number as Double
    153
    154                // check if starting new column
    155                if (x != this.xValues[k].doubleValue()) {
    156                    tmpVector.add(new Integer(k)); //store index where new
    157                                                   //column starts
    158                    x = this.xValues[k].doubleValue();
    159                                             // set x to most recent value
    160                }
    161            }
    162        }
    163
    164        Object[] inttmp = tmpVector.toArray();
    165        this.xIndex = new int[inttmp.length];  // create array xIndex to hold
    166                                               // new column indices
    167
    168        for (int i = 0; i < inttmp.length; i++) {
    169            this.xIndex[i] = ((Integer) inttmp[i]).intValue();
    170        }
    171        for (int k = 0; k < this.yValues.length; k++) { // store y and z axes
    172                                                        // as Doubles
    173            this.yValues[k] = (Double) yData[k];
    174            if (zData[k] != null) {
    175                this.zValues[k] = (Double) zData[k];
    176            }
    177        }
    178    }
    179
    180    /**
    181     * Creates an object array from an array of doubles.
    182     *
    183     * @param data  the data.
    184     *
    185     * @return An array of <code>Double</code> objects.
    186     */
    187    public static Object[][] formObjectArray(double[][] data) {
    188        Object[][] object = new Double[data.length][data[0].length];
    189
    190        for (int i = 0; i < object.length; i++) {
    191            for (int j = 0; j < object[i].length; j++) {
    192                object[i][j] = new Double(data[i][j]);
    193            }
    194        }
    195        return object;
    196    }
    197
    198    /**
    199     * Creates an object array from an array of doubles.
    200     *
    201     * @param data  the data.
    202     *
    203     * @return An array of <code>Double</code> objects.
    204     */
    205    public static Object[] formObjectArray(double[] data) {
    206        Object[] object = new Double[data.length];
    207        for (int i = 0; i < object.length; i++) {
    208            object[i] = new Double(data[i]);
    209        }
    210        return object;
    211    }
    212
    213    /**
    214     * Returns the number of items in the specified series.  This method
    215     * is provided to satisfy the {@link XYDataset} interface implementation.
    216     *
    217     * @param series  must be zero, as this dataset only supports one series.
    218     *
    219     * @return The item count.
    220     */
    221    public int getItemCount(int series) {
    222        if (series > 0) {
    223            throw new IllegalArgumentException("Only one series for contour");
    224        }
    225        return this.zValues.length;
    226    }
    227
    228    /**
    229     * Returns the maximum z-value.
    230     *
    231     * @return The maximum z-value.
    232     */
    233    public double getMaxZValue() {
    234        double zMax = -1.e20;
    235        for (int k = 0; k < this.zValues.length; k++) {
    236            if (this.zValues[k] != null) {
    237                zMax = Math.max(zMax, this.zValues[k].doubleValue());
    238            }
    239        }
    240        return zMax;
    241    }
    242
    243    /**
    244     * Returns the minimum z-value.
    245     *
    246     * @return The minimum z-value.
    247     */
    248    public double getMinZValue() {
    249        double zMin = 1.e20;
    250        for (int k = 0; k < this.zValues.length; k++) {
    251            if (this.zValues[k] != null) {
    252                zMin = Math.min(zMin, this.zValues[k].doubleValue());
    253            }
    254        }
    255        return zMin;
    256    }
    257
    258    /**
    259     * Returns the maximum z-value within visible region of plot.
    260     *
    261     * @param x  the x range.
    262     * @param y  the y range.
    263     *
    264     * @return The z range.
    265     */
    266    public Range getZValueRange(Range x, Range y) {
    267
    268        double minX = x.getLowerBound();
    269        double minY = y.getLowerBound();
    270        double maxX = x.getUpperBound();
    271        double maxY = y.getUpperBound();
    272
    273        double zMin = 1.e20;
    274        double zMax = -1.e20;
    275        for (int k = 0; k < this.zValues.length; k++) {
    276            if (this.xValues[k].doubleValue() >= minX
    277                && this.xValues[k].doubleValue() <= maxX
    278                && this.yValues[k].doubleValue() >= minY
    279                && this.yValues[k].doubleValue() <= maxY) {
    280                if (this.zValues[k] != null) {
    281                    zMin = Math.min(zMin, this.zValues[k].doubleValue());
    282                    zMax = Math.max(zMax, this.zValues[k].doubleValue());
    283                }
    284            }
    285        }
    286
    287        return new Range(zMin, zMax);
    288    }
    289
    290    /**
    291     * Returns the minimum z-value.
    292     *
    293     * @param minX  the minimum x value.
    294     * @param minY  the minimum y value.
    295     * @param maxX  the maximum x value.
    296     * @param maxY  the maximum y value.
    297     *
    298     * @return The minimum z-value.
    299     */
    300    public double getMinZValue(double minX,
    301                               double minY,
    302                               double maxX,
    303                               double maxY) {
    304
    305        double zMin = 1.e20;
    306        for (int k = 0; k < this.zValues.length; k++) {
    307            if (this.zValues[k] != null) {
    308                zMin = Math.min(zMin, this.zValues[k].doubleValue());
    309            }
    310        }
    311        return zMin;
    312
    313    }
    314
    315    /**
    316     * Returns the number of series.
    317     * <P>
    318     * Required by XYDataset interface (this will always return 1)
    319     *
    320     * @return 1.
    321     */
    322    public int getSeriesCount() {
    323        return 1;
    324    }
    325
    326    /**
    327     * Returns the name of the specified series.
    328     *
    329     * Method provided to satisfy the XYDataset interface implementation
    330     *
    331     * @param series must be zero.
    332     *
    333     * @return The series name.
    334     */
    335    public Comparable getSeriesKey(int series) {
    336        if (series > 0) {
    337            throw new IllegalArgumentException("Only one series for contour");
    338        }
    339        return this.seriesKey;
    340    }
    341
    342    /**
    343     * Returns the index of the xvalues.
    344     *
    345     * @return The x values.
    346     */
    347    public int[] getXIndices() {
    348        return this.xIndex;
    349    }
    350
    351    /**
    352     * Returns the x values.
    353     *
    354     * @return The x values.
    355     */
    356    public Number[] getXValues() {
    357        return this.xValues;
    358    }
    359
    360    /**
    361     * Returns the x value for the specified series and index (zero-based
    362     * indices).  Required by the {@link XYDataset}.
    363     *
    364     * @param series  must be zero;
    365     * @param item  the item index (zero-based).
    366     *
    367     * @return The x value.
    368     */
    369    public Number getX(int series, int item) {
    370        if (series > 0) {
    371            throw new IllegalArgumentException("Only one series for contour");
    372        }
    373        return this.xValues[item];
    374    }
    375
    376    /**
    377     * Returns an x value.
    378     *
    379     * @param item  the item index (zero-based).
    380     *
    381     * @return The X value.
    382     */
    383    public Number getXValue(int item) {
    384        return this.xValues[item];
    385    }
    386
    387    /**
    388     * Returns a Number array containing all y values.
    389     *
    390     * @return The Y values.
    391     */
    392    public Number[] getYValues() {
    393        return this.yValues;
    394    }
    395
    396    /**
    397     * Returns the y value for the specified series and index (zero-based
    398     * indices).  Required by the {@link XYDataset}.
    399     *
    400     * @param series  the series index (must be zero for this dataset).
    401     * @param item  the item index (zero-based).
    402     *
    403     * @return The Y value.
    404     */
    405    public Number getY(int series, int item) {
    406        if (series > 0) {
    407            throw new IllegalArgumentException("Only one series for contour");
    408        }
    409        return this.yValues[item];
    410    }
    411
    412    /**
    413     * Returns a Number array containing all z values.
    414     *
    415     * @return The Z values.
    416     */
    417    public Number[] getZValues() {
    418        return this.zValues;
    419    }
    420
    421    /**
    422     * Returns the z value for the specified series and index (zero-based
    423     * indices).  Required by the {@link XYDataset}
    424     *
    425     * @param series  the series index (must be zero for this dataset).
    426     * @param item  the item index (zero-based).
    427     *
    428     * @return The Z value.
    429     */
    430    public Number getZ(int series, int item) {
    431        if (series > 0) {
    432            throw new IllegalArgumentException("Only one series for contour");
    433        }
    434        return this.zValues[item];
    435    }
    436
    437    /**
    438     * Returns an int array contain the index into the x values.
    439     *
    440     * @return The X values.
    441     */
    442    public int[] indexX() {
    443        int[] index = new int[this.xValues.length];
    444        for (int k = 0; k < index.length; k++) {
    445            index[k] = indexX(k);
    446        }
    447        return index;
    448    }
    449
    450    /**
    451     * Given index k, returns the column index containing k.
    452     *
    453     * @param k index of interest.
    454     *
    455     * @return The column index.
    456     */
    457    public int indexX(int k) {
    458        int i = Arrays.binarySearch(this.xIndex, k);
    459        if (i >= 0) {
    460            return i;
    461        }
    462        else {
    463            return -1 * i - 2;
    464        }
    465    }
    466
    467
    468    /**
    469     * Given index k, return the row index containing k.
    470     *
    471     * @param k index of interest.
    472     *
    473     * @return The row index.
    474     */
    475    public int indexY(int k) { // this may be obsolete (not used anywhere)
    476        return (k / this.xValues.length);
    477    }
    478
    479    /**
    480     * Given column and row indices, returns the k index.
    481     *
    482     * @param i index of along x-axis.
    483     * @param j index of along y-axis.
    484     *
    485     * @return The Z index.
    486     */
    487    public int indexZ(int i, int j) {
    488        return this.xValues.length * j + i;
    489    }
    490
    491    /**
    492     * Returns true if axis are dates.
    493     *
    494     * @param axisNumber The axis where 0-x, 1-y, and 2-z.
    495     *
    496     * @return A boolean.
    497     */
    498    public boolean isDateAxis(int axisNumber) {
    499        if (axisNumber < 0 || axisNumber > 2) {
    500            return false; // bad axisNumber
    501        }
    502        return this.dateAxis[axisNumber];
    503    }
    504
    505    /**
    506     * Sets the names of the series in the data source.
    507     *
    508     * @param seriesKeys  the keys of the series in the data source.
    509     */
    510    public void setSeriesKeys(Comparable[] seriesKeys) {
    511        if (seriesKeys.length > 1) {
    512            throw new IllegalArgumentException(
    513                    "Contours only support one series");
    514        }
    515        this.seriesKey = seriesKeys[0];
    516        fireDatasetChanged();
    517    }
    518
    519}