Source for org.jfree.data.xy.DefaultWindDataset

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, 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:  * DefaultWindDataset.java
  29:  * -----------------------
  30:  * (C) Copyright 2001-2007, by Achilleus Mantzios and Contributors.
  31:  *
  32:  * Original Author:  Achilleus Mantzios;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * $Id: DefaultWindDataset.java,v 1.5.2.4 2007/02/02 15:14:53 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 06-Feb-2002 : Version 1, based on code contributed by Achilleus 
  40:  *               Mantzios (DG);
  41:  * 05-May-2004 : Now extends AbstractXYDataset (DG);
  42:  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
  43:  *               getYValue() (DG);
  44:  * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
  45:  *
  46:  */
  47: 
  48: package org.jfree.data.xy;
  49: 
  50: import java.io.Serializable;
  51: import java.util.Arrays;
  52: import java.util.Collections;
  53: import java.util.Date;
  54: import java.util.List;
  55: 
  56: /**
  57:  * A default implementation of the {@link WindDataset} interface.
  58:  */
  59: public class DefaultWindDataset extends AbstractXYDataset 
  60:                                 implements WindDataset {
  61: 
  62:     /** The keys for the series. */
  63:     private List seriesKeys;
  64: 
  65:     /** Storage for the series data. */
  66:     private List allSeriesData;
  67: 
  68:     /**
  69:      * Constructs a new, empty, dataset.  Since there are currently no methods
  70:      * to add data to an existing dataset, you should probably use a different
  71:      * constructor.
  72:      */
  73:     public DefaultWindDataset() {
  74:         this.seriesKeys = new java.util.ArrayList();
  75:         this.allSeriesData = new java.util.ArrayList();
  76:     }
  77: 
  78:     /**
  79:      * Constructs a dataset based on the specified data array.
  80:      *
  81:      * @param data  the data (<code>null</code> not permitted).
  82:      * 
  83:      * @throws NullPointerException if <code>data</code> is <code>null</code>.
  84:      */
  85:     public DefaultWindDataset(Object[][][] data) {
  86:         this(seriesNameListFromDataArray(data), data);
  87:     }
  88: 
  89:     /**
  90:      * Constructs a dataset based on the specified data array.
  91:      *
  92:      * @param seriesNames  the names of the series (<code>null</code> not 
  93:      *     permitted).
  94:      * @param data  the wind data.
  95:      * 
  96:      * @throws NullPointerException if <code>seriesNames</code> is 
  97:      *     <code>null</code>.
  98:      */
  99:     public DefaultWindDataset(String[] seriesNames, Object[][][] data) {
 100:         this(Arrays.asList(seriesNames), data);
 101:     }
 102: 
 103:     /**
 104:      * Constructs a dataset based on the specified data array.  The array
 105:      * can contain multiple series, each series can contain multiple items,
 106:      * and each item is as follows:
 107:      * <ul>
 108:      * <li><code>data[series][item][0]</code> - the date (either a 
 109:      *   <code>Date</code> or a <code>Number</code> that is the milliseconds 
 110:      *   since 1-Jan-1970);</li>
 111:      * <li><code>data[series][item][1]</code> - the wind direction (1 - 12, 
 112:      *   like the numbers on a clock face);</li>
 113:      * <li><code>data[series][item][2]</code> - the wind force (1 - 12 on the
 114:      *   Beaufort scale)</li>
 115:      * </ul>
 116:      * 
 117:      * @param seriesKeys  the names of the series (<code>null</code> not 
 118:      *     permitted).
 119:      * @param data  the wind dataset (<code>null</code> not permitted).
 120:      * 
 121:      * @throws IllegalArgumentException if <code>seriesKeys</code> is 
 122:      *     <code>null</code>.
 123:      * @throws IllegalArgumentException if the number of series keys does not
 124:      *     match the number of series in the array.
 125:      * @throws NullPointerException if <code>data</code> is <code>null</code>.
 126:      */
 127:     public DefaultWindDataset(List seriesKeys, Object[][][] data) {
 128:         if (seriesKeys == null) {
 129:             throw new IllegalArgumentException("Null 'seriesKeys' argument.");
 130:         }
 131:         if (seriesKeys.size() != data.length) {
 132:             throw new IllegalArgumentException("The number of series keys does "
 133:                     + "not match the number of series in the data array.");
 134:         }
 135:         this.seriesKeys = seriesKeys;
 136:         int seriesCount = data.length;
 137:         this.allSeriesData = new java.util.ArrayList(seriesCount);
 138: 
 139:         for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) {
 140:             List oneSeriesData = new java.util.ArrayList();
 141:             int maxItemCount = data[seriesIndex].length;
 142:             for (int itemIndex = 0; itemIndex < maxItemCount; itemIndex++) {
 143:                 Object xObject = data[seriesIndex][itemIndex][0];
 144:                 if (xObject != null) {
 145:                     Number xNumber;
 146:                     if (xObject instanceof Number) {
 147:                         xNumber = (Number) xObject;
 148:                     }
 149:                     else {
 150:                         if (xObject instanceof Date) {
 151:                             Date xDate = (Date) xObject;
 152:                             xNumber = new Long(xDate.getTime());
 153:                         }
 154:                         else {
 155:                             xNumber = new Integer(0);
 156:                         }
 157:                     }
 158:                     Number windDir = (Number) data[seriesIndex][itemIndex][1];
 159:                     Number windForce = (Number) data[seriesIndex][itemIndex][2];
 160:                     oneSeriesData.add(new WindDataItem(xNumber, windDir, 
 161:                             windForce));
 162:                 }
 163:             }
 164:             Collections.sort(oneSeriesData);
 165:             this.allSeriesData.add(seriesIndex, oneSeriesData);
 166:         }
 167: 
 168:     }
 169: 
 170:     /**
 171:      * Returns the number of series in the dataset.
 172:      * 
 173:      * @return The series count.
 174:      */
 175:     public int getSeriesCount() {
 176:         return this.allSeriesData.size();
 177:     }
 178: 
 179:     /**
 180:      * Returns the number of items in a series.
 181:      * 
 182:      * @param series  the series (zero-based index).
 183:      * 
 184:      * @return The item count.
 185:      */
 186:     public int getItemCount(int series) {
 187:         if (series < 0 || series >= getSeriesCount()) {
 188:             throw new IllegalArgumentException("Invalid series index: " 
 189:                     + series);
 190:         }
 191:         List oneSeriesData = (List) this.allSeriesData.get(series);
 192:         return oneSeriesData.size();
 193:     }
 194: 
 195:     /**
 196:      * Returns the key for a series.
 197:      * 
 198:      * @param series  the series (zero-based index).
 199:      * 
 200:      * @return The series key.
 201:      */
 202:     public Comparable getSeriesKey(int series) {
 203:         if (series < 0 || series >= getSeriesCount()) {
 204:             throw new IllegalArgumentException("Invalid series index: " 
 205:                     + series);
 206:         }
 207:         return (Comparable) this.seriesKeys.get(series);
 208:     }
 209: 
 210:     /**
 211:      * Returns the x-value for one item within a series.  This should represent
 212:      * a point in time, encoded as milliseconds in the same way as
 213:      * java.util.Date.
 214:      *
 215:      * @param series  the series (zero-based index).
 216:      * @param item  the item (zero-based index).
 217:      * 
 218:      * @return The x-value for the item within the series.
 219:      */
 220:     public Number getX(int series, int item) {
 221:         List oneSeriesData = (List) this.allSeriesData.get(series);
 222:         WindDataItem windItem = (WindDataItem) oneSeriesData.get(item);
 223:         return windItem.getX();
 224:     }
 225: 
 226:     /**
 227:      * Returns the y-value for one item within a series.  This maps to the
 228:      * {@link #getWindForce(int, int)} method and is implemented because 
 229:      * <code>WindDataset</code> is an extension of {@link XYDataset}.
 230:      *
 231:      * @param series  the series (zero-based index).
 232:      * @param item  the item (zero-based index).
 233:      * 
 234:      * @return The y-value for the item within the series.
 235:      */
 236:     public Number getY(int series, int item) {
 237:         return getWindForce(series, item);
 238:     }
 239: 
 240:     /**
 241:      * Returns the wind direction for one item within a series.  This is a
 242:      * number between 0 and 12, like the numbers on an upside-down clock face.
 243:      * 
 244:      * @param series  the series (zero-based index).
 245:      * @param item  the item (zero-based index).
 246:      * 
 247:      * @return The wind direction for the item within the series.
 248:      */
 249:     public Number getWindDirection(int series, int item) {
 250:         List oneSeriesData = (List) this.allSeriesData.get(series);
 251:         WindDataItem windItem = (WindDataItem) oneSeriesData.get(item);
 252:         return windItem.getWindDirection();
 253:     }
 254: 
 255:     /**
 256:      * Returns the wind force for one item within a series.  This is a number
 257:      * between 0 and 12, as defined by the Beaufort scale.
 258:      * 
 259:      * @param series  the series (zero-based index).
 260:      * @param item  the item (zero-based index).
 261:      * 
 262:      * @return The wind force for the item within the series.
 263:      */
 264:     public Number getWindForce(int series, int item) {
 265:         List oneSeriesData = (List) this.allSeriesData.get(series);
 266:         WindDataItem windItem = (WindDataItem) oneSeriesData.get(item);
 267:         return windItem.getWindForce();
 268:     }
 269: 
 270:     /**
 271:      * Utility method for automatically generating series names.
 272:      * 
 273:      * @param data  the wind data (<code>null</code> not permitted).
 274:      *
 275:      * @return An array of <i>Series N</i> with N = { 1 .. data.length }.
 276:      * 
 277:      * @throws NullPointerException if <code>data</code> is <code>null</code>.
 278:      */
 279:     public static List seriesNameListFromDataArray(Object[][] data) {
 280: 
 281:         int seriesCount = data.length;
 282:         List seriesNameList = new java.util.ArrayList(seriesCount);
 283:         for (int i = 0; i < seriesCount; i++) {
 284:             seriesNameList.add("Series " + (i + 1));
 285:         }
 286:         return seriesNameList;
 287: 
 288:     }
 289:     
 290:     /**
 291:      * Checks this <code>WindDataset</code> for equality with an arbitrary
 292:      * object.  This method returns <code>true</code> if and only if:
 293:      * <ul>
 294:      *   <li><code>obj</code> is not <code>null</code>;</li>
 295:      *   <li><code>obj</code> is an instance of 
 296:      *       <code>DefaultWindDataset</code>;</li>
 297:      *   <li>both datasets have the same number of series containing identical
 298:      *       values.</li>
 299:      * <ul>
 300:      * 
 301:      * @param obj  the object (<code>null</code> permitted).
 302:      * 
 303:      * @return A boolean.
 304:      */
 305:     public boolean equals(Object obj) {
 306:         if (this == obj) {
 307:             return true;
 308:         }
 309:         if (!(obj instanceof DefaultWindDataset)) {
 310:             return false;
 311:         }
 312:         DefaultWindDataset that = (DefaultWindDataset) obj;
 313:         if (!this.seriesKeys.equals(that.seriesKeys)) {
 314:             return false;
 315:         }
 316:         if (!this.allSeriesData.equals(that.allSeriesData)) {
 317:             return false;
 318:         }
 319:         return true;
 320:     }
 321: 
 322: }
 323: 
 324: /**
 325:  * A wind data item.
 326:  */
 327: class WindDataItem implements Comparable, Serializable {
 328: 
 329:     /** The x-value. */
 330:     private Number x;
 331: 
 332:     /** The wind direction. */
 333:     private Number windDir;
 334: 
 335:     /** The wind force. */
 336:     private Number windForce;
 337: 
 338:     /**
 339:      * Creates a new wind data item.
 340:      *
 341:      * @param x  the x-value.
 342:      * @param windDir  the direction.
 343:      * @param windForce  the force.
 344:      */
 345:     public WindDataItem(Number x, Number windDir, Number windForce) {
 346:         this.x = x;
 347:         this.windDir = windDir;
 348:         this.windForce = windForce;
 349:     }
 350: 
 351:     /**
 352:      * Returns the x-value.
 353:      *
 354:      * @return The x-value.
 355:      */
 356:     public Number getX() {
 357:         return this.x;
 358:     }
 359: 
 360:     /**
 361:      * Returns the wind direction.
 362:      *
 363:      * @return The wind direction.
 364:      */
 365:     public Number getWindDirection() {
 366:         return this.windDir;
 367:     }
 368: 
 369:     /**
 370:      * Returns the wind force.
 371:      *
 372:      * @return The wind force.
 373:      */
 374:     public Number getWindForce() {
 375:         return this.windForce;
 376:     }
 377: 
 378:     /**
 379:      * Compares this item to another object.
 380:      *
 381:      * @param object  the other object.
 382:      *
 383:      * @return An int that indicates the relative comparison.
 384:      */
 385:     public int compareTo(Object object) {
 386:         if (object instanceof WindDataItem) {
 387:             WindDataItem item = (WindDataItem) object;
 388:             if (this.x.doubleValue() > item.x.doubleValue()) {
 389:                 return 1;
 390:             }
 391:             else if (this.x.equals(item.x)) {
 392:                 return 0;
 393:             }
 394:             else {
 395:                 return -1;
 396:             }
 397:         }
 398:         else {
 399:             throw new ClassCastException("WindDataItem.compareTo(error)");
 400:         }
 401:     }
 402:     
 403:     /**
 404:      * Tests this <code>WindDataItem</code> for equality with an arbitrary
 405:      * object.
 406:      * 
 407:      * @param obj  the object (<code>null</code> permitted).
 408:      * 
 409:      * @return A boolean.
 410:      */
 411:     public boolean equals(Object obj) {
 412:         if (this == obj) {
 413:             return false;
 414:         }
 415:         if (!(obj instanceof WindDataItem)) {
 416:             return false;
 417:         }
 418:         WindDataItem that = (WindDataItem) obj;
 419:         if (!this.x.equals(that.x)) {
 420:             return false;
 421:         }
 422:         if (!this.windDir.equals(that.windDir)) {
 423:             return false;
 424:         }
 425:         if (!this.windForce.equals(that.windForce)) {
 426:             return false;
 427:         }
 428:         return true;
 429:     }
 430: 
 431: }