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