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 /** Sets the color of each line of a line plot */ 227 public void setLineColors(Color c[]) { 228 lineColors = c; 229 } 230 231 /** Sets the labels of each line. */ 232 public void setLineLabels(String l[]) { 233 lineLabels = l; 234 } 235 236 /** Set the data type of the plot data to be integer. */ 237 public void setTypeToInteger() { 238 isInteger = true; 239 } 240 241 /** find and set the minimum and maximum values of the data */ 242 private void findDataRange() { 243 if (data == null) { 244 return; 245 } 246 247 ymin = ymax = data[0][0]; 248 for (int i = 0; i < numberOfLines; i++) { 249 for (int j = 0; j < numberOfPoints; j++) { 250 if (data[i][j] < ymin) { 251 ymin = data[i][j]; 252 } 253 254 if (data[i][j] > ymax) { 255 ymax = data[i][j]; 256 } 257 } 258 } 259 } 260 261 /** The canvas that paints the data lines. */ 262 private class ChartPanel extends JComponent { 263 private static final long serialVersionUID = -3701826094727309097L; 264 265 /** 266 * Paints the plot components. 267 */ 268 @Override 269 public void paint(Graphics g) { 270 if (numberOfLines <= 0) { 271 return; // no data 272 } 273 274 Dimension d = getSize(); 275 int gap = 20; 276 int xgap = 2 * gap; 277 int ygap = 2 * gap; 278 int legendSpace = 0; 279 if ((chartStyle == LINEPLOT) && (lineLabels != null)) { 280 legendSpace = 60; 281 } 282 283 int h = d.height - gap; 284 int w = d.width - (3 * gap + legendSpace); 285 int xnpoints = Math.min(10, numberOfPoints - 1); 286 int ynpoints = 10; 287 288 // draw the X axis 289 g.drawLine(xgap, h, w + xgap, h); 290 291 // draw the Y axis 292 g.drawLine(ygap, h, ygap, 0); 293 294 // draw x labels 295 double xp = 0, x = xmin; 296 double dw = (double) w / (double) xnpoints; 297 double dx = (xmax - xmin) / xnpoints; 298 boolean gtOne = (dx >= 1); 299 for (int i = 0; i <= xnpoints; i++) { 300 x = xmin + i * dx; 301 xp = xgap + i * dw; 302 g.drawLine((int) xp, h, (int) xp, h - 5); 303 if (gtOne) { 304 g 305 .drawString(String.valueOf((int) x), (int) xp - 5, 306 h + gap); 307 } 308 else { 309 g.drawString(String.valueOf(x), (int) xp - 5, h + gap); 310 } 311 } 312 313 // draw y labels 314 double yp = 0, y = ymin; 315 double dh = (double) h / (double) ynpoints; 316 double dy = (ymax - ymin) / (ynpoints); 317 if (dy > 1) { 318 dy = Math.round(dy * 10.0) / 10.0; 319 } 320 for (int i = 0; i <= ynpoints; i++) { 321 yp = i * dh; 322 y = i * dy + ymin; 323 g.drawLine(ygap, h - (int) yp, ygap + 5, h - (int) yp); 324 if (isInteger) { 325 g.drawString(String.valueOf((int) y), 0, h - (int) yp + 8); 326 } 327 else { 328 g.drawString(format.format(y), 0, h - (int) yp + 8); 329 } 330 } 331 332 Color c = g.getColor(); 333 double x0, y0, x1, y1; 334 if (chartStyle == LINEPLOT) { 335 dw = (double) w / (double) (numberOfPoints - 1); 336 337 // use y = a + b* x to calculate pixel positions 338 double b = h / (ymin - ymax); 339 double a = -b * ymax; 340 boolean hasXdata = ((xData != null) && (xData.length >= numberOfPoints)); 341 double xRatio = (1 / (xmax - xmin)) * w; 342 double xD = (xmin / (xmax - xmin)) * w; 343 344 // draw lines for selected spreadsheet columns 345 for (int i = 0; i < numberOfLines; i++) { 346 if ((lineColors != null) 347 && (lineColors.length >= numberOfLines)) { 348 g.setColor(lineColors[i]); 349 } 350 351 // set up the line data for drawing one line a time 352 if (hasXdata) { 353 x0 = xgap + xData[0] * xRatio - xD; 354 } 355 else { 356 x0 = xgap; 357 } 358 y0 = a + b * data[i][0]; 359 360 for (int j = 1; j < numberOfPoints; j++) { 361 if (hasXdata) { 362 x1 = xgap + xData[j] * xRatio - xD; 363 } 364 else { 365 x1 = xgap + j * dw; 366 } 367 368 y1 = a + b * data[i][j]; 369 g.drawLine((int) x0, (int) y0, (int) x1, (int) y1); 370 371 x0 = x1; 372 y0 = y1; 373 } 374 375 // draw line legend 376 if ((lineLabels != null) 377 && (lineLabels.length >= numberOfLines)) { 378 x0 = w + legendSpace; 379 y0 = gap + gap * i; 380 g.drawLine((int) x0, (int) y0, (int) x0 + 7, (int) y0); 381 g 382 .drawString(lineLabels[i], (int) x0 + 10, 383 (int) y0 + 3); 384 } 385 } 386 387 g.setColor(c); // set the color back to its default 388 389 // draw a box on the legend 390 if ((lineLabels != null) 391 && (lineLabels.length >= numberOfLines)) { 392 g.drawRect(w + legendSpace - 10, 10, legendSpace, 10 * gap); 393 } 394 395 } // if (chartStyle == LINEPLOT) 396 else if (chartStyle == HISTOGRAM) { 397 // draw histogram for selected image area 398 xp = xgap; 399 yp = 0; 400 g.setColor(Color.blue); 401 int barWidth = w / numberOfPoints; 402 if (barWidth <= 0) { 403 barWidth = 1; 404 } 405 dw = (double) w / (double) numberOfPoints; 406 for (int j = 0; j < numberOfPoints; j++) { 407 xp = xgap + j * dw; 408 yp = (int) (h * (data[0][j] - ymin) / (ymax - ymin)); 409 g.fillRect((int) xp, (int) (h - yp), barWidth, (int) yp); 410 } 411 412 g.setColor(c); // set the color back to its default 413 } // else if (chartStyle == HISTOGRAM) 414 } // public void paint(Graphics g) 415 } // private class ChartPanel extends Canvas 416 417}