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