Package com.bbn.openmap.layer.location

Source Code of com.bbn.openmap.layer.location.Location

// **********************************************************************
//
// <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/location/Location.java,v $
// $RCSfile: Location.java,v $
// $Revision: 1.6.2.5 $
// $Date: 2005/08/11 21:03:23 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.layer.location;

/*  Java Core  */
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;

import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.Layer;
import com.bbn.openmap.layer.DeclutterMatrix;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMPoint;
import com.bbn.openmap.omGraphics.OMText;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;

/**
* A Location is a place. It can be thought of as a lat/lon place,
* with or without an pixel offset, or a place on the screen. A
* location is basically thought of as having a name, which get
* represented as a label, and some graphical represenation. It is
* abstract because it doesn't really know what kind of markers or
* labels are being used or how they are being positioned around the
* particular point. Therefore, it should be extended, and the
* setGraphicLocations methods implemented to position the marker and
* text as desired.
* <P>
*/
public abstract class Location extends OMGraphic {

    /**
     * The main latitude of object, in decimal degrees, for
     * RENDERTYPE_LATLON and RENDERTYPE_OFFSET locations.
     */
    public float lat = 0.0f;
    /**
     * The main longitude of object, in decimal degrees, for
     * RENDERTYPE_LATLON and RENDERTYPE_OFFSET locations.
     */
    public float lon = 0.0f;
    /**
     * The x pixel offset from the longitude, for RENDERTYPE_OFFSET
     * locations.
     */
    public int xOffset = 0;
    /**
     * The y pixel offset from the latitude, for RENDERTYPE_OFFSET
     * locations.
     */
    public int yOffset = 0;
    /** The x object location, in pixels, for RENDERTYPE_XY locations. */
    public int x = 0;
    /** The y object location, in pixels, for RENDERTYPE_XY locations. */
    public int y = 0;
    /** The name of the location. */
    public String name = null;
    /**
     * The LocationHandler that is handling the location. Need this to
     * check for more global settings for rendering.
     */
    public LocationHandler handler;

    public final static int DECLUTTER_LOCALLY = -1;
    public final static int DECLUTTER_ANYWHERE = -2;

    /** The Label of the object. */
    protected OMText label = null;
    /** The simple location marker of the object. */
    protected OMGraphic location = null;
    /** The URL to display when the object is gestured upon. */
    protected String details = "";
    /** The flag for displaying the location marker. */
    protected boolean showLocation = true;
    /** The flag for displaying the name label. */
    protected boolean showName = true;
    /**
     * The original offset/y location, kept for resetting the
     * placement of the label after decluttering and/or location
     * placement.
     */
    public int origYLabelOffset = 0;
    /**
     * The original offset/x location, kept for resetting the
     * placement of the label after decluttering and/or location
     * placement.
     */
    public int origXLabelOffset = 0;
    /**
     * the default distance away a label should be placed from a
     * location marker.
     */
    public final static int DEFAULT_SPACING = 6;
    /**
     * The pixel limit where the delcutter matrix won't draw the name,
     * if it can't put the name at least this close to the original
     * place. DECLUTTER_LOCALLY keeps the limit to twice the height of
     * the label. DECLUTTER_ANYWHERE will place the thing anywhere it
     * fits. Anything else is the pixel limit.
     */
    protected int declutterLimit = DECLUTTER_LOCALLY;
    /** Set whether you want this location label decluttered. */
    protected boolean allowDecluttering = true;
    /**
     * The horizontal pixel distance you want to place the text away
     * from the actual location - to put space between the graphic.
     */
    protected int horizontalLabelBuffer = 0;

    /**
     * A plain contructor if you are planning on setting everything
     * yourself.
     */
    public Location() {}

    /**
     * Create a location at a latitude/longitude. If the
     * locationMarker is null, a small rectangle (dot) will be created
     * to mark the location.
     *
     * @param latitude the latitude, in decimal degrees, of the
     *        location.
     * @param longitude the longitude, in decimal degrees, of the
     *        location.
     * @param name the name of the location, also used in the label.
     * @param locationMarker the OMGraphic to use for the location
     *        mark.
     */
    public Location(float latitude, float longitude, String name,
            OMGraphic locationMarker) {

        setLocation(latitude, longitude);
        this.name = name;

        if (Debug.debugging("locationdetail")) {
            Debug.output("Location Lat/Lon(" + latitude + ", " + longitude
                    + ", " + name + ")");
        }

        if (locationMarker == null) {
            location = new OMPoint(lat, lon);
        } else {
            location = locationMarker;
        }

        // We can do the x offset off the location here, we'll do the
        // vertical offset later, when we can figure out the height of
        // the text and can line the middle of the text up with the
        // location.

        // If the caller has supplied a substitute graphic for
        // the location spot, it's up to them to horizontally
        // offset the label appropriately. They should do that
        // here, or in an extended class.
        label = new OMText(lat, lon, 0, 0, name, OMText.JUSTIFY_LEFT);
    }

    /**
     * Create a location at a map location. If the locationMarker is
     * null, a small rectangle (dot) will be created to mark the
     * location.
     *
     * @param x the pixel location of the object from the let of the
     *        map.
     * @param y the pixel location of the object from the top of the
     *        map
     * @param name the name of the location, also used in the label.
     * @param locationMarker the OMGraphic to use for the location
     *        mark.
     */
    public Location(int x, int y, String name, OMGraphic locationMarker) {

        setLocation(x, y);
        this.name = name;

        if (Debug.debugging("locationdetail")) {
            Debug.output("Location XY(" + x + ", " + y + ", " + name + ")");
        }

        if (locationMarker == null) {
            location = new OMPoint(x, y);
        } else {
            location = locationMarker;
        }

        // We can do the x offset off the location here, we'll do the
        // vertical offset later, when we can figure out the height of
        // the text and can line the middle of the text up with the
        // location.

        // If the caller has supplied a substitute graphic for
        // the location spot, it's up to them to horizontally
        // offset the label appropriately. They should do that
        // here, or in an extended class.
        label = new OMText(x, y, name, OMText.JUSTIFY_LEFT);
    }

    /**
     * Create a location at a pixel offset from a latitude/longitude.
     * If the locationMarker is null, a small rectangle (dot) will be
     * created to mark the location.
     *
     * @param latitude the latitude, in decimal degrees, of the
     *        location.
     * @param longitude the longitude, in decimal degrees, of the
     *        location.
     * @param xOffset the pixel location of the object from the
     *        longitude.
     * @param yOffset the pixel location of the object from the
     *        latitude.
     * @param name the name of the location, also used in the label.
     * @param locationMarker the OMGraphic to use for the location
     *        mark.
     */
    public Location(float latitude, float longitude, int xOffset, int yOffset,
            String name, OMGraphic locationMarker) {
        setLocation(latitude, longitude, xOffset, yOffset);
        this.name = name;

        if (Debug.debugging("locationdetail")) {
            Debug.output("Location(" + latitude + ", " + longitude
                    + ", offset " + x + ", " + y + ", " + name + ")");
        }

        if (locationMarker == null) {
            location = new OMPoint(lat, lon, xOffset, yOffset);
        } else {
            location = locationMarker;
        }

        // We can do the x offset off the location here, we'll do the
        // vertical offset later, when we can figure out the height of
        // the text and can line the middle of the text up with the
        // location.

        // If the caller has supplied a substitute graphic for
        // the location spot, it's up to them to horizontally
        // offset the label appropriately. They should do that
        // here, or in an extended class.
        label = new OMText(lat, lon, xOffset, yOffset, name, OMText.JUSTIFY_LEFT);
    }

    /** Set the placement of the location. */
    public void setLocation(float latitude, float longitude) {
        lat = latitude;
        lon = longitude;

        origYLabelOffset = 0;
        origXLabelOffset = DEFAULT_SPACING;

        setRenderType(RENDERTYPE_LATLON);
        if (location != null && label != null) {
            setGraphicLocations(latitude, longitude);
        }
    }

    /** Set the placement of the location. */
    public void setLocation(int x, int y) {
        this.x = x;
        this.y = y;

        origYLabelOffset = y;
        origXLabelOffset = x + DEFAULT_SPACING;

        setRenderType(RENDERTYPE_XY);
        if (location != null && label != null) {
            setGraphicLocations(x, y);
        }
    }

    /** Set the placement of the location. */
    public void setLocation(float latitude, float longitude, int xOffset,
                            int yOffset) {
        lat = latitude;
        lon = longitude;
        this.xOffset = xOffset;
        this.yOffset = yOffset;

        origYLabelOffset = yOffset;
        origXLabelOffset = xOffset + DEFAULT_SPACING;

        setRenderType(RENDERTYPE_OFFSET);
        if (location != null && label != null) {
            setGraphicLocations(latitude, longitude, xOffset, yOffset);
        }
    }

    /**
     * Convenience method that lets you provide a screen x, y and a
     * projection to the location, and let the location hash out how
     * to place itself based on it's rendertype.
     */
    public void setLocation(int x, int y, Projection proj) {
        int renderType = getRenderType();
        LatLonPoint llp;

        switch (renderType) {
        case RENDERTYPE_LATLON:
            if (proj != null) {
                llp = proj.inverse(x, y);
                setLocation(llp.getLatitude(), llp.getLongitude());
            } else {
                Debug.error("Location can't set lat/lon coordinates without a projection");
            }
            break;
        case RENDERTYPE_OFFSET:
            if (proj != null) {
                llp = proj.inverse(x, y);
                setLocation(llp.getLatitude(),
                        llp.getLongitude(),
                        this.xOffset,
                        this.yOffset);
            } else {
                Debug.error("Location can't set lat/lon coordinates without a projection");
            }
            break;
        default:
            setLocation(x, y);
        }
    }

    public abstract void setGraphicLocations(float latitude, float longitude);

    public abstract void setGraphicLocations(int x, int y);

    public abstract void setGraphicLocations(float latitude, float longitude,
                                             int offsetX, int offsetY);

    /**
     * Set the location handler for the location.
     */
    public void setLocationHandler(LocationHandler lh) {
        handler = lh;
    }

    /**
     * Get the location handler for the location.
     */
    public LocationHandler getLocationHandler() {
        return handler;
    }

    /**
     * Set the edge java.awt.Paint for the marker graphic.
     */
    public void setLocationPaint(Paint locationPaint) {
        if (location != null) {
            location.setLinePaint(locationPaint);
        }
    }

    /**
     * Get the label for the location.
     */
    public OMText getLabel() {
        return label;
    }

    /**
     * Set the label for the location.
     */
    public void setLabel(OMText lable) {
        label = lable;
    }

    /**
     * Get the location marker for this location.
     */
    public OMGraphic getLocationMarker() {
        return location;
    }

    /**
     * Set the graphic for the location.
     */
    public void setLocationMarker(OMGraphic graphic) {
        location = graphic;
    }

    /**
     * Set whether this location should be shown on an individual
     * basis.
     */
    public void setShowLocation(boolean showLocations) {
        showLocation = showLocations;
    }

    /** See of the location is displaying it's location. */
    public boolean isShowLocation() {
        return showLocation;
    }

    /** Set the location to display it's label. */
    public void setShowName(boolean showNames) {
        showName = showNames;
    }

    /** See if the location is displaying it's label. */
    public boolean isShowName() {
        return showName;
    }

    /** Get the name of the location. */
    public String getName() {
        return name;
    }

    /**
     * Set the name of this location.
     */
    public void setName(String name) {
        this.name = name;
        if (label != null) {
            label.setData(name);
        }
    }

    /**
     * Set the details for the location. This should be the contents
     * to be displayed in a web browser.
     */
    public void setDetails(String det) {
        details = det;
    }

    /**
     * Get the details for the location.
     */
    public String getDetails() {
        return details;
    }

    /**
     * Fire a browser to display the location details.
     */
    public void showDetails(Layer layer) {
        if (details != null)
            layer.fireRequestBrowserContent(details);
    }

    /**
     * Set whether you want to allow the label for this location to be
     * decluttered.
     *
     * @param allow if true, label will be decluttered if declutter
     *        matrix is available.
     */
    public void setAllowDecluttering(boolean allow) {
        allowDecluttering = allow;
    }

    /**
     * Get the decluttering allowance setting for this label.
     */
    public boolean isAllowDecluttering() {
        return allowDecluttering;
    }

    /**
     * Set the pixel distance that the label will be moved to the
     * right, to clear space for the graphic marking the location.
     */
    public void setHorizontalLabelBuffer(int buffer) {
        horizontalLabelBuffer = buffer;
    }

    /**
     * Get the pixel distance that the label will be moved to the
     * right, to clear space for the graphic marking the location.
     */
    public int getHorizontalLabelBuffer() {
        return horizontalLabelBuffer;
    }

    ////////////////////////////////////////////////////
    /////////// OMGraphic methods ////////////////////
    ////////////////////////////////////////////////////

    /**
     * Generate the location, and use the declutter matrix to place
     * the label is a spot so that it doesn't interset with other
     * labels.
     *
     * @param proj projection of the map.
     * @param declutterMatrix DeclutterMatrix for the map.
     */
    public boolean generate(Projection proj, DeclutterMatrix declutterMatrix) {

        // Call generate(proj) first, to get the original position
        // set. Then, declutter the text.
        boolean ret = generate(proj);

        if (declutterMatrix != null && label != null && allowDecluttering) {
            declutterLabel(declutterMatrix, proj);
        }
        return ret;
    }

    /**
     * Set the pixel distance that us used by the declutter matrix in
     * trying to find a place for the label. If it can't find a place
     * within this pixel limit, it woun't draw it.
     */
    public void setDeclutterLimit(int value) {
        if (value < 0 && value != DECLUTTER_LOCALLY) {
            declutterLimit = DECLUTTER_ANYWHERE;
        } else {
            declutterLimit = value;
        }
    }

    /**
     * Get the declutter pixel distance limit.
     */
    public int getDeclutterLimit() {
        return declutterLimit;
    }

    protected int currentFontDescent = 0;

    /**
     * Prepare the graphic for rendering. This must be done before
     * calling <code>render()</code>! If a vector graphic has
     * lat-lon components, then we project these vertices into x-y
     * space. For raster graphics we prepare in a different fashion.
     * <p>
     * If the generate is unsuccessful, it's usually because of some
     * oversight, (for instance if <code>proj</code> is null), and
     * if debugging is enabled, a message may be output to the
     * controlling terminal.
     * <p>
     *
     * @param proj Projection
     * @return boolean true if successful, false if not.
     */
    public boolean generate(Projection proj) {
        if (label != null) {
            label.setY(origYLabelOffset);
            label.setX(origXLabelOffset);
        }

        java.awt.Graphics g = DeclutterMatrix.getGraphics();
        if (g != null && label != null) {
            g.setFont(label.getFont());
            // Now set the vertical offset to the original place based
            // off the height of the label, so that the location place
            // is halfway up the text. That way, it looks like a
            // label.
            int height = g.getFontMetrics().getAscent();
            currentFontDescent = g.getFontMetrics().getDescent();
            label.setX(label.getX() + horizontalLabelBuffer);
            label.setY(label.getY() + (height / 2) - 2);
        }

        if (label != null) {
            label.generate(proj);
            label.prepareForRender(g);
        }
        if (location != null)
            location.generate(proj);

        return true;
    }

    /**
     * Paint the graphic and the name of the location. This should
     * only be used if the locations are pretty spread out from each
     * other. If you think you need to declutter, you should render
     * all the graphics, and then render the names, so that the
     * graphics don't cover up the names.
     * <P>
     * This paints the graphic into the Graphics context. This is
     * similar to <code>paint()</code> function of
     * java.awt.Components. Note that if the graphic has not been
     * generated, it will not be rendered. This render will take into
     * account the layer showNames and showLocations settings.
     *
     * @param g Graphics context to render into.
     */
    public void render(Graphics g) {
        renderLocation(g);
        renderName(g);
    }

    /**
     * Paint the graphic label (name) only. This paints the graphic
     * into the Graphics context. This is similar to
     * <code>paint()</code> function of java.awt.Components. Note
     * that if the graphic has not been generated, it will not be
     * rendered. This render will take into account the layer
     * showNames and showLocations settings.
     *
     * @param g Graphics context to render into.
     */
    public void renderName(Graphics g) {
        if (shouldRenderName()) {
            label.render(g);
        }
    }

    /**
     * Paint the graphic location graphic only. This paints the
     * graphic into the Graphics context. This is similar to
     * <code>paint()</code> function of java.awt.Components. Note
     * that if the graphic has not been generated, it will not be
     * rendered. This render will take into account the layer
     * showNames and showLocations settings.
     *
     * @param g Graphics context to render into.
     */
    public void renderLocation(Graphics g) {
        if (shouldRenderLocation()) {
            location.render(g);
        }
    }

    /**
     * Convenience method to see if handler/global settings dictate
     * that the location label should be rendered.
     *
     * @return true if the name label should be rendered.
     */
    protected boolean shouldRenderName() {
        boolean globalShowNames = false;
        boolean forceGlobal = false;
        if (handler != null) {
            globalShowNames = handler.isShowNames();
            forceGlobal = handler.isForceGlobal();
        }

        return label != null
                && ((forceGlobal && globalShowNames) || (!forceGlobal && showName));
    }

    /**
     * Convenience method to see if handler/global settings dictate
     * that the location icon should be rendered.
     *
     * @return true of the location marker should be rendered.
     */
    protected boolean shouldRenderLocation() {
        boolean globalShowLocations = false;
        boolean forceGlobal = false;
        if (handler != null) {
            globalShowLocations = handler.isShowLocations();
            forceGlobal = handler.isForceGlobal();
        }

        return location != null
                && ((forceGlobal && globalShowLocations) || (!forceGlobal && showLocation));
    }

    /**
     * Return the shortest distance from the graphic to an XY-point.
     *
     * @param x X coordinate of the point.
     * @param y Y coordinate of the point.
     * @return float distance from graphic to the point
     */
    public float distance(int x, int y) {
        float labelDist = Float.MAX_VALUE;
        float locationDist = Float.MAX_VALUE;

        if (shouldRenderLocation()) {
            locationDist = location.distance(x, y);
        }

        if (shouldRenderName()) {
            labelDist = label.distance(x, y);
        }

        return (locationDist > labelDist ? labelDist : locationDist);
    }

    /**
     * Given the label is this location has a height and width, find a
     * clean place on the map for it. Assumes label is not null.
     *
     * @param declutter the DeclutterMatrix for the map.
     */
    protected void declutterLabel(DeclutterMatrix declutter, Projection proj) {

        if (Debug.debugging("locationdetail")) {
            Debug.output("\nLocation::RepositionText => " + label.getData());
        }

        // Right now, I think this method takes some presumptutous
        // actions, assuming that you want the graphics to take up
        // space in the declutter mattrix. We might want to delete
        // this showLocation code, and let people create their own
        // location subclasses that define how the graphic should be
        // handled in the declutter matrix.

        // I think I will. This stuff is commented out for the
        // reasons stated above.

        //      if (isShowLocation()) {
        //          Point lp;
        //          // Take up space with the label
        //          if (location instanceof OMRasterObject) {
        //              lp = ((OMRasterObject)location).getMapLocation();
        //              // This location is the upper left location of the
        //              // declutter matrix. The decutter matrix works from
        //              // lower left to upper right.
        //              if (lp != null) {
        //                  int locHeight = ((OMRasterObject)location).getHeight();
        //                  int locWidth = ((OMRasterObject)location).getWidth();
        //                  // Need to get this right for the DeclutterMatrix
        //                  // space, but changing lp changes where the
        //                  // location will appear - fix this later.
        //                  lp.y += locHeight;
        //                  declutter.setTaken(lp, locWidth, locHeight);
        //                  // Reset it to the original projected location.
        //                  lp.y -= locHeight;
        //              }
        //          } else if (renderType != RENDERTYPE_XY) {
        //              lp = proj.forward(lat,lon);
        //              lp.x += xOffset-1;
        //              lp.y += yOffset-1;
        //              declutter.setTaken(lp, 3, 3);
        //          } else {
        //              lp = new Point(x-1, y-1);
        //              declutter.setTaken(lp, 3, 3);
        //          }
        //      }

        if (isShowName() || (handler != null && handler.isShowNames())) {

            if (label == null || label.getPolyBounds() == null) {
                // Why bother going further??
                return;
            }

            Rectangle bounds = label.getPolyBounds().getBounds();
            int height = (int) ((float) (bounds.getHeight() - currentFontDescent / 2));
            int width = (int) bounds.getWidth();
            // Projected location of label on the screen
            Point p = label.getMapLocation();

            if (Debug.debugging("locationdetail")) {
                Debug.output("old point X Y =>" + p.x + " " + p.y
                        + "    height = " + height + " width = " + width);
            }

            int limit;
            if (declutterLimit == DECLUTTER_LOCALLY) {
                limit = height * 2;
            } else {
                limit = declutterLimit;
            }

            // newpoint is the new place on the map to put the label
            Point newpoint = declutter.setNextOpen(p, width, height, limit);

            if (Debug.debugging("locationdetail")) {
                Debug.output("new point X Y =>" + newpoint.x + " " + newpoint.y);
            }

            label.setMapLocation(newpoint);
        }
    }

    /**
     * A simple conversion method for the common String represention
     * of decimal degree coordinates, which is a letter denoting the
     * globle hemisphere (N or S for latitudes, W or E for longitudes,
     * and then a number string. For latitudes, the first two numbers
     * represent the whole degree value, and the rest of the numbers
     * represent the fractional protion. For longitudes, the first
     * three numbers represent the whole degree value. For instance
     * N2443243 equals 24.43243 degrees North, and S2443243 results in
     * -24.43243 degrees. Likewise, w12423443 results in -124.23443
     * degrees.
     *
     * @param coord the coordinate string representing the decimal
     *        degree value, following the format [NSEW]XXXXXXXXX.
     * @return the decimal degrees for the string. There is no
     *         notation for you to know whether it's a latitude or
     *         longitude value.
     */
    public static float convertCoordinateString(String coord)
            throws NumberFormatException {

        float ret = 0f;
        String mantissa;
        char direction = coord.charAt(0);
        if (direction == 'N' || direction == 'S' || direction == 'n'
                || direction == 's') {
            float whole = new Float(coord.substring(1, 3)).floatValue();
            ret += whole;
            mantissa = coord.substring(3);
        } else if (direction == 'W' || direction == 'E' || direction == 'w'
                || direction == 'e') {
            ret += new Float(coord.substring(1, 4)).floatValue();
            mantissa = coord.substring(4);
        } else {
            // Don't know the format!!
            throw new NumberFormatException("Location.convertCoordinateString wants <[NSWE]XXXXXXXX>, not getting it.");
        }

        ret += new Float(mantissa).floatValue()
                / (float) (Math.pow(10, mantissa.length()));
        if (direction == 'W' || direction == 'S' || direction == 'w'
                || direction == 's') {
            ret *= -1f;
        }

        return ret;
    }

    /**
     * We're using the main function for Location to test the
     * convertCoordinateString function.
     */
    public static void main(String[] args) {
        if (args.length < 1) {
            Debug.output("  usage: java com.bbn.openmap.layer.location.Location <[NSWE]XXXXXXXX>");
            return;
        }
        float ret = Location.convertCoordinateString(args[0]);
        System.out.println(ret);
    }

}
TOP

Related Classes of com.bbn.openmap.layer.location.Location

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.