class UTM {

    public UTM(double latD, double lonD)
    {
        double falseNorthing = 0;
        if (latD < 0.)
        {
            falseNorthing = 10000000;
            myHemisphere = Hemisphere.SOUTH;
        }
        else
        {
            myHemisphere = Hemisphere.NORTH;
        }

        // Make sure the longitude is in the -180 to 179.999... range.
        double lon = lonD + 180. - (int)((lonD + 180.) / 360.) * 360. - 180.;

        // First find the zone number.
        myZone = findZone(latD, lon);

        final double zoneHalfWidth = 3.;
        double orginLongitude = (myZone - 1) * 6 - 180. + zoneHalfWidth;

        double φ = Math.toRadians(latD);
        double λ = Math.toRadians(lon);
        double λ0 = Math.toRadians(orginLongitude);
        double Δλ = λ - λ0;

        double sinφ = Math.sin(φ);
        double sin2φ = sinφ * sinφ;
        double cosφ = Math.cos(φ);
        double cos2φ = cosφ * cosφ;
        double tanφ = sinφ / cosφ;
        double tan2φ = tanφ * tanφ;
        double tan4φ = tanφ * tanφ;

        double ePrime2 = WGS84EarthConstants.SECOND_ECCENTRICITY_SQ;
        double e2 = WGS84EarthConstants.FIRST_ECCENTRICITY_SQ;
        double e4 = e2 * e2;
        double e6 = e4 * e2;
        double semiMajor = WGS84EarthConstants.SEMI_MAJOR_AXIS_M;

        double v = 1 / Math.sqrt(1 - e2 * sin2φ);
        double a = Δλ * cosφ;
        double a2 = a * a;
        double a3 = a2 * a;
        double a4 = a3 * a;
        double a5 = a4 * a;
        double a6 = a5 * a;

        final double quarter = 0.25;
        // 3/64
        final double n3d64 = 0.046875;
        // 5/256
        final double n5d256 = 0.01953125;
        // 3/8
        final double n3d8 = 0.375;
        // 3/32
        final double n3d32 = 0.09375;
        // 45/1024
        final double n45d1024 = 0.043945313;
        // 15/256
        final double n15d156 = 0.05859375;
        // 35/3072
        final double n35d3072 = 0.011393229;

        double s = (1. - quarter * e2 - n3d64 * e4 - n5d256 * e6) * φ - (n3d8 * e2 + n3d32 * e4 + n45d1024 * e6) * Math.sin(2 * φ)
                + (n15d156 * e4 + n45d1024 * e6) * Math.sin(4 * φ) - n35d3072 * e6 * Math.sin(6 * φ);

        double c = ePrime2 * cos2φ;
        double c2 = c * c;
        myEasting = FALSE_EASTING + SCALE * semiMajor * v * (a + (1 - tan2φ + c) * a3 / 6 + (5 - 18 * tan2φ + tan4φ) * a5 / 120);
        myNorthing = falseNorthing + SCALE * semiMajor
                * (s + v * tanφ * (a2 * .5 + (5 - tan2φ + 9 * c + 4 * c2) * a4 / 24 + (61 - 58 * tan2φ + tan4φ) * a6 / 720));
    }

    /**
     * Constructor.
     *
     * @param geoPos The geographic position (lat/lon).
     */
    public UTM(GeographicPosition geoPos)
    {
        this(geoPos.getLatLonAlt().getLatD(), geoPos.getLatLonAlt().getLonD());
    }

    /**
     * Constructor.
     *
     * @param zone The zone.
     * @param hemisphere The hemisphere.
     * @param easting The easting.
     * @param northing The northing.
     */
    public UTM(int zone, Hemisphere hemisphere, double easting, double northing)
    {
        if (zone < 0 || zone > 60)
        {
            throw new IllegalArgumentException("Zone is outside of allowable range (0 - 60)");
        }
        myZone = zone;
        myHemisphere = hemisphere;

        if (easting < 167000 || easting > 833000)
        {
            throw new IllegalArgumentException("Easting is outside of allowable range (0 - 99999)");
        }
        myEasting = easting;

        if (northing < 0 || northing > 10000000)
        {
            throw new IllegalArgumentException("Northing is outside of allowable range (0 - 99999)");
        }
        myNorthing = northing;
    }

    public  UTM(double latD, double lonD);

}