All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.gov.nasa.worldwind.geom.coords.MGRSCoordConverter Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show newest version
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.geom.coords;

import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.globes.Globe;

/**
 * Converter used to translate MGRS coordinate strings to and from geodetic latitude and longitude.
 *
 * @author Patrick Murris
 * @version $Id: MGRSCoordConverter.java 1171 2013-02-11 21:45:02Z dcollins $
 * @see MGRSCoord
 */

/**
 * Ported to Java from the NGA GeoTrans mgrs.c and mgrs.h code. Contains routines to convert from Geodetic to MGRS and
 * the other direction.
 *
 * @author Garrett Headley, Patrick Murris
 */
class MGRSCoordConverter
{
    public static final int MGRS_NO_ERROR = 0;
    private static final int MGRS_LAT_ERROR = 0x0001;
    private static final int MGRS_LON_ERROR = 0x0002;
    public static final int MGRS_STRING_ERROR = 0x0004;
    private static final int MGRS_PRECISION_ERROR = 0x0008;
    private static final int MGRS_A_ERROR = 0x0010;
    private static final int MGRS_INV_F_ERROR = 0x0020;
    private static final int MGRS_EASTING_ERROR = 0x0040;
    private static final int MGRS_NORTHING_ERROR = 0x0080;
    private static final int MGRS_ZONE_ERROR = 0x0100;
    private static final int MGRS_HEMISPHERE_ERROR = 0x0200;
    private static final int MGRS_LAT_WARNING = 0x0400;
    private static final int MGRS_NOZONE_WARNING = 0x0800;
    private static final int MGRS_UTM_ERROR = 0x1000;
    private static final int MGRS_UPS_ERROR = 0x2000;

    private static final double PI = 3.14159265358979323;
    private static final double PI_OVER_2 = (PI / 2.0e0);
    private static final int MAX_PRECISION = 5;
    private static final double MIN_UTM_LAT = (-80 * PI) / 180.0;    // -80 degrees in radians
    private static final double MAX_UTM_LAT = (84 * PI) / 180.0;     // 84 degrees in radians
    public static final double DEG_TO_RAD = 0.017453292519943295;   // PI/180
    private static final double RAD_TO_DEG = 57.29577951308232087;   // 180/PI

    private static final double MIN_EAST_NORTH = 0;
    private static final double MAX_EAST_NORTH = 4000000;
    private static final double TWOMIL = 2000000;
    private static final double ONEHT = 100000;

    private static final String CLARKE_1866 = "CC";
    private static final String CLARKE_1880 = "CD";
    private static final String BESSEL_1841 = "BR";
    private static final String BESSEL_1841_NAMIBIA = "BN";

    // Ellipsoid parameters, default to WGS 84
    private double MGRS_a = 6378137.0;          // Semi-major axis of ellipsoid in meters
    private double MGRS_f = 1 / 298.257223563;  // Flattening of ellipsoid
    private double MGRS_recpf = 298.257223563;
    private String MGRS_Ellipsoid_Code = "WE";

    private Globe globe;
    private String MGRSString = "";
    private long ltr2_low_value;
    private long ltr2_high_value;       // this is only used for doing MGRS to xxx conversions.
    private double false_northing;
    private long lastLetter;
    private long last_error = MGRS_NO_ERROR;
    private double north, south, min_northing, northing_offset;  //smithjl added north_offset
    private double latitude;
    private double longitude;

    private static final int LETTER_A = 0;   /* ARRAY INDEX FOR LETTER A               */
    private static final int LETTER_B = 1;   /* ARRAY INDEX FOR LETTER B               */
    private static final int LETTER_C = 2;   /* ARRAY INDEX FOR LETTER C               */
    private static final int LETTER_D = 3;   /* ARRAY INDEX FOR LETTER D               */
    private static final int LETTER_E = 4;   /* ARRAY INDEX FOR LETTER E               */
    private static final int LETTER_F = 5;   /* ARRAY INDEX FOR LETTER E               */
    private static final int LETTER_G = 6;   /* ARRAY INDEX FOR LETTER H               */
    private static final int LETTER_H = 7;   /* ARRAY INDEX FOR LETTER H               */
    private static final int LETTER_I = 8;   /* ARRAY INDEX FOR LETTER I               */
    private static final int LETTER_J = 9;   /* ARRAY INDEX FOR LETTER J               */
    private static final int LETTER_K = 10;   /* ARRAY INDEX FOR LETTER J               */
    private static final int LETTER_L = 11;   /* ARRAY INDEX FOR LETTER L               */
    private static final int LETTER_M = 12;   /* ARRAY INDEX FOR LETTER M               */
    private static final int LETTER_N = 13;   /* ARRAY INDEX FOR LETTER N               */
    private static final int LETTER_O = 14;   /* ARRAY INDEX FOR LETTER O               */
    private static final int LETTER_P = 15;   /* ARRAY INDEX FOR LETTER P               */
    private static final int LETTER_Q = 16;   /* ARRAY INDEX FOR LETTER Q               */
    private static final int LETTER_R = 17;   /* ARRAY INDEX FOR LETTER R               */
    private static final int LETTER_S = 18;   /* ARRAY INDEX FOR LETTER S               */
    private static final int LETTER_T = 19;   /* ARRAY INDEX FOR LETTER S               */
    private static final int LETTER_U = 20;   /* ARRAY INDEX FOR LETTER U               */
    private static final int LETTER_V = 21;   /* ARRAY INDEX FOR LETTER V               */
    private static final int LETTER_W = 22;   /* ARRAY INDEX FOR LETTER W               */
    private static final int LETTER_X = 23;   /* ARRAY INDEX FOR LETTER X               */
    private static final int LETTER_Y = 24;   /* ARRAY INDEX FOR LETTER Y               */
    private static final int LETTER_Z = 25;   /* ARRAY INDEX FOR LETTER Z               */
    private static final int MGRS_LETTERS = 3;  /* NUMBER OF LETTERS IN MGRS              */

    private static final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    // UPS Constants are in the following order:
    //    long letter;            /* letter representing latitude band      */
    //    long ltr2_low_value;    /* 2nd letter range - high number         */
    //    long ltr2_high_value;   /* 2nd letter range - low number          */
    //    long ltr3_high_value;   /* 3rd letter range - high number (UPS)   */
    //    double false_easting;   /* False easting based on 2nd letter      */
    //    double false_northing;  /* False northing based on 3rd letter     */
    private static final long[][] upsConstants = {
        {LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000, 800000},
        {LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000, 800000},
        {LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000, 1300000},
        {LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000, 1300000}};

    // Latitude Band Constants are in the following order:
    //        long letter;            /* letter representing latitude band  */
    //        double min_northing;    /* minimum northing for latitude band */
    //        double north;           /* upper latitude for latitude band   */
    //        double south;           /* lower latitude for latitude band   */
    private static final double[][] latitudeBandConstants = {
        {LETTER_C, 1100000.0, -72.0, -80.5, 0.0},
        {LETTER_D, 2000000.0, -64.0, -72.0, 2000000.0},
        {LETTER_E, 2800000.0, -56.0, -64.0, 2000000.0},
        {LETTER_F, 3700000.0, -48.0, -56.0, 2000000.0},
        {LETTER_G, 4600000.0, -40.0, -48.0, 4000000.0},
        {LETTER_H, 5500000.0, -32.0, -40.0, 4000000.0},   //smithjl last column to table
        {LETTER_J, 6400000.0, -24.0, -32.0, 6000000.0},
        {LETTER_K, 7300000.0, -16.0, -24.0, 6000000.0},
        {LETTER_L, 8200000.0, -8.0, -16.0, 8000000.0},
        {LETTER_M, 9100000.0, 0.0, -8.0, 8000000.0},
        {LETTER_N, 0.0, 8.0, 0.0, 0.0},
        {LETTER_P, 800000.0, 16.0, 8.0, 0.0},
        {LETTER_Q, 1700000.0, 24.0, 16.0, 0.0},
        {LETTER_R, 2600000.0, 32.0, 24.0, 2000000.0},
        {LETTER_S, 3500000.0, 40.0, 32.0, 2000000.0},
        {LETTER_T, 4400000.0, 48.0, 40.0, 4000000.0},
        {LETTER_U, 5300000.0, 56.0, 48.0, 4000000.0},
        {LETTER_V, 6200000.0, 64.0, 56.0, 6000000.0},
        {LETTER_W, 7000000.0, 72.0, 64.0, 6000000.0},
        {LETTER_X, 7900000.0, 84.5, 72.0, 6000000.0}};

    private class MGRSComponents
    {
        private final int zone;
        private final int latitudeBand;
        private final int squareLetter1;
        private final int squareLetter2;
        private final double easting;
        private final double northing;
        private final int precision;

        public MGRSComponents(int zone, int latitudeBand, int squareLetter1, int squareLetter2,
            double easting, double northing, int precision)
        {
            this.zone = zone;
            this.latitudeBand = latitudeBand;
            this.squareLetter1 = squareLetter1;
            this.squareLetter2 = squareLetter2;
            this.easting = easting;
            this.northing = northing;
            this.precision = precision;
        }

        public String toString()
        {
            return "MGRS: " + zone + " " +
                alphabet.charAt(latitudeBand) + " " +
                alphabet.charAt(squareLetter1) + alphabet.charAt(squareLetter2) + " " +
                easting + " " +
                northing + " " +
                "(" + precision + ")";
        }
    }

    MGRSCoordConverter(Globe globe)
    {
        this.globe = globe;
        if (globe != null)
        {
            double a = globe.getEquatorialRadius();
            double f = (globe.getEquatorialRadius() - globe.getPolarRadius()) / globe.getEquatorialRadius();
            setMGRSParameters(a, f, MGRS_Ellipsoid_Code);
        }
    }

    /**
     * The function setMGRSParameters receives the ellipsoid parameters and sets the corresponding state variables. If
     * any errors occur, the error code(s) are returned by the function, otherwise MGRS_NO_ERROR is returned.
     *
     * @param mgrs_a        Semi-major axis of ellipsoid in meters
     * @param mgrs_f        Flattening of ellipsoid
     * @param ellipsoidCode 2-letter code for ellipsoid
     *
     * @return error code
     */
    public long setMGRSParameters(double mgrs_a, double mgrs_f, String ellipsoidCode)
    {
        if (mgrs_a <= 0.0)
            return MGRS_A_ERROR;

        if (mgrs_f == 0.0)
            return MGRS_INV_F_ERROR;
        double inv_f = 1 / mgrs_f;
        if (inv_f < 250 || inv_f > 350)
            return MGRS_INV_F_ERROR;

        MGRS_a = mgrs_a;
        MGRS_f = mgrs_f;
        MGRS_Ellipsoid_Code = ellipsoidCode;

        return MGRS_NO_ERROR;
    }

    /** @return Flattening of ellipsoid */
    public double getMGRS_f()
    {
        return MGRS_f;
    }

    /** @return Semi-major axis of ellipsoid in meters */
    public double getMGRS_a()
    {
        return MGRS_a;
    }

    /** @return Latitude band letter */
    private long getLastLetter()
    {
        return lastLetter;
    }

    /** @return 2-letter code for ellipsoid */
    public String getMGRS_Ellipsoid_Code()
    {
        return MGRS_Ellipsoid_Code;
    }

    /**
     * The function ConvertMGRSToGeodetic converts an MGRS coordinate string to Geodetic (latitude and longitude)
     * coordinates according to the current ellipsoid parameters.  If any errors occur, the error code(s) are returned
     * by the function, otherwise UTM_NO_ERROR is returned.
     *
     * @param MGRSString MGRS coordinate string.
     *
     * @return the error code.
     */
    public long convertMGRSToGeodetic(String MGRSString)
    {
        latitude = 0;
        longitude = 0;
        long error_code = checkZone(MGRSString);
        if (error_code == MGRS_NO_ERROR)
        {
            UTMCoord UTM = convertMGRSToUTM(MGRSString);
            if (UTM != null)
            {
                latitude = UTM.getLatitude().radians;
                longitude = UTM.getLongitude().radians;
            }
            else
                error_code = MGRS_UTM_ERROR;
        }
        else if (error_code == MGRS_NOZONE_WARNING)
        {
            // TODO: polar conversion
            UPSCoord UPS = convertMGRSToUPS(MGRSString);
            if (UPS != null)
            {
                latitude = UPS.getLatitude().radians;
                longitude = UPS.getLongitude().radians;
            }
            else
                error_code = MGRS_UPS_ERROR;
        }
        return (error_code);
    }

    public double getLatitude()
    {
        return latitude;
    }

    public double getLongitude()
    {
        return longitude;
    }

    /**
     * The function Break_MGRS_String breaks down an MGRS coordinate string into its component parts. Updates
     * last_error.
     *
     * @param MGRSString the MGRS coordinate string
     *
     * @return the corresponding MGRSComponents or null.
     */
    private MGRSComponents breakMGRSString(String MGRSString)
    {
        int num_digits;
        int num_letters;
        int i = 0;
        int j = 0;
        long error_code = MGRS_NO_ERROR;

        int zone = 0;
        int[] letters = new int[3];
        long easting = 0;
        long northing = 0;
        int precision = 0;

        while (i < MGRSString.length() && MGRSString.charAt(i) == ' ')
        {
            i++;  /* skip any leading blanks */
        }
        j = i;
        while (i < MGRSString.length() && Character.isDigit(MGRSString.charAt(i)))
        {
            i++;
        }
        num_digits = i - j;
        if (num_digits <= 2)
            if (num_digits > 0)
            {
                /* get zone */
                zone = Integer.parseInt(MGRSString.substring(j, i));
                if ((zone < 1) || (zone > 60))
                    error_code |= MGRS_STRING_ERROR;
            }
            else
                error_code |= MGRS_STRING_ERROR;
        j = i;

        while (i < MGRSString.length() && Character.isLetter(MGRSString.charAt(i)))
        {
            i++;
        }
        num_letters = i - j;
        if (num_letters == 3)
        {
            /* get letters */
            letters[0] = alphabet.indexOf(Character.toUpperCase(MGRSString.charAt(j)));
            if ((letters[0] == LETTER_I) || (letters[0] == LETTER_O))
                error_code |= MGRS_STRING_ERROR;
            letters[1] = alphabet.indexOf(Character.toUpperCase(MGRSString.charAt(j + 1)));
            if ((letters[1] == LETTER_I) || (letters[1] == LETTER_O))
                error_code |= MGRS_STRING_ERROR;
            letters[2] = alphabet.indexOf(Character.toUpperCase(MGRSString.charAt(j + 2)));
            if ((letters[2] == LETTER_I) || (letters[2] == LETTER_O))
                error_code |= MGRS_STRING_ERROR;
        }
        else
            error_code |= MGRS_STRING_ERROR;
        j = i;
        while (i < MGRSString.length() && Character.isDigit(MGRSString.charAt(i)))
        {
            i++;
        }
        num_digits = i - j;
        if ((num_digits <= 10) && (num_digits % 2 == 0))
        {
            /* get easting, northing and precision */
            int n;
            double multiplier;
            /* get easting & northing */
            n = num_digits / 2;
            precision = n;
            if (n > 0)
            {
                easting = Integer.parseInt(MGRSString.substring(j, j + n));
                northing = Integer.parseInt(MGRSString.substring(j + n, j + n + n));
                multiplier = Math.pow(10.0, 5 - n);
                easting *= multiplier;
                northing *= multiplier;
            }
            else
            {
                easting = 0;
                northing = 0;
            }
        }
        else
            error_code |= MGRS_STRING_ERROR;

        last_error = error_code;
        if (error_code == MGRS_NO_ERROR)
            return new MGRSComponents(zone, letters[0], letters[1], letters[2], easting, northing, precision);

        return null;
    }

    /**
     * The function Check_Zone receives an MGRS coordinate string. If a zone is given, MGRS_NO_ERROR is returned.
     * Otherwise, MGRS_NOZONE_WARNING. is returned.
     *
     * @param MGRSString the MGRS coordinate string.
     *
     * @return the error code.
     */
    private long checkZone(String MGRSString)
    {
        int i = 0;
        int j = 0;
        int num_digits = 0;
        long error_code = MGRS_NO_ERROR;

        /* skip any leading blanks */
        while (i < MGRSString.length() && MGRSString.charAt(i) == ' ')
        {
            i++;
        }
        j = i;
        while (i < MGRSString.length() && Character.isDigit(MGRSString.charAt(i)))
        {
            i++;
        }
        num_digits = i - j;
        if (num_digits > 2)
            error_code |= MGRS_STRING_ERROR;
        else if (num_digits <= 0)
            error_code |= MGRS_NOZONE_WARNING;

        return error_code;
    }

    /**
     * The function Get_Latitude_Band_Min_Northing receives a latitude band letter and uses the Latitude_Band_Table to
     * determine the minimum northing for that latitude band letter. Updates min_northing.
     *
     * @param letter Latitude band letter.
     *
     * @return the error code.
     */
    private long getLatitudeBandMinNorthing(int letter)
    {
        long error_code = MGRS_NO_ERROR;

        if ((letter >= LETTER_C) && (letter <= LETTER_H))
        {
            min_northing = latitudeBandConstants[letter - 2][1];
            northing_offset = latitudeBandConstants[letter - 2][4];        //smithjl
        }
        else if ((letter >= LETTER_J) && (letter <= LETTER_N))
        {
            min_northing = latitudeBandConstants[letter - 3][1];
            northing_offset = latitudeBandConstants[letter - 3][4];        //smithjl
        }
        else if ((letter >= LETTER_P) && (letter <= LETTER_X))
        {
            min_northing = latitudeBandConstants[letter - 4][1];
            northing_offset = latitudeBandConstants[letter - 4][4];        //smithjl
        }
        else
            error_code |= MGRS_STRING_ERROR;
        return error_code;
    }

    /**
     * The function Get_Latitude_Range receives a latitude band letter and uses the Latitude_Band_Table to determine the
     * latitude band boundaries for that latitude band letter. Updates north and south.
     *
     * @param letter the Latitude band letter
     *
     * @return the error code.
     */
    private long getLatitudeRange(int letter)
    {
        long error_code = MGRS_NO_ERROR;

        if ((letter >= LETTER_C) && (letter <= LETTER_H))
        {
            north = latitudeBandConstants[letter - 2][2] * DEG_TO_RAD;
            south = latitudeBandConstants[letter - 2][3] * DEG_TO_RAD;
        }
        else if ((letter >= LETTER_J) && (letter <= LETTER_N))
        {
            north = latitudeBandConstants[letter - 3][2] * DEG_TO_RAD;
            south = latitudeBandConstants[letter - 3][3] * DEG_TO_RAD;
        }
        else if ((letter >= LETTER_P) && (letter <= LETTER_X))
        {
            north = latitudeBandConstants[letter - 4][2] * DEG_TO_RAD;
            south = latitudeBandConstants[letter - 4][3] * DEG_TO_RAD;
        }
        else
            error_code |= MGRS_STRING_ERROR;

        return error_code;
    }

    /**
     * The function convertMGRSToUTM converts an MGRS coordinate string to UTM projection (zone, hemisphere, easting and
     * northing) coordinates according to the current ellipsoid parameters.  Updates last_error if any errors occured.
     *
     * @param MGRSString the MGRS coordinate string
     *
     * @return the corresponding UTMComponents or null.
     */
    private UTMCoord convertMGRSToUTM(String MGRSString)
    {
        double scaled_min_northing;
        double grid_easting;        /* Easting for 100,000 meter grid square      */
        double grid_northing;       /* Northing for 100,000 meter grid square     */
        double temp_grid_northing = 0.0;
        double fabs_grid_northing = 0.0;
        double latitude = 0.0;
        double longitude = 0.0;
        double divisor = 1.0;
        long error_code = MGRS_NO_ERROR;

        String hemisphere = AVKey.NORTH;
        double easting = 0;
        double northing = 0;
        UTMCoord UTM = null;

        MGRSComponents MGRS = breakMGRSString(MGRSString);
        if (MGRS == null)
            error_code |= MGRS_STRING_ERROR;
        else
        {
            if (error_code == MGRS_NO_ERROR)
            {
                if ((MGRS.latitudeBand == LETTER_X) && ((MGRS.zone == 32) || (MGRS.zone == 34) || (MGRS.zone == 36)))
                    error_code |= MGRS_STRING_ERROR;
                else
                {
                    if (MGRS.latitudeBand < LETTER_N)
                        hemisphere = AVKey.SOUTH;
                    else
                        hemisphere = AVKey.NORTH;

                    getGridValues(MGRS.zone);

                    // Check that the second letter of the MGRS string is within
                    // the range of valid second letter values
                    // Also check that the third letter is valid
                    if ((MGRS.squareLetter1 < ltr2_low_value) || (MGRS.squareLetter1 > ltr2_high_value) ||
                        (MGRS.squareLetter2 > LETTER_V))
                        error_code |= MGRS_STRING_ERROR;

                    if (error_code == MGRS_NO_ERROR)
                    {
                        grid_northing =
                            (double) (MGRS.squareLetter2) * ONEHT;  //   smithjl  commented out + false_northing;
                        grid_easting = (double) ((MGRS.squareLetter1) - ltr2_low_value + 1) * ONEHT;
                        if ((ltr2_low_value == LETTER_J) && (MGRS.squareLetter1 > LETTER_O))
                            grid_easting = grid_easting - ONEHT;

                        if (MGRS.squareLetter2 > LETTER_O)
                            grid_northing = grid_northing - ONEHT;

                        if (MGRS.squareLetter2 > LETTER_I)
                            grid_northing = grid_northing - ONEHT;

                        if (grid_northing >= TWOMIL)
                            grid_northing = grid_northing - TWOMIL;

                        error_code = getLatitudeBandMinNorthing(MGRS.latitudeBand);
                        if (error_code == MGRS_NO_ERROR)
                        {
                            /*smithjl Deleted code here and added this*/
                            grid_northing = grid_northing - false_northing;

                            if (grid_northing < 0.0)
                                grid_northing += TWOMIL;

                            grid_northing += northing_offset;

                            if (grid_northing < min_northing)
                                grid_northing += TWOMIL;

                            /* smithjl End of added code */

                            easting = grid_easting + MGRS.easting;
                            northing = grid_northing + MGRS.northing;

                            try
                            {
                                UTM = UTMCoord.fromUTM(MGRS.zone, hemisphere, easting, northing, globe);
                                latitude = UTM.getLatitude().radians;
                                divisor = Math.pow(10.0, MGRS.precision);
                                error_code = getLatitudeRange(MGRS.latitudeBand);
                                if (error_code == MGRS_NO_ERROR)
                                {
                                    if (!(((south - DEG_TO_RAD / divisor) <= latitude)
                                        && (latitude <= (north + DEG_TO_RAD / divisor))))
                                        error_code |= MGRS_LAT_WARNING;
                                }
                            }
                            catch (Exception e)
                            {
                                error_code = MGRS_UTM_ERROR;
                            }
                        }
                    }
                }
            }
        }

        last_error = error_code;
        if (error_code == MGRS_NO_ERROR || error_code == MGRS_LAT_WARNING)
            return UTM;

        return null;
    } /* Convert_MGRS_To_UTM */

    /**
     * The function convertGeodeticToMGRS converts Geodetic (latitude and longitude) coordinates to an MGRS coordinate
     * string, according to the current ellipsoid parameters.  If any errors occur, the error code(s) are returned by
     * the function, otherwise MGRS_NO_ERROR is returned.
     *
     * @param latitude  Latitude in radians
     * @param longitude Longitude in radian
     * @param precision Precision level of MGRS string
     *
     * @return error code
     */
    public long convertGeodeticToMGRS(double latitude, double longitude, int precision)
    {
        String Hemisphere = AVKey.NORTH;
        double Easting = 0.0;
        double Northing = 0.0;

        MGRSString = "";

        long error_code = MGRS_NO_ERROR;
        if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
        { /* Latitude out of range */
            error_code = MGRS_LAT_ERROR;
        }

        if ((longitude < -PI) || (longitude > (2 * PI)))
        { /* Longitude out of range */
            error_code = MGRS_LON_ERROR;
        }

        if ((precision < 0) || (precision > MAX_PRECISION))
            error_code = MGRS_PRECISION_ERROR;

        if (error_code == MGRS_NO_ERROR)
        {
            if ((latitude < MIN_UTM_LAT) || (latitude > MAX_UTM_LAT))
            {
                // TODO: polar
                try
                {
                    UPSCoord UPS =
                        UPSCoord.fromLatLon(Angle.fromRadians(latitude), Angle.fromRadians(longitude), globe);
                    error_code |= convertUPSToMGRS(UPS.getHemisphere(), UPS.getEasting(),
                        UPS.getNorthing(), precision);
                }
                catch (Exception e)
                {
                    error_code = MGRS_UPS_ERROR;
                }
            }
            else
            {
                try
                {
                    UTMCoord UTM =
                        UTMCoord.fromLatLon(Angle.fromRadians(latitude), Angle.fromRadians(longitude), globe);
                    error_code |= convertUTMToMGRS(UTM.getZone(), latitude, UTM.getEasting(),
                        UTM.getNorthing(), precision);
                }
                catch (Exception e)
                {
                    error_code = MGRS_UTM_ERROR;
                }
            }
        }

        return error_code;
    }

    /** @return converted MGRS string */
    public String getMGRSString()
    {
        return MGRSString;
    }

    /**
     * The function Convert_UPS_To_MGRS converts UPS (hemisphere, easting, and northing) coordinates to an MGRS
     * coordinate string according to the current ellipsoid parameters.  If any errors occur, the error code(s) are
     * returned by the function, otherwise MGRS_NO_ERROR is returned.
     *
     * @param Hemisphere Hemisphere either, {@link gov.nasa.worldwind.avlist.AVKey#NORTH} or {@link
     *                   gov.nasa.worldwind.avlist.AVKey#SOUTH}.
     * @param Easting    Easting/X in meters
     * @param Northing   Northing/Y in meters
     * @param Precision  Precision level of MGRS string
     *
     * @return error value
     */
    private long convertUPSToMGRS(String Hemisphere, Double Easting, Double Northing, long Precision)
    {
        double false_easting;       /* False easting for 2nd letter                 */
        double false_northing;      /* False northing for 3rd letter                */
        double grid_easting;        /* Easting used to derive 2nd letter of MGRS    */
        double grid_northing;       /* Northing used to derive 3rd letter of MGRS   */
        int ltr2_low_value;        /* 2nd letter range - low number                */
        long[] letters = new long[MGRS_LETTERS];  /* Number location of 3 letters in alphabet     */
        double divisor;
        int index;
        long error_code = MGRS_NO_ERROR;

        if (!AVKey.NORTH.equals(Hemisphere) && !AVKey.SOUTH.equals(Hemisphere))
            error_code |= MGRS_HEMISPHERE_ERROR;
        if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH))
            error_code |= MGRS_EASTING_ERROR;
        if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH))
            error_code |= MGRS_NORTHING_ERROR;
        if ((Precision < 0) || (Precision > MAX_PRECISION))
            error_code |= MGRS_PRECISION_ERROR;

        if (error_code == MGRS_NO_ERROR)
        {
            divisor = Math.pow(10.0, (5 - Precision));
            Easting = roundMGRS(Easting / divisor) * divisor;
            Northing = roundMGRS(Northing / divisor) * divisor;

            if (AVKey.NORTH.equals(Hemisphere))
            {
                if (Easting >= TWOMIL)
                    letters[0] = LETTER_Z;
                else
                    letters[0] = LETTER_Y;

                index = (int) letters[0] - 22;
//                ltr2_low_value = UPS_Constant_Table.get(index).ltr2_low_value;
//                false_easting = UPS_Constant_Table.get(index).false_easting;
//                false_northing = UPS_Constant_Table.get(index).false_northing;
                ltr2_low_value = (int) upsConstants[index][1];
                false_easting = (double) upsConstants[index][4];
                false_northing = (double) upsConstants[index][5];
            }
            else // AVKey.SOUTH.equals(Hemisphere)
            {
                if (Easting >= TWOMIL)
                    letters[0] = LETTER_B;
                else
                    letters[0] = LETTER_A;

//                ltr2_low_value = UPS_Constant_Table.get((int) letters[0]).ltr2_low_value;
//                false_easting = UPS_Constant_Table.get((int) letters[0]).false_easting;
//                false_northing = UPS_Constant_Table.get((int) letters[0]).false_northing;
                ltr2_low_value = (int) upsConstants[(int) letters[0]][1];
                false_easting = (double) upsConstants[(int) letters[0]][4];
                false_northing = (double) upsConstants[(int) letters[0]][5];
            }

            grid_northing = Northing;
            grid_northing = grid_northing - false_northing;
            letters[2] = (int) (grid_northing / ONEHT);

            if (letters[2] > LETTER_H)
                letters[2] = letters[2] + 1;

            if (letters[2] > LETTER_N)
                letters[2] = letters[2] + 1;

            grid_easting = Easting;
            grid_easting = grid_easting - false_easting;
            letters[1] = (int) ltr2_low_value + ((int) (grid_easting / ONEHT));

            if (Easting < TWOMIL)
            {
                if (letters[1] > LETTER_L)
                    letters[1] = letters[1] + 3;

                if (letters[1] > LETTER_U)
                    letters[1] = letters[1] + 2;
            }
            else
            {
                if (letters[1] > LETTER_C)
                    letters[1] = letters[1] + 2;

                if (letters[1] > LETTER_H)
                    letters[1] = letters[1] + 1;

                if (letters[1] > LETTER_L)
                    letters[1] = letters[1] + 3;
            }

            makeMGRSString(0, letters, Easting, Northing, Precision);
        }
        return (error_code);
    }

    /**
     * The function UTM_To_MGRS calculates an MGRS coordinate string based on the zone, latitude, easting and northing.
     *
     * @param Zone      Zone number
     * @param Latitude  Latitude in radians
     * @param Easting   Easting
     * @param Northing  Northing
     * @param Precision Precision
     *
     * @return error code
     */
    private long convertUTMToMGRS(long Zone, double Latitude, double Easting, double Northing, long Precision)
    {
        double grid_easting;        /* Easting used to derive 2nd letter of MGRS   */
        double grid_northing;       /* Northing used to derive 3rd letter of MGRS  */
        long[] letters = new long[MGRS_LETTERS];  /* Number location of 3 letters in alphabet    */
        double divisor;
        long error_code;

        /* Round easting and northing values */
        divisor = Math.pow(10.0, (5 - Precision));
        Easting = roundMGRS(Easting / divisor) * divisor;
        Northing = roundMGRS(Northing / divisor) * divisor;

        getGridValues(Zone);

        error_code = getLatitudeLetter(Latitude);
        letters[0] = getLastLetter();

        if (error_code == MGRS_NO_ERROR)
        {
            grid_northing = Northing;
            if (grid_northing == 1.e7)
                grid_northing = grid_northing - 1.0;

            while (grid_northing >= TWOMIL)
            {
                grid_northing = grid_northing - TWOMIL;
            }
            grid_northing = grid_northing + false_northing;   //smithjl

            if (grid_northing >= TWOMIL)                     //smithjl
                grid_northing = grid_northing - TWOMIL;        //smithjl

            letters[2] = (long) (grid_northing / ONEHT);
            if (letters[2] > LETTER_H)
                letters[2] = letters[2] + 1;

            if (letters[2] > LETTER_N)
                letters[2] = letters[2] + 1;

            grid_easting = Easting;
            if (((letters[0] == LETTER_V) && (Zone == 31)) && (grid_easting == 500000.0))
                grid_easting = grid_easting - 1.0; /* SUBTRACT 1 METER */

            letters[1] = ltr2_low_value + ((long) (grid_easting / ONEHT) - 1);
            if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
                letters[1] = letters[1] + 1;

            makeMGRSString(Zone, letters, Easting, Northing, Precision);
        }
        return error_code;
    }

    /**
     * The function Get_Grid_Values sets the letter range used for the 2nd letter in the MGRS coordinate string, based
     * on the set number of the utm zone. It also sets the false northing using a value of A for the second letter of
     * the grid square, based on the grid pattern and set number of the utm zone.
     * 

* Key values that are set in this function include: ltr2_low_value, ltr2_high_value, and false_northing. * * @param zone Zone number */ private void getGridValues(long zone) { long set_number; /* Set number (1-6) based on UTM zone number */ long aa_pattern; /* Pattern based on ellipsoid code */ set_number = zone % 6; if (set_number == 0) set_number = 6; if (MGRS_Ellipsoid_Code.compareTo(CLARKE_1866) == 0 || MGRS_Ellipsoid_Code.compareTo(CLARKE_1880) == 0 || MGRS_Ellipsoid_Code.compareTo(BESSEL_1841) == 0 || MGRS_Ellipsoid_Code.compareTo(BESSEL_1841_NAMIBIA) == 0) aa_pattern = 0L; else aa_pattern = 1L; if ((set_number == 1) || (set_number == 4)) { ltr2_low_value = (long) LETTER_A; ltr2_high_value = (long) LETTER_H; } else if ((set_number == 2) || (set_number == 5)) { ltr2_low_value = (long) LETTER_J; ltr2_high_value = (long) LETTER_R; } else if ((set_number == 3) || (set_number == 6)) { ltr2_low_value = (long) LETTER_S; ltr2_high_value = (long) LETTER_Z; } /* False northing at A for second letter of grid square */ if (aa_pattern == 1L) { if ((set_number % 2) == 0) false_northing = 500000.0; //smithjl was 1500000 else false_northing = 0.0; } else { if ((set_number % 2) == 0) false_northing = 1500000.0; //smithjl was 500000 else false_northing = 1000000.00; } } /** * The function Get_Latitude_Letter receives a latitude value and uses the Latitude_Band_Table to determine the * latitude band letter for that latitude. * * @param latitude latitude to turn into code * * @return error code */ private long getLatitudeLetter(double latitude) { double temp; long error_code = MGRS_NO_ERROR; double lat_deg = latitude * RAD_TO_DEG; if (lat_deg >= 72 && lat_deg < 84.5) lastLetter = (long) LETTER_X; else if (lat_deg > -80.5 && lat_deg < 72) { temp = ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12; // lastLetter = Latitude_Band_Table.get((int) temp).letter; lastLetter = (long) latitudeBandConstants[(int) temp][0]; } else error_code |= MGRS_LAT_ERROR; return error_code; } /** * The function Round_MGRS rounds the input value to the nearest integer, using the standard engineering rule. The * rounded integer value is then returned. * * @param value Value to be rounded * * @return rounded double value */ private double roundMGRS(double value) { double ivalue = Math.floor(value); long ival; double fraction = value - ivalue; // double fraction = modf (value, &ivalue); ival = (long) (ivalue); if ((fraction > 0.5) || ((fraction == 0.5) && (ival % 2 == 1))) ival++; return (double) ival; } /** * The function Make_MGRS_String constructs an MGRS string from its component parts. * * @param Zone UTM Zone * @param Letters MGRS coordinate string letters * @param Easting Easting value * @param Northing Northing value * @param Precision Precision level of MGRS string * * @return error code */ private long makeMGRSString(long Zone, long[] Letters, double Easting, double Northing, long Precision) { int j; double divisor; long east; long north; long error_code = MGRS_NO_ERROR; if (Zone != 0) MGRSString = String.format("%02d", Zone); else MGRSString = " "; for (j = 0; j < 3; j++) { if (Letters[j] < 0 || Letters[j] > 26) return MGRS_ZONE_ERROR; // TODO: Find out why this happens MGRSString = MGRSString + alphabet.charAt((int) Letters[j]); } divisor = Math.pow(10.0, (5 - Precision)); Easting = Easting % 100000.0; if (Easting >= 99999.5) Easting = 99999.0; east = (long) (Easting / divisor); // Here we need to only use the number requesting in the precision Integer iEast = (int) east; String sEast = iEast.toString(); if (sEast.length() > Precision) sEast = sEast.substring(0, (int) Precision - 1); else { int i; int length = sEast.length(); for (i = 0; i < Precision - length; i++) { sEast = "0" + sEast; } } MGRSString = MGRSString + " " + sEast; Northing = Northing % 100000.0; if (Northing >= 99999.5) Northing = 99999.0; north = (long) (Northing / divisor); Integer iNorth = (int) north; String sNorth = iNorth.toString(); if (sNorth.length() > Precision) sNorth = sNorth.substring(0, (int) Precision - 1); else { int i; int length = sNorth.length(); for (i = 0; i < Precision - length; i++) { sNorth = "0" + sNorth; } } MGRSString = MGRSString + " " + sNorth; return (error_code); } /** * Get the last error code. * * @return the last error code. */ public long getError() { return last_error; } /** * The function Convert_MGRS_To_UPS converts an MGRS coordinate string to UPS (hemisphere, easting, and northing) * coordinates, according to the current ellipsoid parameters. If any errors occur, the error code(s) are returned * by the function, otherwide UPS_NO_ERROR is returned. * * @param MGRS the MGRS coordinate string. * * @return a corresponding {@link UPSCoord} instance. */ private UPSCoord convertMGRSToUPS(String MGRS) { long ltr2_high_value; /* 2nd letter range - high number */ long ltr3_high_value; /* 3rd letter range - high number (UPS) */ long ltr2_low_value; /* 2nd letter range - low number */ double false_easting; /* False easting for 2nd letter */ double false_northing; /* False northing for 3rd letter */ double grid_easting; /* easting for 100,000 meter grid square */ double grid_northing; /* northing for 100,000 meter grid square */ int index = 0; long error_code = MGRS_NO_ERROR; String hemisphere; double easting, northing; MGRSComponents mgrs = breakMGRSString(MGRS); if (mgrs == null) error_code = this.last_error; if (mgrs != null && mgrs.zone > 0) error_code |= MGRS_STRING_ERROR; if (error_code == MGRS_NO_ERROR) { easting = mgrs.easting; northing = mgrs.northing; if (mgrs.latitudeBand >= LETTER_Y) { hemisphere = AVKey.NORTH; index = mgrs.latitudeBand - 22; ltr2_low_value = upsConstants[index][1]; //.ltr2_low_value; ltr2_high_value = upsConstants[index][2]; //.ltr2_high_value; ltr3_high_value = upsConstants[index][3]; //.ltr3_high_value; false_easting = upsConstants[index][4]; //.false_easting; false_northing = upsConstants[index][5]; //.false_northing; } else { hemisphere = AVKey.SOUTH; ltr2_low_value = upsConstants[mgrs.latitudeBand][12]; //.ltr2_low_value; ltr2_high_value = upsConstants[mgrs.latitudeBand][2]; //.ltr2_high_value; ltr3_high_value = upsConstants[mgrs.latitudeBand][3]; //.ltr3_high_value; false_easting = upsConstants[mgrs.latitudeBand][4]; //.false_easting; false_northing = upsConstants[mgrs.latitudeBand][5]; //.false_northing; } // Check that the second letter of the MGRS string is within // the range of valid second letter values // Also check that the third letter is valid if ((mgrs.squareLetter1 < ltr2_low_value) || (mgrs.squareLetter1 > ltr2_high_value) || ((mgrs.squareLetter1 == LETTER_D) || (mgrs.squareLetter1 == LETTER_E) || (mgrs.squareLetter1 == LETTER_M) || (mgrs.squareLetter1 == LETTER_N) || (mgrs.squareLetter1 == LETTER_V) || (mgrs.squareLetter1 == LETTER_W)) || (mgrs.squareLetter2 > ltr3_high_value)) error_code = MGRS_STRING_ERROR; if (error_code == MGRS_NO_ERROR) { grid_northing = (double) mgrs.squareLetter2 * ONEHT + false_northing; if (mgrs.squareLetter2 > LETTER_I) grid_northing = grid_northing - ONEHT; if (mgrs.squareLetter2 > LETTER_O) grid_northing = grid_northing - ONEHT; grid_easting = (double) ((mgrs.squareLetter1) - ltr2_low_value) * ONEHT + false_easting; if (ltr2_low_value != LETTER_A) { if (mgrs.squareLetter1 > LETTER_L) grid_easting = grid_easting - 300000.0; if (mgrs.squareLetter1 > LETTER_U) grid_easting = grid_easting - 200000.0; } else { if (mgrs.squareLetter1 > LETTER_C) grid_easting = grid_easting - 200000.0; if (mgrs.squareLetter1 > LETTER_I) grid_easting = grid_easting - ONEHT; if (mgrs.squareLetter1 > LETTER_L) grid_easting = grid_easting - 300000.0; } easting = grid_easting + easting; northing = grid_northing + northing; return UPSCoord.fromUPS(hemisphere, easting, northing, globe); } } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy