Package com.bbn.openmap.dataAccess.dted

Source Code of com.bbn.openmap.dataAccess.dted.DTEDFrameCacheHandler

// **********************************************************************
//
// <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/dataAccess/dted/DTEDFrameCacheHandler.java,v $
// $RCSfile: DTEDFrameCacheHandler.java,v $
// $Revision: 1.2.2.3 $
// $Date: 2006/01/13 22:04:20 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.dataAccess.dted;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.PropertyConsumer;
import com.bbn.openmap.layer.util.cacheHandler.CacheHandler;
import com.bbn.openmap.layer.util.cacheHandler.CacheObject;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMGrid;
import com.bbn.openmap.omGraphics.grid.GeneratorLoader;
import com.bbn.openmap.omGraphics.grid.OMGridGenerator;
import com.bbn.openmap.omGraphics.grid.SinkGenerator;
import com.bbn.openmap.proj.EqualArc;
import com.bbn.openmap.util.ComponentFactory;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;

/**
* The DTEDFrameCacheHandler is a cache for objects being rendered on
* the map as a result of reading in DTED data. It communicates with a
* DTEDFrameCache to get OMGrid data from the actual DTED data files,
* and then sets OMGridGenerators on those OMGrids to create
* representations of the DTED.
* <P>
*
* The DTEDFrameCacheHandler uses GeneratorLoaders to create
* OMGridGenerators for its OMGrids. The GeneratorLoaders provide a
* GUI for controlling those OMGridGenerator parameters. The list of
* GeneratorLoaders can be set via Properties. In general, properties
* being set for a DTEDFrameCacheHandler:
* <P>
*
* <pre>
*  
*  
*    markerName.generators=greys colors
*    markerName.greys.class=com.bbn.openmap.omGraphics.grid.SlopeGeneratorLoader
*    markerName.greys.prettyName=Slope Shading
*    markerName.greys.colorsClass=com.bbn.openmap.omGraphics.grid.GreyscaleSlopeColors
*    markerName.colors.class=com.bbn.openmap.omGraphics.grid.SlopeGeneratorLoader
*    markerName.colors.prettyName=Elevation Shading
*    markerName.colors.colorsClass=com.bbn.openmap.omGraphics.grid.ColoredShadingColors
*  
*   
* </pre>
*
* The only properties that are required for the DTEDFrameCacheHandler
* are the generators property, and then the .class properties for the
* generator loader class names. All of the other generator loader
* properties are sent to the generator loader for interpretation.
* <p>
*
* The markerName is generally provided by the parent component of the
* DTEDFrameCacheHandler, like the DTEDFrameCacheLayer.
*/
public class DTEDFrameCacheHandler extends CacheHandler implements
        DTEDConstants, PropertyConsumer, PropertyChangeListener {

    public final static String GeneratorLoadersProperty = "generators";

    /** The real frame cache. */
    protected DTEDFrameCache frameCache;

    // Setting up the screen...
    protected double frameUp, frameDown, frameLeft, frameRight;

    // Returning the images...
    protected boolean firstImageReturned = true;
    protected double frameLon = 0.0;
    protected double frameLat = 0.0;
    protected boolean newframe = false;

    protected int dtedLevel = LEVEL_0;
    /**
     * The active GeneratorLoader providing OMGridGenerators to the
     * OMGrids.
     */
    protected GeneratorLoader activeGeneratorLoader = null;
    /**
     * The list of GeneratorLoaders.
     */
    protected ArrayList generatorLoaders = new ArrayList();

    /**
     * The DTEDFrameCache must be set at some point.
     */
    protected DTEDFrameCacheHandler() {
        this(null);
    }

    /**
     * Create a handler for the DTEDFrameCache.
     */
    public DTEDFrameCacheHandler(DTEDFrameCache dfc) {
        setFrameCache(dfc);
    }

    /**
     * Set the DTEDFrameCache.
     */
    public void setFrameCache(DTEDFrameCache dfc) {
        frameCache = dfc;
    }

    /**
     * Get the DTEDFrameCache.
     */
    public DTEDFrameCache getFrameCache() {
        return frameCache;
    }

    /**
     * Get an elevation at a point. Always uses the cache to load the
     * frame and get the data. DTED data is in meters.
     */
    public int getElevation(float lat, float lon) {
        if (frameCache != null) {
            return frameCache.getElevation(lat, lon);
        } else {
            return DTEDFrameCache.NO_DATA;
        }
    }

    /**
     * Set the DTED level to get from the DTEDFrameCache.
     */
    public void setDtedLevel(int level) {
        dtedLevel = level;
    }

    /**
     * Get the DTED level being retrieved from the DTEDFrameCache.
     */
    public int getDtedLevel() {
        return dtedLevel;
    }

    /**
     * Set the active GeneratorLoader based on a pretty name from one
     * of the loaders.
     */
    public void setActiveGeneratorLoader(String active) {
        for (Iterator it = generatorLoaders.iterator(); it.hasNext();) {
            GeneratorLoader gl = (GeneratorLoader) it.next();
            if (active.equals(gl.getPrettyName())
                    && gl != activeGeneratorLoader) {
                activeGeneratorLoader = gl;
                clear();
            }
        }
    }

    /**
     * Get a new OMGridGenerator from the active GeneratorLoader.
     */
    public OMGridGenerator getGenerator() {
        if (activeGeneratorLoader != null) {
            return activeGeneratorLoader.getGenerator();
        } else {
            return new SinkGenerator();
        }
    }

    /**
     * GUI Panel holding the GeneratorLoader GUIs.
     */
    final JPanel cards = new JPanel(new CardLayout());

    /**
     * Get the GUI for the GeneratorLoaders.
     */
    public Component getGUI() {
        JPanel pane = new JPanel(new BorderLayout());

        int numLoaders = generatorLoaders.size();
        String comboBoxItems[] = new String[numLoaders];
        int count = 0;

        for (Iterator it = generatorLoaders.iterator(); it.hasNext();) {

            GeneratorLoader gl = (GeneratorLoader) it.next();
            String prettyName = gl.getPrettyName();
            comboBoxItems[count++] = prettyName;

            Component glGui = gl.getGUI();
            if (glGui == null) {
                glGui = new JLabel("No options available.");
            }

            cards.add(glGui, prettyName);
        }

        JComboBox cb = new JComboBox(comboBoxItems);
        cb.setEditable(false);
        cb.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent evt) {
                CardLayout cl = (CardLayout) (cards.getLayout());
                String active = (String) evt.getItem();
                cl.show(cards, active);
                setActiveGeneratorLoader(active);
            }
        });
        // Put the JComboBox in a JPanel to get a nicer look.
        JPanel comboBoxPane = new JPanel(); // use FlowLayout
        comboBoxPane.add(cb);

        pane.add(comboBoxPane, BorderLayout.NORTH);
        pane.add(cards, BorderLayout.CENTER);
        return pane;
    }

    /**
     * The call to the cache that lets you choose what kind of
     * information is returned. This function also figures out what
     * part of the earth is covered on the screen, and creates
     * auxillary cache handlers as needed.
     *
     * @param proj The projection of the screen (CADRG).
     * @return List of rasters to display.
     */
    public OMGraphicList getRectangle(EqualArc proj) {

        float[] lat = new float[3];
        float[] lon = new float[3];

        // This next bit of mumbo jumbo is to handle the equator and
        // dateline: Worst case, crossing both, treat each area
        // separately, so it is the same as handling four requests for
        // data - above and below the equator, and left and right of
        // the dateline. Normal case, there is only one box. Two
        // boxes if crossing only one of the boundaries.

        int xa = 2;
        int ya = 2;
        int lat_minus = 2;
        int lon_minus = 2;
        // Set up checks for equator and dateline
        LatLonPoint ll1 = proj.getUpperLeft();
        LatLonPoint ll2 = proj.getLowerRight();

        lat[0] = ll1.getLatitude();
        lon[0] = ll1.getLongitude();
        lat[1] = ll2.getLatitude();
        lon[1] = ll2.getLongitude();
        lat[2] = ll2.getLatitude();
        lon[2] = ll2.getLongitude();

        if (lon[0] > 0 && lon[2] < 0) {
            lon[1] = -179.999f; // put a little breather on the
            // dateline
            lon_minus = 1;
        }
        if (lat[0] > 0 && lat[2] < 0) {
            lat[1] = -0.0001f; // put a little breather on the equator
            lat_minus = 1;
        }

        if (Debug.debugging("dteddetail")) {
            Debug.output("For :");
            Debug.output("lat[0] " + lat[0]);
            Debug.output("lon[0] " + lon[0]);
            Debug.output("lat[1] " + lat[1]);
            Debug.output("lon[1] " + lon[1]);
            Debug.output("lat[2] " + lat[2]);
            Debug.output("lon[2] " + lon[2]);
            Debug.output("lat_minus = " + lat_minus);
            Debug.output("lon_minus = " + lon_minus);
        }

        /*
         * Look at all the paths if needed. Worst case, there are four
         * boxes on the screen. Best case, there is one. The things
         * that create boxes and dictates how large they are are the
         * equator and the dateline. When the screen straddles one or
         * both of these lat/lon lines, lon_minus and lat_minus get
         * adjusted, causing two or four different calls to the
         * tochandler to get the data above/below the equator, and
         * left/right of the dateline. Plus, each path gets checked
         * until the required boxes are filled.
         */

        if (Debug.debugging("dted")) {
            Debug.output("--- DTEDFrameCacheHandler: getting images: ---");
        }

        setProjection(proj,
                lat[ya - lat_minus],
                lon[xa - lon_minus],
                lat[ya],
                lon[xa]);

        OMGraphicList list = loadListFromHandler(null);

        // Dateline split
        if (lon_minus == 1) {
            setProjection(proj, lat[ya - lat_minus], lon[0], lat[ya], -1f
                    * lon[1]); // -1 to make it 180
            list = loadListFromHandler(list);
        }

        // Equator Split
        if (lat_minus == 1) {
            setProjection(proj, lat[0], lon[xa - lon_minus], -1f * lat[1], // flip
                    // breather
                    lon[xa]);
            list = loadListFromHandler(list);
        }

        // Both!!
        if (lon_minus == 1 && lat_minus == 1) {
            setProjection(proj, lat[0], lon[0], -1f * lat[1],// flip
                    // breather
                    -1f * lon[1]);// -1 to make it 180, not -180
            list = loadListFromHandler(list);
        }

        if (Debug.debugging("dted")) {
            Debug.output("--- DTEDFrameCacheHandler: finished getting images ---");
        }

        return list;
    }

    /**
     * Method that pings the cache for images based on the projection
     * that has been set on it. If the cache returns null from
     * getNextImage(), it's done. Method creates and returns a
     * graphics list if the one passed in is null, otherwise it
     * returns the one passed in.
     */
    protected OMGraphicList loadListFromHandler(OMGraphicList graphics) {
        if (graphics == null) {
            graphics = new OMGraphicList();
        }

        OMGraphic image = getNextImage();

        while (image != null) {
            graphics.add(image);
            image = getNextImage();
        }

        return graphics;
    }

    /**
     * The method to call to let the cache handler know what the
     * projection looks like so it can figure out which frames (and
     * subframes) will be needed.
     *
     * @param proj the EqualArc projection of the screen.
     */
    public void setProjection(EqualArc proj) {
        setProjection(proj,
                proj.getUpperLeft().getLatitude(),
                proj.getUpperLeft().getLongitude(),
                proj.getLowerRight().getLatitude(),
                proj.getLowerRight().getLongitude());
    }

    /**
     * The method to call to let the cache handler know what the
     * projection looks like so it can figure out which frames (and
     * subframes) will be needed. Should be called when the
     * CacheHandler is dealing with just a part of the map, such as
     * when the map covers the dateline or equator.
     *
     * @param proj the EqualArc projection of the screen.
     * @param lat1 latitude of the upper left corner of the window, in
     *        decimal degrees.
     * @param lon1 longitude of the upper left corner of the window,
     *        in decimal degrees.
     * @param lat2 latitude of the lower right corner of the window,
     *        in decimal degrees.
     * @param lon2 longitude of the lower right corner of the window,
     *        in decimal degrees.
     */
    public void setProjection(EqualArc proj, float lat1, float lon1,
                              float lat2, float lon2) {

        firstImageReturned = true;

        // upper lat of top frame of the screen
        // lower lat of bottom frame of the screen
        // left lon of left frame of the screen
        // upper lon of right frame of the screen
        frameUp = Math.floor((double) lat1);
        frameDown = Math.floor((double) lat2);
        frameLeft = Math.floor((double) lon1);
        frameRight = Math.ceil((double) lon2);

        if (Debug.debugging("dted"))
            Debug.output("frameUp = " + frameUp + ", frameDown = " + frameDown
                    + ", frameLeft = " + frameLeft + ", frameRight = "
                    + frameRight);
    }

    /**
     * Returns the next OMRaster image. When setProjection() is
     * called, the cache sets the projection parameters it needs, and
     * also resets this popping mechanism. When this mechanism is
     * reset, you can keep calling this method to get another subframe
     * image. When it returns a null value, it is done. It will
     * automatically skip over window frames it doesn't have, and
     * return the next one it does have. It traverses from the top
     * left to right frames, and top to bottom for each column of
     * frames. It handles all the subframes for a frame at one time.
     *
     * @return OMRaster image.
     */
    public OMGraphic getNextImage() {

        if (Debug.debugging("dted"))
            Debug.output("--- DTEDFrameCacheHandler: getNextImage:");

        while (true) {

            if (firstImageReturned == true) {
                frameLon = frameLeft;
                frameLat = frameDown;
                newframe = true;
                firstImageReturned = false;
            } else if (frameLon < frameRight) {
                // update statics to look for next frame
                if (frameLat < frameUp) {
                    frameLat++;
                } else {
                    frameLat = frameDown;
                    frameLon++;
                }
                newframe = true;
            } else { // bounds exceeded, all done
                return (OMGraphic) null;
            }

            if (newframe && frameLon < frameRight) {
                if (Debug.debugging("dted")) {
                    Debug.output(" gni: Getting new frame Lat = " + frameLat
                            + " Lon = " + frameLon);
                }

                OMGraphic omg = get(frameLat, frameLon, dtedLevel);
                if (omg != null) {
                    return omg;
                }
            }
        }
    }

    /**
     * Return an OMGraphic for the Dted Frame, given A lat, lon and
     * dted level.
     *
     * @param lat latitude of point
     * @param lon longitude of point
     * @param level the dted level wanted (0, 1, 2)
     * @return OMGraphic, most likely an OMGrid.
     */
    public OMGraphic get(double lat, double lon, int level) {
        // First, put together a key from the above info, and then
        // look for it in the local cache. If it's not there, then go
        // to the DTEDFrameCache.

        String key = new String(lat + ":" + lon + ":" + level);

        CacheObject ret = searchCache(key);
        if (ret != null) {
            if (Debug.debugging("dted")) {
                Debug.output("DTEDFrameCacheHandler.get():  retrieving frame from cache ("
                        + lat + ":" + lon + ":" + level + ")");
            }
            return (OMGraphic) ret.obj;
        }

        ret = load(key, lat, lon, level);
        if (ret == null) {
            return null;
        }

        replaceLeastUsed(ret);
        if (Debug.debugging("dted")) {
            Debug.output("DTEDFrameCacheHandler.get():  loading new frame into cache ("
                    + lat + ":" + lon + ":" + level + ")");
        }
        return (OMGraphic) ret.obj;
    }

    /**
     * Load a dted frame into the cache, based on the path of the
     * frame as a key. Implements abstract CacheHandler method.
     *
     * @param key key to remember raster created for DTED frame.
     * @return DTED frame, hidden as a CacheObject.
     */
    public CacheObject load(String key, double lat, double lon, int level) {
        if (frameCache != null) {

            DTEDFrame frame = frameCache.get(lat, lon, level);
            if (frame != null) {
                OMGrid omgrid = frame.getOMGrid();

                // Need to create a unique generator for each OMGrid.
                omgrid.setGenerator(getGenerator());
                return new DTEDCacheObject(key, omgrid);
            }
        }

        return null;
    }

    public CacheObject load(String key) {
        // Do nothing, because this implementation doesn't use it.
        // The get() method has been overridden to c all the other
        // load method with addition needed information to better call
        // the DTEDFrameCache.
        return null;
    }

    /**
     * A private class that makes sure that cached frames get disposed
     * properly.
     */
    private static class DTEDCacheObject extends CacheObject {
        /**
         * Construct a DTEDCacheObject, just calls superclass
         * constructor
         *
         * @param id passed to superclass
         * @param obj passed to superclass
         */
        public DTEDCacheObject(String id, OMGraphic omg) {
            super(id, omg);
        }
    }

    // //// PropertyConsumer Interface Methods

    /**
     * Token uniquely identifying this component in the application
     * properties.
     */
    protected String propertyPrefix = null;

    /**
     * Sets the properties for the OMComponent.
     *
     * @param props the <code>Properties</code> object.
     */
    public void setProperties(java.util.Properties props) {
        setProperties(getPropertyPrefix(), props);
    }

    /**
     * Sets the properties for the OMComponent.
     *
     * @param prefix the token to prefix the property names
     * @param props the <code>Properties</code> object
     */
    public void setProperties(String prefix, java.util.Properties props) {
        setPropertyPrefix(prefix);

        String realPrefix = PropUtils.getScopedPropertyPrefix(prefix);
        String generatorList = props.getProperty(realPrefix
                + GeneratorLoadersProperty);
        if (generatorList != null) {
            Vector generatorMarkers = PropUtils.parseSpacedMarkers(generatorList);
            for (Iterator it = generatorMarkers.iterator(); it.hasNext();) {
                String loaderPrefix = realPrefix + (String) it.next();
                String loaderClassnameProperty = loaderPrefix + ".class";
                String classname = props.getProperty(loaderClassnameProperty);
               
                try {
                    GeneratorLoader loader = (GeneratorLoader) ComponentFactory.create(classname);
                    loader.setProperties(loaderPrefix, props);

                    generatorLoaders.add(loader);
                    loader.addPropertyChangeListener(this);
                    // Initialize
                    if (activeGeneratorLoader == null) {
                        activeGeneratorLoader = loader;
                    }
                } catch (ClassCastException cce) {
                    Debug.output("DTEDFrameCacheHandler created " + classname
                            + ", but it's not a GeneratorLoader");
                } catch (NullPointerException npe) {
                    Debug.error("DTEDFrameCacheHandler:  problem creating generator loader: " + classname + " from " +
                            loaderClassnameProperty);
                }
            }
        }
    }

    /**
     * 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.
     *
     * @param props a Properties object to load the PropertyConsumer
     *        properties into. If props equals null, then a new
     *        Properties object should be created.
     * @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(this);

        StringBuffer sb = new StringBuffer();
        for (Iterator it = generatorLoaders.iterator(); it.hasNext();) {
            GeneratorLoader gl = (GeneratorLoader) it.next();
            String pref = gl.getPropertyPrefix();
            props.put(pref + ".class", gl.getClass().getName());
            gl.getProperties(props);

            int index = pref.indexOf(prefix);
            if (index != -1) {
                pref = pref.substring(index + prefix.length());
            }
            sb.append(pref + " ");
        }

        props.put(prefix + GeneratorLoadersProperty, sb.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.
     *
     * @param list a Properties object to load the PropertyConsumer
     *        properties into. If getList equals null, then a new
     *        Properties object should be created.
     * @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();
        }
        // Not sure how to set up an inspector to create child classes
        // yet.
        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.
     *
     * @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.
     *
     * @return the property prefix string
     */
    public String getPropertyPrefix() {
        return propertyPrefix;
    }

    /**
     * The DTEDFrameCacheHandler needs to sign up as a
     * PropertyChangeListener so if anything on the GeneratorLoader
     * GUI changes, it knows to dump the current representations so
     * they can be rebuild with the current GUI settings.
     */
    public void propertyChange(PropertyChangeEvent pce) {
        clear();
    }

}
TOP

Related Classes of com.bbn.openmap.dataAccess.dted.DTEDFrameCacheHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.