// ********************************************************************** // // <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/vpf/VPFLayer.java,v $ // $RCSfile: VPFLayer.java,v $ // $Revision: 1.21 $ // $Date: 2006/03/06 16:13:59 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.vpf; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.beancontext.BeanContext; import java.io.Serializable; import java.util.Properties; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JPanel; import com.bbn.openmap.event.ProjectionListener; import com.bbn.openmap.gui.WindowSupport; import com.bbn.openmap.layer.OMGraphicHandlerLayer; import com.bbn.openmap.omGraphics.DrawingAttributes; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMGraphicConstants; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.proj.GeoProj; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.proj.coords.LatLonPoint; import com.bbn.openmap.util.PaletteHelper; import com.bbn.openmap.util.PropUtils; /** * Implement an OpenMap Layer for display of NIMA data sources in the VPF * (Mil-Std 2407) format. * <p> * 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. * * <pre> * * * * #----------------------------- * # 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 - choose .vpfPath or .libraryBean * # .vpfPath specifies a ';' separated list of paths to data, each of these * #directories should have a "lat" or "lat." file in them. * vmapPol.vpfPath= e:/VMAPLV0 * # .libraryBean specifies a separate object in the properties file that * # locates vpf data. You should use this option if you have multiple VPF * # layers displaying the same VPF database. (For example, you have 3 VMAP * # layers, displaying coastlines, railroads and rivers. Each layer would * # then specify the same libraryBean name. This reduces the memory * # consumption of the VPF layers.) * # See {@link com.bbn.openmap.layer.vpf.LibraryBean LibraryBean javadoc} for properties info. (Example below) * vmapPol.libraryBean= VMAPdata * VMAPData.class=com.bbn.openmap.layer.vpf.LibraryBean * VMAPData.vpfPath=e:/VMAPLV0 * # Don't forget to add VMAPData to the openmap.components property list, too. * # * #Optional (default is true) changes how features are located. Should * #use this option for multiple coverage types, or when null pointer errors * #are encountered. * vmapPol.searchByFeature=true * * #Limit which VPF library is used (optional). If not specified, * #all available libraries will be checked and used. * vmapPol.libraryName=noamer * # * # 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" "text" "epoint" and "cpoint" * 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. * * # The column name in the feature table that should be displayed. The easiest * # way to find out what these columns should be for an application is to run the * # application with the other properties set for the layer, and then bring up * # the layer's palette. When the feature is selected, there are additional * # controls that let you display the coverage column names and display type, * # and the coverage column names in the popup are the ones that can be used * # in the properties (the column names are in parenthesis). NOTE: The features * # have to be added to the map and visible (proper scale setting < 30,000,000) * # in order for the palette controls to be visible. * vmapPol.attribute=na2 * # How the attributes should be displayed, either 'label', 'tooltip', or 'information line' * vmapPol.attributeDisplay=label * * #------------------------------------ * # 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 Political layer * # Basic political boundaries with DCW * dcwPolitical.class=com.bbn.openmap.layer.vpf.VPFLayer * dcwPolitical.prettyName=DCW Political Boundaries * dcwPolitical.vpfPath=path to data * dcwPolitical.coverageType=po * dcwPolitical.featureTypes=edge area * * * * </pre> */ public class VPFLayer extends OMGraphicHandlerLayer implements ProjectionListener, ActionListener, Serializable { private static final long serialVersionUID = 1L; public static Logger logger = Logger.getLogger("com.bbn.openmap.layer.vpf.VPFLayer"); /** property extension used to set the VPF root directory */ public static final String pathProperty = "vpfPath"; /** * property extension used to set the desired coverage type. Examples of * coverage types include "po" for DCW and "hyd" for VMAP Level 0. */ 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"; /** * Property that lets you search for graphics via feature type. Dangerously * slow for features that have many graphics spread out over several tiles. * Set to true to search by feature, false (default) to get the tiles first, * and then look for graphics. */ public static final String searchByFeatureProperty = "searchByFeature"; /** Property method for setting VPF data path */ public static final String libraryProperty = "libraryBean"; /** Property for setting VPF cutoff scale */ public static final String cutoffScaleProperty = "cutoffScale"; /** Property for setting VPF library name to use */ public static final String LibraryNameProperty = "libraryName"; /** the object that knows all the nitty-gritty vpf stuff */ protected transient LibrarySelectionTable lst; /** our own little graphics factory */ protected transient LayerGraphicWarehouseSupport warehouse; /** are we searching by feature table (true) or tile (false) */ protected boolean searchByFeatures = false; /** the name of the data bean to look for in beancontext */ protected String libraryBeanName = null; /** * hang onto prefix used to initialize warehouse in setProperties() */ protected String prefix; /** hang onto properties file used to initialize warehouse */ protected Properties props; /** the path to the root VPF directory */ protected String[] dataPaths = null; /** the coverage type that we display */ protected String coverageType = "po"; protected int cutoffScale = LibrarySelectionTable.DEFAULT_BROWSE_CUTOFF; /** the library name to focus on */ protected String libraryName = null; /** * Construct a VPF layer. */ public VPFLayer() { setProjectionChangePolicy(new com.bbn.openmap.layer.policy.ListResetPCPolicy(this)); setMouseModeIDsForEvents(new String[] { "Gestures" }); } /** * Construct a VPFLayer, and sets its name. * * @param name the name of the layer. */ public VPFLayer(String name) { this(); setName(name); } /** * 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, Properties props) { super.setProperties(prefix, props); setAddToBeanContext(true); String realPrefix = PropUtils.getScopedPropertyPrefix(prefix); cutoffScale = PropUtils.intFromProperties(props, realPrefix + cutoffScaleProperty, cutoffScale); libraryBeanName = props.getProperty(realPrefix + libraryProperty, libraryBeanName); libraryName = props.getProperty(realPrefix + LibraryNameProperty, libraryName); String path[] = PropUtils.initPathsFromProperties(props, realPrefix + pathProperty); if (path != null && path.length != 0) { setPath(path); } String defaultProperty = props.getProperty(realPrefix + defaultLayerProperty); if (defaultProperty != null) { prefix = defaultProperty; props = VPFUtil.getDefaultProperties(); } // need to save these so we can call setProperties on the // warehouse, // which we probably can't construct yet this.prefix = prefix; this.props = props; String coverage = props.getProperty(realPrefix + coverageTypeProperty); if (coverage != null) { setDataTypes(coverage); } searchByFeatures = PropUtils.booleanFromProperties(props, realPrefix + searchByFeatureProperty, searchByFeatures); checkWarehouse(searchByFeatures); if (warehouse != null) { warehouse.setProperties(prefix, props); warehouse.setUseLibraries(PropUtils.parseSpacedMarkers(libraryName)); box = null; resetPalette(); } try { // force lst and warehosue to re-init with current // properties // LST now set when paths are set. } catch (IllegalArgumentException iae) { logger.warning("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); } } public Properties getProperties(Properties props) { props = super.getProperties(props); String realPrefix = PropUtils.getScopedPropertyPrefix(this); props.put(realPrefix + cutoffScaleProperty, Integer.toString(cutoffScale)); if (libraryBeanName != null) { props.put(realPrefix + libraryProperty, libraryBeanName); } else { StringBuffer paths = new StringBuffer(); String[] ps = getPath(); for (int i = 0; ps != null && i < ps.length; i++) { paths.append(ps[i]); if (i < ps.length - 1) paths.append(";"); } props.put(realPrefix + pathProperty, paths.toString()); } // For the library in a vpf package props.put(realPrefix + LibraryNameProperty, PropUtils.unnull(libraryName)); props.put(realPrefix + coverageTypeProperty, getDataTypes()); props.put(realPrefix + searchByFeatureProperty, Boolean.toString(searchByFeatures)); if (warehouse != null) { warehouse.getProperties(props); } return props; } /** Where we store our default properties once we've loaded them. */ private Properties defaultProps; /** * Set the data path to a single place. */ public void setPath(String newPath) { logger.fine("setting paths to " + newPath); setPath(new String[] { newPath }); } /** * Set the data path to multiple places. */ public void setPath(String[] newPaths) { dataPaths = newPaths; lst = null; initLST(); } /** * 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 entity points. */ public void setEPointsEnabled(boolean value) { warehouse.setEPointFeatures(value); } /** * Find out if entity points are enabled. */ public boolean getEPointsEnabled() { return warehouse.drawEPointFeatures(); } /** * Enable/Disable the display of connected points. */ public void setCPointsEnabled(boolean value) { warehouse.setCPointFeatures(value); } /** * Find out if connected points are enabled. */ public boolean getCPointsEnabled() { return warehouse.drawCPointFeatures(); } /** * 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); } /** * initialize the library selection table. */ protected void initLST() { logger.fine("initializing Library Selection Table (LST)"); try { if (lst == null) { if (libraryBeanName != null) { LibraryBean libraryBean = null; BeanContext beanContext = getBeanContext(); if (beanContext == null) { // no bean context yet return; } for (Object obj : beanContext) { if (obj instanceof LibraryBean) { LibraryBean lb = (LibraryBean) obj; if (libraryBeanName.equals(lb.getName())) { if (logger.isLoggable(Level.FINE)) { logger.fine(getName() + ": setting library bean to " + lb.getName()); } libraryBean = lb; break; } } } if (libraryBean != null) { lst = libraryBean.getLibrarySelectionTable(); warehouse = libraryBean.getWarehouse(); // Set the warehouse with the properties // received when the layer was created. warehouse.setProperties(getPropertyPrefix(), props); searchByFeatures = true; // because it is. box = null;// force GUI to rebuild logger.fine("VPFLayer.initLST(libraryBean)"); } else { if (logger.isLoggable(Level.FINE)) { // Encasing it in a debug statement, // because we could get here by adding the // LayerHandler to the MapHandler before // the LibraryBean. logger.fine("Couldn't find libraryBean " + libraryBeanName + " to read VPF data"); } } } else { if (dataPaths == null) { logger.info("VPFLayer|" + getName() + ": path not set"); } else { logger.fine("VPFLayer.initLST(dataPaths)"); lst = new LibrarySelectionTable(dataPaths); lst.setCutoffScale(cutoffScale); } } } } catch (com.bbn.openmap.io.FormatException f) { throw new java.lang.IllegalArgumentException(f.getMessage()); // } catch (NullPointerException npe) { // throw new // java.lang.IllegalArgumentException("VPFLayer|" + // getName() + // ": path name not valid"); } } public void setWarehouse(LayerGraphicWarehouseSupport wh) { warehouse = wh; } public LayerGraphicWarehouseSupport getWarehouse() { return warehouse; } /** * If the warehouse gets set as a result of this method being called, the * properties will beed to be reset on it. * * @param sbf Search by features. */ public void checkWarehouse(boolean sbf) { if (warehouse == null) { logger.fine("need to create warehouse"); if (lst != null && lst.getDatabaseName() != null && lst.getDatabaseName().equals("DCW")) { warehouse = new VPFLayerDCWWarehouse(); } else if (sbf) { warehouse = new VPFFeatureGraphicWarehouse(); } else { warehouse = new VPFLayerGraphicWarehouse(); } } else if ((sbf && !(warehouse instanceof VPFFeatureGraphicWarehouse)) || (!sbf && warehouse instanceof VPFFeatureGraphicWarehouse)) { warehouse = null; checkWarehouse(sbf); } } /** * Use doPrepare() method instead. This was the old method call to do the * same thing doPrepare is now doing, from the OMGraphicHandler superclass. * doPrepare() launches a thread to do the work. * * @deprecated use doPrepare() instead of computeLayer(); */ public void computeLayer() { doPrepare(); } /** * Use prepare instead. This was the old method call to do the same thing * prepare() is now doing. * * @deprecated use prepare() instead of getRectangle(); */ public OMGraphicList getRectangle() { return prepare(); } /** * Create the OMGraphicList to use on the map. OMGraphicHandler methods call * this. */ public synchronized OMGraphicList prepare() { if (lst == null) { try { initLST(); } catch (IllegalArgumentException iae) { logger.warning("VPFLayer.prepare: 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 (lst == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("VPFLayer| " + getName() + " prepare(), Library Selection Table not set."); } return null; } } if (warehouse == null) { StringBuffer dpb = new StringBuffer(); if (dataPaths != null) { for (int num = 0; num < dataPaths.length; num++) { if (num > 0) { dpb.append(":"); } dpb.append(dataPaths[num]); } } logger.warning("VPFLayer.getRectangle: Data path probably wasn't set correctly (" + dpb.toString() + "). The warehouse not initialized."); return null; } Projection p = getProjection(); if (p == null || !(p instanceof GeoProj)) { if (logger.isLoggable(Level.FINE)) { logger.fine("VPFLayer.getRectangle() called with a projection (" + p + ") set in the layer, which isn't being handled."); } return new OMGraphicList(); } LatLonPoint upperleft = (LatLonPoint) p.getUpperLeft(); LatLonPoint lowerright = (LatLonPoint) p.getLowerRight(); if (logger.isLoggable(Level.FINER)) { logger.finer("VPFLayer.getRectangle: " + coverageType /* * + " " + * dynamicArgs */); } 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 (logger.isLoggable(Level.FINE)) { logger.fine("calling draw with boundaries: " + upperleft + " " + lowerright); } long start = System.currentTimeMillis(); StringTokenizer t = new StringTokenizer(coverageType); while (t.hasMoreTokens()) { String currentCoverage = t.nextToken(); if (searchByFeatures) { lst.drawFeatures((int) p.getScale(), p.getWidth(), p.getHeight(), currentCoverage, (VPFFeatureWarehouse) warehouse, upperleft, lowerright); } else { lst.drawTile((int) p.getScale(), p.getWidth(), p.getHeight(), currentCoverage, 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 (logger.isLoggable(Level.FINE)) { logger.fine("read time: " + ((stop - start) / 1000d) + " seconds"); } OMGraphicList omglist = warehouse.getGraphics(); // Don't forget to project. start = System.currentTimeMillis(); omglist.project(p); stop = System.currentTimeMillis(); if (logger.isLoggable(Level.FINE)) { logger.fine("proj time: " + ((stop - start) / 1000d) + " seconds"); } return omglist; } private transient JPanel box; /** * Gets the palette associated with the layer. * * @return Component or null */ public Component getGUI() { if (lst == null) { try { initLST(); } catch (IllegalArgumentException iie) { logger.warning(iie.getMessage()); } } if (warehouse == null) { return (new javax.swing.JLabel("VPF Layer data not loaded properly.")); } if (box == null) { box = new JPanel(); box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); box.setAlignmentX(Component.LEFT_ALIGNMENT); JPanel stuff = new JPanel(); 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; case 3: warehouse.setEPointFeatures(!warehouse.drawEPointFeatures()); break; case 4: warehouse.setCPointFeatures(!warehouse.drawCPointFeatures()); break; default: throw new RuntimeException("argh!"); } } }; pal = PaletteHelper.createCheckbox("Show:", new String[] { VPFUtil.Edges, VPFUtil.Text, VPFUtil.Area, VPFUtil.EPoint, VPFUtil.CPoint }, new boolean[] { warehouse.drawEdgeFeatures(), warehouse.drawTextFeatures(), warehouse.drawAreaFeatures(), warehouse.drawEPointFeatures(), warehouse.drawCPointFeatures() }, al); stuff.add(pal); if (lst != null) { Component warehouseGUI = warehouse.getGUI(lst); if (warehouseGUI != null) { stuff.add(warehouseGUI); } } box.add(stuff); JPanel pal2 = new JPanel(); JButton config = new JButton("Configure Layer"); config.setActionCommand(ConfigCmd); config.addActionListener(this); pal2.add(config); JButton redraw = new JButton("Redraw Layer"); redraw.setActionCommand(RedrawCmd); redraw.addActionListener(this); pal2.add(redraw); box.add(pal2); } return box; } public final static String ConfigCmd = "CONFIGURE"; protected WindowSupport configWindowSupport = null; public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd == RedrawCmd) { setList(null); if (isVisible()) { doPrepare(); } } else if (cmd == ConfigCmd) { if (configWindowSupport == null) { configWindowSupport = new WindowSupport(new VPFConfig(this), "Configure " + getName() + " Features"); } else { configWindowSupport.setTitle(getName()); } configWindowSupport.displayInWindow(); } else { super.actionPerformed(e); } } protected void setConfigSettings(String prefix, Properties props) { lst = null; setProperties(prefix, props); if (isVisible()) { doPrepare(); } if (configWindowSupport != null) { configWindowSupport.setTitle(getName()); configWindowSupport.killWindow(); } } public String getToolTipTextFor(OMGraphic omg) { return (String) omg.getAttribute(OMGraphicConstants.TOOLTIP); } public String getInfoText(OMGraphic omg) { return (String) omg.getAttribute(OMGraphicConstants.INFOLINE); } }