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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 13:27:19