Does anyone know of a way, in Java, to convert an earth surface position from lat, lon to UTM (say in WGS84)? I'm currently looking at Geotools but unfortunately the solution is not obvious.
-
1Yeah GeoTools documentation assumes that the end-user will already be very familiar with the API, and GIS in general. It's very difficult to understand. I am currently having a hard time getting plot points on a map using it. http://stackoverflow.com/questions/29567231/why-cant-this-code-produce-a-points-layer-in-geotools – cj5 Jun 16 '15 at 16:30
-
[Steve Dutch at University of Wisconson](http://www.uwgb.edu/dutchs/UsefulData/UTMFormulas.htm) has a pretty good write-up on the algorithm. Also includes an Excel document to help verify your numbers. – James Schek Oct 06 '08 at 21:58
-
Take a look at [OpenMap](http://openmap.bbn.com/) specifically the [com.bbn.openmap.proj.coords ](http://openmap.bbn.com/doc/api/com/bbn/openmap/proj/coords/package-summary.html) package in the API. – Matt Oct 06 '08 at 21:53
-
the Alberta 10 TM answer is probably overkill for what you need - [this link](http://www.ibm.com/developerworks/java/library/j-coordconvert/index.html) from developer works probably has all the information you need. – Michael Sharek Oct 06 '08 at 20:56
-
I suggest [JCoord](http://www.jstott.me.uk/jcoord/). It allows you to convert between various cartographic coordinate schemes using a very simple API. Iy you're feeling saucy, have a look at the source code; its pages and pages of dense trigonometry. Splendid stuff. There's also a javascript version called JSCoord. – skaffman Oct 07 '08 at 07:33
5 Answers
No Library, No Nothing. Copy This!
Using These Two Classes , You can Convert Degree(latitude/longitude) to UTM and Vice Versa!
private class Deg2UTM
{
double Easting;
double Northing;
int Zone;
char Letter;
private Deg2UTM(double Lat,double Lon)
{
Zone= (int) Math.floor(Lon/6+31);
if (Lat<-72)
Letter='C';
else if (Lat<-64)
Letter='D';
else if (Lat<-56)
Letter='E';
else if (Lat<-48)
Letter='F';
else if (Lat<-40)
Letter='G';
else if (Lat<-32)
Letter='H';
else if (Lat<-24)
Letter='J';
else if (Lat<-16)
Letter='K';
else if (Lat<-8)
Letter='L';
else if (Lat<0)
Letter='M';
else if (Lat<8)
Letter='N';
else if (Lat<16)
Letter='P';
else if (Lat<24)
Letter='Q';
else if (Lat<32)
Letter='R';
else if (Lat<40)
Letter='S';
else if (Lat<48)
Letter='T';
else if (Lat<56)
Letter='U';
else if (Lat<64)
Letter='V';
else if (Lat<72)
Letter='W';
else
Letter='X';
Easting=0.5*Math.log((1+Math.cos(Lat*Math.PI/180)*Math.sin(Lon*Math.PI/180-(6*Zone-183)*Math.PI/180))/(1-Math.cos(Lat*Math.PI/180)*Math.sin(Lon*Math.PI/180-(6*Zone-183)*Math.PI/180)))*0.9996*6399593.62/Math.pow((1+Math.pow(0.0820944379, 2)*Math.pow(Math.cos(Lat*Math.PI/180), 2)), 0.5)*(1+ Math.pow(0.0820944379,2)/2*Math.pow((0.5*Math.log((1+Math.cos(Lat*Math.PI/180)*Math.sin(Lon*Math.PI/180-(6*Zone-183)*Math.PI/180))/(1-Math.cos(Lat*Math.PI/180)*Math.sin(Lon*Math.PI/180-(6*Zone-183)*Math.PI/180)))),2)*Math.pow(Math.cos(Lat*Math.PI/180),2)/3)+500000;
Easting=Math.round(Easting*100)*0.01;
Northing = (Math.atan(Math.tan(Lat*Math.PI/180)/Math.cos((Lon*Math.PI/180-(6*Zone -183)*Math.PI/180)))-Lat*Math.PI/180)*0.9996*6399593.625/Math.sqrt(1+0.006739496742*Math.pow(Math.cos(Lat*Math.PI/180),2))*(1+0.006739496742/2*Math.pow(0.5*Math.log((1+Math.cos(Lat*Math.PI/180)*Math.sin((Lon*Math.PI/180-(6*Zone -183)*Math.PI/180)))/(1-Math.cos(Lat*Math.PI/180)*Math.sin((Lon*Math.PI/180-(6*Zone -183)*Math.PI/180)))),2)*Math.pow(Math.cos(Lat*Math.PI/180),2))+0.9996*6399593.625*(Lat*Math.PI/180-0.005054622556*(Lat*Math.PI/180+Math.sin(2*Lat*Math.PI/180)/2)+4.258201531e-05*(3*(Lat*Math.PI/180+Math.sin(2*Lat*Math.PI/180)/2)+Math.sin(2*Lat*Math.PI/180)*Math.pow(Math.cos(Lat*Math.PI/180),2))/4-1.674057895e-07*(5*(3*(Lat*Math.PI/180+Math.sin(2*Lat*Math.PI/180)/2)+Math.sin(2*Lat*Math.PI/180)*Math.pow(Math.cos(Lat*Math.PI/180),2))/4+Math.sin(2*Lat*Math.PI/180)*Math.pow(Math.cos(Lat*Math.PI/180),2)*Math.pow(Math.cos(Lat*Math.PI/180),2))/3);
if (Letter<'M')
Northing = Northing + 10000000;
Northing=Math.round(Northing*100)*0.01;
}
}
private class UTM2Deg
{
double latitude;
double longitude;
private UTM2Deg(String UTM)
{
String[] parts=UTM.split(" ");
int Zone=Integer.parseInt(parts[0]);
char Letter=parts[1].toUpperCase(Locale.ENGLISH).charAt(0);
double Easting=Double.parseDouble(parts[2]);
double Northing=Double.parseDouble(parts[3]);
double Hem;
if (Letter>'M')
Hem='N';
else
Hem='S';
double north;
if (Hem == 'S')
north = Northing - 10000000;
else
north = Northing;
latitude = (north/6366197.724/0.9996+(1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)-0.006739496742*Math.sin(north/6366197.724/0.9996)*Math.cos(north/6366197.724/0.9996)*(Math.atan(Math.cos(Math.atan(( Math.exp((Easting - 500000) / (0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting - 500000) / (0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2)/3))-Math.exp(-(Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*( 1 - 0.006739496742*Math.pow((Easting - 500000) / (0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2)/3)))/2/Math.cos((north-0.9996*6399593.625*(north/6366197.724/0.9996-0.006739496742*3/4*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.pow(0.006739496742*3/4,2)*5/3*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996 )/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4-Math.pow(0.006739496742*3/4,3)*35/27*(5*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/3))/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2))+north/6366197.724/0.9996)))*Math.tan((north-0.9996*6399593.625*(north/6366197.724/0.9996 - 0.006739496742*3/4*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.pow(0.006739496742*3/4,2)*5/3*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996 )*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4-Math.pow(0.006739496742*3/4,3)*35/27*(5*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/3))/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2))+north/6366197.724/0.9996))-north/6366197.724/0.9996)*3/2)*(Math.atan(Math.cos(Math.atan((Math.exp((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2)/3))-Math.exp(-(Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2)/3)))/2/Math.cos((north-0.9996*6399593.625*(north/6366197.724/0.9996-0.006739496742*3/4*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.pow(0.006739496742*3/4,2)*5/3*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4-Math.pow(0.006739496742*3/4,3)*35/27*(5*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/3))/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2))+north/6366197.724/0.9996)))*Math.tan((north-0.9996*6399593.625*(north/6366197.724/0.9996-0.006739496742*3/4*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.pow(0.006739496742*3/4,2)*5/3*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4-Math.pow(0.006739496742*3/4,3)*35/27*(5*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/3))/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2))+north/6366197.724/0.9996))-north/6366197.724/0.9996))*180/Math.PI;
latitude=Math.round(latitude*10000000);
latitude=latitude/10000000;
longitude =Math.atan((Math.exp((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2)/3))-Math.exp(-(Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2)/3)))/2/Math.cos((north-0.9996*6399593.625*( north/6366197.724/0.9996-0.006739496742*3/4*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.pow(0.006739496742*3/4,2)*5/3*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2* north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4-Math.pow(0.006739496742*3/4,3)*35/27*(5*(3*(north/6366197.724/0.9996+Math.sin(2*north/6366197.724/0.9996)/2)+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/4+Math.sin(2*north/6366197.724/0.9996)*Math.pow(Math.cos(north/6366197.724/0.9996),2)*Math.pow(Math.cos(north/6366197.724/0.9996),2))/3)) / (0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2))))*(1-0.006739496742*Math.pow((Easting-500000)/(0.9996*6399593.625/Math.sqrt((1+0.006739496742*Math.pow(Math.cos(north/6366197.724/0.9996),2)))),2)/2*Math.pow(Math.cos(north/6366197.724/0.9996),2))+north/6366197.724/0.9996))*180/Math.PI+Zone*6-183;
longitude=Math.round(longitude*10000000);
longitude=longitude/10000000;
}
}

- 869
- 1
- 8
- 14
-
1Maybe a short comment for better understanding would be great. – Robin Ellerkmann Jan 29 '15 at 21:23
-
Use These Classes Like This : UTM2Deg pos =new UTM2Deg("35 R 312915.84 4451481.33") And lat=pos.latitude. – user2548538 Jan 30 '15 at 07:57
-
3
-
The UTM WGS-84 projection does, so if the code correctly converts into that projection, then it does that (not verified.) Looking at the math, it seems to use some factors to squash the spheroid, so there's some chance it's right (although I see 0.9996, which is off by an order of magnitude from the 0.996 ratio between pole/equator, so it could also be wrong, not for lack of trying, but for being wrong :-) As with any GIS math package, you have to verify it thoroughly before you trust it, or your ship will hit a rock. – Jon Watte Mar 22 '15 at 19:05
-
5I know that this thread is so old by now, but I wanted to thank you for providing this code. IT IS AWESOMELY-SIMPLE AND SURPRISINGLY PRECISE! – Aleksandar Stefanović Jun 20 '15 at 22:31
-
@JonWatte 0.9996 is the central scaling factor of the UTM projection. It is not meant to deal with the ellipsoidal shape of the earth, that is taken care of elsewhere in the formulae. – Trygve Flathen Aug 02 '15 at 11:51
-
10with no licence and an encourangement to "copy this", I cleaned up the code a bit, made it a bit more object oriented and in line with some java idioms. The result is uploaded to [github](https://github.com/rolfrander/geo) – Rolf Rander Dec 29 '15 at 00:07
-
@user2548538 thanks for the code, it seems to work just fine. Could you give more details about its source and its license? – ocroquette Mar 28 '16 at 17:21
-
3WARNING: I think I discovered a bug in this code. I believe if (Letter<'M') Northing = Northing + 10000000; should be if (Letter<='M') Northing = Northing + 10000000; Otherwise you can get negative UTM Northings. – Apr 01 '17 at 01:24
-
-
1While this code is amazing and many thanks to the author, it is off by a couple of centimeters comparing to known libraries. My needs are to be millimeter precise and I need a super fast conversion (I am converting hundred times a second and existing libraries are slow). If the author could extract constants from the code, so perhaps eath's polar raius or eath's equatorial radius can be set this will become even more amazing code! please break the code to editable constants! – Elad Lavi May 05 '19 at 07:55
-
What if I want to deliberately convert to the coordinates of a specific zone? Point clouds are often in UTM format, but if you create a Point cloud that crosses UTM zone borders you want to remain in the same UTM Zone, even though that will introduce some error, as otherwise, the Points don't have a consistent coordinate system. Also, it would be lovely to have this explained a little better, or at least have the constants as named constants. – Emil S. Dec 18 '22 at 09:49
I was able to use Geotools 2.4 to get something that works, based on some example code.
double utmZoneCenterLongitude = ... // Center lon of zone, example: zone 10 = -123
int zoneNumber = ... // zone number, example: 10
double latitude, longitude = ... // lat, lon in degrees
MathTransformFactory mtFactory = ReferencingFactoryFinder.getMathTransformFactory(null);
ReferencingFactoryContainer factories = new ReferencingFactoryContainer(null);
GeographicCRS geoCRS = org.geotools.referencing.crs.DefaultGeographicCRS.WGS84;
CartesianCS cartCS = org.geotools.referencing.cs.DefaultCartesianCS.GENERIC_2D;
ParameterValueGroup parameters = mtFactory.getDefaultParameters("Transverse_Mercator");
parameters.parameter("central_meridian").setValue(utmZoneCenterLongitude);
parameters.parameter("latitude_of_origin").setValue(0.0);
parameters.parameter("scale_factor").setValue(0.9996);
parameters.parameter("false_easting").setValue(500000.0);
parameters.parameter("false_northing").setValue(0.0);
Map properties = Collections.singletonMap("name", "WGS 84 / UTM Zone " + zoneNumber);
ProjectedCRS projCRS = factories.createProjectedCRS(properties, geoCRS, null, parameters, cartCS);
MathTransform transform = CRS.findMathTransform(geoCRS, projCRS);
double[] dest = new double[2];
transform.transform(new double[] {longitude, latitude}, 0, dest, 0, 1);
int easting = (int)Math.round(dest[0]);
int northing = (int)Math.round(dest[1]);

- 61,876
- 75
- 195
- 257
-
can you please look into this?https://stackoverflow.com/questions/61995654/how-to-convert-x-and-y-to-lat-and-long/61995863#61995863 – Gen Jun 01 '20 at 06:07
The transformation of a coordinate can actually be done in only a few lines of code:
Coordinate coordinate = new Coordinate(x, y);
MathTransform transform = CRS.findMathTransform(CRS.decode("EPSG:4326"), CRS.decode("EPSG:3857"), false);
JTS.transform(coordinate, coordinate, transform);
This will transform a longitude/latitude coordinate (EPSG:4326) into Web Mercator projection (EPSG:3857) coordinate.
You just need to depend on the following two GeoTools libraries in your build tool (e.g. maven):
<repositories>
<repository>
<id>osgeo</id>
<name>Open Source Geospatial Foundation Repository</name>
<url>http://download.osgeo.org/webdav/geotools/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-api</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>${geotools.version}</version>
</dependency>
</dependencies>
This answer builds on a question/reply on gis.stackexchange.com. Posted my reply because the current answers here seem to be quite verbose.
For my projects I've using the library LatLongLib, from Ahmed Taha. I think that it's very easy to convert coordinates from the UTM system to the Latitude-Longitude system and vice-versa. You just need to play with the classes UTMUtils, UTMPoint and LatLonPoint.
Time ago I also considered choosing Jcoord. It was easy and straight to the point too. However, I needed to use the WGS84 ellipsoid and, at that time, only LatLongLib seemed to have that feature.

- 151
- 1
- 3
- 4
-
Jcoord now supports WGS84, I'm using it to convert WGS84 to UK Ordnance survey and the results are excellent. – UnixNerd Sep 05 '19 at 16:47
You can use this project https://github.com/Berico-Technologies/Geo-Coordinate-Conversion-Java/, adding it to your existing pom.xml using jitpack.
I've successfully been able to convert UTM coordinates (30N, this is, 30 zone and northern hemisphere) to Latitude and Longitude. See my example below:
public void setPunto(Point punto) {
this.punto = punto;
LatLon latlon = UTMCoord.locationFromUTMCoord(30, AVKey.NORTH, punto.getX(), punto.getY());
this.latitud = latlon.getLatitude().degrees;
this.longitud = latlon.getLongitude().degrees;
}
Note that Point class is of com.vividsolutions.jts.geom.Point class type.

- 1,775
- 6
- 31
- 59