将 ggplot2 地图旋转到任意角度

rotate ggplot2 maps to arbitrary angles

类似于,但我不希望image/square旋转,我希望数据在框架内旋转。

例如,如果我以此开头:

library(ggplot2)
usa <- maps::map('usa', plot=FALSE)

ggplot(as.data.frame(usa[c("x","y")]), aes(x,y)) +
  coord_quickmap() +
  geom_path()

我希望能够生成这个:

我想知道这是否可以通过 CRS/projections 来完成,但我对它们不够聪明,无法使用它 formally/correctly.

为了在旋转时保持地图的形状,我们首先需要将经纬度转换为 conformal coordinate system where local angles are preserved. We will use a Lambert conformal conic projection, specifically ESRI:102004 for contiguous USA。我们将 usa 对象强制转换为 sf 对象并应用 CRS 转换。

lambert_proj4 <- '+proj=lcc +lat_1=33 +lat_2=45 +lat_0=39 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs'

library(sf)

usa_trans <- usa %>% 
  st_as_sf %>%
  st_transform(crs = lambert_proj4)

结果如下所示:

ggplot(usa_trans) + geom_sf()

接下来我们修改 sf documentation on affine transformations 中的过程以围绕其质心旋转几何体。

下面的函数定义了变换矩阵。

rot = function(a) matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2)

从变换后的对象中提取几何图形,然后获取质心。这将return十点,因为除了大陆之外,还有9个大岛。 (例如长岛)。

usa_geom <- st_geometry(usa_trans)
usa_centroid <- st_geometry(st_centroid(usa_trans))

因此我们取大陆多边形的质心usa_centroid[1,]减去它,逆时针旋转45度,再加回质心。

usa_rot_geom <- (usa_geom - usa_centroid[1,]) * rot(-45 * 2*pi/360) + usa_centroid[1,]

结果如下所示:

ggplot(usa_rot_geom) + geom_sf()

最后,如果需要,您可以在绘图之前再次反向转换为经纬度。

usa_rot_latlong <- usa_rot_geom %>%
  st_set_crs(lambert_proj4) %>%
  st_transform(4326) %>%
  st_as_sf

结果:

ggplot(usa_rot_latlong) + geom_sf()

edit2: 使用 oblique mercator projection 旋转地图:

#crs with 45degree shift using +gamma
# lat_0 and lonc approximate centroid of US
crs_string = +proj=omerc +lat_0=36.934 +lonc=-90.849 +alpha=0 +k_0=.7 +datum=WGS84 +units=m +no_defs +gamma=45"

# states data & libraries in code chunk below
ggplot(states) +
    geom_sf() +
    geom_sf(data = x, color = 'red') +
    coord_sf(crs = crs_string, 
             xlim = c(-3172628,2201692),    #wide limits chosen for animation
             ylim = c(-1951676,2601638)) +  # set as needed
    theme_bw() +
    theme(axis.text = element_blank()) 

来自 0:360 的 gamma 动画,增量为 10deg,alpha 常数为 0。伪影来自 gif 压缩,实际图看起来像上面标记为 gamma 45 的图。

先前的回答: 我认为您可以通过将投影更改为 Lambert 方位角等面积并调整投影字符串中的 +lon_0=x,从不同的角度 'looking' 在地球上 'rotate' 绘图(包括经纬网)。

这应该可以满足您的大部分目标,但我不知道如何获得精确的度数旋转。

下面我在绘图之前手动转换了 states_sf sf 对象。通过使用数据的 crs 4326 并在 ggplot() + 调用的末尾添加 + coord_sf(crs = "+proj=laea +x_0=0 +y_0=0 +lon_0=-140 +lat_0=40") 来转换绘图(以及所有正在绘制的 sf 数据)可能更容易。

library(sf)
#> Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
library(urbnmapr)
library(tidyverse)

# get sf of the us, remove AK & HI, 
#  transform to crs 4326 (lat & lon)
  states_sf <- get_urbn_map("states", sf = TRUE) %>%
    filter(!state_abbv %in% c('AK', 'HI')) %>%
    st_transform(4326)

  #centroid of us to 'look' at the US from directly above the center
  us_centroid <- st_union(states_sf) %>% st_centroid() %>% st_transform(4326)

  st_coordinates(us_centroid)
#>           X        Y
#> 1 -99.38208 39.39364
  
# Plots, changing the +lon_0=xxx of the projection
  p1 <- states_sf %>% 
  st_transform("+proj=laea +x_0=0 +y_0=0 +lon_0=-99.382 +lat_0=39.394") %>%
  ggplot(aes()) +
  geom_sf(fill = "black", color = "#ffffff") +
    ggtitle('US from above its centroid 39.3N 99.4W')

  p2 <- states_sf %>% 
    st_transform("+proj=laea +x_0=0 +y_0=0 +lon_0=-70 +lat_0=40") %>%
    ggplot(aes()) +
    geom_sf(fill = "black", color = "#ffffff") +
    ggtitle('US from 40N 70W')
  
  p3 <- states_sf %>% 
    st_transform("+proj=laea +x_0=0 +y_0=0 +lon_0=-50 +lat_0=40") %>%
    ggplot(aes()) +
    geom_sf(fill = "black", color = "#ffffff") +
    ggtitle('US from 40N 50W')
  
  p4 <- states_sf %>% 
    st_transform("+proj=laea +x_0=0 +y_0=0 +lon_0=-30 +lat_0=40") %>%
    ggplot(aes()) +
    geom_sf(fill = "black", color = "#ffffff") +
    ggtitle('US from 40N 30W')
  
  p5 <- states_sf %>% 
    st_transform("+proj=laea +x_0=0 +y_0=0 +lon_0=-10 +lat_0=40") %>%
    ggplot(aes()) +
    geom_sf(fill = "black", color = "#ffffff") +
    ggtitle('US from 40N 10W')
  
  p6 <- states_sf %>% 
    st_transform("+proj=laea +x_0=0 +y_0=0 +lon_0=-140 +lat_0=40") %>%
    ggplot(aes()) +
    geom_sf(fill = "black", color = "#ffffff") +
    ggtitle('US from 40N 140W')
  
 p1

 p3

 p5

 p6

reprex package (v0.3.0)

于 2021-03-31 创建

编辑:

看起来在使用投影 "+proj=omerc +lonc=-90 +lat_0=40 +gamma=0 +alpha=0" 时,您可以通过 alpha 和 gamma 的组合获得任何角度。我不知道它们到底是如何相关的(与方位角有关),但这应该有助于形象化:

# Basic template for rotating, keeping US map centered.
#  adjust alpha and gamma by trial & error
crs <- "+proj=omerc +lonc=90 +lat_0=40 +gamma=90 +alpha=0"
 
 states_sf %>% 
   ggplot() + 
   geom_sf(fill = 'green') +
   coord_sf(crs = crs)

更广泛的 alpha 和 gamma 动画 can be found here.