001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * Copyright by the Board of Trustees of the University of Illinois. * 004 * All rights reserved. * 005 * * 006 * This file is part of the HDF Java Products distribution. * 007 * The full copyright notice, including terms governing use, modification, * 008 * and redistribution, is contained in the files COPYING and Copyright.html. * 009 * COPYING can be found at the root of the source code distribution tree. * 010 * Or, see http://hdfgroup.org/products/hdf-java/doc/Copyright.html. * 011 * If you do not have access to either file, you may request a copy from * 012 * help@hdfgroup.org. * 013 ****************************************************************************/ 014 015package hdf.view; 016 017import java.awt.BorderLayout; 018import java.awt.Color; 019import java.awt.Dimension; 020import java.awt.Frame; 021import java.awt.Graphics; 022import java.awt.Point; 023import java.awt.Window; 024import java.awt.event.ActionEvent; 025import java.awt.event.ActionListener; 026import java.lang.reflect.Array; 027 028import javax.swing.BorderFactory; 029import javax.swing.JButton; 030import javax.swing.JComponent; 031import javax.swing.JDialog; 032import javax.swing.JPanel; 033import javax.swing.WindowConstants; 034 035/** 036 * ChartView displays histogram/line chart of selected row/column of table data 037 * or image. There are two types of chart, histogram and line plot. 038 * 039 * @author Peter X. Cao 040 * @version 2.4 9/6/2007 041 */ 042public class Chart extends JDialog implements ActionListener { 043 044 /** 045 * 046 */ 047 private static final long serialVersionUID = 6306479533747330357L; 048 049 /** histogram style chart */ 050 public static final int HISTOGRAM = 0; 051 052 /** line style chart */ 053 public static final int LINEPLOT = 1; 054 055 /** The default colors of lines for selected columns */ 056 public static final Color[] LINE_COLORS = { Color.black, Color.red, 057 Color.green.darker(), Color.blue, Color.magenta, Color.pink, 058 Color.yellow, Color.orange, Color.gray, Color.cyan }; 059 060 /** the data values of line points or histogram */ 061 protected double data[][]; 062 063 /** Panel that draws plot of data values. */ 064 protected ChartPanel chartP; 065 066 /** number of data points */ 067 protected int numberOfPoints; 068 069 /** the style of chart: histogram or line */ 070 private int chartStyle; 071 072 /** the maximum value of the Y axis */ 073 private double ymax; 074 075 /** the minimum value of the Y axis */ 076 private double ymin; 077 078 /** the maximum value of the X axis */ 079 private double xmax; 080 081 /** the minimum value of the X axis */ 082 private double xmin; 083 084 /** line labels */ 085 private String lineLabels[]; 086 087 /** line colors */ 088 private Color lineColors[]; 089 090 /** number of lines */ 091 private int numberOfLines; 092 093 /* the data to plot against */ 094 private double[] xData = null; 095 096 /** 097 * True if the original data is integer (byte, short, integer, long). 098 */ 099 private boolean isInteger; 100 101 private java.text.DecimalFormat format; 102 103 /** 104 * Constructs a new ChartView given data and data ranges. 105 * <p> 106 * 107 * @param owner 108 * the owner frame of this dialog. 109 * @param title 110 * the title of this dialog. 111 * @param style 112 * the style of the chart. Valid values are: HISTOGRAM and LINE 113 * @param data 114 * the two dimensional data array: data[linenumber][datapoints] 115 * @param xData 116 * the range of the X values, xRange[0]=xmin, xRange[1]=xmax. 117 * @param yRange 118 * the range of the Y values, yRange[0]=ymin, yRange[1]=ymax. 119 */ 120 public Chart(Frame owner, String title, int style, double[][] data, 121 double[] xData, double[] yRange) { 122 super(owner, title, false); 123 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 124 format = new java.text.DecimalFormat("0.00E0"); 125 126 if (data == null) { 127 return; 128 } 129 130 this.chartStyle = style; 131 this.data = data; 132 133 if (style == HISTOGRAM) { 134 isInteger = true; 135 } 136 else { 137 isInteger = false; 138 } 139 140 if (xData != null) { 141 int len = xData.length; 142 if (len == 2) { 143 this.xmin = xData[0]; 144 this.xmax = xData[1]; 145 } 146 else { 147 this.xData = xData; 148 xmin = xmax = xData[0]; 149 for (int i = 0; i < len; i++) { 150 if (xData[i] < xmin) { 151 xmin = xData[i]; 152 } 153 154 if (xData[i] > xmax) { 155 xmax = xData[i]; 156 } 157 } 158 } 159 } 160 else { 161 this.xmin = 1; 162 this.xmax = data[0].length; 163 } 164 165 this.numberOfLines = Array.getLength(data); 166 this.numberOfPoints = Array.getLength(data[0]); 167 this.lineColors = LINE_COLORS; 168 169 if (yRange != null) { 170 // data range is given 171 this.ymin = yRange[0]; 172 this.ymax = yRange[1]; 173 } 174 else { 175 // search data range from the data 176 findDataRange(); 177 } 178 179 if ((ymax < 0.0001) || (ymax > 100000)) { 180 format = new java.text.DecimalFormat("###.####E0#"); 181 } 182 chartP = new ChartPanel(); 183 chartP.setBackground(Color.white); 184 185 createUI(); 186 } 187 188 /** 189 * Creates and layouts GUI components. 190 */ 191 protected void createUI() { 192 Window owner = getOwner(); 193 194 JPanel contentPane = (JPanel) getContentPane(); 195 contentPane.setLayout(new BorderLayout(5, 5)); 196 contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 197 int w = 640 + (ViewProperties.getFontSize() - 12) * 15; 198 int h = 400 + (ViewProperties.getFontSize() - 12) * 10; 199 200 contentPane.setPreferredSize(new Dimension(w, h)); 201 202 contentPane.add(chartP, BorderLayout.CENTER); 203 204 JButton button = new JButton("Close"); 205 button.addActionListener(this); 206 button.setActionCommand("Close"); 207 JPanel tmp = new JPanel(); 208 tmp.add(button); 209 contentPane.add(tmp, BorderLayout.SOUTH); 210 211 Point l = owner.getLocation(); 212 l.x += 220; 213 l.y += 100; 214 setLocation(l); 215 pack(); 216 } 217 218 public void actionPerformed(ActionEvent e) { 219 String cmd = e.getActionCommand(); 220 221 if (cmd.equals("Close")) { 222 dispose(); 223 } 224 } 225 226 /** 227 * Sets the color of each line of a line plot. 228 * 229 * @param c The array of colors of each line of the line plot 230 */ 231 public void setLineColors(Color c[]) { 232 lineColors = c; 233 } 234 235 /** 236 * Sets the labels of each line. 237 * 238 * @param l The array of labels for each line of the line plot 239 */ 240 public void setLineLabels(String l[]) { 241 lineLabels = l; 242 } 243 244 /** Set the data type of the plot data to be integer. */ 245 public void setTypeToInteger() { 246 isInteger = true; 247 } 248 249 /** find and set the minimum and maximum values of the data */ 250 private void findDataRange() { 251 if (data == null) { 252 return; 253 } 254 255 ymin = ymax = data[0][0]; 256 for (int i = 0; i < numberOfLines; i++) { 257 for (int j = 0; j < numberOfPoints; j++) { 258 if (data[i][j] < ymin) { 259 ymin = data[i][j]; 260 } 261 262 if (data[i][j] > ymax) { 263 ymax = data[i][j]; 264 } 265 } 266 } 267 } 268 269 /** The canvas that paints the data lines. */ 270 private class ChartPanel extends JComponent { 271 private static final long serialVersionUID = -3701826094727309097L; 272 273 /** 274 * Paints the plot components. 275 */ 276 @Override 277 public void paint(Graphics g) { 278 if (numberOfLines <= 0) { 279 return; // no data 280 } 281 282 Dimension d = getSize(); 283 int gap = 20; 284 int xgap = 2 * gap; 285 int ygap = 2 * gap; 286 int legendSpace = 0; 287 if ((chartStyle == LINEPLOT) && (lineLabels != null)) { 288 legendSpace = 60; 289 } 290 291 int h = d.height - gap; 292 int w = d.width - (3 * gap + legendSpace); 293 int xnpoints = Math.min(10, numberOfPoints - 1); 294 int ynpoints = 10; 295 296 // draw the X axis 297 g.drawLine(xgap, h, w + xgap, h); 298 299 // draw the Y axis 300 g.drawLine(ygap, h, ygap, 0); 301 302 // draw x labels 303 double xp = 0, x = xmin; 304 double dw = (double) w / (double) xnpoints; 305 double dx = (xmax - xmin) / xnpoints; 306 boolean gtOne = (dx >= 1); 307 for (int i = 0; i <= xnpoints; i++) { 308 x = xmin + i * dx; 309 xp = xgap + i * dw; 310 g.drawLine((int) xp, h, (int) xp, h - 5); 311 if (gtOne) { 312 g 313 .drawString(String.valueOf((int) x), (int) xp - 5, 314 h + gap); 315 } 316 else { 317 g.drawString(String.valueOf(x), (int) xp - 5, h + gap); 318 } 319 } 320 321 // draw y labels 322 double yp = 0, y = ymin; 323 double dh = (double) h / (double) ynpoints; 324 double dy = (ymax - ymin) / (ynpoints); 325 if (dy > 1) { 326 dy = Math.round(dy * 10.0) / 10.0; 327 } 328 for (int i = 0; i <= ynpoints; i++) { 329 yp = i * dh; 330 y = i * dy + ymin; 331 g.drawLine(ygap, h - (int) yp, ygap + 5, h - (int) yp); 332 if (isInteger) { 333 g.drawString(String.valueOf((int) y), 0, h - (int) yp + 8); 334 } 335 else { 336 g.drawString(format.format(y), 0, h - (int) yp + 8); 337 } 338 } 339 340 Color c = g.getColor(); 341 double x0, y0, x1, y1; 342 if (chartStyle == LINEPLOT) { 343 dw = (double) w / (double) (numberOfPoints - 1); 344 345 // use y = a + b* x to calculate pixel positions 346 double b = h / (ymin - ymax); 347 double a = -b * ymax; 348 boolean hasXdata = ((xData != null) && (xData.length >= numberOfPoints)); 349 double xRatio = (1 / (xmax - xmin)) * w; 350 double xD = (xmin / (xmax - xmin)) * w; 351 352 // draw lines for selected spreadsheet columns 353 for (int i = 0; i < numberOfLines; i++) { 354 if ((lineColors != null) 355 && (lineColors.length >= numberOfLines)) { 356 g.setColor(lineColors[i]); 357 } 358 359 // set up the line data for drawing one line a time 360 if (hasXdata) { 361 x0 = xgap + xData[0] * xRatio - xD; 362 } 363 else { 364 x0 = xgap; 365 } 366 y0 = a + b * data[i][0]; 367 368 for (int j = 1; j < numberOfPoints; j++) { 369 if (hasXdata) { 370 x1 = xgap + xData[j] * xRatio - xD; 371 } 372 else { 373 x1 = xgap + j * dw; 374 } 375 376 y1 = a + b * data[i][j]; 377 g.drawLine((int) x0, (int) y0, (int) x1, (int) y1); 378 379 x0 = x1; 380 y0 = y1; 381 } 382 383 // draw line legend 384 if ((lineLabels != null) 385 && (lineLabels.length >= numberOfLines)) { 386 x0 = w + legendSpace; 387 y0 = gap + gap * i; 388 g.drawLine((int) x0, (int) y0, (int) x0 + 7, (int) y0); 389 g 390 .drawString(lineLabels[i], (int) x0 + 10, 391 (int) y0 + 3); 392 } 393 } 394 395 g.setColor(c); // set the color back to its default 396 397 // draw a box on the legend 398 if ((lineLabels != null) 399 && (lineLabels.length >= numberOfLines)) { 400 g.drawRect(w + legendSpace - 10, 10, legendSpace, 10 * gap); 401 } 402 403 } // if (chartStyle == LINEPLOT) 404 else if (chartStyle == HISTOGRAM) { 405 // draw histogram for selected image area 406 xp = xgap; 407 yp = 0; 408 g.setColor(Color.blue); 409 int barWidth = w / numberOfPoints; 410 if (barWidth <= 0) { 411 barWidth = 1; 412 } 413 dw = (double) w / (double) numberOfPoints; 414 for (int j = 0; j < numberOfPoints; j++) { 415 xp = xgap + j * dw; 416 yp = (int) (h * (data[0][j] - ymin) / (ymax - ymin)); 417 g.fillRect((int) xp, (int) (h - yp), barWidth, (int) yp); 418 } 419 420 g.setColor(c); // set the color back to its default 421 } // else if (chartStyle == HISTOGRAM) 422 } // public void paint(Graphics g) 423 } // private class ChartPanel extends Canvas 424 425}