在ggplot2的geom_sf生成图像上叠加绘点的位置偏移问题
问题描述
我尝试在ggplot2+geom_sf生成的图像上叠加绘制点。用普通ggplot做模拟示例时,叠加的点位置完全正确;但切换为geom_sf生成图像后,点的Y轴位置正确,X轴却明显偏移。
正常工作的普通ggplot示例代码
library(ggplot2) library(magick) library(sf) df <- data.frame(x = 1:10, y = 1:10) ggplot(df) + geom_point(aes(x = x, y = y)) + theme_void() + scale_x_continuous(breaks = 1:10, limits = c(0, 11), expand = c(0, 0)) + scale_y_continuous(breaks = 1:10, limits = c(0, 11), expand = c(0, 0)) ggsave("My image.png", width = 5, height = 5, units = "in") img <- image_read("My image.png") plot(NULL, xlim = c(0, 11), ylim = c(0, 11), axes=FALSE, xlab = "", ylab = "") rasterImage(img, 0, 0, 11, 11) points(df$x, df$y, col = "red")
geom_sf生成图像时出现偏移的代码
采用相同方法但使用geom_sf生成图像时,点会偏离真实位置(应绘制在矩形的两个角上):
rect <- data.frame(lon = c(-94.9, -93), lat = c(32, 35)) rect_sf <- st_polygon(list(cbind( rect$lon[c(1,2,2,1,1)], rect$lat[c(1,1,2,2,1)]))) %>% st_sfc(crs = 4326) ggplot() + geom_sf(data = rect_sf, fill = "grey85", colour = "black") + theme_void() + coord_sf(xlim = rect$lon, ylim = rect$lat, expand = FALSE) ggsave("My image.png", width = 5, height = 5, units = "in") img <- image_read("My image.png") plot(NULL, xlim = rect$lon, ylim = rect$lat, axes=FALSE, xlab = "", ylab = "") rasterImage(img, rect$lon[1], rect$lat[1], rect$lon[2], rect$lat[2]) points(rect$lon, rect$lat, col = "red")
验证纵横比影响的代码
我推测问题出在保存图像的纵横比上。以下是使用两种不同保存选项(宽型和高型)的代码,生成图像后叠加的红点偏移程度会相应变化。偏移量似乎是图像生成时因纵横比错误添加的空白区域。请问该如何指定正确的纵横比?
rect <- data.frame(lon = c(-94.9, -93), lat = c(32, 35)) rect_sf <- st_polygon(list(cbind( rect$lon[c(1,2,2,1,1)], rect$lat[c(1,1,2,2,1)]))) %>% st_sfc(crs = 4326) ggplot() + geom_sf(data = rect_sf, fill = "grey85", colour = "black") + theme_void() + coord_sf(xlim = rect$lon, ylim = rect$lat, expand = FALSE) ggsave("My image.png", width = 8, height = 2, units = "in") ggsave("My image.png", width = 3, height = 9, units = "in") img <- image_read("My image.png") plot(NULL, xlim = rect$lon, ylim = rect$lat, axes=FALSE, xlab = "", ylab = "") rasterImage(img, rect$lon[1], rect$lat[1], rect$lon[2], rect$lat[2]) points(rect$lon, rect$lat, col = "red")
解决方案
问题核心在于geom_sf配合coord_sf时会自动应用地理坐标系的纵横比约束,手动设置的ggsave宽高比和地图实际地理宽高比不匹配,导致ggplot自动添加空白区域,最终叠加时坐标错位。
步骤1:计算地图的正确纵横比
对于WGS84(EPSG:4326)经纬度投影,需要将经纬度范围转换为实际距离比例(经度距离随纬度变化):
# 获取地理范围的宽度和高度 x_range <- diff(rect$lon) y_range <- diff(rect$lat) # 计算中心点纬度,修正经度的实际距离比例 mid_lat <- mean(rect$lat) x_km_per_deg <- 111 * cos(mid_lat * pi / 180) y_km_per_deg <- 111 # 计算符合地理投影的宽高比 aspect_ratio <- (x_range * x_km_per_deg) / (y_range * y_km_per_deg)
步骤2:用正确的纵横比保存图像
在ggsave中绑定计算好的比例,避免手动设置宽高带来的错位:
# 方法1:指定宽度,自动匹配高度 ggsave("My image.png", width = 5, units = "in", aspect.ratio = aspect_ratio) # 方法2:手动计算高度后指定 height <- 5 / aspect_ratio ggsave("My image.png", width = 5, height = height, units = "in")
步骤3:验证叠加效果
使用正确比例保存的图像,叠加点时位置会完全匹配:
img <- image_read("My image.png") plot(NULL, xlim = rect$lon, ylim = rect$lat, axes=FALSE, xlab = "", ylab = "") rasterImage(img, rect$lon[1], rect$lat[1], rect$lon[2], rect$lat[2]) points(rect$lon, rect$lat, col = "red")
替代方案:直接在ggplot中叠加点
如果不需要导出后再叠加,更简单的方式是直接在生成地图时添加点,完全避免坐标转换问题:
ggplot() + geom_sf(data = rect_sf, fill = "grey85", colour = "black") + geom_sf(data = st_as_sf(rect, coords = c("lon", "lat"), crs = 4326), color = "red") + theme_void() + coord_sf(xlim = rect$lon, ylim = rect$lat, expand = FALSE)
内容的提问来源于stack exchange,提问作者user2602640
相关产品推荐
相关产品推荐

