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 License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * --------------------------
  27. * DefaultContourDataset.java
  28. * --------------------------
  29. * (C) Copyright 2002-2005, by David M. O'Donnell and Contributors.
  30. *
  31. * Original Author: David M. O'Donnell;
  32. * Contributor(s): David Gilbert (for Object Refinery Limited);
  33. *
  34. * $Id: DefaultContourDataset.java,v 1.3 2005/02/24 10:11:25 mungady Exp $
  35. *
  36. * Changes (from 23-Jan-2003)
  37. * --------------------------
  38. * 23-Jan-2003 : Added standard header (DG);
  39. * 20-May-2003 : removed member vars numX and numY, which were never used (TM);
  40. * 06-May-2004 : Now extends AbstractXYZDataset (DG);
  41. * 15-Jul-2004 : Switched getX() with getXValue(), getY() with getYValue() and
  42. * getZ() with getZValue() methods (DG);
  43. *
  44. */
  45. package org.jfree.data.contour;
  46. import java.util.Arrays;
  47. import java.util.Date;
  48. import java.util.Vector;
  49. import org.jfree.data.Range;
  50. import org.jfree.data.xy.AbstractXYZDataset;
  51. import org.jfree.data.xy.XYDataset;
  52. /**
  53. * A convenience class that provides a default implementation of the
  54. * {@link ContourDataset} interface.
  55. *
  56. * @author David M. O'Donnell
  57. */
  58. public class DefaultContourDataset extends AbstractXYZDataset
  59. implements ContourDataset {
  60. /** The series name (this dataset supports only one series). */
  61. protected String seriesName = null;
  62. /** Storage for the x values. */
  63. protected Number[] xValues = null;
  64. /** Storage for the y values. */
  65. protected Number[] yValues = null;
  66. /** Storage for the z values. */
  67. protected Number[] zValues = null;
  68. /** The index for the start of each column in the data. */
  69. protected int[] xIndex = null;
  70. /** Flags that track whether x, y and z are dates. */
  71. boolean[] dateAxis = new boolean[3];
  72. /**
  73. * Creates a new dataset, initially empty.
  74. */
  75. public DefaultContourDataset() {
  76. super();
  77. }
  78. /**
  79. * Constructs a new dataset with the given data.
  80. *
  81. * @param seriesName the series name.
  82. * @param xData the x values.
  83. * @param yData the y values.
  84. * @param zData the z values.
  85. */
  86. public DefaultContourDataset(String seriesName,
  87. Object[] xData,
  88. Object[] yData,
  89. Object[] zData) {
  90. this.seriesName = seriesName;
  91. initialize(xData, yData, zData);
  92. }
  93. /**
  94. * Initialises the dataset.
  95. *
  96. * @param xData the x values.
  97. * @param yData the y values.
  98. * @param zData the z values.
  99. */
  100. public void initialize(Object[] xData,
  101. Object[] yData,
  102. Object[] zData) {
  103. this.xValues = new Double[xData.length];
  104. this.yValues = new Double[yData.length];
  105. this.zValues = new Double[zData.length];
  106. // We organise the data with the following assumption:
  107. // 1) the data are sorted by x then y
  108. // 2) that the data will be represented by a rectangle formed by
  109. // using x[i+1], x, y[j+1], and y.
  110. // 3) we march along the y-axis at the same value of x until a new
  111. // value x is found at which point we will flag the index
  112. // where x[i+1]<>x[i]
  113. Vector tmpVector = new Vector(); //create a temporary vector
  114. double x = 1.123452e31; // set x to some arbitary value (used below)
  115. for (int k = 0; k < this.xValues.length; k++) {
  116. if (xData[k] != null) {
  117. Number xNumber;
  118. if (xData[k] instanceof Number) {
  119. xNumber = (Number) xData[k];
  120. }
  121. else if (xData[k] instanceof Date) {
  122. this.dateAxis[0] = true;
  123. Date xDate = (Date) xData[k];
  124. xNumber = new Long(xDate.getTime()); //store data as Long
  125. }
  126. else {
  127. xNumber = new Integer(0);
  128. }
  129. this.xValues[k] = new Double(xNumber.doubleValue());
  130. // store Number as Double
  131. // check if starting new column
  132. if (x != this.xValues[k].doubleValue()) {
  133. tmpVector.add(new Integer(k)); //store index where new
  134. //column starts
  135. x = this.xValues[k].doubleValue();
  136. // set x to most recent value
  137. }
  138. }
  139. }
  140. Object[] inttmp = tmpVector.toArray();
  141. this.xIndex = new int[inttmp.length]; // create array xIndex to hold
  142. // new column indices
  143. for (int i = 0; i < inttmp.length; i++) {
  144. this.xIndex[i] = ((Integer) inttmp[i]).intValue();
  145. }
  146. for (int k = 0; k < this.yValues.length; k++) { // store y and z axes
  147. // as Doubles
  148. this.yValues[k] = (Double) yData[k];
  149. if (zData[k] != null) {
  150. this.zValues[k] = (Double) zData[k];
  151. }
  152. }
  153. }
  154. /**
  155. * Creates an object array from an array of doubles.
  156. *
  157. * @param data the data.
  158. *
  159. * @return An array of <code>Double</code> objects.
  160. */
  161. public static Object[][] formObjectArray(double[][] data) {
  162. Object[][] object = new Double[data.length][data[0].length];
  163. for (int i = 0; i < object.length; i++) {
  164. for (int j = 0; j < object[i].length; j++) {
  165. object[i][j] = new Double(data[i][j]);
  166. }
  167. }
  168. return object;
  169. }
  170. /**
  171. * Creates an object array from an array of doubles.
  172. *
  173. * @param data the data.
  174. *
  175. * @return An array of <code>Double</code> objects.
  176. */
  177. public static Object[] formObjectArray(double[] data) {
  178. Object[] object = new Double[data.length];
  179. for (int i = 0; i < object.length; i++) {
  180. object[i] = new Double(data[i]);
  181. }
  182. return object;
  183. }
  184. /**
  185. * Returns the number of items in the specified series. This method
  186. * is provided to satisfy the {@link XYDataset} interface implementation.
  187. *
  188. * @param series must be zero, as this dataset only supports one series.
  189. *
  190. * @return The item count.
  191. */
  192. public int getItemCount(int series) {
  193. if (series > 0) {
  194. throw new IllegalArgumentException("Only one series for contour");
  195. }
  196. return this.zValues.length;
  197. }
  198. /**
  199. * Returns the maximum z-value.
  200. *
  201. * @return The maximum z-value.
  202. */
  203. public double getMaxZValue() {
  204. double zMax = -1.e20;
  205. for (int k = 0; k < this.zValues.length; k++) {
  206. if (this.zValues[k] != null) {
  207. zMax = Math.max(zMax, this.zValues[k].doubleValue());
  208. }
  209. }
  210. return zMax;
  211. }
  212. /**
  213. * Returns the minimum z-value.
  214. *
  215. * @return The minimum z-value.
  216. */
  217. public double getMinZValue() {
  218. double zMin = 1.e20;
  219. for (int k = 0; k < this.zValues.length; k++) {
  220. if (this.zValues[k] != null) {
  221. zMin = Math.min(zMin, this.zValues[k].doubleValue());
  222. }
  223. }
  224. return zMin;
  225. }
  226. /**
  227. * Returns the maximum z-value within visible region of plot.
  228. *
  229. * @param x the x range.
  230. * @param y the y range.
  231. *
  232. * @return The z range.
  233. */
  234. public Range getZValueRange(Range x, Range y) {
  235. double minX = x.getLowerBound();
  236. double minY = y.getLowerBound();
  237. double maxX = x.getUpperBound();
  238. double maxY = y.getUpperBound();
  239. double zMin = 1.e20;
  240. double zMax = -1.e20;
  241. for (int k = 0; k < this.zValues.length; k++) {
  242. if (this.xValues[k].doubleValue() >= minX
  243. && this.xValues[k].doubleValue() <= maxX
  244. && this.yValues[k].doubleValue() >= minY
  245. && this.yValues[k].doubleValue() <= maxY) {
  246. if (this.zValues[k] != null) {
  247. zMin = Math.min(zMin, this.zValues[k].doubleValue());
  248. zMax = Math.max(zMax, this.zValues[k].doubleValue());
  249. }
  250. }
  251. }
  252. return new Range(zMin, zMax);
  253. }
  254. /**
  255. * Returns the minimum z-value.
  256. *
  257. * @param minX the minimum x value.
  258. * @param minY the minimum y value.
  259. * @param maxX the maximum x value.
  260. * @param maxY the maximum y value.
  261. *
  262. * @return the minimum z-value.
  263. */
  264. public double getMinZValue(double minX,
  265. double minY,
  266. double maxX,
  267. double maxY) {
  268. double zMin = 1.e20;
  269. for (int k = 0; k < this.zValues.length; k++) {
  270. if (this.zValues[k] != null) {
  271. zMin = Math.min(zMin, this.zValues[k].doubleValue());
  272. }
  273. }
  274. return zMin;
  275. }
  276. /**
  277. * Returns the number of series.
  278. * <P>
  279. * Required by XYDataset interface (this will always return 1)
  280. *
  281. * @return 1.
  282. */
  283. public int getSeriesCount() {
  284. return 1;
  285. }
  286. /**
  287. * Returns the name of the specified series.
  288. *
  289. * Method provided to satisfy the XYDataset interface implementation
  290. *
  291. * @param series must be zero.
  292. *
  293. * @return the series name.
  294. */
  295. public String getSeriesName(int series) {
  296. if (series > 0) {
  297. throw new IllegalArgumentException("Only one series for contour");
  298. }
  299. return this.seriesName;
  300. }
  301. /**
  302. * Returns the index of the xvalues.
  303. *
  304. * @return The x values.
  305. */
  306. public int[] getXIndices() {
  307. return this.xIndex;
  308. }
  309. /**
  310. * Returns the x values.
  311. *
  312. * @return The x values.
  313. */
  314. public Number[] getXValues() {
  315. return this.xValues;
  316. }
  317. /**
  318. * Returns the x value for the specified series and index (zero-based
  319. * indices). Required by the {@link XYDataset}.
  320. *
  321. * @param series must be zero;
  322. * @param item the item index (zero-based).
  323. *
  324. * @return The x value.
  325. */
  326. public Number getX(int series, int item) {
  327. if (series > 0) {
  328. throw new IllegalArgumentException("Only one series for contour");
  329. }
  330. return this.xValues[item];
  331. }
  332. /**
  333. * Returns an x value.
  334. *
  335. * @param item the item index (zero-based).
  336. *
  337. * @return The X value.
  338. */
  339. public Number getXValue(int item) {
  340. return this.xValues[item];
  341. }
  342. /**
  343. * Returns a Number array containing all y values.
  344. *
  345. * @return The Y values.
  346. */
  347. public Number[] getYValues() {
  348. return this.yValues;
  349. }
  350. /**
  351. * Returns the y value for the specified series and index (zero-based
  352. * indices). Required by the {@link XYDataset}.
  353. *
  354. * @param series the series index (must be zero for this dataset).
  355. * @param item the item index (zero-based).
  356. *
  357. * @return The Y value.
  358. */
  359. public Number getY(int series, int item) {
  360. if (series > 0) {
  361. throw new IllegalArgumentException("Only one series for contour");
  362. }
  363. return this.yValues[item];
  364. }
  365. /**
  366. * Returns a Number array containing all z values.
  367. *
  368. * @return The Z values.
  369. */
  370. public Number[] getZValues() {
  371. return this.zValues;
  372. }
  373. /**
  374. * Returns the z value for the specified series and index (zero-based
  375. * indices). Required by the {@link XYDataset}
  376. *
  377. * @param series the series index (must be zero for this dataset).
  378. * @param item the item index (zero-based).
  379. *
  380. * @return The Z value.
  381. */
  382. public Number getZ(int series, int item) {
  383. if (series > 0) {
  384. throw new IllegalArgumentException("Only one series for contour");
  385. }
  386. return this.zValues[item];
  387. }
  388. /**
  389. * Returns an int array contain the index into the x values.
  390. *
  391. * @return The X values.
  392. */
  393. public int[] indexX() {
  394. int[] index = new int[this.xValues.length];
  395. for (int k = 0; k < index.length; k++) {
  396. index[k] = indexX(k);
  397. }
  398. return index;
  399. }
  400. /**
  401. * Given index k, returns the column index containing k.
  402. *
  403. * @param k index of interest.
  404. *
  405. * @return The column index.
  406. */
  407. public int indexX(int k) {
  408. int i = Arrays.binarySearch(this.xIndex, k);
  409. if (i >= 0) {
  410. return i;
  411. }
  412. else {
  413. return -1 * i - 2;
  414. }
  415. }
  416. /**
  417. * Given index k, return the row index containing k.
  418. *
  419. * @param k index of interest.
  420. *
  421. * @return The row index.
  422. */
  423. public int indexY(int k) { // this may be obsolete (not used anywhere)
  424. return (k / this.xValues.length);
  425. }
  426. /**
  427. * Given column and row indices, returns the k index.
  428. *
  429. * @param i index of along x-axis.
  430. * @param j index of along y-axis.
  431. *
  432. * @return The Z index.
  433. */
  434. public int indexZ(int i, int j) {
  435. return this.xValues.length * j + i;
  436. }
  437. /**
  438. * Returns true if axis are dates.
  439. *
  440. * @param axisNumber The axis where 0-x, 1-y, and 2-z.
  441. *
  442. * @return A boolean.
  443. */
  444. public boolean isDateAxis(int axisNumber) {
  445. if (axisNumber < 0 || axisNumber > 2) {
  446. return false; // bad axisNumber
  447. }
  448. return this.dateAxis[axisNumber];
  449. }
  450. /**
  451. * Sets the names of the series in the data source.
  452. *
  453. * @param seriesNames The names of the series in the data source.
  454. */
  455. public void setSeriesNames(String[] seriesNames) {
  456. if (seriesNames.length > 1) {
  457. throw new IllegalArgumentException(
  458. "Contours only support one series"
  459. );
  460. }
  461. this.seriesName = seriesNames[0];
  462. fireDatasetChanged();
  463. }
  464. }