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