001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2014, 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 * DefaultHeatMapDataset.java
029 * --------------------------
030 * (C) Copyright 2009-2014, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes:
036 * --------
037 * 28-Jan-2009 : Version 1 (DG);
038 *
039 */
040
041package org.jfree.data.general;
042
043import java.io.Serializable;
044import org.jfree.data.DataUtilities;
045import org.jfree.util.PublicCloneable;
046
047/**
048 * A default implementation of the {@link HeatMapDataset} interface.
049 *
050 * @since 1.0.13
051 */
052public class DefaultHeatMapDataset extends AbstractDataset
053        implements HeatMapDataset, Cloneable, PublicCloneable, Serializable {
054
055    /** The number of samples in this dataset for the x-dimension. */
056    private int xSamples;
057
058    /** The number of samples in this dataset for the y-dimension. */
059    private int ySamples;
060
061    /** The minimum x-value in the dataset. */
062    private double minX;
063
064    /** The maximum x-value in the dataset. */
065    private double maxX;
066
067    /** The minimum y-value in the dataset. */
068    private double minY;
069
070    /** The maximum y-value in the dataset. */
071    private double maxY;
072
073    /** Storage for the z-values. */
074    private double[][] zValues;
075
076    /**
077     * Creates a new dataset where all the z-values are initially 0.  This is
078     * a fixed size array of z-values.
079     *
080     * @param xSamples  the number of x-values.
081     * @param ySamples  the number of y-values
082     * @param minX  the minimum x-value in the dataset.
083     * @param maxX  the maximum x-value in the dataset.
084     * @param minY  the minimum y-value in the dataset.
085     * @param maxY  the maximum y-value in the dataset.
086     */
087    public DefaultHeatMapDataset(int xSamples, int ySamples, double minX,
088            double maxX, double minY, double maxY) {
089
090        if (xSamples < 1) {
091            throw new IllegalArgumentException("Requires 'xSamples' > 0");
092        }
093        if (ySamples < 1) {
094            throw new IllegalArgumentException("Requires 'ySamples' > 0");
095        }
096        if (Double.isInfinite(minX) || Double.isNaN(minX)) {
097            throw new IllegalArgumentException("'minX' cannot be INF or NaN.");
098        }
099        if (Double.isInfinite(maxX) || Double.isNaN(maxX)) {
100            throw new IllegalArgumentException("'maxX' cannot be INF or NaN.");
101        }
102        if (Double.isInfinite(minY) || Double.isNaN(minY)) {
103            throw new IllegalArgumentException("'minY' cannot be INF or NaN.");
104        }
105        if (Double.isInfinite(maxY) || Double.isNaN(maxY)) {
106            throw new IllegalArgumentException("'maxY' cannot be INF or NaN.");
107        }
108
109        this.xSamples = xSamples;
110        this.ySamples = ySamples;
111        this.minX = minX;
112        this.maxX = maxX;
113        this.minY = minY;
114        this.maxY = maxY;
115        this.zValues = new double[xSamples][];
116        for (int x = 0; x < xSamples; x++) {
117            this.zValues[x] = new double[ySamples];
118        }
119    }
120
121    /**
122     * Returns the number of x values across the width of the dataset.  The
123     * values are evenly spaced between {@link #getMinimumXValue()} and
124     * {@link #getMaximumXValue()}.
125     *
126     * @return The number of x-values (always &gt; 0).
127     */
128    @Override
129    public int getXSampleCount() {
130        return this.xSamples;
131    }
132
133    /**
134     * Returns the number of y values (or samples) for the dataset.  The
135     * values are evenly spaced between {@link #getMinimumYValue()} and
136     * {@link #getMaximumYValue()}.
137     *
138     * @return The number of y-values (always &gt; 0).
139     */
140    @Override
141    public int getYSampleCount() {
142        return this.ySamples;
143    }
144
145    /**
146     * Returns the lowest x-value represented in this dataset.  A requirement
147     * of this interface is that this method must never return infinite or
148     * Double.NAN values.
149     *
150     * @return The lowest x-value represented in this dataset.
151     */
152    @Override
153    public double getMinimumXValue() {
154        return this.minX;
155    }
156
157    /**
158     * Returns the highest x-value represented in this dataset.  A requirement
159     * of this interface is that this method must never return infinite or
160     * Double.NAN values.
161     *
162     * @return The highest x-value represented in this dataset.
163     */
164    @Override
165    public double getMaximumXValue() {
166        return this.maxX;
167    }
168
169    /**
170     * Returns the lowest y-value represented in this dataset.  A requirement
171     * of this interface is that this method must never return infinite or
172     * Double.NAN values.
173     *
174     * @return The lowest y-value represented in this dataset.
175     */
176    @Override
177    public double getMinimumYValue() {
178        return this.minY;
179    }
180
181    /**
182     * Returns the highest y-value represented in this dataset.  A requirement
183     * of this interface is that this method must never return infinite or
184     * Double.NAN values.
185     *
186     * @return The highest y-value represented in this dataset.
187     */
188    @Override
189    public double getMaximumYValue() {
190        return this.maxY;
191    }
192
193    /**
194     * A convenience method that returns the x-value for the given index.
195     *
196     * @param xIndex  the xIndex.
197     *
198     * @return The x-value.
199     */
200    @Override
201    public double getXValue(int xIndex) {
202        double x = this.minX
203                + (this.maxX - this.minX) * (xIndex / (double) this.xSamples);
204        return x;
205    }
206
207    /**
208     * A convenience method that returns the y-value for the given index.
209     *
210     * @param yIndex  the yIndex.
211     *
212     * @return The y-value.
213     */
214    @Override
215    public double getYValue(int yIndex) {
216        double y = this.minY
217                + (this.maxY - this.minY) * (yIndex / (double) this.ySamples);
218        return y;
219    }
220
221    /**
222     * Returns the z-value at the specified sample position in the dataset.
223     * For a missing or unknown value, this method should return Double.NAN.
224     *
225     * @param xIndex  the position of the x sample in the dataset.
226     * @param yIndex  the position of the y sample in the dataset.
227     *
228     * @return The z-value.
229     */
230    @Override
231    public double getZValue(int xIndex, int yIndex) {
232        return this.zValues[xIndex][yIndex];
233    }
234
235    /**
236     * Returns the z-value at the specified sample position in the dataset.
237     * In this implementation, where the underlying values are stored in an
238     * array of double primitives, you should avoid using this method and
239     * use {@link #getZValue(int, int)} instead.
240     *
241     * @param xIndex  the position of the x sample in the dataset.
242     * @param yIndex  the position of the y sample in the dataset.
243     *
244     * @return The z-value.
245     */
246    @Override
247    public Number getZ(int xIndex, int yIndex) {
248        return new Double(getZValue(xIndex, yIndex));
249    }
250
251    /**
252     * Updates a z-value in the dataset and sends a {@link DatasetChangeEvent}
253     * to all registered listeners.
254     *
255     * @param xIndex  the x-index.
256     * @param yIndex  the y-index.
257     * @param z  the new z-value.
258     */
259    public void setZValue(int xIndex, int yIndex, double z) {
260        setZValue(xIndex, yIndex, z, true);
261    }
262
263    /**
264     * Updates a z-value in the dataset and, if requested, sends a
265     * {@link DatasetChangeEvent} to all registered listeners.
266     *
267     * @param xIndex  the x-index.
268     * @param yIndex  the y-index.
269     * @param z  the new z-value.
270     * @param notify  notify listeners?
271     */
272    public void setZValue(int xIndex, int yIndex, double z, boolean notify) {
273        this.zValues[xIndex][yIndex] = z;
274        if (notify) {
275            fireDatasetChanged();
276        }
277    }
278
279    /**
280     * Tests this dataset for equality with an arbitrary object.
281     *
282     * @param obj  the object (<code>null</code> permitted).
283     *
284     * @return A boolean.
285     */
286    @Override
287    public boolean equals(Object obj) {
288        if (obj == this) {
289            return true;
290        }
291        if (!(obj instanceof DefaultHeatMapDataset)) {
292            return false;
293        }
294        DefaultHeatMapDataset that = (DefaultHeatMapDataset) obj;
295        if (this.xSamples != that.xSamples) {
296            return false;
297        }
298        if (this.ySamples != that.ySamples) {
299            return false;
300        }
301        if (this.minX != that.minX) {
302            return false;
303        }
304        if (this.maxX != that.maxX) {
305            return false;
306        }
307        if (this.minY != that.minY) {
308            return false;
309        }
310        if (this.maxY != that.maxY) {
311            return false;
312        }
313        if (!DataUtilities.equal(this.zValues, that.zValues)) {
314            return false;
315        }
316        // can't find any differences
317        return true;
318    }
319
320    /**
321     * Returns an independent copy of this dataset.
322     *
323     * @return A clone.
324     *
325     * @throws java.lang.CloneNotSupportedException if there is a problem 
326     *         cloning.
327     */
328    @Override
329    public Object clone() throws CloneNotSupportedException {
330        DefaultHeatMapDataset clone = (DefaultHeatMapDataset) super.clone();
331        clone.zValues = DataUtilities.clone(this.zValues);
332        return clone;
333    }
334
335}