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    }