非格林尼治中心的sf对象如何使用geom_segment或geom_spatial_segment?
在ggplot2的geom_sf地图中添加路径:非格林尼治中心/不同CRS下的解决方案
我想在ggplot2的geom_sf生成的地图里添加路径,熟悉以格林尼治为中心的标准地图绘制,但更改地图CRS后,常规方法就失效了。
正常工作的格林尼治中心地图
这段代码可在格林尼治中心的WGS84(EPSG:4326)地图上正常绘制点和路径:
point_df=data.frame( location=c('Tokyo','Brasilia','France'), lat=c(35.65,-15.79,48.86), long=c(139.84,-47.88,2.35) ) point_df2=data.frame( from_location=c('Tokyo','Brasilia'), from_lat=c(35.65,-15.79), from_long=c(139.84,-47.88), to_location=c('France'), to_lat=c(48.86), to_long=c(2.35) ) library("rnaturalearth") world <- ne_countries(scale = "medium", returnclass = "sf") transpoint = st_as_sf(point_df,coords=c("long","lat"),crs=4326) world_sf <- st_transform(world, crs=4326) ggplot(world_sf)+geom_sf()+ geom_point(data=transpoint,aes(geometry=geometry,color=location),stat="sf_coordinates")+ geom_text_repel(data=transpoint,aes(geometry=geometry,label=location),stat="sf_coordinates")+ geom_curve(data=point_df2,aes(x=from_long,y=from_lat,xend=to_long,yend=to_lat),colour='blue')
(对应效果:格林尼治中心地图,点和路径均正常显示)
失效场景:非格林尼治中心或不同CRS的地图
但换成以太平洋为中心的地图或椭圆投影地图时,原有方法完全失效。
基于sf::st_break_antimeridian()的太平洋中心地图
针对太平洋中心地图,改编代码后geom_point可正常工作,但geom_segment完全失效:
robinson <- "+proj=robin +lon_0=-90 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs" world_pac <- world_sf %>% st_break_antimeridian(lon_0 = -90) %>% st_transform(crs = robinson) transpoint = st_as_sf(point_df,coords=c("long","lat"),crs=4326) dtran = st_transform(transpoint,robinson) library(ggrepel) ggplot(world_pac)+geom_sf()+ geom_point(data=dtran,aes(geometry=geometry,color=location),stat="sf_coordinates")+ geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat="sf_coordinates")+ geom_segment(data=point_df2, aes(x=from_long,y=from_lat,xend=to_long,yend=to_lat),colour='blue')
(对应效果:太平洋中心地图使用geom_segment,路径完全不显示)
尝试使用ggspatial::geom_spatial_segment()能显示线段,但效果异常:
ggplot(world_pac)+geom_sf()+ geom_point(data=dtran,aes(geometry=geometry,color=location),stat="sf_coordinates")+ geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat="sf_coordinates")+ geom_spatial_segment(data=point_df2, aes(x=from_long,y=from_lat,xend=to_long,yend=to_lat),crs=4326,wrap_dateline = FALSE,colour='blue')
(对应效果:太平洋中心地图使用geom_spatial_segment,路径穿过地图边缘,显示异常)
椭圆投影地图(CRS=2163)
转换为CRS=2163时,geom_segment和geom_spatial_segment都无法正常工作:
world_ellipse <- world_sf %>% st_transform(crs=2163) transpoint = st_as_sf(point_df,coords=c("long","lat"),crs=4326) dtran = st_transform(transpoint,crs=2163) ggplot(world_ellipse)+geom_sf()+ geom_point(data=dtran,aes(geometry=geometry,color=location),stat="sf_coordinates")+ geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat="sf_coordinates")+ geom_spatial_segment(data=point_df2,aes(from_long, from_lat, xend = to_long, yend = to_lat),crs = 2163,wrap_dateline = FALSE)
(对应效果:椭圆投影地图使用geom_spatial_segment,路径显示异常)
问题核心与解决方案
你忽略的关键点
- CRS不匹配:地图转换为目标CRS后,仍用WGS84经纬度直接绘制路径,坐标系统不兼容导致路径无法正确映射。
- 跨日界线处理缺失:太平洋中心地图跨越日界线,常规线段绘制不会自动拆分路径,导致穿模或不显示。
- 参数错误:椭圆投影场景中,
geom_spatial_segment的crs参数应传入原始数据的WGS84(4326),而非目标地图的2163。
通用解决方案:将路径转换为sf的LINESTRING对象,统一CRS
正确做法是先把路径数据转为sf的LINESTRING类型,再和地图统一转换到目标CRS,让geom_sf直接识别绘制。
步骤1:将路径数据转换为sf的LINESTRING
# 生成路径的LINESTRING对象 paths_sf <- point_df2 %>% rowwise() %>% mutate(geometry = st_linestring(rbind(c(from_long, from_lat), c(to_long, to_lat)))) %>% st_sfc(crs = 4326) %>% st_sf()
步骤2:太平洋中心地图适配
对路径进行日界线拆分后转换CRS:
robinson <- "+proj=robin +lon_0=-90 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs" # 拆分路径日界线并转换CRS paths_pac <- paths_sf %>% st_break_antimeridian(lon_0 = -90) %>% st_transform(crs = robinson) # 绘制地图 ggplot(world_pac) + geom_sf() + geom_point(data=dtran,aes(geometry=geometry,color=location),stat="sf_coordinates") + geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat="sf_coordinates") + geom_sf(data = paths_pac, color = "blue")
步骤3:椭圆投影(CRS=2163)适配
直接将路径转换到目标CRS即可:
# 转换路径到2163 CRS paths_ellipse <- paths_sf %>% st_transform(crs = 2163) # 绘制地图 ggplot(world_ellipse) + geom_sf() + geom_point(data=dtran,aes(geometry=geometry,color=location),stat="sf_coordinates") + geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat="sf_coordinates") + geom_sf(data = paths_ellipse, color = "blue")
额外提示:曲线路径替代方案
如果需要曲线路径,可使用ggforce::geom_bezier配合转换后的sf点坐标,核心依然是先统一CRS。
内容的提问来源于stack exchange,提问作者hamagust
相关产品推荐
相关产品推荐

