    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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-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
    047    package org.jfree.data.contour;
    048
    049    import java.util.Arrays;
    050    import java.util.Date;
    051    import java.util.Vector;
    052
    053    import org.jfree.chart.plot.XYPlot;
    054    import org.jfree.chart.renderer.xy.XYBlockRenderer;
    055    import org.jfree.data.Range;
    056    import org.jfree.data.xy.AbstractXYZDataset;
    057    import 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     */
    067    public 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    }