Определение UTM зоны (для конвертации) по долготе / широте
Я пишу программу, которая ожидает несколько широт / длинных точек, и я конвертирую их внутренне в UTM, чтобы выполнить некоторые вычисления в метрах.
Диапазон самих широт / длинных точек довольно мал - около 200 х 200 м. На них почти всегда можно положиться, что они находятся в пределах одной зоны UTM (если вам не повезло и вы оказались за границей зоны).
Однако зона, в которой находятся латы / лонги, не ограничена. Однажды программа может быть запущена для людей в Австралии (и, о, сколько зон находится даже в одном штате, и сколько боли это уже причинило мне...), и еще один день для людей в Мексике.
Мой вопрос - есть ли способ определить, в какой зоне находится конкретный long/lat, чтобы он мог быть передан в библиотеку конвертации (в настоящее время я использую proj4, а также пакет R) rgdal
).
Мой язык - R, но ответ не должен быть - возможно, это просто простой расчет, или, возможно, я могу встроить системный вызов в proj
exectuable.
веселит.
6 ответов
Изменить: Для (не R) кода, который работает для всех неполярных областей на земле, см. Здесь или здесь.
Если вы не имеете дело с данными из нескольких исключительных областей ( Шпицберген и часть Норвегии), это достаточно простое вычисление, так что вы можете просто сделать это самостоятельно в R. Вот описание Википедии о том, как долгота связана с номером зоны UTM:
Система UTM делит поверхность Земли между 80°S и 84°N широты на 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.
Я сделал эту функцию для себя, используя предыдущие ответы. Может тут кому пригодится =)
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
Я не знаю 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;
Итак, сегодня у меня возникла проблема: мне нужно было найти зону UTM от широты / долготы. Вот мой сценарий R с тестами в конце для некоторых из фигурных краев, таких как Шпицберген, Норвегия и полюса:
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)
Версия для 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}`);
}
Версия 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))