// ********************************************************************** // // <copyright> // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/dted/DTEDLayer.java,v $ // $RCSfile: DTEDLayer.java,v $ // $Revision: 1.13 $ // $Date: 2005/12/09 21:09:06 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.dted; /* Java Core */ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.bbn.openmap.layer.OMGraphicHandlerLayer; import com.bbn.openmap.layer.policy.ListResetPCPolicy; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.omGraphics.OMRect; import com.bbn.openmap.omGraphics.OMText; import com.bbn.openmap.omGraphics.event.MapMouseInterpreter; import com.bbn.openmap.omGraphics.event.StandardMapMouseInterpreter; import com.bbn.openmap.proj.EqualArc; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.proj.coords.LatLonPoint; import com.bbn.openmap.util.Debug; import com.bbn.openmap.util.PaletteHelper; import com.bbn.openmap.util.PropUtils; /** * The DTEDLayer fills the screen with DTED data. To view the DTED images, the * projection has to be set in a Equal ARC projection, which OpenMap calls the * CADRG projection or the LLXY projection. In Gesture mode, clicking on the map * will cause the DTEDLayer to place a point on the window and show the * elevation of that point. The Gesture response is not dependent on the scale * or projection of the screen. * <P> * * The DTEDLayer uses the DTEDCacheManager to get the images it needs. The * DTEDLayer receives projection change events, and then asks the cache manager * for the images it needs based on the new projection. * * The DTEDLayer also relies on properties to set its variables, such as the * dted frame paths (there can be several at a time), the opaqueness of the * frame images, number of colors to use, and some other display variables. The * DTEDLayer properties look something like this: * <P> * * NOTE: Make sure your DTED file and directory names are in lower case. You can * use the com.bbn.openmap.util.wanderer.ChangeCase class to make modifications * if necessary. * <P> * * <pre> * * #------------------------------ * # Properties for DTEDLayer * #------------------------------ * # This property should reflect the paths to the dted level 0, 1 and newer 2 (file extension .dt2) data directories, separated by a semicolon. * dted.paths=/usr/local/matt/data/dted;/cdrom/cdrom0/dted * * # Number between 0-255: 0 is transparent, 255 is opaque * dted.opaque=255 * * # Number of colors to use on the maps - 16, 32, 216 * dted.number.colors=216 * * # Level of DTED data to use 0, 1, 2 * dted.level=0 * * # Type of display for the data * # 0 = no shading at all * # 1 = greyscale slope shading * # 2 = band shading, in meters * # 3 = band shading, in feet * # 4 = subframe testing * # 5 = elevation, colored * dted.view.type=5 * * # Contrast setting, 1-5 * dted.contrast=3 * * # height (meters or feet) between color changes in band shading * dted.band.height=25 * * # Minumum scale to display images. Larger numbers mean smaller scale, * # and are more zoomed out. * dted.min.scale=20000000 * * # Delete the cache if the layer is removed from the map. * dted.kill.cache=true * # Number of frames to hold in the cache. The default is * # DTEDFrameCache.FRAME_CACHE_SIZE, which is 15 to help smaller systems. Better * # caching happens, the larger the number. * dted.cacheSize=40 * #------------------------------------- * # End of properties for DTEDLayer * #------------------------------------- * * </pre> * * @see com.bbn.openmap.util.wanderer.ChangeCase */ public class DTEDLayer extends OMGraphicHandlerLayer { /** The cache manager. */ protected transient DTEDCacheManager cache = null; /** * Set when the projection has changed while a swing worker is gathering * graphics, and we want him to stop early. */ protected boolean cancelled = false; /** * The paths to the DTED Level 0, 1 directories, telling where the data is. * Newer level 2 data, with the .dt2 file extensions, should be set in this * path property. */ protected String[] paths; /** * The level of DTED to use. Level 0 is 1km post spacing, Level 1 is 100m * post spacing. Level 2 is 30m post spacing */ protected int dtedLevel = DTEDFrameSubframe.LEVEL_0; /** * The display type for the dted images. Slope shading is greyscale terrain * modeling with highlights and shading, with the 'sun' being in the * NorthWest. Colored Elevation shading is the same thing, except colors are * added to indicate the elevation. Band shading colors the pixels according * to a range of elevations. */ protected int viewType = DTEDFrameSubframe.NOSHADING; /** The elevation range to use for each color in band shading. */ protected int bandHeight = 25; /** A contrast adjustment, for slope shading (1-5). */ protected int slopeAdjust = DTEDFrameSubframe.DEFAULT_SLOPE_ADJUST; protected int numColors = DTEDFrameColorTable.DTED_COLORS; protected int opaqueness = DTEDFrameColorTable.DEFAULT_OPAQUENESS; /** Flag to delete the cache if the layer is removed from the map. */ protected boolean killCache = true; /** The number of frames held by the cache objects. */ protected int cacheSize = DTEDCacheHandler.FRAME_CACHE_SIZE; public static final String DTEDPathsProperty = "paths"; public static final String DTED2PathsProperty = "level2.paths"; public static final String OpaquenessProperty = "opaque"; public static final String NumColorsProperty = "number.colors"; public static final String DTEDLevelProperty = "level"; public static final String DTEDViewTypeProperty = "view.type"; public static final String DTEDSlopeAdjustProperty = "contrast"; public static final String DTEDBandHeightProperty = "band.height"; public static final String DTEDMinScaleProperty = "min.scale"; public static final String DTEDKillCacheProperty = "kill.cache"; public static final String DTEDFrameCacheSizeProperty = "cacheSize"; private String level0Command = "setLevelTo0"; private String level1Command = "setLevelTo1"; private String level2Command = "setLevelTo2"; /** The elevation spot used in the gesture mode. */ DTEDLocation location = null; /** * Instances of this class are used to display elevation labels on the map. */ static class DTEDLocation { OMText text; OMRect dot; public DTEDLocation(int x, int y) { text = new OMText(x + 10, y, (String) null, (java.awt.Font) null, OMText.JUSTIFY_LEFT); dot = new OMRect(x - 1, y - 1, x + 1, y + 1); text.setLinePaint(java.awt.Color.red); dot.setLinePaint(java.awt.Color.red); } /** * Set the text to the elevation text. * * @param elevation elevation of the point in meters. */ public void setElevation(int elevation) { // m - ft conversion if (elevation < -100) text.setData("No Data Here"); else { int elevation_ft = (int) ((float) elevation * 3.280840f); text.setData(elevation + " m / " + elevation_ft + " ft"); } } /** Set the x-y location of the combo in the screen */ public void setLocation(int x, int y) { text.setX(x + 10); text.setY(y); dot.setLocation(x - 1, y - 1, x + 1, y + 1); } public void render(java.awt.Graphics g) { text.render(g); dot.render(g); } public void generate(Projection proj) { text.generate(proj); dot.generate(proj); } } /** * The default constructor for the Layer. All of the attributes are set to * their default values. */ public DTEDLayer() { this(null); } /** * The default constructor for the Layer. All of the attributes are set to * their default values. * * @param pathsToDTEDDirs paths to the DTED directories that hold level 0, 1 * and 2 data. */ public DTEDLayer(String[] pathsToDTEDDirs) { setName("DTED"); setDefaultValues(); setPaths(pathsToDTEDDirs); setMouseModeIDsForEvents(new String[] { "Gestures" }); setProjectionChangePolicy(new ListResetPCPolicy(this)); } /** * Set the paths to the DTED directories. */ public void setPaths(String[] pathsToDTEDDirs) { paths = pathsToDTEDDirs; if (cache != null) { cache.setDtedDirPaths(pathsToDTEDDirs); } } /** * Get the paths to the DTED directories. */ public String[] getPaths() { return paths; } public DTEDCacheManager getCache() { if (cache == null) { // Debug.output("DTEDLayer: Creating cache! (This is a one-time operation!)"); cache = new DTEDCacheManager(paths, numColors, opaqueness); cache.setCacheSize(cacheSize); DTEDFrameSubframeInfo dfsi = new DTEDFrameSubframeInfo(viewType, bandHeight, dtedLevel, slopeAdjust); cache.setSubframeInfo(dfsi); } return cache; } public void setCache(DTEDCacheManager cache) { this.cache = cache; } protected void setDefaultValues() { // defaults paths = null; setOpaqueness(DTEDFrameColorTable.DEFAULT_OPAQUENESS); setDtedLevel(DTEDFrameSubframe.LEVEL_0); setBandHeight(DTEDFrameSubframe.DEFAULT_BANDHEIGHT); setSlopeAdjust(DTEDFrameSubframe.DEFAULT_SLOPE_ADJUST); setViewType(DTEDFrameSubframe.COLOREDSHADING); setMaxScale(20000000); } /** * Set all the DTED properties from a properties object. */ public void setProperties(String prefix, java.util.Properties properties) { super.setProperties(prefix, properties); prefix = PropUtils.getScopedPropertyPrefix(this); paths = PropUtils.initPathsFromProperties(properties, prefix + DTEDPathsProperty, paths); setOpaqueness(PropUtils.intFromProperties(properties, prefix + OpaquenessProperty, getOpaqueness())); setNumColors(PropUtils.intFromProperties(properties, prefix + NumColorsProperty, getNumColors())); setDtedLevel(PropUtils.intFromProperties(properties, prefix + DTEDLevelProperty, getDtedLevel())); setViewType(PropUtils.intFromProperties(properties, prefix + DTEDViewTypeProperty, getViewType())); setSlopeAdjust(PropUtils.intFromProperties(properties, prefix + DTEDSlopeAdjustProperty, getSlopeAdjust())); setBandHeight(PropUtils.intFromProperties(properties, prefix + DTEDBandHeightProperty, getBandHeight())); // The Layer maxScale is talking the place of the DTEDLayer minScale // property. setMaxScale(PropUtils.floatFromProperties(properties, prefix + DTEDMinScaleProperty, getMaxScale())); setCacheSize((int) PropUtils.intFromProperties(properties, prefix + DTEDFrameCacheSizeProperty, getCacheSize())); setKillCache(PropUtils.booleanFromProperties(properties, prefix + DTEDKillCacheProperty, getKillCache())); } /** * Called when the layer is no longer part of the map. In this case, we * should disconnect from the server if we have a link. */ public void removed(java.awt.Container cont) { if (killCache) { Debug.output("DTEDLayer: emptying cache!"); cache = null; } } /** * Prepares the graphics for the layer. This is where the getRectangle() * method call is made on the dted. * <p> * Occasionally it is necessary to abort a prepare call. When this happens, * the map will set the cancel bit in the LayerThread, (the thread that is * running the prepare). If this Layer needs to do any cleanups during the * abort, it should do so, but return out of the prepare asap. * */ public synchronized OMGraphicList prepare() { Projection projection = getProjection(); if (projection == null) { Debug.error("DTED Layer needs to be added to the MapBean before it can draw images!"); return new OMGraphicList(); } DTEDCacheManager cache = getCache(); if (!(projection instanceof EqualArc)) { //fireRequestInfoLine("DTED works faster with an Equal Arc projection (CADRG/LLXY)."); } Debug.message("basic", getName() + "|DTEDLayer.prepare(): doing it"); // Setting the OMGraphicsList for this layer. Remember, the // OMGraphicList is made up of OMGraphics, which are generated // (projected) when the graphics are added to the list. So, // after this call, the list is ready for painting. // call getRectangle(); if (Debug.debugging("dted")) { Debug.output(getName() + "|DTEDLayer.prepare(): " + "calling getRectangle " + " with projection: " + projection + " ul = " + projection.getUpperLeft() + " lr = " + projection.getLowerRight()); } OMGraphicList omGraphicList; if (projection.getScale() < maxScale) { omGraphicList = cache.getRectangle(projection); } else { fireRequestInfoLine(" The scale is too small for DTED viewing."); Debug.error("DTEDLayer: scale (1:" + projection.getScale() + ") is smaller than minimum (1:" + maxScale + ") allowed."); omGraphicList = new OMGraphicList(); } // /////////////////// // safe quit int size = 0; if (omGraphicList != null) { size = omGraphicList.size(); Debug.message("basic", getName() + "|DTEDLayer.prepare(): finished with " + size + " graphics"); // // Don't forget to project them. Since they are only // // being recalled if the projection has changed, then we // // need to force a reprojection of all of them because the // // screen position has changed. // omGraphicList.project(projection, true); } else { Debug.message("basic", getName() + "|DTEDLayer.prepare(): finished with null graphics list"); } return omGraphicList; } /** * Paints the layer. * * @param g the Graphics context for painting */ public void paint(java.awt.Graphics g) { Debug.message("dted", getName() + "|DTEDLayer.paint()"); super.paint(g); if (location != null) location.render(g); location = null; } /** * Get the view type set for creating images. * <P> * * <pre> * * * * * 0: DTEDFrameSubframe.NOSHADING * 1: DTEDFrameSubframe.SLOPESHADING * 2: DTEDFrameSubframe.COLOREDSHADING * 3: DTEDFrameSubframe.METERSHADING * 4: DTEDFrameSubframe.FEETSHADING * * * * * </pre> */ public int getViewType() { return viewType; } public void setViewType(int vt) { switch (vt) { case DTEDFrameSubframe.NOSHADING: case DTEDFrameSubframe.SLOPESHADING: case DTEDFrameSubframe.COLOREDSHADING: case DTEDFrameSubframe.METERSHADING: case DTEDFrameSubframe.FEETSHADING: viewType = vt; if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.viewType = viewType; } break; default: // unchanged } } /** * Get the value for the interval between band colors for meter and feet * shading view types. */ public int getBandHeight() { return bandHeight; } public void setBandHeight(int bh) { bandHeight = bh; if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.bandHeight = bandHeight; } } /** * Get the value for contrast adjustments, 1-5. */ public int getSlopeAdjust() { return slopeAdjust; } public void setSlopeAdjust(int sa) { if (sa > 0 && sa <= 5) { slopeAdjust = sa; if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.slopeAdjust = slopeAdjust; } } else { Debug.output("DTEDLayer (" + getName() + ") being told to set slope adjustment to invalid value (" + sa + "), must be 1-5"); } } /** * Get the value set for which DTED level is being used, 0-2. */ public int getDtedLevel() { return dtedLevel; } public void setDtedLevel(int level) { dtedLevel = level; } /** * Get the opaqueness value used for the images, 0-255. */ public int getOpaqueness() { return opaqueness; } public void setOpaqueness(int o) { if (o >= 0) { opaqueness = o; if (cache != null) { cache.setOpaqueness(opaqueness); } } } /** * Get whether the cache will be killed when the layer is removed from the * map. */ public boolean getKillCache() { return killCache; } public void setKillCache(boolean kc) { killCache = kc; } /** * Get the cache size, or how many DTED frames are held in memory. */ public int getCacheSize() { return cacheSize; } public void setCacheSize(int cs) { if (cs > 0) { cacheSize = cs; } else { Debug.output("DTEDLayer (" + getName() + ") being told to set cache size to invalid value (" + cs + ")."); } } /** * Get the number of colors used in the DTED images. */ public int getNumColors() { return numColors; } public void setNumColors(int nc) { if (nc > 0) { numColors = nc; if (cache != null) { cache.setNumColors(numColors); } } } // ---------------------------------------------------------------------- // GUI // ---------------------------------------------------------------------- /** The user interface palette for the DTED layer. */ protected Box paletteBox = null; /** Creates the interface palette. */ public java.awt.Component getGUI() { if (paletteBox == null) { if (Debug.debugging("dted")) Debug.output("DTEDLayer: creating DTED Palette."); paletteBox = Box.createVerticalBox(); Box subbox1 = Box.createHorizontalBox(); Box subbox2 = Box.createVerticalBox(); Box subbox3 = Box.createHorizontalBox(); // palette = new JPanel(); // palette.setLayout(new GridLayout(0, 1)); // The DTED Level selector JPanel levelPanel = PaletteHelper.createPaletteJPanel("DTED Level"); ButtonGroup levels = new ButtonGroup(); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { if (cache != null) { String ac = e.getActionCommand(); int newLevel; if (ac.equalsIgnoreCase(level2Command)) newLevel = DTEDFrameSubframe.LEVEL_2; else if (ac.equalsIgnoreCase(level1Command)) newLevel = DTEDFrameSubframe.LEVEL_1; else newLevel = DTEDFrameSubframe.LEVEL_0; DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.dtedLevel = newLevel; // cache.setSubframeInfo(dfsi); } } }; JRadioButton level0 = new JRadioButton("Level 0"); level0.addActionListener(al); level0.setActionCommand(level0Command); JRadioButton level1 = new JRadioButton("Level 1"); level1.addActionListener(al); level1.setActionCommand(level1Command); JRadioButton level2 = new JRadioButton("Level 2"); level2.addActionListener(al); level2.setActionCommand(level2Command); levels.add(level0); levels.add(level1); levels.add(level2); switch (dtedLevel) { case 2: level2.setSelected(true); break; case 1: level1.setSelected(true); break; case 0: default: level0.setSelected(true); } levelPanel.add(level0); levelPanel.add(level1); levelPanel.add(level2); // The DTED view selector JPanel viewPanel = PaletteHelper.createPaletteJPanel("View Type"); String[] viewStrings = { "None", "Shading", "Elevation Shading", "Elevation Bands (Meters)", "Elevation Bands (Feet)" }; JComboBox viewList = new JComboBox(viewStrings); viewList.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JComboBox jcb = (JComboBox) e.getSource(); int newView = jcb.getSelectedIndex(); switch (newView) { case 0: viewType = DTEDFrameSubframe.NOSHADING; break; case 1: viewType = DTEDFrameSubframe.SLOPESHADING; break; case 2: viewType = DTEDFrameSubframe.COLOREDSHADING; break; case 3: viewType = DTEDFrameSubframe.METERSHADING; break; case 4: viewType = DTEDFrameSubframe.FEETSHADING; break; default: viewType = DTEDFrameSubframe.NOSHADING; } if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.viewType = viewType; // cache.setSubframeInfo(dfsi); } } }); int selectedView; switch (viewType) { case 0: case 1: selectedView = viewType; break; case 2: case 3: selectedView = viewType + 1; break; case 4: // This puts the layer in testing mode, and the menu // changes. String[] viewStrings2 = { "None", "Shading", "Elevation Bands (Meters)", "Elevation Bands (Feet)", "Subframe Testing", "Elevation Shading" }; viewList = new JComboBox(viewStrings2); viewList.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JComboBox jcb = (JComboBox) e.getSource(); int newView = jcb.getSelectedIndex(); if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.viewType = newView; // cache.setSubframeInfo(dfsi); } } }); selectedView = viewType; break; case 5: selectedView = 2; // DTEDFrameSubframe.COLOREDSHADING break; default: selectedView = DTEDFrameSubframe.NOSHADING; } viewList.setSelectedIndex(selectedView); viewPanel.add(viewList); // The DTED Contrast Adjuster JPanel contrastPanel = PaletteHelper.createPaletteJPanel("Contrast Adjustment"); JSlider contrastSlide = new JSlider(JSlider.HORIZONTAL, 1/* min */, 5/* max */, slopeAdjust/* initial */); java.util.Hashtable<Integer, JLabel> dict = new java.util.Hashtable<Integer, JLabel>(); dict.put(new Integer(1), new JLabel("min")); dict.put(new Integer(5), new JLabel("max")); contrastSlide.setLabelTable(dict); contrastSlide.setPaintLabels(true); contrastSlide.setMajorTickSpacing(1); contrastSlide.setPaintTicks(true); contrastSlide.setSnapToTicks(true); contrastSlide.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent ce) { JSlider slider = (JSlider) ce.getSource(); if (slider.getValueIsAdjusting()) { fireRequestInfoLine(getName() + " - Contrast Slider value = " + slider.getValue()); slopeAdjust = slider.getValue(); if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.slopeAdjust = slopeAdjust; // cache.setSubframeInfo(dfsi); } } } }); contrastPanel.add(contrastSlide); // The DTED Band Height Adjuster JPanel bandPanel = PaletteHelper.createPaletteJPanel("Band Elevation Spacing"); JSlider bandSlide = new JSlider(JSlider.HORIZONTAL, 0/* min */, 1000/* max */, bandHeight/* initial */); bandSlide.setLabelTable(bandSlide.createStandardLabels(250)); bandSlide.setPaintLabels(true); bandSlide.setMajorTickSpacing(250); bandSlide.setMinorTickSpacing(50); bandSlide.setPaintTicks(true); bandSlide.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent ce) { JSlider slider = (JSlider) ce.getSource(); if (slider.getValueIsAdjusting()) { fireRequestInfoLine(getName() + " - Band Slider value = " + slider.getValue()); bandHeight = slider.getValue(); if (cache != null) { DTEDFrameSubframeInfo dfsi = cache.getSubframeInfo(); dfsi.bandHeight = bandHeight; // cache.setSubframeInfo(dfsi); } } } }); bandPanel.add(bandSlide); JButton redraw = new JButton("Redraw DTED Layer"); redraw.setActionCommand(RedrawCmd); redraw.addActionListener(this); subbox1.add(levelPanel); subbox1.add(viewPanel); paletteBox.add(subbox1); subbox2.add(contrastPanel); subbox2.add(bandPanel); paletteBox.add(subbox2); subbox3.add(redraw); paletteBox.add(subbox3); } return paletteBox; } /** * Overridden to modify the MapMouseInterpreter used by the layer. */ public synchronized MapMouseInterpreter getMouseEventInterpreter() { if (getMouseModeIDsForEvents() != null && mouseEventInterpreter == null) { setMouseEventInterpreter(new StandardMapMouseInterpreter(this) { public boolean leftClick(MouseEvent me) { super.leftClick(me); determineLocation(me); return true; } public boolean leftClick(OMGraphic omg, MouseEvent me) { super.leftClick(omg, me); determineLocation(me); return true; } }); } return mouseEventInterpreter; } public boolean determineLocation(MouseEvent e) { Projection projection = getProjection(); if (cache != null && projection != null) { LatLonPoint ll = projection.inverse(e.getX(), e.getY(), new LatLonPoint.Double()); location = new DTEDLocation(e.getX(), e.getY()); location.setElevation(cache.getElevation((float) ll.getY(), (float) ll.getX())); location.generate(projection); repaint(); return true; } return false; } /** * Don't need DTEDFrames highlighting themselves. */ public boolean isHighlightable(OMGraphic omg) { return false; } }