/*
Copyright (C) 2001, 2008 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.globes.Globe;
import gov.nasa.worldwind.geom.Angle;
/**
* Converter used to translate UTM coordinates to and from geodetic latitude and longitude.
*
* @author Patrick Murris
* @version $Id: UTMCoordConverter.java 5188 2008-04-27 02:05:53Z patrickmurris $
* @see UTMCoord, TMCoordConverter
*/
/**
* Ported to Java from the NGA GeoTrans utm.c and utm.h
*
* @author Garrett Headley, Patrick Murris
*/
class UTMCoordConverter
{
public final static int UTM_NO_ERROR = 0x0000;
public final static int UTM_LAT_ERROR = 0x0001;
public final static int UTM_LON_ERROR = 0x0002;
public final static int UTM_EASTING_ERROR = 0x0004;
public final static int UTM_NORTHING_ERROR = 0x0008;
public final static int UTM_ZONE_ERROR = 0x0010;
public final static int UTM_HEMISPHERE_ERROR = 0x0020;
public final static int UTM_ZONE_OVERRIDE_ERROR = 0x0040;
public final static int UTM_A_ERROR = 0x0080;
public final static int UTM_INV_F_ERROR = 0x0100;
public final static int UTM_TM_ERROR = 0x0200;
private final static double PI = 3.14159265358979323;
private final static double MIN_LAT = ((-80.5 * PI) / 180.0); /* -80.5 degrees in radians */
private final static double MAX_LAT = ((84.5 * PI) / 180.0); /* 84.5 degrees in radians */
private final static int MIN_EASTING = 100000;
private final static int MAX_EASTING = 900000;
private final static int MIN_NORTHING = 0;
private final static int MAX_NORTHING = 10000000;
private final Globe globe;
private double UTM_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
private double UTM_f = 1 / 298.257223563; /* Flattening of ellipsoid */
private long UTM_Override = 0; /* Zone override flag */
private double Easting;
private double Northing;
private char Hemisphere;
private int Zone;
private double Latitude;
private double Longitude;
UTMCoordConverter(Globe globe)
{
this.globe = globe;
if (globe != null)
{
double a = globe.getEquatorialRadius();
double f = (globe.getEquatorialRadius() - globe.getPolarRadius()) / globe.getEquatorialRadius();
setUTMParameters(a, f, 0);
}
}
/**
* The function Set_UTM_Parameters receives the ellipsoid parameters and UTM zone override parameter as inputs, and
* sets the corresponding state variables. If any errors occur, the error code(s) are returned by the function,
* otherwise UTM_NO_ERROR is returned.
*
* @param a Semi-major axis of ellipsoid, in meters
* @param f Flattening of ellipsoid
* @param override UTM override zone, zero indicates no override
* @return error code
*/
private long setUTMParameters(double a, double f, long override)
{
double inv_f = 1 / f;
long Error_Code = UTM_NO_ERROR;
if (a <= 0.0)
{ /* Semi-major axis must be greater than zero */
Error_Code |= UTM_A_ERROR;
}
if ((inv_f < 250) || (inv_f > 350))
{ /* Inverse flattening must be between 250 and 350 */
Error_Code |= UTM_INV_F_ERROR;
}
if ((override < 0) || (override > 60))
{
Error_Code |= UTM_ZONE_OVERRIDE_ERROR;
}
if (Error_Code == UTM_NO_ERROR)
{ /* no errors */
UTM_a = a;
UTM_f = f;
UTM_Override = override;
}
return (Error_Code);
}
/**
* The function Convert_Geodetic_To_UTM converts geodetic (latitude and longitude) coordinates to UTM projection
* (zone, hemisphere, easting and northing) coordinates according to the current ellipsoid and UTM zone override
* parameters. If any errors occur, the error code(s) are returned by the function, otherwise UTM_NO_ERROR is
* returned.
*
* @param Latitude Latitude in radians
* @param Longitude Longitude in radians
* @return error code
*/
public long convertGeodeticToUTM(double Latitude, double Longitude)
{
long Lat_Degrees;
long Long_Degrees;
long temp_zone;
long Error_Code = UTM_NO_ERROR;
double Origin_Latitude = 0;
double Central_Meridian = 0;
double False_Easting = 500000;
double False_Northing = 0;
double Scale = 0.9996;
if ((Latitude < MIN_LAT) || (Latitude > MAX_LAT))
{ /* Latitude out of range */
Error_Code |= UTM_LAT_ERROR;
}
if ((Longitude < -PI) || (Longitude > (2 * PI)))
{ /* Longitude out of range */
Error_Code |= UTM_LON_ERROR;
}
if (Error_Code == UTM_NO_ERROR)
{ /* no errors */
if (Longitude < 0)
Longitude += (2 * PI) + 1.0e-10;
Lat_Degrees = (long) (Latitude * 180.0 / PI);
Long_Degrees = (long) (Longitude * 180.0 / PI);
if (Longitude < PI)
temp_zone = (long) (31 + ((Longitude * 180.0 / PI) / 6.0));
else
temp_zone = (long) (((Longitude * 180.0 / PI) / 6.0) - 29);
if (temp_zone > 60)
temp_zone = 1;
/* UTM special cases */
if ((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > -1) && (Long_Degrees < 3))
temp_zone = 31;
if ((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > 2) && (Long_Degrees < 12))
temp_zone = 32;
if ((Lat_Degrees > 71) && (Long_Degrees > -1) && (Long_Degrees < 9))
temp_zone = 31;
if ((Lat_Degrees > 71) && (Long_Degrees > 8) && (Long_Degrees < 21))
temp_zone = 33;
if ((Lat_Degrees > 71) && (Long_Degrees > 20) && (Long_Degrees < 33))
temp_zone = 35;
if ((Lat_Degrees > 71) && (Long_Degrees > 32) && (Long_Degrees < 42))
temp_zone = 37;
if (UTM_Override != 0)
{
if ((temp_zone == 1) && (UTM_Override == 60))
temp_zone = UTM_Override;
else if ((temp_zone == 60) && (UTM_Override == 1))
temp_zone = UTM_Override;
else if (((temp_zone - 1) <= UTM_Override) && (UTM_Override <= (temp_zone + 1)))
temp_zone = UTM_Override;
else
Error_Code = UTM_ZONE_OVERRIDE_ERROR;
}
if (Error_Code == UTM_NO_ERROR)
{
if (temp_zone >= 31)
Central_Meridian = (6 * temp_zone - 183) * PI / 180.0;
else
Central_Meridian = (6 * temp_zone + 177) * PI / 180.0;
Zone = (int) temp_zone;
if (Latitude < 0)
{
False_Northing = 10000000;
Hemisphere = 'S';
} else
Hemisphere = 'N';
try
{
TMCoord TM = TMCoord.fromLatLon(Angle.fromRadians(Latitude), Angle.fromRadians(Longitude),
this.globe, Angle.fromRadians(Origin_Latitude), Angle.fromRadians(Central_Meridian),
False_Easting, False_Northing, Scale);
Easting = TM.getEasting();
Northing = TM.getNorthing();
if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING))
Error_Code = UTM_EASTING_ERROR;
if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING))
Error_Code |= UTM_NORTHING_ERROR;
}
catch (Exception e)
{
Error_Code = UTM_TM_ERROR;
}
}
}
return (Error_Code);
}
/**
* @return Easting (X) in meters
*/
public double getEasting()
{
return Easting;
}
/**
* @return Northing (Y) in meters
*/
public double getNorthing()
{
return Northing;
}
/**
* @return North or South hemisphere
*/
public char getHemisphere()
{
return Hemisphere;
}
/**
* @return UTM zone
*/
public int getZone()
{
return Zone;
}
/**
* The function Convert_UTM_To_Geodetic converts UTM projection (zone,
* hemisphere, easting and northing) coordinates 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 Zone UTM zone.
* @param Hemisphere North or South hemisphere.
* @param Easting Easting (X) in meters.
* @param Northing Northing (Y) in meters.
* @return error code.
*/
public long convertUTMToGeodetic(long Zone, char Hemisphere, double Easting, double Northing)
{
long Error_Code = UTM_NO_ERROR;
double Origin_Latitude = 0;
double Central_Meridian = 0;
double False_Easting = 500000;
double False_Northing = 0;
double Scale = 0.9996;
if ((Zone < 1) || (Zone > 60))
Error_Code |= UTM_ZONE_ERROR;
if ((Hemisphere != 'S') && (Hemisphere != 'N'))
Error_Code |= UTM_HEMISPHERE_ERROR;
if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING))
Error_Code |= UTM_EASTING_ERROR;
if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING))
Error_Code |= UTM_NORTHING_ERROR;
if (Error_Code == UTM_NO_ERROR)
{ /* no errors */
if (Zone >= 31)
Central_Meridian = ((6 * Zone - 183) * PI / 180.0 /*+ 0.00000005*/);
else
Central_Meridian = ((6 * Zone + 177) * PI / 180.0 /*+ 0.00000005*/);
if (Hemisphere == 'S')
False_Northing = 10000000;
try
{
TMCoord TM = TMCoord.fromTM(Easting, Northing,
this.globe, Angle.fromRadians(Origin_Latitude), Angle.fromRadians(Central_Meridian),
False_Easting, False_Northing, Scale);
Latitude = TM.getLatitude().radians;
Longitude = TM.getLongitude().radians;
if ((Latitude < MIN_LAT) || (Latitude > MAX_LAT))
{ /* Latitude out of range */
Error_Code |= UTM_NORTHING_ERROR;
}
}
catch (Exception e)
{
Error_Code = UTM_TM_ERROR;
}
}
return (Error_Code);
}
/**
* @return Latitude in radians.
*/
public double getLatitude()
{
return Latitude;
}
/**
* @return Longitude in radians.
*/
public double getLongitude()
{
return Longitude;
}
}