package gov.nasa.worldwind.applications.sar; import gov.nasa.worldwind.WorldWindow; import gov.nasa.worldwind.avlist.AVKey; import gov.nasa.worldwind.event.SelectEvent; import gov.nasa.worldwind.event.SelectListener; import gov.nasa.worldwind.geom.Position; import gov.nasa.worldwind.geom.Vec4; import gov.nasa.worldwind.globes.Globe; import gov.nasa.worldwind.layers.AnnotationLayer; import gov.nasa.worldwind.pick.PickedObject; import gov.nasa.worldwind.pick.PickedObjectList; import gov.nasa.worldwind.render.*; import gov.nasa.worldwind.view.OrbitView; import javax.swing.*; import java.awt.*; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; /** * Handles SAR annotations * @author Patrick Murris * @version $Id: SARAnnotationSupport.java 4919 2008-04-04 17:32:58Z dcollins $ */ public class SARAnnotationSupport { private AnnotationLayer annotationLayer; private AnnotationAttributes defaults; private WorldWindow wwd; private SARAnnotation lastPickedObject; private SARAnnotation currentAnnotation; private ScreenAnnotation helpMessageAnnotation; private Color savedBorderColor; public SARAnnotationSupport() { this.annotationLayer = new AnnotationLayer(); this.helpMessageAnnotation = new ScreenAnnotation("", new Point(0, 0)); this.helpMessageAnnotation.getAttributes().setFrameShape(FrameFactory.SHAPE_NONE); this.helpMessageAnnotation.getAttributes().setInsets(new Insets(0, 0, 0, 0)); this.helpMessageAnnotation.getAttributes().setDrawOffset(new Point(0, -20)); this.helpMessageAnnotation.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); this.helpMessageAnnotation.getAttributes().setEffect(MultiLineTextRenderer.EFFECT_OUTLINE); this.helpMessageAnnotation.getAttributes().setFont(Font.decode("Arial-Bold-14")); this.helpMessageAnnotation.getAttributes().setTextColor(Color.YELLOW); this.helpMessageAnnotation.getAttributes().setSize(new Dimension(220, 0)); this.annotationLayer.addAnnotation(this.helpMessageAnnotation); this.defaults = new AnnotationAttributes(); this.defaults.setHighlightScale(1.1); } /** * Set the WorldWindow reference. Adds an annotation layer and a SelectListener to WW. * @param wwd the WorldWindow reference. */ public void setWwd(WorldWindow wwd) { this.wwd = wwd; // Add annotation layer this.wwd.getModel().getLayers().add(this.annotationLayer); // Add a select listener to select or highlight annotations on rollover this.wwd.addSelectListener(new SelectListener() { private BasicDragger2 dragger = new BasicDragger2(SARAnnotationSupport.this.wwd); public void selected(SelectEvent event) { // Select/unselect on left click on annotations if (event.getEventAction().equals(SelectEvent.LEFT_CLICK)) { if (event.hasObjects()) { if (event.getTopObject() instanceof Annotation) { // Check for text or url PickedObject po = event.getTopPickedObject(); if(po.getValue(AVKey.TEXT) != null) { //System.out.println("Text: \"" + po.getValue(AVKey.TEXT) + "\" Hyperlink: " + po.getValue(AVKey.URL)); //if(SARAnnotationSupport.this.currentAnnotation == event.getTopObject()) // return; } // Left click on an annotation - select select(event.getTopObject()); } } } // Edit annotation on double click else if (event.getEventAction().equals(SelectEvent.LEFT_DOUBLE_CLICK)) { if (event.hasObjects()) { if (event.getTopObject() instanceof Annotation) { edit((SARAnnotation)event.getTopObject()); } } } // Highlight on rollover else if (event.getEventAction().equals(SelectEvent.ROLLOVER) && !this.dragger.isDragging()) { highlight(event.getTopObject()); } // Have drag events drag the selected object. else if (event.getEventAction().equals(SelectEvent.DRAG_END) || event.getEventAction().equals(SelectEvent.DRAG)) { if (event.hasObjects()) { // If selected annotation delegate dragging computations to a dragger. if(event.getTopObject() == SARAnnotationSupport.this.currentAnnotation) { this.dragger.selected(event); // Update help text when dragging updateHelpMessage(SARAnnotationSupport.this.currentAnnotation); // Mark the owner track as dirty. if (SARAnnotationSupport.this.currentAnnotation.getOwner() != null) SARAnnotationSupport.this.currentAnnotation.getOwner().markDirty(); } } // We missed any roll-over events while dragging, so highlight any under the cursor now, // or de-highlight the dragged shape if it's no longer under the cursor. if (event.getEventAction().equals(SelectEvent.DRAG_END)) { PickedObjectList pol = SARAnnotationSupport.this.wwd.getObjectsAtCurrentPosition(); if (pol != null) { highlight(pol.getTopObject()); SARAnnotationSupport.this.wwd.redraw(); } } } } }); // Add a mouse listener to deselect annotation on click anywhere - including terrain this.wwd.getInputHandler().addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent mouseEvent) { if (currentAnnotation != null && mouseEvent.getButton() == MouseEvent.BUTTON1) { select(null); mouseEvent.consume(); getWwd().redraw(); } } }); } public WorldWindow getWwd() { return this.wwd; } private void select(Object o) { if(this.currentAnnotation != null) { // Unselect current //this.currentAnnotation.getAttributes().setHighlighted(false); this.currentAnnotation.getAttributes().setBorderColor(SARAnnotationSupport.this.savedBorderColor); } if(o != null && o instanceof SARAnnotation && this.currentAnnotation != o) { // Select new one if not current one already this.currentAnnotation = (SARAnnotation)o; //this.currentAnnotation.getAttributes().setHighlighted(true); this.savedBorderColor = this.currentAnnotation.getAttributes().getBorderColor(); this.currentAnnotation.getAttributes().setBorderColor(Color.YELLOW); } else { // Clear current annotation this.currentAnnotation = null; // switch off } } private void highlight(Object o) { // Manage highlighting of Annotations. if (this.lastPickedObject == o) return; // same thing selected // Turn off highlight if on. if (this.lastPickedObject != null) // && this.lastPickedObject != this.currentAnnotation) { this.lastPickedObject.getAttributes().setHighlighted(false); this.lastPickedObject = null; updateHelpMessage(null); // Clear help text } // Turn on highlight if object selected. if (o != null && o instanceof SARAnnotation) { this.lastPickedObject = (SARAnnotation) o; this.lastPickedObject.getAttributes().setHighlighted(true); updateHelpMessage(this.lastPickedObject); // Update help text } } private void updateHelpMessage(SARAnnotation annotation) { if (annotation != null) { Position pos = annotation.getPosition(); this.helpMessageAnnotation.getAttributes().setVisible(true); this.helpMessageAnnotation.setText(String.format("Lat %7.4f\u00B0 Lon %7.4f\u00B0", pos.getLatitude().degrees, pos.getLongitude().degrees)); // set help message screen position - follow annotation Vec4 surfacePoint = this.getWwd().getSceneController().getTerrain().getSurfacePoint( pos.getLatitude(), pos.getLongitude()); if (surfacePoint == null) { Globe globe = this.getWwd().getModel().getGlobe(); surfacePoint = globe.computePointFromPosition(pos.getLatitude(), pos.getLongitude(), globe.getElevation(pos.getLatitude(), pos.getLongitude())); } Vec4 screenPoint = this.getWwd().getView().project(surfacePoint); if (screenPoint != null) this.helpMessageAnnotation.setScreenPoint(new Point((int)screenPoint.x, (int)screenPoint.y)); } else { this.helpMessageAnnotation.getAttributes().setVisible(false); } } /** * Add a new annotation in the screen center. * @param text the <code>Annotation</code> text. * @param owner if not null, the SARTrack to add the annotation to. * annotation's border and text will be colored according to the owner SARTrack. */ public void addNew(String text, SARTrack owner) { if (text == null) text = showAnnotationDialog("Add New Annotation", null); OrbitView view = (OrbitView)this.wwd.getView(); if(text != null && text.length() > 0 && view != null) { Position centerPosition = new Position(view.getCenterPosition().getLatLon(), 0); SARAnnotation annotation = new SARAnnotation(text, centerPosition); addNew(annotation, owner); select(annotation); } } public void addNew(SARAnnotation annotation, SARTrack owner) { if (annotation != null) { annotation.getAttributes().setDefaults(this.defaults); // Reduce annotation distance scaling effect annotation.getAttributes().setDistanceMinScale(0.7); annotation.getAttributes().setDistanceMaxScale(1.3); if (owner != null) { annotation.setOwner(owner); annotation.getAttributes().setTextColor(owner.getColor()); //annotation.getAttributes().setBorderColor(color); } add(annotation); } } /** * Multi line input dialog. Returns the input text or null if canceled. * @param title the dialog window title. * @param text the initial text. * @return the input text or null if the dialog was canceled. */ private String showAnnotationDialog(String title, String text) { final JTextArea textArea = new JTextArea(5, 10); if (text != null) textArea.setText(text); // Focus to text area from http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6420212 textArea.addHierarchyListener(new HierarchyListener() { public void hierarchyChanged(HierarchyEvent he) { if ((he.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (textArea.isShowing()) { SwingUtilities.invokeLater(new Runnable() { public void run() { textArea.requestFocus(); } }); } } } }); int dialogResult = -1; JScrollPane pane = new JScrollPane(textArea); if (text != null && text.length() > 0) { Object[] options = {"Save", "Delete", "Cancel"}; dialogResult = JOptionPane.showOptionDialog(null, new Object[] {"Enter text", pane}, title, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); } else { dialogResult = JOptionPane.showOptionDialog(null, new Object[] {"Enter text", pane}, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); } String newText = null; if (dialogResult == JOptionPane.OK_OPTION || dialogResult == JOptionPane.YES_OPTION) newText = textArea.getText(); if (dialogResult == JOptionPane.NO_OPTION) newText = ""; return newText; } /** * Add an annotation. * @param annotation the annotation to add. */ public void add(SARAnnotation annotation) { if(annotation != null) { annotationLayer.addAnnotation(annotation); // Mark the SARTrack as dirty. if (annotation.getOwner() != null) annotation.getOwner().markDirty(); } } /** * Edit an annotation. * @param annotation the Annotation to be edited. */ public void edit(SARAnnotation annotation) { if(annotation != null) { String text = showAnnotationDialog("Edit Annotation", annotation.getText()); if(text != null) { if (text.length() > 0) { annotation.setText(text); // Mark the owner track as dirty. if (annotation.getOwner() != null) annotation.getOwner().markDirty(); } else { // The remove operation will mark the // owner track as tirty. this.remove(annotation); } } this.wwd.redraw(); } } /** * Remove an annotation. * @param annotation the annotation to be removed. */ public void remove(SARAnnotation annotation) { if (annotation != null) { annotationLayer.removeAnnotation(annotation); if (currentAnnotation == annotation) currentAnnotation = null; // Mark the SARTrack as dirty. if (annotation.getOwner() != null) annotation.getOwner().markDirty(); } } public void removeAnnotationsForTrack(SARTrack owner) { for (SARAnnotation sa : getAnnotationsForTrack(owner)) { if (sa != null) remove(sa); } } /** * Get current annotation. * @return current annotation. */ public SARAnnotation getCurrent() { return currentAnnotation; } /** * Get the annotation collection from the RenderableLayer * @return an Annotation collection. */ public Iterable<Annotation> getAnnotations() { return annotationLayer.getAnnotations(); } public Iterable<SARAnnotation> getAnnotationsForTrack(SARTrack owner) { java.util.ArrayList<SARAnnotation> result = new java.util.ArrayList<SARAnnotation>(); for (Annotation a : this.annotationLayer.getAnnotations()) { if (a != null && a instanceof SARAnnotation) { if (owner == ((SARAnnotation) a).getOwner()) result.add((SARAnnotation) a); } } return result; } /** * Set annotations enabled state * @param state true if annotations should be enabled. */ public void setEnabled(boolean state) { annotationLayer.setEnabled(state); } /** * Get the annotations enabled state. * @return true if annotations are enabled. */ public boolean getEnabled() { return annotationLayer.isEnabled(); } /** * Get the default attribute set used for all annotations. * @return the default attribute set used for all annotations. */ public AnnotationAttributes getDefaults() { return this.defaults; } public void writeAnnotations(String filePath, SARTrack trackOwner) throws IOException { try { if (filePath != null) { SARAnnotationWriter writer = new SARAnnotationWriter(filePath); writer.writeAnnotations(getAnnotationsForTrack(trackOwner)); writer.close(); } } catch (javax.xml.parsers.ParserConfigurationException e) { throw new IllegalArgumentException(e); } catch (javax.xml.transform.TransformerException e) { throw new IllegalArgumentException(e); } } public void readAnnotations(String filePath, SARTrack trackOwner) throws IOException { try { if (filePath != null) { SARAnnotationReader reader = new SARAnnotationReader(); reader.readFile(filePath); for (SARAnnotation sa : reader.getSARAnnotations()) addNew(sa, trackOwner); } } catch (javax.xml.parsers.ParserConfigurationException e) { throw new IllegalArgumentException(e); } catch (org.xml.sax.SAXException e) { throw new IllegalArgumentException(e); } } }