从经纬度确定UTM带号(用于转换)

33

我正在编写一个程序,需要输入一些纬度/经度点,并将它们内部转换为UTM以进行一些以米为单位的计算。

这些纬度/经度点的范围相当小,大约为200米×200米。它们几乎总是可以在单个UTM区域内(除非你不幸地跨越了区域边界)。

但是,这些纬度/经度所处的区域是没有限制的。有一天,该程序可能会运行在澳大利亚的用户身上(哦,即使一个州横跨多少个区域,给我带来了多少痛苦...),而另一天则是墨西哥的用户。

我的问题是 - 是否有一种方法可以确定特定纬度/经度所在的区域,以便将其输入到转换库中(我目前使用proj4和R包rgdal)。

我的语言是R,但答案不一定要是R语言--也许只是一种简单的计算,或者我可以嵌入一个系统调用到proj可执行文件中。

谢谢。


http://stat.ethz.ch/R-manual/R-patched/library/base/html/timezones.html - aatrujillob
1
我建议将这个问题转到gis.stackexchange.com。 - Jesse Anderson
@AndresT - 这是关于时区的问题。我想要的是UTM区域。 - mathematical.coffee
@blindJesse - 我不知道有那个stackexchange,谢谢。 - mathematical.coffee
@mathematical.coffee 我对此一无所知,但似乎有一个名为PBSmapping的软件包和一个名为convUL的函数可以在经纬度和UTM之间进行转换。 - aatrujillob
6个回答

50

编辑:对于适用于全球所有非极地地区的非 R 代码,请参见此处此处


除非您处理来自一些特殊区域(斯瓦尔巴和挪威部分地区)的数据,否则这是一个足够简单的计算,您可以在R中自己完成。以下是维基百科对经度与UTM带号关系的描述

UTM系统将地球表面从南纬80°到北纬84°划分为60个区域,每个区域宽度为6°经度。第1带覆盖经度180°至174°W;向东编号逐渐增加,第60带覆盖174度至180度东经。

因此,假设您的数据中西经本初子午线的经度编码为-180至0度,请参考以下R代码实现:

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

那个表达式显然可以简化一下,但我认为在这种形式下,构建它的逻辑最清晰。 %% 60 部分是为了防止你的经度大于180或小于-180。

啊哈,这就是我想要的计算结果 - 我花了很长时间在Google上搜索“如何从纬度/经度计算UTM区域”,甚至没有想到去看Wiki。感谢! - mathematical.coffee
@ToolmakerSteve -- 感谢您提出的纠正意见(该意见被审查的 5 名编辑中的 3 名错误地拒绝了)。 - Josh O'Brien
1
@JoshO'Brien 公式太简单了:它不能处理挪威和斯瓦尔巴特群岛的两个UTM区域异常情况。 - AlexWien
@AlexWien -- 感谢您指出这一点!我已经进行了编辑,以便注意这些例外情况。既然您“在附近”,不介意看一下我最近的问题,并让我知道您是否有任何建议吗? - Josh O'Brien
2
附加说明:要获取北半球UTM的EPSG代码,请将其值加上32600。对于南半球UTM,请加上32700。 - Josh O'Brien
显示剩余3条评论

4

我使用之前的答案为自己编写了这个函数。 也许对这里的某些人有用 =)

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

3

我不懂R语言,但是我认为这段PL/SQL代码可以帮助你处理异常:

   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;

1
今天我遇到了一个问题,需要找到全球各地点的经纬度对应的UTM带。问题在于存在许多特殊情况,例如挪威斯瓦尔巴群岛和两极地区UTM curly edge cases(在这张地图上以红色标出),如果你认为它是规则的,那么你会被这些特殊情况搞糊涂!
下面是我的R函数,用于查找经纬度对应的UTM带,并在最后进行所有特殊情况的测试。
require(tidyverse)
require(purrr)
require(testthat)

find_one_utm_zone <- function(longitude, latitude) {

  # Special zones for Svalbard
  if (latitude >= 72.0 && latitude <= 84.0 ) {
    if (longitude >= 0.0  && longitude <  9.0)
      return("31X");
    if (longitude >= 9.0  && longitude < 21.0)
      return("33X")
    if (longitude >= 21.0 && longitude < 33.0)
      return("35X")
    if (longitude >= 33.0 && longitude < 42.0)
      return("37X")
  }
  # Special zones for Norway
  if (latitude >= 56.0 && latitude < 64.0 ) {
    if (longitude >= 0.0  && longitude <  3.0)
      return("31V");
    if (longitude >= 3.0  && longitude < 12.0)
      return("32V")
  }

  # North + South Poles

  if (latitude > 84.0){
    if ((longitude+180)%%360-180 < 0) {return("Y")}
    if ((longitude+180)%%360-180 > 0) {return("Z")}
  } else if (latitude < -80.0){
    if ((longitude+180)%%360-180 < 0) {return("A")}
    if ((longitude+180)%%360-180 > 0) {return("B")}
  }

  # Everything in the middle

  if ( (latitude>-80.0) && (latitude<=84.0) ){

    mid_zones <- LETTERS[c(3:8,10:14,16:24)] # C to X, skip I and O
    utm_letter <- mid_zones[ min(floor( (latitude + 80) / 8 )+1 , 20) ]
    utm_number <- (floor( (longitude + 180) / 6 ) %% 60) + 1 # modulo in case longitude is 0 to 360 instead of -180 to 180
    utm_zone <- paste0(utm_number, utm_letter)
    return(utm_zone)

  } else {
      stop("lat long not valid (or something else broke)")
    }
}
find_utm_zone <- function(lon, lat){
  purrr::map2_chr(.x = lon, .y = lat, .f = find_one_utm_zone)
}

使用示例
locs <-
  tibble(lon = c(-100,30,150, 4, 7, 22, 0, 12, -34, -20),
         lat = c(-45, 85, 12, 57, 81, 83, 5, -81, 85, 83),
         desired_utm_zone = c("14G","Z","56P", "32V" ,"31X","35X","31N", "B","Y","27X"))

locs2 <-
  locs %>%
  mutate(utm_zone = find_utm_zone(lon = lon,lat = lat))

测试它是否有效:

测试一下:

testthat::expect_equal(locs2$utm_zone, locs2$desired_utm_zone)

0

Python版本:

def get_utm_fromLatLon(lat, lon):
    #Special Cases for Norway and Svalbard
    if (lat > 55 and lat < 64 and lon > 2 and lon < 6):
        return 32
    elif (lat > 71 and lon >= 6 and lon < 9):
        return 31
    elif (lat > 71 and ((lon >= 9 and lon < 12) or (lon >= 18 and lon < 21))):
        return 33
    elif (lat > 71 and ((lon >= 21 and lon < 24) or (lon >= 30 and lon < 33))):
        return 35
    # Rest of the world
    elif (lon >= -180 and lon <= 180):
        return (math.floor((lon + 180) / 6) % 60) + 1
    else:
        raise ValueError('Cannot figure out UTM zone from given Lat: {0}, Lon: {1}.'.format(lat, lon))


0

基于Luiz Bondi的总结的TypeScript版本:

export function utmZoneFromLatLng(lat: number, lon: number) {
  // Special Cases for Norway & Svalbard
  if (lat > 55 && lat < 64 && lon > 2 && lon < 6) {
    return 32;
  }
  if (lat > 71 && lon >= 6 && lon < 9) {
    return 31;
  }
  if (lat > 71 && ((lon >= 9 && lon < 12) || (lon >= 18 && lon < 21))) {
    return 33;
  }
  if (lat > 71 && ((lon >= 21 && lon < 24) || (lon >= 30 && lon < 33))) {
    return 35;
  }
  // Rest of the world
  if (lon >= -180 && lon <= 180) {
    return (Math.floor((lon + 180) / 6) % 60) + 1;
  }

  throw new Error(`utmZoneFromLatLng: Cannot figure out UTM zone from give Lat: ${lat}, Lng: ${lon}`);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接