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
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}