Source for org.jfree.data.contour.DefaultContourDataset

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