/* Copyright (C) 2001, 2006, 2007 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.examples; import gov.nasa.worldwind.layers.*; import gov.nasa.worldwind.geom.*; import gov.nasa.worldwind.render.*; import gov.nasa.worldwind.event.SelectListener; import gov.nasa.worldwind.event.SelectEvent; import gov.nasa.worldwind.View; import gov.nasa.worldwind.awt.WorldWindowGLCanvas; import gov.nasa.worldwind.pick.PickedObjectList; import gov.nasa.worldwind.pick.PickedObject; import gov.nasa.worldwind.avlist.AVKey; import javax.swing.*; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import javax.swing.border.CompoundBorder; import javax.swing.border.TitledBorder; import javax.media.opengl.GL; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.Iterator; import com.sun.opengl.util.j2d.TextRenderer; /** * @author Patrick Murris * @version $Id: Annotations.java 3541 2007-11-15 23:41:07Z patrickmurris $ */ public class Annotations extends ApplicationTemplate { private static class AppFrame extends ApplicationTemplate.AppFrame // implements MouseListener { private AnnotationLayer layer; private Annotation currentAnnotation; // Static private final static String IMAGE_WWJ_SPLASH = "images/400x230-splash-nww.png"; private final static String IMAGE_NASA = "images/32x32-icon-nasa.png"; private final static String IMAGE_EARTH = "images/32x32-icon-earth.png"; // UI components private JTextArea inputTextArea; private JCheckBox cbAdjustWidth; private JSlider widthSlider, heightSlider; private JSlider scaleSlider, opacitySlider, cornerRadiusSlider, borderWidthSlider, stippleFactorSlider; private JComboBox cbFontName, cbFontStyle, cbFontSize, cbTextAlign, cbShape, cbLeader; private JComboBox cbImage, cbImageRepeat, cbAntialias, cbTextEffect; private JSlider imageOpacitySlider, imageScaleSlider, imageOffsetXSlider, imageOffsetYSlider; private JSlider offsetXSlider, offsetYSlider; private JSlider distanceMinScaleSlider, distanceMaxScaleSlider, distanceMinOpacitySlider; private JSlider highlightScaleSlider; private JSpinner insetsTop, insetsRight, insetsBottom, insetsLeft; private JButton btTextColor, btBackColor, btBorderColor; private JComboBox cbTextColorAlpha, cbBackColorAlpha, cbBorderColorAlpha; private JButton btAdd, btApply, btRemove; private boolean suspendUpdate = false; private Color savedBorderColor; private BufferedImage savedImage; private Annotation lastPickedObject; public AppFrame() { // Create a renderable layer with one stand alone annotations RenderableLayer rl = new RenderableLayer(); rl.addRenderable(new GlobeAnnotation("Lat-Lon zero", Position.fromDegrees(0, 0, 0))); insertBeforeCompass(this.getWwd(), rl); // Create an AnnotationLayer with lots of annotations this.layer = new AnnotationLayer(); GlobeAnnotation ga; // Add some annotations to the layer // NOTE: use unicode for annotation text // Towns layer.addAnnotation(new GlobeAnnotation("MONACO", Position.fromDegrees(43.7340, 7.4211, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("NICE", Position.fromDegrees(43.696, 7.27, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("ANTIBES", Position.fromDegrees(43.5810, 7.1248, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("CANNES", Position.fromDegrees(43.5536, 7.0171, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("GRASSE", Position.fromDegrees(43.6590, 6.9240, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("FREJUS", Position.fromDegrees(43.4326, 6.7356, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("SAINTE MAXIME", Position.fromDegrees(43.3087, 6.6353, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("SAINT TROPEZ", Position.fromDegrees(43.2710, 6.6386, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("TOULON", Position.fromDegrees(43.1264, 5.9126, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("MARSEILLE", Position.fromDegrees(43.2904, 5.3806, 0), Font.decode("Arial-BOLD-12"))); layer.addAnnotation(new GlobeAnnotation("AIX EN PROVENCE", Position.fromDegrees(43.5286, 5.4485, 0), Font.decode("Arial-BOLD-12"))); // Special places layer.addAnnotation(new GlobeAnnotation("A\u00e9roport International\nNice C\u00f4te d'Azur", Position.fromDegrees(43.6582, 7.2167, 0), Font.decode("Arial-BOLDITALIC-10"), Color.YELLOW)); layer.addAnnotation(new GlobeAnnotation("Sophia Antipolis", Position.fromDegrees(43.6222, 7.0474, 0), Font.decode("Arial-BOLDITALIC-10"), Color.YELLOW)); // Geographical features - use a common default AnnotationAttributes object AnnotationAttributes geoAttr = new AnnotationAttributes(); geoAttr.setFrameShape(FrameFactory.SHAPE_NONE); // No frame geoAttr.setFont(Font.decode("Arial-ITALIC-10")); geoAttr.setTextColor(Color.GREEN); geoAttr.setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); geoAttr.setDrawOffset(new Point(0, 5)); // centered just above geoAttr.setEffect(MultiLineTextRenderer.EFFECT_OUTLINE); // Black outline geoAttr.setBackgroundColor(Color.BLACK); layer.addAnnotation(new GlobeAnnotation("Mont Chauve\nFort militaire\nAlt: 853m", Position.fromDegrees(43.7701, 7.2544, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Mont Agel\nFort militaire\nAlt: 1148m", Position.fromDegrees(43.7704, 7.4203, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Cap Ferrat", Position.fromDegrees(43.6820, 7.3290, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Gorges du Loup", Position.fromDegrees(43.7351, 6.9988, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Cap d'Antibes", Position.fromDegrees(43.5526, 7.1297, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Iles de L\u00e9rins", Position.fromDegrees(43.5125, 7.0467, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Montagne du Cheiron\nAlt: 1778m", Position.fromDegrees(43.8149, 6.9669, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Giens", Position.fromDegrees(43.0394, 6.1384, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Iles de Porquerolles", Position.fromDegrees(42.9974, 6.2147, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Ile du Levent", Position.fromDegrees(43.0315, 6.4702, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Ile de Port Cros", Position.fromDegrees(43.0045, 6.3959, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Montagne Sainte Victoire\nAlt: 1010m", Position.fromDegrees(43.5319, 5.6120, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Sainte Baume\nAlt: 1147m", Position.fromDegrees(43.3373, 5.8008, 0), geoAttr)); layer.addAnnotation(new GlobeAnnotation("Pic de l'Ours\nAlt: 496m", Position.fromDegrees(43.4763, 6.9042, 0), geoAttr)); // Water bodies - ellipse shape and centered text ga = new GlobeAnnotation("Lac de Sainte Croix", Position.fromDegrees(43.7720, 6.1879, 0), Font.decode("Arial-ITALIC-10"), Color.CYAN); ga.getAttributes().setFrameShape(FrameFactory.SHAPE_ELLIPSE); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); ga.getAttributes().setInsets(new Insets(8, 12, 9, 12)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Lac de Castillon", Position.fromDegrees(43.9008, 6.5348, 0), Font.decode("Arial-ITALIC-10"), Color.CYAN); ga.getAttributes().setFrameShape(FrameFactory.SHAPE_ELLIPSE); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); ga.getAttributes().setInsets(new Insets(8, 12, 9, 12)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Lac de Serre Pon\u00e7on", Position.fromDegrees(44.5081, 6.3242, 0), Font.decode("Arial-ITALIC-10"), Color.CYAN); ga.getAttributes().setFrameShape(FrameFactory.SHAPE_ELLIPSE); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); ga.getAttributes().setInsets(new Insets(8, 12, 9, 12)); layer.addAnnotation(ga); // Longer text, custom colors and text align ga = new GlobeAnnotation("Transition Permien-Trias\nDate: 251Ma \nPlus grand \u00e9pisode d'extinction massive.", Position.fromDegrees(44.0551, 7.1215, 0), Font.decode("Arial-ITALIC-12"), Color.DARK_GRAY); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_RIGHT); ga.getAttributes().setBackgroundColor(new Color(.8f, .8f, .8f, .7f)); ga.getAttributes().setBorderColor(Color.BLACK); layer.addAnnotation(ga); // With HTML tags and background image no repeat ga = new GlobeAnnotation("<p>\n<b><font color=\"#664400\">LA CLAPI\u00c8RE</font></b><br />\n<i>Alt: 1100-1700m</i>\n</p>\n<p>\n<b>Glissement de terrain majeur</b> dans la haute Tin\u00e9e, sur un flanc du <a href=\"http://www.mercantour.eu\">Parc du Mercantour</a>, Alpes Maritimes.\n</p>\n<p>\nRisque aggrav\u00e9 d'<b>inondation</b> du village de <i>Saint \u00c9tienne de Tin\u00e9e</i> juste en amont.\n</p>", Position.fromDegrees(44.2522, 6.9424, 0), Font.decode("Serif-PLAIN-14"), Color.DARK_GRAY); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_RIGHT); ga.getAttributes().setBackgroundColor(new Color(1f, 1f, 1f, .7f)); ga.getAttributes().setBorderColor(Color.BLACK); ga.getAttributes().setSize(new Dimension(220, 0)); // Preferred max width, no length limit (default max width is 160) ga.getAttributes().setImageSource(IMAGE_EARTH); ga.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_NONE); ga.getAttributes().setImageOpacity(.6); ga.getAttributes().setImageScale(.7); ga.getAttributes().setImageOffset(new Point(7, 7)); layer.addAnnotation(ga); // With some border stippling, width and antialias ga = new GlobeAnnotation("Latitude: 44.0 N\nLongitude: 7.0 W", Position.fromDegrees(44.0000, 7.000, 0), Font.decode("Arial-ITALIC-12"), Color.DARK_GRAY); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); ga.getAttributes().setBackgroundColor(new Color(.9f, .9f, .8f, .7f)); ga.getAttributes().setBorderColor(Color.BLACK); ga.getAttributes().setBorderWidth(2); ga.getAttributes().setBorderStippleFactor(3); ga.getAttributes().setAntiAliasHint(Annotation.ANTIALIAS_NICEST); layer.addAnnotation(ga); // With background texture repeat Y ga = new GlobeAnnotation("SAHARA DESERT\n\nThe Sahara is technically the world's second largest desert after Antarctica.\n\nAt over 9,000,000 square kilometres (3,500,000 sq mi), it covers most parts of northern Africa. ", Position.fromDegrees(22, 12, 0), Font.decode("Arial-BOLD-12")); ga.getAttributes().setImageSource(IMAGE_NASA); ga.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_Y); ga.getAttributes().setImageOpacity(.6); ga.getAttributes().setImageScale(.7); ga.getAttributes().setImageOffset(new Point(1, 1)); ga.getAttributes().setInsets(new Insets(6, 28, 6, 6)); layer.addAnnotation(ga); // Splash screen with NPOT background texture ga = new GlobeAnnotation("Java SDK", Position.fromDegrees(20, 00, 0), Font.decode("Arial-BOLD-14")); ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_RIGHT); ga.getAttributes().setImageSource(IMAGE_WWJ_SPLASH); ga.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_NONE); ga.getAttributes().setImageOpacity(.7); ga.getAttributes().setImageScale(.5); // scale texture to half size ga.getAttributes().setSize(new Dimension(200, 115)); // use this dimensions (half texture) ga.getAttributes().setAdjustWidthToText(Annotation.SIZE_FIXED); // use strict dimension - dont follow text width layer.addAnnotation(ga); // With background pattern and forced height ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(10, 100, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.GRADIENT_VLINEAR, new Dimension(32, 128), 1f, Color.WHITE, new Color(0f, 0f, 0f, 0f))); // White to transparent ga.getAttributes().setSize(new Dimension(200, 128)); // force height to 128 layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(10, 110, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.GRADIENT_VLINEAR, new Dimension(32, 64), 1f, Color.LIGHT_GRAY, Color.WHITE)); // gray/white ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(10, 120, 0), Font.decode("Arial-BOLD-16"), Color.BLACK); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.PATTERN_DIAGONAL_UP, Color.YELLOW)); // yellow stripes ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(0, 100, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.GRADIENT_HLINEAR, new Dimension(256, 32), 1f, Color.WHITE, new Color(0f, 0f, 0f, 0f))); // White to transparent ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(0, 110, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.GRADIENT_HLINEAR, new Dimension(32, 64), 1f, Color.LIGHT_GRAY, Color.WHITE)); // gray/white ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(0, 120, 0), Font.decode("Arial-BOLD-16"), Color.BLACK); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.PATTERN_SQUARES, Color.YELLOW)); // yellow circles ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(-10, 100, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.GRADIENT_HLINEAR, new Dimension(16, 16), 1f, Color.BLACK, Color.WHITE)); // Black to white ga.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_Y); ga.getAttributes().setBackgroundColor(Color.WHITE); ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(-10, 110, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.GRADIENT_VLINEAR, new Dimension(16, 16), 1f, Color.BLACK, Color.WHITE)); // Black to white ga.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_X); ga.getAttributes().setBackgroundColor(Color.WHITE); ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); ga = new GlobeAnnotation("Background patterns...", Position.fromDegrees(-10, 120, 0), Font.decode("Arial-BOLD-16"), Color.BLACK); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.PATTERN_HVLINE, .15f, Color.GREEN)); // green + lines ga.getAttributes().setImageScale(.4); ga.getAttributes().setSize(new Dimension(200, 128)); layer.addAnnotation(ga); // Shows pattern scale effect on circles pattern for (int i = 1; i <= 10; i++) { ga = new GlobeAnnotation("Pattern scale:" + (float)i / 10, Position.fromDegrees(-20, 97 + i * 3, 0), Font.decode("Arial-BOLD-16"), Color.GRAY); ga.getAttributes().setImageSource(PatternFactory.createPattern(PatternFactory.PATTERN_CIRCLES, (float)i / 10, Color.LIGHT_GRAY)); ga.getAttributes().setImageScale(.4); ga.getAttributes().setSize(new Dimension(160, 60)); layer.addAnnotation(ga); } // Using a GlobeAnnotation subclass to override drawing class SimpleGlobeAnnotation extends GlobeAnnotation { TextRenderer textRenderer; Font font = Font.decode("Arial-PLAIN-12"); public SimpleGlobeAnnotation(String text, Position position) { super(text, position); } public void drawAnnotation(DrawContext dc, Point screenPoint, double drawScale, double drawAlpha, Position pickPosition) { if(this.textRenderer == null) this.textRenderer = getTextRenderer(dc, this.font); // Draw text centered just above the screen point - use annotation's colors String text = getText().split("\n")[0]; // First line only int textWidth = (int)this.textRenderer.getBounds(text).getWidth(); Color textColor = getAttributes().getTextColor(); Color backColor = getAttributes().getBackgroundColor(); this.textRenderer.begin3DRendering(); this.textRenderer.setColor(backColor); this.textRenderer.draw(text, - textWidth / 2 + 1, 12 - 1); // Background 'shadow' this.textRenderer.setColor(textColor); this.textRenderer.draw(text, - textWidth / 2, 12); // Foreground text this.textRenderer.end3DRendering(); // Draw little square around screen point - use annotation's color Color borderColor = getAttributes().getBorderColor(); setDrawColor(dc, borderColor); // Draw 3x3 shape from its bottom left corner dc.getGL().glTranslated(-1, -1, 0); FrameFactory.drawShape(dc, FrameFactory.SHAPE_RECTANGLE, 3, 3, GL.GL_LINE_STRIP, 0); } } ga = new SimpleGlobeAnnotation("Mount Rainier\nAlt: 4392m", Position.fromDegrees(46.8534, -121.7609, 0)); layer.addAnnotation(ga); ga = new SimpleGlobeAnnotation("Mount Adams\nAlt: 3742m", Position.fromDegrees(46.2018, -121.4931, 0)); layer.addAnnotation(ga); ga = new SimpleGlobeAnnotation("Mount Saint Helens\nAlt: 4392m", Position.fromDegrees(46.1991, -122.1882, 0)); layer.addAnnotation(ga); // Using an anonymous subclass to change annotation text on the fly ga = new GlobeAnnotation("DRAG ME!", Position.fromDegrees(42, -118, 0), Font.decode("Arial-BOLD-18")) { public void drawAnnotation(DrawContext dc, Point screenPoint, double drawScale, double drawAlpha, Position pickPosition) { // if annotation has moved, set its text if(getPosition().getLatitude().degrees != 42 || getPosition().getLongitude().degrees != -118) setText(String.format("Lat %7.4f\u00B0\nLon %7.4f\u00B0", getPosition().getLatitude().degrees, getPosition().getLongitude().degrees)); // Keep rendering super.drawAnnotation(dc, screenPoint, drawScale, drawAlpha, pickPosition); } }; layer.addAnnotation(ga); // Using post drawing code in an anonymous subclass ga = new GlobeAnnotation("Annotation with extra frames drawn by a render delegate.", Position.fromDegrees(40, -116, 0), Font.decode("Serif-BOLD-18"), Color.DARK_GRAY) { public void drawAnnotation(DrawContext dc, Point screenPoint, double drawScale, double drawAlpha, Position pickPosition) { // Let normal rendering happen super.drawAnnotation(dc, screenPoint, drawScale, drawAlpha, pickPosition); // Draw second light gray frame outside draw rectangle // Refers to scaleFactor, alphaFactor, drawRectangle and freeRectangle which have been // set during drawing. setDrawColor(dc, new Color(0f, 0f, 0f, .5f * (float)alphaFactor)); dc.getGL().glLineWidth((float)(scaleFactor)); // Translate to draw area bottom left corner, 2 pixels outside dc.getGL().glTranslated(-screenPoint.x + drawRectangle.x - 3, -screenPoint.y + drawRectangle.y - 3, 0); FrameFactory.drawShape(dc, FrameFactory.SHAPE_RECTANGLE, drawRectangle.width + 6, drawRectangle.height + 6, GL.GL_LINE_STRIP, 4); // Draw another frame in the free space if any if(freeRectangle.height > 0) { dc.getGL().glTranslated(+3, +3, 0); FrameFactory.drawShape(dc, FrameFactory.SHAPE_ELLIPSE, freeRectangle.width, freeRectangle.height, GL.GL_TRIANGLE_FAN, 0); } } }; ga.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); ga.getAttributes().setBackgroundColor(new Color(1f, 1f, 1f, .7f)); ga.getAttributes().setBorderColor(Color.BLACK); ga.getAttributes().setSize(new Dimension(160, 200)); layer.addAnnotation(ga); // Using a ScreenAnnotation ScreenAnnotation sa = new ScreenAnnotation("Fixed position annotation", new Point(20, 20)); sa.getAttributes().setCornerRadius(0); sa.getAttributes().setSize(new Dimension(200, 0)); sa.getAttributes().setAdjustWidthToText(Annotation.SIZE_FIXED); // use strict dimension width - 200 sa.getAttributes().setDrawOffset(new Point(100, 0)); // screen point is annotation bottom left corner sa.getAttributes().setHighlightScale(1); // No highlighting either layer.addAnnotation(sa); // Add layer to the layer list and update the layer panel insertBeforeCompass(this.getWwd(), layer); getLayerPanel().update(getWwd()); // Add control panel this.getLayerPanel().add(makeControlPanel(), BorderLayout.SOUTH); // Test save as kml placemarks //save(); // Add a select listener to select or highlight annotations on rollover this.getWwd().addSelectListener(new SelectListener() { private BasicDragger dragger = new BasicDragger(getWwd()); 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(AppFrame.this.currentAnnotation == event.getTopObject()) return; } // Left click on an annotation - select if(AppFrame.this.currentAnnotation != null) { // Unselect current //AppFrame.this.currentAnnotation.getAttributes().setHighlighted(false); AppFrame.this.currentAnnotation.getAttributes().setBorderColor(AppFrame.this.savedBorderColor); } if(AppFrame.this.currentAnnotation != event.getTopObject()) { // Select new one if not current one already AppFrame.this.currentAnnotation = (Annotation)event.getTopObject(); //AppFrame.this.currentAnnotation.getAttributes().setHighlighted(true); AppFrame.this.savedBorderColor = AppFrame.this.currentAnnotation.getAttributes().getBorderColor(); AppFrame.this.savedImage = AppFrame.this.currentAnnotation.getAttributes() .getImageSource() instanceof BufferedImage ? (BufferedImage)AppFrame.this.currentAnnotation.getAttributes().getImageSource() : null; AppFrame.this.currentAnnotation.getAttributes().setBorderColor(Color.YELLOW); } else { // Clear current annotation AppFrame.this.currentAnnotation = null; // switch off } // Update control panel AppFrame.this.updateControlPanel(); } else System.out.println("Left click on " + event.getTopObject()); } } // Highlight on rollover else if (event.getEventAction().equals(SelectEvent.ROLLOVER) && !this.dragger.isDragging()) { AppFrame.this.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() == AppFrame.this.currentAnnotation) this.dragger.selected(event); } // 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 = getWwd().getObjectsAtCurrentPosition(); if (pol != null) { AppFrame.this.highlight(pol.getTopObject()); AppFrame.this.getWwd().repaint(); } } } } }); } 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; } // Turn on highlight if object selected. if (o != null && o instanceof Annotation) { this.lastPickedObject = (Annotation) o; this.lastPickedObject.getAttributes().setHighlighted(true); } } // -- Control panel --------------------------------------------------------------- private JPanel makeControlPanel() { //-- Annotation text area. ---------------------------------------------- this.inputTextArea = new JTextArea(); this.inputTextArea.setFont(new Font("Sans_Serif", Font.PLAIN, 16)); this.inputTextArea.setLineWrap(true); this.inputTextArea.setWrapStyleWord(true); JScrollPane textScrollPane = new JScrollPane(this.inputTextArea); textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); textScrollPane.setPreferredSize(new Dimension(200, 100)); textScrollPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); //-- Width panel -------------------------------------------------------- JPanel sizePanel = new JPanel(new GridLayout(0, 1, 0, 0)); sizePanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Width and Height"))); this.widthSlider = new JSlider(JSlider.HORIZONTAL, 0, 800, 160); this.widthSlider.setMajorTickSpacing(100); this.widthSlider.setMinorTickSpacing(10); //this.widthSlider.setPaintTicks(true); this.widthSlider.setPaintLabels(true); this.widthSlider.setToolTipText("Preferred annotation width"); this.widthSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); sizePanel.add(this.widthSlider); this.heightSlider = new JSlider(JSlider.HORIZONTAL, 0, 800, 0); this.heightSlider.setMajorTickSpacing(100); this.heightSlider.setMinorTickSpacing(10); //this.widthSlider.setPaintTicks(true); this.heightSlider.setPaintLabels(true); this.heightSlider.setToolTipText("Preferred annotation height, zero = no limit"); this.heightSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); sizePanel.add(this.heightSlider); //-- Corner radius panel ---------------------------------------------------- JPanel cornerRadiusPanel = new JPanel(); cornerRadiusPanel.setLayout(new BoxLayout(cornerRadiusPanel, BoxLayout.X_AXIS)); cornerRadiusPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); cornerRadiusPanel.add(new JLabel("Corner radius:")); cornerRadiusPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.cornerRadiusSlider = new JSlider(JSlider.HORIZONTAL, 0, 50, 10); this.cornerRadiusSlider.setMajorTickSpacing(10); this.cornerRadiusSlider.setMinorTickSpacing(1); //this.cornerRadiusSlider.setPaintTicks(true); this.cornerRadiusSlider.setPaintLabels(true); this.cornerRadiusSlider.setToolTipText("Rounded corners radius"); this.cornerRadiusSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); cornerRadiusPanel.add(this.cornerRadiusSlider); //-- Insets panel ---------------------------------------------------- JPanel insetsPanel = new JPanel(); insetsPanel.setLayout(new BoxLayout(insetsPanel, BoxLayout.X_AXIS)); insetsPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); insetsPanel.add(new JLabel("Insets:")); insetsPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.insetsTop = new JSpinner(); this.insetsTop.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); this.insetsRight = new JSpinner(); this.insetsRight.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); this.insetsBottom = new JSpinner(); this.insetsBottom.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); this.insetsLeft = new JSpinner(); this.insetsLeft.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); insetsPanel.add(this.insetsTop); insetsPanel.add(Box.createRigidArea(new Dimension(10, 0))); insetsPanel.add(this.insetsRight); insetsPanel.add(Box.createRigidArea(new Dimension(10, 0))); insetsPanel.add(this.insetsBottom); insetsPanel.add(Box.createRigidArea(new Dimension(10, 0))); insetsPanel.add(this.insetsLeft); //-- Border width panel ---------------------------------------------------- JPanel borderWidthPanel = new JPanel(); borderWidthPanel.setLayout(new BoxLayout(borderWidthPanel, BoxLayout.X_AXIS)); borderWidthPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); borderWidthPanel.add(new JLabel("Border width:")); borderWidthPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.borderWidthSlider = new JSlider(JSlider.HORIZONTAL, 0, 50, 10); this.borderWidthSlider.setMajorTickSpacing(10); this.borderWidthSlider.setMinorTickSpacing(1); //this.borderWidthSlider.setPaintTicks(true); this.borderWidthSlider.setPaintLabels(true); this.borderWidthSlider.setToolTipText("Border width 1/10th"); this.borderWidthSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); borderWidthPanel.add(this.borderWidthSlider); //-- Stipple factor panel ---------------------------------------------------- JPanel stippleFactorPanel = new JPanel(); stippleFactorPanel.setLayout(new BoxLayout(stippleFactorPanel, BoxLayout.X_AXIS)); stippleFactorPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); stippleFactorPanel.add(new JLabel("Stipple factor:")); stippleFactorPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.stippleFactorSlider = new JSlider(JSlider.HORIZONTAL, 0, 10, 0); this.stippleFactorSlider.setMajorTickSpacing(1); this.stippleFactorSlider.setPaintLabels(true); this.stippleFactorSlider.setToolTipText("Border line pattern repeat factor"); this.stippleFactorSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { if (currentAnnotation != null) { updateAnnotation(); } } }); stippleFactorPanel.add(this.stippleFactorSlider); // -- Border antialias panel ----------------------------------------------- final JPanel antialiasPanel = new JPanel(new GridLayout(0, 2, 5, 5)); antialiasPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); antialiasPanel.add(new JLabel("Antialias:")); this.cbAntialias = new JComboBox(new String[] {"Dont care", "Fastest", "Nicest"}); this.cbAntialias.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); antialiasPanel.add(this.cbAntialias); //-- Scale and opacity panel ---------------------------------------------------- JPanel scalePanel = new JPanel(new GridLayout(0, 1, 0, 0)); scalePanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Scale and Opacity"))); this.scaleSlider = new JSlider(JSlider.HORIZONTAL, 0, 30, 10); this.scaleSlider.setMajorTickSpacing(10); this.scaleSlider.setMinorTickSpacing(1); //this.scaleSlider.setPaintTicks(true); this.scaleSlider.setPaintLabels(true); this.scaleSlider.setToolTipText("Annotation scaling"); this.scaleSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); scalePanel.add(this.scaleSlider); this.opacitySlider = new JSlider(JSlider.HORIZONTAL, 0, 10, 10); this.opacitySlider.setMajorTickSpacing(1); //this.opacitySlider.setMinorTickSpacing(1); //this.opacitySlider.setPaintTicks(true); this.opacitySlider.setPaintLabels(true); this.opacitySlider.setToolTipText("Annotation opacity"); this.opacitySlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); scalePanel.add(this.opacitySlider); // -- Font -------------------------------------------------------------- JPanel fontPanel = new JPanel(); fontPanel.setLayout(new BoxLayout(fontPanel, BoxLayout.X_AXIS)); fontPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); fontPanel.add(new JLabel("Font")); fontPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.cbFontName = new JComboBox(new String[] {"Arial", "SansSerif", "Serif", "Courier", "Times", "Helvetica", "Trebuchet", "Tahoma"}); this.cbFontName.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); fontPanel.add(this.cbFontName); fontPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.cbFontStyle = new JComboBox(new String[] {"Plain", "Bold", "Italic", "BoldItalic"}); this.cbFontStyle.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); fontPanel.add(this.cbFontStyle); fontPanel.add(Box.createRigidArea(new Dimension(10, 0))); this.cbFontSize = new JComboBox(new String[] {"10", "12", "14", "16", "18", "20", "24", "28", "34", "48", "64"}); this.cbFontSize.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); fontPanel.add(this.cbFontSize); //fontPanel.add(Box.createRigidArea(new Dimension(10, 0))); // -- Text align panel ------------------------------------------------------- final JPanel alignPanel = new JPanel(new GridLayout(0, 3, 5, 5)); alignPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); alignPanel.add(new JLabel("Align & Effect:")); this.cbTextAlign = new JComboBox(new String[] {"Left", "Center", "Right"}); this.cbTextAlign.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); alignPanel.add(this.cbTextAlign); this.cbTextEffect = new JComboBox(new String[] {"None", "Shadow", "Outline"}); this.cbTextEffect.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); alignPanel.add(this.cbTextEffect); // -- Adjust width panel ------------------------------------------------------- final JPanel adjustWidthPanel = new JPanel(new GridLayout(0, 1, 5, 5)); adjustWidthPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); this.cbAdjustWidth = new JCheckBox("Adjust width to text"); this.cbAdjustWidth.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); this.cbAdjustWidth.setSelected(true); adjustWidthPanel.add(this.cbAdjustWidth); // -- Shape and leader panel ----------------------------------------------- final JPanel shapePanel = new JPanel(new GridLayout(0, 2, 5, 5)); shapePanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); shapePanel.add(new JLabel("Shape:")); this.cbShape = new JComboBox(new String[] {"Rectangle", "Ellipse", "None"}); this.cbShape.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); shapePanel.add(this.cbShape); shapePanel.add(new JLabel("Leader:")); this.cbLeader = new JComboBox(new String[] {"Triangle", "None"}); this.cbLeader.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); shapePanel.add(this.cbLeader); // -- Image select panel ----------------------------------------------- final JPanel imagePanel = new JPanel(new GridLayout(0, 2, 5, 5)); imagePanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); imagePanel.add(new JLabel("Image:")); this.cbImage = new JComboBox(new String[] {"None", "Earth", "NASA", "WWJ Splash", "Custom"}); this.cbImage.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); imagePanel.add(this.cbImage); imagePanel.add(new JLabel("Repeat:")); this.cbImageRepeat = new JComboBox(new String[] {"None", "Repeat-X", "Repeat-Y", "Repeat-XY"}); this.cbImageRepeat.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); imagePanel.add(this.cbImageRepeat); //-- Image scale and opacity panel ---------------------------------------------- JPanel imageScalePanel = new JPanel(new GridLayout(0, 1, 0, 0)); imageScalePanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Scale and Opacity"))); this.imageScaleSlider = new JSlider(JSlider.HORIZONTAL, 0, 30, 10); this.imageScaleSlider.setMajorTickSpacing(10); this.imageScaleSlider.setMinorTickSpacing(1); this.imageScaleSlider.setPaintLabels(true); this.imageScaleSlider.setToolTipText("Background image scaling"); this.imageScaleSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); imageScalePanel.add(this.imageScaleSlider); this.imageOpacitySlider = new JSlider(JSlider.HORIZONTAL, 0, 10, 10); this.imageOpacitySlider.setMajorTickSpacing(1); this.imageOpacitySlider.setPaintLabels(true); this.imageOpacitySlider.setToolTipText("Background image opacity"); this.imageOpacitySlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); imageScalePanel.add(this.imageOpacitySlider); //-- Image offset panel ---------------------------------------------- JPanel imageOffsetPanel = new JPanel(new GridLayout(0, 1, 0, 0)); imageOffsetPanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Offset"))); this.imageOffsetXSlider = new JSlider(JSlider.HORIZONTAL, -200, 200, 0); this.imageOffsetXSlider.setMajorTickSpacing(100); this.imageOffsetXSlider.setMinorTickSpacing(1); this.imageOffsetXSlider.setPaintLabels(true); this.imageOffsetXSlider.setToolTipText("Background image horizontal offset (X)"); this.imageOffsetXSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); imageOffsetPanel.add(this.imageOffsetXSlider); this.imageOffsetYSlider = new JSlider(JSlider.HORIZONTAL, -200, 200, 0); this.imageOffsetYSlider.setMajorTickSpacing(100); this.imageOffsetXSlider.setMinorTickSpacing(1); this.imageOffsetYSlider.setPaintLabels(true); this.imageOffsetYSlider.setToolTipText("Background image vertical offset (Y)"); this.imageOffsetYSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); imageOffsetPanel.add(this.imageOffsetYSlider); //-- Annotation offset panel ---------------------------------------------- JPanel offsetPanel = new JPanel(new GridLayout(0, 1, 0, 0)); offsetPanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Draw offset"))); this.offsetXSlider = new JSlider(JSlider.HORIZONTAL, -200, 200, 0); this.offsetXSlider.setMajorTickSpacing(100); this.offsetXSlider.setMinorTickSpacing(1); this.offsetXSlider.setPaintLabels(true); this.offsetXSlider.setToolTipText("Annotation horizontal offset (X)"); this.offsetXSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); offsetPanel.add(this.offsetXSlider); this.offsetYSlider = new JSlider(JSlider.HORIZONTAL, 0, 200, 0); this.offsetYSlider.setMajorTickSpacing(100); this.offsetYSlider.setMinorTickSpacing(1); this.offsetYSlider.setPaintLabels(true); this.offsetYSlider.setToolTipText("Annotation vertical offset (Y)"); this.offsetYSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); offsetPanel.add(this.offsetYSlider); //-- Annotation distance scalling panel --------------------------------------- JPanel distancePanel = new JPanel(new GridLayout(0, 1, 0, 0)); distancePanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Distance min/max scale and opacity"))); this.distanceMinScaleSlider = new JSlider(JSlider.HORIZONTAL, 0, 30, 10); this.distanceMinScaleSlider.setMajorTickSpacing(10); this.distanceMinScaleSlider.setMinorTickSpacing(1); this.distanceMinScaleSlider.setPaintLabels(true); this.distanceMinScaleSlider.setToolTipText("Minimum scale 1/10th"); this.distanceMinScaleSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); distancePanel.add(this.distanceMinScaleSlider); this.distanceMaxScaleSlider = new JSlider(JSlider.HORIZONTAL, 0, 30, 10); this.distanceMaxScaleSlider.setMajorTickSpacing(10); this.distanceMaxScaleSlider.setMinorTickSpacing(1); this.distanceMaxScaleSlider.setPaintLabels(true); this.distanceMaxScaleSlider.setToolTipText("Maximum scale 1/10th"); this.distanceMaxScaleSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); distancePanel.add(this.distanceMaxScaleSlider); this.distanceMinOpacitySlider = new JSlider(JSlider.HORIZONTAL, 0, 10, 10); this.distanceMinOpacitySlider.setMajorTickSpacing(1); this.distanceMinOpacitySlider.setPaintLabels(true); this.distanceMinOpacitySlider.setToolTipText("Minimum opacity"); this.distanceMinOpacitySlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); distancePanel.add(this.distanceMinOpacitySlider); //-- Highlight scale panel --------------------------------------- JPanel highlightPanel = new JPanel(new GridLayout(0, 1, 0, 0)); highlightPanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4), new TitledBorder("Highlight scale"))); this.highlightScaleSlider = new JSlider(JSlider.HORIZONTAL, 0, 30, 10); this.highlightScaleSlider.setMajorTickSpacing(10); this.highlightScaleSlider.setMinorTickSpacing(1); this.highlightScaleSlider.setPaintLabels(true); this.highlightScaleSlider.setToolTipText("Highlight scale 1/10th"); this.highlightScaleSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent event) { JSlider s = (JSlider)event.getSource(); if (currentAnnotation != null) { updateAnnotation(); } } }); highlightPanel.add(this.highlightScaleSlider); // -- Color panel ------------------------------------------------------- final JPanel colorPanel = new JPanel(new GridLayout(0, 3, 5, 5)); colorPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); colorPanel.add(new JLabel("Text color:")); this.btTextColor = new JButton(""); this.btTextColor.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Color c = JColorChooser.showDialog(colorPanel, "Choose a color...", ((JButton)event.getSource()).getBackground()); if (c != null) { ((JButton)event.getSource()).setBackground(c); if(currentAnnotation != null) updateAnnotation(); } } }); colorPanel.add(this.btTextColor); this.cbTextColorAlpha = new JComboBox(new String[] {"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}); this.cbTextColorAlpha.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); colorPanel.add(this.cbTextColorAlpha); colorPanel.add(new JLabel("Back color:")); this.btBackColor = new JButton(""); this.btBackColor.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Color c = JColorChooser.showDialog(colorPanel, "Choose a color...", ((JButton)event.getSource()).getBackground()); if (c != null) { ((JButton)event.getSource()).setBackground(c); if(currentAnnotation != null) updateAnnotation(); } } }); colorPanel.add(this.btBackColor); this.cbBackColorAlpha = new JComboBox(new String[] {"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}); this.cbBackColorAlpha.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); colorPanel.add(this.cbBackColorAlpha); colorPanel.add(new JLabel("Border color:")); this.btBorderColor = new JButton(""); this.btBorderColor.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Color c = JColorChooser.showDialog(colorPanel, "Choose a color...", ((JButton)event.getSource()).getBackground()); if (c != null) { ((JButton)event.getSource()).setBackground(c); if(currentAnnotation != null) updateAnnotation(); } } }); colorPanel.add(this.btBorderColor); this.cbBorderColorAlpha = new JComboBox(new String[] {"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}); this.cbBorderColorAlpha.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { if(currentAnnotation != null) { updateAnnotation(); } } }); colorPanel.add(this.cbBorderColorAlpha); // -- Button panel ------------------------------------------------------ JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); buttonPanel.setBorder(BorderFactory.createEmptyBorder(14, 4, 4, 4)); // Apply changes button this.btApply = new JButton("Apply"); this.btApply.setEnabled(false); this.btApply.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if(currentAnnotation != null) { updateAnnotation(); } } }); // Add annotation button this.btAdd = new JButton("Add new"); this.btAdd.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Position lookAtPos = computeGroundPosition(getWwd()); if(lookAtPos != null && inputTextArea.getText().length() > 0) { Annotation a = currentAnnotation; currentAnnotation = new GlobeAnnotation(inputTextArea.getText(), lookAtPos); updateAnnotation(); layer.addAnnotation(currentAnnotation); currentAnnotation = a; getWwd().repaint(); } } }); // Remove button this.btRemove = new JButton("Remove"); this.btRemove.setEnabled(false); this.btRemove.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if(currentAnnotation != null) { layer.removeAnnotation(currentAnnotation); // Clear current annotation currentAnnotation = null; inputTextArea.setText(""); widthSlider.setEnabled(false); btApply.setEnabled(false); btRemove.setEnabled(false); getWwd().repaint(); } } }); buttonPanel.add(this.btAdd); buttonPanel.add(Box.createRigidArea(new Dimension(10, 0))); buttonPanel.add(this.btApply); buttonPanel.add(Box.createRigidArea(new Dimension(10, 0))); buttonPanel.add(this.btRemove); //-- end button panel //-- Tabbed pane assembly --------------- JTabbedPane tabbedPane = new JTabbedPane(); // Text and colors JPanel controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); controlPanel.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9)); controlPanel.setToolTipText("Text and Colors"); controlPanel.add(textScrollPane); controlPanel.add(fontPanel); controlPanel.add(alignPanel); controlPanel.add(colorPanel); tabbedPane.add(controlPanel); tabbedPane.setTitleAt(0, "Text"); // Size, scale and opacity controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); controlPanel.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9)); controlPanel.setToolTipText("Size, scale and opacity"); controlPanel.add(adjustWidthPanel); controlPanel.add(sizePanel); controlPanel.add(scalePanel); tabbedPane.add(controlPanel); tabbedPane.setTitleAt(1, "Size"); // Shape, insets, corner radius controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); controlPanel.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9)); controlPanel.setToolTipText("Shape, insets, corner radius, border width and patterns"); controlPanel.add(shapePanel); controlPanel.add(cornerRadiusPanel); controlPanel.add(insetsPanel); controlPanel.add(borderWidthPanel); controlPanel.add(stippleFactorPanel); controlPanel.add(antialiasPanel); tabbedPane.add(controlPanel); tabbedPane.setTitleAt(2, "Shape"); // Image controls controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); controlPanel.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9)); controlPanel.setToolTipText("Background image texture controls"); controlPanel.add(imagePanel); controlPanel.add(imageScalePanel); controlPanel.add(imageOffsetPanel); tabbedPane.add(controlPanel); tabbedPane.setTitleAt(3, "Image"); // Misc controls controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); controlPanel.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9)); controlPanel.setToolTipText("Offset, distance scaling and fading, highlight"); controlPanel.add(offsetPanel); controlPanel.add(distancePanel); controlPanel.add(highlightPanel); tabbedPane.add(controlPanel); tabbedPane.setTitleAt(4, "Misc."); //-- Combine tabbed panes with buttons at the bottom controlPanel = new JPanel(); controlPanel.setBorder( new CompoundBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9), new TitledBorder("Annotation"))); controlPanel.setToolTipText("Current or new annotation"); controlPanel.setLayout(new BorderLayout()); controlPanel.add(tabbedPane, BorderLayout.NORTH); controlPanel.add(buttonPanel, BorderLayout.SOUTH); return controlPanel; } private void updateControlPanel() { if(this.currentAnnotation != null) { this.suspendUpdate = true; this.inputTextArea.setText(this.currentAnnotation.getText()); this.widthSlider.setValue(this.currentAnnotation.getAttributes().getSize().width); this.heightSlider.setValue(this.currentAnnotation.getAttributes().getSize().height); this.scaleSlider.setValue((int)(this.currentAnnotation.getAttributes().getScale() * 10)); this.opacitySlider.setValue((int)(this.currentAnnotation.getAttributes().getOpacity() * 10)); this.cbAdjustWidth.setSelected(this.currentAnnotation.getAttributes() .getAdjustWidthToText().equals(Annotation.SIZE_FIT_TEXT)); Font font = currentAnnotation.getAttributes().getFont(); if(font != null) { this.cbFontName.setSelectedItem(font.getName()); this.cbFontStyle.setSelectedIndex(font.getStyle()); this.cbFontSize.setSelectedItem(String.valueOf(font.getSize())); } else { this.cbFontName.setSelectedItem("Arial"); this.cbFontStyle.setSelectedItem("Plain"); this.cbFontSize.setSelectedItem("12"); } Color color = currentAnnotation.getAttributes().getTextColor(); String colorAlpha = color != null ? String.valueOf((int)Math.round((float)color.getAlpha() / 25.5f)) : "8"; this.btTextColor.setBackground(color != null ? new Color(color.getRed(), color.getGreen(), color.getBlue()) : Color.WHITE); this.cbTextColorAlpha.setSelectedItem(colorAlpha); color = currentAnnotation.getAttributes().getBackgroundColor(); colorAlpha = color != null ? String.valueOf((int)Math.round((float)color.getAlpha() / 25.5f)) : "4"; this.btBackColor.setBackground(color != null ? new Color(color.getRed(), color.getGreen(), color.getBlue()) : Color.BLACK); this.cbBackColorAlpha.setSelectedItem(colorAlpha); //color = currentAnnotation.getBorderColor(); color = this.savedBorderColor; colorAlpha = color != null ? String.valueOf((int)Math.round((float)color.getAlpha() / 25.5f)) : "7"; this.btBorderColor.setBackground(color != null ? new Color(color.getRed(), color.getGreen(), color.getBlue()) : Color.WHITE); this.cbBorderColorAlpha.setSelectedItem(colorAlpha); switch (currentAnnotation.getAttributes().getTextAlign()) { case MultiLineTextRenderer.ALIGN_LEFT : {cbTextAlign.setSelectedIndex(0); break;} case MultiLineTextRenderer.ALIGN_CENTER : {cbTextAlign.setSelectedIndex(1); break;} case MultiLineTextRenderer.ALIGN_RIGHT : {cbTextAlign.setSelectedIndex(2); break;} } if (currentAnnotation.getAttributes().getEffect().equals(MultiLineTextRenderer.EFFECT_NONE)) cbTextEffect.setSelectedIndex(0); else if (currentAnnotation.getAttributes().getEffect().equals(MultiLineTextRenderer.EFFECT_SHADOW)) cbTextEffect.setSelectedIndex(1); else if (currentAnnotation.getAttributes().getEffect().equals(MultiLineTextRenderer.EFFECT_OUTLINE)) cbTextEffect.setSelectedIndex(2); if (currentAnnotation.getAttributes().getFrameShape().equals(FrameFactory.SHAPE_RECTANGLE)) cbShape.setSelectedIndex(0); else if (currentAnnotation.getAttributes().getFrameShape().equals(FrameFactory.SHAPE_ELLIPSE)) cbShape.setSelectedIndex(1); else cbShape.setSelectedIndex(2); if (currentAnnotation.getAttributes().getLeader().equals(FrameFactory.LEADER_TRIANGLE)) cbLeader.setSelectedIndex(0); else cbLeader.setSelectedIndex(1); this.cornerRadiusSlider.setValue(currentAnnotation.getAttributes().getCornerRadius()); Insets insets = currentAnnotation.getAttributes().getInsets(); this.insetsTop.setValue(insets.top); this.insetsRight.setValue(insets.right); this.insetsBottom.setValue(insets.bottom); this.insetsLeft.setValue(insets.left); this.borderWidthSlider.setValue((int)(currentAnnotation.getAttributes().getBorderWidth() * 10)); this.stippleFactorSlider.setValue((int)(currentAnnotation.getAttributes().getBorderStippleFactor())); switch (currentAnnotation.getAttributes().getAntiAliasHint()) { case Annotation.ANTIALIAS_DONT_CARE: this.cbAntialias.setSelectedIndex(0); break; case Annotation.ANTIALIAS_FASTEST: this.cbAntialias.setSelectedIndex(1); break; case Annotation.ANTIALIAS_NICEST: this.cbAntialias.setSelectedIndex(2); break; } if (currentAnnotation.getAttributes().getImageSource() != null) { if (currentAnnotation.getAttributes().getImageSource() instanceof String) { String image = (String)currentAnnotation.getAttributes().getImageSource(); if(image.equals(IMAGE_EARTH)) this.cbImage.setSelectedIndex(1); else if(image.equals(IMAGE_NASA)) this.cbImage.setSelectedIndex(2); else if(image.equals(IMAGE_WWJ_SPLASH)) this.cbImage.setSelectedIndex(3); else this.cbImage.setSelectedIndex(4); // Custom } else this.cbImage.setSelectedIndex(4); // Custom } else this.cbImage.setSelectedIndex(0); // None String imageRepeat = currentAnnotation.getAttributes().getImageRepeat(); if (imageRepeat.equals(Annotation.IMAGE_REPEAT_NONE)) this.cbImageRepeat.setSelectedIndex(0); else if (imageRepeat.equals(Annotation.IMAGE_REPEAT_X)) this.cbImageRepeat.setSelectedIndex(1); else if (imageRepeat.equals(Annotation.IMAGE_REPEAT_Y)) this.cbImageRepeat.setSelectedIndex(2); else if (imageRepeat.equals(Annotation.IMAGE_REPEAT_XY)) this.cbImageRepeat.setSelectedIndex(3); this.imageScaleSlider.setValue((int)(this.currentAnnotation.getAttributes().getImageScale() * 10)); this.imageOpacitySlider.setValue((int)(this.currentAnnotation.getAttributes().getImageOpacity() * 10)); this.imageOffsetXSlider.setValue((int)(this.currentAnnotation.getAttributes().getImageOffset().x)); this.imageOffsetYSlider.setValue((int)(this.currentAnnotation.getAttributes().getImageOffset().y)); this.offsetXSlider.setValue(this.currentAnnotation.getAttributes().getDrawOffset().x); this.offsetYSlider.setValue(this.currentAnnotation.getAttributes().getDrawOffset().y); this.distanceMinScaleSlider.setValue((int)(this.currentAnnotation.getAttributes().getDistanceMinScale() * 10)); this.distanceMaxScaleSlider.setValue((int)(this.currentAnnotation.getAttributes().getDistanceMaxScale() * 10)); this.distanceMinOpacitySlider.setValue((int)(this.currentAnnotation.getAttributes().getDistanceMinOpacity() * 10)); this.highlightScaleSlider.setValue((int)(this.currentAnnotation.getAttributes().getHighlightScale() * 10)); AppFrame.this.btApply.setEnabled(true); AppFrame.this.btRemove.setEnabled(true); this.suspendUpdate = false; } else { this.inputTextArea.setText(""); AppFrame.this.btApply.setEnabled(false); AppFrame.this.btRemove.setEnabled(false); } } private void updateAnnotation() { if(this.currentAnnotation != null && !this.suspendUpdate) { this.currentAnnotation.setText(this.inputTextArea.getText()); this.currentAnnotation.getAttributes().setSize(new Dimension(this.widthSlider.getValue(), this.heightSlider.getValue())); this.currentAnnotation.getAttributes().setScale((double)this.scaleSlider.getValue() / 10); this.currentAnnotation.getAttributes().setOpacity((double)this.opacitySlider.getValue() / 10); this.currentAnnotation.getAttributes().setAdjustWidthToText(this.cbAdjustWidth.isSelected() ? Annotation.SIZE_FIT_TEXT : Annotation.SIZE_FIXED); String fontString = this.cbFontName.getSelectedItem() + "-" + this.cbFontStyle.getSelectedItem().toString().toUpperCase() + "-" + this.cbFontSize.getSelectedItem(); this.currentAnnotation.getAttributes().setFont(Font.decode(fontString)); Color color = this.btTextColor.getBackground(); int alpha = (int)(Float.valueOf((String)this.cbTextColorAlpha.getSelectedItem()) * 25.5f); this.currentAnnotation.getAttributes().setTextColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha)); color = this.btBackColor.getBackground(); alpha = (int)(Float.valueOf((String)this.cbBackColorAlpha.getSelectedItem()) * 25.5f); this.currentAnnotation.getAttributes().setBackgroundColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha)); color = this.btBorderColor.getBackground(); alpha = (int)(Float.valueOf((String)this.cbBorderColorAlpha.getSelectedItem()) * 25.5f); this.currentAnnotation.getAttributes().setBorderColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha)); this.savedBorderColor = this.currentAnnotation.getAttributes().getBorderColor(); switch (cbTextAlign.getSelectedIndex()) { case 0 : {this.currentAnnotation.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_LEFT); break;} case 1 : {this.currentAnnotation.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_CENTER); break;} case 2 : {this.currentAnnotation.getAttributes().setTextAlign(MultiLineTextRenderer.ALIGN_RIGHT); break;} } switch (cbTextEffect.getSelectedIndex()) { case 0 : {this.currentAnnotation.getAttributes().setEffect(MultiLineTextRenderer.EFFECT_NONE); break;} case 1 : {this.currentAnnotation.getAttributes().setEffect(MultiLineTextRenderer.EFFECT_SHADOW); break;} case 2 : {this.currentAnnotation.getAttributes().setEffect(MultiLineTextRenderer.EFFECT_OUTLINE); break;} } switch (cbShape.getSelectedIndex()) { case 0 : {this.currentAnnotation.getAttributes().setFrameShape(FrameFactory.SHAPE_RECTANGLE); break;} case 1 : {this.currentAnnotation.getAttributes().setFrameShape(FrameFactory.SHAPE_ELLIPSE); break;} case 2 : {this.currentAnnotation.getAttributes().setFrameShape(FrameFactory.SHAPE_NONE); break;} } switch (cbLeader.getSelectedIndex()) { case 0 : {this.currentAnnotation.getAttributes().setLeader(FrameFactory.LEADER_TRIANGLE); break;} case 1 : {this.currentAnnotation.getAttributes().setLeader(FrameFactory.LEADER_NONE); break;} } currentAnnotation.getAttributes().setCornerRadius(this.cornerRadiusSlider.getValue()); currentAnnotation.getAttributes().setInsets(new Insets( Integer.parseInt(insetsTop.getValue().toString()), Integer.parseInt(insetsLeft.getValue().toString()), Integer.parseInt(insetsBottom.getValue().toString()), Integer.parseInt(insetsRight.getValue().toString()))); currentAnnotation.getAttributes().setBorderWidth((double)borderWidthSlider.getValue() / 10); currentAnnotation.getAttributes().setBorderStippleFactor(stippleFactorSlider.getValue()); switch (cbAntialias.getSelectedIndex()) { case 0: currentAnnotation.getAttributes().setAntiAliasHint(Annotation.ANTIALIAS_DONT_CARE); break; case 1: currentAnnotation.getAttributes().setAntiAliasHint(Annotation.ANTIALIAS_FASTEST); break; case 2: currentAnnotation.getAttributes().setAntiAliasHint(Annotation.ANTIALIAS_NICEST); break; } switch (cbImage.getSelectedIndex()) { case 0: currentAnnotation.getAttributes().setImageSource(null); break; case 1: currentAnnotation.getAttributes().setImageSource(IMAGE_EARTH); break; case 2: currentAnnotation.getAttributes().setImageSource(IMAGE_NASA); break; case 3: currentAnnotation.getAttributes().setImageSource(IMAGE_WWJ_SPLASH); break; case 4: currentAnnotation.getAttributes().setImageSource(savedImage); break; } switch (cbImageRepeat.getSelectedIndex()) { case 0: currentAnnotation.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_NONE); break; case 1: currentAnnotation.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_X); break; case 2: currentAnnotation.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_Y); break; case 3: currentAnnotation.getAttributes().setImageRepeat(Annotation.IMAGE_REPEAT_XY); break; } this.currentAnnotation.getAttributes().setImageScale((double)this.imageScaleSlider.getValue() / 10); this.currentAnnotation.getAttributes().setImageOpacity((double)this.imageOpacitySlider.getValue() / 10); this.currentAnnotation.getAttributes().setImageOffset(new Point(imageOffsetXSlider.getValue(), imageOffsetYSlider.getValue())); this.currentAnnotation.getAttributes().setDrawOffset(new Point(offsetXSlider.getValue(), offsetYSlider.getValue())); this.currentAnnotation.getAttributes().setDistanceMinScale((double)this.distanceMinScaleSlider.getValue() / 10); this.currentAnnotation.getAttributes().setDistanceMaxScale((double)this.distanceMaxScaleSlider.getValue() / 10); this.currentAnnotation.getAttributes().setDistanceMinOpacity((double)this.distanceMinOpacitySlider.getValue() / 10); this.currentAnnotation.getAttributes().setHighlightScale((double)this.highlightScaleSlider.getValue() / 10); getWwd().repaint(); } } private Position computeGroundPosition(WorldWindowGLCanvas wwd) { View view = wwd.getView(); if (view == null) return null; Position groundPos = view.computePositionFromScreenPoint( view.getViewport().getWidth() / 2, view.getViewport().getHeight() / 2); return groundPos; } /** * Simple (and unfinished) save annotations to a KML file. */ private void save() { StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); sb.append("<kml xmlns=\"http://earth.google.com/kml/2.2\">\n"); sb.append("<Folder>\n"); sb.append(" <name>WWJ Annotations</name>\n"); Iterator<Annotation> annotations = layer.getAnnotations().iterator(); while(annotations.hasNext()) { GlobeAnnotation ga = (GlobeAnnotation)annotations.next(); String bgColor = ga.getAttributes().getBackgroundColor() != null ? getHexaColorABGR(ga.getAttributes().getBackgroundColor()) : "66000000"; String textColor = ga.getAttributes().getTextColor() != null ? getHexaColorABGR(ga.getAttributes().getTextColor()) : "ccffffff"; String borderColor = ga.getAttributes().getBorderColor() != null ? getHexaColorABGR(ga.getAttributes().getBorderColor()) : "ccffffff"; sb.append(" <Placemark>\n"); sb.append(" <Style>\n"); sb.append(" <BalloonStyle>\n"); if(bgColor.length() > 0) sb.append(" <bgColor>" + bgColor + "</bgColor>\n"); if(textColor.length() > 0) sb.append(" <textColor>" + textColor + "</textColor>\n"); if(borderColor.length() > 0) sb.append(" <borderColor>" + borderColor + "</borderColor>\n"); sb.append(" </BalloonStyle>\n"); sb.append(" </Style>\n"); sb.append(" <name>"); sb.append(ga.getText().length() > 50 ? ga.getText().substring(0, 47) + "..." : ga.getText() ); sb.append("</name>\n"); sb.append(" <description>"); sb.append("<![CDATA[" + ga.getText().replaceAll("\n", "<br />") + "]]>"); sb.append("</description>\n"); sb.append(" <Point>"); sb.append("<coordinates>" + ga.getPosition().getLongitude().degrees + "," + ga.getPosition().getLatitude().degrees + "," + ga.getPosition().getElevation() + "</coordinates>"); sb.append("</Point>\n"); sb.append("\n"); sb.append(" </Placemark>\n"); } sb.append("</Folder>\n"); sb.append("</kml>\n"); System.out.println(sb); } private String getHexaColorABGR(Color color) { return String.format("%1$02x%2$02x%3$02x%4$02x", color.getAlpha(), color.getBlue(), color.getGreen(), color.getRed()); } } public static void main(String[] args) { ApplicationTemplate.start("World Wind Annotations", AppFrame.class); } }