You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 16:36:03