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