Package com.bbn.openmap.layer

Source Code of com.bbn.openmap.layer.GraticuleLayer

// **********************************************************************
//
// <copyright>
//
// BBN Technologies, a Verizon Company
// 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/GraticuleLayer.java,v $
// $RCSfile: GraticuleLayer.java,v $
// $Revision: 1.7.2.6 $
// $Date: 2005/08/09 18:10:43 $
// $Author: dietrick $
//
// **********************************************************************

// Modified 28 September 2002 by David N. Allsopp to allow font size
// to be changed. See sections commented with 'DNA'.

package com.bbn.openmap.layer;

import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Properties;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JPanel;

import com.bbn.openmap.Environment;
import com.bbn.openmap.I18n;
import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.MoreMath;
import com.bbn.openmap.event.ProjectionEvent;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMPoly;
import com.bbn.openmap.omGraphics.OMText;
import com.bbn.openmap.proj.Cylindrical;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PaletteHelper;
import com.bbn.openmap.util.PropUtils;

/**
* Layer that draws graticule lines. If the showRuler property is set
* to true, then longitude values are displayed on the bottom of the
* map, and latitude values are displayed on the left side. If the
* show1And5Lines property is true, then 5 degree lines are drawn when
* there are &lt;= threshold ten degree latitude or longitude lines,
* and 1 degree lines are drawn when there are &lt;= threshold five
* degree latitude or longitude degree lines.
*
* <P>
* The openmap.properties file can control the layer with the
* following settings: <code><pre>
*
*   # Show lat / lon spacing labels
*   graticule.showRuler=true
*   graticule.show1And5Lines=true
*   # Controls when the five degree lines and one degree lines kick in
*   #- when there is less than the threshold of ten degree lat or lon
*   #lines, five degree lines are drawn. The same relationship is there
*   #for one to five degree lines.
*   graticule.threshold=2
*   # the color of 10 degree spacing lines (Hex ARGB)
*   graticule.10DegreeColor=FF000000
*   # the color of 5 degree spacing lines (Hex ARGB)
*   graticule.5DegreeColor=C7009900
*   # the color of 1 degree spacing lines (Hex ARGB)
*   graticule.1DegreeColor=C7003300
*   # the color of the equator (Hex ARGB)
*   graticule.equatorColor=FFFF0000
*   # the color of the international dateline (Hex ARGB)
*   graticule.datelineColor=7F000099
*   # the color of the special lines (Hex ARGB)
*   graticule.specialLineColor=FF000000
*   # the color of the labels (Hex ARGB)
*   graticule.textColor=FF000000
*  
* </pre></code> In addition, you can get this layer to work with the
* OpenMap viewer by editing your openmap.properties file: <code><pre>
*
*   # layers
*   openmap.layers=graticule ...
*   # class
*   graticule.class=com.bbn.openmap.layer.GraticuleLayer
*   # name
*   graticule.prettyName=Graticule
*  
* </pre></code>
*/
public class GraticuleLayer extends OMGraphicHandlerLayer implements
        ActionListener {

    protected I18n i18n = Environment.getI18n();
    // default to not showing the ruler (mimicing older
    // GraticuleLayer)
    protected boolean defaultShowRuler = true;
    protected boolean defaultShowOneAndFiveLines = true;
    protected boolean defaultShowBelowOneLines = false;
    protected int defaultThreshold = 2;

    /**
     * Flag for lineType - true is LINETYPE_STRAIGHT, false is
     * LINETYPE_GREATCIRCLE.
     */
    protected boolean boxy = true;
    /**
     * Threshold is the total number of ten lines on the screen before
     * the five lines appear, and the total number of five lines on
     * the screen before the one lines appear.
     */
    protected int threshold = defaultThreshold;
    /** The ten degree latitude and longitude lines, premade. */
    protected OMGraphicList tenDegreeLines = null;
    /** The equator, dateline and meridian lines, premade. */
    protected OMGraphicList markerLines = null;

    private final static int SHOW_TENS = 0;
    private final static int SHOW_FIVES = 1;
    private final static int SHOW_ONES = 2;

    protected boolean showOneAndFiveLines = defaultShowOneAndFiveLines;
    protected boolean showBelowOneLines = defaultShowBelowOneLines;
    protected boolean showRuler = defaultShowRuler;

    // protected Font font = new Font("Helvetica",
    // java.awt.Font.PLAIN, 10);
    protected Font font = null;
    protected int fontSize = 10;

    // Color variables for different line types
    protected Color tenDegreeColor = null;
    protected Color fiveDegreeColor = null;
    protected Color oneDegreeColor = null;
    protected Color belowOneDegreeColor = null;
    protected Color equatorColor = null;
    protected Color dateLineColor = null;
    protected Color specialLineColor = null; // Tropic of Cancer,
    // Capricorn
    protected Color textColor = null;

    // Default colors to use, if not specified in the properties.
    protected String defaultTenDegreeColorString = "000000";
    protected String defaultFiveDegreeColorString = "33009900";
    protected String defaultOneDegreeColorString = "33003300";
    protected String defaultBelowOneDegreeColorString = "9900ff00";
    protected String defaultEquatorColorString = "990000";
    protected String defaultDateLineColorString = "000099";
    protected String defaultSpecialLineColorString = "000000";
    protected String defaultTextColorString = "000000";

    // property text values
    public static final String TenDegreeColorProperty = "10DegreeColor";
    public static final String FiveDegreeColorProperty = "5DegreeColor";
    public static final String OneDegreeColorProperty = "1DegreeColor";
    public static final String BelowOneDegreeColorProperty = "Below1DegreeColor";
    public static final String EquatorColorProperty = "equatorColor";
    public static final String DateLineColorProperty = "datelineColor";
    public static final String SpecialLineColorProperty = "specialLineColor";
    public static final String TextColorProperty = "textColor";
    public static final String ThresholdProperty = "threshold";
    public static final String ShowRulerProperty = "showRuler";
    public static final String ShowOneAndFiveProperty = "show1And5Lines";
    public static final String ShowBelowOneProperty = "showBelow1Lines";
    public static final String FontSizeProperty = "fontSize"; //DNA

    /**
     * Construct the GraticuleLayer.
     */
    public GraticuleLayer() {
        // precalculate for boxy
        boxy = true;
        setName("Graticule");
    }

    /**
     * The properties and prefix are managed and decoded here, for the
     * standard uses of the GraticuleLayer.
     *
     * @param prefix string prefix used in the properties file for
     *        this layer.
     * @param properties the properties set in the properties file.
     */
    public void setProperties(String prefix, java.util.Properties properties) {
        super.setProperties(prefix, properties);
        prefix = PropUtils.getScopedPropertyPrefix(prefix);

        tenDegreeColor = PropUtils.parseColorFromProperties(properties, prefix
                + TenDegreeColorProperty, defaultTenDegreeColorString);

        fiveDegreeColor = PropUtils.parseColorFromProperties(properties, prefix
                + FiveDegreeColorProperty, defaultFiveDegreeColorString);

        oneDegreeColor = PropUtils.parseColorFromProperties(properties, prefix
                + OneDegreeColorProperty, defaultOneDegreeColorString);

        belowOneDegreeColor = PropUtils.parseColorFromProperties(properties,
                prefix + BelowOneDegreeColorProperty,
                defaultBelowOneDegreeColorString);

        equatorColor = PropUtils.parseColorFromProperties(properties, prefix
                + EquatorColorProperty, defaultEquatorColorString);

        dateLineColor = PropUtils.parseColorFromProperties(properties, prefix
                + DateLineColorProperty, defaultDateLineColorString);

        specialLineColor = PropUtils.parseColorFromProperties(properties,
                prefix + SpecialLineColorProperty,
                defaultSpecialLineColorString);

        textColor = PropUtils.parseColorFromProperties(properties, prefix
                + TextColorProperty, defaultTextColorString);

        threshold = PropUtils.intFromProperties(properties, prefix
                + ThresholdProperty, defaultThreshold);

        fontSize = PropUtils.intFromProperties(properties, prefix
                + FontSizeProperty, fontSize);

        font = new Font("Helvetica", java.awt.Font.PLAIN, fontSize);

        setShowOneAndFiveLines(PropUtils.booleanFromProperties(properties,
                prefix + ShowOneAndFiveProperty,
                defaultShowOneAndFiveLines));

        setShowBelowOneLines(PropUtils.booleanFromProperties(properties, prefix
                + ShowBelowOneProperty, defaultShowBelowOneLines));

        setShowRuler(PropUtils.booleanFromProperties(properties, prefix
                + ShowRulerProperty, defaultShowRuler));

        // So they will get re-created.
        tenDegreeLines = null;
        markerLines = null;
    }

    protected JCheckBox showRulerButton = null;
    protected JCheckBox show15Button = null;
    protected JCheckBox showBelow1Button = null;

    public void setShowOneAndFiveLines(boolean set) {
        showOneAndFiveLines = set;
        if (show15Button != null) {
            show15Button.setSelected(set);
        }
    }

    public void setShowBelowOneLines(boolean set) {
        showBelowOneLines = set;
        if (showBelow1Button != null) {
            showBelow1Button.setSelected(set);
        }
    }

    public boolean getShowOneAndFiveLines() {
        return showOneAndFiveLines;
    }

    public boolean getShowBelowOneLines() {
        return showBelowOneLines;
    }

    public void setShowRuler(boolean set) {
        showRuler = set;
        if (showRulerButton != null) {
            showRulerButton.setSelected(set);
        }
    }

    public boolean getShowRuler() {
        return showRuler;
    }

    /**
     * The properties and prefix are managed and decoded here, for the
     * standard uses of the GraticuleLayer.
     *
     * @param properties the properties set in the properties file.
     */
    public Properties getProperties(Properties properties) {
        properties = super.getProperties(properties);

        String prefix = PropUtils.getScopedPropertyPrefix(this);
        String colorString;

        if (tenDegreeColor == null) {
            colorString = defaultTenDegreeColorString;
        } else {
            colorString = Integer.toHexString(tenDegreeColor.getRGB());
        }
        properties.put(prefix + TenDegreeColorProperty, colorString);

        if (fiveDegreeColor == null) {
            colorString = defaultFiveDegreeColorString;
        } else {
            colorString = Integer.toHexString(fiveDegreeColor.getRGB());
        }
        properties.put(prefix + FiveDegreeColorProperty, colorString);

        if (oneDegreeColor == null) {
            colorString = defaultOneDegreeColorString;
        } else {
            colorString = Integer.toHexString(oneDegreeColor.getRGB());
        }
        properties.put(prefix + OneDegreeColorProperty, colorString);

        if (belowOneDegreeColor == null) {
            colorString = defaultBelowOneDegreeColorString;
        } else {
            colorString = Integer.toHexString(belowOneDegreeColor.getRGB());
        }
        properties.put(prefix + BelowOneDegreeColorProperty, colorString);

        if (equatorColor == null) {
            colorString = defaultEquatorColorString;
        } else {
            colorString = Integer.toHexString(equatorColor.getRGB());
        }
        properties.put(prefix + EquatorColorProperty, colorString);

        if (dateLineColor == null) {
            colorString = defaultDateLineColorString;
        } else {
            colorString = Integer.toHexString(dateLineColor.getRGB());
        }
        properties.put(prefix + DateLineColorProperty, colorString);

        if (specialLineColor == null) {
            colorString = defaultSpecialLineColorString;
        } else {
            colorString = Integer.toHexString(specialLineColor.getRGB());
        }
        properties.put(prefix + SpecialLineColorProperty, colorString);

        if (textColor == null) {
            colorString = defaultTextColorString;
        } else {
            colorString = Integer.toHexString(textColor.getRGB());
        }
        properties.put(prefix + TextColorProperty, colorString);

        properties.put(prefix + ThresholdProperty, Integer.toString(threshold));
        properties.put(prefix + FontSizeProperty, Integer.toString(fontSize)); //DNA

        properties.put(prefix + ShowOneAndFiveProperty,
                new Boolean(showOneAndFiveLines).toString());

        properties.put(prefix + ShowBelowOneProperty,
                new Boolean(showBelowOneLines).toString());

        properties.put(prefix + ShowRulerProperty,
                new Boolean(showRuler).toString());

        return properties;
    }

    /**
     * The properties and prefix are managed and decoded here, for the
     * standard uses of the GraticuleLayer.
     *
     * @param properties the properties set in the properties file.
     */
    public Properties getPropertyInfo(Properties properties) {
        properties = super.getPropertyInfo(properties);
        String interString;
        properties.put(initPropertiesProperty, TenDegreeColorProperty + " "
                + FiveDegreeColorProperty + " " + OneDegreeColorProperty + " "
                + /*BelowOneDegreeColorProperty + " " + */ EquatorColorProperty
                + " " + DateLineColorProperty + " " + SpecialLineColorProperty
                + " " + ShowOneAndFiveProperty /* + " " + ShowBelowOneProperty */
                + " " + ShowRulerProperty + " " + ThresholdProperty + " "
                + FontSizeProperty);

        interString = i18n.get(GraticuleLayer.class,
                TenDegreeColorProperty,
                I18n.TOOLTIP,
                "Color of the ten degree graticule lines.");
        properties.put(TenDegreeColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                TenDegreeColorProperty,
                "Ten Degree Color");
        properties.put(TenDegreeColorProperty + LabelEditorProperty,
                interString);
        properties.put(TenDegreeColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                FiveDegreeColorProperty,
                I18n.TOOLTIP,
                "Color of the five degree graticule lines.");
        properties.put(FiveDegreeColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                FiveDegreeColorProperty,
                "Color of the five degree graticule lines.");
        interString = i18n.get(GraticuleLayer.class,
                FiveDegreeColorProperty,
                "File Degree Color");
        properties.put(FiveDegreeColorProperty + LabelEditorProperty,
                interString);
        properties.put(FiveDegreeColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                OneDegreeColorProperty,
                I18n.TOOLTIP,
                "Color of the one degree graticule lines.");
        properties.put(OneDegreeColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                OneDegreeColorProperty,
                "1 Degree Color");
        properties.put(OneDegreeColorProperty + LabelEditorProperty,
                interString);
        properties.put(OneDegreeColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                BelowOneDegreeColorProperty,
                I18n.TOOLTIP,
                "Color of the sub-one degree graticule lines.");
        properties.put(BelowOneDegreeColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                BelowOneDegreeColorProperty,
                "Sub-One Degree Color");
        properties.put(BelowOneDegreeColorProperty + LabelEditorProperty,
                interString);
        properties.put(BelowOneDegreeColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                EquatorColorProperty,
                I18n.TOOLTIP,
                "Color of the Equator.");
        properties.put(EquatorColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                EquatorColorProperty,
                "Equator Line Color");
        properties.put(EquatorColorProperty + LabelEditorProperty, interString);
        properties.put(EquatorColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                DateLineColorProperty,
                I18n.TOOLTIP,
                "Color of the Date line.");
        properties.put(DateLineColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                DateLineColorProperty,
                "Date Line Color");
        properties.put(DateLineColorProperty + LabelEditorProperty, interString);
        properties.put(DateLineColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                SpecialLineColorProperty,
                I18n.TOOLTIP,
                "Color of Tropic of Cancer, Capricorn lines.");
        properties.put(SpecialLineColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                SpecialLineColorProperty,
                "Special Line Color");
        properties.put(SpecialLineColorProperty + LabelEditorProperty,
                interString);
        properties.put(SpecialLineColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                TextColorProperty,
                I18n.TOOLTIP,
                "Color of the line label text.");
        properties.put(TextColorProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                TextColorProperty,
                "Text Color");
        properties.put(TextColorProperty + LabelEditorProperty, interString);
        properties.put(TextColorProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                ThresholdProperty,
                I18n.TOOLTIP,
                "The number of lines showing before finer grain lines appear.");
        properties.put(ThresholdProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                ThresholdProperty,
                "Line Threshold");
        properties.put(ThresholdProperty + LabelEditorProperty, interString);

        interString = i18n.get(GraticuleLayer.class,
                ShowOneAndFiveProperty,
                I18n.TOOLTIP,
                "Show the one and five degree lines.");
        properties.put(ShowOneAndFiveProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                ShowOneAndFiveProperty,
                "Show 1 and 5 Lines");
        properties.put(ShowOneAndFiveProperty + LabelEditorProperty,
                interString);
        properties.put(ShowOneAndFiveProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.TrueFalsePropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                ShowBelowOneProperty,
                I18n.TOOLTIP,
                "Show the one and five degree lines.");
        properties.put(ShowBelowOneProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                ShowBelowOneProperty,
                "Show Sub-1 Lines");
        properties.put(ShowBelowOneProperty + LabelEditorProperty, interString);
        properties.put(ShowBelowOneProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.TrueFalsePropertyEditor");

        interString = i18n.get(GraticuleLayer.class,
                ShowRulerProperty,
                I18n.TOOLTIP,
                "Show the line label text.");
        properties.put(ShowRulerProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                ShowRulerProperty,
                "Show Labels");
        properties.put(ShowRulerProperty + LabelEditorProperty, interString);
        properties.put(ShowRulerProperty + ScopedEditorProperty,
                "com.bbn.openmap.util.propertyEditor.TrueFalsePropertyEditor");

        //DNA
        interString = i18n.get(GraticuleLayer.class,
                FontSizeProperty,
                I18n.TOOLTIP,
                "The size of the font, in points, of the line labels.");
        properties.put(FontSizeProperty, interString);
        interString = i18n.get(GraticuleLayer.class,
                FontSizeProperty,
                "Label Font Size");
        properties.put(FontSizeProperty + LabelEditorProperty, interString);
        //DNA
        return properties;
    }

    /**
     * Implementing the ProjectionPainter interface.
     */
    public synchronized void renderDataForProjection(Projection proj,
                                                     java.awt.Graphics g) {
        if (proj == null) {
            Debug.error("GraticuleLayer.renderDataForProjection: null projection!");
            return;
        } else if (!proj.equals(getProjection())) {
            setProjection(proj.makeClone());
            // Figure out which line type to use
            if (proj instanceof Cylindrical)
                boxy = true;
            else
                boxy = false;

            setList(constructGraticuleLines());
        }
        paint(g);
    }

    /**
     * Invoked when the projection has changed or this Layer has been
     * added to the MapBean.
     * <p>
     * Perform some extra checks to see if reprojection of the
     * graphics is really necessary.
     *
     * @param e ProjectionEvent
     * 
     */
    public void projectionChanged(ProjectionEvent e) {

        // extract the projection and check to see if it's really
        // different.
        // if it isn't then we don't need to do all the work again,
        // just
        // repaint.
        Projection proj = setProjection(e);
        if (proj == null) {
            repaint();
            return;
        }

        // Figure out which line type to use
        if (proj instanceof Cylindrical)
            boxy = true;
        else
            boxy = false;

        setList(null);
        doPrepare();
    }

    /**
     * Creates the OMGraphic list with graticule lines.
     */
    public synchronized OMGraphicList prepare() {
        return constructGraticuleLines();
    }

    /**
     * Create the graticule lines.
     * <p>
     * NOTES:
     * <ul>
     * <li>Currently graticule lines are hardcoded to 10 degree
     * intervals.
     * <li>No thought has been given to clipping based on the view
     * rectangle. For non-boxy projections performance may be degraded
     * at very large scales. (But we make up for this by running the
     * task in its own thread to support liveness).
     * </ul>
     *
     * @return OMGraphicList new graphic list
     */
    protected OMGraphicList constructGraticuleLines() {

        OMGraphicList newgraphics = new OMGraphicList(20);
        // Lets figure out which lines should be painted...
        Projection projection = getProjection();

        if (projection == null) {
            return newgraphics;
        }
        tenDegreeLines = null;

        if (showOneAndFiveLines || showRuler || showBelowOneLines) {

            float left = projection.getUpperLeft().getLongitude();
            float right = projection.getLowerRight().getLongitude();
            float up = projection.getUpperLeft().getLatitude();
            float down = projection.getLowerRight().getLatitude();

            if (up > 80.0f)
                up = 80.0f;
            if (down > 80.0f)
                down = 80f; // unlikely
            if (up < -80.0f)
                up = -80.0f; // unlikely
            if (down < -80)
                down = -80.0f;

            int showWhichLines = evaluateSpacing(up, down, left, right);

            // Find out whether we need to do one or two queries,
            // depending on if we're straddling the dateline.
            if ((left > 0 && right < 0) || (left > right)
                    || (Math.abs(left - right) < 1)) {
                // Test to draw the ones and fives, which will also do
                // the labels.

                if (showWhichLines != SHOW_TENS) {
                    newgraphics.add(constructGraticuleLines(up,
                            down,
                            left,
                            180.0f,
                            showWhichLines));
                    newgraphics.add(constructGraticuleLines(up,
                            down,
                            -180.0f,
                            right,
                            showWhichLines));
                } else if (showRuler) { // Just do the labels for the
                    // tens lines
                    newgraphics.add(constructTensLabels(up,
                            down,
                            left,
                            180.0f,
                            true));
                    newgraphics.add(constructTensLabels(up,
                            down,
                            -180.0f,
                            right,
                            false));
                }
            } else {
                // Test to draw the ones and fives, which will also do
                // the labels.
                if (showWhichLines != SHOW_TENS) {
                    newgraphics = constructGraticuleLines(up,
                            down,
                            left,
                            right,
                            showWhichLines);
                } else if (showRuler) { // Just do the labels for the
                    // tens lines
                    newgraphics.add(constructTensLabels(up,
                            down,
                            left,
                            right,
                            true));
                }
            }
        }

        OMGraphicList list;
        if (tenDegreeLines == null) {
            list = constructTenDegreeLines();
            tenDegreeLines = list;
        } else {
            synchronized (tenDegreeLines) {
                setLineTypeAndProject(tenDegreeLines,
                        boxy ? OMGraphic.LINETYPE_STRAIGHT
                                : OMGraphic.LINETYPE_RHUMB);
            }
        }
        if (markerLines == null) {
            list = constructMarkerLines();
            markerLines = list;
        } else {
            synchronized (markerLines) {
                setLineTypeAndProject(markerLines,
                        boxy ? OMGraphic.LINETYPE_STRAIGHT
                                : OMGraphic.LINETYPE_RHUMB);
            }
        }

        newgraphics.add(markerLines);
        newgraphics.add(tenDegreeLines);

        if (Debug.debugging("graticule")) {
            Debug.output("GraticuleLayer.constructGraticuleLines(): "
                    + "constructed " + newgraphics.size() + " graticule lines");
        }

        return newgraphics;
    }

    /**
     * Figure out which graticule lines should be drawn based on the
     * treshold set in the layer, and the coordinates of the screen.
     * Method checks for crossing of the dateline, but still assumes
     * that the up and down latitude coordinates are less than
     * abs(+/-80). This is because the projection shouldn't give
     * anything above 90 degrees, and we limit the lines to less than
     * 80..
     *
     * @param up northern latitude corrdinate, in decimal degrees,
     * @param down southern latitude coordinate, in decimal degrees.
     * @param left western longitude coordinate, in decimal degrees,
     * @param right eastern longitude coordinate, in decimal degrees.
     * @return which lines should be shown, either SHOW_TENS,
     *         SHOW_FIVES and SHOW_ONES.
     */
    protected int evaluateSpacing(float up, float down, float left, float right) {
        int ret = SHOW_TENS;

        // Set the flag for when labels are wanted, but not the 1 and
        // 5 lines;
        if (!showOneAndFiveLines && !showBelowOneLines) {
            return ret;
        }

        // Find the north - south difference
        float nsdiff = up - down;
        // And the east - west difference
        float ewdiff;
        // Check for straddling the dateline -west is positive while
        // right is negative, or, in a big picture view, the west is
        // positive, east is positive, and western hemisphere is
        // between them.
        if ((left > 0 && right < 0) || (left > right)
                || (Math.abs(left - right) < 1)) {
            ewdiff = (180.0f - left) + (right + 180.0f);
        } else {
            ewdiff = right - left;
        }

        // And use the lesser of the two.
        float diff = (nsdiff < ewdiff) ? nsdiff : ewdiff;
        // number of 10 degree lines
        if ((diff / 10) <= (float) threshold)
            ret = SHOW_FIVES;
        // number of five degree lines
        if ((diff / 5) <= (float) threshold)
            ret = SHOW_ONES;

        return ret;
    }

    /**
     * Construct the five degree and one degree graticule lines,
     * depending on the showWhichLines setting. Assumes that the
     * coordinates passed in do not cross the dateline, and that the
     * up is not greater than 80 and that the south is not less than
     * -80.
     *
     * @param up northern latitude corrdinate, in decimal degrees,
     * @param down southern latitude coordinate, in decimal degrees.
     * @param left western longitude coordinate, in decimal degrees,
     * @param right eastern longitude coordinate, in decimal degrees.
     * @param showWhichLines indicator for which level of lines should
     *        be included, either SHOW_FIVES or SHOW_ONES. SHOW_TENS
     *        could be there, too, but then we wouldn't do anything.
     */
    protected OMGraphicList constructGraticuleLines(float up, float down,
                                                    float left, float right,
                                                    int showWhichLines) {
        OMGraphicList lines = new OMGraphicList();

        // Set the line limits for the lat/lon lines...
        int north = (int) Math.ceil(up);
        if (north > 80)
            north = 80;

        int south = (int) Math.floor(down);
        south -= (south % 10); // Push down to the lowest 10 degree
        // line.
        // for neg numbers, Mod raised it, lower it again. Also
        // handle straddling the equator.
        if ((south < 0 && south > -80) || south == 0)
            south -= 10;

        int west = (int) Math.floor(left);
        west -= (west % 10);
        // for neg numbers, Mod raised it, lower it again. Also
        // handle straddling the prime meridian.
        if ((west < 0 && west > -180) || west == 0)
            west -= 10;

        int east = (int) Math.ceil(right);
        if (east > 180)
            east = 180;

        int stepSize;
        // Choose how far apart the lines will be.
        stepSize = ((showWhichLines == SHOW_ONES) ? 1 : 5);
        float[] llp;
        OMPoly currentLine;
        OMText currentText;

        // For calculating text locations
        java.awt.Point point;
        LatLonPoint llpoint;

        Projection projection = getProjection();

        // generate other parallels of latitude be creating series
        // of polylines
        for (int i = south; i < north; i += stepSize) {
            float lat = (float) i;
            // generate parallel of latitude North/South of the
            // equator
            if (west < 0 && east > 0) {
                llp = new float[6];
                llp[2] = lat;
                llp[3] = 0f;
                llp[4] = lat;
                llp[5] = east;
            } else {
                llp = new float[4];
                llp[2] = lat;
                llp[3] = east;
            }
            llp[0] = lat;
            llp[1] = west;

            // Do not duplicate the 10 degree line.
            if ((lat % 10) != 0) {
                currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES, boxy ? OMGraphic.LINETYPE_STRAIGHT
                        : OMGraphic.LINETYPE_RHUMB);
                if ((lat % 5) == 0) {
                    currentLine.setLinePaint(fiveDegreeColor);
                } else {
                    currentLine.setLinePaint(oneDegreeColor);
                }
                lines.addOMGraphic(currentLine);
            }

            if (showRuler && (lat % 2) == 0) {
                if (boxy) {
                    point = projection.forward(lat, west);
                    point.x = 0;
                    llpoint = projection.inverse(point);
                } else {
                    llpoint = new LatLonPoint(lat, west);
                    while (projection.forward(llpoint).x < 0) {
                        llpoint.setLongitude(llpoint.getLongitude() + stepSize);
                    }
                }

                currentText = new OMText(llpoint.getLatitude(), llpoint.getLongitude(),
                // Move them up a little
                        (int) 2, (int) -2, Integer.toString((int) lat), font, OMText.JUSTIFY_LEFT);
                currentText.setLinePaint(textColor);
                lines.addOMGraphic(currentText);
            }
        }

        // generate lines of longitude
        for (int i = west; i < east; i += stepSize) {
            float lon = (float) i;

            if (north < 0 && south > 0) {
                llp = new float[6];
                llp[2] = 0f;
                llp[3] = lon;
                llp[4] = south;
                llp[5] = lon;
            } else {
                llp = new float[4];
                llp[2] = south;
                llp[3] = lon;
            }
            llp[0] = north;
            llp[1] = lon;

            if ((lon % 10) != 0) {
                currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES, boxy ? OMGraphic.LINETYPE_STRAIGHT
                        : OMGraphic.LINETYPE_GREATCIRCLE);
                if ((lon % 5) == 0) {
                    currentLine.setLinePaint(fiveDegreeColor);
                } else {
                    currentLine.setLinePaint(oneDegreeColor);
                }
                lines.addOMGraphic(currentLine);
            }

            if (showRuler && (lon % 2) == 0) {
                if (boxy) {
                    point = projection.forward(south, lon);
                    point.y = projection.getHeight();
                    llpoint = projection.inverse(point);
                } else {
                    llpoint = new LatLonPoint(south, lon);
                    while (projection.forward(llpoint).y > projection.getHeight()) {
                        llpoint.setLatitude(llpoint.getLatitude() + stepSize);
                    }
                }

                currentText = new OMText(llpoint.getLatitude(), llpoint.getLongitude(),
                // Move them up a little
                        (int) 2, (int) -5, Integer.toString((int) lon), font, OMText.JUSTIFY_CENTER);
                currentText.setLinePaint(textColor);
                lines.addOMGraphic(currentText);

            }
        }

        if (Debug.debugging("graticule")) {
            Debug.output("GraticuleLayer.constructTenDegreeLines(): "
                    + "constructed " + lines.size() + " graticule lines");
        }
        lines.generate(projection);
        return lines;
    }

    /** Create the ten degree lines. */
    protected OMGraphicList constructTenDegreeLines() {

        OMGraphicList lines = new OMGraphicList(3);
        OMPoly currentLine;

        // generate other parallels of latitude by creating series
        // of polylines
        for (int i = 1; i <= 8; i++) {
            for (int j = -1; j < 2; j += 2) {
                float lat = (float) (10 * i * j);
                // generate parallel of latitude North/South of the
                // equator
                float[] llp = { lat, -180f, lat, -90f, lat, 0f, lat, 90f, lat,
                        180f };
                currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES, boxy ? OMGraphic.LINETYPE_STRAIGHT
                        : OMGraphic.LINETYPE_RHUMB);
                currentLine.setLinePaint(tenDegreeColor);
                lines.addOMGraphic(currentLine);
            }
        }

        // generate lines of longitude
        for (int i = 1; i < 18; i++) {
            for (int j = -1; j < 2; j += 2) {
                float lon = (float) (10 * i * j);
                //not quite 90.0 for beautification reasons.
                float[] llp = { 80f, lon, 0f, lon, -80f, lon };
                if (MoreMath.approximately_equal(Math.abs(lon), 90f, 0.001f)) {
                    llp[0] = 90f;
                    llp[4] = -90f;
                }
                currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES, boxy ? OMGraphic.LINETYPE_STRAIGHT
                        : OMGraphic.LINETYPE_GREATCIRCLE);
                currentLine.setLinePaint(tenDegreeColor);
                lines.addOMGraphic(currentLine);
            }
        }

        if (Debug.debugging("graticule")) {
            Debug.output("GraticuleLayer.constructTenDegreeLines(): "
                    + "constructed " + lines.size() + " graticule lines");
        }
        lines.generate(getProjection());
        return lines;
    }

    /**
     * Constructs the labels for the tens lines. Called from within
     * the constructGraticuleLines if the showRuler variable is true.
     * Usually called only if the ones and fives lines are not being
     * drawn.
     *
     * @param up northern latitude corrdinate, in decimal degrees,
     * @param down southern latitude coordinate, in decimal degrees.
     * @param left western longitude coordinate, in decimal degrees,
     * @param right eastern longitude coordinate, in decimal degrees.
     * @param doLats do the latitude labels if true.
     * @return OMGraphicList of labels.
     */
    protected OMGraphicList constructTensLabels(float up, float down,
                                                float left, float right,
                                                boolean doLats) {

        OMGraphicList labels = new OMGraphicList();

        // Set the line limits for the lat/lon lines...
        int north = (int) Math.ceil(up);
        if (north > 80)
            north = 80;

        int south = (int) Math.floor(down);
        south -= (south % 10); // Push down to the lowest 10 degree
        // line.
        // for neg numbers, Mod raised it, lower it again
        if ((south < 0 && south > -70) || south == 0) {
            south -= 10;
        }

        int west = (int) Math.floor(left);
        west -= (west % 10);
        // for neg numbers, Mod raised it, lower it again
        if ((west < 0 && west > -170) || west == 0) {
            west -= 10;
        }

        int east = (int) Math.ceil(right);
        if (east > 180)
            east = 180;

        int stepSize = 10;
        OMText currentText;

        // For calculating text locations
        java.awt.Point point;
        LatLonPoint llpoint;
        Projection projection = getProjection();

        if (doLats) {

            // generate other parallels of latitude be creating series
            // of labels
            for (int i = south; i < north; i += stepSize) {
                float lat = (float) i;

                if ((lat % 2) == 0) {
                    if (boxy) {
                        point = projection.forward(lat, west);
                        point.x = 0;
                        llpoint = projection.inverse(point);
                    } else {
                        llpoint = new LatLonPoint(lat, west);
                        while (projection.forward(llpoint).x < 0) {
                            llpoint.setLongitude(llpoint.getLongitude()
                                    + stepSize);
                        }
                    }

                    currentText = new OMText(llpoint.getLatitude(), llpoint.getLongitude(), (int) 2, (int) -2, // Move
                            // them
                            // up a
                            // little
                            Integer.toString((int) lat), font, OMText.JUSTIFY_LEFT);
                    currentText.setLinePaint(textColor);
                    labels.addOMGraphic(currentText);
                }
            }
        }

        // generate labels of longitude
        for (int i = west; i < east; i += stepSize) {
            float lon = (float) i;

            if ((lon % 2) == 0) {
                if (boxy) {
                    point = projection.forward(south, lon);
                    point.y = projection.getHeight();
                    llpoint = projection.inverse(point);
                } else {
                    llpoint = new LatLonPoint(south, lon);
                    while (projection.forward(llpoint).y > projection.getHeight()) {
                        llpoint.setLatitude(llpoint.getLatitude() + stepSize);
                    }
                }

                currentText = new OMText(llpoint.getLatitude(), llpoint.getLongitude(),
                // Move them up a little
                        (int) 2, (int) -5, Integer.toString((int) lon), font, OMText.JUSTIFY_CENTER);
                currentText.setLinePaint(textColor);
                labels.addOMGraphic(currentText);

            }
        }

        if (Debug.debugging("graticule")) {
            Debug.output("GraticuleLayer.constructTensLabels(): "
                    + "constructed " + labels.size() + " graticule labels");
        }
        labels.generate(projection);
        return labels;
    }

    /** Constructs the Dateline and Prime Meridian lines. */
    protected OMGraphicList constructMarkerLines() {

        OMGraphicList lines = new OMGraphicList(3);
        OMPoly currentLine;

        // generate Prime Meridian and Dateline
        for (int j = 0; j < 360; j += 180) {
            float lon = (float) j;
            float[] llp = { 90f, lon, 0f, lon, -90f, lon };
            currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES, boxy ? OMGraphic.LINETYPE_STRAIGHT
                    : OMGraphic.LINETYPE_GREATCIRCLE);
            currentLine.setLinePaint(dateLineColor);
            lines.addOMGraphic(currentLine);
        }

        // equator
        float[] llp = { 0f, -180f, 0f, -90f, 0f, 0f, 0f, 90f, 0f, 180f };
        // polyline
        currentLine = new OMPoly(llp, OMGraphic.DECIMAL_DEGREES, boxy ? OMGraphic.LINETYPE_STRAIGHT
                : OMGraphic.LINETYPE_GREATCIRCLE);
        currentLine.setLinePaint(equatorColor);
        lines.addOMGraphic(currentLine);

        if (Debug.debugging("graticule")) {
            Debug.output("GraticuleLayer.constructMarkerLines(): "
                    + "constructed " + lines.size() + " graticule lines");
        }
        lines.generate(getProjection());
        return lines;
    }

    /**
     * Take a graphic list, and set all the items on the list to the
     * line type specified, and project them into the current
     * projection.
     *
     * @param list the list containing the lines to change.
     * @param lineType the line type to cahnge the lines to.
     */
    protected void setLineTypeAndProject(OMGraphicList list, int lineType) {
        int size = list.size();
        OMGraphic graphic;
        for (int i = 0; i < size; i++) {
            graphic = list.getOMGraphicAt(i);
            graphic.setLineType(lineType);
            graphic.generate(getProjection());
        }
    }

    //----------------------------------------------------------------------
    // GUI
    //----------------------------------------------------------------------

    /** The user interface palette for the DTED layer. */
    protected Box palette = null;

    /** Creates the interface palette. */
    public java.awt.Component getGUI() {

        if (palette == null) {
            if (Debug.debugging("graticule"))
                Debug.output("GraticuleLayer: creating Graticule Palette.");

            palette = Box.createVerticalBox();

            JPanel layerPanel = PaletteHelper.createPaletteJPanel(i18n.get(GraticuleLayer.class,
                    "layerPanel",
                    "Graticule Layer Options"));

            ActionListener al = new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    String ac = e.getActionCommand();

                    if (ac.equalsIgnoreCase(ShowRulerProperty)) {
                        JCheckBox jcb = (JCheckBox) e.getSource();
                        showRuler = jcb.isSelected();
                    } else if (ac.equalsIgnoreCase(ShowOneAndFiveProperty)) {
                        JCheckBox jcb = (JCheckBox) e.getSource();
                        showOneAndFiveLines = jcb.isSelected();
                    } else {
                        Debug.error("Unknown action command \"" + ac
                                + "\" in GraticuleLayer.actionPerformed().");
                    }
                }
            };

            showRulerButton = new JCheckBox(i18n.get(GraticuleLayer.class,
                    "showRulerButton",
                    "Show Lat/Lon Labels"), showRuler);
            showRulerButton.addActionListener(al);
            showRulerButton.setActionCommand(ShowRulerProperty);

            show15Button = new JCheckBox(i18n.get(GraticuleLayer.class,
                    "show15Button",
                    "Show 1, 5 Degree Lines"), showOneAndFiveLines);
            show15Button.addActionListener(al);
            show15Button.setActionCommand(ShowOneAndFiveProperty);

//            showBelow1Button = new JCheckBox(i18n.get(GraticuleLayer.class,
//                    "showSub1Button",
//                    "Show Sub-1 Degree Lines"), showBelowOneLines);
//            showBelow1Button.addActionListener(al);
//            showBelow1Button.setActionCommand(ShowBelowOneProperty);

            layerPanel.add(showRulerButton);
            layerPanel.add(show15Button);
//            layerPanel.add(showBelow1Button);
            palette.add(layerPanel);

            JPanel subbox3 = new JPanel(new GridLayout(0, 1));

            JButton setProperties = new JButton(i18n.get(GraticuleLayer.class,
                    "setProperties",
                    "Preferences"));
            setProperties.setActionCommand(DisplayPropertiesCmd);
            setProperties.addActionListener(this);
            subbox3.add(setProperties);

            JButton redraw = new JButton(i18n.get(GraticuleLayer.class,
                    "redraw",
                    "Redraw Graticule Layer"));
            redraw.setActionCommand(RedrawCmd);
            redraw.addActionListener(this);
            subbox3.add(redraw);
            palette.add(subbox3);
        }
        return palette;
    }

    //----------------------------------------------------------------------

    // ActionListener interface implementation
    //----------------------------------------------------------------------

    /**
     * Used just for the redraw button.
     */
    public void actionPerformed(ActionEvent e) {
        super.actionPerformed(e);
        String command = e.getActionCommand();

        if (command == RedrawCmd) {
            //redrawbutton
            if (isVisible()) {
                doPrepare();
            }
        }
    }

}
TOP

Related Classes of com.bbn.openmap.layer.GraticuleLayer

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.