Android Open Source - android-openmap-framework M G R S Point






From Project

Back to project page android-openmap-framework.

License

The source code is released under:

Apache License

If you think the Android project android-openmap-framework listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

// **********************************************************************
//// www.jav  a2s.com
// <copyright>
//
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
//
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************

package com.jwetherell.openmap.common;

public class MGRSPoint extends ZonedUTMPoint {

    /**
     * UTM zones are grouped, and assigned to one of a group of 6 sets.
     */
    private final static int NUM_100K_SETS = 6;

    /**
     * The column letters (for easting) of the lower left value, per set.
     */
    private final static int[] SET_ORIGIN_COLUMN_LETTERS = { 'A', 'J', 'S', 'A', 'J', 'S' };

    /**
     * The row letters (for northing) of the lower left value, per set.
     */
    private final static int[] SET_ORIGIN_ROW_LETTERS = { 'A', 'F', 'A', 'F', 'A', 'F' };

    public final static int ACCURACY_1_METER = 5;
    public final static int ACCURACY_10_METER = 4;
    public final static int ACCURACY_100_METER = 3;
    public final static int ACCURACY_1000_METER = 2;
    public final static int ACCURACY_10000_METER = 1;

    /** The set origin column letters to use. */
    private int[] originColumnLetters = SET_ORIGIN_COLUMN_LETTERS;
    /** The set origin row letters to use. */
    private int[] originRowLetters = SET_ORIGIN_ROW_LETTERS;

    private final static int A = 'A';
    private final static int I = 'I';
    private final static int O = 'O';
    private final static int V = 'V';
    private final static int Z = 'Z';

    /** The String holding the MGRS coordinate value. */
    private String mgrs = null;

    /**
     * Controls the number of digits that the MGRS coordinate will have, which
     * directly affects the accuracy of the coordinate. Default is
     * ACCURACY_1_METER, which indicates that MGRS coordinates will have 10
     * digits (5 easting, 5 northing) after the 100k two letter code, indicating
     * 1 meter resolution.
     */
    private int accuracy = ACCURACY_1_METER;

    public MGRSPoint() {
    }

    public MGRSPoint(LatLonPoint llpoint) {
        this(llpoint, Ellipsoid.WGS_84);
    }

    public MGRSPoint(LatLonPoint llpoint, Ellipsoid ellip) {
        this();
        LLtoMGRS(llpoint, ellip, this);
    }

    public MGRSPoint(String mgrsString) throws NumberFormatException {
        this();
        setMGRS(mgrsString);
    }

    public MGRSPoint(double northing, double easting, int zoneNumber, char zoneLetter) {
        super(northing, easting, zoneNumber, zoneLetter);
    }

    /**
     * Set the MGRS value for this Point. Will be decoded, and the UTM values
     * figured out. You can call toLatLonPoint() to translate it to lat/lon
     * decimal degrees.
     */
    public void setMGRS(String mgrsString) throws NumberFormatException {
        try {
            mgrs = mgrsString.toUpperCase(); // Just to make sure.
            decode(mgrs);
        } catch (StringIndexOutOfBoundsException sioobe) {
            throw new NumberFormatException("MGRSPoint has bad string: " + mgrsString);
        } catch (NullPointerException npe) {
            // Blow off
        }
    }

    /**
     * Set the UTM parameters from a MGRS string.
     * 
     * @param mgrsString
     *            an UPPERCASE coordinate string is expected.
     */
    protected void decode(String mgrsString) throws NumberFormatException {
        if (mgrsString == null || mgrsString.length() == 0) {
            throw new NumberFormatException("MGRSPoint coverting from nothing");
        }

        // Ensure an upper-case string
        mgrsString = mgrsString.toUpperCase();

        int length = mgrsString.length();

        String hunK = null;
        StringBuffer sb = new StringBuffer();
        char testChar;
        int i = 0;

        // get Zone number
        while (!Character.isLetter(testChar = mgrsString.charAt(i))) {
            if (i >= 2) {
                throw new NumberFormatException("MGRSPoint bad conversion from: " + mgrsString + ", first two characters need to be a number between 1-60.");
            }
            sb.append(testChar);
            i++;
        }

        zone_number = Integer.parseInt(sb.toString());

        if (zone_number < 1 || zone_number > 60) {
            throw new NumberFormatException("MGRSPoint bad conversion from: " + mgrsString + ", first two characters need to be a number between 1-60.");
        }

        if (i == 0 || i + 3 > length) {
            // A good MGRS string has to be 4-5 digits long,
            // ##AAA/#AAA at least.
            throw new NumberFormatException("MGRSPoint bad conversion from: " + mgrsString + ", MGRS string must be at least 4-5 digits long");
        }

        zone_letter = mgrsString.charAt(i++);

        // Should we check the zone letter here? Why not.
        if (zone_letter <= 'A' || zone_letter == 'B' || zone_letter == 'Y' || zone_letter >= 'Z' || zone_letter == 'I' || zone_letter == 'O') {
            throw new NumberFormatException("MGRSPoint zone letter " + zone_letter + " not handled: " + mgrsString);
        }

        hunK = mgrsString.substring(i, i += 2);

        // Validate, check the zone, make sure each letter is between A-Z, not I
        // or O
        char char1 = hunK.charAt(0);
        char char2 = hunK.charAt(1);
        if (char1 < 'A' || char2 < 'A' || char1 > 'Z' || char2 > 'Z' || char1 == 'I' || char2 == 'I' || char1 == 'O' || char2 == 'O') {
            throw new NumberFormatException("MGRSPoint bad conversion from " + mgrsString + ", invalid 100k designator");
        }

        int set = get100kSetForZone(zone_number);

        float east100k = getEastingFromChar(char1, set);
        float north100k = getNorthingFromChar(char2, set);

        // We have a bug where the northing may be 2000000 too low.
        // How do we know when to roll over?

        while (north100k < getMinNorthing(zone_letter)) {
            north100k += 2000000;
        }

        // calculate the char index for easting/northing separator
        int remainder = length - i;

        if (remainder % 2 != 0) {
            throw new NumberFormatException(
                    "MGRSPoint has to have an even number \nof digits after the zone letter and two 100km letters - front \nhalf for easting meters, second half for \nnorthing meters"
                            + mgrsString);
        }

        int sep = remainder / 2;

        float sepEasting = 0f;
        float sepNorthing = 0f;

        if (sep > 0) {
            float accuracyBonus = 100000f / (float) Math.pow(10, sep);
            String sepEastingString = mgrsString.substring(i, i + sep);
            sepEasting = Float.parseFloat(sepEastingString) * accuracyBonus;
            String sepNorthingString = mgrsString.substring(i + sep);
            sepNorthing = Float.parseFloat(sepNorthingString) * accuracyBonus;
        }

        easting = sepEasting + east100k;
        northing = sepNorthing + north100k;
    }

    /**
     * Given the first letter from a two-letter MGRS 100k zone, and given the
     * MGRS table set for the zone number, figure out the easting value that
     * should be added to the other, secondary easting value.
     */
    protected float getEastingFromChar(char e, int set) {
        int baseCol[] = getOriginColumnLetters();
        // colOrigin is the letter at the origin of the set for the
        // column
        int curCol = baseCol[set - 1];
        float eastingValue = 100000f;
        boolean rewindMarker = false;

        while (curCol != e) {
            curCol++;
            if (curCol == I) curCol++;
            if (curCol == O) curCol++;
            if (curCol > Z) {
                if (rewindMarker) {
                    throw new NumberFormatException("Bad character: " + e);
                }
                curCol = A;
                rewindMarker = true;
            }
            eastingValue += 100000f;
        }

        return eastingValue;
    }

    /**
     * Given the second letter from a two-letter MGRS 100k zone, and given the
     * MGRS table set for the zone number, figure out the northing value that
     * should be added to the other, secondary northing value. You have to
     * remember that Northings are determined from the equator, and the vertical
     * cycle of letters mean a 2000000 additional northing meters. This happens
     * approx. every 18 degrees of latitude. This method does *NOT* count any
     * additional northings. You have to figure out how many 2000000 meters need
     * to be added for the zone letter of the MGRS coordinate.
     * 
     * @param n
     *            second letter of the MGRS 100k zone
     * @param set
     *            the MGRS table set number, which is dependent on the UTM zone
     *            number.
     */
    protected float getNorthingFromChar(char n, int set) {

        if (n > 'V') {
            throw new NumberFormatException("MGRSPoint given invalid Northing " + n);
        }

        int baseRow[] = getOriginRowLetters();
        // rowOrigin is the letter at the origin of the set for the
        // column
        int curRow = baseRow[set - 1];
        float northingValue = 0f;
        boolean rewindMarker = false;

        while (curRow != n) {
            curRow++;
            if (curRow == I) curRow++;
            if (curRow == O) curRow++;
            // fixing a bug making whole application hang in this loop
            // when 'n' is a wrong character
            if (curRow > V) {
                if (rewindMarker) { // making sure that this loop ends
                    throw new NumberFormatException("Bad character: " + n);
                }
                curRow = A;
                rewindMarker = true;
            }
            northingValue += 100000f;
        }

        return northingValue;
    }

    /**
     * The function getMinNorthing returns the minimum northing value of a MGRS
     * zone.
     * 
     * portted from Geotrans' c Latitude_Band_Value structure table. zoneLetter
     * : MGRS zone (input)
     */
    protected float getMinNorthing(char zoneLetter) throws NumberFormatException {
        float northing;
        switch (zoneLetter) {
            case 'C':
                northing = 1100000.0f;
                break;
            case 'D':
                northing = 2000000.0f;
                break;
            case 'E':
                northing = 2800000.0f;
                break;
            case 'F':
                northing = 3700000.0f;
                break;
            case 'G':
                northing = 4600000.0f;
                break;
            case 'H':
                northing = 5500000.0f;
                break;
            case 'J':
                northing = 6400000.0f;
                break;
            case 'K':
                northing = 7300000.0f;
                break;
            case 'L':
                northing = 8200000.0f;
                break;
            case 'M':
                northing = 9100000.0f;
                break;
            case 'N':
                northing = 0.0f;
                break;
            case 'P':
                northing = 800000.0f;
                break;
            case 'Q':
                northing = 1700000.0f;
                break;
            case 'R':
                northing = 2600000.0f;
                break;
            case 'S':
                northing = 3500000.0f;
                break;
            case 'T':
                northing = 4400000.0f;
                break;
            case 'U':
                northing = 5300000.0f;
                break;
            case 'V':
                northing = 6200000.0f;
                break;
            case 'W':
                northing = 7000000.0f;
                break;
            case 'X':
                northing = 7900000.0f;
                break;
            default:
                northing = -1.0f;
        }
        if (northing >= 0.0)
            return northing;
        // else
        throw new NumberFormatException("Invalid zone letter: " + zone_letter);
    }

    /**
     * Convert this MGRSPoint to a LatLonPoint, and assume a WGS_84 ellipsoid.
     */
    public LatLonPoint toLatLonPoint() {
        return toLatLonPoint(Ellipsoid.WGS_84, new LatLonPoint());
    }

    /**
     * Convert this MGRSPoint to a LatLonPoint, and use the given ellipsoid.
     */
    public LatLonPoint toLatLonPoint(Ellipsoid ellip) {
        return toLatLonPoint(ellip, new LatLonPoint());
    }

    /**
     * Fill in the given LatLonPoint with the converted values of this
     * MGRSPoint, and use the given ellipsoid.
     */
    public LatLonPoint toLatLonPoint(Ellipsoid ellip, LatLonPoint llpoint) {
        return MGRStoLL(this, ellip, llpoint);
    }

    /**
     * Create a LatLonPoint from a MGRSPoint.
     * 
     * @param mgrsp
     *            to convert.
     * @param ellip
     *            Ellipsoid for earth model.
     * @param llp
     *            a LatLonPoint to fill in values for. If null, a new
     *            LatLonPoint will be returned. If not null, the new values will
     *            be set in this object, and it will be returned.
     * @return LatLonPoint with values converted from MGRS coordinate.
     */
    public static LatLonPoint MGRStoLL(MGRSPoint mgrsp, Ellipsoid ellip, LatLonPoint llp) {
        return UTMtoLL(ellip, mgrsp.northing, mgrsp.easting, mgrsp.zone_number, MGRSPoint.MGRSZoneToUTMZone(mgrsp.zone_letter), llp);
    }

    /**
     * Converts a LatLonPoint to a MGRS Point, assuming the WGS_84 ellipsoid.
     * 
     * @return MGRSPoint, or null if something bad happened.
     */
    public static MGRSPoint LLtoMGRS(LatLonPoint llpoint) {
        return LLtoMGRS(llpoint, Ellipsoid.WGS_84, new MGRSPoint());
    }

    /**
     * Create a MGRSPoint from a LatLonPoint.
     * 
     * @param llp
     *            LatLonPoint to convert.
     * @param ellip
     *            Ellipsoid for earth model.
     * @param mgrsp
     *            a MGRSPoint to fill in values for. If null, a new MGRSPoint
     *            will be returned. If not null, the new values will be set in
     *            this object, and it will be returned.
     * @return MGRSPoint with values converted from lat/lon.
     */
    public static MGRSPoint LLtoMGRS(LatLonPoint llp, Ellipsoid ellip, MGRSPoint mgrsp) {
        if (mgrsp == null) {
            mgrsp = new MGRSPoint();
        }

        // Calling LLtoUTM here results in N/S zone letters! wrong!
        mgrsp = (MGRSPoint) LLtoUTM(llp, ellip, mgrsp);
        // Need to add this to set the right letter for the latitude.
        mgrsp.zone_letter = mgrsp.getLetterDesignator(llp.getLatitude());
        mgrsp.resolve();
        return mgrsp;
    }

    /**
     * Convert MGRS zone letter to UTM zone letter, N or S.
     * 
     * @param mgrsZone
     * @return N of given zone is equal or larger than N, S otherwise.
     */
    public static char MGRSZoneToUTMZone(char mgrsZone) {
        if (Character.toUpperCase(mgrsZone) >= 'N')
            return 'N';
        // else
        return 'S';
    }

    /**
     * Method that provides a check for MGRS zone letters. Returns an uppercase
     * version of any valid letter passed in.
     */
    public char checkZone(char zone) {
        zone = Character.toUpperCase(zone);

        if (zone <= 'A' || zone == 'B' || zone == 'Y' || zone >= 'Z' || zone == 'I' || zone == 'O') {
            throw new NumberFormatException("Invalid MGRSPoint zone letter: " + zone);
        }

        return zone;
    }

    /**
     * Set the number of digits to use for easting and northing numbers in the
     * mgrs string, which reflects the accuracy of the coordinate. From 5 (1
     * meter) to 1 (10,000 meter).
     */
    public void setAccuracy(int value) {
        accuracy = value;
        mgrs = null;
    }

    public int getAccuracy() {
        return accuracy;
    }

    /**
     * Create the mgrs string based on the internal UTM settings, should be
     * called if the accuracy changes.
     * 
     * @param digitAccuracy
     *            The number of digits to use for the northing and easting
     *            numbers. 5 digits reflect a 1 meter accuracy, 4 - 10 meter, 3
     *            - 100 meter, 2 - 1000 meter, 1 - 10,000 meter.
     */
    public void resolve(int digitAccuracy) {
        setAccuracy(digitAccuracy);
        resolve();
    }

    /**
     * Create the mgrs string based on the internal UTM settings, using the
     * accuracy set in the MGRSPoint.
     */
    public void resolve() {
        if (zone_letter == 'Z') {
            mgrs = "Latitude limit exceeded";
        } else {
            StringBuffer sb = new StringBuffer(Integer.toString(zone_number)).append(zone_letter).append(get100kID(easting, northing, zone_number));
            StringBuffer seasting = new StringBuffer(Integer.toString((int) easting));
            StringBuffer snorthing = new StringBuffer(Integer.toString((int) northing));

            while (accuracy + 1 > seasting.length()) {
                seasting.insert(0, '0');
            }

            // We have to be careful here, the 100k values shouldn't
            // be
            // used for calculating stuff here.

            while (accuracy + 1 > snorthing.length()) {
                snorthing.insert(0, '0');
            }

            while (snorthing.length() > 6) {
                snorthing.deleteCharAt(0);
            }

            try {
                sb.append(seasting.substring(1, accuracy + 1)).append(snorthing.substring(1, accuracy + 1));

                mgrs = sb.toString();
            } catch (IndexOutOfBoundsException ioobe) {
                mgrs = null;
            }
        }
    }

    /**
     * Given a UTM zone number, figure out the MGRS 100K set it is in.
     */
    private int get100kSetForZone(int i) {
        int set = i % NUM_100K_SETS;
        if (set == 0) set = NUM_100K_SETS;
        return set;
    }

    /**
     * Provided so that extensions to this class can provide different origin
     * letters, in case of different ellipsoids. The int[] represents all of the
     * first letters in the bottom left corner of each set box, as shown in an
     * MGRS 100K box layout.
     */
    private int[] getOriginColumnLetters() {
        return originColumnLetters;
    }

    /**
     * Provided so that extensions to this class can provide different origin
     * letters, in case of different ellipsoids. The int[] represents all of the
     * second letters in the bottom left corner of each set box, as shown in an
     * MGRS 100K box layout.
     */
    private int[] getOriginRowLetters() {
        return originRowLetters;
    }

    /**
     * Get the two letter 100k designator for a given UTM easting, northing and
     * zone number value.
     */
    private String get100kID(double easting, double northing, int zone_number) {
        int set = get100kSetForZone(zone_number);
        int setColumn = ((int) easting / 100000);
        int setRow = ((int) northing / 100000) % 20;
        return get100kID(setColumn, setRow, set);
    }

    /**
     * Get the two-letter MGRS 100k designator given information translated from
     * the UTM northing, easting and zone number.
     * 
     * @param setColumn
     *            the column index as it relates to the MGRS 100k set
     *            spreadsheet, created from the UTM easting. Values are 1-8.
     * @param setRow
     *            the row index as it relates to the MGRS 100k set spreadsheet,
     *            created from the UTM northing value. Values are from 0-19.
     * @param set
     *            the set block, as it relates to the MGRS 100k set spreadsheet,
     *            created from the UTM zone. Values are from 1-60.
     * @return two letter MGRS 100k code.
     */
    private String get100kID(int setColumn, int setRow, int set) {
        int baseCol[] = getOriginColumnLetters();
        int baseRow[] = getOriginRowLetters();

        // colOrigin and rowOrigin are the letters at the origin of
        // the set
        int colOrigin = baseCol[set - 1];
        int rowOrigin = baseRow[set - 1];

        // colInt and rowInt are the letters to build to return
        int colInt = colOrigin + setColumn - 1;
        int rowInt = rowOrigin + setRow;
        boolean rollover = false;

        if (colInt > Z) {
            colInt = colInt - Z + A - 1;
            rollover = true;
        }

        if (colInt == I || (colOrigin < I && colInt > I) || ((colInt > I || colOrigin < I) && rollover)) {
            colInt++;
        }
        if (colInt == O || (colOrigin < O && colInt > O) || ((colInt > O || colOrigin < O) && rollover)) {
            colInt++;
            if (colInt == I) {
                colInt++;
            }
        }

        if (colInt > Z) {
            colInt = colInt - Z + A - 1;
        }

        if (rowInt > V) {
            rowInt = rowInt - V + A - 1;
            rollover = true;
        } else {
            rollover = false;
        }

        if (rowInt == I || (rowOrigin < I && rowInt > I) || ((rowInt > I || rowOrigin < I) && rollover)) {
            rowInt++;
        }

        if (rowInt == O || (rowOrigin < O && rowInt > O) || ((rowInt > O || rowOrigin < O) && rollover)) {
            rowInt++;
            if (rowInt == I) {
                rowInt++;
            }
        }

        if (rowInt > V) {
            rowInt = rowInt - V + A - 1;
        }

        String twoLetter = (char) colInt + "" + (char) rowInt;

        return twoLetter;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return mgrs;
    }
}




Java Source Code List

com.jwetherell.openmap.activity.CenteredMapActivity.java
com.jwetherell.openmap.activity.CustomMapActivity.java
com.jwetherell.openmap.activity.OpenMapDemo.java
com.jwetherell.openmap.activity.configure.ConfigureUser.java
com.jwetherell.openmap.common.Ellipsoid.java
com.jwetherell.openmap.common.GreatCircle.java
com.jwetherell.openmap.common.LatLonPoint.java
com.jwetherell.openmap.common.Length.java
com.jwetherell.openmap.common.MGRSPoint.java
com.jwetherell.openmap.common.MoreMath.java
com.jwetherell.openmap.common.Planet.java
com.jwetherell.openmap.common.ProjMath.java
com.jwetherell.openmap.common.UTMPoint.java
com.jwetherell.openmap.common.ZonedUTMPoint.java
com.jwetherell.openmap.data.MessageUtilities.java
com.jwetherell.openmap.data.UserData.java
com.jwetherell.openmap.overlay.HashMapItemizedOverlay.java
com.jwetherell.openmap.utilities.Utilities.java