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    }