001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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}