R绘制等值区域图如何将点位标签移至图边距避免重叠
R ggplot2 等值区域图外标签排布方案
问题根源
之前标签堆叠、无法移动、调整范围导致地图缩放的问题,核心是三个错误:
nudge_x/nudge_y取值完全不合理:经纬度单位是度,你用到的俄亥俄5个县总纬度跨度不到0.7度,之前代码填的nudge_y=40相当于把标签推到4000公里外,ggrepel的防重叠算法根本无法在超远偏移距离下生效,标签自然全部堆叠- 直接在顶层用
xlim()/ylim()调整范围会强制裁剪缩放绘图面板,破坏地图原有显示比例 - 没有给ggrepel指定标签的允许排布边界,算法默认只会在地图绘图区域内调整标签位置,推到区域外的标签不会做防重叠计算
修正方案
不需要拆分多个数据框,只要给原始provider数据加一列分组标识,配合coord_cartesian预留边距、给每组标签单独指定排布范围即可:
- 用
coord_cartesian()扩展绘图区域,关闭面板裁剪,完全保留地图原有显示比例,不会缩放 - 按点位的经纬度给服务提供方打北/中/南分组标签,给每组标签指定推离方向、允许排布的边距范围、错开方向,ggrepel会自动在指定范围内错开标签,不会堆叠
- 固定随机种子,保证每次运行标签位置一致
可运行修正代码(基于你提供的示例数据)
library(maps) library(tidyverse) library(ggrepel) # 读取俄亥俄示例县边界 ohiocounty_map <- map_data("county") %>% subset(region == "ohio") %>% mutate(County = str_to_sentence(subregion)) %>% filter(County %in% c("Butler", "Clermont", "Clinton", "Hamilton", "Warren")) # 构造测试用服务提供方点位 Provider_Name <- sapply(letters[1:8], function(x) paste(rep(x, 20), collapse = ""), USE.NAMES = F) Latitude_P <- as.numeric(c("39.18268", "39.09723", "39.06838", "39.13517", "39.243835", "39.323032", "39.445957", "39.505478")) Longitude_P <- as.numeric(c("-84.48057", "-84.64043", "-84.10078", "-84.612465", "-84.463478", "-84.504646", "-84.27858", "-84.739334")) provider <- data.frame(Provider_Name, Latitude_P, Longitude_P) # 按纬度给点位打北/中/南分组,不需要拆分单独数据框 provider <- provider %>% mutate( pos_group = case_when( Latitude_P > 39.4 ~ "North", Latitude_P < 39.1 ~ "South", TRUE ~ "Central" ) ) # 提取地图边界经纬度,用来计算边距范围 map_xrange <- range(ohiocounty_map$long) map_yrange <- range(ohiocounty_map$lat) ggplot() + # 绘制县边界 geom_polygon(data = ohiocounty_map, aes(x = long, y = lat, group = group), fill = NA, color = "black", linewidth = 1) + # 绘制服务提供方点位 geom_point(data = provider, aes(x = Longitude_P, y = Latitude_P), color = "red", size = 3) + # 北部点位标签:排布在地图顶部边距,水平方向错开 geom_label_repel( data = provider %>% filter(pos_group == "North"), aes(x = Longitude_P, y = Latitude_P, label = Provider_Name), min.segment.length = 0, nudge_y = 0.8, ylim = c(map_yrange[2] + 0.3, map_yrange[2] + 1), direction = "x", seed = 123 ) + # 中部点位标签:排布在地图左侧边距,垂直方向错开 geom_label_repel( data = provider %>% filter(pos_group == "Central"), aes(x = Longitude_P, y = Latitude_P, label = Provider_Name), min.segment.length = 0, nudge_x = -0.8, xlim = c(map_xrange[1] - 1, map_xrange[1] - 0.3), direction = "y", seed = 123 ) + # 南部点位标签:排布在地图底部边距,水平方向错开 geom_label_repel( data = provider %>% filter(pos_group == "South"), aes(x = Longitude_P, y = Latitude_P, label = Provider_Name), min.segment.length = 0, nudge_y = -0.8, ylim = c(map_yrange[1] - 1, map_yrange[1] - 0.3), direction = "x", seed = 123 ) + # 扩展绘图范围,关闭裁剪,不改变地图原有比例 coord_cartesian( xlim = c(map_xrange[1] - 1.2, map_xrange[2] + 0.3), ylim = c(map_yrange[1] - 1.2, map_yrange[2] + 1.2), clip = "off" ) + # 关闭默认轴扩展留白 scale_x_continuous(expand = c(0,0)) + scale_y_continuous(expand = c(0,0)) + theme_void() # 地图用空主题,需要保留坐标轴可替换为对应主题
关键参数说明
- 每个
geom_label_repel里的xlim/ylim是用来限定标签的可排布范围,不是调整整个地图的显示范围,ggrepel会自动在这个矩形范围内错开所有标签,完全不会出现堆叠 - 顶/底部标签设置
direction = "x"让标签沿水平方向错开,左/右侧标签设置direction = "y"沿垂直方向错开,排布效率最高,不会浪费边距空间 - 城市点位的标签可以用完全相同的逻辑,单独指定排布区域(比如右侧边距)即可,不会和服务提供方标签重叠
- 如果标签太长放不下,直接调大coord_cartesian里对应方向的扩展范围就行,地图本身的大小比例不会变
内容的提问来源于stack exchange,提问作者mkelley
相关产品推荐
相关产品推荐

