/* ********************************************************************** * * BBNT Solutions LLC, A part of GTE * 10 Moulton St. * Cambridge, MA 02138 * (617) 873-2000 * * Copyright (C) 1998-2000 * This software is subject to copyright protection under the laws of * the United States and other countries. * * ********************************************************************** * * $Source: /net/bitburg/u4/distapps/rcs/submissions/layerEnabledNotification/VPFLayer.java,v $ * $Revision: 1.1 $ * $Date: 2000/12/22 01:51:12 $ * $Author: dietrick $ * * ********************************************************************** */ package com.bbn.openmap.layer.vpf; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.Serializable; import java.util.Hashtable; import java.util.StringTokenizer; import javax.swing.*; import com.bbn.openmap.LatLonPoint; import com.bbn.openmap.Layer; import com.bbn.openmap.event.*; import com.bbn.openmap.layer.util.DrawingAttributes; import com.bbn.openmap.layer.util.LayerUtils; import com.bbn.openmap.omGraphics.*; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.Debug; import com.bbn.openmap.util.PaletteHelper; import com.bbn.openmap.util.SwingWorker; /** * Implement an OpenMap Layer for display of NIMA data sources in the * VPF (Mil-Std 2407) format.
* The properties needed to configure this layer to display VPF data include * some "magic" strings specific to the VPF database you are trying to * display. {@link com.bbn.openmap.layer.vpf.DescribeDB DescribeDB} is * a utility to help you figure out what those strings are. *
*#----------------------------- *# Properties for a VMAP political layer *#----------------------------- *# Mandatory properties *# Mandatory for all layers *vmapPol.class= com.bbn.openmap.layer.vpf.VPFLayer *vmapPol.prettyName= Political Boundaries from VMAP *#Mandatory for VPF - a ';' separated list of paths, each of these *#directories should have a "lat" or "lat." file in them. *vmapPol.vpfPath= e:/VMAPLV0 *# *# Choose either .defaultLayer or .coverageType *# *# .defaultLayer results in the layer looking up the remainder of the *# properties in the defaultVPFLayers.properties files. *vmapPol.defaultLayer= vmapPolitical *# *# .coverageType continues in this property file - chose the VMAP bnd *# (Boundary) coverage *vmapPol.coverageType= bnd *# Select if we want edges (polylines), areas (filled polygons) or text *# This is a space-separated list of "edge" "area" and "text" *vmapPol.featureTypes= edge area *#For DCW, the remaining 3 properties are ignored *#Select the text featureclasses we'd like to display. Since we didn't *#select text above, this is ignored *vmapPol.text= *#Select the edge featureclasses we'd like to display. In this case, *#draw political boundaries and coastline, but skip anything else. *vmapPol.edge=polbndl coastl *#Select the area featureclasses we'd like to display. In this case, *#draw politcal areas, but skip anything else. *vmapPol.area=polbnda *#Selectable drawing attributes - for default values, they don't need to *#be included. A hex ARGB color looks like FF000000 for black. *vmapPol.lineColor=hex ARGB color value - Black is default. *vmapPol.fillColor=hex ARGB color value - Clear is default. *vmapPol.lineWidth=float value, 1f is the default, 10f is the max. //RMS>> * # Minumum scale to display images. Larger numbers mean smaller scale, * # and are more zoomed out. *vmapPol.min.scale=20000000 //RMS<< * *#------------------------------------ *# End of properties for a VMAP political layer *#------------------------------------ * *### Now a VMAP Coastline layer *vmapCoast.class=com.bbn.openmap.layer.vpf.VPFLayer *vmapCoast.prettyName=VMAP Coastline Layer *vmapCoast.vpfPath=/u5/vmap/vmaplv0 *## a predefined layer from the VPF predefined layer set found in *## com/bbn/openmap/layer/vpf/defaultVPFLayers.properties *vmapCoast.defaultLayer=vmapCoastline * *### Now a DCW Politcal layer *# Basic political boundaries with DCW *dcwPolitical.class=com.bbn.openmap.leyer.vpf.VPFLayer *dcwPolitical.prettyName=DCW Political Boundaries *dcwPolitical.vpfPath=path to data *dcwPolitical.coverageType=po *dcwPolitical.featureTypes=edge area **/ public class VPFLayer extends Layer implements ProjectionListener, ActionListener, ComponentListener, Serializable { /** property extension used to set the VPF root directory */ public static final String pathProperty = ".vpfPath"; /** property extension used to set the desired coverage type. e.g. po, hyd */ public static final String coverageTypeProperty = ".coverageType"; /** property extension used to set the desired feature types. * e.g. line area text */ public static final String featureTypesProperty = ".featureTypes"; /** property extension used to specify a default property set */ public static final String defaultLayerProperty = ".defaultLayer"; //RMS>> /** Property extension used to specify a minimum scale to display the data contained in this layer. The data is displayed if the current projections scale is equal to or less than the minScale for this layer. */ public static final String minScaleProperty = "min.scale"; protected long minScale = 20000000; //RMS<< /** command for the palette redraw button. */ public static final String redrawCommand = "redraw"; /** the object that knows all the nitty-gritty vpf stuff */ transient LibrarySelectionTable lst; /** some graphics */ transient OMGraphicList graphics; /** some more graphics */ transient OMGraphicList omglist; /** our own little graphics factory */ private transient LayerGraphicWarehouseSupport warehouse; /** the worker thread we spin off to collect the data */ private transient DcwWorker currentWorker = null; /** used to communicate with our worker thread*/ protected transient boolean cancelled = false; /** * If you need it, use the accessor! * @see #setProjection * @see #getProjection */ private transient Projection projection; /** the path to the root VPF directory */ protected String[] dataPaths = null; /** the coverage type that we display */ protected String coverageType = "po"; /** * Construct a VPF layer. */ public VPFLayer () { addComponentListener(this); } /** * Construct a VPFLayer, and sets its name. * @param name the name of the layer. */ public VPFLayer (String name) { this(); setName(name); } /** * Sets the arguments.
* * @param args String arguments. * * This overrides the same call in Layer. We parse the argument list * looking for: DCWType DCWPath Area/Edges
* */ public void setArgs(String args) { // @HACK: not actually dynamic since they're coming from the OT. dynamicArgs = args; StringTokenizer t = new StringTokenizer(dynamicArgs); if (!t.hasMoreTokens()) return; setDataTypes(t.nextToken()); if (!t.hasMoreTokens()) return; setPath(t.nextToken()); if (!t.hasMoreTokens()) return; setAreasEnabled(t.nextToken().equalsIgnoreCase(VPFUtil.Area)); } /** * Sets the arguments.
* * @param args String arguments. * * This overrides the same call in Layer. We parse the argument list * looking for: DCWType DCWPath Area/Edges
*
*/
public void setArgs(String[] argv) {
Debug.output("VPFLayer.setArgs(argv)");
Debug.output("Setting path to: " + argv[0]);
setPath(argv[0]);
}
/**
* Sets the features (lines, areas, text, points) that get displayed.
* @param features a whitespace-separated list of features to display.
*/
public void setFeatures(String features) {
warehouse.setFeatures(features);
}
/**
* Another way to set the parameters of the DcwLayer.
* @see #pathProperty
* @see #coverageTypeProperty
* @see #featureTypesProperty
*/
public void setProperties(String prefix, java.util.Properties props) {
super.setProperties(prefix, props);
String path[] = LayerUtils.initPathsFromProperties(props,
prefix
+ pathProperty);
if (path != null) {
setPath(path);
}
//RMS>> Code moved from DTEDLayer to set minScale
String realPrefix;
if (prefix != null){
realPrefix = prefix + ".";
} else {
realPrefix = "";
}
minScale = (long) LayerUtils.intFromProperties(props,
realPrefix + minScaleProperty,
20000000);
if (minScale < 100){
minScale = 20000000;
Debug.error("VPFLayer: min.scale unreasonable, setting to 20M");
}
//RMS<<
String defaultProperty = props.getProperty(prefix
+ defaultLayerProperty);
if (defaultProperty != null) {
prefix = defaultProperty;
props = getDefaultProperties();
}
String coverage = props.getProperty(prefix + coverageTypeProperty);
if (coverage != null) {
setDataTypes(coverage);
}
try {
initLST();
if (lst.getDatabaseName().equals("DCW")) {
warehouse = new VPFLayerDCWWarehouse();
} else {
warehouse = new VPFLayerGraphicWarehouse();
}
warehouse.setProperties(prefix, props);
} catch (IllegalArgumentException iae){
Debug.error("VPFLayer.setProperties: Illegal Argument Exception.\n\nPerhaps a file not found. Check to make sure that the paths to the VPF data directories are the parents of \"lat\" or \"lat.\" files. \n\n" + iae);
}
}
/** Where we store our default properties once we've loaded them. */
private static java.util.Properties defaultProps;
/**
* Return our default properties for vpf land.
*/
public static java.util.Properties getDefaultProperties() {
if (defaultProps == null) {
try {
InputStream in = VPFLayer.class.getResourceAsStream("defaultVPFlayers.properties");
//use a temporary so other threads won't see an
//empty properties file
java.util.Properties tmp = new java.util.Properties();
if (in != null) {
tmp.load(in);
in.close();
} else {
Debug.error("VPFLayer: can't load default properties file");
//just use an empty properties file
}
defaultProps = tmp;
} catch (IOException io) {
Debug.error("VPFLayer: can't load default properties: "
+ io);
defaultProps = new java.util.Properties();
}
}
return defaultProps;
}
/**
* Set the projection we're using.
* @param p the projection.
*/
protected synchronized void setProjection(Projection p) {
projection = p;
}
/**
* Get the current projection.
*/
protected synchronized Projection getProjection() {
return projection;
}
/**
* Set the data path to a single place.
*/
public void setPath(String newPath) {
dataPaths = new String[]{newPath};
}
/**
* Set the data path to multiple places.
*/
public void setPath(String[] newPaths) {
dataPaths = newPaths;
}
/**
* Returns the list of paths we use to look for data.
* @return the list of paths. Don't modify the array!
*/
public String[] getPath() {
return dataPaths;
}
/**
* Set the coveragetype of the layer.
* @param dataTypes the coveragetype to display.
*/
public void setDataTypes (String dataTypes) {
coverageType = dataTypes;
}
/**
* Get the current coverage type.
* @return the current coverage type.
*/
public String getDataTypes () {
return coverageType;
}
/**
* Enable/Disable the display of areas.
*/
public void setAreasEnabled (boolean value) {
warehouse.setAreaFeatures(value);
}
/**
* Find out if areas are enabled.
*/
public boolean getAreasEnabled () {
return warehouse.drawAreaFeatures();
}
/**
* Enable/Disable the display of edges.
*/
public void setEdgesEnabled (boolean value) {
warehouse.setEdgeFeatures(value);
}
/**
* Find out if edges are enabled.
*/
public boolean getEdgesEnabled () {
return warehouse.drawEdgeFeatures();
}
/**
* Enable/Disable the display of text.
*/
public void setTextEnabled (boolean value) {
warehouse.setTextFeatures(value);
}
/**
* Find out if text is enabled.
*/
public boolean getTextEnabled () {
return warehouse.drawTextFeatures();
}
/**
* Get the DrawingAttributes used for the coverage type.
*/
public DrawingAttributes getDrawingAttributes(){
return warehouse.getDrawingAttributes();
}
/**
* Set the drawing attributes for the coverage type.
*/
public void setDrawingAttributes(DrawingAttributes da){
warehouse.setDrawingAttributes(da);
}
/**
* Should only be called by Swing to render the layer.
*/
public void paint (Graphics g) {
if (graphics == null) return;
graphics.render(g);
}
/**
* Start fetching graphics to render. This will invoke a
* swing repaint when its completed.
*/
protected synchronized void computeLayer() {
if (getProjection() == null) return;
//RMS>>
// Only display this layers data when zoomed in more than
// the minimum scale set in the properties file
if ( getProjection().getScale() > minScale )
{
// Grey out the Layers menu item
fireEnabledUpdate(LayerEnabledEvent.LAYER_DISABLED);
return;
}
fireEnabledUpdate(LayerEnabledEvent.LAYER_ENABLED);
//RMS<<
if (isVisible()) {
if (getOMGraphics() != null) {
// If we already have graphics, just slap 'em up there.
repaint();
} else if (currentWorker == null) {
// No graphics. If we're not already computing graphics,
// then compute new graphics.
currentWorker = new DcwWorker();
Debug.message("vpfdetail",
"VPFLayer.computeLayer: Executing...");
currentWorker.execute();
Debug.message("vpfdetail",
"VPFLayer.computeLayer: Done executing.");
} else {
// We're already computing. Set the cancel flag and
// return. The DcwWorker management code will fire
// up a new computation when the current worker stops.
cancelled = true;
}
}
}
/**
* Implementing the ProjectionPainter interface.
*/
public synchronized void renderDataForProjection(Projection proj, java.awt.Graphics g){
if (proj == null){
Debug.error("VPFLayer.renderDataForProjection: null projection!");
return;
} else if (!proj.equals(projection)){
setProjection(proj.makeClone());
setOMGraphics(getRectangle());
}
paint(g);
}
/**
* Handle a new projection.
*/
public void projectionChanged (ProjectionEvent e) {
if (Debug.debugging("basic")) {
Debug.output(getName()+"|DcwLayer: projectionChanged()");
}
Projection oldProjection = getProjection();
Projection newProjection = e.getProjection();
// newProjection.equals(null) ==> false
// so this next call is safe even if oldProjection is null
if (newProjection.equals(oldProjection)) {
repaint();
return;
}
// Clear the layer
setOMGraphics(null);
// Install the new projection
setProjection((Projection) newProjection.makeClone());
// Create new graphics
computeLayer();
}
/**
* Get the graphics from the last fetch.
*/
protected synchronized OMGraphicList getOMGraphics () {
return graphics;
}
/**
* Set the omgraphiclist to get returned.
*/
protected synchronized void setOMGraphics (OMGraphicList aGraphicList) {
graphics = aGraphicList;
}
/**
* Called by the worker thread when it was done. If the current
* worker was cancelled, a new one gets started.
*/
protected synchronized void workerComplete (DcwWorker worker) {
if (!cancelled) {
currentWorker = null;
setOMGraphics((OMGraphicList)worker.get());
repaint();
}
else{
cancelled = false;
currentWorker = new DcwWorker();
currentWorker.execute();
}
}
/**
* A thread class used to fetch VPF data, so that the GUI
* doesn't lock while all the work is done.
*/
class DcwWorker extends SwingWorker {
public DcwWorker () {
super();
}
/**
* Compute the value to be returned by the get method.
*/
public Object construct() {
fireStatusUpdate(LayerStatusEvent.START_WORKING);
try {
return getRectangle();
} catch (OutOfMemoryError e) {
graphics = null;
omglist.clear();
String msg = getName()+"|VPFLayer.DcwWorker.construct(): " + e;
System.err.println(msg);
e.printStackTrace();
fireRequestMessage(
new InfoDisplayEvent(this, msg));
fireStatusUpdate(LayerStatusEvent.FINISH_WORKING);
return null;
}
}
/**
* Called on the event dispatching thread (not on the worker thread)
* after the construct method has returned.
*/
public void finished() {
fireStatusUpdate(LayerStatusEvent.FINISH_WORKING);
workerComplete(this);
}
}
/**
* initialize the library selection table.
*/
private void initLST() {
try {
if (lst == null)
lst = new LibrarySelectionTable(dataPaths);
} catch (com.bbn.openmap.util.FormatException f) {
throw new java.lang.IllegalArgumentException(f.getMessage());
}
}
public OMGraphicList getRectangle() {
if (lst == null) {
try {
initLST();
} catch (IllegalArgumentException iae){
Debug.error("VPFLayer.getRectangle: Illegal Argument Exception.\n\nPerhaps a file not found. Check to make sure that the paths to the VPF data directories are the parents of \"lat\" or \"lat.\" files. \n\n" + iae);
return null;
}
}
if (warehouse == null){
Debug.error("VPFLayer.getRectangle: warehouse not initialized - should be OK on restart...");
return null;
}
Projection p = getProjection();
LatLonPoint upperleft = p.getUpperLeft();
LatLonPoint lowerright = p.getLowerRight();
if (Debug.debugging("vpfdetail")){
Debug.output("VPFLayer.getRectangle: " +
coverageType + " " + dynamicArgs);
}
omglist = null; // free up previous list for gc
warehouse.clear();
// int edgecount[] = new int[] { 0 , 0 };
// int textcount[] = new int[] { 0 , 0 };
// int areacount[] = new int[] { 0 , 0 };
// Check both dynamic args and palette values when
// deciding what to draw.
if (Debug.debugging("vpf")) {
Debug.output("VPFLayer.getRectangle(): "
+ "calling drawTile with boundaries: "
+ upperleft + " " + lowerright);
}
long start = System.currentTimeMillis();
lst.drawTile((int)p.getScale(),
p.getWidth(),
p.getHeight(),
coverageType, warehouse, upperleft, lowerright);
long stop = System.currentTimeMillis();
// if (Debug.debugging("vpfdetail")) {
// Debug.output("Returned " + edgecount[0] +
// " polys with " + edgecount[1] + " points\n" +
// "Returned " + textcount[0] +
// " texts with " + textcount[1] + " points\n" +
// "Returned " + areacount[0] +
// " areas with " + areacount[1] + " points");
// }
if (Debug.debugging("vpf")) {
Debug.output("VPFLayer.getRectangle(): read time: " +
((stop-start)/1000d) + " seconds");
}
omglist = warehouse.getGraphics();
// Don't forget to project.
start = System.currentTimeMillis();
omglist.project(p);
stop = System.currentTimeMillis();
if (Debug.debugging("vpf")) {
Debug.output("VPFLayer.getRectangle(): proj time: "
+ ((stop-start)/1000d) + " seconds");
}
return omglist;
}
private transient Box box;
/**
* Gets the palette associated with the layer.
*
* @return Component or null */ public Component getGUI() { if (box == null) { box = Box.createVerticalBox(); JPanel pal = null; ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { int index = Integer.parseInt( e.getActionCommand(), 10); switch (index) { case 0: warehouse.setEdgeFeatures(!warehouse.drawEdgeFeatures()); break; case 1: warehouse.setTextFeatures(!warehouse.drawTextFeatures()); break; case 2: warehouse.setAreaFeatures(!warehouse.drawAreaFeatures()); break; default: throw new RuntimeException("argh!"); } } }; pal = PaletteHelper.createCheckbox( getName(), new String[] { VPFUtil.Edges, VPFUtil.Text, VPFUtil.Area }, new boolean[] { warehouse.drawEdgeFeatures(), warehouse.drawTextFeatures(), warehouse.drawAreaFeatures() }, al ); box.add(pal); JPanel pal2 = new JPanel(); JButton redraw = new JButton("Redraw Layer"); redraw.setActionCommand(redrawCommand); redraw.addActionListener(this); pal2.add(redraw); box.add(pal2); } return box; } public void actionPerformed (ActionEvent e) { String cmd = e.getActionCommand(); if (cmd == redrawCommand) { setOMGraphics(null); computeLayer(); } } //---------------------------------------------------------------------- // Component Listener implementation //---------------------------------------------------------------------- /** * Empty. */ public void componentResized(ComponentEvent e) {} /** * Empty. */ public void componentMoved(ComponentEvent e) {} /** * When component gets shown, compute the graticule. * * @param e a component event */ public void componentShown(ComponentEvent e) { computeLayer(); } /** * Empty. */ public void componentHidden(ComponentEvent e) {} }