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