001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas/ 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see https://www.gnu.org/licenses/. 027 */ 028 029package edu.wisc.ssec.mcidasv.data; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.Component; 034import java.awt.Container; 035import java.awt.Dimension; 036import java.awt.FlowLayout; 037import java.awt.GridLayout; 038import java.awt.event.ActionEvent; 039import java.awt.event.ActionListener; 040import java.awt.event.WindowEvent; 041import java.awt.event.WindowAdapter; 042import java.awt.geom.Rectangle2D; 043import java.net.URL; 044import java.rmi.RemoteException; 045import java.util.ArrayList; 046import java.util.List; 047import java.io.PrintWriter; 048import java.io.File; 049 050import javax.swing.JFrame; 051import javax.swing.ButtonGroup; 052import javax.swing.ImageIcon; 053import javax.swing.JComponent; 054import javax.swing.JPanel; 055import javax.swing.JRadioButton; 056import javax.swing.JToggleButton; 057import javax.swing.JButton; 058import javax.swing.JScrollPane; 059import javax.swing.JTable; 060import javax.swing.JFileChooser; 061import javax.swing.filechooser.FileFilter; 062import javax.swing.filechooser.FileNameExtensionFilter; 063import javax.swing.table.AbstractTableModel; 064import javax.swing.table.TableCellRenderer; 065import javax.swing.border.CompoundBorder; 066import javax.swing.border.EmptyBorder; 067import javax.swing.border.LineBorder; 068 069import org.slf4j.Logger; 070import org.slf4j.LoggerFactory; 071 072import visad.AxisScale; 073import visad.BaseColorControl; 074import visad.CellImpl; 075import visad.CoordinateSystem; 076import visad.Data; 077import visad.DelaunayCustom; 078import visad.DisplayEvent; 079import visad.DisplayListener; 080import visad.Real; 081import visad.FieldImpl; 082import visad.FlatField; 083import visad.FunctionType; 084import visad.Gridded2DSet; 085import visad.Gridded3DSet; 086import visad.Integer1DSet; 087import visad.Linear2DSet; 088import visad.LinearLatLonSet; 089import visad.RealTupleType; 090import visad.MathType; 091import visad.RealType; 092import visad.SampledSet; 093import visad.ScalarMap; 094import visad.Set; 095import visad.SetType; 096import visad.UnionSet; 097import visad.VisADException; 098import visad.data.mcidas.BaseMapAdapter; 099import visad.georef.MapProjection; 100import visad.georef.TrivialMapProjection; 101import visad.python.JPythonMethods; 102 103import edu.wisc.ssec.mcidasv.data.hydra.Statistics; 104 105public class StatsTable extends AbstractTableModel { 106 107 private static final Logger logger = 108 LoggerFactory.getLogger(StatsTable.class); 109 110 String [][] data; 111 JTable table; 112 JFrame statsWindow; 113 double total_area = 0.0; 114 int numCols; 115 boolean isShowing = false; 116 Color[] coltab = {new Color(0xf0f0f0), new Color(0xffd0ff), 117 new Color(0xd0ffd0), new Color(0xc0d0ff)}; 118 119 final int maxCols = 9; 120 String[] colNames = {"Stats Parameter","Whole Field X","Whole Field Y", 121 "Magenta X","Magenta Y", "Green X","Green Y","Blue X","Blue Y"}; 122 123 final int maxRows = 13; 124 final String[] rowNames = {"Maximum","Minimum", 125 "Number of points","Mean","Median","Variance","Kurtosis", 126 "Std Dev","Correlation","Difference Maximum", 127 "Difference Minimum","Difference Mean","Area [km^2]"}; 128 129 boolean saveStats = true; 130 131 132 public StatsTable() { 133 this(true); 134 } 135 136 public StatsTable(boolean saveStats) { super(); 137 this.saveStats = saveStats; 138 139 data = new String[maxRows][maxCols]; 140 numCols = 1; 141 142 for (int i=0; i<maxRows; i++) { 143 data[i][0] = rowNames[i]; 144 for (int j=1; j<maxCols; j++) { 145 data[i][j] = " "; 146 } 147 } 148 149 150 table = new JTable(this) { 151 public Component prepareRenderer( 152 TableCellRenderer renderer, int row, int col) { 153 Component comp = super.prepareRenderer(renderer, row, col); 154 Color c = Color.white; 155 if (col == 0) c = coltab[0]; 156 if (col == 3 || col == 4) c = coltab[1]; 157 if (col == 5 || col == 6) c = coltab[2]; 158 if (col == 7 || col == 8) c = coltab[3]; 159 comp.setBackground(c); 160 return comp; 161 } 162 163 }; 164 table.setFillsViewportHeight(true); 165 table.setPreferredScrollableViewportSize(new Dimension(620,220)); 166 table.setRowSelectionAllowed(true); 167 table.setColumnSelectionAllowed(false); 168 169 JButton saveStatsButt = new JButton("Save As CSV"); 170 JScrollPane sp = new JScrollPane(table); 171 statsWindow = new JFrame("Scatter Statistics"); 172 statsWindow.setLayout(new BorderLayout()); 173 statsWindow.getContentPane().add(sp,BorderLayout.NORTH); 174 JPanel bpan = new JPanel(new FlowLayout()); 175 bpan.add(saveStatsButt); 176 if (saveStats) { 177 statsWindow.getContentPane().add(bpan,BorderLayout.SOUTH); 178 } 179 statsWindow.setSize(650,340); 180 statsWindow.pack(); 181 statsWindow.addWindowListener(new WindowAdapter() { 182 public void windowClosing(WindowEvent e) { 183 isShowing = false; 184 } 185 }); 186 187 saveStatsButt.addActionListener(new ActionListener() { 188 public void actionPerformed(final ActionEvent e) { 189 JFileChooser chzr = new JFileChooser(); 190 FileFilter filt = new FileNameExtensionFilter("csv","txt"); 191 chzr.addChoosableFileFilter(filt); 192 int rv = chzr.showSaveDialog(statsWindow); 193 if (rv == JFileChooser.APPROVE_OPTION) { 194 try { 195 File fpw = chzr.getSelectedFile(); 196 statsWindow.setTitle("Scatter Statistics saved to "+fpw.toString()); 197 PrintWriter pw = new PrintWriter(fpw); 198 String line = ""; 199 for (int k=0; k<colNames.length; k++) { 200 if (k != 0) line = line + ","; 201 line = line + colNames[k]; 202 } 203 pw.println(line); 204 205 for (int i=0; i<data.length; i++) { 206 line = ""; 207 for (int j=0; j<data[i].length; j++) { 208 if (j != 0) line = line+","; 209 line = line+data[i][j]; 210 } 211 pw.println(line); 212 } 213 pw.flush(); 214 pw.close(); 215 } catch (Exception epw) { 216 statsWindow.setTitle("Scatter Statistics: File not saved"); 217 } 218 219 } 220 221 } 222 }); 223 224 isShowing = false; 225 statsWindow.setVisible(false); 226 } 227 228 public void setIsShowing() { 229 isShowing = true; 230 } 231 232 public void resetValues(int col) { 233 for (int i=0; i<maxRows; i++) { 234 int c = 2*col + 3; 235 data[i][c] = " "; 236 data[i][c+1] = " "; 237 } 238 fireTableStructureChanged(); 239 } 240 241 public void setNames(String xn, String yn) { 242 colNames[1] = colNames[3] = colNames[5] = colNames[7] =xn; 243 colNames[2] = colNames[4] = colNames[6] = colNames[8] =yn; 244 } 245 246 // fx, fy are Fields, col = 0,1,2,3 (all, red, green, blue) 247 public void setFields(FlatField fx, FlatField fy, int col) { 248 statsWindow.setTitle("Scatter Statistics"); 249 try { 250 Statistics sx = new Statistics(fx); 251 Statistics sy = new Statistics(fy); 252 Statistics diff = new Statistics((FlatField)fx.subtract(fy)); 253 254 int c = 2*col + 1; 255 data[0][c] = fmtMe(((Real)sx.max()).getValue()); 256 data[0][c+1] = fmtMe(((Real)sy.max()).getValue()); 257 258 data[1][c] = fmtMe(((Real)sx.min()).getValue()); 259 data[1][c+1] = fmtMe(((Real)sy.min()).getValue()); 260 261 data[2][c] = String.format("%d",sx.numPoints()); 262 data[2][c+1] = String.format("%d",sy.numPoints()); 263 264 data[3][c] = fmtMe(((Real)sx.mean()).getValue()); 265 data[3][c+1] = fmtMe(((Real)sy.mean()).getValue()); 266 267 data[4][c] = fmtMe(((Real)sx.median()).getValue()); 268 data[4][c+1] = fmtMe(((Real)sy.median()).getValue()); 269 270 data[5][c] = fmtMe(((Real)sx.variance()).getValue()); 271 data[5][c+1] = fmtMe(((Real)sy.variance()).getValue()); 272 273 data[6][c] = fmtMe(((Real)sx.kurtosis()).getValue()); 274 data[6][c+1] = fmtMe(((Real)sy.kurtosis()).getValue()); 275 276 data[7][c] = fmtMe(((Real)sx.standardDeviation()).getValue()); 277 data[7][c+1] = fmtMe(((Real)sy.standardDeviation()).getValue()); 278 279 data[8][c] = fmtMe(((Real)sx.correlation(fy)).getValue()); 280 data[8][c+1] = " "; 281 282 data[9][c] = fmtMe(((Real)diff.max()).getValue()); 283 data[9][c+1] = " "; 284 285 data[10][c] = fmtMe(((Real)diff.min()).getValue()); 286 data[10][c+1] = " "; 287 288 data[11][c] = fmtMe(((Real)diff.mean()).getValue()); 289 data[11][c+1] = " "; 290 291 if (c == 1) { 292 data[12][c] = " "; 293 } else { 294 data[12][c] = fmtMe(total_area); 295 } 296 data[12][c+1] = " "; 297 298 if (c+2 > numCols) numCols = c+2; 299 fireTableStructureChanged(); 300 301 } catch (Exception exc) { 302 logger.error("Problem setting fields", exc); 303 } 304 305 if (isShowing) statsWindow.setVisible(true); 306 } 307 308 public static String fmtMe(double val) { 309 310 if (Math.abs(val) == 0.0) { 311 return "0.00"; 312 313 } else if (Math.abs(val) > 9999.9 || Math.abs(val) < .0010) { 314 return String.format("%.6e", val); 315 316 } else if (Math.abs(val) < 1.0) { 317 return String.format("%.5f", val); 318 319 } else if (Math.abs(val) < 10.0) { 320 return String.format("%.3f", val); 321 322 } else { 323 return String.format("%.2f", val); 324 } 325 } 326 327 public void setPoints(float[][] markScatter, int len, int indx, double area) { 328 try { 329 total_area = area; 330 Integer1DSet sdset = new Integer1DSet(len); 331 FlatField scattX = new FlatField( 332 new FunctionType(RealType.Generic, RealType.Generic), sdset); 333 334 float[][] scattValsX = new float[1][len]; 335 System.arraycopy(markScatter[0],0,scattValsX[0],0,len); 336 scattX.setSamples(scattValsX, false); 337 338 FlatField scattY = new FlatField( 339 new FunctionType(RealType.Generic, RealType.Generic), sdset); 340 float[][] scattValsY = new float[1][len]; 341 System.arraycopy(markScatter[1],0,scattValsY[0],0,len); 342 scattY.setSamples(scattValsY, false); 343 344 setFields(scattX, scattY, indx); 345 346 } catch (Exception esd) { 347 logger.error("Problem setting points", esd); 348 } 349 } 350 351 public int getRowCount() { 352 return maxRows; 353 } 354 public int getColumnCount() { 355 return numCols; 356 } 357 public String getValueAt(int row, int col) { 358 return data[row][col]; 359 } 360 public String getColumnName(int col) { 361 return colNames[col]; 362 } 363}