001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * DefaultContourDataset.java 029 * -------------------------- 030 * (C) Copyright 2002-2005, by David M. O'Donnell and Contributors. 031 * 032 * Original Author: David M. O'Donnell; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * $Id: DefaultContourDataset.java,v 1.6.2.1 2005/10/25 21:30:20 mungady Exp $ 036 * 037 * Changes (from 23-Jan-2003) 038 * -------------------------- 039 * 23-Jan-2003 : Added standard header (DG); 040 * 20-May-2003 : removed member vars numX and numY, which were never used (TM); 041 * 06-May-2004 : Now extends AbstractXYZDataset (DG); 042 * 15-Jul-2004 : Switched getX() with getXValue(), getY() with getYValue() and 043 * getZ() with getZValue() methods (DG); 044 * 045 */ 046 047 package org.jfree.data.contour; 048 049 import java.util.Arrays; 050 import java.util.Date; 051 import java.util.Vector; 052 053 import org.jfree.data.Range; 054 import org.jfree.data.xy.AbstractXYZDataset; 055 import org.jfree.data.xy.XYDataset; 056 057 /** 058 * A convenience class that provides a default implementation of the 059 * {@link ContourDataset} interface. 060 * 061 * @author David M. O'Donnell 062 */ 063 public class DefaultContourDataset extends AbstractXYZDataset 064 implements ContourDataset { 065 066 /** The series name (this dataset supports only one series). */ 067 protected Comparable seriesKey = null; 068 069 /** Storage for the x values. */ 070 protected Number[] xValues = null; 071 072 /** Storage for the y values. */ 073 protected Number[] yValues = null; 074 075 /** Storage for the z values. */ 076 protected Number[] zValues = null; 077 078 /** The index for the start of each column in the data. */ 079 protected int[] xIndex = null; 080 081 /** Flags that track whether x, y and z are dates. */ 082 boolean[] dateAxis = new boolean[3]; 083 084 /** 085 * Creates a new dataset, initially empty. 086 */ 087 public DefaultContourDataset() { 088 super(); 089 } 090 091 /** 092 * Constructs a new dataset with the given data. 093 * 094 * @param seriesKey the series key. 095 * @param xData the x values. 096 * @param yData the y values. 097 * @param zData the z values. 098 */ 099 public DefaultContourDataset(Comparable seriesKey, 100 Object[] xData, 101 Object[] yData, 102 Object[] zData) { 103 104 this.seriesKey = seriesKey; 105 initialize(xData, yData, zData); 106 } 107 108 /** 109 * Initialises the dataset. 110 * 111 * @param xData the x values. 112 * @param yData the y values. 113 * @param zData the z values. 114 */ 115 public void initialize(Object[] xData, 116 Object[] yData, 117 Object[] zData) { 118 119 this.xValues = new Double[xData.length]; 120 this.yValues = new Double[yData.length]; 121 this.zValues = new Double[zData.length]; 122 123 // We organise the data with the following assumption: 124 // 1) the data are sorted by x then y 125 // 2) that the data will be represented by a rectangle formed by 126 // using x[i+1], x, y[j+1], and y. 127 // 3) we march along the y-axis at the same value of x until a new 128 // value x is found at which point we will flag the index 129 // where x[i+1]<>x[i] 130 131 Vector tmpVector = new Vector(); //create a temporary vector 132 double x = 1.123452e31; // set x to some arbitary value (used below) 133 for (int k = 0; k < this.xValues.length; k++) { 134 if (xData[k] != null) { 135 Number xNumber; 136 if (xData[k] instanceof Number) { 137 xNumber = (Number) xData[k]; 138 } 139 else if (xData[k] instanceof Date) { 140 this.dateAxis[0] = true; 141 Date xDate = (Date) xData[k]; 142 xNumber = new Long(xDate.getTime()); //store data as Long 143 } 144 else { 145 xNumber = new Integer(0); 146 } 147 this.xValues[k] = new Double(xNumber.doubleValue()); 148 // store Number as Double 149 150 // check if starting new column 151 if (x != this.xValues[k].doubleValue()) { 152 tmpVector.add(new Integer(k)); //store index where new 153 //column starts 154 x = this.xValues[k].doubleValue(); 155 // set x to most recent value 156 } 157 } 158 } 159 160 Object[] inttmp = tmpVector.toArray(); 161 this.xIndex = new int[inttmp.length]; // create array xIndex to hold 162 // new column indices 163 164 for (int i = 0; i < inttmp.length; i++) { 165 this.xIndex[i] = ((Integer) inttmp[i]).intValue(); 166 } 167 for (int k = 0; k < this.yValues.length; k++) { // store y and z axes 168 // as Doubles 169 this.yValues[k] = (Double) yData[k]; 170 if (zData[k] != null) { 171 this.zValues[k] = (Double) zData[k]; 172 } 173 } 174 } 175 176 /** 177 * Creates an object array from an array of doubles. 178 * 179 * @param data the data. 180 * 181 * @return An array of <code>Double</code> objects. 182 */ 183 public static Object[][] formObjectArray(double[][] data) { 184 Object[][] object = new Double[data.length][data[0].length]; 185 186 for (int i = 0; i < object.length; i++) { 187 for (int j = 0; j < object[i].length; j++) { 188 object[i][j] = new Double(data[i][j]); 189 } 190 } 191 return object; 192 } 193 194 /** 195 * Creates an object array from an array of doubles. 196 * 197 * @param data the data. 198 * 199 * @return An array of <code>Double</code> objects. 200 */ 201 public static Object[] formObjectArray(double[] data) { 202 Object[] object = new Double[data.length]; 203 for (int i = 0; i < object.length; i++) { 204 object[i] = new Double(data[i]); 205 } 206 return object; 207 } 208 209 /** 210 * Returns the number of items in the specified series. This method 211 * is provided to satisfy the {@link XYDataset} interface implementation. 212 * 213 * @param series must be zero, as this dataset only supports one series. 214 * 215 * @return The item count. 216 */ 217 public int getItemCount(int series) { 218 if (series > 0) { 219 throw new IllegalArgumentException("Only one series for contour"); 220 } 221 return this.zValues.length; 222 } 223 224 /** 225 * Returns the maximum z-value. 226 * 227 * @return The maximum z-value. 228 */ 229 public double getMaxZValue() { 230 double zMax = -1.e20; 231 for (int k = 0; k < this.zValues.length; k++) { 232 if (this.zValues[k] != null) { 233 zMax = Math.max(zMax, this.zValues[k].doubleValue()); 234 } 235 } 236 return zMax; 237 } 238 239 /** 240 * Returns the minimum z-value. 241 * 242 * @return The minimum z-value. 243 */ 244 public double getMinZValue() { 245 double zMin = 1.e20; 246 for (int k = 0; k < this.zValues.length; k++) { 247 if (this.zValues[k] != null) { 248 zMin = Math.min(zMin, this.zValues[k].doubleValue()); 249 } 250 } 251 return zMin; 252 } 253 254 /** 255 * Returns the maximum z-value within visible region of plot. 256 * 257 * @param x the x range. 258 * @param y the y range. 259 * 260 * @return The z range. 261 */ 262 public Range getZValueRange(Range x, Range y) { 263 264 double minX = x.getLowerBound(); 265 double minY = y.getLowerBound(); 266 double maxX = x.getUpperBound(); 267 double maxY = y.getUpperBound(); 268 269 double zMin = 1.e20; 270 double zMax = -1.e20; 271 for (int k = 0; k < this.zValues.length; k++) { 272 if (this.xValues[k].doubleValue() >= minX 273 && this.xValues[k].doubleValue() <= maxX 274 && this.yValues[k].doubleValue() >= minY 275 && this.yValues[k].doubleValue() <= maxY) { 276 if (this.zValues[k] != null) { 277 zMin = Math.min(zMin, this.zValues[k].doubleValue()); 278 zMax = Math.max(zMax, this.zValues[k].doubleValue()); 279 } 280 } 281 } 282 283 return new Range(zMin, zMax); 284 } 285 286 /** 287 * Returns the minimum z-value. 288 * 289 * @param minX the minimum x value. 290 * @param minY the minimum y value. 291 * @param maxX the maximum x value. 292 * @param maxY the maximum y value. 293 * 294 * @return The minimum z-value. 295 */ 296 public double getMinZValue(double minX, 297 double minY, 298 double maxX, 299 double maxY) { 300 301 double zMin = 1.e20; 302 for (int k = 0; k < this.zValues.length; k++) { 303 if (this.zValues[k] != null) { 304 zMin = Math.min(zMin, this.zValues[k].doubleValue()); 305 } 306 } 307 return zMin; 308 309 } 310 311 /** 312 * Returns the number of series. 313 * <P> 314 * Required by XYDataset interface (this will always return 1) 315 * 316 * @return 1. 317 */ 318 public int getSeriesCount() { 319 return 1; 320 } 321 322 /** 323 * Returns the name of the specified series. 324 * 325 * Method provided to satisfy the XYDataset interface implementation 326 * 327 * @param series must be zero. 328 * 329 * @return The series name. 330 */ 331 public Comparable getSeriesKey(int series) { 332 if (series > 0) { 333 throw new IllegalArgumentException("Only one series for contour"); 334 } 335 return this.seriesKey; 336 } 337 338 /** 339 * Returns the index of the xvalues. 340 * 341 * @return The x values. 342 */ 343 public int[] getXIndices() { 344 return this.xIndex; 345 } 346 347 /** 348 * Returns the x values. 349 * 350 * @return The x values. 351 */ 352 public Number[] getXValues() { 353 return this.xValues; 354 } 355 356 /** 357 * Returns the x value for the specified series and index (zero-based 358 * indices). Required by the {@link XYDataset}. 359 * 360 * @param series must be zero; 361 * @param item the item index (zero-based). 362 * 363 * @return The x value. 364 */ 365 public Number getX(int series, int item) { 366 if (series > 0) { 367 throw new IllegalArgumentException("Only one series for contour"); 368 } 369 return this.xValues[item]; 370 } 371 372 /** 373 * Returns an x value. 374 * 375 * @param item the item index (zero-based). 376 * 377 * @return The X value. 378 */ 379 public Number getXValue(int item) { 380 return this.xValues[item]; 381 } 382 383 /** 384 * Returns a Number array containing all y values. 385 * 386 * @return The Y values. 387 */ 388 public Number[] getYValues() { 389 return this.yValues; 390 } 391 392 /** 393 * Returns the y value for the specified series and index (zero-based 394 * indices). Required by the {@link XYDataset}. 395 * 396 * @param series the series index (must be zero for this dataset). 397 * @param item the item index (zero-based). 398 * 399 * @return The Y value. 400 */ 401 public Number getY(int series, int item) { 402 if (series > 0) { 403 throw new IllegalArgumentException("Only one series for contour"); 404 } 405 return this.yValues[item]; 406 } 407 408 /** 409 * Returns a Number array containing all z values. 410 * 411 * @return The Z values. 412 */ 413 public Number[] getZValues() { 414 return this.zValues; 415 } 416 417 /** 418 * Returns the z value for the specified series and index (zero-based 419 * indices). Required by the {@link XYDataset} 420 * 421 * @param series the series index (must be zero for this dataset). 422 * @param item the item index (zero-based). 423 * 424 * @return The Z value. 425 */ 426 public Number getZ(int series, int item) { 427 if (series > 0) { 428 throw new IllegalArgumentException("Only one series for contour"); 429 } 430 return this.zValues[item]; 431 } 432 433 /** 434 * Returns an int array contain the index into the x values. 435 * 436 * @return The X values. 437 */ 438 public int[] indexX() { 439 int[] index = new int[this.xValues.length]; 440 for (int k = 0; k < index.length; k++) { 441 index[k] = indexX(k); 442 } 443 return index; 444 } 445 446 /** 447 * Given index k, returns the column index containing k. 448 * 449 * @param k index of interest. 450 * 451 * @return The column index. 452 */ 453 public int indexX(int k) { 454 int i = Arrays.binarySearch(this.xIndex, k); 455 if (i >= 0) { 456 return i; 457 } 458 else { 459 return -1 * i - 2; 460 } 461 } 462 463 464 /** 465 * Given index k, return the row index containing k. 466 * 467 * @param k index of interest. 468 * 469 * @return The row index. 470 */ 471 public int indexY(int k) { // this may be obsolete (not used anywhere) 472 return (k / this.xValues.length); 473 } 474 475 /** 476 * Given column and row indices, returns the k index. 477 * 478 * @param i index of along x-axis. 479 * @param j index of along y-axis. 480 * 481 * @return The Z index. 482 */ 483 public int indexZ(int i, int j) { 484 return this.xValues.length * j + i; 485 } 486 487 /** 488 * Returns true if axis are dates. 489 * 490 * @param axisNumber The axis where 0-x, 1-y, and 2-z. 491 * 492 * @return A boolean. 493 */ 494 public boolean isDateAxis(int axisNumber) { 495 if (axisNumber < 0 || axisNumber > 2) { 496 return false; // bad axisNumber 497 } 498 return this.dateAxis[axisNumber]; 499 } 500 501 /** 502 * Sets the names of the series in the data source. 503 * 504 * @param seriesKeys the keys of the series in the data source. 505 */ 506 public void setSeriesKeys(Comparable[] seriesKeys) { 507 if (seriesKeys.length > 1) { 508 throw new IllegalArgumentException( 509 "Contours only support one series" 510 ); 511 } 512 this.seriesKey = seriesKeys[0]; 513 fireDatasetChanged(); 514 } 515 516 }