// // Nasti.java // /* TO DO list: 1. click on pixel and see spectrum 2. click on pixel and do retrieval 3. display linear combinations of channels 4. Fortran retrieval subroutine takes arrays of radiances and wave numbers, or ?? 5. change which channels are in retrieval 6. compare selected-channel retrieval with standard retrieval 7. see weighting functions associated with each channel - possibly in 3-D like Aune's (turning satellite images on their side) 8. compare area of overlap of weighting functions of selected channels, with area of non-overlap */ package visad.paoloa; // import needed classes import visad.*; import visad.java3d.DisplayImplJ3D; import visad.java3d.DirectManipulationRendererJ3D; import visad.java3d.TwoDDisplayRendererJ3D; import visad.java2d.DisplayImplJ2D; import visad.java2d.DirectManipulationRendererJ2D; import visad.util.LabeledColorWidget; import visad.data.netcdf.Plain; import java.rmi.RemoteException; import java.io.IOException; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class Nasti { // number of times in file int ntimes; // size of image array generated from file int nlines; int nelements; // pointers from time index to image sample index int[] time_to_sample; // pointers from image line, element to time index int[][] sample_to_time; // VisAD Field data object created from file Field nasti; // index of spectrum in nasti range tuple int spectrum_index; // flag to use Java2D boolean java2d = false; // RealTypes for data RealType time; RealType wnum1; RealType atmosphericRadiance; RealType image_line; RealType image_element; // range of wave numbers form file float wnum_low; float wnum_hi; // sample set for image pixels Linear2DSet image_set; // MathTypes for image RealTupleType image_domain; FunctionType image_type; // MathType for red_bar overlaid on spectrum display FunctionType red_bar_type; // type 'java visad.paoloa.Nasti file.nc' to run this application public static void main(String args[]) throws VisADException, RemoteException, IOException { if (args.length < 1) { /* CTR: 29 September 1998 */ System.out.println("To run this program, type " + "\"java visad.paoloa.Nasti file.nc\""); System.out.println("where file.nc is a netCDF file containing a " + "NAST-I file."); return; } Nasti nasti = new Nasti(args[0]); } public Nasti(String filename) throws VisADException, RemoteException, IOException { // create a netCDF reader Plain plain = new Plain(); // open a netCDF file containing a NAST-I file Tuple nasti_tuple = (Tuple) plain.open(filename); plain = null; // extract the time sequence of spectra nasti = (Field) nasti_tuple.getComponent(2); // extract the type of image and use // it to determine how images are displayed FunctionType nasti_type = (FunctionType) nasti.getType(); TupleType nasti_range_type = (TupleType) nasti_type.getRange(); int angle_index = nasti_range_type.getIndex("sceneMirrorAngle"); time = (RealType) ((RealTupleType) nasti_type.getDomain()).getComponent(0); // compute how times map to image pixels Gridded1DSet time_set = (Gridded1DSet) nasti.getDomainSet(); float[][] t = time_set.getSamples(); float[] times = t[0]; ntimes = time_set.getLength(); double[] angles = new double[ntimes]; int[] elements = new int[ntimes]; int[] lines = new int[ntimes]; int line = 0; int max_element = 0; for (int i=0; i<ntimes; i++) { // sceneMirrorAngle, scans between -45 and +45 and definme scan lines angles[i] = ((Real) ((Tuple) nasti.getSample(i)).getComponent(angle_index)).getValue(); /* System.out.println("sceneMirrorAngle[" + i + "] = " + angles[i] + " time = " + times[i]); */ if (i > 0 && angles[i] < angles[i-1]) line++; lines[i] = line; elements[i] = (int) Math.round((angles[i] + 45.0) / 7.5); if (elements[i] < 0) { System.out.println("negative element " + elements[i]); System.exit(0); } if (elements[i] > 12) { System.out.println("element > 12: " + elements[i]); System.exit(0); } if (elements[i] > max_element) max_element = elements[i]; } if (max_element > 12) { System.out.println("max_element = " + max_element + " too large"); System.exit(0); } // size of image nlines = line + 1; nelements = 13; System.out.println("nlines = " + nlines + " nelements = " + nelements); // set up mappings between times and image time_to_sample = new int[ntimes]; sample_to_time = new int[nlines][nelements]; for (int i=0; i<nlines; i++) { for (int j=0; j<nelements; j++) { sample_to_time[i][j] = -1; } } for (int i=0; i<ntimes; i++) { time_to_sample[i] = elements[i] + nelements * lines[i]; sample_to_time[lines[i]][elements[i]] = i; } // get spectrum and types spectrum_index = nasti_range_type.getDimension() - 1; FunctionType spectrum_type = (FunctionType) nasti_range_type.getComponent(spectrum_index); wnum1 = (RealType) ((RealTupleType) spectrum_type.getDomain()).getComponent(0); atmosphericRadiance = (RealType) spectrum_type.getRange(); // build red_bar_type red_bar_type = new FunctionType(atmosphericRadiance, wnum1); // get first spectrum and its sampling Field spectrum0 = (Field) ((Tuple) nasti.getSample(0)).getComponent(spectrum_index); Gridded1DSet spectrum_set = (Gridded1DSet) spectrum0.getDomainSet(); // System.out.println("spectrum_set = " + spectrum_set); float[] lows = spectrum_set.getLow(); float[] his = spectrum_set.getHi(); int spectrum_set_length = spectrum_set.getLength(); // range of wave numbers wnum_low = lows[0]; wnum_hi = his[0]; // set up image // image_set = new Integer2DSet(nelements, nlines); image_set = new Linear2DSet(-48.75, 48.75, 13, -0.5, (double) (nlines - 0.5), nlines); image_line = RealType.getRealType("image_line"); image_element = RealType.getRealType("image_element"); image_domain = new RealTupleType(image_element, image_line); image_type = new FunctionType(image_domain, atmosphericRadiance); // create JFrame (i.e., a window) for display and slider JFrame frame = new JFrame("Nasti VisAD Application"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); // create JPanel in JFrame JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.setAlignmentY(JPanel.TOP_ALIGNMENT); panel.setAlignmentX(JPanel.LEFT_ALIGNMENT); // create two image-spectrum interfaces (each have // interacting image and spectrum displays) ChannelImage channel_image1 = new ChannelImage(); ChannelImage channel_image2 = new ChannelImage(); // add image-spectrum interfaces to the JFrame panel.add(channel_image1); panel.add(channel_image2); frame.getContentPane().add(panel); // set size of JFrame and make it visible // frame.setSize(400, 900); frame.setSize(800, 900); frame.setVisible(true); } /** this make an image of one NAST-I channel, with a JTextField for channel selection, a LabeledColorWidget for pixel colors and a spectrum display */ class ChannelImage extends JPanel implements ActionListener, ItemListener, ScalarMapListener { // array for image radiances double[][] radiances; // image data object for display FlatField image; // declare DataReferences for displaying white_cursor, red_cursor, // spectrum and red_bar DataReferenceImpl image_ref; DataReferenceImpl white_cursor_ref; DataReferenceImpl red_cursor_ref; DataReferenceImpl spectrum_ref; DataReferenceImpl red_bar_ref; // ScalarMap for atmosphericRadiance in the spectrum display ScalarMap radiance_map2; // ScalarMap for wnum1 in the spectrum display ScalarMap wnum_map; // some GUI components JPanel wpanel; JLabel wnum_label; JTextField wnum_field; JPanel zpanel; JCheckBox wnum_zoom; JButton recenter; JPanel dpanel1, dpanel2; // last valid wave number from text field float wnum_last; // true to zoom whum1 range in spectrum display boolean wzoom; // flag to skip one red_cursor_cell event boolean skip_red = false; // construct a image-spectrum interface ChannelImage() throws VisADException, RemoteException { // GUI layout setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setAlignmentY(JPanel.TOP_ALIGNMENT); setAlignmentX(JPanel.LEFT_ALIGNMENT); // construct DataReferences for displaying white_cursor, red_cursor, // spectrum and red_bar white_cursor_ref = new DataReferenceImpl("white_cursor_ref"); red_cursor_ref = new DataReferenceImpl("red_cursor_ref"); spectrum_ref = new DataReferenceImpl("spectrum_ref"); red_bar_ref = new DataReferenceImpl("red_bar_ref"); // create text field for entering wave number wpanel = new JPanel(); wpanel.setLayout(new BoxLayout(wpanel, BoxLayout.X_AXIS)); wnum_label = new JLabel("wave number:"); wnum_field = new JTextField("---"); // WLH 2 Dec 98 Dimension msize = wnum_field.getMaximumSize(); Dimension psize = wnum_field.getPreferredSize(); msize.height = psize.height; wnum_field.setMaximumSize(msize); wnum_field.addActionListener(this); wnum_field.setActionCommand("wavenum"); wnum_field.setEnabled(true); wpanel.add(wnum_label); wpanel.add(wnum_field); wpanel.add(Box.createRigidArea(new Dimension(10, 0))); add(wpanel); // initial wave number in middle of spectrum wnum_last = (wnum_low + wnum_hi) / 2.0f; wnum_field.setText(PlotText.shortString(wnum_last)); // white_cursor in image display for selecting spectrum RealTuple init_white_cursor = new RealTuple(new Real[] {new Real(image_element, 0.0), new Real(image_line, 0.0)}); white_cursor_ref.setData(init_white_cursor); // create image data object for display and initialize radiance // array to missing image = new FlatField(image_type, image_set); radiances = new double[1][nelements * nlines]; for (int i=0; i<nelements * nlines; i++) { radiances[0][i] = Double.NaN; } image_ref = new DataReferenceImpl("image_ref"); image_ref.setData(image); // create red_cursor in spectrum display for setting wave number Real init_red_cursor = new Real(wnum1, (double) wnum_last); red_cursor_ref.setData(init_red_cursor); // initialize image to initial wave number do_image(wnum_last); // create image Display using Java3D in 2-D mode DisplayImpl display1 = null; if (!java2d) { try { display1 = new DisplayImplJ3D("image display", new TwoDDisplayRendererJ3D()); } catch (UnsatisfiedLinkError e) { java2d = true; } } if (java2d) { display1 = new DisplayImplJ2D("image display"); } ScalarMap line_map = new ScalarMap(image_line, Display.YAxis); display1.addMap(line_map); line_map.setRange(12.5, -0.5); ScalarMap element_map = new ScalarMap(image_element, Display.XAxis); display1.addMap(element_map); element_map.setRange(-48.75, 48.75); ScalarMap radiance_map1 = new ScalarMap(atmosphericRadiance, Display.RGB); display1.addMap(radiance_map1); // always autoscale color map to range of radiances display1.setAlwaysAutoScale(true); // turn on scales for image line and element GraphicsModeControl mode1 = display1.getGraphicsModeControl(); // mode1.setScaleEnable(true); // link image to display display1.addReference(image_ref); // make white_cursor and link to display with direct manipulation // (so white_cursor can select spectrum) ConstantMap[] wmaps = {new ConstantMap(1.0, Display.Blue), new ConstantMap(1.0, Display.Red), new ConstantMap(1.0, Display.Green), new ConstantMap(4.0, Display.PointSize)}; if (java2d) { display1.addReferences(new DirectManipulationRendererJ2D(), white_cursor_ref, wmaps); } else { display1.addReferences(new DirectManipulationRendererJ3D(), white_cursor_ref, wmaps); } // create panel for display with border dpanel1 = new JPanel(); dpanel1.setLayout(new BoxLayout(dpanel1, BoxLayout.X_AXIS)); dpanel1.add(display1.getComponent()); dpanel1.add(Box.createHorizontalStrut(0)); Border etchedBorder5 = new CompoundBorder(new EtchedBorder(), new EmptyBorder(5, 5, 5, 5)); dpanel1.setBorder(etchedBorder5); add(dpanel1); // create color widget for atmosphericRadiance LabeledColorWidget lw = new LabeledColorWidget(radiance_map1); Dimension d = new Dimension(400, 200); lw.setMaximumSize(d); JPanel lpanel = new JPanel(); lpanel.setLayout(new BoxLayout(lpanel, BoxLayout.X_AXIS)); lpanel.add(lw); lpanel.setBorder(etchedBorder5); add(lpanel); // create buttons for zooming and center spectrum zpanel = new JPanel(); zpanel.setLayout(new BoxLayout(zpanel, BoxLayout.X_AXIS)); wnum_zoom = new JCheckBox("wave number zoom", false); wnum_zoom.addItemListener(this); recenter = new JButton("Recenter"); recenter.addActionListener(this); recenter.setActionCommand("recenter"); zpanel.add(wnum_zoom); zpanel.add(recenter); add(zpanel); // create spectrum Display using Java3D in 2-D mode DisplayImpl display2 = null; if (java2d) { display2 = new DisplayImplJ2D("spectrum display"); } else { display2 = new DisplayImplJ3D("spectrum display", new TwoDDisplayRendererJ3D()); } wnum_map = new ScalarMap(wnum1, Display.XAxis); display2.addMap(wnum_map); radiance_map2 = new ScalarMap(atmosphericRadiance, Display.YAxis); display2.addMap(radiance_map2); // get autoscale events for atmosphericRadiance, to set length // of red_bar radiance_map2.addScalarMapListener(this); // always autoscale YAxis to range of radiances display1.setAlwaysAutoScale(true); // turn on scales for image line and element GraphicsModeControl mode2 = display2.getGraphicsModeControl(); mode2.setScaleEnable(true); // link spectrum to display display2.addReference(spectrum_ref); // link red_bar for display ConstantMap[] bmaps = {new ConstantMap(0.0, Display.Blue), new ConstantMap(1.0, Display.Red), new ConstantMap(0.0, Display.Green)}; display2.addReference(red_bar_ref, bmaps); // link red_cursor to display with direct manipulation // (so red_cursor can select wave number) ConstantMap[] rmaps = {new ConstantMap(-1.0, Display.YAxis), new ConstantMap(0.0, Display.Blue), new ConstantMap(1.0, Display.Red), new ConstantMap(0.0, Display.Green), new ConstantMap(4.0, Display.PointSize)}; if (java2d) { display2.addReferences(new DirectManipulationRendererJ2D(), red_cursor_ref, rmaps); } else { display2.addReferences(new DirectManipulationRendererJ3D(), red_cursor_ref, rmaps); } // create panel for display with border dpanel2 = new JPanel(); dpanel2.setLayout(new BoxLayout(dpanel2, BoxLayout.X_AXIS)); dpanel2.add(display2.getComponent()); dpanel2.add(Box.createHorizontalStrut(0)); dpanel2.setBorder(etchedBorder5); add(dpanel2); // CellImpl to change spectrum when user moves white_cursor CellImpl white_cursor_cell = new CellImpl() { public void doAction() throws VisADException, RemoteException { int i; red_bar_ref.setData(null); RealTuple white_cursor = (RealTuple) white_cursor_ref.getData(); float elem = (float) ((Real) white_cursor.getComponent(0)).getValue(); int element = (int) Math.round((elem + 45.0) / 7.5); int line = (int) Math.round( ((Real) white_cursor.getComponent(1)).getValue() ); if (0 <= line && line < nlines && 0 <= element && element < nelements) { i = sample_to_time[line][element]; } else { i = -1; } if (i >= 0) { Field spectrum = (Field) ((Tuple) nasti.getSample(i)).getComponent(spectrum_index); spectrum_ref.setData(spectrum); } else { spectrum_ref.setData(null); } } }; // link white_cursor to white_cursor_cell white_cursor_cell.addReference(white_cursor_ref); // CellImpl to change wave number when user moves red_cursor CellImpl red_cursor_cell = new CellImpl() { public void doAction() throws VisADException, RemoteException { int i; if (skip_red) { skip_red = false; return; } Real red_cursor = (Real) red_cursor_ref.getData(); if (red_cursor == null) return; float wnum = (float) red_cursor.getValue(); if (wnum < wnum_low) { wnum = wnum_low; } if (wnum > wnum_hi) { wnum = wnum_hi; } try { do_image(wnum); wnum_last = wnum; do_red_bar(wnum); wnum_field.setText(PlotText.shortString(Math.abs(wnum))); } catch (VisADException exc) { } catch (RemoteException exc) { } } }; // link red_cursor to red_cursor_cell red_cursor_cell.addReference(red_cursor_ref); } /** update image based on wave number */ void do_image(float wnum) throws VisADException, RemoteException { double radiance; for (int i=0; i<ntimes; i++) { Field spectrum = (Field) ((Tuple) nasti.getSample(i)).getComponent(spectrum_index); try { radiance = ((Real) spectrum.evaluate(new Real(wnum1, wnum))).getValue(); } catch (VisADException e1) { radiance = Double.NaN; } radiances[0][time_to_sample[i]] = radiance; } image.setSamples(radiances); } /** update red_bar based on wave number */ synchronized void do_red_bar(float wnum) throws VisADException, RemoteException { double[] rads = radiance_map2.getRange(); if (rads[0] == rads[0] && rads[1] == rads[1]) { float[][] red_bar_set_samples = {{(float) rads[0], (float) rads[1]}}; Gridded1DSet red_bar_set = new Gridded1DSet(atmosphericRadiance, red_bar_set_samples, 2); FlatField red_bar = new FlatField(red_bar_type, red_bar_set); float[][] red_bar_samples = {{wnum, wnum}}; red_bar.setSamples(red_bar_samples); red_bar_ref.setData(red_bar); } } synchronized void do_wzoom() throws VisADException, RemoteException { if (wzoom) { wnum_map.setRange((double) (wnum_last - 10.0), (double) (wnum_last + 10.0)); } else { wnum_map.setRange((double) wnum_low, (double) wnum_hi); } } /** respond to autoscale of atmosphericRadiance */ public void mapChanged(ScalarMapEvent e) { if (radiance_map2.equals(e.getScalarMap())) { try { do_red_bar(wnum_last); } catch (VisADException e2) { } catch (RemoteException e2) { } } } /** ignore changes to ScalarMap control */ public void controlChanged(ScalarMapControlEvent evt) { } /** respond to user type-ins of wave number */ public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("wavenum")) { float wnum = Float.NaN; try { wnum = Float.valueOf(wnum_field.getText()).floatValue(); } catch (NumberFormatException exc) { wnum_field.setText(PlotText.shortString(Math.abs(wnum_last))); } if (wnum == wnum) { if (wnum < wnum_low) { wnum = wnum_low; wnum_field.setText(PlotText.shortString(Math.abs(wnum))); } if (wnum > wnum_hi) { wnum = wnum_hi; wnum_field.setText(PlotText.shortString(Math.abs(wnum))); } try { do_image(wnum); wnum_last = wnum; do_red_bar(wnum); do_wzoom(); skip_red = true; Real red_cursor = new Real(wnum1, (double) wnum_last); red_cursor_ref.setData(red_cursor); } catch (VisADException exc) { wnum_field.setText(PlotText.shortString(Math.abs(wnum_last))); } catch (RemoteException exc) { wnum_field.setText(PlotText.shortString(Math.abs(wnum_last))); } } else wnum_field.setText(PlotText.shortString(Math.abs(wnum_last))); } // end if (cmd.equals("wavenum")) if (cmd.equals("recenter")) { try { do_wzoom(); } catch (VisADException exc) { } catch (RemoteException exc) { } } } public void itemStateChanged(ItemEvent e) { Object o = e.getItemSelectable(); boolean on = (e.getStateChange() == ItemEvent.SELECTED); if (o == wnum_zoom) { try { wzoom = on; do_wzoom(); } catch (VisADException e2) { } catch (RemoteException e2) { } } } } // end class ChannelImage }