001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2009, 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 * DefaultHeatMapDataset.java
029 * --------------------------
030 * (C) Copyright 2009, 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
041 package org.jfree.data.general;
042
043 import java.io.Serializable;
044 import org.jfree.data.DataUtilities;
045 import org.jfree.util.PublicCloneable;
046
047 /**
048 * A default implementation of the {@link HeatMapDataset} interface.
049 *
050 * @since 1.0.13
051 */
052 public 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 > 0).
127 */
128 public int getXSampleCount() {
129 return this.xSamples;
130 }
131
132 /**
133 * Returns the number of y values (or samples) for the dataset. The
134 * values are evenly spaced between {@link #getMinimumYValue()} and
135 * {@link #getMaximumYValue()}.
136 *
137 * @return The number of y-values (always > 0).
138 */
139 public int getYSampleCount() {
140 return this.ySamples;
141 }
142
143 /**
144 * Returns the lowest x-value represented in this dataset. A requirement
145 * of this interface is that this method must never return infinite or
146 * Double.NAN values.
147 *
148 * @return The lowest x-value represented in this dataset.
149 */
150 public double getMinimumXValue() {
151 return this.minX;
152 }
153
154 /**
155 * Returns the highest x-value represented in this dataset. A requirement
156 * of this interface is that this method must never return infinite or
157 * Double.NAN values.
158 *
159 * @return The highest x-value represented in this dataset.
160 */
161 public double getMaximumXValue() {
162 return this.maxX;
163 }
164
165 /**
166 * Returns the lowest y-value represented in this dataset. A requirement
167 * of this interface is that this method must never return infinite or
168 * Double.NAN values.
169 *
170 * @return The lowest y-value represented in this dataset.
171 */
172 public double getMinimumYValue() {
173 return this.minY;
174 }
175
176 /**
177 * Returns the highest y-value represented in this dataset. A requirement
178 * of this interface is that this method must never return infinite or
179 * Double.NAN values.
180 *
181 * @return The highest y-value represented in this dataset.
182 */
183 public double getMaximumYValue() {
184 return this.maxY;
185 }
186
187 /**
188 * A convenience method that returns the x-value for the given index.
189 *
190 * @param xIndex the xIndex.
191 *
192 * @return The x-value.
193 */
194 public double getXValue(int xIndex) {
195 double x = this.minX
196 + (this.maxX - this.minX) * (xIndex / (double) this.xSamples);
197 return x;
198 }
199
200 /**
201 * A convenience method that returns the y-value for the given index.
202 *
203 * @param yIndex the yIndex.
204 *
205 * @return The y-value.
206 */
207 public double getYValue(int yIndex) {
208 double y = this.minY
209 + (this.maxY - this.minY) * (yIndex / (double) this.ySamples);
210 return y;
211 }
212
213 /**
214 * Returns the z-value at the specified sample position in the dataset.
215 * For a missing or unknown value, this method should return Double.NAN.
216 *
217 * @param xIndex the position of the x sample in the dataset.
218 * @param yIndex the position of the y sample in the dataset.
219 *
220 * @return The z-value.
221 */
222 public double getZValue(int xIndex, int yIndex) {
223 return this.zValues[xIndex][yIndex];
224 }
225
226 /**
227 * Returns the z-value at the specified sample position in the dataset.
228 * In this implementation, where the underlying values are stored in an
229 * array of double primitives, you should avoid using this method and
230 * use {@link #getZValue(int, int)} instead.
231 *
232 * @param xIndex the position of the x sample in the dataset.
233 * @param yIndex the position of the y sample in the dataset.
234 *
235 * @return The z-value.
236 */
237 public Number getZ(int xIndex, int yIndex) {
238 return new Double(getZValue(xIndex, yIndex));
239 }
240
241 /**
242 * Updates a z-value in the dataset and sends a {@link DatasetChangeEvent}
243 * to all registered listeners.
244 *
245 * @param xIndex the x-index.
246 * @param yIndex the y-index.
247 * @param z the new z-value.
248 */
249 public void setZValue(int xIndex, int yIndex, double z) {
250 setZValue(xIndex, yIndex, z, true);
251 }
252
253 /**
254 * Updates a z-value in the dataset and, if requested, sends a
255 * {@link DatasetChangeEvent} to all registered listeners.
256 *
257 * @param xIndex the x-index.
258 * @param yIndex the y-index.
259 * @param z the new z-value.
260 * @param notify notify listeners?
261 */
262 public void setZValue(int xIndex, int yIndex, double z, boolean notify) {
263 this.zValues[xIndex][yIndex] = z;
264 if (notify) {
265 fireDatasetChanged();
266 }
267 }
268
269 /**
270 * Tests this dataset for equality with an arbitrary object.
271 *
272 * @param obj the object (<code>null</code> permitted).
273 *
274 * @return A boolean.
275 */
276 public boolean equals(Object obj) {
277 if (obj == this) {
278 return true;
279 }
280 if (!(obj instanceof DefaultHeatMapDataset)) {
281 return false;
282 }
283 DefaultHeatMapDataset that = (DefaultHeatMapDataset) obj;
284 if (this.xSamples != that.xSamples) {
285 return false;
286 }
287 if (this.ySamples != that.ySamples) {
288 return false;
289 }
290 if (this.minX != that.minX) {
291 return false;
292 }
293 if (this.maxX != that.maxX) {
294 return false;
295 }
296 if (this.minY != that.minY) {
297 return false;
298 }
299 if (this.maxY != that.maxY) {
300 return false;
301 }
302 if (!DataUtilities.equal(this.zValues, that.zValues)) {
303 return false;
304 }
305 // can't find any differences
306 return true;
307 }
308
309 /**
310 * Returns an independent copy of this dataset.
311 *
312 * @return A clone.
313 *
314 * @throws java.lang.CloneNotSupportedException
315 */
316 public Object clone() throws CloneNotSupportedException {
317 DefaultHeatMapDataset clone = (DefaultHeatMapDataset) super.clone();
318 clone.zValues = DataUtilities.clone(this.zValues);
319 return clone;
320 }
321
322 }