/* Copyright (C) 2001, 2007 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.applications.sar; import gov.nasa.worldwind.WorldWindow; import gov.nasa.worldwind.Configuration; import gov.nasa.worldwind.layers.Earth.*; import gov.nasa.worldwind.layers.Layer; import gov.nasa.worldwind.util.WWIO; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.URL; import java.net.MalformedURLException; import java.nio.ByteBuffer; /** * @author tag * @version $Id: SAR2.java 5059 2008-04-14 19:05:09Z dcollins $ */ public class SAR2 extends JFrame { // Track and WWJ components. private TrackController trackController; private SARAnnotationSupport annotationSupport; private WorldWindow wwd; // Timer components. @SuppressWarnings({"FieldCanBeLocal"}) private Timer redrawTimer; private static final int REDRAW_TIMER_DELAY = 1000; // 1 sec // UI components. private ControlPanel controlPanel; private WWPanel wwPanel; private LayerMenu layerMenu; private JCheckBoxMenuItem feetMenuItem; private JCheckBoxMenuItem metersMenuItem; private HelpFrame helpFrame; private JFileChooser openFileChooser; private SaveTrackDialog saveTrackDialog; private static final int OK = 0; private static final int CANCELLED = 2; private static final int ERROR = 4; // Unit constants. private String elevationUnit; public static final String ELEVATION_UNIT = "SAR2.ElevationUnit"; public static final String UNIT_IMPERIAL = "Imperial"; public static final String UNIT_METRIC = "Metric"; private final static double METER_TO_FEET = 3.280839895; public SAR2() { ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); initComponents(); this.setTitle(SARApp.APP_NAME_AND_VERSION); this.wwd = this.wwPanel.getWwd(); for (Layer layer : this.wwd.getModel().getLayers()) { if (layer instanceof USGSDigitalOrtho) { layer.setOpacity(0.5); layer.setEnabled(false); } else if (layer instanceof USGSTopographicMaps) { layer.setEnabled(false); layer.setOpacity(0.5); } else if (layer instanceof USGSUrbanAreaOrtho) { layer.setEnabled(false); } } this.getAnalysisPanel().setWwd(this.wwd); trackController = new TrackController(); this.trackController.setWwd(this.wwd); this.trackController.setTracksPanel(this.getTracksPanel()); this.trackController.setAnalysisPanel(this.getAnalysisPanel()); this.layerMenu.setWwd(this.wwd); this.annotationSupport = new SARAnnotationSupport(); this.annotationSupport.setWwd(this.wwd); setElevationUnit(UNIT_IMPERIAL); // Setup and start redraw timer - to force downloads to completion without user interaction this.redrawTimer = new Timer(REDRAW_TIMER_DELAY, new ActionListener() // 1 sec { public void actionPerformed(ActionEvent event) { wwd.redraw(); } }); this.redrawTimer.start(); } public static void centerWindowInDesktop(Window window) { if (window != null) { int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width; int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(window.getGraphicsConfiguration()); int desktopWidth = screenWidth - screenInsets.left - screenInsets.right; int desktopHeight = screenHeight - screenInsets.bottom - screenInsets.top; int frameWidth = window.getSize().width; int frameHeight = window.getSize().height; if (frameWidth > desktopWidth) frameWidth = Math.min(frameWidth, desktopWidth); if (frameHeight > desktopHeight) frameHeight = Math.min(frameHeight, desktopHeight); window.setPreferredSize(new Dimension( frameWidth, frameHeight)); window.pack(); window.setLocation( (desktopWidth - frameWidth) / 2 + screenInsets.left, (desktopHeight - frameHeight) / 2 + screenInsets.top); } } public static double metersToFeet(double meters) { return meters * METER_TO_FEET; } public static double feetToMeters(double feet) { return feet / METER_TO_FEET; } public String getElevationUnit() { return this.elevationUnit; } public void setElevationUnit(String unit) { String oldValue = this.elevationUnit; this.elevationUnit = unit; elevationUnitChanged(oldValue, this.elevationUnit); } private void elevationUnitChanged(String oldValue, String newValue) { // Update unit menu selection. if (UNIT_IMPERIAL.equals(newValue)) this.feetMenuItem.setSelected(true); else if (UNIT_METRIC.equals(newValue)) this.metersMenuItem.setSelected(true); // The TracksPanel doesn't listen to the WorldWindow. Handle it as a special case. getTracksPanel().setElevationUnit(newValue); // Use the WorldWindow as a vehicle for communicating the value change. // Components that need to know the current unit will listen on this WorldWindow // for a change with the name ELEVATION_UNIT. this.wwd.setValue(ELEVATION_UNIT, newValue); this.wwd.firePropertyChange(ELEVATION_UNIT, oldValue, newValue); this.wwd.redraw(); } public SARTrack getCurrentTrack() { return getTracksPanel().getCurrentTrack(); } public TracksPanel getTracksPanel() { return controlPanel.getTracksPanel(); } public AnalysisPanel getAnalysisPanel() { return controlPanel.getAnalysisPanel1(); } private void newTrack(String name) { Object inputValue = JOptionPane.showInputDialog(this, "Enter a new track name", "Add New Track", JOptionPane.QUESTION_MESSAGE, null, null, name); if (inputValue == null) return; name = inputValue.toString(); SARTrack st = new SARTrack(name); trackController.addTrack(st); st.clearDirtyBit(); } private void newTrackFromFile(String filePath, String name) { if (filePath == null) { File file = showOpenDialog("Choose a track file"); if (file != null) filePath = file.getPath(); } if (filePath == null) return; SARTrack track = null; try { track = SARTrack.fromFile(filePath); } catch (Exception e) { e.printStackTrace(); } if (track == null) return; if (name != null) track.setName(name); trackController.addTrack(track); try { String annotationFilePath = getAnnotationsPath(filePath); this.annotationSupport.readAnnotations(annotationFilePath, track); } catch (Exception e) { e.printStackTrace(); } track.clearDirtyBit(); } private File showOpenDialog(String title) { if (this.openFileChooser == null) this.openFileChooser = new JFileChooser(); if (title == null) title = "Open Track"; this.openFileChooser.setDialogTitle(title); this.openFileChooser.setMultiSelectionEnabled(false); int state = this.openFileChooser.showOpenDialog(this); return (state == JFileChooser.APPROVE_OPTION) ? this.openFileChooser.getSelectedFile() : null; } private void newTrackFromURL(String urlString, String name) { if (urlString == null) { Object input = JOptionPane.showInputDialog(SAR2.this, "Enter a track URL", "Add New Track", JOptionPane.QUESTION_MESSAGE, null, null, null); if (input != null) urlString = input.toString(); } if (urlString == null) return; URL url = makeURL(urlString); if (url == null) return; SARTrack track = null; try { ByteBuffer bb = WWIO.readURLContentToBuffer(url); File file = WWIO.saveBufferToTempFile(bb, ".xml"); track = SARTrack.fromFile(file.getPath()); } catch (IOException e) { e.printStackTrace(); } if (track == null) return; if (name == null) name = urlString; track.setFile(null); track.setName(name); trackController.addTrack(track); track.markDirty(); } private static URL makeURL(String urlString) { URL url = null; try { if (urlString != null) url = new URL(urlString); } catch (MalformedURLException e) { url = null; } return url; } private int removeTrack(SARTrack track, boolean forceSavePrompt) { if (track == null) return OK; int status = OK; if (track.isDirty() || forceSavePrompt) { int option = SaveTrackDialog.showSaveChangesPrompt(this, null, null, track); // Show a save track dialog that will // always prompt the user to choose a location. if (option == JOptionPane.YES_OPTION) status = saveTrack(track, true); else if (option == JOptionPane.CANCEL_OPTION) status = CANCELLED; } if (status != OK) return status; try { track.firePropertyChange(TrackController.TRACK_REMOVE, null, track); this.trackController.refreshCurrentTrack(); this.annotationSupport.removeAnnotationsForTrack(track); this.wwd.redraw(); } catch (Exception e) { e.printStackTrace(); return ERROR; } return OK; } private int removeAllTracks(boolean forceSavePrompt) { int status = OK; for (SARTrack track : getTracksPanel().getAllTracks()) { status |= removeTrack(track, forceSavePrompt); if ((status & CANCELLED) != 0) return status; } return status; } private int saveTrack(SARTrack track, boolean forceSavePrompt) { return saveTrack( track, null, // Use track's file, or prompt user. 0, // Use track's format. true, // Save annotations forceSavePrompt); } private int saveTrack(SARTrack track, File file, int format, boolean saveAnnotations, boolean forceSavePrompt) { if (track == null) return OK; if (file == null) file = track.getFile(); if (format == 0) format = track.getFormat(); // Show the "Overwrite?" dialog if: // * The current track has a source file. // * AND The caller has specified not to show the save dialog. //if (file != null && !forceSavePrompt) //{ // int result = SaveTrackDialog.showOverwritePrompt(this, null, null, JOptionPane.YES_NO_CANCEL_OPTION, file); // if (result == JOptionPane.CANCEL_OPTION) // return CANCELLED; // else if (result == JOptionPane.NO_OPTION) // forceSavePrompt = true; //} // Show the "Save As..." dialog if either: // * The current track has no source file. // * The caller has specified that the user should prompted to select a file, if (file == null || forceSavePrompt) { int result = showSaveDialog(track); if (result == SaveTrackDialog.CANCEL_OPTION) return CANCELLED; else if (result == SaveTrackDialog.ERROR_OPTION) return ERROR; file = this.saveTrackDialog.getSelectedFile(); format = this.saveTrackDialog.getFileFormat(); saveAnnotations = this.saveTrackDialog.isSaveAnnotations(); } try { // Get the file's last modified time, // or zero if the file does not exist. long time = file.exists() ? file.lastModified() : 0; SARTrack.toFile(track, file.getPath(), format); if (saveAnnotations) { String annotationFilePath = getAnnotationsPath(file.getPath()); this.annotationSupport.writeAnnotations(annotationFilePath, track); } // If the track was saved sucessfully (it exists and // is newer than is was before the save operation), // then adopt the properties of the new // file and format, and clear the track's dirty bit. if (file.exists() && time <= file.lastModified()) { track.setFile(file); track.setFormat(format); track.setName(file.getName()); track.clearDirtyBit(); } } catch (Exception e) { e.printStackTrace(); return ERROR; } return OK; } private int showSaveDialog(SARTrack track) { if (this.saveTrackDialog == null) this.saveTrackDialog = new SaveTrackDialog(); this.saveTrackDialog.setDialogTitle(track); this.saveTrackDialog.setFileFormat(track); this.saveTrackDialog.setSelectedFile(track); return this.saveTrackDialog.showSaveDialog(this); } private SARAnnotation getCurrentAnnotation() { return this.annotationSupport.getCurrent(); } private void newAnnotation() { newAnnotation(null, getCurrentTrack()); } private void newAnnotation(String text, SARTrack track) { this.annotationSupport.addNew(text, track); this.wwd.redraw(); } private void removeAnnotation(SARAnnotation annotation) { if (annotation != null) { this.annotationSupport.remove(annotation); } this.wwd.redraw(); } private void setAnnotationsEnabled(boolean show) { this.annotationSupport.setEnabled(show); this.wwd.redraw(); } private void showHelp() { try { if (this.helpFrame == null) this.helpFrame = new HelpFrame(); this.helpFrame.setVisible(true); } catch (IOException e1) { System.err.println("Unable to open Help window"); e1.printStackTrace(); } } public void showAbout() { SARAboutDialog dialog = new SARAboutDialog(); dialog.showDialog(this); } public boolean exit() { int status = removeAllTracks(false); if ((status & CANCELLED) != 0) return false; dispose(); System.exit(0); return true; } private String getAnnotationsPath(String trackFilePath) { return (trackFilePath != null) ? trackFilePath + ".sta" : null; } private void initComponents() { //======== this ======== setTitle("World Wind Search and Rescue"); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { exit(); } public void windowClosed(WindowEvent event) { exit(); } }); Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); controlPanel = new ControlPanel(); contentPane.add(controlPanel, BorderLayout.WEST); //---- WWPanel ---- wwPanel = new WWPanel(); wwPanel.setPreferredSize(new Dimension(1000, 800)); contentPane.add(wwPanel, BorderLayout.CENTER); //======== MenuBar ======== JMenuBar menuBar = new JMenuBar(); { JMenu fileMenu = new JMenu(); //======== "File" ======== { fileMenu.setText("File"); fileMenu.setMnemonic('F'); //---- "New Track" ---- JMenuItem newTrack = new JMenuItem(); newTrack.setText("New Track..."); newTrack.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newTrack(null); } }); fileMenu.add(newTrack); //---- "Open Track File" ---- JMenuItem openTrackFile = new JMenuItem(); openTrackFile.setText("Open Track File..."); openTrackFile.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newTrackFromFile(null, null); } }); fileMenu.add(openTrackFile); //---- "Open Track URL..." ---- JMenuItem openTrackURL = new JMenuItem(); openTrackURL.setText("Open Track URL..."); openTrackURL.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newTrackFromURL(null, null); } }); fileMenu.add(openTrackURL); //---- "Close Track" ---- JMenuItem removeTrack = new JMenuItem(); removeTrack.setText("Close Track"); removeTrack.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeTrack(getCurrentTrack(), false); } }); fileMenu.add(removeTrack); //-------- fileMenu.addSeparator(); //---- "Save Track" ---- JMenuItem saveTrack = new JMenuItem(); saveTrack.setText("Save Track"); saveTrack.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { // Show a save track dialog that won't prompt the user // to choose a location unless it has to. saveTrack(getCurrentTrack(), true); } }); fileMenu.add(saveTrack); //---- "Save Track As..." ---- JMenuItem saveTrackAs = new JMenuItem(); saveTrackAs.setText("Save Track As..."); saveTrackAs.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { // Show a save track dialog that will always // prompt the user to choose a location. saveTrack(getCurrentTrack(), true); } }); fileMenu.add(saveTrackAs); //-------- fileMenu.addSeparator(); //---- urlTrackFetch1 ---- JMenuItem urlTrackFetch1 = new JMenuItem(); urlTrackFetch1.setText("PipeTrackTest.gpx"); urlTrackFetch1.setActionCommand("http://worldwind.arc.nasa.gov/java/apps/SARApp/PipeTrackTest.xml"); urlTrackFetch1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newTrackFromURL(e.getActionCommand(), null); } }); fileMenu.add(urlTrackFetch1); //---- urlTrackFetch2 ---- JMenuItem urlTrackFetch2 = new JMenuItem(); urlTrackFetch2.setText("PipeTracks2.gpx"); urlTrackFetch2.setActionCommand("http://worldwind.arc.nasa.gov/java/apps/SARApp/PipeTracks2.xml"); urlTrackFetch2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newTrackFromURL(e.getActionCommand(), null); } }); fileMenu.add(urlTrackFetch2); //---- urlTrackFetch3 ---- JMenuItem urlTrackFetch3 = new JMenuItem(); urlTrackFetch3.setText("PipeTracks3.gpx"); urlTrackFetch3.setActionCommand("http://worldwind.arc.nasa.gov/java/apps/SARApp/PipeTracks3.xml"); urlTrackFetch3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newTrackFromURL(e.getActionCommand(), null); } }); fileMenu.add(urlTrackFetch3); if (!Configuration.isMacOS()) { //-------- fileMenu.addSeparator(); JMenuItem exit = new JMenuItem(); exit.setText("Exit"); exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { exit(); } }); fileMenu.add(exit); } else { try { OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("exit", (Class[]) null)); } catch (Exception e) { e.printStackTrace(); } } } menuBar.add(fileMenu); //======== "View" ======== JMenu viewMenu = new JMenu(); { viewMenu.setText("Units"); viewMenu.setMnemonic('V'); //---- "Meters" ---- metersMenuItem = new JCheckBoxMenuItem(); metersMenuItem.setText("Meters"); metersMenuItem.setActionCommand(UNIT_METRIC); metersMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setElevationUnit(e.getActionCommand()); } }); viewMenu.add(metersMenuItem); //---- "Feet" ---- feetMenuItem = new JCheckBoxMenuItem(); feetMenuItem.setText("Feet"); feetMenuItem.setActionCommand(UNIT_IMPERIAL); feetMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setElevationUnit(e.getActionCommand()); } }); viewMenu.add(feetMenuItem); ButtonGroup unitGroup = new ButtonGroup(); unitGroup.add(metersMenuItem); unitGroup.add(feetMenuItem); } menuBar.add(viewMenu); //======== "Annotation" ======== JMenu annotationMenu = new JMenu(); { annotationMenu.setText("Annotation"); annotationMenu.setMnemonic('A'); //---- "New Annotation..." ---- JMenuItem newAnnotation = new JMenuItem(); newAnnotation.setText("New Annotation..."); newAnnotation.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newAnnotation(); } }); annotationMenu.add(newAnnotation); //---- "Remove Annotation" ---- JMenuItem removeAnnotation = new JMenuItem(); removeAnnotation.setText("Remove Annotation"); removeAnnotation.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { removeAnnotation(getCurrentAnnotation()); } }); annotationMenu.add(removeAnnotation); //---- "Show Annotations" ---- JCheckBoxMenuItem showAnnotations = new JCheckBoxMenuItem(); showAnnotations.setText("Show Annotations"); showAnnotations.setSelected(true); showAnnotations.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { setAnnotationsEnabled(e.getStateChange() == ItemEvent.SELECTED); } }); annotationMenu.add(showAnnotations); } menuBar.add(annotationMenu); //======== "Layers" ======== layerMenu = new LayerMenu(); { layerMenu.setMnemonic('L'); } menuBar.add(layerMenu); //======== "Help" ======== JMenu helpMenu = new JMenu(); { helpMenu.setText("Help"); helpMenu.setMnemonic('H'); //---- "Search and Rescue Help" ---- JMenuItem sarHelp = new JMenuItem(); sarHelp.setText("Search and Rescue Help"); sarHelp.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0)); sarHelp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showHelp(); } }); helpMenu.add(sarHelp); //---- "About [World Wind Search and Rescue Prototype]" ---- if (!Configuration.isMacOS()) { JMenuItem about = new JMenuItem(); about.setText("About"); about.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { showAbout(); } }); helpMenu.add(about); } else { try { OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod("showAbout", (Class[]) null)); } catch (Exception e) { e.printStackTrace(); } } } menuBar.add(helpMenu); } setJMenuBar(menuBar); pack(); centerWindowInDesktop(this); } }