Re: [OpenMap Users] Changes in OpenMap 4.6.2

From: Don Dietrick <dietrick_at_bbn.com>
Date: Wed, 26 Jan 2005 09:49:38 -0500

Hi Piotr,

Thanks for all the updates, I'll try to get them into 4.6.2. I might
have to wait and do a quick 4.6.3 release, I'm juggling a few things
right now and need to get the 4.6.2 release out.

On Jan 25, 2005, at 11:31 AM, Piotr KamiƄski wrote:

> A few additional questions:Ƃa
> 1. UTMPoint was changed. Instead of Zone letter code there is N or S
> now. Why such a change? I though that zone letters was some kind of
> standard. I assume that you decided to use ONLY S or N letter. Am I
> right? There is erronous comment in UTMPoint constructor - "If the
> letter is something else instead of 'N' or 'S', 'N' is assumed."
> Method checkZone throws exception when something different than N or S
> is specified.

It turns out that N or S are the only valid zones for UTM. All the
other zone letters only pertain to MGRS. I had a reference that
blurred this line, which is the reason for the original design.

Thanks for picking up on the comment, it's a result of hesitation on my
part, I was trying to decide whether it was better to throw an
exception or quietly interpret incorrect input. I decided to throw an
exception, but forgot to update the comment.

> 2. What about changing LatLonPoint from float to double type. I had
> asked about this some time ago but haven't found any change in source
> code. I saw reports on OpenMap's list about successful transformation
> to double. Maybe just such change is enough? Our changes in ProjMath
> (insufficient precision of float type) testifies against current,
> mostly float based, implementation of calculation algorithms.

It's not going to happen for the 4.6.2 release, but I've hammered out a
design in a different project (which uses OpenMap, too) that preserves
backward compatibility which providing a choice between floats and
doubles. The API change to the projection libraries will be
significant, (but still backward compatible) so I think a 4.7 version
change will be proper.

> 3. In LatLonPoint class equals method is not "synchronized" with
> hashCode method. It is recommended that hashCode and equals are
> symmetric methods -> if x.equals(y) then x.hashCode(x) must ==
> y.hashCode(). I think that current implementation not supports this.
> .I wander if using LatLonPoint as a key in HashMap or Set would lead
> to some "strange" problems.

Good point, I'll look into this before releasing 4.6.2.

Thanks again,

Don


>
> Waiting to hear from you.,
> Best regards,
> Piotr Kaminski
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/LayerPane.java,v
> $
> // $RCSfile: LayerPane.java,v $
> // $Revision: 1.2 $
> // $Date: 2003/10/10 15:43:21 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.gui;
>
> import java.awt.*;
> import java.awt.event.*;
> import java.io.Serializable;
> import java.net.URL;
>
> import javax.swing.*;
>
> import com.bbn.openmap.*;
> import com.bbn.openmap.util.Debug;
>
> /**
> * A LayerPane is a single instance of how a layer represented in the
> * LayersPanel. It contains three widgets: an on/off button; a palette
> * button; and a toggle button with the layer name.
> * <P>
> */
> public class LayerPane extends JPanel implements Serializable,
> ActionListener,
> ComponentListener {
>
> protected transient boolean paletteButtonAsCheckBox = false;
> protected transient JToggleButton onoffButton;
> // check box behavior of palette button
> protected transient JToggleButton paletteCheckBox;
> // simple button behavior of palette button
> protected transient JButton paletteButton;
> protected transient JToggleButton layerName;
> protected transient boolean selected;
> protected transient Layer layer;
>
> protected transient LayerHandler layerHandler;
>
> // the icons
> protected static transient URL url1;
> protected static transient ImageIcon paletteIcon;
> protected static transient URL url2;
> protected static transient ImageIcon paletteOnIcon;
> protected static transient URL url3;
> protected static transient ImageIcon layerOnIcon;
> protected static transient URL url4;
> protected static transient ImageIcon layerOffIcon;
>
> public transient final static String showPaletteCmd =
> "showPalette";
> public transient final static String toggleLayerCmd =
> "toggleLayerCmd";
>
> protected Color offColor;
> protected Color onColor = new Color(0xFF0066CC);
>
> // default initializations
> static {
> url1 = LayerPane.class.getResource("PaletteOff.gif");
> paletteIcon = new ImageIcon(url1, "palette");
> url2 = LayerPane.class.getResource("PaletteOn.gif");
> paletteOnIcon = new ImageIcon(url2, "palette on");
>
> url3 = LayerPane.class.getResource("BulbOn.gif");
> layerOnIcon = new ImageIcon(url3, "layer selected");
> url4 = LayerPane.class.getResource("BulbOff.gif");
> layerOffIcon = new ImageIcon(url4, "layer not selected");
> }
>
> /**
> * _at_param layer the layer to be represented by the pane.
> * _at_param bg the buttongroup for the layer
> * _at_param layerHandler the LayerHandler that contains information
> * about the Layers.
> */
> public LayerPane(Layer layer, LayerHandler layerHandler,
> ButtonGroup bg) {
> this(layer, layerHandler, bg, false);
> }
>
> /**
> * _at_param layer the layer to be represented by the pane.
> * _at_param bg the buttongroup for the layer
> * _at_param layerHandler the LayerHandler that contains information
> * about the Layers.
> * _at_param paletteButtonAsCheckBox true if palette button should
> behave like CheckBox, false - regular button.
> * The default value is false.
> */
> public LayerPane(Layer layer, LayerHandler layerHandler,
> ButtonGroup bg, boolean paletteButtonAsCheckBox) {
> super();
> this.paletteButtonAsCheckBox = paletteButtonAsCheckBox;
> this.layer = layer;
> setLayerHandler(layerHandler);
> createGUI(bg);
> layer.addComponentListener(this);
> }
>
> protected void createGUI(ButtonGroup bg) {
> GridBagLayout gridbag = new GridBagLayout();
> GridBagConstraints c = new GridBagConstraints();
> setLayout(gridbag);
>
> //onoffButton = new JCheckBox();
> onoffButton = new JToggleButton(layerOnIcon); //byÂło Off
> onoffButton.setMargin(new Insets(0,0,0,0));
>
> onoffButton.setSelectedIcon(layerOnIcon);
> onoffButton.setActionCommand(toggleLayerCmd);
> onoffButton.addActionListener(this);
> onoffButton.setToolTipText("Turn " + layer.getName() + " layer
> on/off");
>
> // Determine if this layer has already been activated
> onoffButton.setSelected(layer.isVisible());
>
> // add the palette show/hide checkbutton
> if (this.paletteButtonAsCheckBox) {
> createPaletteCheckBox();
> } else {
> createPaletteButton();
> }
>
> layerName = new JToggleButton(layer.getName());
> layerName.setBorderPainted(false);
> layerName.addActionListener(this);
> offColor = layerName.getBackground();
> layerName.setToolTipText("Click to select layer");
> layerName.setHorizontalAlignment(SwingConstants.LEFT);
> bg.add(layerName);
>
> c.gridy = 0;
> c.gridx = GridBagConstraints.RELATIVE;
> c.anchor = GridBagConstraints.WEST;
> gridbag.setConstraints(onoffButton, c);
> add(onoffButton);
>
> if (this.paletteButtonAsCheckBox) {
> gridbag.setConstraints(paletteCheckBox, c);
> add(paletteCheckBox);
> } else {
> gridbag.setConstraints(paletteButton, c);
> add(paletteButton);
> }
>
> c.weightx = 1;
> c.fill = GridBagConstraints.HORIZONTAL;
> c.insets = new Insets(1, 2, 1, 15);
> gridbag.setConstraints(layerName, c);
> add(layerName);
> }
>
> protected void createPaletteCheckBox() {
> paletteCheckBox = new JToggleButton(paletteOnIcon);
> paletteCheckBox.setMargin(new Insets(0,0,0,0));
> //paletteCheckBox = new JCheckBox();
> //paletteCheckBox = new JCheckBox(paletteIcon);
> paletteCheckBox.setSelected(false);
> //paletteCheckBox.setSelectedIcon(paletteOnIcon);
> if (layer.getGUI() == null) {
> paletteCheckBox.setEnabled(false);
> paletteCheckBox.setToolTipText("No tools available for " +
> layer.getName() + " layer");
> } else {
> paletteCheckBox.setToolTipText("Display/Hide tools for " +
> layer.getName() + " layer");
> }
> paletteCheckBox.setActionCommand(showPaletteCmd);
> paletteCheckBox.addActionListener(this);
> }
>
> protected void createPaletteButton() {
> paletteButton = new JButton(paletteIcon);
> paletteButton.setBorderPainted(false);
> if (layer.getGUI() == null) {
> paletteButton.setEnabled(false);
> paletteButton.setToolTipText("No tools available for " +
> layer.getName() + " layer");
> } else {
> paletteButton.setToolTipText("Display tools for " +
> layer.getName() + " layer");
> }
> paletteButton.setActionCommand(showPaletteCmd);
> paletteButton.addActionListener(this);
> }
>
> /**
> * Used for the background LayerPanel marker.
> */
> protected LayerPane(String title) {
> super();
> // prevent null pointers somewhere...
> this.layer = com.bbn.openmap.layer.SinkLayer.getSharedInstance();
> GridBagLayout gridbag = new GridBagLayout();
> GridBagConstraints c = new GridBagConstraints();
> setLayout(gridbag);
>
> JSeparator sep = new JSeparator();
> sep.setToolTipText(title);
>
> c.anchor = GridBagConstraints.WEST;
> c.weightx = 1;
> c.fill = GridBagConstraints.HORIZONTAL;
> gridbag.setConstraints(sep, c);
> add(sep);
> }
>
> public void setLayerHandler(LayerHandler in_layerHandler) {
> layerHandler = in_layerHandler;
> }
>
> /**
> * _at_return LayerHandler if it has been found in the MapHandler or
> * set.
> */
> protected LayerHandler getLayerHandler() {
> return layerHandler;
> }
>
> /**
> * Same as cleanup, except the layer name toggle button gets
> * removed from the given button group.
> */
> public void cleanup(ButtonGroup bg) {
> if (bg != null) {
> bg.remove(layerName);
> }
> cleanup();
> }
>
> /**
> * LayerPane disconnects from listeners, nulls out components.
> */
> public void cleanup() {
> if (layer != null) {
> this.layer.removeComponentListener(this);
> }
> if (onoffButton != null) {
> onoffButton.removeActionListener(this);
> }
> if (paletteButton != null) {
> paletteButton.removeActionListener(this);
> }
> if (paletteCheckBox != null) {
> paletteCheckBox.removeActionListener(this);
> }
> layerName = null;
> this.layer = null;
> this.layerHandler = null;
> this.removeAll();
> }
>
> public Dimension getPreferredSize() {
> return new Dimension(200, 32);
> }
>
> public Dimension getMinimumSize() {
> return new Dimension(100, 20);
> }
>
> /**
> * _at_return whether the layer is on
> */
> public boolean isLayerOn() {
> return onoffButton.isSelected();
> }
>
> /**
> * Turns the button on or off
> */
> public void setLayerOn(boolean value) {
> onoffButton.setSelected(value);
> }
>
> /**
> * _at_return whether the palette for this layer is on
> */
> public boolean isPaletteOn() {
> return this.paletteButtonAsCheckBox ? paletteCheckBox.isSelected() :
> paletteButton.isSelected();
> }
>
> /**
> * Turns the palette button on or off
> */
> public void setPaletteOn(boolean value) {
>
> if (this.paletteButtonAsCheckBox) {
> paletteCheckBox.setSelected(value);
> } else {
> paletteButton.setSelected(value);
> }
> }
>
> /**
> * _at_return the status of the layerName toggle button
> */
> public boolean isSelected() {
> return layerName.isSelected();
> }
>
> /**
> * Highlights/unhighlights the layerName toggle button
> */
> public void setSelected(boolean select) {
> layerName.setSelected(select);
>
> String command = select ? LayersPanel.LayerSelectedCmd
> : LayersPanel.LayerDeselectedCmd;
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayerPane for " + getLayer().getName() + " "
> + command + ", firing event");
> }
>
> firePropertyChange(command, null, getLayer());
> }
>
> /**
> * _at_return the layer represented by this LayerPane
> */
> public Layer getLayer() {
> return layer;
> }
>
> public void finalize() {
> if (Debug.debugging("gc")) {
> Debug.output("LayerPane getting GC'd");
> }
> }
>
> /**
> * Tell the pane to check with the layer to get the current layer
> * name for it's label.
> */
> public void updateLayerLabel() {
> layerName.setText(getLayer().getName());
> }
>
> protected void showPalette() {
> layer.showPalette();
> }
>
>
> protected void hidePalette() {
> layer.hidePalette();
> }
>
> /**
> * ActionListener interface.
> * _at_param e ActionEvent
> */
> public void actionPerformed(java.awt.event.ActionEvent e) {
>
> if (e.getSource().equals(paletteButton)){
> setSelected(true);
> paletteButton.setIcon(paletteOnIcon);
> showPalette();
>
> } else if (e.getSource().equals(paletteCheckBox)){
> if (paletteCheckBox.isSelected()) {
> System.out.println("SELECTED");
> showPalette();
> } else {
> System.out.println("NOT SELECTED");
> hidePalette();
> }
>
> } else if (e.getSource().equals(onoffButton)) {
> setSelected(true);
> // layer is selected, add it to or remove it from map
> if (layerHandler != null) {
> Debug.message("layerspanel","LayerPane|actionPerformed calling
> layerHandler.turnLayerOn()");
> layerHandler.turnLayerOn(onoffButton.isSelected(), layer);
> }
>
> if (Debug.debugging("layerspanel")){
> Debug.output("LayerPane: Layer " + layer.getName() +
> (layer.isVisible()?" is visible.":" is NOT visible"));
> }
> } else if (e.getSource().equals(layerName)) {
> setSelected(true);
> }
> }
>
> /**
> * Invoked when component has been resized.
> */
> public void componentResized(ComponentEvent e) {}
>
> /**
> * Invoked when component has been moved.
> */
> public void componentMoved(ComponentEvent e) {}
>
> /**
> * Invoked when component has been shown.
> */
> public void componentShown(ComponentEvent e) {
> if (Debug.debugging("layerspanel")){
> Debug.output("LayerPane: layer pane for " + layer.getName() +
> " receiving componentShown event");
> }
>
> Component comp = e.getComponent();
> if (comp == null) {
> } else if (comp == layer){
> if (isLayerOn() != true) {
> setLayerOn(true);
> if (Debug.debugging("layerspanel")){
> Debug.output("LayerPane: layer " + layer.getName() +
> " is now visible.");
> }
> }
> } else if (comp == layer.getPalette()) {
> if (this.paletteButtonAsCheckBox) {
> System.out.println("COMP SHOWN");
> paletteCheckBox.setSelected(true);
> } else {
> paletteButton.setIcon(paletteOnIcon);
> }
> }
> }
>
> /**
> * Invoked when component has been hidden.
> */
> public void componentHidden(ComponentEvent e) {
> if (Debug.debugging("layerspanel")){
> Debug.output("LayerPane: layer pane for " + layer.getName() +
> " receiving componentHidden event");
> }
> Component comp = e.getComponent();
>
> if (comp == layer) {
> if (isLayerOn() != false){
> setLayerOn(false);
> if (Debug.debugging("layerspanel")){
> Debug.output("LayerPane: layer " + layer.getName() +
> " is now hidden.");
> }
> }
> } else if (comp == layer.getPalette()) {
> if (this.paletteButtonAsCheckBox) {
> paletteCheckBox.setSelected(false);
> } else {
> paletteButton.setIcon(paletteIcon);
> }
> } else if (comp == null) {
> if (Debug.debugging("layerspanel")){
> Debug.output("LayerPane: layer " + layer.getName() +
> " is now hidden.");
> }
> }
> }
>
> protected static LayerPane backgroundLayerSeparator;
>
> public static LayerPane getBackgroundLayerSeparator(String title) {
> if (backgroundLayerSeparator == null) {
> backgroundLayerSeparator = new LayerPane(title);
> }
> return backgroundLayerSeparator;
> }
> }
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/
> LayerControlButtonPanel.java,v $
> // $RCSfile: LayerControlButtonPanel.java,v $
> // $Revision: 1.6 $
> // $Date: 2004/10/14 18:18:16 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.gui;
>
> import java.awt.*;
> import java.awt.event.*;
> import java.beans.PropertyChangeEvent;
> import java.beans.PropertyChangeListener;
> import java.net.URL;
> import java.util.Properties;
> import javax.swing.*;
>
> import com.bbn.openmap.Layer;
> import com.bbn.openmap.layer.util.LayerUtils;
> import com.bbn.openmap.util.Debug;
> import com.bbn.openmap.util.PropUtils;
> import com.bbn.openmap.util.propertyEditor.*;
>
> /**
> * A OMComponentPanel that provides controls to manupulate layer order
> * in the LayersPanel, and to provide add layer and delete layer
> * buttons. This panel can be embedded into the LayersPanel, or it can
> * be positioned somewhere else in the application. The LayersPanel
> * can be set to create one of these itself, in which case it will be
> * embedded. If you don't want an embedded version, create one and add
> * it to the MapHandler, it will hook up to the LayersPanel when it
> * finds it. The LayerPanes, LayersPanel and LayerControlButtonPanel
> * communicate with each other using PropertyChangeEvents. The
> * LayerPanes notify the LayersPanelwhen one of them is selected, and
> * that event gets passed to this panel. When a button on this panel
> * is pressed, it fires a PropertyChangeEvent with the layer and
> * command to take to all its PropertyChangeListeners.
> * <P>
> *
> * The LayerControlButtonPanel takes these properties:
> *
> * <pre>
> *
> *
> *
> * # Direction buttons are laid out, vertical or horizontal
> (vertical is default).
> * orientation=vertical
> * # Flag on whether to insert buttons onto LayersPanel (true by
> default).
> * embedded=true
> * # Configuration setting when embedding into LayersPanel (WEST,
> * # NORTH, EAST, SOUTH, NORTH_SOUTH) NORTH_SOUTH puts up button
> above
> * # list, down button below list.
> * configuration=WEST
> * # Flag to put button that lets the user delete layers (true by
> default).
> * delete=true
> * # Flag to put button that lets the user add layers, if the
> * # LayersAddPanel is discovered in the MapHandler (true by
> default)
> * add=true
> *
> *
> *
> * </pre>
> */
> public class LayerControlButtonPanel extends OMComponentPanel
> implements
> ActionListener, PropertyChangeListener {
>
> // Images
> protected static transient URL urlup;
> protected static transient ImageIcon upgif;
> protected static transient URL urlupc;
> protected static transient ImageIcon upclickedgif;
> protected static transient URL urltop;
> protected static transient ImageIcon topgif;
> protected static transient URL urltopc;
> protected static transient ImageIcon topclickedgif;
> protected static transient URL urldown;
> protected static transient ImageIcon downgif;
> protected static transient URL urldownc;
> protected static transient ImageIcon downclickedgif;
> protected static transient URL urlbottom;
> protected static transient ImageIcon bottomgif;
> protected static transient URL urlbottomc;
> protected static transient ImageIcon bottomclickedgif;
> protected static transient URL urldelete;
> protected static transient ImageIcon deletegif;
> protected static transient URL urldeletec;
> protected static transient ImageIcon deleteclickedgif;
> protected static transient URL urladd;
> protected static transient ImageIcon addgif;
> protected static transient URL urladdc;
> protected static transient ImageIcon addclickedgif;
>
> /**
> * Static default initializations.
> */
> static {
> urlup = LayersPanel.class.getResource("Up.gif");
> upgif = new ImageIcon(urlup, "Up");
>
> urlupc = LayersPanel.class.getResource("Up.gif");
> upclickedgif = new ImageIcon(urlupc, "Up (clicked)");
>
> urltop = LayersPanel.class.getResource("DoubleUp.gif");
> topgif = new ImageIcon(urltop, "Top");
>
> urltopc = LayersPanel.class.getResource("DoubleUp.gif");
> topclickedgif = new ImageIcon(urltopc, "Top (clicked)");
>
> urldown = LayersPanel.class.getResource("Down.gif");
> downgif = new ImageIcon(urldown, "Down");
>
> urldownc = LayersPanel.class.getResource("Down.gif");
> downclickedgif = new ImageIcon(urldownc, "Down (clicked)");
>
> urlbottom = LayersPanel.class.getResource("DoubleDown.gif");
> bottomgif = new ImageIcon(urlbottom, "Bottom");
>
> urlbottomc = LayersPanel.class.getResource("DoubleDown.gif");
> bottomclickedgif = new ImageIcon(urlbottomc, "Bottom (clicked)");
>
> urldelete = LayersPanel.class.getResource("DeleteLayer.gif");
> deletegif = new ImageIcon(urldelete, "Delete");
>
> urldeletec = LayersPanel.class.getResource("DeleteLayer.gif");
> deleteclickedgif = new ImageIcon(urldeletec, "Delete (clicked)");
>
> urladd = LayersPanel.class.getResource("AddLayer.gif");
> addgif = new ImageIcon(urladd, "Add");
>
> urladdc = LayersPanel.class.getResource("AddLayer.gif");
> addclickedgif = new ImageIcon(urladdc, "Add (clicked)");
> }
>
> protected JButton add = null;
> protected JButton delete = null;
> protected JButton top = null;
> protected JButton up = null;
> protected JButton down = null;
> protected JButton bottom = null;
>
> protected LayerAddPanel layerAddPanel;
>
> public final static String OrientationProperty = "orientation";
> public final static String ConfigurationProperty = "configuration";
> public final static String EmbeddedProperty = "embedded";
> public final static String DeleteLayersProperty = "delete";
> public final static String AddLayersProperty = "add";
>
> public final static String HORIZONTAL_CONFIG =
> OrientationPropertyEditor.HORIZONTAL;
> public final static String VERTICAL_CONFIG =
> OrientationPropertyEditor.VERTICAL;
>
> public final static String WEST_CONFIG = "WEST";
> public final static String EAST_CONFIG = "EAST";
> public final static String NORTH_CONFIG = "NORTH";
> public final static String SOUTH_CONFIG = "SOUTH";
> public final static String NORTH_SOUTH_CONFIG = "NORTH_SOUTH";
> public final static String DefaultConfiguration = WEST_CONFIG;
>
> protected int orientation = BoxLayout.Y_AXIS; // BoxLayout.X_AXIS
> protected String configuration = DefaultConfiguration;
> protected boolean embedded = true;
> protected boolean deleteLayers = true;
> protected boolean addLayers = true;
>
> public LayerControlButtonPanel() {
> super();
> createInterface();
> }
>
> public LayerControlButtonPanel(LayersPanel panel) {
> this();
> setLayersPanel(panel);
> }
>
> public void removeLayersPanel(LayersPanel panel) {
> if (panel != null) {
> panel.setControls(null);
> panel.removePropertyChangeListener(this);
> removePropertyChangeListener(panel);
>
> if (embedded) {
> if
> (configuration.equalsIgnoreCase(NORTH_SOUTH_CONFIG)) {
> panel.remove(up);
> panel.remove(down);
> } else {
> panel.remove(this);
> }
> }
> }
> }
>
> /**
> * Sets this panel to control the LayersPanel. If you want to
> * extend this class and change how the buttons are displayed in
> * the LayersPanel, change this method.
> */
> public void setLayersPanel(LayersPanel panel) {
> if (panel != null) {
> // Just in case it's already been added.
> panel.removePropertyChangeListener(this);
>
> panel.addPropertyChangeListener(this);
> addPropertyChangeListener(panel);
>
> if (embedded) {
> createInterface(); // again, reset for new config
> // values
>
> setLayout(new BoxLayout(this, orientation));
>
> if (panel.getLayout() instanceof BorderLayout) {
> if (configuration.equalsIgnoreCase(WEST_CONFIG)) {
> panel.add(this, BorderLayout.WEST);
> } else if (configuration.equalsIgnoreCase(EAST_CONFIG)) {
> panel.add(this, BorderLayout.EAST);
> } else if (configuration.equalsIgnoreCase(NORTH_CONFIG)) {
> panel.add(this, BorderLayout.NORTH);
> } else if (configuration.equalsIgnoreCase(SOUTH_CONFIG)) {
> panel.add(this, BorderLayout.SOUTH);
> } else if (configuration.equalsIgnoreCase(NORTH_SOUTH_CONFIG)) {
> panel.add(up, BorderLayout.NORTH);
> panel.add(down, BorderLayout.SOUTH);
> }
> } else {
> panel.add(this);
> }
> }
> // Let the LayersPanel know who is controlling it.
> panel.setControls(this);
> }
> }
>
> protected void createInterface() {
> removeAll();
>
> setAlignmentX(LEFT_ALIGNMENT);
> setAlignmentY(CENTER_ALIGNMENT);
> setLayout(new BoxLayout(this, orientation));
>
> top = new JButton(topgif);
> top.setActionCommand(LayersPanel.LayerTopCmd);
> top.setPressedIcon(topclickedgif);
>
> top.setToolTipText(i18n.get(LayerControlButtonPanel.class,"moveLayerToT
> op","Move selected layer to top"));
> top.addActionListener(this);
> add(top);
>
> up = new JButton(upgif);
> up.setActionCommand(LayersPanel.LayerUpCmd);
> up.setPressedIcon(upclickedgif);
>
> up.setToolTipText(i18n.get(LayerControlButtonPanel.class,"moveLayerUpOn
> e","Move selected layer up one"));
> up.addActionListener(this);
> add(up);
>
> down = new JButton(downgif);
> down.setPressedIcon(downclickedgif);
> down.setActionCommand(LayersPanel.LayerDownCmd);
>
> down.setToolTipText(i18n.get(LayerControlButtonPanel.class,"moveLayerDo
> wnOne","Move selected layer down one"));
> down.addActionListener(this);
> add(down);
>
> bottom = new JButton(bottomgif);
> bottom.setPressedIcon(bottomclickedgif);
> bottom.setActionCommand(LayersPanel.LayerBottomCmd);
>
> bottom.setToolTipText(i18n.get(LayerControlButtonPanel.class,"moveLayer
> ToBottom","Move selected layer to bottom"));
> bottom.addActionListener(this);
> add(bottom);
>
> if (deleteLayers) {
> JLabel blank = new JLabel(" ");
> add(blank);
>
> delete = new JButton(deletegif);
> delete.setActionCommand(LayersPanel.LayerRemoveCmd);
>
> delete.setToolTipText(i18n.get(LayerControlButtonPanel.class,"removeLay
> er","Remove selected layer"));
> delete.addActionListener(this);
> delete.setEnabled(false);
> add(delete);
> }
>
> if (addLayers && add != null) {
> add(add);
> }
> }
>
> /**
> * Set the panel that brings up an interface to dynamically add
> * layers.
> */
> public void setLayerAddPanel(LayerAddPanel lap) {
> layerAddPanel = lap;
>
> if (layerAddPanel != null) {
> add = new JButton(addgif);
> add.setActionCommand(LayersPanel.LayerAddCmd);
>
> add.setToolTipText(i18n.get(LayerControlButtonPanel.class,"addLayer","A
> dd a layer"));
> add.addActionListener(this);
> if (addLayers) {
> this.add(add);
> }
> } else if (add != null) {
> this.remove(add);
> }
>
> }
>
> /**
> * Get the panel interface to dynamically add layers.
> */
> public LayerAddPanel getLayerAddPanel() {
> return layerAddPanel;
> }
>
> /**
> * Method associated with the ActionListener interface. This
> * method listens for action events meant to change the order of
> * the layers, as fired by the layer order buttons.
> *
> * _at_param e ActionEvent
> */
> public void actionPerformed(java.awt.event.ActionEvent e) {
>
> String command = e.getActionCommand();
>
> if (Debug.debugging("layerbuttons")) {
> Debug.output("LayersPanel.actionPerformed(): " + command);
> }
>
> if (command == LayersPanel.LayerTopCmd
> || command == LayersPanel.LayerBottomCmd
> || command == LayersPanel.LayerUpCmd
> || command == LayersPanel.LayerDownCmd
> || command == LayersPanel.LayerRemoveCmd) {
> if (selected != null) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayerControlButtonPanel: button
> firing "
> + command + " event for " +
> selected.getName());
> }
> firePropertyChange(command, null, selected);
> } else {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayerControlButtonPanel: button
> firing "
> + command + " event with no layer
> selected");
> }
> }
> } else if (command.equals(LayersPanel.LayerAddCmd)) {
> if (layerAddPanel != null) {
> layerAddPanel.showPanel();
> }
> }
> }
>
> protected Layer selected = null;
>
> public void propertyChange(PropertyChangeEvent pce) {
> String command = pce.getPropertyName();
> Object obj = pce.getNewValue();
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayerControlButtonPanel: receiving
> PropertyChangeEvent "
> + pce.getPropertyName());
> }
>
> if (command == LayersPanel.LayerSelectedCmd && obj instanceof
> Layer) {
>
> selected = (Layer) obj;
>
> delete.setEnabled(selected.isRemoveable());
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayerControlButtonPanel: got
> notification that layer is selected: "
> + selected.getName());
> }
> } else if (command == LayersPanel.LayerDeselectedCmd &&
> selected == obj) {
> selected = null;
> delete.setEnabled(false);
> }
> }
>
> public void findAndInit(Object someObj) {
> if (someObj instanceof LayerAddPanel) {
> setLayerAddPanel((LayerAddPanel) someObj);
> }
>
> if (someObj instanceof LayersPanel) {
> setLayersPanel((LayersPanel) someObj);
> }
> }
>
> public void findAndUndo(Object someObj) {
> if (someObj instanceof LayerAddPanel) {
> if (getLayerAddPanel() == someObj) {
> setLayerAddPanel(null);
> }
> }
>
> if (someObj instanceof LayersPanel) {
> removeLayersPanel((LayersPanel) someObj);
> }
> }
>
> public void setProperties(String prefix, Properties props) {
> super.setProperties(prefix, props);
> prefix = PropUtils.getScopedPropertyPrefix(prefix);
>
> configuration = props.getProperty(prefix +
> ConfigurationProperty);
>
> if (configuration == null) {
> configuration = DefaultConfiguration;
> }
>
> embedded = LayerUtils.booleanFromProperties(props, prefix
> + EmbeddedProperty, embedded);
> deleteLayers = LayerUtils.booleanFromProperties(props, prefix
> + DeleteLayersProperty, deleteLayers);
> addLayers = LayerUtils.booleanFromProperties(props, prefix
> + AddLayersProperty, addLayers);
>
> String orient = props.getProperty(prefix +
> OrientationProperty);
> if (orient != null
> && (orient.equalsIgnoreCase(HORIZONTAL_CONFIG) ||
> (orient.equalsIgnoreCase("false")))) {
> orientation = BoxLayout.X_AXIS;
> }
> }
>
> public Properties getProperties(Properties props) {
> props = super.getProperties(props);
> String prefix = PropUtils.getScopedPropertyPrefix(this);
>
> props.put(prefix + ConfigurationProperty, configuration);
> props.put(prefix + OrientationProperty,
> (orientation == BoxLayout.X_AXIS ? HORIZONTAL_CONFIG
> : VERTICAL_CONFIG));
> props.put(prefix + EmbeddedProperty, new
> Boolean(embedded).toString());
> props.put(prefix + DeleteLayersProperty,
> new Boolean(deleteLayers).toString());
> props.put(prefix + AddLayersProperty, new
> Boolean(addLayers).toString());
> return props;
> }
>
> public Properties getPropertyInfo(Properties props) {
> props = super.getPropertyInfo(props);
>
> props.put(ConfigurationProperty,
> "Pre-Defined Configuration String (WEST, EAST, NORTH,
> SOUTH, NORTH_SOUTH).");
> props.put(OrientationProperty, "Horizontal or Vertical.");
> props.put(OrientationProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.OrientationPropertyEditor");
> props.put(EmbeddedProperty, "Insert itself into LayersPanel.");
> props.put(EmbeddedProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> props.put(DeleteLayersProperty, "Include button to delete
> layers.");
> props.put(DeleteLayersProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> props.put(AddLayersProperty, "Include button to add layers.");
> props.put(AddLayersProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> return props;
> }
>
> }//
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/
> LayersPanel.java,v $
> // $RCSfile: LayersPanel.java,v $
> // $Revision: 1.2 $
> // $Date: 2003/10/03 00:47:11 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.gui;
>
> import java.awt.*;
> import java.awt.event.*;
> import java.beans.*;
> import java.io.Serializable;
> import java.util.Hashtable;
> import java.util.Iterator;
> import java.util.LinkedList;
> import java.util.Properties;
>
> import javax.swing.*;
>
> import com.bbn.openmap.*;
> import com.bbn.openmap.layer.util.LayerUtils;
> import com.bbn.openmap.event.LayerEvent;
> import com.bbn.openmap.event.LayerListener;
> import com.bbn.openmap.util.ComponentFactory;
> import com.bbn.openmap.util.Debug;
> import com.bbn.openmap.util.PropUtils;
>
> /**
> * The LayersPanel displays the list of layers that OpenMap can
> * display. The layer name is displayed accompanied by an on/off
> * button and a tool palette button. Pressing the on/off button will
> * cause the the map to display/remove the layer. Pressing the tool
> * palette button will cause a window to be displayed containing
> * widgets specific to that layer.
> * <p>
> *
> * The order of the layers in the list reflects the order that the
> * layers are displayed on the map, with the bottom-most layer listed
> * on the panel underneath all the the other layers displayed on the
> * map. The order of the layers is determined by their order in the
> * Layer[] passed in the setLayers method.
> * <p>
> *
> * The order of the layers can be changed by sending the LayersPanel
> * an ActionEvent with one of the string commands in the class, or by
> * sending a PropertyChangeEvent with a command and a Layer as the new
> * value.
> * <P>
> *
> * In the standard GUI, the order can be changed by selecting a layer
> * by clicking on the layer's name (or on either of buttons), then
> * clicking on one of the four buttons on the left side of the panel.
> * The four buttons signify, from top to bottom: Move the selected
> * layer to the top; Move the selected layer up one position; Move the
> * selected layer down one position; Move the selected layer to the
> * bottom.
> * <P>
> *
> * The LayersPanel can be used within a BeanContext. If it is added to
> * a BeanConext, it will look for a LayerHandler to add itself to as a
> * LayerListener. The LayersPanel can only listen to one LayerHandler,
> * so if more than one is found, only the last one found will be used.
> * If another LayerHandler is added to the BeanContext later, the new
> * LayerHandler will be used. The LayersPanel is also considered to be
> * a Tool, which will cause a button that will bring up the
> * LayersPanel to be automatically added to the ToolPanel if a
> * ToolPanel is part of the BeanContext.
> * <P>
> *
> * When the LayersPanel discovers a BufferedLayerMapBean is being
> * used, it adds a special LayerPane to its LayerPane list that shows
> * which layers are being buffered in the MapBean. This special
> * LayerPane shows up as a line in the list, and all layers below that
> * line are being specially buffered by the BufferedLayerMapBean.
> * <P>
> *
> * The properties that can be set for the LayersPanel:
> *
> * <pre>
> *
> *
> *
> * # Use LayerStatusPanes for the layers if true, otherwise
> * # LayerPanes. LayerStatusPanes turn the on/off bulbs to
> green/red
> * # bulbs when the layer is resting/working. LayerPanes just show
> * # yellow bulbs when the layer is part of the map.
> * showStatus=true
> * # Specify the behavior of palette button. If false (default)
> * # the button behaves as a simple button. If true it behaves as
> * # a check box.
> * paletteButtonAsCheckBox=true
> * # When the BufferedLayerMapBean is used, a divider will be
> * # displayed in the list of layers showing which layers are in the
> * # MapBean buffer (below the line). Commands to move layers, by
> * # default, respect this divider, requiring more commands to have
> * # layers cross it.
> * boundary=true
> * # Add control buttons - use &quot;none&quot; for no button. If
> undefined,
> * # the LayerControlButtonPanel will be created automatically.
> * controls=com.bbn.openmap.gui.LayerControlButtonPanel
> * # Any control properties added here, prepended by
> &quot;controls&quot;...
> * controls.configuration=WEST
> *
> *
> *
> * </pre>
> */
> public class LayersPanel extends OMToolComponent implements
> Serializable,
> ActionListener, LayerListener, PropertyChangeListener {
> /** Action command for the layer order buttons. */
> public final static String LayerTopCmd = "LayerTopCmd";
> /** Action command for the layer order buttons. */
> public final static String LayerBottomCmd = "LayerBottomCmd";
> /** Action command for the layer order buttons. */
> public final static String LayerUpCmd = "LayerUpCmd";
> /** Action command for the layer order buttons. */
> public final static String LayerDownCmd = "LayerDownCmd";
> /** Action command removing a layer. */
> public final static String LayerRemoveCmd = "LayerRemoveCmd";
> /** Action command adding a layer. */
> public final static String LayerAddCmd = "LayerAddCmd";
> /** Action command for notification that a layer has been
> selected. */
> public final static String LayerSelectedCmd = "LayerSelected";
> /**
> * Action command for notification that a layer has been
> * deselected. Not so reliable. Usually a selection notification
> * means that others are deselected.
> */
> public final static String LayerDeselectedCmd = "LayerDeselected";
>
> /**
> * A property to set the class to create for layer order controls.
> * If undefined, a LayerControlButtonPanel in its default
> * configuration will be created. For no controls added, use
> * (none) for this property.
> */
> public final static String ControlButtonsProperty = "controls";
> /**
> * A property that can be used for controlling how the to top and
> * to bottom cammands will be interpreted when a
> * BufferedLayerMapBean is used. See the definition of
> * bufferedBoundary.
> */
> public final static String BufferedBoundaryProperty = "boundary";
> /**
> * A property that can be used for controlling what type of
> * LayerPanes are used. If true (default) a LayerStatusPane will
> * be created for each layer. Otherwise, a LayerPane will be used.
> */
> public final static String ShowStatusProperty = "showStatus";
> /**
> * A property that can be used for controlling behavior of
> * palette button.
> */
> public final static String PaletteButtonAsCheckBoxProperty =
> "PaletteButtonAsCheckBox";
>
> /**
> * A value for the (controls) property to not include control
> * buttons in the interface.
> */
> public final static String NO_CONTROLS = "none";
>
> /** Default key for the LayersPanel Tool. */
> public final static String defaultKey = "layerspanel";
>
> /**
> * The LayerHandler to listen to for LayerEvents, and also to
> * notify if the layer order should change.
> */
> protected transient LayerHandler layerHandler = null;
> /**
> * Panel that lets you dynamically add and configure layers.
> */
> protected transient LayerAddPanel layerAddPanel = null;
> /**
> * The components holding the layer name label, the on/off
> * indicator and on button, and the palette on/off indicator and
> * palette on button.
> */
> protected transient LinkedList panes;
> /** The internal component that holds the panes. */
> protected transient JPanel panesPanel;
> /** The scroll pane to use for panes. */
> protected transient JScrollPane scrollPane;
> /** The Layer order adjustment button group. */
> protected transient ButtonGroup bg;
> /** The ActionListener that will bring up the LayersPanel. */
> protected ActionListener actionListener;
> /**
> * The frame used when the LayersPanel is used in an application
> * and the actionListener is called.
> */
> protected transient JFrame layersWindowFrame;
> /**
> * The frame used when the LayersPanel is used in an applet and
> * the actionListener is called.
> */
> protected transient JInternalFrame layersWindow;
> /** The set of buttons that control the layers. */
> protected LayerControlButtonPanel controls = null;
> /**
> * Hashtable that tracks LayerPanes for layers, with the layer as
> * the key and LayerPane as the value.
> */
> protected Hashtable paneLookUp = new Hashtable();
> /**
> * A special LayerPane used when the LayersPanel senses that a
> * BufferedLayerMapBean is being used. This LayersPanel is a
> * separating line showing which layers are part of the MapBean's
> * buffer, and which are not.
> */
> protected LayerPane backgroundLayerSeparator = null;
> /**
> * Behavior flag so that if there is a background buffered layer
> * on the MapBean, and a buffered layer divider in the
> * LayersPanel, whether commands instructing a layer to the top or
> * bottom of the list should honor the virtual boundary between
> * buffered and unbuffered layers. That is, if a layer is on the
> * bottom of the buffered list and is instructed to go to the top
> * of the overal list, it will only first travel to the top of the
> * buffered layers. On a subsequent top command, it will go to the
> * top of the list. The same behavior applies for going down. True
> * is default. If set to false, these commands will just send the
> * selected layer to the top and bottom of the entire list.
> */
> protected boolean bufferedBoundary = true;
>
> /**
> * Behavior flag that determines what kind of LayerPane is used
> * for the layers. If true (default) the LayerStatusPane will be
> * used. Otherwise, the LayerPane will be used instead.
> */
> protected boolean showStatus = true;
>
> /**
> * Behavior flag that determines how palette button should work:
> * as a check box or as a simple button. If false (default)
> * it behaves as a simple button.
> */
> protected boolean paletteButtonAsCheckBox = false;
> /**
> * Construct the LayersPanel.
> */
> public LayersPanel() {
> super();
> setKey(defaultKey);
> setLayout(new BorderLayout());
> setWindowSupport(new WindowSupport(this, "Layers"));
> }
>
> /**
> * Construct the LayersPanel.
> *
> * _at_param lHandler the LayerHandler controlling the layers.
> */
> public LayersPanel(LayerHandler lHandler) {
> this();
> setLayerHandler(lHandler);
> }
>
> /**
> * Set the LayerHandler that the LayersPanel listens to. If the
> * LayerHandler passed in is not null, the LayersMenu will be
> * added to the LayerHandler LayerListener list, and the
> * LayersMenu will receive a LayerEvent with the current layers.
> * <P>
> *
> * If there is a LayerHandler that is already being listened to,
> * then the LayersPanel will remove itself from current
> * LayerHandler as a LayerListener, before adding itself to the
> * new LayerHandler.
> * <P>
> *
> * Lastly, if the LayerHandler passed in is null, the LayersPanel
> * will disconnect itself from any LayerHandler currently held,
> * and reset itself with no layers.
> *
> * _at_param lh LayerHandler to listen to, and to use to reorder the
> * layers.
> */
> public void setLayerHandler(LayerHandler lh) {
> if (layerHandler != null) {
> layerHandler.removeLayerListener(this);
> }
> layerHandler = lh;
> if (layerHandler != null) {
> layerHandler.addLayerListener(this);
> } else {
> setLayers(new Layer[0]);
> }
> updateLayerPanes(layerHandler);
> }
>
> /**
> * Get the LayerHandler that the LayersPanel listens to and uses
> * to reorder layers.
> *
> * _at_return LayerHandler.
> */
> public LayerHandler getLayerHandler() {
> return layerHandler;
> }
>
> /**
> * Set the layerpanes with the given layerhandler
> *
> * _at_param layerHandler The LayerHandler controlling the layers
> */
> protected void updateLayerPanes(LayerHandler layerHandler) {
> Iterator it = getPanes().iterator();
> while (it.hasNext()) {
> ((LayerPane) it.next()).setLayerHandler(layerHandler);
> }
> }
>
> /**
> * LayerListener interface method. A list of layers will be added,
> * removed, or replaced based on on the type of LayerEvent. The
> * LayersPanel only reacts to LayerEvent.ALL events, to reset the
> * components in the LayersPanel.
> *
> * _at_param evt a LayerEvent.
> */
> public void setLayers(LayerEvent evt) {
> Layer[] layers = evt.getLayers();
> int type = evt.getType();
>
> if (type == LayerEvent.ALL) {
> Debug.message("layerspanel", "LayersPanel received layers
> update");
> setLayers(layers);
> }
> }
>
> /**
> * Tool interface method. The retrieval tool's interface. This
> * method creates a button that will bring up the LayersPanel.
> *
> * _at_return String The key for this tool.
> */
> public Container getFace() {
> JButton layerButton = null;
>
> if (getUseAsTool()) {
> layerButton = new JButton(new
> ImageIcon(OMToolSet.class.getResource("layers.gif"), "Layer
> Controls"));
> layerButton.setBorderPainted(false);
> layerButton.setToolTipText("Layer Controls");
> layerButton.setMargin(new Insets(0, 0, 0, 0));
> layerButton.addActionListener(getActionListener());
> }
>
> return layerButton;
> }
>
> /**
> * Get the ActionListener that triggers the LayersPanel. Useful to
> * have to provide an alternative way to bring up the LayersPanel.
> *
> * _at_return ActionListener
> */
> public ActionListener getActionListener() {
> return new ActionListener() {
> public void actionPerformed(ActionEvent evt) {
> WindowSupport ws = getWindowSupport();
> int w = 328;
> int h = 300;
>
> Dimension dim = ws.getComponentSize();
> if (dim != null) {
> w = (int) dim.getWidth();
> h = (int) dim.getHeight();
> }
>
> int x = 10;
> int y = 10;
>
> Point loc = ws.getComponentLocation();
> if (loc != null) {
> x = (int) loc.getX();
> y = (int) loc.getY();
> }
>
> MapHandler mh = (MapHandler) getBeanContext();
> Frame frame = null;
> if (mh != null) {
> frame = (Frame) mh.get(java.awt.Frame.class);
> }
>
> ws.displayInWindow(frame, x, y, w, h);
> }
> };
> }
>
> /**
> * Set the layers that are in the LayersPanel. Make sure that the
> * layer[] is the same as that passed to any other OpenMap
> * component, like the LayersMenu. This method checks to see if
> * the layer[] has actually changed, in order or in size. If it
> * has, then createPanel() is called to rebuild the LayersPanel.
> *
> * _at_param inLayers the array of layers.
> */
> public void setLayers(Layer[] inLayers) {
> Layer[] layers = inLayers;
>
> if (inLayers == null) {
> layers = new Layer[0];
> }
>
> if (Debug.debugging("layerspanel")) {
> Debug.output("LayersPanel.setLayers() with " +
> layers.length
> + " layers.");
> }
>
> LinkedList panes = getPanes();
> int separatorOffset = 0;
> if (backgroundLayerSeparator != null
> && panes.contains(backgroundLayerSeparator)) {
> separatorOffset = 1;
> }
>
> if (panes.size() - separatorOffset != layers.length) {
> // if the panel hasn't been created yet, or if someone has
> // changed the layers on us, rebuild the panel.
> createPanel(layers);
> return;
> }
>
> int i = 0;
> Iterator it = panes.iterator();
> while (it.hasNext() && i < layers.length) {
> LayerPane pane = (LayerPane) it.next();
>
> if (pane == backgroundLayerSeparator) {
> continue;
> }
>
> if (pane.getLayer() != layers[i]) {
> // If the layer order sways at all, then we start over
> // and rebuild the panel
> createPanel(layers);
> return;
> } else {
> pane.updateLayerLabel();
> }
>
> // Do this just in case someone has changed something
> // somewhere else...
> pane.setLayerOn(layers[i].isVisible());
> i++;
> }
>
> // One last check for a mismatch...
> if (it.hasNext() || i < layers.length) {
> createPanel(layers);
> }
> // If we get here, it means that what we had is what we
> // wanted.
> }
>
> protected LinkedList getPanes() {
> if (panes == null) {
> panes = new LinkedList();
> }
> return panes;
> }
>
> protected void setPanes(LinkedList lpa) {
> panes = lpa;
> }
>
> /**
> * Create the panel that shows the LayerPanes. This method creates
> * the on/off buttons, palette buttons, and layer labels, and adds
> * them to the scrollPane used to display all the layers.
> *
> * _at_param inLayers the Layer[] that reflects all possible layers
> * that can be added to the map.
> */
> public void createPanel(Layer[] inLayers) {
> Debug.message("layerspanel", "LayersPanel.createPanel()");
>
> if (scrollPane != null) {
> remove(scrollPane);
> }
>
> Layer[] layers = inLayers;
> if (layers == null) {
> layers = new Layer[0];
> }
>
> if (panesPanel == null) {
> panesPanel = new JPanel();
> panesPanel.setLayout(new BoxLayout(panesPanel, BoxLayout.Y_AXIS));
> panesPanel.setAlignmentX(LEFT_ALIGNMENT);
> panesPanel.setAlignmentY(BOTTOM_ALIGNMENT);
> } else {
> ((BoxLayout) panesPanel.getLayout()).invalidateLayout(panesPanel);
> panesPanel.removeAll();
> }
>
> if (bg == null) {
> bg = new ButtonGroup();
> }
>
> LinkedList panes = new LinkedList();
> LinkedList backgroundPanes = new LinkedList();
>
> // populate the arrays of CheckBoxes and strings used to fill
> // the JPanel for the panes
> for (int i = 0; i < layers.length; i++) {
> Layer layer = layers[i];
> if (layer == null) {
> Debug.output("LayersPanel caught null layer, " + i + "
> out of "
> + layers.length);
> continue;
> }
>
> LayerPane lpane = (LayerPane) paneLookUp.get(layer);
>
> if (lpane == null) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel: Creating LayerPane for "
> + layer.getName());
> }
> lpane = createLayerPaneForLayer(layer, layerHandler,
> bg);
> lpane.addPropertyChangeListener(LayerSelectedCmd,
> this);
> lpane.addPropertyChangeListener(LayerDeselectedCmd,
> this);
> paneLookUp.put(layer, lpane);
> } else {
> // In case this has been modified elsewhere...
> lpane.setLayerOn(layer.isVisible());
> }
>
> if (layer.getAddAsBackground() && backgroundLayerSeparator
> != null) {
> backgroundPanes.add(lpane);
> } else {
> panes.add(lpane);
> panesPanel.add(lpane);
> }
> }
>
> if (backgroundPanes.size() != 0) {
> if (Debug.debugging("layerspanel")) {
> Debug.output("Adding BackgroundLayerSeparator");
> }
> panes.add(backgroundLayerSeparator);
> panesPanel.add(backgroundLayerSeparator);
> panes.addAll(backgroundPanes);
>
> Iterator it = backgroundPanes.iterator();
> while (it.hasNext()) {
> panesPanel.add((LayerPane) it.next());
> }
>
> } else if (backgroundLayerSeparator != null) {
> if (Debug.debugging("layerspanel")) {
> Debug.output("No layers are background layers, adding
> separator");
> }
> panes.add(backgroundLayerSeparator);
> panesPanel.add(backgroundLayerSeparator);
> }
>
> setPanes(panes);
>
> if (scrollPane != null) {
> remove(scrollPane);
> scrollPane.removeAll();
> scrollPane = null;
> }
>
> scrollPane = new JScrollPane(panesPanel,
> ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
> ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
>
> add(scrollPane, BorderLayout.CENTER);
> revalidate();
> }
>
> /**
> * Called when a new LayerPane needs to be created for a layer.
> * You can use this to extend LayerPane and return something else
> * that fits your GUI.
> */
> protected LayerPane createLayerPaneForLayer(Layer layer,
> LayerHandler layerHandler,
> ButtonGroup bg) {
> if (showStatus) {
> return new LayerStatusPane(layer, layerHandler, bg);
> } else {
> return new LayerPane(layer, layerHandler, bg);
> }
> }
>
> public void deletePanes(LinkedList dpanes) {
> Debug.message("layerspanel", "LayersPanel.deletePanes()");
> if (dpanes != null) {
> paneLookUp.clear();
> Iterator it = dpanes.iterator();
> while (it.hasNext()) {
> LayerPane pane = (LayerPane) it.next();
> if (pane != null && pane != backgroundLayerSeparator) {
> pane.removePropertyChangeListener(this);
> pane.cleanup(bg);
> }
> }
> }
>
> // Shouldn't call this, but it's the only thing
> // that seems to make it work...
> if (Debug.debugging("helpgc")) {
> System.gc();
> }
> }
>
> /**
> * Set up the buttons used to move layers up and down, or
> * add/remove layers. The button component should hook itself up
> * to the LayersPanel, and assume that the LayersPanel has a
> * BorderLayout with the list in the center spot.
> */
> protected void createControlButtons() {
> controls = new LayerControlButtonPanel(this);
> }
>
> /**
> * Set the LayerControlButtonPanel as the controls to this
> * LayersPanel. This method is just settng the controls variable
> * to point to the panel. No further hookups are made.
> *
> * _at_param lpb
> */
> public void setControls(LayerControlButtonPanel lpb) {
> controls = lpb;
> }
>
> public LayerControlButtonPanel getControls() {
> return controls;
> }
>
> /**
> * Method associated with the ActionListener interface. This
> * method listens for action events meant to change the order of
> * the layers, as fired by the layer order buttons.
> *
> * _at_param e ActionEvent
> */
> public void actionPerformed(java.awt.event.ActionEvent e) {
> String command = e.getActionCommand();
> if (Debug.debugging("layerspanel")) {
> Debug.output("LayersPanel.actionPerformed(): " + command);
> }
>
> try {
> LayerPane pane = findSelectedPane();
> if (pane != null) {
> moveLayer(pane, command);
> }
> } catch (NullPointerException npe) {
> } catch (ArrayIndexOutOfBoundsException aioobe) {
> }
> }
>
> /**
> * Change a layer's position.
> */
> public void moveLayer(Layer layer, String command) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel.moveLayer(): " + command + " for
> "
> + layer.getName());
> }
>
> moveLayer((LayerPane) paneLookUp.get(layer), command);
> }
>
>
> /**
> * Change a layer's position, with the layer represented by a
> * LayerPane.
> */
> protected void moveLayer(LayerPane lp, String command) {
>
> if (lp == null) {
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel.moveLayer(): LayerPane not
> represented on list");
> }
>
> if (command == LayerRemoveCmd) {
> // OK, here's a hidden trick. If no layers are
> // selected
> // and the minus sign is clicked, then this is called.
> System.gc();
> }
> return;
> }
>
> LinkedList panes = getPanes();
> int row = panes.indexOf(lp);
>
> boolean boundary = false;
> int bls_row = -1;
> if (backgroundLayerSeparator != null) {
> bls_row = panes.indexOf(backgroundLayerSeparator);
> boundary = bufferedBoundary;
> }
>
> if (command.equals(LayerTopCmd)) {
> // Move layer selected layer to top
> panes.remove(lp);
> if (boundary && bls_row > 0 && row > bls_row + 1) {
> // If the backgroundLayerSeparator is more than one
> // above it, move to just below it on the first top
> // command.
> panes.add(bls_row + 1, lp);
> } else {
> panes.addFirst(lp);
> }
>
> rejiggerMapLayers();
> } else if (command.equals(LayerBottomCmd)) {
> // Move layer selected layer to bottom
> panes.remove(lp);
>
> if (boundary && bls_row > 0 && row < bls_row - 1) {
> // If the backgroundLayerSeparator is more than one
> // below it, move to just above it on the first top
> // command.
> panes.add(bls_row - 1, lp);
> } else {
> panes.addLast(lp);
> }
>
> rejiggerMapLayers();
> } else if (command.equals(LayerUpCmd)) {
> // Move layer selected layer up one
> if (row <= 0)
> return;
> panes.remove(row);
> panes.add(row - 1, lp);
> rejiggerMapLayers();
> } else if (command.equals(LayerDownCmd)) {
> // Move layer selected layer up one
> if (row < 0 || row == panes.size() - 1)
> return;
> panes.remove(row);
> panes.add(row + 1, lp);
> rejiggerMapLayers();
> } else if (command.equals(LayerRemoveCmd)) {
>
> if (layerHandler == null ||
> !lp.getLayer().removeConfirmed()) {
> return;
> }
>
> // This order is somewhat important. lp.getLayer() will
> // be null after lp.cleanup. lp.setSelected() will cause
> // a series of property change notifications.
> lp.setSelected(false);
> lp.getLayer().setPaletteVisible(false);
> paneLookUp.remove(lp.getLayer());
> layerHandler.removeLayer(lp.getLayer());
> lp.cleanup(bg);
>
> // Shouldn't call this, but it's the only thing
> // that seems to make it work...
> if (Debug.debugging("helpgc")) {
> System.gc();
> }
>
> return;
>
> } else if (command.equals(LayerAddCmd)) {
> if (layerAddPanel != null) {
> layerAddPanel.showPanel();
> }
> }
> }
>
> /**
> * Find the selected LayerPane in the current LayerPane list. Will
> * return null if there isn't a selected pane.
> */
> protected LayerPane findSelectedPane() {
> Iterator it = getPanes().iterator();
> while (it.hasNext()) {
> LayerPane pane = (LayerPane) it.next();
> if (pane.isSelected()) {
> return pane;
> }
> }
> return null;
> }
>
> /**
> * Makes a new layer cake of active layers to send to
> * LayerHandler.setLayers().
> */
> protected void rejiggerMapLayers() {
> Debug.message("layerspanel",
> "LayersPanel.rejiggerMapLayers()");
>
> if (layerHandler == null) {
> // Why bother doing anything??
> return;
> }
>
> int selectedRow = -1;
>
> panesPanel.removeAll();
>
> LinkedList panes = getPanes();
> LinkedList layerList = new LinkedList();
>
> int bufferIndex = Integer.MAX_VALUE;
>
> int i = 0; // track layer index
> Iterator it = panes.iterator();
> while (it.hasNext()) {
>
> LayerPane pane = (LayerPane) it.next();
>
> if (pane == backgroundLayerSeparator) {
> panesPanel.add(backgroundLayerSeparator);
> bufferIndex = i++;
> continue;
> }
>
> Layer layer = pane.getLayer();
> layer.setAddAsBackground(i > bufferIndex);
> panesPanel.add(pane);
> layerList.add(layer);
>
> if (pane.isSelected()) {
> selectedRow = i;
> }
> i++;
> }
>
> scrollPane.revalidate();
>
> // Scroll up or down as necessary to keep selected row
> // viewable
> if (selectedRow >= 0) {
> int spheight = scrollPane.getHeight();
> JScrollBar sb = scrollPane.getVerticalScrollBar();
> int sv = sb.getValue();
> int paneheight = ((LayerPane)
> panes.get(selectedRow)).getHeight();
> int rowvalue = selectedRow * paneheight;
> // Don't reset scrollBar unless the selected row
> // is not in the viewable range
> if (!((rowvalue > sv) && (rowvalue < spheight + sv))) {
> sb.setValue(rowvalue);
> }
> }
>
> Object[] layerArray = layerList.toArray();
> int length = layerArray.length;
> Layer[] newLayers = new Layer[length];
>
> for (int j = 0; j < length; j++) {
> newLayers[j] = (Layer) layerArray[j];
> }
>
> layerHandler.setLayers(newLayers);
> }
>
> /**
> * Update the layer names - if a layer name has changed, tell the
> * LayerPanes to check with their layers to update their labels.
> */
> public synchronized void updateLayerLabels() {
> Iterator it = getPanes().iterator();
> while (it.hasNext()) {
> ((LayerPane) it.next()).updateLayerLabel();
> }
> }
>
> public void propertyChange(PropertyChangeEvent pce) {
> String command = pce.getPropertyName();
> Object obj = pce.getNewValue();
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel receiving PropertyChangeEvent "
> + command
> + ", " + pce.toString());
> }
>
> if ((command == LayerSelectedCmd || command ==
> LayerDeselectedCmd)
> && obj instanceof Layer) {
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel: layer panel notification
> that layer is selected: "
> + ((Layer) obj).getName());
> }
> firePropertyChange(command, null, ((Layer) obj));
>
> } else if ((command == LayersPanel.LayerTopCmd
> || command == LayersPanel.LayerBottomCmd
> || command == LayersPanel.LayerUpCmd
> || command == LayersPanel.LayerDownCmd || command ==
> LayersPanel.LayerRemoveCmd)
> && obj instanceof Layer) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel: layer panel notification
> that layer should be raised: "
> + ((Layer) obj).getName());
> }
> moveLayer((Layer) obj, command);
> }
> }
>
> /**
> * Called when the LayersPanel is added the BeanContext, or when
> * another object is added to the BeanContext after the
> * LayerHandler has been added. This allows the LayersPanel to
> * keep up-to-date with any objects that it may be interested in,
> * namely, the LayerHandler. If a LayerHandler has already been
> * added, the new LayerHandler will replace it.
> *
> * _at_param someObj the object being added to the BeanContext
> */
> public void findAndInit(Object someObj) {
> if (someObj instanceof LayerHandler) {
> // do the initializing that need to be done here
> Debug.message("layerspanel", "LayersPanel found a
> LayerHandler");
> setLayerHandler((LayerHandler) someObj);
> }
>
> if (someObj instanceof BufferedLayerMapBean) {
> if (Debug.debugging("layerspanel")) {
> Debug.output("LayersPanel found BufferedLayerMapBean,
> creating separator panel");
> }
> backgroundLayerSeparator =
> LayerPane.getBackgroundLayerSeparator(" --- Background Layers --- ");
> }
>
> // Don't want to forward ourselves on to controls, supposedly
> // they already know.
> if (controls instanceof LightMapHandlerChild && someObj !=
> this) {
> ((LightMapHandlerChild) controls).findAndInit(someObj);
> }
> }
>
> /**
> * BeanContextMembershipListener method. Called when an object has
> * been removed from the parent BeanContext. If a LayerHandler is
> * removed, and it's the current one being listened to, then the
> * layers in the panel will be wiped clean.
> *
> * _at_param someObj the object being removed from the BeanContext
> */
> public void findAndUndo(Object someObj) {
> if (someObj instanceof LayerHandler) {
> // do the initializing that need to be done here
> Debug.message("layerspanel", "LayersPanel removing
> LayerHandler");
> if (getLayerHandler() == (LayerHandler) someObj) {
> setLayerHandler(null);
> }
> }
>
> // Don't want to forward ourselves on to controls, supposedly
> // they already know.
> if (controls instanceof LightMapHandlerChild && someObj !=
> this) {
> ((LightMapHandlerChild) controls).findAndUndo(someObj);
> }
> }
>
> public void setProperties(String prefix, Properties props) {
> super.setProperties(prefix, props);
> prefix = PropUtils.getScopedPropertyPrefix(prefix);
>
> String controlString = props.getProperty(prefix
> + ControlButtonsProperty);
>
> if (controlString != NO_CONTROLS) {
> if (controlString == null) {
> createControlButtons();
> } else {
> Object obj = ComponentFactory.create(controlString,
> prefix
> + ControlButtonsProperty, props);
>
> if (obj instanceof LayerControlButtonPanel) {
> ((LayerControlButtonPanel)
> obj).setLayersPanel(this);
> }
> }
> }
>
> bufferedBoundary = LayerUtils.booleanFromProperties(props,
> prefix
> + BufferedBoundaryProperty, bufferedBoundary);
> showStatus = LayerUtils.booleanFromProperties(props, prefix
> + ShowStatusProperty, showStatus);
> paletteButtonAsCheckBox =
> LayerUtils.booleanFromProperties(props, prefix +
> PaletteButtonAsCheckBoxProperty, paletteButtonAsCheckBox);
> }
>
> public Properties getProperties(Properties props) {
> props = super.getProperties(props);
>
> String prefix = PropUtils.getScopedPropertyPrefix(this);
> LayerControlButtonPanel controls = getControls();
> if (controls != null) {
> props.put(prefix + ControlButtonsProperty,
> controls.getClass()
> .getName());
> controls.getProperties(props);
> }
> props.put(prefix + BufferedBoundaryProperty,
> new Boolean(bufferedBoundary).toString());
> props.put(prefix + ShowStatusProperty,
> new Boolean(showStatus).toString());
> props.put(prefix + PaletteButtonAsCheckBoxProperty, new
> Boolean(paletteButtonAsCheckBox).toString());
> return props;
> }
>
> public Properties getPropertyInfo(Properties props) {
> props = super.getPropertyInfo(props);
> props.put(ControlButtonsProperty,
> "Class to use for layer control buttons (Optional)");
> LayerControlButtonPanel controls = getControls();
> if (controls != null) {
> controls.getPropertyInfo(props);
> }
> props.put(BufferedBoundaryProperty,
> "Force layer movement to respect background layer
> boundary.");
> props.put(BufferedBoundaryProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> props.put(ShowStatusProperty, "Use Layer Panes that show layer
> status.");
> props.put(ShowStatusProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> props.put(PaletteButtonAsCheckBoxProperty, "If palette button
> should behave as a check box or as a simple button.");
> props.put(PaletteButtonAsCheckBoxProperty +
> ScopedEditorProperty,
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> return props;
> }
> }
>
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/
> NavigatePanel.java,v $
> // $RCSfile: NavigatePanel.java,v $
> // $Revision: 1.4 $
> // $Date: 2003/10/24 23:36:45 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
>
> package com.bbn.openmap.gui;
>
> import java.net.URL;
> import java.io.Serializable;
> import java.awt.*;
> import java.awt.event.*;
> import javax.swing.*;
>
> import com.bbn.openmap.*;
> import com.bbn.openmap.event.*;
> import com.bbn.openmap.util.Debug;
>
>
> /**
> * A Navigation Rosette Bean.
> * This bean is a source for PanEvents and CenterEvents.
> */
> public class NavigatePanel extends OMToolComponent
> implements Serializable, ActionListener {
>
> public final static String panNWCmd = "panNW";
> public final static String panNCmd = "panN";
> public final static String panNECmd = "panNE";
> public final static String panECmd = "panE";
> public final static String panSECmd = "panSE";
> public final static String panSCmd = "panS";
> public final static String panSWCmd = "panSW";
> public final static String panWCmd = "panW";
> public final static String centerCmd = "center";
>
> protected transient JButton nwButton = null;
> protected transient JButton nButton = null;
> protected transient JButton neButton = null;
> protected transient JButton eButton = null;
> protected transient JButton seButton = null;
> protected transient JButton sButton = null;
> protected transient JButton swButton = null;
> protected transient JButton wButton = null;
> protected transient JButton cButton = null;
>
> // default icon names
> protected static String nwName = "nw.gif";
> protected static String nName = "n.gif";
> protected static String neName = "ne.gif";
> protected static String eName = "e.gif";
> protected static String seName = "se.gif";
> protected static String sName = "s.gif";
> protected static String swName = "sw.gif";
> protected static String wName = "w.gif";
> protected static String cName = "center.gif";
>
> protected PanSupport panDelegate;
> protected CenterSupport centerDelegate;
> protected boolean useTips = false;
> protected float panFactor = 1f;
>
> // protected int height = 0; // calculated
> // protected int width = 0; // calculated
>
> protected boolean useDefaultCenter = false;
> protected float defaultCenterLat = 0;
> protected float defaultCenterLon = 0;
>
> public final static String defaultKey = "navigatepanel";
>
> /**
> * Construct the NavigationPanel.
> */
> public NavigatePanel() {
> super();
> setKey(defaultKey);
> panDelegate = new PanSupport(this);
> centerDelegate = new CenterSupport(this);
>
> JPanel panel = new JPanel();
> GridBagLayout internalGridbag = new GridBagLayout();
> GridBagConstraints c2 = new GridBagConstraints();
> panel.setLayout(internalGridbag);
>
> // begin top row
> String info =
> i18n.get(NavigatePanel.class,"panel.northwest","Pan Northwest");
> nwButton = getButton(nwName, info, panNWCmd);
> c2.gridx = 0;
> c2.gridy = 0;
> internalGridbag.setConstraints(nwButton, c2);
> panel.add(nwButton);
>
> info = i18n.get(NavigatePanel.class,"panel.north","Pan North");
> nButton = getButton(nName, info, panNCmd);
> c2.gridx = 1;
> c2.gridy = 0;
> internalGridbag.setConstraints(nButton, c2);
> panel.add(nButton);
>
> info = i18n.get(NavigatePanel.class,"panel.northeast","Pan
> Northeast");
> neButton = getButton(neName, info, panNECmd);
> c2.gridx = 2;
> c2.gridy = 0;
> internalGridbag.setConstraints(neButton, c2);
> panel.add(neButton);
>
> // begin middle row
> info = i18n.get(NavigatePanel.class,"panel.west","Pan West");
> wButton = getButton(wName, info, panWCmd);
> c2.gridx = 0;
> c2.gridy = 1;
> internalGridbag.setConstraints(wButton, c2);
> panel.add(wButton);
>
> info =
> i18n.get(NavigatePanel.class,"panel.centerAtStart","Center Map at
> Starting Coords");
> cButton = getButton(cName, info, centerCmd);
> c2.gridx = 1;
> c2.gridy = 1;
> internalGridbag.setConstraints(cButton, c2);
> panel.add(cButton);
>
> info = i18n.get(NavigatePanel.class,"panel.east","Pan East");
> eButton = getButton(eName, info, panECmd);
> c2.gridx = 2;
> c2.gridy = 1;
> internalGridbag.setConstraints(eButton, c2);
> panel.add(eButton);
>
> // begin bottom row
> info = i18n.get(NavigatePanel.class,"panel.southwest","Pan
> Southwest");
> swButton = getButton(swName, info, panSWCmd);
> c2.gridx = 0;
> c2.gridy = 2;
> internalGridbag.setConstraints(swButton, c2);
> panel.add(swButton);
>
> info = i18n.get(NavigatePanel.class,"panel.south","Pan South");
> sButton = getButton(sName, info, panSCmd);
> c2.gridx = 1;
> c2.gridy = 2;
> internalGridbag.setConstraints(sButton, c2);
> panel.add(sButton);
>
> info = i18n.get(NavigatePanel.class,"panel.southeast","Pan
> Southeast");
> seButton = getButton(seName, info, panSECmd);
> c2.gridx = 2;
> c2.gridy = 2;
> internalGridbag.setConstraints(seButton, c2);
> panel.add(seButton);
>
> add(panel);
> }
>
> /**
> * Add the named button to the panel.
> *
> * _at_param name GIF image name
> * _at_param info ToolTip text
> * _at_param command String command name
> *
> */
> protected JButton getButton(String name, String info, String
> command) {
> URL url = NavigatePanel.class.getResource(name);
> ImageIcon icon = new ImageIcon(url, info);
> JButton b = new JButton(icon);
> b.setPreferredSize(new Dimension(icon.getIconWidth(),
> icon.getIconHeight()));
> b.setToolTipText(info);
> b.setMargin(new Insets(0,0,0,0));
> b.setActionCommand(command);
> b.addActionListener(this);
> b.setBorderPainted(Debug.debugging("layout"));
> b.setOpaque(false);
> return b;
> }
>
> /**
> * Add a CenterListener.
> * _at_param listener CenterListener
> */
> public synchronized void addCenterListener(CenterListener
> listener) {
> centerDelegate.addCenterListener(listener);
> }
>
> /**
> * Remove a CenterListener
> * _at_param listener CenterListener
> */
> public synchronized void removeCenterListener(CenterListener
> listener) {
> centerDelegate.removeCenterListener(listener);
> }
>
> /**
> * Add a PanListener.
> * _at_param listener PanListener
> */
> public synchronized void addPanListener(PanListener listener) {
> panDelegate.addPanListener(listener);
> }
>
> /**
> * Remove a PanListener
> * _at_param listener PanListener
> */
> public synchronized void removePanListener(PanListener listener) {
> panDelegate.removePanListener(listener);
> }
>
> /**
> * Fire a CenterEvent.
> */
> protected synchronized void fireCenterEvent(float lat, float lon) {
> centerDelegate.fireCenter(lat, lon);
> }
>
> /**
> * Fire a PanEvent.
> * _at_param az azimuth east of north
> */
> protected synchronized void firePanEvent(float az) {
> panDelegate.firePan(az);
> }
>
> /**
> * Get the pan factor.
> * <p>
> * The panFactor is the amount of screen to shift
> * when panning in a certain direction: 0=none, 1=half-screen
> shift.
> * _at_return float panFactor (0.0 &lt;= panFactor &lt;= 1.0)
> */
> public float getPanFactor() {
> return panFactor;
> }
>
> /**
> * Set the pan factor.
> * <p>
> * This defaults to 1.0. The panFactor is the amount of screen to
> shift
> * when panning in a certain direction: 0=none, 1=half-screen
> shift.
> * _at_param panFactor (0.0 &lt;= panFactor &lt;= 1.0)
> */
> public void setPanFactor(float panFactor) {
> if ((panFactor < 0f) || (panFactor > 1f)) {
> throw new IllegalArgumentException(
> "should be: (0.0 <= panFactor <= 1.0)");
> }
> this.panFactor = panFactor;
> }
>
> /**
> * Use this function to set where you want the map projection to
> * pan to when the user clicks on "center" button on the
> * navigation panel. The scale does not change. When you call
> * this function, the projection does not change.
> * _at_param passedLat float the center latitude (in degrees)
> * _at_param passedLon float the center longitude (in degrees)
> */
> public void setDefaultCenter(float passedLat, float passedLon) {
> useDefaultCenter = true;
> defaultCenterLat = passedLat;
> defaultCenterLon = passedLon;
> }
>
> /**
> * ActionListener Interface.
> * _at_param e ActionEvent
> */
> public void actionPerformed(java.awt.event.ActionEvent e) {
>
> String command = e.getActionCommand();
>
> Debug.message("navpanel", "NavigatePanel.actionPerformed(): " +
> command);
> if (command.equals(panNWCmd)) {
> firePanEvent(-45f);
> } else if (command.equals(panNCmd)) {
> firePanEvent(0f);
> } else if (command.equals(panNECmd)) {
> firePanEvent(45f);
> } else if (command.equals(panECmd)) {
> firePanEvent(90f);
> } else if (command.equals(panSECmd)) {
> firePanEvent(135f);
> } else if (command.equals(panSCmd)) {
> firePanEvent(180f);
> } else if (command.equals(panSWCmd)) {
> firePanEvent(-135f);
> } else if (command.equals(panWCmd)) {
> firePanEvent(-90f);
> } else if (command.equals(centerCmd)) {
> // go back to the center point
>
> float lat;
> float lon;
> if (useDefaultCenter) {
> lat = defaultCenterLat;
> lon = defaultCenterLon;
> } else {
> lat = Environment.getFloat(Environment.Latitude, 0f);
> lon = Environment.getFloat(Environment.Longitude, 0f);
> }
> fireCenterEvent(lat, lon);
> }
> }
>
>
> //////////////////////////////////////////////////////////////////////
> /////
> //// OMComponentPanel methods to make the tool work with
> //// the MapHandler to find objects it needs.
>
> //////////////////////////////////////////////////////////////////////
> /////
>
> public void findAndInit(Object obj) {
> if (obj instanceof PanListener) {
> addPanListener((PanListener)obj);
> }
> if (obj instanceof CenterListener) {
> addCenterListener((CenterListener)obj);
> }
> }
>
> public void findAndUndo(Object obj) {
> if (obj instanceof PanListener) {
> removePanListener((PanListener)obj);
> }
> if (obj instanceof CenterListener) {
> removeCenterListener((CenterListener)obj);
> }
> }
>
> }
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/
> LayersPanel.java,v $
> // $RCSfile: LayersPanel.java,v $
> // $Revision: 1.2 $
> // $Date: 2003/10/03 00:47:11 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
>
> package com.bbn.openmap.gui;
>
> import java.awt.*;
> import java.awt.event.*;
> import java.beans.*;
> import java.beans.beancontext.*;
> import java.io.Serializable;
> import java.net.URL;
> import java.util.Hashtable;
> import java.util.Iterator;
> import java.util.LinkedList;
> import java.util.Properties;
>
> import javax.swing.*;
> import javax.swing.event.InternalFrameEvent;
> import javax.accessibility.*;
>
> import com.bbn.openmap.*;
> import com.bbn.openmap.layer.util.LayerUtils;
> import com.bbn.openmap.event.LayerEvent;
> import com.bbn.openmap.event.LayerListener;
> import com.bbn.openmap.event.LayerSupport;
> import com.bbn.openmap.util.ComponentFactory;
> import com.bbn.openmap.util.Debug;
> import com.bbn.openmap.util.PropUtils;
>
> /**
> * The LayersPanel displays the list of layers that OpenMap can
> display.
> * The layer name is displayed accompanied by an on/off button and a
> * tool palette button. Pressing the on/off button will cause the the
> * map to display/remove the layer. Pressing the tool palette button
> * will cause a window to be displayed containing widgets specific to
> * that layer. <p>
> *
> * The order of the layers in the list reflects the order that the
> * layers are displayed on the map, with the bottom-most layer listed
> * on the panel underneath all the the other layers displayed on the
> * map. The order of the layers is determined by their order in the
> * Layer[] passed in the setLayers method. <p>
> *
> * The order of the layers can be changed by sending the LayersPanel
> * an ActionEvent with one of the string commands in the class, or by
> * sending a PropertyChangeEvent with a command and a Layer as the new
> * value. <P>
> *
> * In the standard GUI, the order can be changed by selecting a layer
> * by clicking on the layer's name (or on either of buttons), then
> * clicking on one of the four buttons on the left side of the panel.
> * The four buttons signify, from top to bottom: Move the selected
> * layer to the top; Move the selected layer up one position; Move the
> * selected layer down one position; Move the selected layer to the
> * bottom. <P>
> *
> * The LayersPanel can be used within a BeanContext. If it is added
> * to a BeanConext, it will look for a LayerHandler to add itself to
> * as a LayerListener. The LayersPanel can only listen to one
> * LayerHandler, so if more than one is found, only the last one found
> * will be used. If another LayerHandler is added to the BeanContext
> * later, the new LayerHandler will be used. The LayersPanel is also
> * considered to be a Tool, which will cause a button that will bring
> * up the LayersPanel to be automatically added to the ToolPanel if a
> * ToolPanel is part of the BeanContext.<P>
> *
> * When the LayersPanel discovers a BufferedLayerMapBean is being
> * used, it adds a special LayerPane to its LayerPane list that shows
> * which layers are being buffered in the MapBean. This special
> * LayerPane shows up as a line in the list, and all layers below that
> * line are being specially buffered by the BufferedLayerMapBean. <P>
> *
> * The properties that can be set for the LayersPanel: <pre>
> * # Use LayerStatusPanes for the layers if true, otherwise
> * # LayerPanes. LayerStatusPanes turn the on/off bulbs to green/red
> * # bulbs when the layer is resting/working. LayerPanes just show
> * # yellow bulbs when the layer is part of the map.
> * showStatus=true
> * # Specify the behavior of palette button. If false (default)
> * # the button behaves as a simple button. If true it behaves as
> * # a check box.
> * paletteButtonAsCheckBox=true
> * # When the BufferedLayerMapBean is used, a divider will be
> * # displayed in the list of layers showing which layers are in the
> * # MapBean buffer (below the line). Commands to move layers, by
> * # default, respect this divider, requiring more commands to have
> * # layers cross it.
> * boundary=true
> * # Add control buttons - use "none" for no button. If undefined,
> * # the LayerControlButtonPanel will be created automatically.
> * controls=com.bbn.openmap.gui.LayerControlButtonPanel
> * # Any control properties added here, prepended by "controls"...
> * controls.configuration=WEST
> * </pre>
> */
> public class LayersPanel extends OMToolComponent
> implements Serializable, ActionListener, LayerListener,
> PropertyChangeListener
> {
> /** Action command for the layer order buttons. */
> public final static String LayerTopCmd = "LayerTopCmd";
> /** Action command for the layer order buttons. */
> public final static String LayerBottomCmd = "LayerBottomCmd";
> /** Action command for the layer order buttons. */
> public final static String LayerUpCmd = "LayerUpCmd";
> /** Action command for the layer order buttons. */
> public final static String LayerDownCmd = "LayerDownCmd";
> /** Action command removing a layer. */
> public final static String LayerRemoveCmd = "LayerRemoveCmd";
> /** Action command adding a layer. */
> public final static String LayerAddCmd = "LayerAddCmd";
> /** Action command for notification that a layer has been
> selected. */
> public final static String LayerSelectedCmd = "LayerSelected";
> /**
> * Action command for notification that a layer has been
> * deselected. Not so reliable. Usually a selection notification
> * means that others are deselected.
> */
> public final static String LayerDeselectedCmd = "LayerDeselected";
>
> /**
> * A property to set the class to create for layer order controls.
> * If undefined, a LayerControlButtonPanel in its default
> configuration
> * will be created. For no controls added, use (none) for this
> * property.
> */
> public final static String ControlButtonsProperty = "controls";
> /**
> * A property that can be used for controlling how the to top and
> * to bottom cammands will be interpreted when a
> * BufferedLayerMapBean is used. See the definition of
> * bufferedBoundary.
> */
> public final static String BufferedBoundaryProperty = "boundary";
> /**
> * A property that can be used for controlling what type of
> * LayerPanes are used. If true (default) a LayerStatusPane will
> * be created for each layer. Otherwise, a LayerPane will be
> * used.
> */
> public final static String ShowStatusProperty = "showStatus";
> /**
> * A property that can be used for controlling behavior of
> * palette button.
> */
> public final static String PaletteButtonAsCheckBoxProperty =
> "PaletteButtonAsCheckBox";
>
> /**
> * A value for the (controls) property to not include control
> * buttons in the interface.
> */
> public final static String NO_CONTROLS = "none";
>
> /** Default key for the LayersPanel Tool. */
> public final static String defaultKey = "layerspanel";
>
> /**
> * The LayerHandler to listen to for LayerEvents, and also to
> * notify if the layer order should change.
> */
> protected transient LayerHandler layerHandler = null;
> /**
> * Panel that lets you dynamically add and configure layers.
> */
> protected transient LayerAddPanel layerAddPanel = null;
> /**
> * The components holding the layer name label, the on/off
> * indicator and on button, and the palette on/off indicator and
> * palette on button.
> */
> protected transient LinkedList panes;
> /** The internal component that holds the panes. */
> protected transient JPanel panesPanel;
> /** The scroll pane to use for panes. */
> protected transient JScrollPane scrollPane;
> /** The Layer order adjustment button group. */
> protected transient ButtonGroup bg;
> /** The ActionListener that will bring up the LayersPanel. */
> protected ActionListener actionListener;
> /**
> * The frame used when the LayersPanel is used in an application
> * and the actionListener is called.
> */
> protected transient JFrame layersWindowFrame;
> /**
> * The frame used when the LayersPanel is used in an applet and
> * the actionListener is called.
> */
> protected transient JInternalFrame layersWindow;
> /** The set of buttons that control the layers. */
> protected LayerControlButtonPanel controls = null;
> /**
> * Hashtable that tracks LayerPanes for layers, with the layer as
> * the key and LayerPane as the value.
> */
> protected Hashtable paneLookUp = new Hashtable();
> /**
> * A special LayerPane used when the LayersPanel senses that a
> * BufferedLayerMapBean is being used. This LayersPanel is a
> * separating line showing which layers are part of the MapBean's
> * buffer, and which are not.
> */
> protected LayerPane backgroundLayerSeparator = null;
> /**
> * Behavior flag so that if there is a background buffered layer
> * on the MapBean, and a buffered layer divider in the
> * LayersPanel, whether commands instructing a layer to the top or
> * bottom of the list should honor the virtual boundary between
> * buffered and unbuffered layers. That is, if a layer is on the
> * bottom of the buffered list and is instructed to go to the top
> * of the overal list, it will only first travel to the top of the
> * buffered layers. On a subsequent top command, it will go to
> * the top of the list. The same behavior applies for going down.
> * True is default. If set to false, these commands will just
> * send the selected layer to the top and bottom of the entire
> * list.
> */
> protected boolean bufferedBoundary = true;
>
> /**
> * Behavior flag that determines what kind of LayerPane is used
> * for the layers. If true (default) the LayerStatusPane will be
> * used. Otherwise, the LayerPane will be used instead.
> */
> protected boolean showStatus = true;
>
> /**
> * Behavior flag that determines how palette button should work:
> * as a check box or as a simple button. If false (default)
> * it behaves as a simple button.
> */
> protected boolean paletteButtonAsCheckBox = false;
> /**
> * Construct the LayersPanel.
> */
> public LayersPanel() {
> super();
> setKey(defaultKey);
> setLayout(new BorderLayout());
> setWindowSupport(new WindowSupport(this, "Layers"));
> }
>
> /**
> * Construct the LayersPanel.
> *
> * _at_param lHandler the LayerHandler controlling the layers.
> */
> public LayersPanel(LayerHandler lHandler) {
> this();
> setLayerHandler(lHandler);
> }
>
> /**
> * Set the LayerHandler that the LayersPanel listens to. If the
> * LayerHandler passed in is not null, the LayersMenu will be
> * added to the LayerHandler LayerListener list, and the
> * LayersMenu will receive a LayerEvent with the current
> * layers. <P>
> *
> * If there is a LayerHandler that is already being listened to,
> * then the LayersPanel will remove itself from current
> LayerHandler
> * as a LayerListener, before adding itself to the new
> LayerHandler. <P>
> *
> * Lastly, if the LayerHandler passed in is null, the LayersPanel
> * will disconnect itself from any LayerHandler currently held,
> * and reset itself with no layers.
> *
> * _at_param lh LayerHandler to listen to, and to use to reorder the
> * layers.
> */
> public void setLayerHandler(LayerHandler lh) {
> if (layerHandler != null) {
> layerHandler.removeLayerListener(this);
> }
> layerHandler = lh;
> if (layerHandler != null) {
> layerHandler.addLayerListener(this);
> } else {
> setLayers(new Layer[0]);
> }
> updateLayerPanes(layerHandler);
> }
>
> /**
> * Get the LayerHandler that the LayersPanel listens to and uses
> * to reorder layers.
> * _at_return LayerHandler.
> */
> public LayerHandler getLayerHandler() {
> return layerHandler;
> }
>
> /**
> * Set the layerpanes with the given layerhandler
> * _at_param layerHandler The LayerHandler controlling the layers
> */
> protected void updateLayerPanes(LayerHandler layerHandler) {
> Iterator it = getPanes().iterator();
> while (it.hasNext()) {
> ((LayerPane)it.next()).setLayerHandler(layerHandler);
> }
> }
>
> /**
> * LayerListener interface method. A list of layers will be
> * added, removed, or replaced based on on the type of LayerEvent.
> * The LayersPanel only reacts to LayerEvent.ALL events, to reset
> * the components in the LayersPanel.
> *
> * _at_param evt a LayerEvent.
> */
> public void setLayers(LayerEvent evt) {
> Layer[] layers = evt.getLayers();
> int type = evt.getType();
>
> if (type==LayerEvent.ALL) {
> Debug.message("layerspanel", "LayersPanel received layers
> update");
> setLayers(layers);
> }
> }
>
> /**
> * Tool interface method. The retrieval tool's interface. This
> * method creates a button that will bring up the LayersPanel.
> *
> * _at_return String The key for this tool.
> */
> public Container getFace() {
> JButton layerButton = null;
>
> if (getUseAsTool()) {
> layerButton = new JButton(new
> ImageIcon(OMToolSet.class.getResource("layers.gif"), "Layer
> Controls"));
> layerButton.setBorderPainted(false);
> layerButton.setToolTipText("Layer Controls");
> layerButton.setMargin(new Insets(0,0,0,0));
> layerButton.addActionListener(getActionListener());
> }
>
> return layerButton;
> }
>
> /**
> * Get the ActionListener that triggers the LayersPanel. Useful
> * to have to provide an alternative way to bring up the
> * LayersPanel.
> *
> * _at_return ActionListener
> */
> public ActionListener getActionListener() {
> return new ActionListener() {
> public void actionPerformed(ActionEvent evt) {
> WindowSupport ws = getWindowSupport();
> int w = 328;
> int h = 300;
>
> Dimension dim = ws.getComponentSize();
> if (dim != null) {
> w = (int)dim.getWidth();
> h = (int)dim.getHeight();
> }
>
> int x = 10;
> int y = 10;
>
> Point loc = ws.getComponentLocation();
> if (loc != null) {
> x = (int) loc.getX();
> y = (int) loc.getY();
> }
>
> MapHandler mh = (MapHandler) getBeanContext();
> Frame frame = null;
> if (mh != null) {
> frame = (Frame)mh.get(java.awt.Frame.class);
> }
>
> ws.displayInWindow(frame, x, y, w, h);
> }
> };
> }
>
> /**
> * Set the layers that are in the LayersPanel. Make sure that the
> * layer[] is the same as that passed to any other OpenMap
> * component, like the LayersMenu. This method checks to see if
> * the layer[] has actually changed, in order or in size. If it
> * has, then createPanel() is called to rebuild the LayersPanel.
> *
> * _at_param inLayers the array of layers.
> */
> public void setLayers(Layer[] inLayers) {
> Layer[] layers = inLayers;
>
> if (inLayers == null) {
> layers = new Layer[0];
> }
>
> if (Debug.debugging("layerspanel")) {
> Debug.output("LayersPanel.setLayers() with " +
> layers.length + " layers.");
> }
>
> LinkedList panes = getPanes();
> int separatorOffset = 0;
> if (backgroundLayerSeparator != null &&
> panes.contains(backgroundLayerSeparator)) {
> separatorOffset = 1;
> }
>
> if (panes.size() - separatorOffset != layers.length) {
> // if the panel hasn't been created yet, or if someone has
> // changed the layers on us, rebuild the panel.
> createPanel(layers);
> return;
> }
>
> int i = 0;
> Iterator it = panes.iterator();
> while (it.hasNext() && i < layers.length) {
> LayerPane pane = (LayerPane)it.next();
>
> if (pane == backgroundLayerSeparator) {
> continue;
> }
>
> if (pane.getLayer() != layers[i]) {
> // If the layer order sways at all, then we start over
> // and rebuild the panel
> createPanel(layers);
> return;
> } else {
> pane.updateLayerLabel();
> }
>
> // Do this just in case someone has changed something
> // somewhere else...
> pane.setLayerOn(layers[i].isVisible());
> i++;
> }
>
> // One last check for a mismatch...
> if (it.hasNext() || i < layers.length) {
> createPanel(layers);
> }
> // If we get here, it means that what we had is what we
> // wanted.
> }
>
> protected LinkedList getPanes() {
> if (panes == null) {
> panes = new LinkedList();
> }
> return panes;
> }
>
> protected void setPanes(LinkedList lpa) {
> panes = lpa;
> }
>
> /**
> * Create the panel that shows the LayerPanes. This method
> * creates the on/off buttons, palette buttons, and layer labels,
> * and adds them to the scrollPane used to display all the layers.
> *
> * _at_param inLayers the Layer[] that reflects all possible layers
> * that can be added to the map.
> */
> public void createPanel(Layer[] inLayers) {
> Debug.message("layerspanel", "LayersPanel.createPanel()");
>
> if (scrollPane != null) {
> remove(scrollPane);
> }
>
> Layer[] layers = inLayers;
> if (layers == null) {
> layers = new Layer[0];
> }
>
> if (panesPanel == null) {
> panesPanel = new JPanel();
> panesPanel.setLayout(new BoxLayout(panesPanel, BoxLayout.Y_AXIS));
> panesPanel.setAlignmentX(LEFT_ALIGNMENT);
> panesPanel.setAlignmentY(BOTTOM_ALIGNMENT);
> } else {
> ((BoxLayout)panesPanel.getLayout()).invalidateLayout(panesPanel);
> panesPanel.removeAll();
> }
>
> if (bg == null) {
> bg = new ButtonGroup();
> }
>
> LinkedList panes = new LinkedList();
> LinkedList backgroundPanes = new LinkedList();
>
> // populate the arrays of CheckBoxes and strings used to fill
> // the JPanel for the panes
> for (int i = 0; i < layers.length; i++) {
> Layer layer = layers[i];
> if (layer == null) {
> Debug.output("LayersPanel caught null layer, " + i +
> " out of " + layers.length);
> continue;
> }
>
> LayerPane lpane = (LayerPane)paneLookUp.get(layer);
>
> if (lpane == null) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel: Creating LayerPane for " +
> layer.getName());
> }
> lpane = createLayerPaneForLayer(layer, layerHandler, bg);
> lpane.addPropertyChangeListener(LayerSelectedCmd, this);
> lpane.addPropertyChangeListener(LayerDeselectedCmd, this);
> paneLookUp.put(layer, lpane);
> } else {
> // In case this has been modified elsewhere...
> lpane.setLayerOn(layer.isVisible());
> }
>
> if (layer.getAddAsBackground() &&
> backgroundLayerSeparator != null) {
> backgroundPanes.add(lpane);
> } else {
> panes.add(lpane);
> panesPanel.add(lpane);
> }
> }
>
> if (backgroundPanes.size() != 0) {
> if (Debug.debugging("layerspanel")) {
> Debug.output("Adding BackgroundLayerSeparator");
> }
> panes.add(backgroundLayerSeparator);
> panesPanel.add(backgroundLayerSeparator);
> panes.addAll(backgroundPanes);
>
> Iterator it = backgroundPanes.iterator();
> while (it.hasNext()) {
> panesPanel.add((LayerPane)it.next());
> }
>
> } else if (backgroundLayerSeparator != null) {
> if (Debug.debugging("layerspanel")) {
> Debug.output("No layers are background layers, adding separator");
> }
> panes.add(backgroundLayerSeparator);
> panesPanel.add(backgroundLayerSeparator);
> }
>
> setPanes(panes);
>
> if (scrollPane != null) {
> remove(scrollPane);
> scrollPane.removeAll();
> scrollPane = null;
> }
>
> scrollPane = new JScrollPane(
> panesPanel,
> ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
> ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
>
> add(scrollPane, BorderLayout.CENTER);
> revalidate();
> }
>
> /**
> * Called when a new LayerPane needs to be created for a layer.
> * You can use this to extend LayerPane and return something else
> * that fits your GUI.
> */
> protected LayerPane createLayerPaneForLayer(Layer layer,
> LayerHandler layerHandler,
> ButtonGroup bg) {
> if (showStatus) {
> return new LayerStatusPane(layer, layerHandler, bg,
> paletteButtonAsCheckBox);
> } else {
> return new LayerPane(layer, layerHandler, bg,
> paletteButtonAsCheckBox);
> }
> }
>
> public void deletePanes(LinkedList dpanes) {
> Debug.message("layerspanel", "LayersPanel.deletePanes()");
> if (dpanes != null) {
> paneLookUp.clear();
> Iterator it = dpanes.iterator();
> while (it.hasNext()) {
> LayerPane pane = (LayerPane)it.next();
> if (pane != null && pane != backgroundLayerSeparator) {
> pane.removePropertyChangeListener(this);
> pane.cleanup(bg);
> }
> }
> }
>
> // Shouldn't call this, but it's the only thing
> // that seems to make it work...
> if (Debug.debugging("helpgc")) {
> System.gc();
> }
> }
>
> /**
> * Set up the buttons used to move layers up and down, or
> * add/remove layers. The button component should hook itself up
> * to the LayersPanel, and assume that the LayersPanel has a
> * BorderLayout with the list in the center spot.
> */
> protected void createControlButtons() {
> controls = new LayerControlButtonPanel(this);
> }
>
> public void setControls(LayerControlButtonPanel lpb) {
> controls = lpb;
> if (lpb != null) {
> lpb.setLayersPanel(this);
> }
> }
>
> public LayerControlButtonPanel getControls() {
> return controls;
> }
>
> /**
> * Method associated with the ActionListener interface. This
> * method listens for action events meant to change the order of
> * the layers, as fired by the layer order buttons.
> *
> * _at_param e ActionEvent
> */
> public void actionPerformed(java.awt.event.ActionEvent e) {
> String command = e.getActionCommand();
> if (Debug.debugging("layerspanel")) {
> Debug.output("LayersPanel.actionPerformed(): " + command);
> }
>
> try {
> LayerPane pane = findSelectedPane();
> if (pane != null) {
> moveLayer(pane, command);
> }
> } catch (NullPointerException npe) {
> } catch (ArrayIndexOutOfBoundsException aioobe) {
> }
> }
>
> /**
> * Change a layer's position.
> */
> public void moveLayer(Layer layer, String command) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel.moveLayer(): " + command +
> " for " + layer.getName());
> }
>
> moveLayer((LayerPane)paneLookUp.get(layer), command);
> }
>
>
> /**
> * Change a layer's position, with the layer represented by a
> * LayerPane.
> */
> protected void moveLayer(LayerPane lp, String command) {
>
> if (lp == null) {
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel.moveLayer(): LayerPane not represented on
> list");
> }
>
> if (command == LayerRemoveCmd) {
> // OK, here's a hidden trick. If no layers are selected
> // and the minus sign is clicked, then this is called.
> System.gc();
> }
> return;
> }
>
> LinkedList panes = getPanes();
> int row = panes.indexOf(lp);
>
> boolean boundary = false;
> int bls_row = -1;
> if (backgroundLayerSeparator != null) {
> bls_row = panes.indexOf(backgroundLayerSeparator);
> boundary = bufferedBoundary;
> }
>
> if (command.equals(LayerTopCmd)) {
> // Move layer selected layer to top
> panes.remove(lp);
> if (boundary && bls_row > 0 && row > bls_row + 1) {
> // If the backgroundLayerSeparator is more than one
> // above it, move to just below it on the first top
> // command.
> panes.add(bls_row + 1, lp);
> } else {
> panes.addFirst(lp);
> }
>
> rejiggerMapLayers();
> } else if (command.equals(LayerBottomCmd)) {
> // Move layer selected layer to bottom
> panes.remove(lp);
>
> if (boundary && bls_row > 0 && row < bls_row - 1) {
> // If the backgroundLayerSeparator is more than one
> // below it, move to just above it on the first top
> // command.
> panes.add(bls_row - 1, lp);
> } else {
> panes.addLast(lp);
> }
>
> rejiggerMapLayers();
> } else if (command.equals(LayerUpCmd)) {
> // Move layer selected layer up one
> if (row <= 0) return;
> panes.remove(row);
> panes.add(row - 1, lp);
> rejiggerMapLayers();
> } else if (command.equals(LayerDownCmd)) {
> // Move layer selected layer up one
> if (row < 0 || row == panes.size() - 1) return;
> panes.remove(row);
> panes.add(row + 1, lp);
> rejiggerMapLayers();
> } else if (command.equals(LayerRemoveCmd)) {
>
> if (layerHandler == null || !lp.getLayer().removeConfirmed()) {
> return;
> }
>
> // This order is somewhat important. lp.getLayer() will
> // be null after lp.cleanup. lp.setSelected() will cause
> // a series of property change notifications.
> lp.setSelected(false);
> lp.getLayer().setPaletteVisible(false);
> paneLookUp.remove(lp.getLayer());
> layerHandler.removeLayer(lp.getLayer());
> lp.cleanup(bg);
>
> // Shouldn't call this, but it's the only thing
> // that seems to make it work...
> if (Debug.debugging("helpgc")) {
> System.gc();
> }
>
> return;
>
> } else if (command.equals(LayerAddCmd)) {
> if (layerAddPanel != null) {
> layerAddPanel.showPanel();
> }
> }
> }
>
> /**
> * Find the selected LayerPane in the current LayerPane list.
> * Will return null if there isn't a selected pane.
> */
> protected LayerPane findSelectedPane() {
> Iterator it = getPanes().iterator();
> while (it.hasNext()) {
> LayerPane pane = (LayerPane)it.next();
> if (pane.isSelected()) {
> return pane;
> }
> }
> return null;
> }
>
> /**
> * Makes a new layer cake of active layers to send to
> * LayerHandler.setLayers().
> */
> protected void rejiggerMapLayers() {
> Debug.message("layerspanel", "LayersPanel.rejiggerMapLayers()");
>
> if (layerHandler == null) {
> // Why bother doing anything??
> return;
> }
>
> int selectedRow = -1;
>
> panesPanel.removeAll();
>
> LinkedList panes = getPanes();
> LinkedList layerList = new LinkedList();
>
> int bufferIndex = Integer.MAX_VALUE;
>
> int i = 0; // track layer index
> Iterator it = panes.iterator();
> while (it.hasNext()) {
>
> LayerPane pane = (LayerPane)it.next();
>
> if (pane == backgroundLayerSeparator) {
> panesPanel.add(backgroundLayerSeparator);
> bufferIndex = i++;
> continue;
> }
>
> Layer layer = pane.getLayer();
> layer.setAddAsBackground(i > bufferIndex);
> panesPanel.add(pane);
> layerList.add(layer);
>
> if (pane.isSelected()) {
> selectedRow = i;
> }
> i++;
> }
>
> scrollPane.revalidate();
>
> // Scroll up or down as necessary to keep selected row viewable
> if (selectedRow >= 0) {
> int spheight = scrollPane.getHeight();
> JScrollBar sb = scrollPane.getVerticalScrollBar();
> int sv = sb.getValue();
> int paneheight = ((LayerPane)panes.get(selectedRow)).getHeight();
> int rowvalue = selectedRow*paneheight;
> // Don't reset scrollBar unless the selected row
> // is not in the viewable range
> if (!((rowvalue > sv) && (rowvalue < spheight+sv))) {
> sb.setValue(rowvalue);
> }
> }
>
> Object[] layerArray = layerList.toArray();
> int length = layerArray.length;
> Layer[] newLayers = new Layer[length];
>
> for (int j = 0; j < length; j++) {
> newLayers[j] = (Layer)layerArray[j];
> }
>
> layerHandler.setLayers(newLayers);
> }
>
> /**
> * Update the layer names - if a layer name has changed, tell the
> * LayerPanes to check with their layers to update their labels.
> */
> public synchronized void updateLayerLabels() {
> Iterator it = getPanes().iterator();
> while (it.hasNext()) {
> ((LayerPane)it.next()).updateLayerLabel();
> }
> }
>
> public void propertyChange(PropertyChangeEvent pce) {
> String command = pce.getPropertyName();
> Object obj = pce.getNewValue();
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel receiving PropertyChangeEvent " +
> command + ", " + pce.toString());
> }
>
> if ((command == LayerSelectedCmd ||
> command == LayerDeselectedCmd) &&
> obj instanceof Layer) {
>
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel: layer panel notification that layer is
> selected: " + ((Layer)obj).getName());
> }
> firePropertyChange(command, null, ((Layer)obj));
>
> } else if ((command == LayersPanel.LayerTopCmd ||
> command == LayersPanel.LayerBottomCmd ||
> command == LayersPanel.LayerUpCmd ||
> command == LayersPanel.LayerDownCmd ||
> command == LayersPanel.LayerRemoveCmd) &&
> obj instanceof Layer) {
> if (Debug.debugging("layercontrol")) {
> Debug.output("LayersPanel: layer panel notification that layer
> should be raised: " + ((Layer)obj).getName());
> }
> moveLayer((Layer)obj, command);
> }
> }
>
> /**
> * Called when the LayersPanel is added the BeanContext, or when
> * another object is added to the BeanContext after the
> * LayerHandler has been added. This allows the LayersPanel to
> * keep up-to-date with any objects that it may be interested in,
> * namely, the LayerHandler. If a LayerHandler has already been
> * added, the new LayerHandler will replace it.
> *
> * _at_param someObj the object being added to the BeanContext
> */
> public void findAndInit(Object someObj) {
> if (someObj instanceof LayerHandler) {
> // do the initializing that need to be done here
> Debug.message("layerspanel","LayersPanel found a LayerHandler");
> setLayerHandler((LayerHandler)someObj);
> }
>
> if (someObj instanceof BufferedLayerMapBean) {
> if (Debug.debugging("layerspanel")) {
> Debug.output("LayersPanel found BufferedLayerMapBean, creating
> separator panel");
> }
> backgroundLayerSeparator =
> LayerPane.getBackgroundLayerSeparator(" --- Background Layers --- ");
> }
>
> // Don't want to forward ourselves on to controls, supposedly
> // they already know.
> if (controls instanceof LightMapHandlerChild && someObj != this) {
> ((LightMapHandlerChild)controls).findAndInit(someObj);
> }
> }
>
> /**
> * BeanContextMembershipListener method. Called when an object
> * has been removed from the parent BeanContext. If a
> * LayerHandler is removed, and it's the current one being
> * listened to, then the layers in the panel will be wiped clean.
> *
> * _at_param someObj the object being removed from the BeanContext
> */
> public void findAndUndo(Object someObj) {
> if (someObj instanceof LayerHandler) {
> // do the initializing that need to be done here
> Debug.message("layerspanel","LayersPanel removing LayerHandler");
> if (getLayerHandler() == (LayerHandler) someObj) {
> setLayerHandler(null);
> }
> }
>
> // Don't want to forward ourselves on to controls, supposedly
> // they already know.
> if (controls instanceof LightMapHandlerChild && someObj != this) {
> ((LightMapHandlerChild)controls).findAndUndo(someObj);
> }
> }
>
> public void setProperties(String prefix, Properties props) {
> super.setProperties(prefix, props);
> prefix = PropUtils.getScopedPropertyPrefix(prefix);
>
> String controlString =
> props.getProperty(prefix + ControlButtonsProperty);
>
> if (controlString != NO_CONTROLS) {
> if (controlString == null) {
> createControlButtons();
> } else {
> Object obj = ComponentFactory.create(
> controlString, prefix + ControlButtonsProperty, props);
>
> if (obj instanceof LayerControlButtonPanel) {
> setControls((LayerControlButtonPanel)obj);
> }
> }
> }
>
> bufferedBoundary = LayerUtils.booleanFromProperties(props, prefix +
> BufferedBoundaryProperty, bufferedBoundary);
> showStatus = LayerUtils.booleanFromProperties(props, prefix +
> ShowStatusProperty, showStatus);
> paletteButtonAsCheckBox =
> LayerUtils.booleanFromProperties(props, prefix +
> PaletteButtonAsCheckBoxProperty, paletteButtonAsCheckBox);
> }
>
> public Properties getProperties(Properties props) {
> props = super.getProperties(props);
>
>
> String prefix = PropUtils.getScopedPropertyPrefix(this);
> LayerControlButtonPanel controls = getControls();
> if (controls != null) {
> props.put(prefix + ControlButtonsProperty,
> controls.getClass().getName());
> controls.getProperties(props);
> }
> props.put(prefix + BufferedBoundaryProperty, new
> Boolean(bufferedBoundary).toString());
> props.put(prefix + ShowStatusProperty, new
> Boolean(showStatus).toString());
> props.put(prefix + PaletteButtonAsCheckBoxProperty, new
> Boolean(paletteButtonAsCheckBox).toString());
> return props;
> }
>
> public Properties getPropertyInfo(Properties props) {
> props = super.getPropertyInfo(props);
> props.put(ControlButtonsProperty, "Class to use for layer control
> buttons (Optional)");
> LayerControlButtonPanel controls = getControls();
> if (controls != null) {
> controls.getPropertyInfo(props);
> }
> props.put(BufferedBoundaryProperty, "Force layer movement to respect
> background layer boundary.");
> props.put(BufferedBoundaryProperty + ScopedEditorProperty,
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> props.put(ShowStatusProperty, "Use Layer Panes that show layer
> status.");
> props.put(ShowStatusProperty + ScopedEditorProperty,
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> props.put(PaletteButtonAsCheckBoxProperty, "If palette button should
> behave as a check box or as a simple button.");
> props.put(PaletteButtonAsCheckBoxProperty + ScopedEditorProperty,
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
> return props;
> }
> }
>
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/
> ProjectionStackTool.java,v $
> // $RCSfile: ProjectionStackTool.java,v $
> // $Revision: 1.1.1.1 $
> // $Date: 2003/03/03 21:58:00 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.gui;
>
> import java.awt.*;
> import java.awt.event.*;
> import javax.swing.*;
> import java.util.*;
>
> import com.bbn.openmap.proj.*;
> import com.bbn.openmap.util.Debug;
>
> /**
> * The ProjectionStackTool contains buttons that can trigger a
> * ProjectionStack to change a projection of a MapBean to a previous
> * projection, or to a later projection if the active projection is in
> * the middle of the stack. The OpenMap ProjectionStack will look for
> * one of these, and connect itself to it if it finds one.
> */
> public class ProjectionStackTool extends OMToolComponent
> implements ProjectionStackTrigger {
>
> protected Vector listeners;
>
> protected JButton backButton;
> protected JButton forwardButton;
> // protected JButton clearButton;
>
> protected static transient String backName = "backproj.gif";
> protected static transient String forwardName = "forwardproj.gif";
> protected static transient String dimBackName = "dimbackproj.gif";
> protected static transient String dimForwardName =
> "dimforwardproj.gif";
>
> protected boolean dimBackButton = true;
> protected boolean dimForwardButton = true;
>
> ImageIcon backIcon;
> ImageIcon dimBackIcon;
> ImageIcon forwardIcon;
> ImageIcon dimForwardIcon;
>
> public ProjectionStackTool() {
> super();
> setKey("projectionstacktool");
> resetButtons(!dimBackButton, !dimForwardButton);
> add(backButton);
> add(forwardButton);
> // add(clearButton);
> }
>
> /**
> * Add an ActionListener for events that trigger events to shift
> * the Projection stack. If you are hooking up a ProjectionStack,
> * you don't need to call this. The ProjectionStack will call
> * this when you can addProjectionStackTrigger on it.
> */
> public void addActionListener(ActionListener al) {
> if (backButton != null && forwardButton != null) {
> backButton.addActionListener(al);
> forwardButton.addActionListener(al);
> // clearButton.addActionListener(al);
> } else {
> if (listeners == null) {
> listeners = new Vector();
> }
> listeners.add(al);
> }
> }
>
> /**
> * Remove an ActionListener that receives events that trigger
> * events to shift the Projection stack. If you are hooking up a
> * ProjectionStack, you don't need to call this. The
> * ProjectionStack will call this when you can
> * removeProjectionStackTrigger on it.
> */
> public void removeActionListener(ActionListener al) {
> if (backButton != null && forwardButton != null) {
> backButton.removeActionListener(al);
> forwardButton.removeActionListener(al);
> // clearButton.removeActionListener(al);
> } else if (listeners != null) {
> listeners.remove(al);
> }
> }
>
> /**
> * To receive a status to let the trigger know if any projections
> * in the forward or backward stacks exist, possibly to disable
> * any gui widgets.
> *
> * _at_param containsBackProjections there is at least one past
> * projection in the back cache.
> * _at_param containsForwardProjections there is at least one future
> * projection in the forward cache. Used when a past projection
> * is being used.
> */
> public void updateProjectionStackStatus(boolean
> containsBackProjections,
> boolean containsForwardProjections) {
> dimBackButton = !containsBackProjections;
> dimForwardButton = !containsForwardProjections;
> resetButtons(containsBackProjections, containsForwardProjections);
> }
>
> public void resetButtons(boolean enableBackButton,
> boolean enableForwardButton) {
>
> java.net.URL url;
> JButton b;
>
> if (backIcon == null) {
> url = getClass().getResource(backName);
> backIcon = new ImageIcon(url);
> }
>
> if (dimBackIcon == null) {
> url = getClass().getResource(dimBackName);
> dimBackIcon = new ImageIcon(url);
> }
>
> if (forwardIcon == null) {
> url = getClass().getResource(forwardName);
> forwardIcon = new ImageIcon(url);
> }
>
> if (dimForwardIcon == null) {
> url = getClass().getResource(dimForwardName);
> dimForwardIcon = new ImageIcon(url);
> }
>
> ImageIcon active;
> String toolTip;
> String disabled = " (" +
> i18n.get(ProjectionStackTool.class,"disabled","disabled") + ")";
> int size;
>
> toolTip = i18n.get(ProjectionStackTool.class,"backTip","Go back to
> previous projection");
> if (enableBackButton) {
> active = backIcon;
> } else {
> active = dimBackIcon;
> toolTip += " " + disabled;
> }
>
> if (backButton == null) {
> backButton = new JButton(active);
> backButton.setMargin(new Insets(0,0,0,0));
> backButton.setBorderPainted(false);
> backButton.setActionCommand(ProjectionStack.BackProjCmd);
> if (listeners != null) {
> size = listeners.size();
> for (int i = 0; i < size; i++) {
>
> backButton.addActionListener((ActionListener)listeners.elementAt(i));
> }
> }
>
> } else {
> backButton.setIcon(active);
> }
> backButton.setToolTipText(toolTip);
>
> toolTip = i18n.get(ProjectionStackTool.class,"forwardTip","Go
> forward to next projection");
> if (enableForwardButton) {
> active = forwardIcon;
> } else {
> active = dimForwardIcon;
> toolTip += " " + disabled;
> }
>
> if (forwardButton == null) {
> forwardButton = new JButton(active);
> forwardButton.setMargin(new Insets(0,0,0,0));
> forwardButton.setBorderPainted(false);
> forwardButton.setActionCommand(ProjectionStack.ForwardProjCmd);
>
> if (listeners != null) {
> size = listeners.size();
> for (int i = 0; i < size; i++) {
>
> forwardButton.addActionListener((ActionListener)listeners.elementAt(i))
> ;
> }
> }
> } else {
> forwardButton.setIcon(active);
> }
> forwardButton.setToolTipText(toolTip);
>
> // if (clearButton == null) {
> // clearButton = new JButton("Clear Stack");
> // clearButton.setMargin(new Insets(0,0,0,0));
> // clearButton.setBorderPainted(false);
> // clearButton.setActionCommand(ProjectionStack.ClearStacksCmd);
>
> // if (listeners != null) {
> // size = listeners.size();
> // for (int i = 0; i < size; i++) {
> //
> clearButton.addActionListener((ActionListener)listeners.elementAt(i));
> // }
> // }
> // }
> }
>
> public void findAndInit(Object someObj) {
> if (someObj instanceof ProjectionStack) {
> Debug.message("projectionstacktrigger","ProjectionStackTrigger
> adding a ProjectionStack");
> ((ProjectionStack)someObj).addProjectionStackTrigger(this);
> }
> }
>
> public void findAndUndo(Object someObj) {
> if (someObj instanceof ProjectionStack) {
> Debug.message("projectionstacktrigger","ProjectionStackTrigger
> removing a ProjectionStack");
> ((ProjectionStack)someObj).removeProjectionStackTrigger(this);
> }
> }
> }
>
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/gui/
> OMComponentPanel.java,v $
> // $RCSfile: OMComponentPanel.java,v $
> // $Revision: 1.6 $
> // $Date: 2004/10/14 18:18:16 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.gui;
>
> import java.beans.*;
> import java.beans.beancontext.*;
> import java.util.Iterator;
> import java.util.Properties;
> import javax.swing.*;
>
> import com.bbn.openmap.*;
>
> /**
> * The OMComponentPanel is a convienent super class intended to
> * provide an easy way to extend JPanel while also implementing the
> * common functions of an OMComponent (PropertyConsumer,
> * BeanContextMembershipListener and BeanContextChild). The
> * PropertyListener methods in the BeanContextChild aren't needed,
> * because the java.awt.Component provides them.
> */
> public abstract class OMComponentPanel extends JPanel implements
> PropertyConsumer, BeanContextChild,
> BeanContextMembershipListener,
> LightMapHandlerChild {
>
> /**
> * All OMComponentPanels have access to an I18n object, which is
> * provided by the Environment.
> */
> protected I18n i18n = Environment.getI18n();
>
> /**
> * BeanContextChildSupport object provides helper functions for
> * BeanContextChild interface.
> */
> protected BeanContextChildSupport beanContextChildSupport = new
> BeanContextChildSupport();
>
> protected OMComponentPanel() {
> super();
> }
>
> protected WindowSupport windowSupport;
>
> public void setWindowSupport(WindowSupport ws) {
> windowSupport = ws;
> }
>
> public WindowSupport getWindowSupport() {
> return windowSupport;
> }
>
>
> //////////////////////////////////////////////////////////////////////
> /////
> //// PropertyConsumer methods
>
> //////////////////////////////////////////////////////////////////////
> /////
>
> /**
> * Token uniquely identifying this compoentns in the application
> * properties.
> */
> protected String propertyPrefix = null;
>
> /**
> * Sets the properties for the OMComponent.
> *
> * _at_param props the <code>Properties</code> object.
> */
> public void setProperties(java.util.Properties props) {
> setProperties(getPropertyPrefix(), props);
> }
>
> /**
> * Sets the properties for the OMComponent.
> *
> * _at_param prefix the token to prefix the property names
> * _at_param props the <code>Properties</code> object
> */
> public void setProperties(String prefix, java.util.Properties
> props) {
> setPropertyPrefix(prefix);
>
> // String realPrefix =
> // PropUtils.getScopedPropertyPrefix(prefix);
> }
>
> /**
> * PropertyConsumer method, to fill in a Properties object,
> * reflecting the current values of the OMComponent. If the
> * component has a propertyPrefix set, the property keys should
> * have that prefix plus a separating '.' prepended to each
> * propery key it uses for configuration.
> *
> * _at_param props a Properties object to load the PropertyConsumer
> * properties into. If props equals null, then a new
> * Properties object should be created.
> * _at_return Properties object containing PropertyConsumer property
> * values. If getList was not null, this should equal
> * getList. Otherwise, it should be the Properties object
> * created by the PropertyConsumer.
> */
> public Properties getProperties(Properties props) {
> if (props == null) {
> props = new Properties();
> }
>
> // String prefix =
> // PropUtils.getScopedPropertyPrefix(propertyPrefix);
>
> return props;
> }
>
> /**
> * Method to fill in a Properties object with values reflecting
> * the properties able to be set on this PropertyConsumer. The key
> * for each property should be the raw property name (without a
> * prefix) with a value that is a String that describes what the
> * property key represents, along with any other information about
> * the property that would be helpful (range, default value,
> * etc.). For Layer, this method should at least return the
> * 'prettyName' property.
> *
> * _at_param list a Properties object to load the PropertyConsumer
> * properties into. If getList equals null, then a new
> * Properties object should be created.
> * _at_return Properties object containing PropertyConsumer property
> * values. If getList was not null, this should equal
> * getList. Otherwise, it should be the Properties object
> * created by the PropertyConsumer.
> */
> public Properties getPropertyInfo(Properties list) {
> if (list == null) {
> list = new Properties();
> }
> return list;
> }
>
> /**
> * Set the property key prefix that should be used by the
> * PropertyConsumer. The prefix, along with a '.', should be
> * prepended to the property keys known by the PropertyConsumer.
> *
> * _at_param prefix the prefix String.
> */
> public void setPropertyPrefix(String prefix) {
> propertyPrefix = prefix;
> }
>
> /**
> * Get the property key prefix that is being used to prepend to
> * the property keys for Properties lookups.
> *
> * _at_return the property prefix for the panel
> */
> public String getPropertyPrefix() {
> return propertyPrefix;
> }
>
>
> //////////////////////////////////////////////////////////////////////
> /////
> //// MapHandlerChild methods to make the tool work with
> //// the MapHandler to find any SelectionProviders.
>
> //////////////////////////////////////////////////////////////////////
> /////
>
> public void findAndInit(Object obj) {}
>
> public void findAndUndo(Object obj) {}
>
> /**
> * This is the method that your object can use to find other
> * objects within the MapHandler (BeanContext). This method gets
> * called when the object gets added to the MapHandler, or when
> * another object gets added to the MapHandler after the object is
> * a member.
> *
> * _at_param it Iterator to use to go through a list of objects. Find
> * the ones you need, and hook yourself up.
> */
> public void findAndInit(Iterator it) {
> while (it.hasNext()) {
> findAndInit(it.next());
> }
> }
>
> /**
> * BeanContextMembershipListener method. Called when a new object
> * is added to the BeanContext of this object.
> */
> public void childrenAdded(BeanContextMembershipEvent bcme) {
> findAndInit(bcme.iterator());
> }
>
> /**
> * BeanContextMembershipListener method. Called when a new object
> * is removed from the BeanContext of this object. For the Layer,
> * this method doesn't do anything. If your layer does something
> * with the childrenAdded method, or findAndInit, you should take
> * steps in this method to unhook the layer from the object used
> * in those methods.
> */
> public void childrenRemoved(BeanContextMembershipEvent bcme) {
> Iterator it = bcme.iterator();
> while (it.hasNext()) {
> findAndUndo(it.next());
> }
> }
>
> /** Method for BeanContextChild interface. */
> public BeanContext getBeanContext() {
> return beanContextChildSupport.getBeanContext();
> }
>
> /**
> * Method for BeanContextChild interface. Adds this object as a
> * BeanContextMembership listener, set the BeanContext in this
> * objects BeanContextSupport, and receives the initial list of
> * objects currently contained in the BeanContext.
> */
> public void setBeanContext(BeanContext in_bc) throws
> PropertyVetoException {
>
> if (in_bc != null) {
> in_bc.addBeanContextMembershipListener(this);
> beanContextChildSupport.setBeanContext(in_bc);
> findAndInit(in_bc.iterator());
> }
> }
>
> /**
> * Method for BeanContextChild interface. Uses the
> * BeanContextChildSupport to add a listener to this object's
> * property. This listener wants to have the right to veto a
> * property change.
> */
> public void addVetoableChangeListener(String propertyName,
> VetoableChangeListener
> in_vcl) {
>
> beanContextChildSupport.addVetoableChangeListener(propertyName,
> in_vcl);
> }
>
> /**
> * Method for BeanContextChild interface. Uses the
> * BeanContextChildSupport to remove a listener to this object's
> * property. The listener has the power to veto property changes.
> */
> public void removeVetoableChangeListener(String propertyName,
> VetoableChangeListener
> in_vcl) {
>
> beanContextChildSupport.removeVetoableChangeListener(propertyName,
> in_vcl);
> }
>
> /**
> * Report a vetoable property update to any registered listeners.
> * If anyone vetos the change, then fire a new event reverting
> * everyone to the old value and then rethrow the
> * PropertyVetoException.
> * <P>
> *
> * No event is fired if old and new are equal and non-null.
> * <P>
> *
> * _at_param name The programmatic name of the property that is about
> * to change
> *
> * _at_param oldValue The old value of the property
> * _at_param newValue - The new value of the property
> *
> * _at_throws PropertyVetoException if the recipient wishes the
> * property change to be rolled back.
> */
> public void fireVetoableChange(String name, Object oldValue,
> Object newValue)
> throws PropertyVetoException {
> super.fireVetoableChange(name, oldValue, newValue);
> beanContextChildSupport.fireVetoableChange(name, oldValue,
> newValue);
> }
> }# Sample ResourceBundle properties file
>
> CoordPanel.border=Decimal Degrees
>
> CoordPanel.lonlabel=Longitude:\
>
> CoordPanel.latlabel=Latitude:\
>
> CombinedCoordPanel.closebutton=Close
>
> CombinedCoordPanel.applybutton=Apply
>
> CombinedCoordPanel.tabPane.mgrs=MRGS
>
> CombinedCoordPanel.tabPane.decdeg=Dec Deg
>
> CombinedCoordPanel.tabPane.dms=DMS
>
> CombinedCoordPanel.tabPane.utm=UTM
>
> MGRSCoordPanel.border=MGRS Coordinate
>
> MGRSCoordPanel.mgrsLabel=MGRS:\
>
> DMSCoordPanel.border=Degress|Minutes|Seconds
>
> DMSCoordPanel.latlabel=Latitude DMS:\
>
> DMSCoordPanel.lonlabel=Longitude DMS:\
>
> UTMCoordPanel.border=Zone Number|Hemisphere|Easting|Northing
>
> UTMCoordPanel.zone.tooltip=Zone Number: 0-60
>
> UTMCoordPanel.hemi.tooltip=Hemisphere: N or S
>
> UTMCoordPanel.utmLabel=UTM:\
>
> CoordDialog.defaultTitle=Go To Coordinates
>
> CombinedCoordPanel.defaultComment=Set Center of Map to Coordinates:
>
> NavigatePanel.panel.northwest=Pan Northwest
>
> NavigatePanel.panel.north=Pan North
>
> NavigatePanel.panel.northeast=Pan NortEast
>
> NavigatePanel.panel.west=Pan West
>
> NavigatePanel.panel.centerAtStart=Center Map at Starting Coords
>
> NavigatePanel.panel.east=Pan East
>
> NavigatePanel.panel.southwest=Pan Southwest
>
> NavigatePanel.panel.south=Pan South
>
> NavigatePanel.panel.southeast=Pan Southeast
>
> ProjectionStackTool.backTip=Go back to previous projection
>
> ProjectionStackTool.forwardTip=Go forward to next projection
>
> ProjectionStackTool.disabled=disabled
>
> LayerControlButtonPanel.moveLayerToTop=Move selected layer to top
>
> LayerControlButtonPanel.moveLayerUpOne=Move selected layer up one
>
> LayerControlButtonPanel.moveLayerToBottom=Move selected layer to bottom
>
> LayerControlButtonPanel.moveLayerDownOne=Move selected layer down one
>
> LayerControlButtonPanel.removeLayer=Remove selected layer
>
> LayerControlButtonPanel.addLayer=Add a layer
> # Sample ResourceBundle properties file
>
> CoordPanel.border=Stopnie dziesi\u0119tne
>
> CoordPanel.lonlabel=D\u0142ugo\u015B\u0107:\
>
> CoordPanel.latlabel=Szeroko\u015B\u0107:\
>
> CombinedCoordPanel.closebutton=Rezygnuj
>
> CombinedCoordPanel.applybutton=Akceptuj
>
> CombinedCoordPanel.tabPane.mgrs=MRGS
>
> CombinedCoordPanel.tabPane.decdeg=\ \ \u00B0\ \
>
> CombinedCoordPanel.tabPane.dms=\ \u00B0 ' "\
>
> CombinedCoordPanel.tabPane.utm=UTM
>
> MGRSCoordPanel.border=MGRS wsp\u00F3\u0142rz\u0119dne
>
> MGRSCoordPanel.mgrsLabel=MGRS:\
>
> DMSCoordPanel.border=Stopnie|Minuty|Sekundy
>
> DMSCoordPanel.latlabel=Szeroko\u015B\u0107 SMS:\
>
> DMSCoordPanel.lonlabel=D\u0142ugo\u015B\u0107 SMS:\
>
> UTMCoordPanel.border=Zone Number|Zone Letter|Easting|Northing
>
> UTMCoordPanel.utmLabel=UTM:\
>
> CoordDialog.defaultTitle=Skocz do lokalizacji
>
> CombinedCoordPanel.defaultComment=Ustaw wsp\u00F3\u0142rz\u0119dne
> \u015Brodka mapy:
>
> NavigatePanel.panel.northwest=Przesu\u0144 na p\u00F3\u0142nocny
> zach\u00F3d
>
> NavigatePanel.panel.north=Przesu\u0144 na p\u00F3\u0142noc
>
> NavigatePanel.panel.northeast=Przesu\u0144 na p\u00F3\u0142nocny
> wsch\u00F3d
>
> NavigatePanel.panel.west=Przesu\u0144 na zach\u00F3d
>
> NavigatePanel.panel.centerAtStart=Wr\u00F3\u0107 na
> pocz\u0105tkow\u0105 pozycj\u0119
>
> NavigatePanel.panel.east=Przesu\u0144 na wsch\u00F3d
>
> NavigatePanel.panel.southwest=Przesu\u0144 na po\u0142udniowy
> zach\u00F3d
>
> NavigatePanel.panel.south=Przesu\u0144 na po\u0142udnie
>
> NavigatePanel.panel.southeast=Przesu\u0144 na po\u0142udniowy
> wsch\u00F3d
>
> ProjectionStackTool.backTip=Przejd\u017A do poprzedniej pozycji
>
> ProjectionStackTool.forwardTip=Przejd\u017A do nast\u0119pnej pozycji
> ProjectionStackTool.disabled=niedost\u0119pne
>
> LayerControlButtonPanel.moveLayerToTop=Przesu\u0144 zaznaczon\u0105
> warstw\u0119 na pocz\u0105tek
>
> LayerControlButtonPanel.moveLayerUpOne=Przesu\u0144 zaznaczon\u0105
> warstw\u0119 o 1 w g\u00F3r\u0119
>
> LayerControlButtonPanel.moveLayerToBottom=Przesu\u0144 zaznaczon\u0105
> warstw\u0119 na koniec
>
> LayerControlButtonPanel.moveLayerDownOne=Przesu\u0144 zaznaczon\u0105
> warstw\u0119 o 1 w d\u00F3\u0142
>
> LayerControlButtonPanel.removeLayer=Usu\u0144 zaznaczon\u0105
> warstw\u0119
>
> LayerControlButtonPanel.addLayer=Dodaj warstw\u0119
> # Sample ResourceBundle properties file
>
> MouseModeMenu.mouseModeMenu=Mouse Mode
>
> ProjectionMenu.projectionMenu=Projection
> # Sample ResourceBundle properties file
>
> MouseModeMenu.mouseModeMenu=Tryb pracy myszy
>
> ProjectionMenu.projectionMenu=Odwzorowanie
> # Sample ResourceBundle properties file
>
> DrawingAttributes.lineColor=Line color
>
> DrawingAttributes.lineColor.tooltip=Edge color for graphics.
>
> DrawingAttributes.fillColor=Fill color
>
> DrawingAttributes.fillColor.tooltip=Fill color for graphics.
>
> DrawingAttributes.selectColor=Selected color
>
> DrawingAttributes.selectColor.tooltip=Selected edge color for graphics.
>
> DrawingAttributes.mattingColor=Matting color
>
> DrawingAttributes.mattingColor.tooltip=Matting edge color for graphics.
>
> DrawingAttributes.fillPattern=Image fill pattern
>
> DrawingAttributes.fillPattern.tooltip=Image file to use for fill
> pattern for graphics (optional).
>
> DrawingAttributes.lineWidth=Line width
>
> DrawingAttributes.lineWidth.tooltip=Line width for edges of graphics
>
> DrawingAttributes.dashPattern=Dash pattern
>
> DrawingAttributes.dashPattern.tooltip=Line dash pattern, represented
> by space separated numbers (on off on ...)
>
> DrawingAttributes.dashPhase=Dash phase
>
> DrawingAttributes.dashPhase.tooltip=Phase for dash pattern (Default is
> 0)
>
> DrawingAttributes.baseScale=Base scale
>
> DrawingAttributes.baseScale.tooltip=<HTML><BODY>Scale which should be
> used as the base scale for the <br>patterns and line width. If set,
> size of pattern and <br>widths will be adjusted to the map
> scale</BODY></HTML>
>
> DrawingAttributes.matted=Matted
>
> DrawingAttributes.matted.tooltip=Flag to enable a thin black matting
> to be drawn around graphics..
>
> DrawingAttributes.chooseLineColor=Choose Line Color
>
> DrawingAttributes.chooseMattingColor=Choose Matting Color
>
> DrawingAttributes.chooseFillColor=Choose Fill Color
>
> DrawingAttributes.chooseSelectColor=Choose Select Color
>
> DrawingAttributes.lineButton.tooltip=Modify Line Parameters
>
> DrawingAttributes.mattedCheckBox.tooltip=Enable/Disable Matting on Edge
>
> DrawingAttributes.mattingColorButton.tooltip=Change Matted Edge Color
> (true/opaque)
>
> DrawingAttributes.selectColorButton.tooltip=Change Highlight Edge
> Color (true/opaque)
>
> DrawingAttributes.fillColorButton.tooltip=Change Fill Color
> (true/opaque)
>
> DrawingAttributes.lineColorButton.tooltip=Change Edge Color
> (true/opaque)
> # Sample ResourceBundle properties file
>
> DrawingAttributes.lineColor=Kolor linii
>
> DrawingAttributes.lineColor.tooltip=Kolor krawedzi dla grafiki
>
> DrawingAttributes.fillColor=Kolor wype\u0142nienia
>
> DrawingAttributes.fillColor.tooltip=Kolor wype\u0142nienia dla grafiki
>
> DrawingAttributes.selectColor=Kolor wybranej linii
>
> DrawingAttributes.selectColor.tooltip=Kolor wybranje linii na obiekcie
> graficznym
>
> DrawingAttributes.mattingColor=Kolor cieniowania
>
> DrawingAttributes.mattingColor.tooltip=Kolor cieniowania linii
>
> DrawingAttributes.fillPattern=Wzorzec wype\u0142nienia (obrazek)
>
> DrawingAttributes.fillPattern.tooltip=Obrazek u\u017Cywany jako
> wype\u0142nienie t\u0142a (opcjonalnie)
>
> DrawingAttributes.lineWidth=Szeroko\u015B\u0107 linii
>
> DrawingAttributes.lineWidth.tooltip=Szeroko\u015B\u0107 lini dla
> kraw\u0119dzi rysunku
>
> DrawingAttributes.dashPattern=Wzorzec linii przerywanej
>
> DrawingAttributes.dashPattern.tooltip=Wzorzec linii przerywanej,
> reprezentowany jako spacja odzielana liczbami (linia przerwa lnia...)
>
> DrawingAttributes.dashPhase=Przesuni\u0119cie linii
>
> DrawingAttributes.dashPhase.tooltip=Przesuni\u0119cie lini przerywanej
> (domy\u015Blnie 0)
>
> DrawingAttributes.baseScale=Skala podstawowa
>
> DrawingAttributes.baseScale.tooltip=<HTML><BODY>Skala, kt\u00F3ra
> b\u0119dzie u\u017Cywana jak skala dla <br>wzor\u00F3w i grubo\u015Bci
> linii. Je\u015Bli ustawiona, rozmairy wzor\u00F3w oraz
> <br>grubo\u015Bci linii zostana dostosowane do skali
> mapy.</BODY></HTML>
>
> DrawingAttributes.matted=Cieniowanie
>
> DrawingAttributes.matted.tooltip=Znacznik, okre\u015Blaj\u0105cy czy
> ma by\u0107 rysowana cieniutka siatka dooko\u0142a grafiki
>
> DrawingAttributes.chooseLineColor=Wybierz kolor linii
>
> DrawingAttributes.chooseMattingColor=Wybierz kolor cieniowania
>
> DrawingAttributes.chooseFillColor=Wybierz kolor wype\u0142nienia
>
> DrawingAttributes.chooseSelectColor=Wybierz kolor selekcji
>
> DrawingAttributes.lineButton.tooltip=Modyfikacja parametr\u00F3w linii
>
> DrawingAttributes.mattedCheckBox.tooltip=W\u0142\u0105cz/
> wy\u0142\u0105cz cieniowanie na kraw\u0119dziach
>
> DrawingAttributes.mattingColorButton.tooltip=Zmiana koloru cieniowania
> lini (prawdziwe / nieprze\u017Aroczyste)
>
> DrawingAttributes.selectColorButton.tooltip=Zmiana koloru linii
> pod\u015Bwietlenia (prawdziwe / nieprze\u017Aroczyste)
>
> DrawingAttributes.fillColorButton.tooltip=Zmiana koloru
> wype\u0142nienia (prawdziwe / nieprze\u017Aroczyste)
>
> DrawingAttributes.lineColorButton.tooltip=Zmiana koloru kraw\u0119dzi
> (prawdziwe / nieprze\u017Aroczyste)
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/layer/
> GraticuleLayer.java,v $
> // $RCSfile: GraticuleLayer.java,v $
> // $Revision: 1.7 $
> // $Date: 2004/02/05 18:16:10 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> // Modified 28 September 2002 by David N. Allsopp to allow font size
> // to be changed. See sections commented with 'DNA'.
>
> package com.bbn.openmap.layer;
>
> import java.awt.*;
> import java.awt.event.*;
> import java.util.Properties;
> import javax.swing.*;
>
> import com.bbn.openmap.*;
> import com.bbn.openmap.event.*;
> import com.bbn.openmap.omGraphics.*;
> import com.bbn.openmap.proj.*;
> import com.bbn.openmap.util.*;
>
> /**
> * Layer that draws graticule lines. If the showRuler property is
> * set to true, then longitude values are displayed on the bottom of
> * the map, and latitude values are displayed on the left side. If
> * the show1And5Lines property is true, then 5 degree lines are drawn
> * when there are &lt;= threshold ten degree latitude or longitude
> lines,
> * and 1 degree lines are drawn when there are &lt;= threshold five
> * degree latitude or longitude degree lines.
> *
> * <P> The openmap.properties file can control the layer with the
> * following settings:
> * <code><pre>
> * # Show lat / lon spacing labels
> * graticule.showRuler=true
> * graticule.show1And5Lines=true
> * # Controls when the five degree lines and one degree lines kick in
> * #- when there is less than the threshold of ten degree lat or lon
> * #lines, five degree lines are drawn. The same relationship is there
> * #for one to five degree lines.
> * graticule.threshold=2
> * # the color of 10 degree spacing lines (Hex ARGB)
> * graticule.10DegreeColor=FF000000
> * # the color of 5 degree spacing lines (Hex ARGB)
> * graticule.5DegreeColor=C7009900
> * # the color of 1 degree spacing lines (Hex ARGB)
> * graticule.1DegreeColor=C7003300
> * # the color of the equator (Hex ARGB)
> * graticule.equatorColor=FFFF0000
> * # the color of the international dateline (Hex ARGB)
> * graticule.datelineColor=7F000099
> * # the color of the special lines (Hex ARGB)
> * graticule.specialLineColor=FF000000
> * # the color of the labels (Hex ARGB)
> * graticule.textColor=FF000000
> * </pre></code>
> * In addition, you can get this layer to work with the OpenMap viewer
> * by editing your openmap.properties file:
> * <code><pre>
> * # layers
> * openmap.layers=graticule ...
> * # class
> * graticule.class=com.bbn.openmap.layer.GraticuleLayer
> * # name
> * graticule.prettyName=Graticule
> * </pre></code>
> *
> */
> public class GraticuleLayer extends OMGraphicHandlerLayer
> implements ActionListener {
>
>
> protected I18n i18n = Environment.getI18n();
> // default to not showing the ruler (mimicing older GraticuleLayer)
> protected boolean defaultShowRuler = true;
> protected boolean defaultShowOneAndFiveLines = true;
> protected boolean defaultShowBelowOneLines = false;
> protected int defaultThreshold = 2;
>
> /**
> * Flag for lineType - true is LINETYPE_STRAIGHT, false is
> * LINETYPE_GREATCIRCLE.
> */
> protected boolean boxy = true;
> /**
> * Threshold is the total number of ten lines on the screen before
> the
> * five lines appear, and the total number of five lines on the
> screen
> * before the one lines appear.
> */
> protected int threshold = defaultThreshold;
> /** The ten degree latitude and longitude lines, premade. */
> protected OMGraphicList tenDegreeLines = null;
> /** The equator, dateline and meridian lines, premade. */
> protected OMGraphicList markerLines = null;
>
> private final static int SHOW_TENS = 0;
> private final static int SHOW_FIVES = 1;
> private final static int SHOW_ONES = 2;
>
> protected boolean showOneAndFiveLines = defaultShowOneAndFiveLines;
> protected boolean showBelowOneLines = defaultShowBelowOneLines;
> protected boolean showRuler = defaultShowRuler;
>
> // protected Font font = new Font("Helvetica",
> java.awt.Font.PLAIN, 10);
> protected Font font = null;
> protected int fontSize = 10;
>
> // Color variables for different line types
> protected Color tenDegreeColor = null;
> protected Color fiveDegreeColor = null;
> protected Color oneDegreeColor = null;
> protected Color belowOneDegreeColor = null;
> protected Color equatorColor = null;
> protected Color dateLineColor = null;
> protected Color specialLineColor = null; // Tropic of Cancer,
> Capricorn
> protected Color textColor = null;
>
> // Default colors to use, if not specified in the properties.
> protected String defaultTenDegreeColorString = "000000";
> protected String defaultFiveDegreeColorString = "33009900";
> protected String defaultOneDegreeColorString = "33003300";
> protected String defaultBelowOneDegreeColorString = "9900ff00";
> protected String defaultEquatorColorString = "990000";
> protected String defaultDateLineColorString = "000099";
> protected String defaultSpecialLineColorString = "000000";
> protected String defaultTextColorString = "000000";
>
> // property text values
> public static final String TenDegreeColorProperty =
> "10DegreeColor";
> public static final String FiveDegreeColorProperty =
> "5DegreeColor";
> public static final String OneDegreeColorProperty = "1DegreeColor";
> public static final String BelowOneDegreeColorProperty =
> "Below1DegreeColor";
> public static final String EquatorColorProperty = "equatorColor";
> public static final String DateLineColorProperty = "datelineColor";
> public static final String SpecialLineColorProperty =
> "specialLineColor";
> public static final String TextColorProperty = "textColor";
> public static final String ThresholdProperty = "threshold";
> public static final String ShowRulerProperty = "showRuler";
> public static final String ShowOneAndFiveProperty =
> "show1And5Lines";
> public static final String ShowBelowOneProperty =
> "showBelow1Lines";
> public static final String FontSizeProperty = "fontSize"; //DNA
>
> /**
> * Construct the GraticuleLayer.
> */
> public GraticuleLayer() {
> // precalculate for boxy
> boxy = true;
> setName("Graticule");
> }
>
> /**
> * The properties and prefix are managed and decoded here, for
> * the standard uses of the GraticuleLayer.
> *
> * _at_param prefix string prefix used in the properties file for
> this layer.
> * _at_param properties the properties set in the properties file.
> */
> public void setProperties(String prefix, java.util.Properties
> properties) {
> super.setProperties(prefix, properties);
> prefix = PropUtils.getScopedPropertyPrefix(prefix);
>
> tenDegreeColor = PropUtils.parseColorFromProperties(properties,
> prefix +
> TenDegreeColorProperty,
>
> defaultTenDegreeColorString);
>
> fiveDegreeColor =
> PropUtils.parseColorFromProperties(properties,
> prefix +
> FiveDegreeColorProperty,
>
> defaultFiveDegreeColorString);
>
> oneDegreeColor = PropUtils.parseColorFromProperties(properties,
> prefix +
> OneDegreeColorProperty,
>
> defaultOneDegreeColorString);
>
> belowOneDegreeColor =
> PropUtils.parseColorFromProperties(properties,
> prefix +
> BelowOneDegreeColorProperty,
>
> defaultBelowOneDegreeColorString);
>
> equatorColor = PropUtils.parseColorFromProperties(properties,
> prefix +
> EquatorColorProperty,
>
> defaultEquatorColorString);
>
> dateLineColor = PropUtils.parseColorFromProperties(properties,
> prefix +
> DateLineColorProperty,
>
> defaultDateLineColorString);
>
> specialLineColor =
> PropUtils.parseColorFromProperties(properties,
> prefix +
> SpecialLineColorProperty,
>
> defaultSpecialLineColorString);
>
> textColor = PropUtils.parseColorFromProperties(properties,
> prefix +
> TextColorProperty,
>
> defaultTextColorString);
>
> threshold = PropUtils.intFromProperties(properties,
> prefix +
> ThresholdProperty,
> defaultThreshold);
>
> fontSize = PropUtils.intFromProperties(properties,
> prefix +
> FontSizeProperty,
> fontSize);
>
>
> font = new Font("Helvetica", java.awt.Font.PLAIN, fontSize);
>
>
> setShowOneAndFiveLines(PropUtils.booleanFromProperties(properties,
> prefix
> + ShowOneAndFiveProperty,
>
> defaultShowOneAndFiveLines));
>
>
> setShowBelowOneLines(PropUtils.booleanFromProperties(properties,
> prefix
> + ShowBelowOneProperty,
>
> defaultShowBelowOneLines));
>
> setShowRuler(PropUtils.booleanFromProperties(properties,
> prefix +
> ShowRulerProperty,
>
> defaultShowRuler));
>
> // So they will get re-created.
> tenDegreeLines = null;
> markerLines = null;
> }
>
> protected JCheckBox showRulerButton = null;
> protected JCheckBox show15Button = null;
> protected JCheckBox showBelow1Button = null;
>
> public void setShowOneAndFiveLines(boolean set) {
> showOneAndFiveLines = set;
> if (show15Button != null) {
> show15Button.setSelected(set);
> }
> }
>
> public void setShowBelowOneLines(boolean set) {
> showBelowOneLines = set;
> if (showBelow1Button != null) {
> showBelow1Button.setSelected(set);
> }
> }
>
> public boolean getShowOneAndFiveLines() {
> return showOneAndFiveLines;
> }
>
> public boolean getShowBelowOneLines() {
> return showBelowOneLines;
> }
>
> public void setShowRuler(boolean set) {
> showRuler = set;
> if (showRulerButton != null) {
> showRulerButton.setSelected(set);
> }
> }
>
> public boolean getShowRuler() {
> return showRuler;
> }
>
> /**
> * The properties and prefix are managed and decoded here, for
> * the standard uses of the GraticuleLayer.
> *
> * _at_param properties the properties set in the properties file.
> */
> public Properties getProperties(Properties properties) {
> properties = super.getProperties(properties);
>
> String prefix = PropUtils.getScopedPropertyPrefix(this);
> String colorString;
>
> if (tenDegreeColor == null) {
> colorString = defaultTenDegreeColorString;
> } else {
> colorString = Integer.toHexString(tenDegreeColor.getRGB());
> }
> properties.put(prefix + TenDegreeColorProperty, colorString);
>
> if (fiveDegreeColor == null) {
> colorString = defaultFiveDegreeColorString;
> } else {
> colorString =
> Integer.toHexString(fiveDegreeColor.getRGB());
> }
> properties.put(prefix + FiveDegreeColorProperty, colorString);
>
> if (oneDegreeColor == null) {
> colorString = defaultOneDegreeColorString;
> } else {
> colorString = Integer.toHexString(oneDegreeColor.getRGB());
> }
> properties.put(prefix + OneDegreeColorProperty, colorString);
>
> properties.put(prefix + BelowOneDegreeColorProperty,
> colorString);
>
> if (equatorColor == null) {
> colorString = defaultEquatorColorString;
> } else {
> colorString = Integer.toHexString(equatorColor.getRGB());
> }
> properties.put(prefix + EquatorColorProperty, colorString);
>
> if (dateLineColor == null) {
> colorString = defaultDateLineColorString;
> } else {
> colorString = Integer.toHexString(dateLineColor.getRGB());
> }
> properties.put(prefix + DateLineColorProperty, colorString);
>
> if (specialLineColor == null) {
> colorString = defaultSpecialLineColorString;
> } else {
> colorString =
> Integer.toHexString(specialLineColor.getRGB());
> }
> properties.put(prefix + SpecialLineColorProperty, colorString);
>
> if (textColor == null) {
> colorString = defaultTextColorString;
> } else {
> colorString = Integer.toHexString(textColor.getRGB());
> }
> properties.put(prefix + TextColorProperty, colorString);
>
> properties.put(prefix + ThresholdProperty,
> Integer.toString(threshold));
> properties.put(prefix + FontSizeProperty,
> Integer.toString(fontSize)); //DNA
>
> properties.put(prefix + ShowOneAndFiveProperty, new
> Boolean(showOneAndFiveLines).toString());
>
> properties.put(prefix + ShowRulerProperty, new
> Boolean(showRuler).toString());
>
> return properties;
> }
>
> /**
> * The properties and prefix are managed and decoded here, for
> * the standard uses of the GraticuleLayer.
> *
> * _at_param properties the properties set in the properties file.
> */
> public Properties getPropertyInfo(Properties properties) {
> properties = super.getPropertyInfo(properties);
> String interString;
> properties.put(initPropertiesProperty, TenDegreeColorProperty
> + " " + FiveDegreeColorProperty + " " + OneDegreeColorProperty + " " +
> EquatorColorProperty + " " + DateLineColorProperty + " " +
> SpecialLineColorProperty + " " + ShowOneAndFiveProperty + " " +
> ShowRulerProperty + " " + ThresholdProperty + " " + FontSizeProperty);
>
> interString =
> i18n.get(GraticuleLayer.class,TenDegreeColorProperty,I18n.TOOLTIP,"Colo
> r of the ten degree graticule lines.");
> properties.put(TenDegreeColorProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,TenDegreeColorProperty,TenDegreeColorProp
> erty);
> properties.put(TenDegreeColorProperty + LabelEditorProperty,
> interString);
> properties.put(TenDegreeColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,FiveDegreeColorProperty,I18n.TOOLTIP,"Col
> or of the five degree graticule lines.");
> properties.put(FiveDegreeColorProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,FiveDegreeColorProperty,"Color of the
> five degree graticule lines.");
> properties.put(FiveDegreeColorProperty + ScopedEditorProperty,
> interString);
> interString =
> i18n.get(GraticuleLayer.class,FiveDegreeColorProperty,FiveDegreeColorPr
> operty);
> properties.put(FiveDegreeColorProperty + LabelEditorProperty,
> interString);
> properties.put(FiveDegreeColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,OneDegreeColorProperty,I18n.TOOLTIP,"Colo
> r of the one degree graticule lines.");
> properties.put(OneDegreeColorProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,OneDegreeColorProperty,OneDegreeColorProp
> erty);
> properties.put(OneDegreeColorProperty + LabelEditorProperty,
> interString);
> properties.put(OneDegreeColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,EquatorColorProperty,I18n.TOOLTIP,"Color
> of the Equator.");
> properties.put(EquatorColorProperty,interString );
> interString =
> i18n.get(GraticuleLayer.class,EquatorColorProperty,EquatorColorProperty
> );
> properties.put(EquatorColorProperty + LabelEditorProperty,
> interString);
> properties.put(EquatorColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,DateLineColorProperty,I18n.TOOLTIP,"Color
> of the Date line.");
> properties.put(DateLineColorProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,DateLineColorProperty,DateLineColorProper
> ty);
> properties.put(DateLineColorProperty + LabelEditorProperty,
> interString);
> properties.put(DateLineColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,SpecialLineColorProperty,I18n.TOOLTIP,"Co
> lor of Tropic of Cancer, Capricorn lines.");
> properties.put(SpecialLineColorProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,SpecialLineColorProperty,SpecialLineColor
> Property);
> properties.put(SpecialLineColorProperty + LabelEditorProperty,
> interString);
> properties.put(SpecialLineColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,TextColorProperty,I18n.TOOLTIP,"Color of
> the line label text.");
> properties.put(TextColorProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,TextColorProperty,TextColorProperty);
> properties.put(TextColorProperty + LabelEditorProperty,
> interString);
> properties.put(TextColorProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,ThresholdProperty,I18n.TOOLTIP,"The
> number of lines showing before finer grain lines appear.");
> properties.put(ThresholdProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,ThresholdProperty,ThresholdProperty);
> properties.put(ThresholdProperty + LabelEditorProperty,
> interString);
>
> interString =
> i18n.get(GraticuleLayer.class,ShowOneAndFiveProperty,I18n.TOOLTIP,"Show
> the one and five degree lines.");
> properties.put(ShowOneAndFiveProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,ShowOneAndFiveProperty,ShowOneAndFiveProp
> erty);
> properties.put(ShowOneAndFiveProperty + LabelEditorProperty,
> interString);
> properties.put(ShowOneAndFiveProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.TrueFalsePropertyEditor");
>
> interString =
> i18n.get(GraticuleLayer.class,ShowRulerProperty,I18n.TOOLTIP,"Show the
> line label text.");
> properties.put(ShowRulerProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,ShowRulerProperty,ShowRulerProperty);
> properties.put(ShowRulerProperty + LabelEditorProperty,
> interString);
> properties.put(ShowRulerProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.TrueFalsePropertyEditor");
>
> //DNA
> interString =
> i18n.get(GraticuleLayer.class,FontSizeProperty,I18n.TOOLTIP, "The size
> of the font, in points, of the line labels.");
> properties.put(FontSizeProperty, interString);
> interString =
> i18n.get(GraticuleLayer.class,FontSizeProperty,FontSizeProperty);
> properties.put(FontSizeProperty + LabelEditorProperty,
> interString);
> //DNA
> return properties;
> }
>
> /**
> * Implementing the ProjectionPainter interface.
> */
> public synchronized void renderDataForProjection(Projection proj,
> java.awt.Graphics g) {
> if (proj == null) {
> Debug.error("GraticuleLayer.renderDataForProjection: null
> projection!");
> return;
> } else if (!proj.equals(getProjection())) {
> setProjection(proj.makeClone());
> // Figure out which line type to use
> if (proj instanceof Cylindrical) boxy = true;
> else boxy = false;
>
> setList(constructGraticuleLines());
> }
> paint(g);
> }
>
> /**
> * Invoked when the projection has changed or this Layer has been
> added to
> * the MapBean.
> * <p>
> * Perform some extra checks to see if reprojection of the
> graphics is
> * really necessary.
> * _at_param e ProjectionEvent
> *
> */
> public void projectionChanged(ProjectionEvent e) {
>
> // extract the projection and check to see if it's really
> different.
> // if it isn't then we don't need to do all the work again,
> just
> // repaint.
> Projection proj = setProjection(e);
> if (proj == null) {
> repaint();
> return;
> }
>
> // Figure out which line type to use
> if (proj instanceof Cylindrical) boxy = true;
> else boxy = false;
>
> setList(null);
> doPrepare();
> }
>
> /**
> * Creates the OMGraphic list with graticule lines.
> */
> public synchronized OMGraphicList prepare() {
> return constructGraticuleLines();
> }
>
> /**
> * Create the graticule lines.
> * <p>
> * NOTES:
> * <ul>
> * <li>Currently graticule lines are hardcoded to 10 degree
> intervals.
> * <li>No thought has been given to clipping based on the view
> rectangle.
> * For non-boxy projections performance may be degraded at very
> large
> * scales. (But we make up for this by running the task in its own
> * thread to support liveness).
> * </ul>
> * _at_return OMGraphicList new graphic list
> */
> protected OMGraphicList constructGraticuleLines() {
> float[] llp;
>
> OMGraphicList newgraphics = new OMGraphicList(20);
> // Lets figure out which lines should be painted...
> Projection projection = getProjection();
>
> if (projection == null) {
> return newgraphics;
> }
> tenDegreeLines = null;
>
> if (showOneAndFiveLines || showRuler || showBelowOneLines) {
>
> float left = projection.getUpperLeft().getLongitude();
> float right = projection.getLowerRight().getLongitude();
> float up = projection.getUpperLeft().getLatitude();
> float down = projection.getLowerRight().getLatitude();
>
> if (up > 80.0f) up = 80.0f;
> if (down > 80.0f) down = 80f; // unlikely
> if (up < -80.0f) up = -80.0f; // unlikely
> if (down < -80) down = -80.0f;
>
> int showWhichLines = evaluateSpacing(up, down, left,
> right);
>
> // Find out whether we need to do one or two queries,
> // depending on if we're straddling the dateline.
> if ((left > 0 && right < 0) ||
> (left > right) ||
> (Math.abs(left - right) < 1)) {
> // Test to draw the ones and fives, which will also do
> // the labels.
>
> if (showWhichLines != SHOW_TENS) {
> newgraphics.add(constructGraticuleLines(up, down,
> left, 180.0f, showWhichLines));
> newgraphics.add(constructGraticuleLines(up, down,
> -180.0f, right, showWhichLines));
> } else if (showRuler) { // Just do the labels for the
> tens lines
> newgraphics.add(constructTensLabels(up, down,
> left, 180.0f, true));
> newgraphics.add(constructTensLabels(up, down,
> -180.0f, right, false));
> }
> } else {
> // Test to draw the ones and fives, which will also do
> // the labels.
> if (showWhichLines != SHOW_TENS) {
> newgraphics = constructGraticuleLines(up, down,
> left, right, showWhichLines);
> } else if (showRuler) { // Just do the labels for the
> tens lines
> newgraphics.add(constructTensLabels(up, down,
> left, right, true));
> }
> }
> }
>
> OMGraphicList list;
> if (tenDegreeLines == null) {
> list = constructTenDegreeLines();
> tenDegreeLines = list;
> } else {
> synchronized(tenDegreeLines) {
> setLineTypeAndProject(tenDegreeLines, boxy ?
> OMGraphic.LINETYPE_STRAIGHT : OMGraphic.LINETYPE_RHUMB);
> }
> }
> if (markerLines == null) {
> list = constructMarkerLines();
> markerLines = list;
> } else {
> synchronized(markerLines) {
> setLineTypeAndProject(markerLines, boxy ?
> OMGraphic.LINETYPE_STRAIGHT : OMGraphic.LINETYPE_RHUMB);
> }
> }
>
> newgraphics.add(markerLines);
> newgraphics.add(tenDegreeLines);
>
> if (Debug.debugging("graticule")) {
> Debug.output("GraticuleLayer.constructGraticuleLines(): " +
> "constructed " + newgraphics.size() + "
> graticule lines");
> }
>
> return newgraphics;
> }
>
> /**
> * Figure out which graticule lines should be drawn based on the
> * treshold set in the layer, and the coordinates of the screen.
> * Method checks for crossing of the dateline, but still assumes
> * that the up and down latitude coordinates are less than
> * abs(+/-80). This is because the projection shouldn't give
> * anything above 90 degrees, and we limit the lines to less than
> * 80..
> *
> * _at_param up northern latitude corrdinate, in decimal degrees,
> * _at_param down southern latitude coordinate, in decimal degrees.
> * _at_param left western longitude coordinate, in decimal degrees,
> * _at_param right eastern longitude coordinate, in decimal degrees.
> * _at_return which lines should be shown, either SHOW_TENS,
> * SHOW_FIVES and SHOW_ONES.
> */
> protected int evaluateSpacing(float up, float down,
> float left, float right) {
> int ret = SHOW_TENS;
>
> // Set the flag for when labels are wanted, but not the 1 and
> // 5 lines;
> if (!showOneAndFiveLines && !showBelowOneLines) {
> return ret;
> }
>
> // Find the north - south difference
> float nsdiff = up - down;
> // And the east - west difference
> float ewdiff;
> // Check for straddling the dateline -west is positive while
> // right is negative, or, in a big picture view, the west is
> // positive, east is positive, and western hemisphere is
> // between them.
> if ((left > 0 && right < 0) ||
> (left > right) ||
> (Math.abs(left - right) < 1)) {
> ewdiff = (180.0f - left) + (right + 180.0f);
> } else {
> ewdiff = right - left;
> }
>
> // And use the lesser of the two.
> float diff = (nsdiff < ewdiff)?nsdiff:ewdiff;
> // number of 10 degree lines
> if ((diff/10) <= (float)threshold) ret = SHOW_FIVES;
> // number of five degree lines
> if ((diff/5) <= (float)threshold) ret = SHOW_ONES;
>
> return ret;
> }
>
> /**
> * Construct the five degree and one degree graticule lines,
> * depending on the showWhichLines setting. Assumes that the
> * coordinates passed in do not cross the dateline, and that the
> * up is not greater than 80 and that the south is not less than
> * -80.
> *
> * _at_param up northern latitude corrdinate, in decimal degrees,
> * _at_param down southern latitude coordinate, in decimal degrees.
> * _at_param left western longitude coordinate, in decimal degrees,
> * _at_param right eastern longitude coordinate, in decimal degrees.
> * _at_param showWhichLines indicator for which level of lines should
> * be included, either SHOW_FIVES or SHOW_ONES. SHOW_TENS could
> * be there, too, but then we wouldn't do anything.
> */
> protected OMGraphicList constructGraticuleLines(float up, float
> down,
> float left, float
> right,
> int
> showWhichLines) {
> OMGraphicList lines = new OMGraphicList();
>
> // Set the line limits for the lat/lon lines...
> int north = (int)Math.ceil(up);
> if (north > 80) north = 80;
>
> int south = (int)Math.floor(down);
> south -= (south%10); // Push down to the lowest 10 degree line.
> // for neg numbers, Mod raised it, lower it again. Also
> // handle straddling the equator.
> if ((south < 0 && south > -80) || south == 0) south -= 10;
>
> int west = (int)Math.floor(left);
> west -= (west%10);
> // for neg numbers, Mod raised it, lower it again. Also
> // handle straddling the prime meridian.
> if ((west < 0 && west > -180) || west == 0) west -= 10;
>
> int east = (int)Math.ceil(right);
> if (east > 180) east = 180;
>
> int stepSize;
> // Choose how far apart the lines will be.
> stepSize = ((showWhichLines == SHOW_ONES)? 1:5);
> float[] llp;
> OMPoly currentLine;
> OMText currentText;
>
> // For calculating text locations
> java.awt.Point point;
> LatLonPoint llpoint;
>
> Projection projection = getProjection();
>
> // generate other parallels of latitude be creating series
> // of polylines
> for (int i = south; i < north; i += stepSize) {
> float lat = (float)i;
> // generate parallel of latitude North/South of the equator
> if (west < 0 && east > 0) {
> llp = new float[6];
> llp[2] = lat; llp[3] = 0f;
> llp[4] = lat; llp[5] = east;
> } else {
> llp = new float[4];
> llp[2] = lat; llp[3] = east;
> }
> llp[0] = lat; llp[1] = west;
>
> // Do not duplicate the 10 degree line.
> if ((lat%10) != 0) {
> currentLine = new OMPoly(llp,
> OMGraphic.DECIMAL_DEGREES,
> boxy ?
> OMGraphic.LINETYPE_STRAIGHT
> : OMGraphic.LINETYPE_RHUMB);
> if ((lat%5) == 0) {
> currentLine.setLinePaint(fiveDegreeColor);
> } else {
> currentLine.setLinePaint(oneDegreeColor);
> }
> lines.addOMGraphic(currentLine);
> }
>
> if (showRuler && (lat%2) == 0) {
> if (boxy) {
> point = projection.forward(lat, west);
> point.x = 0;
> llpoint = projection.inverse(point);
> } else {
> llpoint = new LatLonPoint(lat, west);
> while (projection.forward(llpoint).x < 0) {
> llpoint.setLongitude(llpoint.getLongitude() +
> stepSize);
> }
> }
>
> currentText = new OMText(llpoint.getLatitude(),
> llpoint.getLongitude(),
> // Move them up a little
> (int)2, (int) -2,
> Integer.toString((int)lat),
> font, OMText.JUSTIFY_LEFT);
> currentText.setLinePaint(textColor);
> lines.addOMGraphic(currentText);
> }
> }
>
> // generate lines of longitude
> for (int i = west; i < east; i += stepSize) {
> float lon = (float)i;
>
> if (north < 0 && south > 0) {
> llp = new float[6];
> llp[2] = 0f; llp[3] = lon;
> llp[4] = south; llp[5] = lon;
> } else {
> llp = new float[4];
> llp[2] = south; llp[3] = lon;
> }
> llp[0] = north; llp[1] = lon;
>
> if ((lon%10) != 0) {
> currentLine = new OMPoly(llp,
> OMGraphic.DECIMAL_DEGREES,
> boxy ?
> OMGraphic.LINETYPE_STRAIGHT
> :
> OMGraphic.LINETYPE_GREATCIRCLE);
> if ((lon%5) == 0) {
> currentLine.setLinePaint(fiveDegreeColor);
> } else {
> currentLine.setLinePaint(oneDegreeColor);
> }
> lines.addOMGraphic(currentLine);
> }
>
> if (showRuler && (lon%2) == 0) {
> if (boxy) {
> point = projection.forward(south, lon);
> point.y = projection.getHeight();
> llpoint = projection.inverse(point);
> } else {
> llpoint = new LatLonPoint(south, lon);
> while (projection.forward(llpoint).y >
> projection.getHeight()) {
> llpoint.setLatitude(llpoint.getLatitude() +
> stepSize);
> }
> }
>
> currentText = new OMText(llpoint.getLatitude(),
> llpoint.getLongitude(),
> // Move them up a little
> (int)2, (int) -5,
> Integer.toString((int)lon),
> font, OMText.JUSTIFY_CENTER);
> currentText.setLinePaint(textColor);
> lines.addOMGraphic(currentText);
>
> }
> }
>
> if (Debug.debugging("graticule")) {
> Debug.output("GraticuleLayer.constructTenDegreeLines(): " +
> "constructed " + lines.size() + " graticule
> lines");
> }
> lines.generate(projection);
> return lines;
> }
>
> /** Create the ten degree lines. */
> protected OMGraphicList constructTenDegreeLines() {
>
> OMGraphicList lines = new OMGraphicList(3);
> OMPoly currentLine;
>
> // generate other parallels of latitude by creating series
> // of polylines
> for (int i = 1; i <= 8; i++) {
> for (int j = -1; j < 2; j += 2) {
> float lat = (float)(10*i*j);
> // generate parallel of latitude North/South of the
> equator
> float[] llp = {lat, -180f, lat, -90f,
> lat, 0f, lat, 90f, lat, 180f};
> currentLine = new OMPoly(llp,
> OMGraphic.DECIMAL_DEGREES,
> boxy ?
> OMGraphic.LINETYPE_STRAIGHT
> : OMGraphic.LINETYPE_RHUMB);
> currentLine.setLinePaint(tenDegreeColor);
> lines.addOMGraphic(currentLine);
> }
> }
>
> // generate lines of longitude
> for (int i = 1; i < 18; i++) {
> for (int j = -1; j < 2; j += 2) {
> float lon = (float)(10*i*j);
> //not quite 90.0 for beautification reasons.
> float[] llp = {80f, lon,
> 0f, lon,
> -80f, lon};
> if (MoreMath.approximately_equal(Math.abs(lon), 90f,
> 0.001f)) {
> llp[0] = 90f;
> llp[4] = -90f;
> }
> currentLine = new OMPoly(llp,
> OMGraphic.DECIMAL_DEGREES,
> boxy ?
> OMGraphic.LINETYPE_STRAIGHT
> :
> OMGraphic.LINETYPE_GREATCIRCLE);
> currentLine.setLinePaint(tenDegreeColor);
> lines.addOMGraphic(currentLine);
> }
> }
>
> if (Debug.debugging("graticule")) {
> Debug.output("GraticuleLayer.constructTenDegreeLines(): " +
> "constructed " + lines.size() + " graticule
> lines");
> }
> lines.generate(getProjection());
> return lines;
> }
>
> /**
> * Constructs the labels for the tens lines. Called from within
> * the constructGraticuleLines if the showRuler variable is true.
> * Usually called only if the ones and fives lines are not being
> * drawn.
> *
> * _at_param up northern latitude corrdinate, in decimal degrees,
> * _at_param down southern latitude coordinate, in decimal degrees.
> * _at_param left western longitude coordinate, in decimal degrees,
> * _at_param right eastern longitude coordinate, in decimal degrees.
> * _at_param doLats do the latitude labels if true.
> * _at_return OMGraphicList of labels.
> */
> protected OMGraphicList constructTensLabels(float up, float down,
> float left, float
> right,
> boolean doLats) {
>
> OMGraphicList labels = new OMGraphicList();
>
> // Set the line limits for the lat/lon lines...
> int north = (int)Math.ceil(up);
> if (north > 80) north = 80;
>
> int south = (int)Math.floor(down);
> south -= (south%10); // Push down to the lowest 10 degree line.
> // for neg numbers, Mod raised it, lower it again
> if ((south < 0 && south > -70) || south == 0) {
> south -= 10;
> }
>
> int west = (int)Math.floor(left);
> west -= (west%10);
> // for neg numbers, Mod raised it, lower it again
> if ((west < 0 && west > -170) || west == 0) {
> west -= 10;
> }
>
> int east = (int)Math.ceil(right);
> if (east > 180) east = 180;
>
> int stepSize = 10;
> OMText currentText;
>
> // For calculating text locations
> java.awt.Point point;
> LatLonPoint llpoint;
> Projection projection = getProjection();
>
> if (doLats) {
>
> // generate other parallels of latitude be creating series
> // of labels
> for (int i = south; i < north; i += stepSize) {
> float lat = (float)i;
>
> if ((lat%2) == 0) {
> if (boxy) {
> point = projection.forward(lat, west);
> point.x = 0;
> llpoint = projection.inverse(point);
> } else {
> llpoint = new LatLonPoint(lat, west);
> while (projection.forward(llpoint).x < 0) {
>
> llpoint.setLongitude(llpoint.getLongitude() + stepSize);
> }
> }
>
> currentText = new OMText(llpoint.getLatitude(),
> llpoint.getLongitude(),
> (int)2, (int) -2, // Move
> them up a little
>
> Integer.toString((int)lat),
> font,
> OMText.JUSTIFY_LEFT);
> currentText.setLinePaint(textColor);
> labels.addOMGraphic(currentText);
> }
> }
> }
>
> // generate labels of longitude
> for (int i = west; i < east; i += stepSize) {
> float lon = (float)i;
>
> if ((lon%2) == 0) {
> if (boxy) {
> point = projection.forward(south, lon);
> point.y = projection.getHeight();
> llpoint = projection.inverse(point);
> } else {
> llpoint = new LatLonPoint(south, lon);
> while (projection.forward(llpoint).y >
> projection.getHeight()) {
> llpoint.setLatitude(llpoint.getLatitude() +
> stepSize);
> }
> }
>
> currentText = new OMText(llpoint.getLatitude(),
> llpoint.getLongitude(),
> // Move them up a little
> (int)2, (int) -5,
> Integer.toString((int)lon),
> font, OMText.JUSTIFY_CENTER);
> currentText.setLinePaint(textColor);
> labels.addOMGraphic(currentText);
>
> }
> }
>
> if (Debug.debugging("graticule")) {
> Debug.output("GraticuleLayer.constructTensLabels(): " +
> "constructed " + labels.size() + " graticule
> labels");
> }
> labels.generate(projection);
> return labels;
> }
>
> /** Constructs the Dateline and Prime Meridian lines. */
> protected OMGraphicList constructMarkerLines() {
>
> OMGraphicList lines = new OMGraphicList(3);
> OMPoly currentLine;
>
> // generate Prime Meridian and Dateline
> for (int j = 0; j < 360; j += 180) {
> float lon = (float)j;
> float[] llp = {90f, lon,
> 0f, lon,
> -90f, lon};
> currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES,
> boxy ? OMGraphic.LINETYPE_STRAIGHT
> : OMGraphic.LINETYPE_GREATCIRCLE);
> currentLine.setLinePaint(dateLineColor);
> lines.addOMGraphic(currentLine);
> }
>
> // equator
> float[] llp = {0f, -180f,
> 0f, -90f,
> 0f, 0f,
> 0f, 90f,
> 0f, 180f};
> // polyline
> currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES,
> boxy ? OMGraphic.LINETYPE_STRAIGHT
> : OMGraphic.LINETYPE_GREATCIRCLE);
> currentLine.setLinePaint(equatorColor);
> lines.addOMGraphic(currentLine);
>
> if (Debug.debugging("graticule")) {
> Debug.output("GraticuleLayer.constructMarkerLines(): " +
> "constructed " + lines.size() + " graticule
> lines");
> }
> lines.generate(getProjection());
> return lines;
> }
>
> /**
> * Take a graphic list, and set all the items on the list to the
> * line type specified, and project them into the current
> * projection.
> *
> * _at_param list the list containing the lines to change.
> * _at_param lineType the line type to cahnge the lines to. */
> protected void setLineTypeAndProject(OMGraphicList list, int
> lineType) {
> int size = list.size();
> OMGraphic graphic;
> for (int i = 0; i < size; i++) {
> graphic = list.getOMGraphicAt(i);
> graphic.setLineType(lineType);
> graphic.generate(getProjection());
> }
> }
>
>
>
> /
> /----------------------------------------------------------------------
> // GUI
>
> /
> /----------------------------------------------------------------------
>
> /** The user interface palette for the DTED layer. */
> protected Box palette = null;
>
> /** Creates the interface palette. */
> public java.awt.Component getGUI() {
>
> if (palette == null) {
> if (Debug.debugging("graticule"))
> Debug.output("GraticuleLayer: creating Graticule
> Palette.");
>
> palette = Box.createVerticalBox();
>
> JPanel layerPanel =
> PaletteHelper.createPaletteJPanel(i18n.get(GraticuleLayer.class,"layerP
> anel","Graticule Layer Options"));
>
> ActionListener al = new ActionListener() {
> public void actionPerformed(ActionEvent e) {
> String ac = e.getActionCommand();
>
> if (ac.equalsIgnoreCase(ShowRulerProperty)) {
> JCheckBox jcb = (JCheckBox)e.getSource();
> showRuler = jcb.isSelected();
> } else if
> (ac.equalsIgnoreCase(ShowOneAndFiveProperty)) {
> JCheckBox jcb = (JCheckBox)e.getSource();
> showOneAndFiveLines = jcb.isSelected();
> } else {
> Debug.error("Unknown action command \"" +
> ac +
> "\" in
> GraticuleLayer.actionPerformed().");
> }
> }
> };
>
> showRulerButton = new
> JCheckBox(i18n.get(GraticuleLayer.class,"showRulerButton","Show
> Lat/Lon Labels"), showRuler);
> showRulerButton.addActionListener(al);
> showRulerButton.setActionCommand(ShowRulerProperty);
>
> show15Button = new
> JCheckBox(i18n.get(GraticuleLayer.class,"show15Button","Show 1, 5
> Degree Lines"), showOneAndFiveLines);
> show15Button.addActionListener(al);
> show15Button.setActionCommand(ShowOneAndFiveProperty);
>
>
> layerPanel.add(showRulerButton);
> layerPanel.add(show15Button);
> palette.add(layerPanel);
>
> JPanel subbox3 = new JPanel(new GridLayout(0, 1));
>
> JButton setProperties = new
> JButton(i18n.get(GraticuleLayer.class,"setProperties","Preferences"));
> setProperties.setActionCommand(DisplayPropertiesCmd);
> setProperties.addActionListener(this);
> subbox3.add(setProperties);
>
> JButton redraw = new
> JButton(i18n.get(GraticuleLayer.class,"redraw","Redraw Graticule
> Layer"));
> redraw.setActionCommand(RedrawCmd);
> redraw.addActionListener(this);
> subbox3.add(redraw);
> palette.add(subbox3);
> }
> return palette;
> }
>
>
> /
> /----------------------------------------------------------------------
> // ActionListener interface implementation
>
> /
> /----------------------------------------------------------------------
>
> /**
> * Used just for the redraw button.
> */
> public void actionPerformed(ActionEvent e) {
> super.actionPerformed(e);
> String command = e.getActionCommand();
>
> if (command == RedrawCmd) {
> //redrawbutton
> if (isVisible()) {
> doPrepare();
> }
> }
> }
>
> }
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/plugin/
> AbstractPlugIn.java,v $
> // $RCSfile: AbstractPlugIn.java,v $
> // $Revision: 1.7 $
> // $Date: 2004/02/04 22:51:38 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
>
> package com.bbn.openmap.plugin;
>
> import java.awt.Component;
> import java.awt.event.MouseEvent;
> import java.util.Properties;
>
> import com.bbn.openmap.Environment;
> import com.bbn.openmap.I18n;
> import com.bbn.openmap.Layer;
> import com.bbn.openmap.PropertyConsumer;
> import com.bbn.openmap.event.MapMouseListener;
> import com.bbn.openmap.event.SelectMouseMode;
> import com.bbn.openmap.layer.util.LayerUtils;
> import com.bbn.openmap.omGraphics.OMGraphicList;
> import com.bbn.openmap.proj.Projection;
> import com.bbn.openmap.util.Debug;
> import com.bbn.openmap.util.PropUtils;
>
> /**
> * This class is an abstract implementation of the PlugIn. It takes
> * care of setting up the layer, setting properties, etc.
> * _at_see com.bbn.openmap.plugin.PlugInLayer
> * _at_see com.bbn.openmap.plugin.PlugIn
> */
> public abstract class AbstractPlugIn
> implements PlugIn, PropertyConsumer, MapMouseListener {
>
> /**
> * Property 'removeable' to designate this layer as removeable
> * from the application, or able to be deleted. True by default.
> */
> public static final String RemoveableProperty = "removeable";
>
> /**
> * Flag to designate the layer as removeable or not.
> */
> protected boolean removeable = true;
>
> /** The parent component, usually the PlugInLayer. */
> protected Component component = null;
> /** The prefix for the plugin's properties. */
> protected String prefix = null;
> /**
> * The pretty name for a plugin, if it was set in the properties.
> */
> protected String name = this.getClass().getName();
>
> /**
> * The object handling mouse events for the plugin. By default,
> * the Plugin is it, but it doesn't have to be.
> */
> protected MapMouseListener mml = this;
>
> /**
> * Flag to denote whether the plugin should be added to the bean
> * context (MapHandler). True by default.
> */
> protected boolean addToBeanContext = true;
>
> /**
> * Internationalization
> */
> public I18n i18n = Environment.getI18n();
>
> public AbstractPlugIn() {}
>
> public AbstractPlugIn(Component comp) {
> setComponent(comp);
> }
>
> /**
> * Set the name of the plugin. If the parent component is a
> * layer, set its pretty name as well.
> */
> public void setName(String name) {
> this.name = name;
> Component comp = getComponent();
> if (comp != null) {
> comp.setName(name);
> }
> }
>
> /**
> * Get the pretty name of the plugin, which is really the pretty
> * name of the parent component if it's set.
> */
> public String getName() {
> Component comp = getComponent();
> if (comp != null) {
> name = comp.getName();
> }
> return name;
> }
>
> /**
> * Set the component that this PlugIn uses as a grip to the map.
> */
> public void setComponent(Component comp) {
> this.component = comp;
> }
>
> /**
> * Get the component that this plugin uses as a grip to the map.
> */
> public Component getComponent() {
> return component;
> }
>
> /**
> * Call repaint on the parent component.
> */
> public void repaint() {
> component.repaint();
> }
>
> /**
> * Checks to see if the parent component is a PlugInLayer, and
> * calls doPrepare() on it if it is.
> */
> public void doPrepare() {
> if (component instanceof PlugInLayer) {
> ((PlugInLayer)component).doPrepare();
> }
> }
>
> /**
> * Set the MapMouseListener for this PlugIn. The MapMouseListener
> * is responsible for handling the MouseEvents that are occuring
> * over the layer using the PlugIn, as well as being able to let
> * others know which MouseModes are of interest to receive
> * MouseEvents from.
> *
> * _at_param mml MapMouseListener.
> */
> public void setMapMouseListener(MapMouseListener mml) {
> this.mml = mml;
> }
>
> /**
> * Returns the MapMouseListener that the plugin thinks should be
> used.
> */
> public MapMouseListener getMapMouseListener() {
> return mml;
> }
>
> /**
> * The getRectangle call is the main call into the PlugIn module.
> * The module is expected to fill a graphics list with objects
> * that are within the screen parameters passed. It's assumed that
> * the PlugIn will call generate(projection) on the OMGraphics
> * returned! If you don't call generate on the OMGraphics, they
> * will not be displayed on the map.
> *
> * _at_param p projection of the screen, holding scale, center
> * coords, height, width. May be null if the parent component
> * hasn't been given a projection.
> */
> public abstract OMGraphicList getRectangle(Projection p);
>
> /**
> */
> public Component getGUI() {
> return null;
> }
>
> public void setAddToBeanContext(boolean value) {
> addToBeanContext = value;
> }
>
> public boolean getAddToBeanContext() {
> return addToBeanContext;
> }
>
> /**
> * Mark the plugin (and layer) as removeable, or one that can be
> deleted from
> * the application. What that means is up to the LayerHandler or
> * other application components.
> */
> public void setRemoveable(boolean set) {
> this.removeable = set;
> Component comp = getComponent();
> if ((comp != null) && (comp instanceof Layer)) {
> ((Layer)comp).setRemoveable(set);
> }
> }
>
> /**
> * Check to see if the plugin (and layer) is marked as one that
> can be removed
> * from an application.
> * _at_return true if plugin should be allowed to be deleted.
> */
> public boolean isRemoveable() {
> Component comp = getComponent();
> if ((comp != null) && (comp instanceof Layer)) {
> this.removeable = ((Layer)comp).isRemoveable();
> }
> return removeable;
> }
>
> ////// PropertyConsumer Interface Methods
>
> /**
> * Method to set the properties in the PropertyConsumer. It is
> * assumed that the properties do not have a prefix associated
> * with them, or that the prefix has already been set.
> *
> * _at_param setList a properties object that the PropertyConsumer
> * can use to retrieve expected properties it can use for
> * configuration.
> */
> public void setProperties(Properties setList) {
> setProperties(null, setList);
> }
>
> /**
> * Method to set the properties in the PropertyConsumer. The
> * prefix is a string that should be prepended to each property
> * key (in addition to a separating '.') in order for the
> * PropertyConsumer to uniquely identify properies meant for it, in
> * the midst of of Properties meant for several objects.
> *
> * _at_param prefix a String used by the PropertyConsumer to prepend
> * to each property value it wants to look up -
> * setList.getProperty(prefix.propertyKey). If the prefix had
> * already been set, then the prefix passed in should replace that
> * previous value.
> * _at_param setList a Properties object that the PropertyConsumer
> * can use to retrieve expected properties it can use for
> * configuration.
> */
> public void setProperties(String prefix, Properties setList) {
> setPropertyPrefix(prefix);
>
> String realPrefix = PropUtils.getScopedPropertyPrefix(prefix);
>
> name = setList.getProperty(realPrefix +
> Layer.PrettyNameProperty);
> setAddToBeanContext(LayerUtils.booleanFromProperties(setList,
> realPrefix + Layer.AddToBeanContextProperty, addToBeanContext));
> setRemoveable(PropUtils.booleanFromProperties(setList,
> realPrefix + RemoveableProperty, removeable));
> }
>
> /**
> * Method to fill in a Properties object, reflecting the current
> * values of the PropertyConsumer. If the PropertyConsumer has a
> * prefix set, the property keys should have that prefix plus a
> * separating '.' prepended to each propery key it uses for
> * configuration.
> *
> * _at_param getList a Properties object to load the PropertyConsumer
> * properties into. If getList equals null, then a new Properties
> * object should be created.
> * _at_return Properties object containing PropertyConsumer property
> * values. If getList was not null, this should equal getList.
> * Otherwise, it should be the Properties object created by the
> * PropertyConsumer.
> */
> public Properties getProperties(Properties getList) {
> if (getList == null) {
> getList = new Properties();
> }
>
> String realPrefix = PropUtils.getScopedPropertyPrefix(this);
> getList.put(realPrefix + Layer.AddToBeanContextProperty,
> new Boolean(addToBeanContext).toString());
> getList.put(prefix + RemoveableProperty, new
> Boolean(removeable).toString());
> return getList;
> }
>
> /**
> * Method to fill in a Properties object with values reflecting
> * the properties able to be set on this PropertyConsumer. The
> * key for each property should be the raw property name (without
> * a prefix) with a value that is a String that describes what the
> * property key represents, along with any other information about
> * the property that would be helpful (range, default value,
> * etc.).
> *
> * _at_param list a Properties object to load the PropertyConsumer
> * properties into. If getList equals null, then a new Properties
> * object should be created.
> * _at_return Properties object containing PropertyConsumer property
> * values. If getList was not null, this should equal getList.
> * Otherwise, it should be the Properties object created by the
> * PropertyConsumer.
> */
> public Properties getPropertyInfo(Properties list) {
> if (list == null) {
> list = new Properties();
> }
> list.put(Layer.AddToBeanContextProperty, "Flag to give the
> PlugIn access to all of the other application components.");
> list.put(Layer.AddToBeanContextProperty +
> ScopedEditorProperty,
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> String internString = i18n.get(AbstractPlugIn.class,
> RemoveableProperty, I18n.TOOLTIP, "Flag to allow layer to be
> deleted.");
> list.put(RemoveableProperty, internString);
> internString = i18n.get(Layer.class, RemoveableProperty,
> "Removeable");
> list.put(RemoveableProperty + LabelEditorProperty,
> internString);
> list.put(RemoveableProperty + ScopedEditorProperty,
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> return list;
> }
>
> /**
> * Set the property key prefix that should be used by the
> * PropertyConsumer. The prefix, along with a '.', should be
> * prepended to the property keys known by the PropertyConsumer.
> *
> * _at_param prefix the prefix String.
> */
> public void setPropertyPrefix(String prefix) {
> this.prefix = prefix;
> }
>
> /**
> * Get the property key prefix that is being used to prepend to
> * the property keys for Properties lookups.
> *
> * _at_return the property prefix for the plugin.
> */
> public String getPropertyPrefix() {
> return prefix;
> }
>
> ///////// MapMouseListener interface methods
>
> /**
> * Return a list of the modes that are interesting to the
> * MapMouseListener. The source MouseEvents will only get sent to
> * the MapMouseListener if the mode is set to one that the
> * listener is interested in.
> * Layers interested in receiving events should register for
> * receiving events in "select" mode:
> * <code>
> * <pre>
> * return new String[] {
> * SelectMouseMode.modeID
> * };
> * </pre>
> * <code>
> * _at_return String[] of modeID's
> * _at_see com.bbn.openmap.event.NavMouseMode#modeID
> * _at_see com.bbn.openmap.event.SelectMouseMode#modeID
> * _at_see com.bbn.openmap.event.NullMouseMode#modeID
> */
> public String[] getMouseModeServiceList() {
> return new String[] {SelectMouseMode.modeID};
> }
>
> // Mouse Listener events
> ////////////////////////
>
> /**
> * Invoked when a mouse button has been pressed on a component.
> * _at_param e MouseEvent
> * _at_return true if the listener was able to process the event.
> */
> public boolean mousePressed(MouseEvent e) {
> return false;
> }
>
> /**
> * Invoked when a mouse button has been released on a component.
> * _at_param e MouseEvent
> * _at_return true if the listener was able to process the event.
> */
> public boolean mouseReleased(MouseEvent e) {
> return false;
> }
>
> /**
> * Invoked when the mouse has been clicked on a component.
> * The listener will receive this event if it successfully
> * processed <code>mousePressed()</code>, or if no other listener
> * processes the event. If the listener successfully processes
> * <code>mouseClicked()</code>, then it will receive the next
> * <code>mouseClicked()</code> notifications that have a click
> * count greater than one.
> * <p>
> * NOTE: We have noticed that this method can sometimes be
> * erroneously invoked. It seems to occur when a light-weight AWT
> * component (like an internal window or menu) closes (removes
> * itself from the window hierarchy). A specific OpenMap example
> * is when you make a menu selection when the MenuItem you select
> * is above the MapBean canvas. After making the selection, the
> * mouseClicked() gets invoked on the MouseDelegator, which passes
> * it to the appropriate listeners depending on the MouseMode.
> * The best way to avoid this problem is to not implement anything
> * crucial in this method. Use a combination of
> * <code>mousePressed()</code> and <code>mouseReleased()</code>
> * instead.
> * _at_param e MouseEvent
> * _at_return true if the listener was able to process the event.
> */
> public boolean mouseClicked(MouseEvent e) {
> return false;
> }
>
> /**
> * Invoked when the mouse enters a component.
> * _at_param e MouseEvent
> */
> public void mouseEntered(MouseEvent e) {}
>
> /**
> * Invoked when the mouse exits a component.
> * _at_param e MouseEvent
> */
> public void mouseExited(MouseEvent e) {}
>
> // Mouse Motion Listener events
> ///////////////////////////////
>
> /**
> * Invoked when a mouse button is pressed on a component and then
> * dragged. The listener will receive these events if it
> * successfully processes mousePressed(), or if no other listener
> * processes the event.
> * _at_param e MouseEvent
> * _at_return true if the listener was able to process the event.
> */
> public boolean mouseDragged(MouseEvent e) {
> return false;
> }
>
> /**
> * Invoked when the mouse button has been moved on a component
> * (with no buttons down).
> * _at_param e MouseEvent
> * _at_return true if the listener was able to process the event.
> */
> public boolean mouseMoved(MouseEvent e) {
> return false;
> }
>
> /**
> * Handle a mouse cursor moving without the button being pressed.
> * This event is intended to tell the listener that there was a
> * mouse movement, but that the event was consumed by another
> * layer. This will allow a mouse listener to clean up actions
> * that might have happened because of another motion event
> * response.
> */
> public void mouseMoved() {}
>
> /**
> * Method that gets called when the PlugInLayer has been removed
> * from the map, so the PlugIn can free up resources.
> */
> public void removed() {}
>
> }
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/proj/ProjMath.java,v
> $
> // $RCSfile: ProjMath.java,v $
> // $Revision: 1.4 $
> // $Date: 2004/10/14 18:18:22 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.proj;
>
> import com.bbn.openmap.MoreMath;
>
> /**
> * Math functions used by projection code.
> */
> public final class ProjMath {
>
> /**
> * North pole latitude in radians.
> */
> public final static transient float NORTH_POLE_F =
> MoreMath.HALF_PI;
>
> /**
> * South pole latitude in radians.
> */
> public final static transient float SOUTH_POLE_F = -NORTH_POLE_F;
>
> /**
> * North pole latitude in radians.
> */
> public final static transient double NORTH_POLE_D =
> MoreMath.HALF_PI_D;
>
> /**
> * South pole latitude in radians.
> */
> public final static transient double SOUTH_POLE_D = -NORTH_POLE_D;
>
> /**
> * Dateline longitude in radians.
> */
> public final static transient float DATELINE_F = (float) Math.PI;
>
> /**
> * Dateline longitude in radians.
> */
> public final static transient double DATELINE_D = Math.PI;
>
> /**
> * Longitude range in radians.
> */
> public final static transient float LON_RANGE_F = (float)
> MoreMath.TWO_PI;
>
> /**
> * Longitude range in radians.
> */
> public final static transient double LON_RANGE_D =
> MoreMath.TWO_PI_D;
>
> // cannot construct
> private ProjMath() {}
>
> /**
> * rounds the quantity away from 0.
> *
> * _at_param x in value
> * _at_return double
> * _at_see #qint(double)
> */
> final public static double roundAdjust(double x) {
> return qint_old(x);
> }
>
> /**
> * Rounds the quantity away from 0.
> *
> * _at_param x value
> * _at_return double
> */
> final public static double qint(double x) {
> return qint_new(x);
> }
>
> final private static double qint_old(double x) {
> return (((int) x) < 0) ? (x - 0.5) : (x + 0.5);
> }
>
> final private static double qint_new(double x) {
> // -1 or +1 away from zero
> return (x <= 0.0) ? (x - 1.0) : (x + 1.0);
> }
>
> /**
> * Calculate the shortest arc distance between two lons.
> *
> * _at_param lon1 radians
> * _at_param lon2 radians
> * _at_return float distance
> */
> final public static float lonDistance(float lon1, float lon2) {
> return (float) Math.min(Math.abs(lon1 - lon2), ((lon1 < 0) ?
> lon1
> + Math.PI : Math.PI - lon1)
> + ((lon2 < 0) ? lon2 + Math.PI : Math.PI - lon2));
> }
>
> /**
> * Convert between decimal degrees and scoords.
> *
> * _at_param deg degrees
> * _at_return long scoords
> *
> */
> final public static long DEG_TO_SC(double deg) {
> return (long) (deg * 3600000);
> }
>
> /**
> * Convert between decimal degrees and scoords.
> *
> * _at_param sc scoords
> * _at_return double decimal degrees
> */
> final public static double SC_TO_DEG(int sc) {
> return ((double) (sc) / (60.0 * 60.0 * 1000.0));
> }
>
> /**
> * Convert radians to degrees.
> *
> * _at_param rad radians
> * _at_return double decimal degrees
> */
> final public static double radToDeg(double rad) {
> return (rad * (180.0d / Math.PI));
> }
>
> /**
> * Convert radians to degrees.
> *
> * _at_param rad radians
> * _at_return float decimal degrees
> */
> final public static float radToDeg(float rad) {
> return (float)radToDeg((double)rad);
> }
>
> /**
> * Convert degrees to radians.
> *
> * _at_param deg degrees
> * _at_return double radians
> */
> final public static double degToRad(double deg) {
> return (deg * (Math.PI / 180.0d));
> }
>
> /**
> * Convert degrees to radians.
> *
> * _at_param deg degrees
> * _at_return float radians
> */
> final public static float degToRad(float deg) {
> return (float)degToRad((double)deg);
> }
>
> /**
> * Generate a hashCode value for a lat/lon pair.
> *
> * _at_param lat latitude
> * _at_param lon longitude
> * _at_return int hashcode
> *
> */
> final public static int hashLatLon(float lat, float lon) {
> if (lat == -0f)
> lat = 0f;//handle negative zero (anything else?)
> if (lon == -0f)
> lon = 0f;
> int tmp = Float.floatToIntBits(lat);
> int hash = (tmp << 5) | (tmp >> 27);//rotate the lat bits
> return hash ^ Float.floatToIntBits(lon);//XOR with lon
> }
>
> /**
> * Converts an array of decimal degrees float lat/lons to float
> * radians in place.
> *
> * _at_param degs float[] lat/lons in decimal degrees
> * _at_return float[] lat/lons in radians
> */
> final public static float[] arrayDegToRad(float[] degs) {
> for (int i = 0; i < degs.length; i++) {
> degs[i] = degToRad(degs[i]);
> }
> return degs;
> }
>
> /**
> * Converts an array of radian float lat/lons to decimal degrees
> * in place.
> *
> * _at_param rads float[] lat/lons in radians
> * _at_return float[] lat/lons in decimal degrees
> */
> final public static float[] arrayRadToDeg(float[] rads) {
> for (int i = 0; i < rads.length; i++) {
> rads[i] = radToDeg(rads[i]);
> }
> return rads;
> }
>
> /**
> * Converts an array of decimal degrees double lat/lons to double
> * radians in place.
> *
> * _at_param degs double[] lat/lons in decimal degrees
> * _at_return double[] lat/lons in radians
> */
> final public static double[] arrayDegToRad(double[] degs) {
> for (int i = 0; i < degs.length; i++) {
> degs[i] = degToRad(degs[i]);
> }
> return degs;
> }
>
> /**
> * Converts an array of radian double lat/lons to decimal degrees
> * in place.
> *
> * _at_param rads double[] lat/lons in radians
> * _at_return double[] lat/lons in decimal degrees
> */
> final public static double[] arrayRadToDeg(double[] rads) {
> for (int i = 0; i < rads.length; i++) {
> rads[i] = radToDeg(rads[i]);
> }
> return rads;
> }
>
> /**
> * Normalizes radian latitude. Normalizes latitude if at or
> * exceeds epsilon distance from a pole.
> *
> * _at_param lat float latitude in radians
> * _at_param epsilon epsilon (&gt;= 0) radians distance from pole
> * _at_return float latitude (-PI/2 &lt;= phi &lt;= PI/2)
> * _at_see Proj#normalize_latitude(float)
> * _at_see com.bbn.openmap.LatLonPoint#normalize_latitude(float)
> */
> public final static float normalize_latitude(float lat, float
> epsilon) {
> if (lat > NORTH_POLE_F - epsilon) {
> return NORTH_POLE_F - epsilon;
> } else if (lat < SOUTH_POLE_F + epsilon) {
> return SOUTH_POLE_F + epsilon;
> }
> return lat;
> }
>
> /**
> * Normalizes radian latitude. Normalizes latitude if at or
> * exceeds epsilon distance from a pole.
> *
> * _at_param lat double latitude in radians
> * _at_param epsilon epsilon (&gt;= 0) radians distance from pole
> * _at_return double latitude (-PI/2 &lt;= phi &lt;= PI/2)
> * _at_see Proj#normalize_latitude(float)
> * _at_see com.bbn.openmap.LatLonPoint#normalize_latitude(float)
> */
> public final static double normalize_latitude(double lat, double
> epsilon) {
> if (lat > NORTH_POLE_D - epsilon) {
> return NORTH_POLE_D - epsilon;
> } else if (lat < SOUTH_POLE_D + epsilon) {
> return SOUTH_POLE_D + epsilon;
> }
> return lat;
> }
>
> /**
> * Sets radian longitude to something sane.
> *
> * _at_param lon float longitude in radians
> * _at_return float longitude (-PI &lt;= lambda &lt; PI)
> * _at_see com.bbn.openmap.LatLonPoint#wrap_longitude(float)
> */
> public final static float wrap_longitude(float lon) {
> if ((lon < -DATELINE_F) || (lon > DATELINE_F)) {
> lon += DATELINE_F;
> lon = (lon % LON_RANGE_F);
> lon += (lon < 0) ? DATELINE_F : -DATELINE_F;
> }
> return lon;
> }
>
> /**
> * Sets radian longitude to something sane.
> *
> * _at_param lon double longitude in radians
> * _at_return double longitude (-PI &lt;= lambda &lt; PI)
> * _at_see #wrap_longitude(float)
> */
> public final static double wrap_longitude(double lon) {
> if ((lon < -DATELINE_D) || (lon > DATELINE_D)) {
> lon += DATELINE_D;
> lon = (lon % LON_RANGE_D);
> lon += (lon < 0) ? DATELINE_D : -DATELINE_D;
> }
> return lon;
> }
>
> /**
> * Converts units (km, nm, miles, etc) to decimal degrees for a
> * spherical planet. This does not check for arc distances &gt;
> * 1/2 planet circumference, which are better represented as (2pi -
> * calculated arc).
> *
> * _at_param u units float value
> * _at_param uCircumference units circumference of planet
> * _at_return float decimal degrees
> */
> final public static float sphericalUnitsToDeg(float u, float
> uCircumference) {
> return 360f * (u / uCircumference);
> }
>
> /**
> * Converts units (km, nm, miles, etc) to arc radians for a
> * spherical planet. This does not check for arc distances &gt;
> * 1/2 planet circumference, which are better represented as (2pi -
> * calculated arc).
> *
> * _at_param u units float value
> * _at_param uCircumference units circumference of planet
> * _at_return float arc radians
> */
> final public static float sphericalUnitsToRad(float u, float
> uCircumference) {
> return MoreMath.TWO_PI * (u / uCircumference);
> }
>
> /**
> * Calculate the geocentric latitude given a geographic latitude.
> * According to John Synder: <br>
> * "The geographic or geodetic latitude is the angle which a line
> * perpendicular to the surface of the ellipsoid at the given
> * point makes with the plane of the equator. ...The geocentric
> * latitude is the angle made by a line to the center of the
> * ellipsoid with the equatorial plane". ( <i>Map Projections --A
> * Working Manual </i>, p 13)
> * <p>
> * Translated from Ken Anderson's lisp code <i>Freeing the Essence
> * of Computation </i>
> *
> * _at_param lat float geographic latitude in radians
> * _at_param flat float flatening factor
> * _at_return float geocentric latitude in radians
> * _at_see #geographic_latitude
> */
> public final static float geocentric_latitude(float lat, float
> flat) {
> float f = 1.0f - flat;
> return (float) Math.atan((f * f) * (float) Math.tan(lat));
> }
>
> /**
> * Calculate the geographic latitude given a geocentric latitude.
> * Translated from Ken Anderson's lisp code <i>Freeing the Essence
> * of Computation </i>
> *
> * _at_param lat float geocentric latitude in radians
> * _at_param flat float flatening factor
> * _at_return float geographic latitude in radians
> * _at_see #geocentric_latitude
> */
> public final static float geographic_latitude(float lat, float
> flat) {
> float f = 1.0f - flat;
> return (float) Math.atan((float) Math.tan(lat) / (f * f));
> }
>
> /**
> * Given a couple of points representing a bounding box, find out
> * what the scale should be in order to make those points appear
> * at the corners of the projection.
> *
> * _at_param ll1 the upper left coordinates of the bounding box.
> * _at_param ll2 the lower right coordinates of the bounding box.
> * _at_param projection the projection to use for other projection
> * parameters, like map width and map height.
> */
> public static float getScale(com.bbn.openmap.LatLonPoint ll1,
> com.bbn.openmap.LatLonPoint ll2,
> Projection projection) {
> if (projection == null) {
> return Float.MAX_VALUE;
> }
>
> java.awt.Point point1 = projection.forward(ll1);
> java.awt.Point point2 = projection.forward(ll2);
>
> return getScale(ll1, ll2, point1, point2, projection);
> }
>
> /**
> * Given a couple of points representing a bounding box, find out
> * what the scale should be in order to make those points appear
> * at the corners of the projection.
> *
> * _at_param point1 a java.awt.Point reflecting a pixel spot on the
> * projection, usually the upper left corner of the area of
> * interest.
> * _at_param point2 a java.awt.Point reflecting a pixel spot on the
> * projection, usually the lower right corner of the area
> * of interest.
> * _at_param projection the projection to use for other projection
> * parameters, like map width and map height.
> */
> public static float getScale(java.awt.Point point1, java.awt.Point
> point2,
> Projection projection) {
>
> if (projection == null) {
> return Float.MAX_VALUE;
> }
>
> com.bbn.openmap.LatLonPoint ll1 = projection.inverse(point1);
> com.bbn.openmap.LatLonPoint ll2 = projection.inverse(point2);
>
> return getScale(ll1, ll2, point1, point2, projection);
> }
>
> /**
> * Given a couple of points representing a bounding box, find out
> * what the scale should be in order to make those points appear
> * at the corners of the projection.
> *
> * _at_param ll1 the upper left coordinates of the bounding box.
> * _at_param ll2 the lower right coordinates of the bounding box.
> * _at_param point1 a java.awt.Point reflecting a pixel spot on the
> * projection that matches the ll1 coordinate, the upper
> * left corner of the area of interest.
> * _at_param point2 a java.awt.Point reflecting a pixel spot on the
> * projection that matches the ll2 coordinate, usually the
> * lower right corner of the area of interest.
> * _at_param projection the projection to use to query to get the
> * scale for, for projection type and height and width.
> */
> protected static float getScale(com.bbn.openmap.LatLonPoint ll1,
> com.bbn.openmap.LatLonPoint ll2,
> java.awt.Point point1,
> java.awt.Point point2, Projection
> projection) {
>
> return projection.getScale(ll1, ll2, point1, point2);
> }
>
> /*
> * public static void main(String[] args) { float degs =
> * sphericalUnitsToRad( Planet.earthEquatorialRadius/2,
> * Planet.earthEquatorialRadius); Debug.output("degs = " + degs);
> * float LAT_DEC_RANGE = 90.0f; float LON_DEC_RANGE = 360.0f;
> * float lat, lon; for (int i = 0; i < 100; i++) { lat =
> * com.bbn.openmap.LatLonPoint.normalize_latitude(
> * (float)Math.random()*LAT_DEC_RANGE); lon =
> * com.bbn.openmap.LatLonPoint.wrap_longitude(
> * (float)Math.random()*LON_DEC_RANGE); Debug.output( "(" + lat +
> * "," + lon + ") : (" + degToRad(lat) + "," + degToRad(lon) + ")
> : (" +
> * radToDeg(degToRad(lat)) + "," + radToDeg(degToRad(lon)) + ")");
> } }
> */
> }//
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/proj/coords/
> MGRSPoint.java,v $
> // $RCSfile: MGRSPoint.java,v $
> // $Revision: 1.8 $
> // $Date: 2004/10/14 18:18:22 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap.proj.coords;
>
>
> import java.io.BufferedInputStream;
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.FileOutputStream;
> import java.io.IOException;
> import java.io.InputStreamReader;
> import java.io.LineNumberReader;
> import java.io.PrintStream;
>
> import com.bbn.openmap.LatLonPoint;
> import com.bbn.openmap.proj.Ellipsoid;
> import com.bbn.openmap.util.ArgParser;
> import com.bbn.openmap.util.Debug;
>
> /**
> * A class representing a MGRS coordinate that has the ability to
> * provide the decimal degree lat/lon equivalent, as well as the UTM
> * equivalent. This class does not do checks to see if the MGRS
> * coordiantes provided actually make sense. It assumes that the
> * values are valid.
> */
> public class MGRSPoint extends UTMPoint {
>
> /**
> * UTM zones are grouped, and assigned to one of a group of 6
> * sets.
> */
> protected final static int NUM_100K_SETS = 6;
> /**
> * The column letters (for easting) of the lower left value, per
> * set.
> */
> public final static int[] SET_ORIGIN_COLUMN_LETTERS = { 'A', 'J',
> 'S', 'A',
> 'J', 'S' };
> /**
> * The row letters (for northing) of the lower left value, per
> * set.
> */
> public final static int[] SET_ORIGIN_ROW_LETTERS = { 'A', 'F',
> 'A', 'F',
> 'A', 'F' };
> /**
> * The column letters (for easting) of the lower left value, per
> * set,, for Bessel Ellipsoid.
> */
> public final static int[] BESSEL_SET_ORIGIN_COLUMN_LETTERS = {
> 'A', 'J',
> 'S', 'A', 'J', 'S' };
> /**
> * The row letters (for northing) of the lower left value, per
> * set, for Bessel Ellipsoid.
> */
> public final static int[] BESSEL_SET_ORIGIN_ROW_LETTERS = { 'L',
> 'R', 'L',
> 'R', 'L', 'R' };
>
> public final static int SET_NORTHING_ROLLOVER = 20000000;
> /**
> * Use 5 digits for northing and easting values, for 1 meter
> * accuracy of coordinate.
> */
> public final static int ACCURACY_1_METER = 5;
> /**
> * Use 4 digits for northing and easting values, for 10 meter
> * accuracy of coordinate.
> */
> public final static int ACCURACY_10_METER = 4;
> /**
> * Use 3 digits for northing and easting values, for 100 meter
> * accuracy of coordinate.
> */
> public final static int ACCURACY_100_METER = 3;
> /**
> * Use 2 digits for northing and easting values, for 1000 meter
> * accuracy of coordinate.
> */
> public final static int ACCURACY_1000_METER = 2;
> /**
> * Use 1 digits for northing and easting values, for 10000 meter
> * accuracy of coordinate.
> */
> public final static int ACCURACY_10000_METER = 1;
>
> /** The set origin column letters to use. */
> protected int[] originColumnLetters = SET_ORIGIN_COLUMN_LETTERS;
> /** The set origin row letters to use. */
> protected int[] originRowLetters = SET_ORIGIN_ROW_LETTERS;
>
> public final static int A = 'A';
> public final static int I = 'I';
> public final static int O = 'O';
> public final static int V = 'V';
> public final static int Z = 'Z';
>
> protected boolean DEBUG = false;
>
> /** The String holding the MGRS coordinate value. */
> protected String mgrs;
>
> /**
> * Controls the number of digits that the MGRS coordinate will
> * have, which directly affects the accuracy of the coordinate.
> * Default is ACCURACY_1_METER, which indicates that MGRS
> * coordinates will have 10 digits (5 easting, 5 northing) after
> * the 100k two letter code, indicating 1 meter resolution.
> */
> protected int accuracy = ACCURACY_1_METER;
>
> /**
> * Point to create if you are going to use the static methods to
> * fill the values in.
> */
> public MGRSPoint() {
> DEBUG = Debug.debugging("mgrs");
> }
>
> /**
> * Constructs a new MGRS instance from a MGRS String.
> */
> public MGRSPoint(String mgrsString) {
> this();
> setMGRS(mgrsString);
> }
>
> /**
> * Contructs a new MGRSPoint instance from values in another
> * MGRSPoint.
> */
> public MGRSPoint(MGRSPoint point) {
> this();
> mgrs = point.mgrs;
> northing = point.northing;
> easting = point.easting;
> zone_number = point.zone_number;
> zone_letter = point.zone_letter;
> accuracy = point.accuracy;
> }
>
> /**
> * Create a MGRSPoint from UTM values;
> */
> public MGRSPoint(float northing, float easting, int zoneNumber,
> char zoneLetter) {
> super(northing, easting, zoneNumber, zoneLetter);
> }
>
> /**
> * Contruct a MGRSPoint from a LatLonPoint, assuming a WGS_84
> * ellipsoid.
> */
> public MGRSPoint(LatLonPoint llpoint) {
> this(llpoint, Ellipsoid.WGS_84);
> }
>
> /**
> * Construct a MGRSPoint from a LatLonPoint and a particular
> * ellipsoid.
> */
> public MGRSPoint(LatLonPoint llpoint, Ellipsoid ellip) {
> this();
> LLtoMGRS(llpoint, ellip, this);
> }
>
> /**
> * Set the MGRS value for this Point. Will be decoded, and the UTM
> * values figured out. You can call toLatLonPoint() to translate
> * it to lat/lon decimal degrees.
> */
> public void setMGRS(String mgrsString) {
> try {
> mgrs = mgrsString.toUpperCase(); // Just to make sure.
> decode(mgrs);
> } catch (StringIndexOutOfBoundsException sioobe) {
> throw new NumberFormatException("MGRSPoint has bad string:
> "
> + mgrsString);
> } catch (NullPointerException npe) {
> // Blow off
> }
> }
>
> /**
> * Get the MGRS string value - the honkin' coordinate value.
> */
> public String getMGRS() {
> if (mgrs == null) {
> resolve();
> }
> return mgrs;
> }
>
> /**
> * Convert this MGRSPoint to a LatLonPoint, and assume a WGS_84
> * ellisoid.
> */
> public LatLonPoint toLatLonPoint() {
> return toLatLonPoint(Ellipsoid.WGS_84, new LatLonPoint());
> }
>
> /**
> * Convert this MGRSPoint to a LatLonPoint, and use the given
> * ellipsoid.
> */
> public LatLonPoint toLatLonPoint(Ellipsoid ellip) {
> return toLatLonPoint(ellip, new LatLonPoint());
> }
>
> /**
> * Fill in the given LatLonPoint with the converted values of this
> * MGRSPoint, and use the given ellipsoid.
> */
> public LatLonPoint toLatLonPoint(Ellipsoid ellip, LatLonPoint
> llpoint) {
> return MGRStoLL(this, ellip, llpoint);
> }
>
> /**
> * Returns a string representation of the object.
> *
> * _at_return String representation
> */
> public String toString() {
> return "MGRSPoint[" + mgrs + "]";
> }
>
> /**
> * Create a LatLonPoint from a MGRSPoint.
> *
> * _at_param mgrsp to convert.
> * _at_param ellip Ellipsoid for earth model.
> * _at_param llp a LatLonPoint to fill in values for. If null, a new
> * LatLonPoint will be returned. If not null, the new
> * values will be set in this object, and it will be
> * returned.
> * _at_return LatLonPoint with values converted from MGRS coordinate.
> */
> public static LatLonPoint MGRStoLL(MGRSPoint mgrsp, Ellipsoid
> ellip,
> LatLonPoint llp) {
> return UTMtoLL(mgrsp, ellip, llp);
> }
>
> /**
> * Converts a LatLonPoint to a MGRS Point, assuming the WGS_84
> * ellipsoid.
> *
> * _at_return MGRSPoint, or null if something bad happened.
> */
> public static MGRSPoint LLtoMGRS(LatLonPoint llpoint) {
> return LLtoMGRS(llpoint, Ellipsoid.WGS_84, new MGRSPoint());
> }
>
> /**
> * Converts a LatLonPoint to a MGRS Point.
> *
> * _at_param llpoint the LatLonPoint to convert.
> * _at_param mgrsp a MGRSPoint to put the results in. If it's null, a
> * MGRSPoint will be allocated.
> * _at_return MGRSPoint, or null if something bad happened. If a
> * MGRSPoint was passed in, it will also be returned on a
> * successful conversion.
> */
> public static MGRSPoint LLtoMGRS(LatLonPoint llpoint, MGRSPoint
> mgrsp) {
> return LLtoMGRS(llpoint, Ellipsoid.WGS_84, mgrsp);
> }
>
> /**
> * Create a MGRSPoint from a LatLonPoint.
> *
> * _at_param llp LatLonPoint to convert.
> * _at_param ellip Ellipsoid for earth model.
> * _at_param mgrsp a MGRSPoint to fill in values for. If null, a new
> * MGRSPoint will be returned. If not null, the new values
> * will be set in this object, and it will be returned.
> * _at_return MGRSPoint with values converted from lat/lon.
> */
> public static MGRSPoint LLtoMGRS(LatLonPoint llp, Ellipsoid ellip,
> MGRSPoint mgrsp) {
> mgrsp = (MGRSPoint) LLtoUTM(llp, ellip, mgrsp);
> mgrsp.resolve();
> return mgrsp;
> }
>
> /**
> * Method that provides a check for MGRS zone letters. Returns an
> * uppercase version of any valid letter passed in.
> */
> protected char checkZone(char zone) {
> zone = Character.toUpperCase(zone);
>
> if (zone <= 'A' || zone == 'B' || zone == 'Y' || zone >= 'Z'
> || zone == 'I' || zone == 'O') {
> throw new NumberFormatException("Invalid MGRSPoint zone
> letter: "
> + zone);
> }
>
> return zone;
> }
>
> /**
> * Determines the correct MGRS letter designator for the given
> * latitude returns 'Z' if latitude is outside the MGRS limits of
> * 84N to 80S.
> *
> * _at_param Lat The float value of the latitude.
> *
> * _at_return A char value which is the MGRS zone letter.
> */
> protected char getLetterDesignator(double lat) {
>
> //This is here as an error flag to show that the Latitude is
> //outside MGRS limits
> char LetterDesignator = 'Z';
>
> if ((84 >= lat) && (lat >= 72))
> LetterDesignator = 'X';
> else if ((72 > lat) && (lat >= 64))
> LetterDesignator = 'W';
> else if ((64 > lat) && (lat >= 56))
> LetterDesignator = 'V';
> else if ((56 > lat) && (lat >= 48))
> LetterDesignator = 'U';
> else if ((48 > lat) && (lat >= 40))
> LetterDesignator = 'T';
> else if ((40 > lat) && (lat >= 32))
> LetterDesignator = 'S';
> else if ((32 > lat) && (lat >= 24))
> LetterDesignator = 'R';
> else if ((24 > lat) && (lat >= 16))
> LetterDesignator = 'Q';
> else if ((16 > lat) && (lat >= 8))
> LetterDesignator = 'P';
> else if ((8 > lat) && (lat >= 0))
> LetterDesignator = 'N';
> else if ((0 > lat) && (lat >= -8))
> LetterDesignator = 'M';
> else if ((-8 > lat) && (lat >= -16))
> LetterDesignator = 'L';
> else if ((-16 > lat) && (lat >= -24))
> LetterDesignator = 'K';
> else if ((-24 > lat) && (lat >= -32))
> LetterDesignator = 'J';
> else if ((-32 > lat) && (lat >= -40))
> LetterDesignator = 'H';
> else if ((-40 > lat) && (lat >= -48))
> LetterDesignator = 'G';
> else if ((-48 > lat) && (lat >= -56))
> LetterDesignator = 'F';
> else if ((-56 > lat) && (lat >= -64))
> LetterDesignator = 'E';
> else if ((-64 > lat) && (lat >= -72))
> LetterDesignator = 'D';
> else if ((-72 > lat) && (lat >= -80))
> LetterDesignator = 'C';
> return LetterDesignator;
> }
>
> /**
> * Set the number of digits to use for easting and northing
> * numbers in the mgrs string, which reflects the accuracy of the
> * corrdinate. From 5 (1 meter) to 1 (10,000 meter).
> */
> public void setAccuracy(int value) {
> accuracy = value;
> mgrs = null;
> }
>
> public int getAccuracy() {
> return accuracy;
> }
>
> /**
> * Set the UTM parameters from a MGRS string.
> *
> * _at_param mgrsString an UPPERCASE coordinate string is expected.
> */
> protected void decode(String mgrsString) throws
> NumberFormatException {
>
> if (mgrsString == null || mgrsString.length() == 0) {
> throw new NumberFormatException("MGRSPoint coverting from
> nothing");
> }
>
> int length = mgrsString.length();
>
> String hunK = null;
> String seasting = null;
> String snorthing = null;
>
> StringBuffer sb = new StringBuffer();
> char testChar;
> int i = 0;
>
> // get Zone number
> while (!Character.isLetter(testChar = mgrsString.charAt(i))) {
> if (i > 2) {
> throw new NumberFormatException("MGRSPoint bad
> conversion from: "
> + mgrsString);
> }
> sb.append(testChar);
> i++;
> }
>
> zone_number = Integer.parseInt(sb.toString());
>
> if (i == 0 || i + 3 > length) {
> // A good MGRS string has to be 4-5 digits long,
> // ##AAA/#AAA at least.
> throw new NumberFormatException("MGRSPoint bad conversion
> from: "
> + mgrsString);
> }
>
> zone_letter = mgrsString.charAt(i++);
>
> // Should we check the zone letter here? Why not.
> if (zone_letter <= 'A' || zone_letter == 'B' || zone_letter ==
> 'Y'
> || zone_letter >= 'Z' || zone_letter == 'I'
> || zone_letter == 'O') {
> throw new NumberFormatException("MGRSPoint zone letter "
> + (char) zone_letter + " not handled: " +
> mgrsString);
> }
>
> hunK = mgrsString.substring(i, i += 2);
>
> int set = get100kSetForZone(zone_number);
>
> float east100k = getEastingFromChar(hunK.charAt(0), set);
> float north100k = getNorthingFromChar(hunK.charAt(1), set);
>
> // We have a bug where the northing may be 2000000 too low.
> // How
> // do we know when to roll over?
>
> while (north100k < getMinNorthing(zone_letter)) {
> north100k += 2000000;
> }
>
> // calculate the char index for easting/northing separator
> int remainder = length - i;
>
> if (remainder % 2 != 0) {
> throw new NumberFormatException("MGRSPoint has to have an
> even number \nof digits after the zone letter and two 100km letters -
> front \nhalf for easting meters, second half for \nnorthing meters"
> + mgrsString);
> }
>
> int sep = remainder / 2;
>
> float sepEasting = 0f;
> float sepNorthing = 0f;
>
> if (sep > 0) {
> if (DEBUG)
> Debug.output(" calculating e/n from " +
> mgrs.substring(i));
> float accuracyBonus = 100000f / (float) Math.pow(10, sep);
> if (DEBUG)
> Debug.output(" calculated accuracy bonus as " +
> accuracyBonus);
> String sepEastingString = mgrsString.substring(i, i + sep);
> if (DEBUG)
> Debug.output(" parsed easting as " + sepEastingString);
> sepEasting = Float.parseFloat(sepEastingString) *
> accuracyBonus;
> String sepNorthingString = mgrsString.substring(i + sep);
> if (DEBUG)
> Debug.output(" parsed northing as " +
> sepNorthingString);
> sepNorthing = Float.parseFloat(sepNorthingString) *
> accuracyBonus;
> }
>
> easting = sepEasting + east100k;
> northing = sepNorthing + north100k;
>
> if (DEBUG) {
> Debug.output("Decoded " + mgrsString + " as zone number: "
> + zone_number + ", zone letter: " + zone_letter
> + ", easting: " + easting + ", northing: " +
> northing
> + ", 100k: " + hunK);
> }
> }
>
> /**
> * Create the mgrs string based on the internal UTM settings,
> * using the accuracy set in the MGRSPoint.
> */
> protected void resolve() {
> resolve(accuracy);
> }
>
> /**
> * Create the mgrs string based on the internal UTM settings.
> *
> * _at_param digitAccuracy The number of digits to use for the
> * northing and easting numbers. 5 digits reflect a 1 meter
> * accuracy, 4 - 10 meter, 3 - 100 meter, 2 - 1000 meter, 1
> -
> * 10,000 meter.
> */
> protected void resolve(int digitAccuracy) {
> if (zone_letter == 'Z') {
> mgrs = "Latitude limit exceeded";
> } else {
> StringBuffer sb = new StringBuffer(zone_number + ""
> + (char) zone_letter
> + get100kID(easting, northing, zone_number));
> StringBuffer seasting = new
> StringBuffer(Integer.toString((int) easting));
> StringBuffer snorthing = new
> StringBuffer(Integer.toString((int) northing));
>
> if (DEBUG) {
> Debug.output(" Resolving MGRS from easting: " +
> seasting
> + " derived from " + easting + ", and
> northing: "
> + snorthing + " derived from " + northing);
> }
>
> while (digitAccuracy + 1 > seasting.length()) {
> seasting.insert(0, '0');
> }
>
> // We have to be careful here, the 100k values shouldn't
> // be
> // used for calculating stuff here.
>
> while (digitAccuracy + 1 > snorthing.length()) {
> snorthing.insert(0, '0');
> }
>
> while (snorthing.length() > 6) {
> snorthing.deleteCharAt(0);
> }
>
> if (DEBUG) {
> Debug.output(" -- modified easting: " + seasting
> + " and northing: " + snorthing);
> }
>
> try {
> sb.append(seasting.substring(1, digitAccuracy + 1)
> + snorthing.substring(1, digitAccuracy + 1));
>
> mgrs = sb.toString();
> } catch (IndexOutOfBoundsException ioobe) {
> mgrs = null;
> }
> }
> }
>
> /**
> * Given a UTM zone number, figure out the MGRS 100K set it is in.
> */
> protected int get100kSetForZone(int i) {
> int set = i % NUM_100K_SETS;
> if (set == 0)
> set = NUM_100K_SETS;
> return set;
> }
>
> /**
> * Provided so that extensions to this class can provide different
> * origin letters, in case of different ellipsoids. The int[]
> * represents all of the first letters in the bottom left corner
> * of each set box, as shown in an MGRS 100K box layout.
> */
> protected int[] getOriginColumnLetters() {
> return originColumnLetters;
> }
>
> /**
> * Provided so that extensions to this class can provide different
> * origin letters, in case of different ellipsoids. The int[]
> * represents all of the first letters in the bottom left corner
> * of each set box, as shown in an MGRS 100K box layout.
> */
> protected void setOriginColumnLetters(int[] letters) {
> originColumnLetters = letters;
> }
>
> /**
> * Provided so that extensions to this class can provide different
> * origin letters, in case of different ellipsoids. The int[]
> * represents all of the second letters in the bottom left corner
> * of each set box, as shown in an MGRS 100K box layout.
> */
> protected int[] getOriginRowLetters() {
> return originRowLetters;
> }
>
> /**
> * Provided so that extensions to this class can provide different
> * origin letters, in case of different ellipsoids. The int[]
> * represents all of the second letters in the bottom left corner
> * of each set box, as shown in an MGRS 100K box layout.
> */
> protected void setOriginRowLetters(int[] letters) {
> originRowLetters = letters;
> }
>
> /**
> * Get the two letter 100k designator for a given UTM easting,
> * northing and zone number value.
> */
> protected String get100kID(float easting, float northing, int
> zone_number) {
> int set = get100kSetForZone(zone_number);
> int setColumn = ((int) easting / 100000);
> int setRow = ((int) northing / 100000) % 20;
> return get100kID(setColumn, setRow, set);
> }
>
> /**
> * Given the first letter from a two-letter MGRS 100k zone, and
> * given the MGRS table set for the zone number, figure out the
> * easting value that should be added to the other, secondary
> * easting value.
> */
> protected float getEastingFromChar(char e, int set) {
> int baseCol[] = getOriginColumnLetters();
> // colOrigin is the letter at the origin of the set for the
> // column
> int curCol = baseCol[set - 1];
> float eastingValue = 100000f;
> boolean rewindMarker = false;
>
> while (curCol != e) {
> curCol++;
> if (curCol == I)
> curCol++;
> if (curCol == O)
> curCol++;
> if (curCol > Z) {
> if(rewindMarker) {
> throw new NumberFormatException("Bad character:
> "+e);
> }
> curCol = A;
> rewindMarker = true;
> }
> eastingValue += 100000f;
> }
>
> if (DEBUG) {
> Debug.output("Easting value for " + (char) e + " from set:
> " + set
> + ", col: " + curCol + " is " + eastingValue);
> }
> return eastingValue;
> }
>
> /**
> * Given the second letter from a two-letter MGRS 100k zone, and
> * given the MGRS table set for the zone number, figure out the
> * northing value that should be added to the other, secondary
> * northing value. You have to remember that Northings are
> * determined from the equator, and the vertical cycle of letters
> * mean a 2000000 additional northing meters. This happens approx.
> * every 18 degrees of latitude. This method does *NOT* count any
> * additional northings. You have to figure out how many 2000000
> * meters need to be added for the zone letter of the MGRS
> * coordinate.
> *
> * _at_param n second letter of the MGRS 100k zone
> * _at_param set the MGRS table set number, which is dependent on the
> * UTM zone number.
> */
> protected float getNorthingFromChar(char n, int set) {
>
> if (n > 'V')
> throw new NumberFormatException("MGRSPoint given invalid
> Northing "
> + n);
>
> int baseRow[] = getOriginRowLetters();
> // rowOrigin is the letter at the origin of the set for the
> // column
> int curRow = baseRow[set - 1];
> float northingValue = 0f;
> boolean rewindMarker = false;
>
> while (curRow != n) {
> curRow++;
> if (curRow == I)
> curRow++;
> if (curRow == O)
> curRow++;
> // fixing a bug making whole application hang in this loop
> // when 'n' is a wrong character
> if (curRow > V) {
> if(rewindMarker) { //making sure that this loop ends
> throw new NumberFormatException("Bad character:
> "+n);
> }
> curRow = A;
> rewindMarker = true;
> }
> northingValue += 100000f;
> }
>
> if (DEBUG) {
> Debug.output("Northing value for " + (char) n + " from
> set: " + set
> + ", row: " + curRow + " is " + northingValue);
> }
>
> return northingValue;
> }
>
> /**
> * Get the two-letter MGRS 100k designator given information
> * translated from the UTM northing, easting and zone number.
> *
> * _at_param setColumn the column index as it relates to the MGRS
> * 100k set spreadsheet, created from the UTM easting.
> * Values are 1-8.
> * _at_param setRow the row index as it relates to the MGRS 100k set
> * spreadsheet, created from the UTM northing value. Values
> * are from 0-19.
> * _at_param set the set block, as it relates to the MGRS 100k set
> * spreadsheet, created from the UTM zone. Values are from
> * 1-60.
> * _at_return two letter MGRS 100k code.
> */
> protected String get100kID(int setColumn, int setRow, int set) {
>
> if (DEBUG) {
> System.out.println("set (" + set + ") column = " +
> setColumn
> + ", row = " + setRow);
> }
>
> int baseCol[] = getOriginColumnLetters();
> int baseRow[] = getOriginRowLetters();
>
> // colOrigin and rowOrigin are the letters at the origin of
> // the set
> int colOrigin = baseCol[set - 1];
> int rowOrigin = baseRow[set - 1];
>
> if (DEBUG) {
> System.out.println("starting at = " + (char) colOrigin
> + (char) rowOrigin);
> }
>
> // colInt and rowInt are the letters to build to return
> int colInt = colOrigin + setColumn - 1;
> int rowInt = rowOrigin + setRow;
> boolean rollover = false;
>
> if (colInt > Z) {
> colInt = colInt - Z + A - 1;
> rollover = true;
> if (DEBUG)
> System.out.println("rolling over col, new value: "
> + (char) colInt);
> }
>
> if (colInt == I || (colOrigin < I && colInt > I)
> || ((colInt > I || colOrigin < I) && rollover)) {
> colInt++;
> if (DEBUG)
> System.out.println("skipping I in col, new value: "
> + (char) colInt);
> }
> if (colInt == O || (colOrigin < O && colInt > O)
> || ((colInt > O || colOrigin < O) && rollover)) {
> colInt++;
> if (DEBUG)
> System.out.println("skipping O in col, new value: "
> + (char) colInt);
> if (colInt == I) {
> colInt++;
> if (DEBUG)
> System.out.println(" hit I, new value: " + (char)
> colInt);
> }
> }
>
> if (colInt > Z) {
> colInt = colInt - Z + A - 1;
> if (DEBUG)
> System.out.println("rolling(2) col, new value: "
> + (char) rowInt);
> }
>
> if (rowInt > V) {
> rowInt = rowInt - V + A - 1;
> rollover = true;
> if (DEBUG)
> System.out.println("rolling over row, new value: "
> + (char) rowInt);
> } else {
> rollover = false;
> }
>
> if (rowInt == I || (rowOrigin < I && rowInt > I)
> || ((rowInt > I || rowOrigin < I) && rollover)) {
> rowInt++;
> if (DEBUG)
> System.out.println("skipping I in row, new value: "
> + (char) rowInt);
> }
>
> if (rowInt == O || (rowOrigin < O && rowInt > O)
> || ((rowInt > O || rowOrigin < O) && rollover)) {
> rowInt++;
> if (DEBUG)
> System.out.println("skipping O in row, new value: "
> + (char) rowInt);
> if (rowInt == I) {
> rowInt++;
> if (DEBUG)
> System.out.println(" hit I, new value: " + (char)
> rowInt);
> }
> }
>
> if (rowInt > V) {
> rowInt = rowInt - V + A - 1;
> if (DEBUG)
> System.out.println("rolling(2) row, new value: "
> + (char) rowInt);
> }
>
> String twoLetter = (char) colInt + "" + (char) rowInt;
>
> if (DEBUG) {
> System.out.println("ending at = " + twoLetter);
> }
>
> return twoLetter;
> }
>
> /**
> * Testing method, used to print out the MGRS 100k two letter set
> * tables.
> */
> protected void print100kSets() {
> StringBuffer sb = null;
> for (int set = 1; set <= 6; set++) {
> System.out.println("-------------\nFor 100K Set " + set
> + ":\n-------------\n");
> for (int i = 19; i >= 0; i -= 1) {
> sb = new StringBuffer((i * 100000) + "\t| ");
>
> for (int j = 1; j <= 8; j++) {
> sb.append(" " + get100kID(j, i, set));
> }
>
> sb.append(" |");
> System.out.println(sb);
> }
> }
> }
>
> /**
> * The function getMinNorthing returns the minimum northing value
> * of a MGRS zone.
> *
> * portted from Geotrans' c Lattitude_Band_Value strucure table.
> * zoneLetter : MGRS zone (input)
> */
>
> protected float getMinNorthing(char zoneLetter)
> throws NumberFormatException {
> float northing;
> switch (zoneLetter) {
> case 'C':
> northing = 1100000.0f;
> break;
> case 'D':
> northing = 2000000.0f;
> break;
> case 'E':
> northing = 2800000.0f;
> break;
> case 'F':
> northing = 3700000.0f;
> break;
> case 'G':
> northing = 4600000.0f;
> break;
> case 'H':
> northing = 5500000.0f;
> break;
> case 'J':
> northing = 6400000.0f;
> break;
> case 'K':
> northing = 7300000.0f;
> break;
> case 'L':
> northing = 8200000.0f;
> break;
> case 'M':
> northing = 9100000.0f;
> break;
> case 'N':
> northing = 0.0f;
> break;
> case 'P':
> northing = 800000.0f;
> break;
> case 'Q':
> northing = 1700000.0f;
> break;
> case 'R':
> northing = 2600000.0f;
> break;
> case 'S':
> northing = 3500000.0f;
> break;
> case 'T':
> northing = 4400000.0f;
> break;
> case 'U':
> northing = 5300000.0f;
> break;
> case 'V':
> northing = 6200000.0f;
> break;
> case 'W':
> northing = 7000000.0f;
> break;
> case 'X':
> northing = 7900000.0f;
> break;
> default:
> northing = -1.0f;
> }
> if (northing >= 0.0) {
> return northing;
> } else {
> throw new NumberFormatException("Invalid zone letter: "
> + zone_letter);
> }
>
> }
>
> private static void runTests(String fName, String inType) {
>
> LineNumberReader lnr = null;
> PrintStream pos = null;
> String record = null;
> StringBuffer outStr1 = new StringBuffer();
> StringBuffer outStr2 = new StringBuffer();
>
> try {
>
> /*
> * File inFile = new File(fName + ".dat"); File outFile =
> * new File(fName + ".out"); FileInputStream fis = new
> * FileInputStream(inFile); FileOutputStream fos = new
> * FileOutputStream(outFile); BufferedInputStream bis =
> * new BufferedInputStream(fis);
> */
> pos = new PrintStream(new FileOutputStream(new File(fName
> + ".out")));
> lnr = new LineNumberReader(new InputStreamReader(new
> BufferedInputStream(new FileInputStream(new File(fName)))));
>
> if (inType.equalsIgnoreCase("MGRS")) {
> outStr1.append("MGRS to
> LatLonPoint\n\tMGRS\t\tLatitude Longitude\n");
> outStr2.append("MGRS to UTM\n\tMGRS\t\tZone Easting
> Northing\n");
> } else if (inType.equalsIgnoreCase("UTM")) {
> outStr1.append("UTM to LatLonPoint\n\tUTM\t\tLatitude
> Longitude\n");
> outStr2.append("UTM to MGRS\n\tUTM\t\tMGRS\n");
> } else if (inType.equalsIgnoreCase("LatLon")) {
> outStr1.append("LatLonPoint to UTM\nLatitude
> Longitude\t\tZone Easting Northing \n");
> outStr2.append("LatLonPoint to MGRS\nLatitude
> Longitude\t\tMGRS\n");
> }
>
> while ((record = lnr.readLine()) != null) {
> if (inType.equalsIgnoreCase("MGRS")) {
> try {
> MGRSPoint mgrsp = new MGRSPoint(record);
> record.trim();
> mgrsp.decode(record);
>
> outStr1.append(record + " is " +
> mgrsp.toLatLonPoint()
> + "\n");
> outStr2.append(record + " to UTM: " +
> mgrsp.zone_number
> + " " + mgrsp.easting + " " +
> mgrsp.northing
> + "\n");
> } catch (NumberFormatException nfe) {
> Debug.error(nfe.getMessage());
> }
>
> } else if (inType.equalsIgnoreCase("UTM")) {
> MGRSPoint mgrsp;
> UTMPoint utmp;
> float e, n;
> int z;
> char zl;
> String tmp;
> record.trim();
> tmp = record.substring(0, 2);
> z = Integer.parseInt(tmp);
> tmp = record.substring(5, 11);
> e = Float.parseFloat(tmp);
> tmp = record.substring(12, 19);
> n = Float.parseFloat(tmp);
> zl = record.charAt(3);
> utmp = new UTMPoint(n, e, z, zl);
> LatLonPoint llp = utmp.toLatLonPoint();
> mgrsp = LLtoMGRS(llp);
> outStr1.append(record + " is " + llp + " back to "
> + LLtoUTM(llp) + "\n");
> outStr2.append(record + " is " + mgrsp + "\n");
> } else if (inType.equalsIgnoreCase("LatLon")) {
> MGRSPoint mgrsp;
> UTMPoint utmp;
> LatLonPoint llp;
> float lat, lon;
> int index;
> String tmp;
> record.trim();
> index = record.indexOf("\040");
> if (index < 0) {
> index = record.indexOf("\011");
> }
> tmp = record.substring(0, index);
> lat = Float.parseFloat(tmp);
> tmp = record.substring(index);
> lon = Float.parseFloat(tmp);
> llp = new LatLonPoint(lat, lon);
> utmp = LLtoUTM(llp);
> mgrsp = LLtoMGRS(llp);
> outStr1.append(record + " to UTM: " +
> mgrsp.zone_number
> + " " + mgrsp.easting + " " +
> mgrsp.northing + "\n");
> outStr2.append(record + " -> " + mgrsp.mgrs
> + "\n");
> }
>
> }
>
> } catch (IOException e) {
> // catch io errors from FileInputStream or readLine()
> System.out.println("IO error: " + e.getMessage());
>
> } finally {
> if (pos != null) {
> pos.print(outStr1.toString());
> pos.print("\n");
> pos.print(outStr2.toString());
> pos.close();
> }
> // if the file opened okay, make sure we close it
> if (lnr != null) {
> try {
> lnr.close();
> } catch (IOException ioe) {
> }
> }
>
> }
>
> }
>
> public static void main(String[] argv) {
> Debug.init();
>
> ArgParser ap = new ArgParser("MGRSPoint");
> ap.add("mgrs", "Print Latitude and Longitude for MGRS value",
> 1);
> ap.add("latlon",
> "Print MGRS for Latitude and Longitude values",
> 2,
> true);
> ap.add("sets", "Print the MGRS 100k table");
> ap.add("altsets", "Print the MGRS 100k table for the Bessel
> ellipsoid");
> ap.add("rtc",
> "Run test case, with filename and input data type
> [MGRS | UTM | LatLon]",
> 2);
>
> if (!ap.parse(argv)) {
> ap.printUsage();
> System.exit(0);
> }
>
> String arg[];
> arg = ap.getArgValues("sets");
> if (arg != null) {
> new MGRSPoint().print100kSets();
> }
>
> arg = ap.getArgValues("altsets");
> if (arg != null) {
> MGRSPoint mgrsp = new MGRSPoint();
>
> mgrsp.setOriginColumnLetters(BESSEL_SET_ORIGIN_COLUMN_LETTERS);
> mgrsp.setOriginRowLetters(BESSEL_SET_ORIGIN_ROW_LETTERS);
> mgrsp.print100kSets();
> }
>
> arg = ap.getArgValues("mgrs");
> if (arg != null) {
> try {
> MGRSPoint mgrsp = new MGRSPoint(arg[0]);
> Debug.output(arg[0] + " is " + mgrsp.toLatLonPoint());
> } catch (NumberFormatException nfe) {
> Debug.error(nfe.getMessage());
> }
> }
>
> arg = ap.getArgValues("latlon");
> if (arg != null) {
> try {
>
> float lat = Float.parseFloat(arg[0]);
> float lon = Float.parseFloat(arg[1]);
>
> LatLonPoint llp = new LatLonPoint(lat, lon);
> MGRSPoint mgrsp = LLtoMGRS(llp);
> UTMPoint utmp = LLtoUTM(llp);
>
> if (utmp.zone_letter == 'Z') {
> Debug.output(llp + "to UTM: latitude limit
> exceeded.");
> } else {
> Debug.output(llp + " is " + utmp);
> }
>
> Debug.output(llp + " is " + mgrsp);
>
> } catch (NumberFormatException nfe) {
> Debug.error("The numbers provided: " + argv[0] + ", "
> + argv[1] + " aren't valid");
> }
> }
>
> arg = ap.getArgValues("rtc");
> if (arg != null) {
> runTests(arg[0], arg[1]);
> }
>
> }
> }
>
> /*
> * _at_(#)Quantize.java 0.90 9/19/00 Adam Doppelt
> */
>
> /**
> * An efficient color quantization algorithm, adapted from the C++
> * implementation quantize.c in <a
> * href="http://www.imagemagick.org/">ImageMagick</a>. The pixels for
> * an image are placed into an oct tree. The oct tree is reduced in
> * size, and the pixels from the original image are reassigned to the
> * nodes in the reduced tree.<p>
> *
> * Here is the copyright notice from ImageMagick:
> *
> * <pre>
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> %%%%%%%%
> % Permission is hereby granted, free of charge, to any person
> obtaining a %
> % copy of this software and associated documentation files
> ("ImageMagick"), %
> % to deal in ImageMagick without restriction, including without
> limitation %
> % the rights to use, copy, modify, merge, publish, distribute,
> sublicense, %
> % and/or sell copies of ImageMagick, and to permit persons to whom
> the %
> % ImageMagick is furnished to do so, subject to the following
> conditions: %
> %
> %
> % The above copyright notice and this permission notice shall be
> included in %
> % all copies or substantial portions of ImageMagick.
> %
> %
> %
> % The software is provided "as is", without warranty of any kind,
> express or %
> % implied, including but not limited to the warranties of
> merchantability, %
> % fitness for a particular purpose and noninfringement. In no event
> shall %
> % E. I. du Pont de Nemours and Company be liable for any claim,
> damages or %
> % other liability, whether in an action of contract, tort or
> otherwise, %
> % arising from, out of or in connection with ImageMagick or the use
> or other %
> % dealings in ImageMagick.
> %
> %
> %
> % Except as contained in this notice, the name of the E. I. du Pont
> de %
> % Nemours and Company shall not be used in advertising or otherwise
> to %
> % promote the sale, use or other dealings in ImageMagick without
> prior %
> % written authorization from the E. I. du Pont de Nemours and
> Company. %
> %
> %
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> %%%%%%%%
> </pre>
> *
> *
> * _at_version 0.90 19 Sep 2000
> * _at_author <a href="http://www.gurge.com/amd/">Adam Doppelt</a>
> */
> package doppelt;
>
> public class Quantize {
>
> /*
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> %%%%%%%%
> %
> %
> %
> %
> %
> %
> % QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE
> %
> % Q Q U U A A NN N T I ZZ E
> %
> % Q Q U U AAAAA N N N T I ZZZ EEEEE
> %
> % Q QQ U U A A N NN T I ZZ E
> %
> % QQQQ UUU A A N N T IIIII ZZZZZ EEEEE
> %
> %
> %
> %
> %
> % Reduce the Number of Unique Colors in an Image
> %
> %
> %
> %
> %
> % Software Design
> %
> % John Cristy
> %
> % July 1992
> %
> %
> %
> %
> %
> % Copyright 1998 E. I. du Pont de Nemours and Company
> %
> %
> %
> % Permission is hereby granted, free of charge, to any person
> obtaining a %
> % copy of this software and associated documentation files
> ("ImageMagick"), %
> % to deal in ImageMagick without restriction, including without
> limitation %
> % the rights to use, copy, modify, merge, publish, distribute,
> sublicense, %
> % and/or sell copies of ImageMagick, and to permit persons to whom
> the %
> % ImageMagick is furnished to do so, subject to the following
> conditions: %
> %
> %
> % The above copyright notice and this permission notice shall be
> included in %
> % all copies or substantial portions of ImageMagick.
> %
> %
> %
> % The software is provided "as is", without warranty of any kind,
> express or %
> % implied, including but not limited to the warranties of
> merchantability, %
> % fitness for a particular purpose and noninfringement. In no event
> shall %
> % E. I. du Pont de Nemours and Company be liable for any claim,
> damages or %
> % other liability, whether in an action of contract, tort or
> otherwise, %
> % arising from, out of or in connection with ImageMagick or the use
> or other %
> % dealings in ImageMagick.
> %
> %
> %
> % Except as contained in this notice, the name of the E. I. du Pont
> de %
> % Nemours and Company shall not be used in advertising or otherwise
> to %
> % promote the sale, use or other dealings in ImageMagick without
> prior %
> % written authorization from the E. I. du Pont de Nemours and
> Company. %
> %
> %
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> %%%%%%%%
> %
> % Realism in computer graphics typically requires using 24 bits/pixel
> to
> % generate an image. Yet many graphic display devices do not contain
> % the amount of memory necessary to match the spatial and color
> % resolution of the human eye. The QUANTIZE program takes a 24 bit
> % image and reduces the number of colors so it can be displayed on
> % raster device with less bits per pixel. In most instances, the
> % quantized image closely resembles the original reference image.
> %
> % A reduction of colors in an image is also desirable for image
> % transmission and real-time animation.
> %
> % Function Quantize takes a standard RGB or monochrome images and
> quantizes
> % them down to some fixed number of colors.
> %
> % For purposes of color allocation, an image is a set of n pixels,
> where
> % each pixel is a point in RGB space. RGB space is a 3-dimensional
> % vector space, and each pixel, pi, is defined by an ordered triple of
> % red, green, and blue coordinates, (ri, gi, bi).
> %
> % Each primary color component (red, green, or blue) represents an
> % intensity which varies linearly from 0 to a maximum value, cmax,
> which
> % corresponds to full saturation of that color. Color allocation is
> % defined over a domain consisting of the cube in RGB space with
> % opposite vertices at (0,0,0) and (cmax,cmax,cmax). QUANTIZE requires
> % cmax = 255.
> %
> % The algorithm maps this domain onto a tree in which each node
> % represents a cube within that domain. In the following discussion
> % these cubes are defined by the coordinate of two opposite vertices:
> % The vertex nearest the origin in RGB space and the vertex farthest
> % from the origin.
> %
> % The tree's root node represents the the entire domain, (0,0,0)
> through
> % (cmax,cmax,cmax). Each lower level in the tree is generated by
> % subdividing one node's cube into eight smaller cubes of equal size.
> % This corresponds to bisecting the parent cube with planes passing
> % through the midpoints of each edge.
> %
> % The basic algorithm operates in three phases: Classification,
> % Reduction, and Assignment. Classification builds a color
> % description tree for the image. Reduction collapses the tree until
> % the number it represents, at most, the number of colors desired in
> the
> % output image. Assignment defines the output image's color map and
> % sets each pixel's color by reclassification in the reduced tree.
> % Our goal is to minimize the numerical discrepancies between the
> original
> % colors and quantized colors (quantization error).
> %
> % Classification begins by initializing a color description tree of
> % sufficient depth to represent each possible input color in a leaf.
> % However, it is impractical to generate a fully-formed color
> % description tree in the classification phase for realistic values of
> % cmax. If colors components in the input image are quantized to k-bit
> % precision, so that cmax= 2k-1, the tree would need k levels below
> the
> % root node to allow representing each possible input color in a leaf.
> % This becomes prohibitive because the tree's total number of nodes is
> % 1 + sum(i=1,k,8k).
> %
> % A complete tree would require 19,173,961 nodes for k = 8, cmax =
> 255.
> % Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
> % Initializes data structures for nodes only as they are needed; (2)
> % Chooses a maximum depth for the tree as a function of the desired
> % number of colors in the output image (currently log2(colormap
> size)).
> %
> % For each pixel in the input image, classification scans downward
> from
> % the root of the color description tree. At each level of the tree it
> % identifies the single node which represents a cube in RGB space
> % containing the pixel's color. It updates the following data for each
> % such node:
> %
> % n1: Number of pixels whose color is contained in the RGB cube
> % which this node represents;
> %
> % n2: Number of pixels whose color is not represented in a node at
> % lower depth in the tree; initially, n2 = 0 for all nodes except
> % leaves of the tree.
> %
> % Sr, Sg, Sb: Sums of the red, green, and blue component values for
> % all pixels not classified at a lower depth. The combination of
> % these sums and n2 will ultimately characterize the mean color of
> a
> % set of pixels represented by this node.
> %
> % E: The distance squared in RGB space between each pixel contained
> % within a node and the nodes' center. This represents the
> quantization
> % error for a node.
> %
> % Reduction repeatedly prunes the tree until the number of nodes with
> % n2 > 0 is less than or equal to the maximum number of colors allowed
> % in the output image. On any given iteration over the tree, it
> selects
> % those nodes whose E count is minimal for pruning and merges their
> % color statistics upward. It uses a pruning threshold, Ep, to govern
> % node selection as follows:
> %
> % Ep = 0
> % while number of nodes with (n2 > 0) > required maximum number of
> colors
> % prune all nodes such that E <= Ep
> % Set Ep to minimum E in remaining nodes
> %
> % This has the effect of minimizing any quantization error when
> merging
> % two nodes together.
> %
> % When a node to be pruned has offspring, the pruning procedure
> invokes
> % itself recursively in order to prune the tree from the leaves
> upward.
> % n2, Sr, Sg, and Sb in a node being pruned are always added to the
> % corresponding data in that node's parent. This retains the pruned
> % node's color characteristics for later averaging.
> %
> % For each node, n2 pixels exist for which that node represents the
> % smallest volume in RGB space containing those pixel's colors. When
> n2
> % > 0 the node will uniquely define a color in the output image. At
> the
> % beginning of reduction, n2 = 0 for all nodes except a the leaves
> of
> % the tree which represent colors present in the input image.
> %
> % The other pixel count, n1, indicates the total number of colors
> % within the cubic volume which the node represents. This includes n1
> -
> % n2 pixels whose colors should be defined by nodes at a lower level
> in
> % the tree.
> %
> % Assignment generates the output image from the pruned tree. The
> % output image consists of two parts: (1) A color map, which is an
> % array of color descriptions (RGB triples) for each color present in
> % the output image; (2) A pixel array, which represents each pixel
> as
> % an index into the color map array.
> %
> % First, the assignment phase makes one pass over the pruned color
> % description tree to establish the image's color map. For each node
> % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the
> % mean color of all pixels that classify no lower than this node. Each
> % of these colors becomes an entry in the color map.
> %
> % Finally, the assignment phase reclassifies each pixel in the pruned
> % tree to identify the deepest node containing the pixel's color. The
> % pixel's value in the pixel array becomes the index of this node's
> mean
> % color in the color map.
> %
> % With the permission of USC Information Sciences Institute, 4676
> Admiralty
> % Way, Marina del Rey, California 90292, this code was adapted from
> module
> % ALCOLS written by Paul Raveling.
> %
> % The names of ISI and USC are not used in advertising or publicity
> % pertaining to distribution of the software without prior specific
> % written permission from ISI.
> %
> */
>
> final static boolean QUICK = true;
>
> final static int MAX_RGB = 255;
> final static int MAX_NODES = 266817;
> final static int MAX_TREE_DEPTH = 8;
>
> // these are precomputed in advance
> static int SQUARES[];
> static int SHIFT[];
>
> static {
> SQUARES = new int[MAX_RGB + MAX_RGB + 1];
> for (int i= -MAX_RGB; i <= MAX_RGB; i++) {
> SQUARES[i + MAX_RGB] = i * i;
> }
>
> SHIFT = new int[MAX_TREE_DEPTH + 1];
> for (int i = 0; i < MAX_TREE_DEPTH + 1; ++i) {
> SHIFT[i] = 1 << (15 - i);
> }
> }
>
> /**
> * Reduce the image to the given number of colors. The pixels are
> * reduced in place.
> * _at_return The new color palette.
> */
> public static int[] quantizeImage(int pixels[][], int max_colors) {
> Cube cube = new Cube(pixels, max_colors);
> cube.classification();
> cube.reduction();
> cube.assignment();
> return cube.colormap;
> }
>
> static class Cube {
> int pixels[][];
> int max_colors;
> int colormap[];
>
> Node root;
> int depth;
>
> // counter for the number of colors in the cube. this gets
> // recalculated often.
> int colors;
>
> // counter for the number of nodes in the tree
> int nodes;
>
> Cube(int pixels[][], int max_colors) {
> this.pixels = pixels;
> this.max_colors = max_colors;
>
> int i = max_colors;
> // tree_depth = log max_colors
> // 4
> for (depth = 1; i != 0; depth++) {
> i /= 4;
> }
> if (depth > 1) {
> --depth;
> }
> if (depth > MAX_TREE_DEPTH) {
> depth = MAX_TREE_DEPTH;
> } else if (depth < 2) {
> depth = 2;
> }
>
> root = new Node(this);
> }
>
> /*
> * Procedure Classification begins by initializing a color
> * description tree of sufficient depth to represent each
> * possible input color in a leaf. However, it is impractical
> * to generate a fully-formed color description tree in the
> * classification phase for realistic values of cmax. If
> * colors components in the input image are quantized to k-bit
> * precision, so that cmax= 2k-1, the tree would need k levels
> * below the root node to allow representing each possible
> * input color in a leaf. This becomes prohibitive because the
> * tree's total number of nodes is 1 + sum(i=1,k,8k).
> *
> * A complete tree would require 19,173,961 nodes for k = 8,
> * cmax = 255. Therefore, to avoid building a fully populated
> * tree, QUANTIZE: (1) Initializes data structures for nodes
> * only as they are needed; (2) Chooses a maximum depth for
> * the tree as a function of the desired number of colors in
> * the output image (currently log2(colormap size)).
> *
> * For each pixel in the input image, classification scans
> * downward from the root of the color description tree. At
> * each level of the tree it identifies the single node which
> * represents a cube in RGB space containing It updates the
> * following data for each such node:
> *
> * number_pixels : Number of pixels whose color is contained
> * in the RGB cube which this node represents;
> *
> * unique : Number of pixels whose color is not represented
> * in a node at lower depth in the tree; initially, n2 = 0
> * for all nodes except leaves of the tree.
> *
> * total_red/green/blue : Sums of the red, green, and blue
> * component values for all pixels not classified at a lower
> * depth. The combination of these sums and n2 will
> * ultimately characterize the mean color of a set of pixels
> * represented by this node.
> */
> void classification() {
> int pixels[][] = this.pixels;
>
> int width = pixels.length;
> int height = pixels[0].length;
>
> // convert to indexed color
> for (int x = width; x-- > 0; ) {
> for (int y = height; y-- > 0; ) {
> int pixel = pixels[x][y];
> int red = (pixel >> 16) & 0xFF;
> int green = (pixel >> 8) & 0xFF;
> int blue = (pixel >> 0) & 0xFF;
>
> // a hard limit on the number of nodes in the tree
> if (nodes > MAX_NODES) {
> System.out.println("pruning");
> root.pruneLevel();
> --depth;
> }
>
> // walk the tree to depth, increasing the
> // number_pixels count for each node
> Node node = root;
> for (int level = 1; level <= depth; ++level) {
> int id = (((red > node.mid_red ? 1 : 0) <<
> 0) |
> ((green > node.mid_green ? 1 : 0) <<
> 1) |
> ((blue > node.mid_blue ? 1 : 0) <<
> 2));
> if (node.child[id] == null) {
> new Node(node, id, level);
> }
> node = node.child[id];
> node.number_pixels += SHIFT[level];
> }
>
> ++node.unique;
> node.total_red += red;
> node.total_green += green;
> node.total_blue += blue;
> }
> }
> }
>
> /*
> * reduction repeatedly prunes the tree until the number of
> * nodes with unique > 0 is less than or equal to the maximum
> * number of colors allowed in the output image.
> *
> * When a node to be pruned has offspring, the pruning
> * procedure invokes itself recursively in order to prune the
> * tree from the leaves upward. The statistics of the node
> * being pruned are always added to the corresponding data in
> * that node's parent. This retains the pruned node's color
> * characteristics for later averaging.
> */
> void reduction() {
> long threshold = 1;
> while (colors > max_colors) {
> colors = 0;
> threshold = root.reduce(threshold, Long.MAX_VALUE);
> }
> }
>
> /**
> * The result of a closest color search.
> */
> static class Search {
> int distance;
> int color_number;
> }
>
> /*
> * Procedure assignment generates the output image from the
> * pruned tree. The output image consists of two parts: (1) A
> * color map, which is an array of color descriptions (RGB
> * triples) for each color present in the output image; (2) A
> * pixel array, which represents each pixel as an index into
> * the color map array.
> *
> * First, the assignment phase makes one pass over the pruned
> * color description tree to establish the image's color map.
> * For each node with n2 > 0, it divides Sr, Sg, and Sb by n2.
> * This produces the mean color of all pixels that classify no
> * lower than this node. Each of these colors becomes an entry
> * in the color map.
> *
> * Finally, the assignment phase reclassifies each pixel in
> * the pruned tree to identify the deepest node containing the
> * pixel's color. The pixel's value in the pixel array becomes
> * the index of this node's mean color in the color map.
> */
> void assignment() {
> colormap = new int[colors];
>
> colors = 0;
> root.colormap();
>
> int pixels[][] = this.pixels;
>
> int width = pixels.length;
> int height = pixels[0].length;
>
> Search search = new Search();
>
> // convert to indexed color
> for (int x = width; x-- > 0; ) {
> for (int y = height; y-- > 0; ) {
> int pixel = pixels[x][y];
> int red = (pixel >> 16) & 0xFF;
> int green = (pixel >> 8) & 0xFF;
> int blue = (pixel >> 0) & 0xFF;
>
> // walk the tree to find the cube containing that
> color
> Node node = root;
> for ( ; ; ) {
> int id = (((red > node.mid_red ? 1 : 0) <<
> 0) |
> ((green > node.mid_green ? 1 : 0) <<
> 1) |
> ((blue > node.mid_blue ? 1 : 0) <<
> 2) );
> if (node.child[id] == null) {
> break;
> }
> node = node.child[id];
> }
>
> if (QUICK) {
> // if QUICK is set, just use that
> // node. Strictly speaking, this isn't
> // necessarily best match.
> pixels[x][y] = node.color_number;
> } else {
> // Find the closest color.
> search.distance = Integer.MAX_VALUE;
> node.parent.closestColor(red, green, blue,
> search);
> pixels[x][y] = search.color_number;
> }
> }
> }
> }
>
> /**
> * A single Node in the tree.
> */
> static class Node {
> Cube cube;
>
> // parent node
> Node parent;
>
> // child nodes
> Node child[];
> int nchild;
>
> // our index within our parent
> int id;
> // our level within the tree
> int level;
> // our color midpoint
> int mid_red;
> int mid_green;
> int mid_blue;
>
> // the pixel count for this node and all children
> long number_pixels;
>
> // the pixel count for this node
> int unique;
> // the sum of all pixels contained in this node
> int total_red;
> int total_green;
> int total_blue;
>
> // used to build the colormap
> int color_number;
>
> Node(Cube cube) {
> this.cube = cube;
> this.parent = this;
> this.child = new Node[8];
> this.id = 0;
> this.level = 0;
>
> this.number_pixels = Long.MAX_VALUE;
>
> this.mid_red = (MAX_RGB + 1) >> 1;
> this.mid_green = (MAX_RGB + 1) >> 1;
> this.mid_blue = (MAX_RGB + 1) >> 1;
> }
>
> Node(Node parent, int id, int level) {
> this.cube = parent.cube;
> this.parent = parent;
> this.child = new Node[8];
> this.id = id;
> this.level = level;
>
> // add to the cube
> ++cube.nodes;
> if (level == cube.depth) {
> ++cube.colors;
> }
>
> // add to the parent
> ++parent.nchild;
> parent.child[id] = this;
>
> // figure out our midpoint
> int bi = (1 << (MAX_TREE_DEPTH - level)) >> 1;
> mid_red = parent.mid_red + ((id & 1) > 0 ? bi :
> -bi);
> mid_green = parent.mid_green + ((id & 2) > 0 ? bi :
> -bi);
> mid_blue = parent.mid_blue + ((id & 4) > 0 ? bi :
> -bi);
> }
>
> /**
> * Remove this child node, and make sure our parent
> * absorbs our pixel statistics.
> */
> void pruneChild() {
> --parent.nchild;
> parent.unique += unique;
> parent.total_red += total_red;
> parent.total_green += total_green;
> parent.total_blue += total_blue;
> parent.child[id] = null;
> --cube.nodes;
> cube = null;
> parent = null;
> }
>
> /**
> * Prune the lowest layer of the tree.
> */
> void pruneLevel() {
> if (nchild != 0) {
> for (int id = 0; id < 8; id++) {
> if (child[id] != null) {
> child[id].pruneLevel();
> }
> }
> }
> if (level == cube.depth) {
> pruneChild();
> }
> }
>
> /**
> * Remove any nodes that have fewer than threshold
> * pixels. Also, as long as we're walking the tree:
> *
> * - figure out the color with the fewest pixels
> * - recalculate the total number of colors in the tree
> */
> long reduce(long threshold, long next_threshold) {
> if (nchild != 0) {
> for (int id = 0; id < 8; id++) {
> if (child[id] != null) {
> next_threshold =
> child[id].reduce(threshold, next_threshold);
> }
> }
> }
> if (number_pixels <= threshold) {
> pruneChild();
> } else {
> if (unique != 0) {
> cube.colors++;
> }
> if (number_pixels < next_threshold) {
> next_threshold = number_pixels;
> }
> }
> return next_threshold;
> }
>
> /*
> * colormap traverses the color cube tree and notes each
> * colormap entry. A colormap entry is any node in the
> * color cube tree where the number of unique colors is
> * not zero.
> */
> void colormap() {
> if (nchild != 0) {
> for (int id = 0; id < 8; id++) {
> if (child[id] != null) {
> child[id].colormap();
> }
> }
> }
> if (unique != 0) {
> int r = ((total_red + (unique >> 1)) / unique);
> int g = ((total_green + (unique >> 1)) / unique);
> int b = ((total_blue + (unique >> 1)) / unique);
> cube.colormap[cube.colors] = ((( 0xFF) << 24) |
> ((r & 0xFF) << 16) |
> ((g & 0xFF) << 8) |
> ((b & 0xFF) << 0));
> color_number = cube.colors++;
> }
> }
>
> /* ClosestColor traverses the color cube tree at a
> * particular node and determines which colormap entry
> * best represents the input color.
> */
> void closestColor(int red, int green, int blue, Search
> search) {
> if (nchild != 0) {
> for (int id = 0; id < 8; id++) {
> if (child[id] != null) {
> child[id].closestColor(red, green, blue,
> search);
> }
> }
> }
>
> if (unique != 0) {
> int color = cube.colormap[color_number];
> int distance = distance(color, red, green, blue);
> if (distance < search.distance) {
> search.distance = distance;
> search.color_number = color_number;
> }
> }
> }
>
> /**
> * Figure out the distance between this node and som color.
> */
> final static int distance(int color, int r, int g, int b) {
> return (SQUARES[((color >> 16) & 0xFF) - r + MAX_RGB] +
> SQUARES[((color >> 8) & 0xFF) - g + MAX_RGB] +
> SQUARES[((color >> 0) & 0xFF) - b + MAX_RGB]);
> }
>
> public String toString() {
> StringBuffer buf = new StringBuffer();
> if (parent == this) {
> buf.append("root");
> } else {
> buf.append("node");
> }
> buf.append(' ');
> buf.append(level);
> buf.append(" [");
> buf.append(mid_red);
> buf.append(',');
> buf.append(mid_green);
> buf.append(',');
> buf.append(mid_blue);
> buf.append(']');
> return new String(buf);
> }
> }
> }
> }
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/openmap/openmap/src/openmap/com/bbn/openmap/Layer.java,v $
> // $RCSfile: Layer.java,v $
> // $Revision: 1.18 $
> // $Date: 2004/10/14 18:18:15 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
> package com.bbn.openmap;
>
> import java.awt.Component;
> import java.awt.Container;
> import java.awt.Frame;
> import java.awt.Graphics;
> import java.awt.event.ActionEvent;
> import java.awt.event.ActionListener;
> import java.awt.event.ComponentAdapter;
> import java.awt.event.ComponentEvent;
> import java.awt.event.ComponentListener;
> import java.awt.event.MouseEvent;
> import java.awt.event.MouseListener;
> import java.beans.PropertyVetoException;
> import java.beans.VetoableChangeListener;
> import java.beans.beancontext.BeanContext;
> import java.beans.beancontext.BeanContextChild;
> import java.beans.beancontext.BeanContextChildSupport;
> import java.beans.beancontext.BeanContextMembershipEvent;
> import java.beans.beancontext.BeanContextMembershipListener;
> import java.util.Iterator;
> import java.util.Properties;
>
> import javax.swing.JComponent;
>
> import com.bbn.openmap.I18n;
> import com.bbn.openmap.ProjectionPainter;
> import com.bbn.openmap.event.InfoDisplayEvent;
> import com.bbn.openmap.event.InfoDisplayListener;
> import com.bbn.openmap.event.LayerStatusEvent;
> import com.bbn.openmap.event.LayerStatusListener;
> import com.bbn.openmap.event.ListenerSupport;
> import com.bbn.openmap.event.MapMouseListener;
> import com.bbn.openmap.event.ProjectionEvent;
> import com.bbn.openmap.event.ProjectionListener;
> import com.bbn.openmap.gui.ScrollPaneWindowSupport;
> import com.bbn.openmap.gui.WindowSupport;
> import com.bbn.openmap.proj.Projection;
> import com.bbn.openmap.util.Debug;
> import com.bbn.openmap.util.PropUtils;
> import com.bbn.openmap.util.propertyEditor.Inspector;
>
> /**
> * Layer objects are components which can be added to the MapBean to
> * make a map.
> * <p>
> *
> * Layers implement the ProjectionListener interface to listen for
> * ProjectionEvents. When the projection changes, they may need to
> * refetch, regenerate their graphics, and then repaint themselves
> * into the new view.
> * <p>
> *
> * When the Layer is added to the MapBean, it will start receiving
> * ProjectionEvents via the ProjectionListener.projectionChanged()
> * method it has to implement. There is a
> * setProjection(ProjectionEvent) methods that should be called from
> * there if you want to save the projection for later use (handling
> * MouseEvents, etc). If you call getProjection() before calling
> * setProjection(), getProjection() will return null, and your
> * OMGraphics will complain and probably freak out at some point.
> *
> * <pre>
> * //// SAMPLE handling of the ProjectionListener interface.
> *
> * public void projectionChanged(com.bbn.openmap.event.ProjectionEvent
> pe) {
> * Projection proj = setProjection(pe);
> * if (proj != null) {
> * // Use the projection to gather OMGraphics in the layer,
> * // and prepare the layer so that in the paint() method,
> * // the OMGraphics get rendered.
> *
> * // Call any methods that kick off work to build graphics
> * // here...
> *
> * // You get the paint() methods called by calling
> * // repaint():
> * repaint();
> * }
> *
> * fireStatusUpdate(LayerStatusEvent.FINISH_WORKING);
> * }
> * </pre>
> *
> * _at_see com.bbn.openmap.event.ProjectionListener
> * _at_see com.bbn.openmap.event.ProjectionEvent
> * _at_see com.bbn.openmap.PropertyConsumer
> */
> public abstract class Layer extends JComponent implements
> ProjectionListener,
> ProjectionPainter, BeanContextChild,
> BeanContextMembershipListener,
> PropertyConsumer, ActionListener {
>
> /**
> * Precaches the swing package. Computed based on the package of
> * <code>JComponent</code>.
> */
> protected static final String SWING_PACKAGE =
> getPackage(JComponent.class);
>
> /**
> * The String to use for a key lookup in a Properties object to
> * find the name to use in a GUI relating to this layer.
> */
> public static final String PrettyNameProperty = "prettyName";
>
> /**
> * The property to set to add the layer to the BeanContext
> * "addToBeanContext". This probably needs be set by the layer
> * itself, because it knows whether it needs other components or
> * not. However, this property is defined in case an option can be
> * given to the user. If a Layer doesn't want this option given,
> * it should reset the addToBeanContext variable after
> * setProperties() is called. The Layer.setProperties() methods
> * maintain the current state of the variable if undefined, which
> * is true by default.
> */
> public static final String AddToBeanContextProperty =
> "addToBeanContext";
>
> /**
> * Property 'background' to designate this layer as a background
> * layer, which will cause extra buffering to occur if the
> * application can handle it. False by default.
> */
> public static final String AddAsBackgroundProperty = "background";
>
> /**
> * Property 'removeable' to designate this layer as removeable
> * from the application, or able to be deleted. True by default.
> */
> public static final String RemoveableProperty = "removeable";
>
> /**
> * The property to show the palette when the layer is created -
> * or, more accurately, when the properties are set.
> */
> public static final String AutoPaletteProperty = "autoPalette";
> /** Layer-defined action event command to display the palette. */
> public static final String DisplayPaletteCmd = "displayPaletteCmd";
> /** Layer-defined action event command to hide the palette. */
> public static final String HidePaletteCmd = "hidePaletteCmd";
> /**
> * Layer-defined action event command to display the properties
> * using an Inspector.
> */
> public static final String DisplayPropertiesCmd =
> "displayPropertiesCmd";
> /**
> * Layer-defined action event command to force a redraw on the
> * layer. The Layer class does not respond to this command, it's
> * provided as a convenience.
> */
> public static final String RedrawCmd = "redrawCmd";
>
> /**
> * The listeners to the Layer that respond to requests for
> * information displays, like messages, requests for URL displays,
> * etc.
> */
> protected ListenerSupport IDListeners = null;
>
> /**
> * List of LayerStatusListeners.
> */
> protected ListenerSupport lsListeners = null;
>
> /**
> * Token uniquely identifying this layer in the application
> * properties.
> */
> protected String propertyPrefix = null;
>
> /**
> * Used by the LayerHandler to check if the layer should be added
> * to the MapHandler BeanContext. See the comments under the
> * AddToBeanContextProperty. True by default.
> */
> protected boolean addToBeanContext = true;
>
> /**
> * Flag used by the layer to indicate that it should be treated as
> * a background layer, indicating that any cache mechanism
> * available can enable extra buffering. This may prevent mouse
> * events from being received by the layer.
> */
> protected boolean addAsBackground = false;
>
> /**
> * Flag to designate the layer as removeable or not.
> */
> protected boolean removeable = true;
>
> /**
> * A flag to have the layer display it's palette when the
> * properties are set. If you are creating a layer manually, just
> * call showPalette() instead.
> */
> protected boolean autoPalette = false;
>
> /**
> * This is a convenience copy of the latest projection received
> * from the MapBean, when the Layer is added to the map. If you
> * need it, use the accessor!.
> */
> private Projection projection = null;
>
> /**
> * Support class that now handles palette windows.
> */
> protected transient WindowSupport windowSupport;
>
> /**
> * A helper component listener that is paying attention to the
> * visibility of the palette.
> */
> protected transient ComponentListener paletteListener;
>
> /**
> * A pointer to the JDialog or JInternalFrame. May be used by the
> * layer's ComponentListeners to figure out if a component event
> * is for the layer or for the palette.
> */
> protected transient Container palette;
>
> /**
> * The BeanContext allows Layers to find other components, and
> * other components to find the layer, if the layer is added to
> * it.
> */
> protected final BeanContextChildSupport beanContextChildSupport =
> new BeanContextChildSupport(this);
> /**
> * All layers have access to an I18n object, which is provided by
> * the Environment.
> */
> protected I18n i18n = Environment.getI18n();
>
> /**
> * Returns the package of the given class as a string.
> *
> * _at_param c a class
> */
> protected static String getPackage(Class c) {
> String className = c.getName();
> int lastDot = className.lastIndexOf('.');
> return className.substring(0, lastDot);
> }
>
> /**
> * Override to only allow swing package listeners. If Listeners
> * get added to the Layers, the mouse events don't make it to the
> * map. Ever.
> * <p>
> * Swing popup menus, like <code>JPopupMenu</code> grab the
> * JComponent by adding themselves as <code>MouseListener</code>
> * s. So this method allows instances of classes in the xxx.swing
> * package to be added as <code>MouseListener</code>s, and no
> * one else.
> *
> * _at_param l a mouse listener.
> */
> public final void addMouseListener(MouseListener l) {
> String pkg = getPackage(l.getClass());
> if (java.beans.Beans.isDesignTime() ||
> pkg.equals(SWING_PACKAGE)
> || pkg.startsWith(SWING_PACKAGE)) {
>
> // Used to do nothing for the equals and startsWith
> // comparison, but that breaks the menus from being
> // recinded when something else is clicked on. Thanks to
> // Tom Peel for pointing this out, 11/29/00.
> super.addMouseListener(l);
>
> } else {
> throw new IllegalArgumentException("This operation is
> disallowed because the package \""
> + pkg
> + "\" is not in the swing package (\""
> + SWING_PACKAGE + "\").");
> }
> }
>
> /**
> * Sets the properties for the <code>Layer</code>. This
> * particular method assumes that the marker name is not needed,
> * because all of the contents of this Properties object are to be
> * used for this layer, and scoping the properties with a prefix
> * is unnecessary.
> *
> * _at_param props the <code>Properties</code> object.
> */
> public void setProperties(Properties props) {
> setProperties(getPropertyPrefix(), props);
> }
>
> /**
> * Sets the properties for the <code>Layer</code>. Part of the
> * PropertyConsumer interface. Layers which override this method
> * should do something like:
> *
> * <code><pre>
> * public void setProperties(String prefix, Properties props) {
> * super.setProperties(prefix, props);
> * // do local stuff
> * }
> * </pre></code>
> *
> * If the addToBeanContext property is not defined, it maintains
> * the same state.
> *
> * _at_param prefix the token to prefix the property names
> * _at_param props the <code>Properties</code> object
> */
> public void setProperties(String prefix, Properties props) {
> String prettyName = PrettyNameProperty;
> setPropertyPrefix(prefix);
>
> String realPrefix = PropUtils.getScopedPropertyPrefix(prefix);
>
> prettyName = realPrefix + PrettyNameProperty;
>
> String defaultName = getName();
> if (defaultName == null) {
> defaultName = "Anonymous";
> }
>
> setName(props.getProperty(prettyName, defaultName));
>
> setAddToBeanContext(PropUtils.booleanFromProperties(props,
> realPrefix
> + AddToBeanContextProperty, addToBeanContext));
>
> setAddAsBackground(PropUtils.booleanFromProperties(props,
> realPrefix
> + AddAsBackgroundProperty, addAsBackground));
>
> setRemoveable(PropUtils.booleanFromProperties(props, realPrefix
> + RemoveableProperty, removeable));
>
> autoPalette = PropUtils.booleanFromProperties(props, realPrefix
> + AutoPaletteProperty, autoPalette);
> }
>
> public void setName(String name) {
> super.setName(name);
>
> BeanContext bc = getBeanContext();
> if (bc != null && bc instanceof MapHandler) {
> LayerHandler lh = (LayerHandler) ((MapHandler)
> bc).get("com.bbn.openmap.LayerHandler");
>
> if (lh != null) {
> lh.setLayers();
> }
> }
> }
>
> /**
> * PropertyConsumer method, to fill in a Properties object,
> * reflecting the current values of the layer. If the layer has a
> * propertyPrefix set, the property keys should have that prefix
> * plus a separating '.' prepended to each propery key it uses for
> * configuration.
> *
> * _at_param props a Properties object to load the PropertyConsumer
> * properties into. If props equals null, then a new
> * Properties object should be created.
> * _at_return Properties object containing PropertyConsumer property
> * values. If getList was not null, this should equal
> * getList. Otherwise, it should be the Properties object
> * created by the PropertyConsumer.
> */
> public Properties getProperties(Properties props) {
> if (props == null) {
> props = new Properties();
> }
>
> String prefix =
> PropUtils.getScopedPropertyPrefix(propertyPrefix);
> props.put(prefix + "class", this.getClass().getName());
>
> String prettyName = getName();
> if (prettyName != null) {
> props.put(prefix + PrettyNameProperty, prettyName);
> }
>
> props.put(prefix + AutoPaletteProperty,
> new Boolean(autoPalette).toString());
> props.put(prefix + AddAsBackgroundProperty,
> new Boolean(addAsBackground).toString());
> props.put(prefix + RemoveableProperty,
> new Boolean(removeable).toString());
> props.put(prefix + AddToBeanContextProperty,
> new Boolean(addToBeanContext).toString());
>
> return props;
> }
>
> /**
> * Method to fill in a Properties object with values reflecting
> * the properties able to be set on this PropertyConsumer. The key
> * for each property should be the raw property name (without a
> * prefix) with a value that is a String that describes what the
> * property key represents, along with any other information about
> * the property that would be helpful (range, default value,
> * etc.). For Layer, this method should at least return the
> * 'prettyName' property.
> *
> * _at_param list a Properties object to load the PropertyConsumer
> * properties into. If getList equals null, then a new
> * Properties object should be created.
> * _at_return Properties object containing PropertyConsumer property
> * values. If getList was not null, this should equal
> * getList. Otherwise, it should be the Properties object
> * created by the PropertyConsumer.
> */
> public Properties getPropertyInfo(Properties list) {
> if (list == null) {
> list = new Properties();
> }
>
> list.put("class", "Class Name used for Layer.");
> list.put("class.editor",
>
> "com.bbn.openmap.util.propertyEditor.NonEditablePropertyEditor");
>
> String internString = i18n.get(Layer.class,
> PrettyNameProperty,
> I18n.TOOLTIP,
> "Presentable name for Layer");
> list.put(PrettyNameProperty, internString);
> internString = i18n.get(Layer.class, PrettyNameProperty,
> "Layer Name");
> list.put(PrettyNameProperty + LabelEditorProperty,
> internString);
> list.put(PrettyNameProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.NonEditablePropertyEditor");
>
> internString = i18n.get(Layer.class,
> AutoPaletteProperty,
> I18n.TOOLTIP,
> "Flag to automatically display palette when properties
> are set");
> list.put(AutoPaletteProperty, internString);
> internString = i18n.get(Layer.class,
> AutoPaletteProperty,
> "Open Palette At Start");
> list.put(AutoPaletteProperty + LabelEditorProperty,
> internString);
> list.put(AutoPaletteProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> internString = i18n.get(Layer.class,
> AddAsBackgroundProperty,
> I18n.TOOLTIP,
> "Flag to use the layer as a background layer");
> list.put(AddAsBackgroundProperty, internString);
> internString = i18n.get(Layer.class,
> AddAsBackgroundProperty,
> "Background");
> list.put(AddAsBackgroundProperty + LabelEditorProperty,
> internString);
> list.put(AddAsBackgroundProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> internString = i18n.get(Layer.class,
> RemoveableProperty,
> I18n.TOOLTIP,
> "Flag to allow layer to be deleted.");
> list.put(RemoveableProperty, internString);
> internString = i18n.get(Layer.class, RemoveableProperty,
> "Removeable");
> list.put(RemoveableProperty + LabelEditorProperty,
> internString);
> list.put(RemoveableProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> internString = i18n.get(Layer.class,
> AddToBeanContextProperty,
> I18n.TOOLTIP,
> "Flag to give the layer access to all of the other
> application components.");
> list.put(AddToBeanContextProperty, internString);
> internString = i18n.get(Layer.class,
> AddToBeanContextProperty,
> "Add to MapHandler");
> list.put(AddToBeanContextProperty + LabelEditorProperty,
> internString);
> list.put(AddToBeanContextProperty + ScopedEditorProperty,
>
> "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
>
> return list;
> }
>
> /**
> * Set the property key prefix that should be used by the
> * PropertyConsumer. The prefix, along with a '.', should be
> * prepended to the property keys known by the PropertyConsumer.
> *
> * _at_param prefix the prefix String.
> */
> public void setPropertyPrefix(String prefix) {
> propertyPrefix = prefix;
> }
>
> /**
> * Get the property key prefix that is being used to prepend to
> * the property keys for Properties lookups.
> *
> * _at_return the property prefix for the layer
> */
> public String getPropertyPrefix() {
> return propertyPrefix;
> }
>
> /**
> * Set the projection the layer should use for calculations. You
> * probably don't need this if you are wondering if you do. Call
> * setProjection(projEvent) instead.
> */
> public void setProjection(Projection proj) {
> projection = proj;
> }
>
> /**
> * This method lets you take the ProjectionEvent received from the
> * MapBean, and lets you know if you should do something with it.
> * MUST to be called in the projectionChanged() method of your
> * layer, if you want to refer to the projection later. If this
> * methods returns null, you probably just want to call repaint()
> * if your layer.paint() method is ready to paint what it should.
> *
> * _at_param projEvent the ProjectionEvent from the
> * ProjectionListener method.
> * _at_return The new Projection if it is different from the one we
> * already have, null if is the same as the current one.
> */
> public Projection setProjection(ProjectionEvent projEvent) {
> Projection newProjection = projEvent.getProjection();
>
> if (!newProjection.equals(getProjection())) {
> Projection clone = newProjection.makeClone();
> setProjection(clone);
> return clone;
> } else {
> return null;
> }
> }
>
> /**
> * Get the latest projection.
> */
> public Projection getProjection() {
> return projection;
> }
>
> /**
> * Returns the MapMouseListener object that handles the mouse
> * events. This method is IGNORED in this class: it returns null.
> * Derived Layers should return the appropriate object if they
> * desire to receive MouseEvents. The easiest thing for a Layer to
> * do in order to receive MouseEvents is to implement the
> * MapMouseListener interface and return itself. A code snippet:
> * <code><pre>
> * public MapMouseListener getMapMouseListener() {
> * return this;
> * }
> *
> * public String[] getMouseModeServiceList() {
> * return new String[] { SelectMouseMode.modeID };
> * }
> * </pre></code>
> *
> * _at_return null
> */
> public MapMouseListener getMapMouseListener() {
> return null;
> }
>
> /**
> * Gets the gui controls associated with the layer. This default
> * implementation returns null indicating that the layer has no
> * gui controls.
> *
> * _at_return java.awt.Component or null
> */
> public Component getGUI() {
> return null;
> }
>
> ///////////////////////////////////////////////////
> // InfoDisplay Handling Setup and Firing
>
> /**
> * Adds a listener for <code>InfoDisplayEvent</code>s.
> *
> * _at_param aInfoDisplayListener the listener to add
> */
> public synchronized void addInfoDisplayListener(
>
> InfoDisplayListener aInfoDisplayListener) {
> if (IDListeners == null) {
> IDListeners = new ListenerSupport(this);
> }
> IDListeners.addListener(aInfoDisplayListener);
> }
>
> /**
> * Removes an InfoDisplayListener from this Layer.
> *
> * _at_param aInfoDisplayListener the listener to remove
> */
> public synchronized void removeInfoDisplayListener(
>
> InfoDisplayListener aInfoDisplayListener) {
>
> if (IDListeners != null) {
> IDListeners.removeListener(aInfoDisplayListener);
> }
> }
>
> /**
> * Sends a request to the InfoDisplayListener to show the
> * information in the InfoDisplay event on an single line display
> * facility.
> *
> * _at_param evt the InfoDisplay event carrying the string.
> */
> public void fireRequestInfoLine(InfoDisplayEvent evt) {
> if (IDListeners != null) {
> for (Iterator it = IDListeners.iterator(); it.hasNext();) {
> ((InfoDisplayListener) it.next()).requestInfoLine(evt);
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireRequestInfoLine(): no info request
> listener!");
> }
> }
>
> /**
> * Sends a request to the InfoDisplay listener to display the
> * information on an single line display facility. The
> * InfoDisplayEvent is created inside this function.
> *
> * _at_param infoLine the string to put in the InfoDisplayEvent.
> */
> public void fireRequestInfoLine(String infoLine) {
> fireRequestInfoLine(new InfoDisplayEvent(this, infoLine));
> }
>
> /**
> * Sends a request to the InfoDisplay listener to display the
> * information on an single line display facility at preferred
> * location. The InfoDisplayEvent is created inside this function.
> *
> * _at_param infoLine the string to put in the InfoDisplayEvent.
> * _at_param loc the index of a preferred location, starting at 0.
> */
> public void fireRequestInfoLine(String infoLine, int loc) {
> fireRequestInfoLine(new InfoDisplayEvent(this, infoLine, loc));
> }
>
> /**
> * Sends a request to the InfoDisplay listener to display the
> * information in the InfoDisplay event in a Browser.
> *
> * _at_param evt the InfoDisplayEvent holding the contents to put in
> * the Browser.
> */
> public void fireRequestBrowserContent(InfoDisplayEvent evt) {
> if (IDListeners != null) {
> for (Iterator it = IDListeners.iterator(); it.hasNext();) {
> ((InfoDisplayListener)
> it.next()).requestBrowserContent(evt);
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireRequestBrowserContent(): no info
> request listener!");
> }
> }
>
> /**
> * Sends a request to the InfoDisplayListener to display the
> * information in a Browser. The InfoDisplayEvent is created here
> * holding the browserContent
> *
> * _at_param browserContent the contents to put in the Browser.
> */
> public void fireRequestBrowserContent(String browserContent) {
> fireRequestBrowserContent(new InfoDisplayEvent(this,
> browserContent));
> }
>
> /**
> * Sends a request to the InfoDisplayListener to display a URL
> * given in the InfoDisplay event in a Browser.
> *
> * _at_param evt the InfoDisplayEvent holding the url location to
> * give to the Browser.
> */
> public void fireRequestURL(InfoDisplayEvent evt) {
> if (IDListeners != null) {
> for (Iterator it = IDListeners.iterator(); it.hasNext();) {
> ((InfoDisplayListener) it.next()).requestURL(evt);
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireRequestURL(): no info request
> listener!");
> }
> }
>
> /**
> * Sends a request to the InfoDisplayListener to display a URL in
> * a browser. The InfoDisplayEvent is created here, and the URL
> * location is put inside it.
> *
> * _at_param url the url location to give to the Browser.
> */
> public void fireRequestURL(String url) {
> fireRequestURL(new InfoDisplayEvent(this, url));
> }
>
> /**
> * Sends a request to the InfoDisplayListener to show a specific
> * cursor over its component area.
> *
> * _at_param cursor the cursor to use.
> */
> public void fireRequestCursor(java.awt.Cursor cursor) {
> if (IDListeners != null) {
> for (Iterator it = IDListeners.iterator(); it.hasNext();) {
> ((InfoDisplayListener)
> it.next()).requestCursor(cursor);
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireRequestCursor(): no info request
> listener!");
> }
> }
>
> /**
> * Sends a request to the InfoDisplayListener to put the
> * information in the InfoDisplay event in a dialog window.
> *
> * _at_param evt the InfoDisplayEvent holding the message to put into
> * the dialog window.
> */
> public void fireRequestMessage(InfoDisplayEvent evt) {
> if (IDListeners != null) {
> for (Iterator it = IDListeners.iterator(); it.hasNext();) {
> ((InfoDisplayListener) it.next()).requestMessage(evt);
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireRequestMessage(): no info request
> listener!");
> }
> }
>
> /**
> * Sends a request to the InfoDisplayListener to display the
> * information in a dialog window. The InfoDisplayEvent is created
> * here, and the URL location is put inside it.
> *
> * _at_param message the message to put in the dialog window.
> */
> public void fireRequestMessage(String message) {
> fireRequestMessage(new InfoDisplayEvent(this, message));
> }
>
> /**
> * Request to show the tool tips on the map.
> *
> * _at_param me MouseEvent location for the tool tip.
> * _at_param tip string to display.
> * _at_deprecated use fireRequestToolTip(String tip) instead.
> */
> public void fireRequestToolTip(MouseEvent me, String tip) {
> fireRequestToolTip(new InfoDisplayEvent(this, tip));
> }
>
> /**
> * Request to show the tool tips on the map.
> *
> * _at_param tip string to display.
> */
> public void fireRequestToolTip(String tip) {
> fireRequestToolTip(new InfoDisplayEvent(this, tip));
> }
>
> /**
> * Request to hide the tool tips on the map.
> *
> * _at_param me MouseEvent location.
> * _at_deprecated use fireHideToolTip() instead.
> */
> public void fireHideToolTip(MouseEvent me) {
> fireRequestToolTip((InfoDisplayEvent) null);
> }
>
> /**
> * Request to hide the tool tips on the map.
> */
> public void fireHideToolTip() {
> fireRequestToolTip((InfoDisplayEvent) null);
> }
>
> /**
> * Fire off a Tool Tip request to the InfoDisplayListeners. If the
> * InfoDisplayEvent is null, then a requestHideToolTip will be
> * fired.
> *
> * _at_deprecated use fireHideToolTip(InfoDisplayEvent) instead.
> */
> public void fireRequestToolTip(MouseEvent me, InfoDisplayEvent
> event) {
> fireRequestToolTip(event);
> }
>
> /**
> * Fire off a Tool Tip request to the InfoDisplayListeners. If the
> * InfoDisplayEvent is null, then a requestHideToolTip will be
> * fired.
> */
> public void fireRequestToolTip(InfoDisplayEvent event) {
> if (IDListeners != null) {
> for (Iterator it = IDListeners.iterator(); it.hasNext();) {
> if (event != null) {
> ((InfoDisplayListener)
> it.next()).requestShowToolTip(event);
> } else {
> ((InfoDisplayListener)
> it.next()).requestHideToolTip();
> }
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireRequestShowToolTip(): no info
> request listener!");
> }
> }
>
> ///////////////////////////////////////////////////
> // LayerStatus Handling Setup and Firing
>
> /**
> * Adds a listener for <code>LayerStatusEvent</code>s.
> *
> * _at_param aLayerStatusListener LayerStatusListener
> */
> public synchronized void addLayerStatusListener(
>
> LayerStatusListener aLayerStatusListener) {
>
> if (lsListeners == null) {
> lsListeners = new ListenerSupport(this);
> }
> lsListeners.addListener(aLayerStatusListener);
> }
>
> /**
> * Removes a LayerStatusListene from this Layer.
> *
> * _at_param aLayerStatusListener the listener to remove
> */
> public synchronized void removeLayerStatusListener(
>
> LayerStatusListener aLayerStatusListener) {
>
> if (lsListeners != null) {
> lsListeners.removeListener(aLayerStatusListener);
> }
> }
>
> /**
> * Sends a status update to the LayerStatusListener.
> *
> * _at_param evt LayerStatusEvent
> */
> public void fireStatusUpdate(LayerStatusEvent evt) {
> // AWTAvailable conditional removed, not used, not useful.
> if (lsListeners != null) {
> for (Iterator it = lsListeners.iterator(); it.hasNext();) {
> ((LayerStatusListener)
> it.next()).updateLayerStatus(evt);
> }
> } else if (Debug.debugging("layer")) {
> Debug.output(getName()
> + "|Layer.fireStatusUpdate(): no
> LayerStatusListeners!");
> }
> }
>
> /**
> * Sends a status update to the LayerStatusListener.
> *
> * _at_param status the new status
> */
> public void fireStatusUpdate(int status) {
> fireStatusUpdate(new LayerStatusEvent(this, status));
> }
>
> /**
> * Repaint the layer. If you are using BufferedMapBean for your
> * application, WE STRONGLY RECOMMEND THAT YOU DO NOT OVERRIDE
> * THIS METHOD. This method marks the layer buffer so that it will
> * be refreshed. If you override this method, and don't call
> * super.repaint(), the layers will not be repainted.
> */
> public void repaint(long tm, int x, int y, int width, int height) {
> Component p = getParent();
> if (p instanceof MapBean) {
> ((MapBean) p).setBufferDirty(true);
> if (Debug.debugging("basic")) {
> Debug.output(getName() + "|Layer: repaint(tm=" + tm +
> ", x="
> + x + ", y=" + y + ", width=" + width + ",
> height="
> + height + ")");
> }
>
> // How dangerous is this? Let the MapBean manage the
> // repaint call? Seems to work OK, and lets the buffered
> // MapBeans work better when they are embedded in other
> // components. It's this call here that makes the
> // BufferedLayer work right.
>
> // This repaint request has been changed to call a
> // specific
> // method on the MapBean, which includes the layer making
> // the request. This is a hook for a policy object in the
> // MapBean to make a decision on whether to honor the
> // request, or to handle it in a different way if the
> // environment dictates that should happen.
>
> // ((MapBean)p).repaint(); to ->
> ((MapBean) p).repaint(this);
> } else if (p != null) {
> p.repaint(tm, x, y, width, height);
> } else {
> super.repaint(tm, x, y, width, height);
> }
> }
>
> /**
> * This method is here to provide a default action for Layers as
> * they act as a ProjectionPainter. Normally, ProjectionPainters
> * are expected to receive the projection, gather/create
> * OMGraphics that apply to the projection, and render them into
> * the Graphics provided. This is supposed to be done in the same
> * thread that calls this function, so the caller knows that when
> * this method returns, everything that the ProjectionPainter
> * needed to do is complete.
> * <P>
> * If the layer doesn't override this method, then the
> * paint(Graphics) method will be called.
> *
> * _at_param proj Projection of the map.
> * _at_param g java.awt.Graphics to draw into.
> */
> public void renderDataForProjection(Projection proj, Graphics g) {
> paint(g);
> }
>
> /**
> * This method is called when the layer is added to the MapBean
> *
> * _at_param cont Container
> */
> public void added(Container cont) {}
>
> /**
> * This method is called after the layer is removed from the
> * MapBean and when the projection changes. We recommend that
> * Layers override this method and nullify memory-intensive
> * variables.
> *
> * _at_param cont Container
> */
> public void removed(Container cont) {}
>
> /**
> * Part of a layer hack to notify the component listener when the
> * component is hidden. These components don't receive the
> * ComponentHidden notification. Remove when it works.
> */
> protected ListenerSupport localHackList;
>
> /**
> * Part of a layer hack to notify the component listener when the
> * component is hidden. These components don't receive the
> * ComponentHidden notification. Remove when it works. Set to
> * false to test.
> */
> protected boolean doHack = true;
>
> /**
> * Part of a layer hack to notify the component listener when the
> * component is hidden. These components don't receive the
> * ComponentHidden notification. Remove when it works.
> */
> public void setVisible(boolean show) {
> super.setVisible(show);
> if (doHack && !show) {
> notifyHideHack();
> }
> }
>
> /**
> * Part of a layer hack to notify the component listener when the
> * component is hidden. These components don't receive the
> * ComponentHidden notification. Remove when it works.
> */
> public void addComponentListener(ComponentListener cl) {
> super.addComponentListener(cl);
> if (localHackList == null) {
> localHackList = new ListenerSupport(this);
> }
> localHackList.addListener(cl);
> }
>
> /**
> * Part of a layer hack to notify the component listener when the
> * component is hidden. These components don't receive the
> * ComponentHidden notification. Remove when it works.
> */
> public void removeComponentListener(ComponentListener cl) {
> super.removeComponentListener(cl);
> if (localHackList != null) {
> localHackList.removeListener(cl);
> }
> }
>
> /**
> * Part of a layer hack to notify the component listener when the
> * component is hidden. These components don't receive the
> * ComponentHidden notification. Remove when it works.
> */
> public void notifyHideHack() {
> if (localHackList == null) {
> return;
> }
>
> ComponentEvent ce = new ComponentEvent(this,
> ComponentEvent.COMPONENT_HIDDEN);
>
> for (Iterator it = localHackList.iterator(); it.hasNext();) {
> ((ComponentListener) it.next()).componentHidden(ce);
> }
> }
>
> /**
> * Set whether the Layer should be added to the BeanContext.
> */
> public void setAddToBeanContext(boolean set) {
> addToBeanContext = set;
> }
>
> /**
> * Set whether the Layer should be added to the BeanContext.
> */
> public boolean getAddToBeanContext() {
> return addToBeanContext;
> }
>
> /**
> * Mark the layer as one that should be considered a background
> * layer. What that means is up to the MapBean or application.
> */
> public void setAddAsBackground(boolean set) {
> addAsBackground = set;
> }
>
> /**
> * Check to see if the layer is marked as one that should be
> * considered a background layer. What that means is up to the
> * MapBean or application.
> *
> * _at_return true if layer is a background layer.
> */
> public boolean getAddAsBackground() {
> return addAsBackground;
> }
>
> /**
> * Mark the layer as removeable, or one that can be deleted from
> * the application. What that means is up to the LayerHandler or
> * other application components.
> */
> public void setRemoveable(boolean set) {
> removeable = set;
> }
>
> /**
> * Check to see if the layer is marked as one that can be removed
> * from an application.
> *
> * _at_return true if layer should be allowed to be deleted.
> */
> public boolean isRemoveable() {
> return removeable;
> }
>
> /**
> * Check to see if the removeable layer can be removed now.
> * _at_return true if layer should be allowed to be deleted.
> */
> public boolean removeConfirmed() {
> return true;
> }
>
> /**
> * This is the method that your layer can use to find other
> * objects within the MapHandler (BeanContext). This method gets
> * called when the Layer gets added to the MapHandler, or when
> * another object gets added to the MapHandler after the Layer is
> * a member. If the LayerHandler creates the Layer from
> * properties, the LayerHandler will add the Layer to the
> * BeanContext if Layer.addToBeanContext is true. It is false by
> * default.
> *
> * For Layers, this method doesn't do anything by default. If you
> * need your layer to get ahold of another object, then you can
> * use the Iterator to go through the objects to look for the one
> * you need.
> */
> public void findAndInit(Iterator it) {
> while (it.hasNext()) {
> findAndInit(it.next());
> }
> }
>
> /**
> * This method is called by the findAndInit(Iterator) method, once
> * for every object inside the iterator. It's here to allow
> * subclasses a way to receive objects and still let the super
> * classes have a shot at the object. So, you can override this
> * method can call super.findAndInit(obj), or override the
> * findAndInit(Iterator) method and call super.findAndInit(obj).
> * Whatever.
> */
> public void findAndInit(Object obj) {}
>
> /**
> * BeanContextMembershipListener method. Called when a new object
> * is added to the BeanContext of this object.
> */
> public void childrenAdded(BeanContextMembershipEvent bcme) {
> findAndInit(bcme.iterator());
> }
>
> /**
> * BeanContextMembershipListener method. Called when a new object
> * is removed from the BeanContext of this object. For the Layer,
> * this method doesn't do anything. If your layer does something
> * with the childrenAdded method, or findAndInit, you should take
> * steps in this method to unhook the layer from the object used
> * in those methods.
> */
> public void childrenRemoved(BeanContextMembershipEvent bcme) {
> Iterator it = bcme.iterator();
> while (it.hasNext()) {
> findAndUndo(it.next());
> }
> }
>
> /**
> * This is the method that does the opposite as the
> * findAndInit(Object). Lets you call super classes with objects
> * that need to be removed.
> */
> public void findAndUndo(Object obj) {}
>
> /** Method for BeanContextChild interface. */
> public BeanContext getBeanContext() {
> return beanContextChildSupport.getBeanContext();
> }
>
> /**
> * Method for BeanContextChild interface. Gets an iterator from
> * the BeanContext to call findAndInit() over.
> */
> public void setBeanContext(BeanContext in_bc) throws
> PropertyVetoException {
>
> if (in_bc != null) {
> connectToBeanContext(in_bc);
> findAndInit(in_bc.iterator());
> }
> }
>
> /**
> * Layer method to just connect to the BeanContext, without
> * grabbing the interator as in setBeanContext(). Good for
> * protected sub-layers where you want to optimize the calling of
> * the findAndInit() method over them.
> */
> public void connectToBeanContext(BeanContext in_bc)
> throws PropertyVetoException {
>
> if (in_bc != null) {
> in_bc.addBeanContextMembershipListener(this);
> beanContextChildSupport.setBeanContext(in_bc);
> }
> }
>
> /**
> * Method for BeanContextChild interface. Uses the
> * BeanContextChildSupport to add a listener to this object's
> * property. This listener wants to have the right to veto a
> * property change.
> */
> public void addVetoableChangeListener(String propertyName,
> VetoableChangeListener
> in_vcl) {
>
> beanContextChildSupport.addVetoableChangeListener(propertyName,
> in_vcl);
> }
>
> /**
> * Method for BeanContextChild interface. Uses the
> * BeanContextChildSupport to remove a listener to this object's
> * property. The listener has the power to veto property changes.
> */
> public void removeVetoableChangeListener(String propertyName,
> VetoableChangeListener
> in_vcl) {
>
> beanContextChildSupport.removeVetoableChangeListener(propertyName,
> in_vcl);
> }
>
> /**
> * Report a vetoable property update to any registered listeners.
> * If anyone vetos the change, then fire a new event reverting
> * everyone to the old value and then rethrow the
> * PropertyVetoException.
> * <P>
> *
> * No event is fired if old and new are equal and non-null.
> * <P>
> *
> * _at_param name The programmatic name of the property that is about
> * to change
> *
> * _at_param oldValue The old value of the property
> * _at_param newValue - The new value of the property
> *
> * _at_throws PropertyVetoException if the recipient wishes the
> * property change to be rolled back.
> */
> public void fireVetoableChange(String name, Object oldValue,
> Object newValue)
> throws PropertyVetoException {
> super.fireVetoableChange(name, oldValue, newValue);
> beanContextChildSupport.fireVetoableChange(name, oldValue,
> newValue);
> }
>
> public void clearListeners() {
> if (localHackList != null) {
> localHackList.removeAll();
> }
> if (IDListeners != null) {
> IDListeners.removeAll();
> }
> if (lsListeners != null) {
> lsListeners.removeAll();
> }
>
> BeanContext bc = getBeanContext();
> if (bc != null) {
> bc.removeBeanContextMembershipListener(this);
> }
> }
>
> public void finalize() {
> if (Debug.debugging("gc")) {
> Debug.output("Layer |" + getName() + " |: getting GC'd");
> }
> }
>
> /**
> * Fire a component event to the Layer component listeners, with
> * the palette as the component, letting them know if it's visible
> * or not.
> */
> public void firePaletteEvent(ComponentEvent event) {
> if (localHackList == null) {
> return;
> }
>
> palette = (Container) event.getSource();
> int eventType = event.getID();
> for (Iterator it = localHackList.iterator(); it.hasNext();) {
> ComponentListener target = (ComponentListener) it.next();
> if (eventType == ComponentEvent.COMPONENT_HIDDEN) {
> target.componentHidden(event);
> } else if (eventType == ComponentEvent.COMPONENT_SHOWN) {
> target.componentShown(event);
> }
> }
>
> if (eventType == ComponentEvent.COMPONENT_HIDDEN) {
> palette = null;
> }
> }
>
> /**
> * Return the JDialog, or JInternalFrame, that serves as the
> * palette for the layer. May be null.
> */
> public Container getPalette() {
> return palette;
> }
>
> /**
> * Called when something about the layer has changed that would
> * require the palette to be reconfigured. Will cause getGUI() to
> * be called again. You should take steps before calling this
> * method to make sure that the getGUI() method is ready to
> * recreate the palette components from scratch if needed.
> */
> protected void resetPalette() {
> java.awt.Container pal = getPalette();
> boolean putUp = false;
> if (pal != null && pal.isVisible()) {
> putUp = true;
> setPaletteVisible(false);
> }
>
> if (putUp) {
> setPaletteVisible(true);
> }
> }
>
> /**
> * Make the palette visible or not, destroy if invisible.
> */
> public void setPaletteVisible(boolean visible) {
> if (visible) {
> showPalette();
> } else {
> hidePalette();
> }
> }
>
> /**
> * Set the WindowSupport object handling the palette.
> */
> public void setWindowSupport(WindowSupport ws) {
> windowSupport = ws;
> }
>
> /**
> * Get the WindowSupport object handling the palette.
> */
> public WindowSupport getWindowSupport() {
> return windowSupport;
> }
>
> protected WindowSupport createWindowSupport() {
> return new ScrollPaneWindowSupport(getGUI(), getName());
> }
>
> /**
> * Make the palette visible. Will automatically determine if we're
> * running in an applet environment and will use a JInternalFrame
> * over a JFrame if necessary.
> */
> public void showPalette() {
>
> WindowSupport ws = getWindowSupport();
> if (ws == null) {
> ws = createWindowSupport();
> paletteListener = new ComponentAdapter() {
> public void componentShown(ComponentEvent e) {
> firePaletteEvent(e);
> }
>
> public void componentHidden(ComponentEvent e) {
> firePaletteEvent(e);
> }
> };
> setWindowSupport(ws);
> } else {
> ws.setTitle(getName());
> ws.setContent(getGUI());
> }
>
> if (ws != null) {
> MapHandler mh = (MapHandler) getBeanContext();
> Frame frame = null;
> if (mh != null) {
> frame = (Frame) mh.get(java.awt.Frame.class);
>
> if (frame == null) {
> MapBean mapBean = (MapBean)
> mh.get("com.bbn.openmap.MapBean");
> if (mapBean == null) {
> Debug.message("layer",
> "Layer.showPalette: Warning...mapBean
> = null");
> } else {
> try {
> java.awt.Component parent =
> mapBean.getParent();
> while (parent.getParent() != null
> && !(parent instanceof
> java.awt.Frame)) {
> parent = parent.getParent();
> }
>
> if (parent instanceof java.awt.Frame) {
> frame = (java.awt.Frame) parent;
> }
>
> } catch (Exception e) {
> e.printStackTrace();
> } // ignore any problems here
> }
> }
> }
>
> if (paletteListener != null) {
> ws.addComponentListener(paletteListener);
> }
> ws.displayInWindow(frame);
> }
> }
>
> /**
> * Hide the layer's palette.
> */
> public void hidePalette() {
> WindowSupport ws = getWindowSupport();
> if (ws != null) {
> ws.killWindow();
> }
> }
>
> /**
> * The default actionPerformed method for Layer. Make sure you
> * call super.actionPerformed if you care about receiving palette
> * show/hide commands. This method is also set up to receive the
> * DisplayPropertiesCmd, and will bring up the Inspector for the
> * layer.
> */
> public void actionPerformed(ActionEvent ae) {
> String command = ae.getActionCommand();
> if (command == DisplayPaletteCmd) {
> if (Debug.debugging("layer")) {
> Debug.output(getName() + " displaying palette");
> }
> showPalette();
> } else if (command == HidePaletteCmd) {
> if (Debug.debugging("layer")) {
> Debug.output(getName() + " hiding palette");
> }
> hidePalette();
> } else if (command == DisplayPropertiesCmd) {
> Inspector inspector = new Inspector();
> inspector.inspectPropertyConsumer(this);
> }
> }
> }


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Don Dietrick, dietrick_at_bbn.com
BBN Technologies, Cambridge, MA
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

--
[To unsubscribe to this list send an email to "majdart_at_bbn.com"
with the following text in the BODY of the message "unsubscribe openmap-users"]
Received on Wed Jan 26 2005 - 09:54:34 EST

This archive was generated by hypermail 2.3.0 : Tue Mar 28 2017 - 23:25:06 EDT