У меня есть многоугольник в равновеликой проекции Северной Америки, и я хотел бы преобразовать его в неспроецированную долготу/широту. Я хотел бы использовать его для обрезки глобального растра в непроецируемом лонглате.
Проблема в том, что он пересекает линию даты и возвращает проблемный многоугольник. Есть ли способ вместо этого вернуть два многоугольника, по одному по обе стороны от линии даты?
Я также открыт для других предложений.
Примечание. Растр, который я собираюсь обрезать, является глобальным и имеет разрешение 1 км. Поэтому я стараюсь избежать необходимости проецировать этот растр, так как это будет связано с большими вычислительными затратами.
library(terra)
library(sf)
library(rnaturalearth)
ee <- ext(c(-4342031, 3457969, -3389091, 4710909))
ee <- as.polygons(ee, crs = "+proj=aea +lat_0=40 +lon_0=-96 +lat_1=20 +lat_2=60 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs")
ee <- st_transform(st_as_sf(ee), 4326)
world <- ne_coastline()
plot(world, lwd =0.5)
plot(ee, add=T)
Существует функция sf::st_break_antimeridian()
, которая отлично подходит для подобных задач, однако она работает только для непроецируемых координат (GCS).
Поскольку ваш объект ee проецируется и становится «проблемным» при преобразовании в WGS84/EPSG:4326, простым решением является:
sf::st_break_antimeridian()
, чтобы разделить/сломать ее на антимеридианеПоскольку ваш SpatRaster имеет высокое разрешение, например. Размер ячейки 1 км, возможно, вам понадобится crop()
и mask()
в два этапа. Когда я попробовал выполнить один шаг, значения в результате были неправильными.
library(terra)
library(sf)
library(rnaturalearth)
library(ggplot2)
library(tidyterra)
# CRS projection strings
prj_aea <- "+proj=aea +lat_0=40 +lon_0=-96 +lat_1=20 +lat_2=60 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs"
prj_wgs84_180 <- "+proj=longlat +datum=WGS84 +lon_0=180"
# Create sf object, project to modified WGS84/EPSG:4326 CRS, split at anti-meridian,
# project back to standard WGS84/EPSG:4326
ee <- as.polygons(ext(c(-4342031, 3457969, -3389091, 4710909)), crs = prj_aea) |>
st_as_sf() |>
st_transform(prj_wgs84_180) |>
st_break_antimeridian(lon_0 = 180) |>
st_transform(4326)
# Example SpatRaster with a global extent at approximately 1km resolution
r <- rast(ext(-180, 180, -90, 90), res = 1000/111320, crs = "EPSG:4326")
set.seed(1)
r[] <- sample(1:5, ncell(r), replace = TRUE)
# Crop, then mask r using ee
r1 <- crop(r, ee)
r1 <- mask(r1, ee)
# Plot
ggplot() +
geom_spatraster(data = r1) +
scale_fill_gradient2(low = "#0072B2",
mid = "#ffff6d", midpoint = 3,
high = "#C74A8F",
na.value = "transparent") +
geom_sf(data = ne_coastline(), colour = "grey45", linewidth = 0.1) +
coord_sf(expand = FALSE) +
theme(panel.background = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
Ниже приведен оригинальный ответ, практичный только в том случае, если ваши растровые данные имеют относительно низкое разрешение.
library(terra)
library(sf)
library(rnaturalearth)
library(ggplot2)
# CRS projection strings
prj_aea <- "+proj=aea +lat_0=40 +lon_0=-96 +lat_1=20 +lat_2=60 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs"
prj_wgs84_180 <- "+proj=longlat +datum=WGS84 +lon_0=180"
# Create sf object, project to modified WGS84 / EPSG:4326 CRS
ee <- as.polygons(ext(c(-4342031, 3457969, -3389091, 4710909)), crs = prj_aea) |>
st_as_sf() |>
st_transform(prj_wgs84_180)
# Create world sf, split at anti-meridian, project to modified WGS84 / EPSG:4326
# Will throw a warning, which you can safely ignore
world <- ne_coastline() |>
st_break_antimeridian(lon_0 = 180) |>
st_transform(prj_wgs84_180)
ggplot() +
geom_sf(data = world, colour = "grey") +
geom_sf(data = ee, fill = NA, linewidth = 1) +
theme_void()
Вот рабочий процесс обрезки растра. Предполагается, что ваш растр представляет собой SpatRaster с WGS84/EPSG:4326 в качестве CRS. Убедитесь, что значения nrow и ncol для объекта r1 соответствуют размерам ваших фактических данных.
# Create example SpatRaster with global extent to represent your data
r <- rast(ext(-180, 180, -90, 90), nrow = 180, ncol = 360, crs = "EPSG:4326")
set.seed(1)
r[] <- sample(1:5, ncell(r), replace = TRUE)
# Project r to same CRS as ee
r1 <- rast(ext(0, 360, -90, 90), nrow = 180, ncol = 360, crs = prj_wgs84_180)
r1 <- project(r, r1)
r1 <- rotate(r1, 180)
# Crop r1 using ee, project back to standard WGS84/EPSG:4326
r2 <- crop(r1, ee, mask = TRUE)
r3 <- project(r2, r)
Постройте график результатов для сравнения:
library(tidyterra)
library(patchwork)
p1 <- ggplot() +
geom_spatraster(data = r2) +
scale_fill_gradient2(low = "#0072B2",
mid = "#ffff6d", midpoint = 3,
high = "#C74A8F",
na.value = "transparent") +
geom_sf(data = world, colour = "grey45", linewidth = 0.1) +
labs(title = "Projected to custom CRS and cropped") +
coord_sf(expand = FALSE) +
theme(panel.background = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
p2 <- ggplot() +
geom_spatraster(data = r3) +
scale_fill_gradient2(low = "#0072B2",
mid = "#ffff6d", midpoint = 3,
high = "#C74A8F",
na.value = "transparent") +
geom_sf(data = ne_coastline(), colour = "grey45", linewidth = 0.1) +
labs(title = "Cropped data projected back to standard WGS84") +
coord_sf(expand = FALSE) +
theme(legend.position = "none",
panel.background = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
p1 + p2
Обратите внимание, что изображение выше имеет относительно низкое разрешение, поэтому если вы увеличите масштаб, появятся расхождения в цветах некоторых одинаковых ячеек (особенно по краям). Эта проблема не сохраняется при разрешении 600 точек на дюйм.
Это интересное решение, @l-tyrone, спасибо! Однако я стараюсь избежать необходимости преобразовывать растр, который мне нужно обрезать, поэтому это работает, но для меня не идеально. Добавлю примечание к своему вопросу.