// **********************************************************************
//
// <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/DTEDFrameCacheLayer.java,v $
// $RCSfile: DTEDFrameCacheLayer.java,v $
// $Revision: 1.10 $
// $Date: 2005/12/09 21:09:06 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.dted;
/* Java Core */
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import com.bbn.openmap.dataAccess.dted.DTEDConstants;
import com.bbn.openmap.dataAccess.dted.DTEDDirectoryHandler;
import com.bbn.openmap.dataAccess.dted.DTEDFrameCacheHandler;
import com.bbn.openmap.dataAccess.dted.DTEDNameTranslator;
import com.bbn.openmap.layer.OMGraphicHandlerLayer;
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.omGraphics.grid.GeneratorLoader;
import com.bbn.openmap.omGraphics.grid.SlopeGeneratorLoader;
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 DTEDFrameCacheLayer fills the screen with DTED data. To view the DTED
* iamges, the projection has to be set in an ARC projection, which OpenMap
* calls the CADRG or LLXY projection. In Gesture mode, clicking on the map will
* cause the DTEDFrameCacheLayer 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 DTEDFrameCacheLayer uses the DTEDCacheHandler to get the images it needs.
* The DTEDFrameCacheLayer receives projection change events, and then asks the
* cache handler for the images it needs based on the new projection.
*
* The DTEDFrameCacheLayer 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 DTEDFrameCacheLayer 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.layer.rpf.ChangeCase class to make modifications if
* necessary.
* <P>
*
* <pre>
*
*
* #------------------------------
* # Properties for DTEDFrameCacheLayer
* #------------------------------
*
* # Level of DTED data to use 0, 1, 2
* dted.level=0
*
* # 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
*
* # Need to set GeneratorLoaders for DTED rendering. These properties get
* # forwarded on to the DTEDFrameCacheHandler.
* dted.generators=greys colors
* dted.greys.class=com.bbn.openmap.omGraphics.grid.SlopeGeneratorLoader
* dted.greys.prettyName=Slope Shading
* dted.greys.colorsClass=com.bbn.openmap.omGraphics.grid.GreyscaleSlopeColors
* dted.colors.class=com.bbn.openmap.omGraphics.grid.SlopeGeneratorLoader
* dted.colors.prettyName=Elevation Shading
* dted.colors.colorsClass=com.bbn.openmap.omGraphics.grid.ColoredShadingColors
*
* #-------------------------------------
* # End of properties for DTEDFrameCacheLayer
* #-------------------------------------
*
*
* </pre>
*
* @see com.bbn.openmap.util.wanderer.ChangeCase
*/
public class DTEDFrameCacheLayer extends OMGraphicHandlerLayer implements ActionListener,
Serializable, DTEDConstants {
/** The cache handler. */
protected transient DTEDFrameCacheHandler cache = new DTEDFrameCacheHandler(null);
protected long minScale = 20000000;
/** Flag to delete the cache if the layer is removed from the map. */
protected boolean killCache = true;
public static final String DTEDLevelProperty = "level";
public static final String DTEDMinScaleProperty = "min.scale";
public static final String DTEDKillCacheProperty = "kill.cache";
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 DTEDFrameCacheLayer() {
setMouseModeIDsForEvents(new String[] { "Gestures" });
setProjectionChangePolicy(new com.bbn.openmap.layer.policy.ListResetPCPolicy(this));
}
/**
* The default constructor for the Layer. All of the attributes are set to
* their default values.
*
* @param dfc paths to the DTED directories that hold level 0 and 1 data.
*/
public DTEDFrameCacheLayer(com.bbn.openmap.dataAccess.dted.DTEDFrameCache dfc) {
this();
setFrameCache(dfc);
}
public void setFrameCache(com.bbn.openmap.dataAccess.dted.DTEDFrameCache dfc) {
cache.setFrameCache(dfc);
cache.resetCache();
}
public com.bbn.openmap.dataAccess.dted.DTEDFrameCache getFrameCache() {
if (cache != null) {
return cache.getFrameCache();
} else
return null;
}
public DTEDFrameCacheHandler getCache() {
return cache;
}
public void setCache(DTEDFrameCacheHandler cache) {
this.cache = cache;
}
protected void setDefaultValues() {
// defaults
setMaxScale(20000000);
}
/**
* Set all the DTED properties from a properties object.
*/
public void setProperties(java.util.Properties properties) {
setProperties(null, properties);
}
/**
* 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);
setDtedLevel(PropUtils.intFromProperties(properties, prefix + DTEDLevelProperty, getDtedLevel()));
cache.setProperties(prefix, properties);
}
/**
* Called when the layer is no longer part of the map.
*/
public void removed(java.awt.Container cont) {
OMGraphicList rasters = getList();
if (rasters != null) {
rasters.clear();
}
}
public void findAndInit(Object someObj) {
if (someObj instanceof com.bbn.openmap.dataAccess.dted.DTEDFrameCache) {
setFrameCache((com.bbn.openmap.dataAccess.dted.DTEDFrameCache) someObj);
}
}
public void findAndUndo(Object someObj) {
if (someObj == getFrameCache()) {
setFrameCache(null);
}
}
/**
* A flag to keep track of when the first time a warning was put up if the
* projection isn't EquiArc.
*/
protected boolean firstProjectionWarningSent = false;
/**
* 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() {
if (cache == null) {
Debug.message("dted", getName()
+ "|DTEDFrameCacheLayer can't add anything to map because the DTEDFrameCache has not been set.");
}
Projection projection = getProjection();
if (projection == null) {
Debug.output("DTED Layer needs to be added to the MapBean before it can draw images!");
return new OMGraphicList();
}
// Check to make sure the projection is EqualArc
if (!(projection instanceof EqualArc)) {
if (!firstProjectionWarningSent) {
fireRequestInfoLine(" DTED requires an Equal Arc projection (CADRG/LLXY) to view images.");
Debug.output("DTEDFrameCacheLayer: DTED requires an Equal Arc projection (CADRG/LLXY) to view images.");
firstProjectionWarningSent = true;
}
return new OMGraphicList();
}
Debug.message("basic", getName() + "|DTEDFrameCacheLayer.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() + "|DTEDFrameCacheLayer.prepare(): " + "calling getRectangle "
+ " with projection: " + projection + " ul = " + projection.getUpperLeft()
+ " lr = " + projection.getLowerRight());
}
OMGraphicList omGraphicList;
if (projection.getScale() < maxScale) {
omGraphicList = cache.getRectangle((EqualArc) projection);
} else {
fireRequestInfoLine(" The scale is too small for DTED viewing.");
Debug.error("DTEDFrameCacheLayer: 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() + "|DTEDFrameCacheLayer.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()
+ "|DTEDFrameCacheLayer.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) {
super.paint(g);
if (location != null)
location.render(g);
location = null;
}
/**
* Get the value set for which DTED level is being used, 0-2.
*/
public int getDtedLevel() {
if (cache != null) {
return cache.getDtedLevel();
} else
return LEVEL_0;
}
public void setDtedLevel(int level) {
if (cache != null) {
cache.setDtedLevel(level);
}
}
/**
* 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;
}
// ----------------------------------------------------------------------
// GUI
// ----------------------------------------------------------------------
/** The user interface palette for the DTED layer. */
protected Box paletteBox = null;
/** Creates the interface palette. */
public Component getGUI() {
if (paletteBox == null) {
if (Debug.debugging("dted"))
Debug.output("DTEDFrameCacheLayer: creating DTED Palette.");
paletteBox = Box.createVerticalBox();
Box subbox1 = Box.createHorizontalBox();
Box subbox3 = Box.createHorizontalBox();
// 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 = LEVEL_2;
else if (ac.equalsIgnoreCase(level1Command))
newLevel = LEVEL_1;
else
newLevel = LEVEL_0;
setDtedLevel(newLevel);
}
}
};
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 (getDtedLevel()) {
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 from DTEDFrameCacheHandler
JPanel viewPanel = PaletteHelper.createPaletteJPanel("View Type");
viewPanel.add(cache.getGUI());
JButton redraw = new JButton("Redraw DTED Layer");
redraw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
doPrepare();
}
});
subbox1.add(levelPanel);
subbox1.add(viewPanel);
paletteBox.add(subbox1);
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;
}
/**
* This is the easiest way to construct a DTEDFrameCacheLayer programmatically.
* Create the Builder, configure it, and call create() to configure the layer.
*/
public static class Builder {
List<DTEDDirectoryHandler> dirHandlers;
List<GeneratorLoader> loaders = new ArrayList<GeneratorLoader>();
DTEDNameTranslator nTranslator;
/**
* Create a builder for a DTEDFrameCacheLayer.
* @param dtedDirectory a path to the dted directory.
*/
public Builder(String dtedDirectory) {
this(new DTEDDirectoryHandler(dtedDirectory));
}
/**
* Create a builder for a DTEDFrameCacheLayer.
*
* @param dirHandler don't pass in a null value, things will get ugly.
*/
public Builder(DTEDDirectoryHandler dirHandler) {
dirHandlers = new ArrayList<DTEDDirectoryHandler>();
dirHandlers.add(dirHandler);
}
/**
* If set, this name translator will be added to all directory handlers
* set in this builder. If not called the StandardDTEDNameTranslator
* will be used.
*
* @param translator DTEDNameTranslator.
* @return this builder.
*/
public Builder setNameTranslator(DTEDNameTranslator translator) {
nTranslator = translator;
return this;
}
/**
* Add a generator loader to the DTEDFrameCache to be used by the layer.
* If not called, the SloperGeneratorLoader will be used.
*
* @param gLoader
* @return this Builder.
*/
public Builder addGeneratorLoader(GeneratorLoader gLoader) {
if (loaders == null) {
loaders = new ArrayList<GeneratorLoader>();
}
if (gLoader != null) {
loaders.add(gLoader);
}
return this;
}
/**
* Create the DTEDFrameCacheLayer.
* @return the new layer, configured with Builder settings.
*/
public DTEDFrameCacheLayer create() {
com.bbn.openmap.dataAccess.dted.DTEDFrameCache dfc = new com.bbn.openmap.dataAccess.dted.DTEDFrameCache();
for (DTEDDirectoryHandler dHandler : dirHandlers) {
if (nTranslator != null) {
dHandler.setTranslator(nTranslator);
}
dfc.addDTEDDirectoryHandler(dHandler);
}
DTEDFrameCacheLayer layer = new DTEDFrameCacheLayer(dfc);
com.bbn.openmap.dataAccess.dted.DTEDFrameCacheHandler dfcHandler = layer.getCache();
if (!loaders.isEmpty()) {
dfcHandler.setGeneratorLoaders(loaders);
} else {
dfcHandler.addGeneratorLoader(new SlopeGeneratorLoader());
}
dfcHandler.setActiveGeneratorLoader(dfcHandler.getGeneratorLoaders().get(0).getPrettyName());
return layer;
}
}
}