将 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()
我希望能够生成这个:
- 旋转需要保留 aspect-ratio 空间数据
- 我需要支持任意旋转,不受90度跳跃的约束
- 我希望可以保留实际的网格(以保留
ggplot2
中的默认行为),但如果不能,我可以手动生成它们
- 我希望通用图的封闭矩形保持不变,包括(此处未显示)标题和副标题
- 我希望坐标系不变,这意味着我可以添加其他图层,这些图层随后会自动欣赏旋转。 @qdread 的回答给出了替代方案,其中单个函数可以计算旋转(尽管我不知道如何用空间数据在数学上做到这一点)
- 请无视:
- top-left角的网格问题,我的photoshop不完整
- x-axis标签,数字是错误的(y-axis是good-ish)(可以去掉,不是必须的)
我想知道这是否可以通过 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.
类似于
例如,如果我以此开头:
library(ggplot2)
usa <- maps::map('usa', plot=FALSE)
ggplot(as.data.frame(usa[c("x","y")]), aes(x,y)) +
coord_quickmap() +
geom_path()
我希望能够生成这个:
- 旋转需要保留 aspect-ratio 空间数据
- 我需要支持任意旋转,不受90度跳跃的约束
- 我希望可以保留实际的网格(以保留
ggplot2
中的默认行为),但如果不能,我可以手动生成它们 - 我希望通用图的封闭矩形保持不变,包括(此处未显示)标题和副标题
- 我希望坐标系不变,这意味着我可以添加其他图层,这些图层随后会自动欣赏旋转。 @qdread 的回答给出了替代方案,其中单个函数可以计算旋转(尽管我不知道如何用空间数据在数学上做到这一点)
- 请无视:
- top-left角的网格问题,我的photoshop不完整
- x-axis标签,数字是错误的(y-axis是good-ish)(可以去掉,不是必须的)
我想知道这是否可以通过 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.