Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * -------------------------- 28: * DefaultContourDataset.java 29: * -------------------------- 30: * (C) Copyright 2002-2005, by David M. O'Donnell and Contributors. 31: * 32: * Original Author: David M. O'Donnell; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * 35: * $Id: DefaultContourDataset.java,v 1.6.2.1 2005/10/25 21:30:20 mungady Exp $ 36: * 37: * Changes (from 23-Jan-2003) 38: * -------------------------- 39: * 23-Jan-2003 : Added standard header (DG); 40: * 20-May-2003 : removed member vars numX and numY, which were never used (TM); 41: * 06-May-2004 : Now extends AbstractXYZDataset (DG); 42: * 15-Jul-2004 : Switched getX() with getXValue(), getY() with getYValue() and 43: * getZ() with getZValue() methods (DG); 44: * 45: */ 46: 47: package org.jfree.data.contour; 48: 49: import java.util.Arrays; 50: import java.util.Date; 51: import java.util.Vector; 52: 53: import org.jfree.data.Range; 54: import org.jfree.data.xy.AbstractXYZDataset; 55: import org.jfree.data.xy.XYDataset; 56: 57: /** 58: * A convenience class that provides a default implementation of the 59: * {@link ContourDataset} interface. 60: * 61: * @author David M. O'Donnell 62: */ 63: public class DefaultContourDataset extends AbstractXYZDataset 64: implements ContourDataset { 65: 66: /** The series name (this dataset supports only one series). */ 67: protected Comparable seriesKey = null; 68: 69: /** Storage for the x values. */ 70: protected Number[] xValues = null; 71: 72: /** Storage for the y values. */ 73: protected Number[] yValues = null; 74: 75: /** Storage for the z values. */ 76: protected Number[] zValues = null; 77: 78: /** The index for the start of each column in the data. */ 79: protected int[] xIndex = null; 80: 81: /** Flags that track whether x, y and z are dates. */ 82: boolean[] dateAxis = new boolean[3]; 83: 84: /** 85: * Creates a new dataset, initially empty. 86: */ 87: public DefaultContourDataset() { 88: super(); 89: } 90: 91: /** 92: * Constructs a new dataset with the given data. 93: * 94: * @param seriesKey the series key. 95: * @param xData the x values. 96: * @param yData the y values. 97: * @param zData the z values. 98: */ 99: 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: }