001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
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    @Override
222    public int getItemCount(int series) {
223        if (series > 0) {
224            throw new IllegalArgumentException("Only one series for contour");
225        }
226        return this.zValues.length;
227    }
228
229    /**
230     * Returns the maximum z-value.
231     *
232     * @return The maximum z-value.
233     */
234    @Override
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    @Override
251    public double getMinZValue() {
252        double zMin = 1.e20;
253        for (int k = 0; k < this.zValues.length; k++) {
254            if (this.zValues[k] != null) {
255                zMin = Math.min(zMin, this.zValues[k].doubleValue());
256            }
257        }
258        return zMin;
259    }
260
261    /**
262     * Returns the maximum z-value within visible region of plot.
263     *
264     * @param x  the x range.
265     * @param y  the y range.
266     *
267     * @return The z range.
268     */
269    @Override
270    public Range getZValueRange(Range x, Range y) {
271
272        double minX = x.getLowerBound();
273        double minY = y.getLowerBound();
274        double maxX = x.getUpperBound();
275        double maxY = y.getUpperBound();
276
277        double zMin = 1.e20;
278        double zMax = -1.e20;
279        for (int k = 0; k < this.zValues.length; k++) {
280            if (this.xValues[k].doubleValue() >= minX
281                && this.xValues[k].doubleValue() <= maxX
282                && this.yValues[k].doubleValue() >= minY
283                && this.yValues[k].doubleValue() <= maxY) {
284                if (this.zValues[k] != null) {
285                    zMin = Math.min(zMin, this.zValues[k].doubleValue());
286                    zMax = Math.max(zMax, this.zValues[k].doubleValue());
287                }
288            }
289        }
290
291        return new Range(zMin, zMax);
292    }
293
294    /**
295     * Returns the minimum z-value.
296     *
297     * @param minX  the minimum x value.
298     * @param minY  the minimum y value.
299     * @param maxX  the maximum x value.
300     * @param maxY  the maximum y value.
301     *
302     * @return The minimum z-value.
303     */
304    public double getMinZValue(double minX,
305                               double minY,
306                               double maxX,
307                               double maxY) {
308
309        double zMin = 1.e20;
310        for (int k = 0; k < this.zValues.length; k++) {
311            if (this.zValues[k] != null) {
312                zMin = Math.min(zMin, this.zValues[k].doubleValue());
313            }
314        }
315        return zMin;
316
317    }
318
319    /**
320     * Returns the number of series.
321     * <P>
322     * Required by XYDataset interface (this will always return 1)
323     *
324     * @return 1.
325     */
326    @Override
327    public int getSeriesCount() {
328        return 1;
329    }
330
331    /**
332     * Returns the name of the specified series.
333     *
334     * Method provided to satisfy the XYDataset interface implementation
335     *
336     * @param series must be zero.
337     *
338     * @return The series name.
339     */
340    @Override
341    public Comparable getSeriesKey(int series) {
342        if (series > 0) {
343            throw new IllegalArgumentException("Only one series for contour");
344        }
345        return this.seriesKey;
346    }
347
348    /**
349     * Returns the index of the xvalues.
350     *
351     * @return The x values.
352     */
353    @Override
354    public int[] getXIndices() {
355        return this.xIndex;
356    }
357
358    /**
359     * Returns the x values.
360     *
361     * @return The x values.
362     */
363    @Override
364    public Number[] getXValues() {
365        return this.xValues;
366    }
367
368    /**
369     * Returns the x value for the specified series and index (zero-based
370     * indices).  Required by the {@link XYDataset}.
371     *
372     * @param series  must be zero;
373     * @param item  the item index (zero-based).
374     *
375     * @return The x value.
376     */
377    @Override
378    public Number getX(int series, int item) {
379        if (series > 0) {
380            throw new IllegalArgumentException("Only one series for contour");
381        }
382        return this.xValues[item];
383    }
384
385    /**
386     * Returns an x value.
387     *
388     * @param item  the item index (zero-based).
389     *
390     * @return The X value.
391     */
392    public Number getXValue(int item) {
393        return this.xValues[item];
394    }
395
396    /**
397     * Returns a Number array containing all y values.
398     *
399     * @return The Y values.
400     */
401    @Override
402    public Number[] getYValues() {
403        return this.yValues;
404    }
405
406    /**
407     * Returns the y value for the specified series and index (zero-based
408     * indices).  Required by the {@link XYDataset}.
409     *
410     * @param series  the series index (must be zero for this dataset).
411     * @param item  the item index (zero-based).
412     *
413     * @return The Y value.
414     */
415    @Override
416    public Number getY(int series, int item) {
417        if (series > 0) {
418            throw new IllegalArgumentException("Only one series for contour");
419        }
420        return this.yValues[item];
421    }
422
423    /**
424     * Returns a Number array containing all z values.
425     *
426     * @return The Z values.
427     */
428    @Override
429    public Number[] getZValues() {
430        return this.zValues;
431    }
432
433    /**
434     * Returns the z value for the specified series and index (zero-based
435     * indices).  Required by the {@link XYDataset}
436     *
437     * @param series  the series index (must be zero for this dataset).
438     * @param item  the item index (zero-based).
439     *
440     * @return The Z value.
441     */
442    @Override
443    public Number getZ(int series, int item) {
444        if (series > 0) {
445            throw new IllegalArgumentException("Only one series for contour");
446        }
447        return this.zValues[item];
448    }
449
450    /**
451     * Returns an int array contain the index into the x values.
452     *
453     * @return The X values.
454     */
455    @Override
456    public int[] indexX() {
457        int[] index = new int[this.xValues.length];
458        for (int k = 0; k < index.length; k++) {
459            index[k] = indexX(k);
460        }
461        return index;
462    }
463
464    /**
465     * Given index k, returns the column index containing k.
466     *
467     * @param k index of interest.
468     *
469     * @return The column index.
470     */
471    public int indexX(int k) {
472        int i = Arrays.binarySearch(this.xIndex, k);
473        if (i >= 0) {
474            return i;
475        }
476        else {
477            return -1 * i - 2;
478        }
479    }
480
481
482    /**
483     * Given index k, return the row index containing k.
484     *
485     * @param k index of interest.
486     *
487     * @return The row index.
488     */
489    public int indexY(int k) { // this may be obsolete (not used anywhere)
490        return (k / this.xValues.length);
491    }
492
493    /**
494     * Given column and row indices, returns the k index.
495     *
496     * @param i index of along x-axis.
497     * @param j index of along y-axis.
498     *
499     * @return The Z index.
500     */
501    public int indexZ(int i, int j) {
502        return this.xValues.length * j + i;
503    }
504
505    /**
506     * Returns true if axis are dates.
507     *
508     * @param axisNumber The axis where 0-x, 1-y, and 2-z.
509     *
510     * @return A boolean.
511     */
512    @Override
513    public boolean isDateAxis(int axisNumber) {
514        if (axisNumber < 0 || axisNumber > 2) {
515            return false; // bad axisNumber
516        }
517        return this.dateAxis[axisNumber];
518    }
519
520    /**
521     * Sets the names of the series in the data source.
522     *
523     * @param seriesKeys  the keys of the series in the data source.
524     */
525    public void setSeriesKeys(Comparable[] seriesKeys) {
526        if (seriesKeys.length > 1) {
527            throw new IllegalArgumentException(
528                    "Contours only support one series");
529        }
530        this.seriesKey = seriesKeys[0];
531        fireDatasetChanged();
532    }
533
534}