Determining UTM zone (to convert) from longitude/latitude
I'm writing a program which expects a number of lat/long points, and I convert them internally to UTM in order to do some calculations in metres.
The range of the lat/long points themselves is quite small -- about 200m x 200m. They can be relied on almost always to be within a single UTM zone (unless you get unlucky and are across the border of a zone).
However, the zone that the lat/longs are in is unrestricted. One day the program might be run for people in Australia (and oh, how many zones does even a single state lie across, and how much pain has that caused me already...), and another day for people in Mexico.
My question is -- is there a way to determine which zone a particular long/lat is in so that it may be fed into a conversion library (I currently use proj4 and also the R package rgdal
).
My language is R, but the answer doesn't have to be -- maybe it's just a simple calculation, or maybe I can embed a system call to the proj
exectuable.
cheers.
Solution 1:
Edit: For (non-R) code that works for all non-polar areas on earth, see here or here.
Unless you are dealing with data from a couple of exceptional areas (Svalbard and parts of Norway), this is a simple enough calculation that you might as well just do it yourself in R. Here is Wikipedia's description of how longitude relates to UTM Zone number:
The UTM system divides the surface of Earth between 80°S and 84°N latitude into 60 zones, each 6° of longitude in width. Zone 1 covers longitude 180° to 174° W; zone numbering increases eastward to zone 60 that covers longitude 174 to 180 East.
So, assuming that in your data longitudes to the west of the Prime Meridian are encoded as running from -180 to 0 degrees, here's an R-code version of the above:
long2UTM <- function(long) {
(floor((long + 180)/6) %% 60) + 1
}
# Trying it out for San Francisco, clearly in UTM Zone 10
# in the figure in the Wikipedia article linked above
SFlong <- -122.4192
long2UTM(SFlong)
# [1] 10
That expression could obviously be simplified a bit, but I think in this form the logic underlying its construction is most clear. The %% 60
bit is in there just in case some of your longitudes are greater than 180 or less than -180.
Solution 2:
I don't know r-code but I suppose this PL/SQL code can help you with the exceptions:
UTMZone := Trunc((lon - Zone0WestMeridian) / d);
--Special Cases for Norway & Svalbard
CASE
WHEN (lat > 55) AND (UTMZone = 31) AND (lat < 64) AND (lon > 2) THEN UTMZone := 32;
WHEN (lat > 71) AND (UTMZone = 32) AND (lon < 9) THEN UTMZone := 31;
WHEN (lat > 71) AND (UTMZone = 32) AND (lon > 8) THEN UTMZone := 33;
WHEN (lat > 71) AND (UTMZone = 34) AND (lon < 21) THEN UTMZone := 33;
WHEN (lat > 71) AND (UTMZone = 34) AND (lon > 20) THEN UTMZone := 35;
WHEN (lat > 71) AND (UTMZone = 36) AND (lon < 33) THEN UTMZone := 35;
WHEN (lat > 71) AND (UTMZone = 36) AND (lon > 32) THEN UTMZone := 37;
ELSE UTMZone := UTMZone;
END CASE;
Solution 3:
I made this function for me using the previous answers. Maybe it is useful to someone here =)
utmzone <- function(lon,lat) {
## Special Cases for Norway & Svalbard
if (lat > 55 & lat < 64 & lon > 2 & lon < 6){
band <- 32
} else {
if (lat > 71 & lon >= 6 & lon < 9){
band <- 31
} else {
if (lat > 71 & lon >= 9 & lon < 12){
band <- 33
} else {
if (lat > 71 & lon >= 18 & lon < 21){
band <- 33
} else {
if (lat > 71 & lon >= 21 & lon < 24){
band <- 35
} else {
if (lat > 71 & lon >= 30 & lon < 33){
band <- 35
} else {
## Rest of the world
if (lon >= -180 & lon <= 180){
band <- (floor((lon + 180)/6) %% 60) + 1
} else {
band <- "something is wrong"
}}}}}}}
return(band)
}
utmzone(-43,-22)
#[1] 23