R语言地图可视化实战:从sf数据处理到ggplot2与leaflet交互地图

发布时间:2026/9/1 4:55:10
R语言地图可视化实战:从sf数据处理到ggplot2与leaflet交互地图 简介面向需要在R语言中完成地图可视化任务的开发者和数据分析师这份代码包系统演示了Rstudio环境下的完整制图流程既可作为新手入门参考也可当作日常绘图时快速查阅的代码片段库。包内共6个文件以R脚本为主体3个配合Markdown说明文档与项目配置文件整体仅7KB轻量易读适合快速参考与二次修改。内容覆盖省级地图数据读取、矢量与栅格数据处理、坐标系设置、底图导入与属性填充并基于ggplot2和ggspatial实现分层可视化同时展示分组分段、图例调整、比例尺与指北针添加等地图细节末尾还利用循环完成批量制图。资源还区分了主流程、依赖安装与数据准备等模块脚本注释清楚便于按步骤拆解学习。已有63人浏览学习适合希望从零掌握R语言空间数据展示、提升科研或业务报告地图表达效果的中初级读者。 前阵子帮朋友做一份区域分析报告他抱着一堆Excel数据问我能不能把这堆数字变成地图上不同颜色的区域我当时用Rstudio花了不到半小时把流程跑通他看得一愣一愣的说“你这是用的什么黑科技”其实我真没用什么特殊东西就是R里的几个地理绘图包组合了一下。今天我把这套地图绘制流程完整写出来从数据准备到静态出图再到交互式地图代码全部可以直接跑。这篇文章适合在Rstudio里做数据可视化、想把业务数据落到地图上的同学也适合那些已经会画柱状图、散点图想往空间可视化再迈一步的人。你可以把它当成一份“抄作业”指南而不是又一篇云里雾里的理论科普。1. 为什么在Rstudio里画地图而不是Python或GIS1.1 地图绘制不是“搞个形状”那么简单很多人以为画地图就是把边界线画出来然后填充颜色这其实只猜对了一半。真正的地图绘制本质上是在做“空间数据”有坐标、有几何对象和“业务数据”有数值、有分类的关联。你手里的Excel数据通常没有坐标信息而地图边界文件里除了坐标也不懂你的业务指标。所以你需要一套工具能同时处理这两类数据并且让它们按地区编码对齐。这事听起来简单但坐标系统、边界精度、数据合并这些环节每一项都能把人卡住大半天。1.2 R的地图生态一套被低估的组合拳R虽然常被吐槽语法老但它在空间统计和地图可视化上的积累其实相当深。早期大家用sp和rgdal现在主流的组合是sf处理空间数据框ggplot2负责出图需要交互式就用tmap或leaflet。这套组合拳的好处是你不需要切换软件数据清洗用dplyr画图用ggplot2交互地图用leaflet全在一个Rmd文件里搞定。而且sf对象本质上就是普通数据框里面多了一列几何信息这意味着你之前学的dplyr操作基本都能复用学习成本远比你想象中低。2. 画地图前的数据准备边界文件、属性数据和坐标系2.1 边界数据从哪来公开渠道与版权提醒地图绘制必须得有边界数据也就是我们常说的shp文件或GeoJSON。对于全球数据我一般直接用R包rnaturalearth它封装了自然地球Natural Earth的公开数据按国家、省、州等行政级别划分下载快、字段清晰用起来非常省心。如果你要画中国省级地图建议从当地测绘主管部门的官方网站下载标准地图或者从国家地理信息公共服务平台获取带审图号的公开数据千万不要随手在网盘里搜一个来源不明的shp文件容易边界错误还会惹上版权和管理合规的问题。我的习惯是先用rnaturalearth做Demo和流程验证最后出正式图再换成官方审图数据。2.2 属性数据怎么和地图关联连接键设计边界文件里每个区域都有一个唯一标识比如国家代码、省份名称或行政区划代码。你的业务数据必须也准备一个同样含义的字段然后用left_join()按这个字段合并。这里的核心坑是“两边字段类型不一致”一边是字符串另一边是数字合并后就会变成NA。所以我在读入数据后会先str()检查字段类型再统一转成字符串。另一个容易忽略的问题是两边的区域名称有细微差别比如“内蒙古”和“内蒙古自治区”这种不一致无法靠命名解决最稳妥的关联键是行政区划代码而不是汉字名称。如果是全球地图优先用ISO三位国家代码作为关联键。2.3 坐标系为什么所有点都偏了坐标系是整个地图绘制里最不容易理解、又最容易出问题的地方。简单说地球是椭球体要把它画到平面上就需要投影转换。网络地图用的通常是WGS84经纬度坐标对应EPSG代码是4326。而你从某处下载的shp可能已经投影到某个平面坐标系里比如中国常用的GCJ02火星坐标系或者某些地方坐标系。如果不先统一坐标系就会出现“点在海洋里”“两块区域错位”的尴尬局面。R里统一用st_crs()查看用st_transform()转换。我的建议是只要不是做高精度测量画地图一律先转成4236也就是经纬度坐标最简单也最通用。3. 第一张静态地图从加载到出图的全流程代码3.1 安装与加载包先安装需要用到的包。如果你是从零开始直接运行下面这行install.packages(c(sf, ggplot2, dplyr, viridis, rnaturalearth, tmap, leaflet, showtext, ggspatial))安装过程可能会有点慢尤其sf会带很多底层依赖建议耐心等待。加载的时候还有一个小技巧rnaturalearth的包名和加载名一样不会有什么歧义tmap如果和ggplot2一起用有时候会提示图形设备冲突一般不影响但建议在画交互地图之前先dev.off()清掉之前的图形。3.2 读取边界数据并关联数据下面用全球数据作为例子因为全球数据获取最方便你跑通之后换成任何省级数据原理都一样。library(sf) library(dplyr) library(ggplot2) library(rnaturalearth) # 获取全球国家边界返回sf对象 world - ne_countries(scale medium, returnclass sf) # 查看字段名 names(world) # 模拟业务数据比如每个国家的人口估计值 # ne_countries本身有pop_est字段这里假设你自己有一份数据 sim_data - data.frame( iso_a3 world$iso_a3, my_value round(runif(nrow(world), 1, 100), 1) ) # 合并到sf对象 world_data - world %% left_join(sim_data, by iso_a3) # 确认合并后的数据没有丢失 sum(is.na(world_data$my_value))这里要注意left_join()之后你得到的新对象仍然是一个sf对象因为几何列还在。但如果你不小心把world$my_value直接作为向量去画图就和几何列没有关系了所以还是推荐把业务数据先合并到sf对象里让每一行都带着对应的几何信息。3.3 用ggplot2绘制分级统计图合并好数据后ggplot2画分级统计图也就是choropleth非常简单核心语句就是geom_sf()。它会把sf对象里的几何列自动当成多边形画出来aes(fill my_value)会按数值填充颜色。ggplot(world_data) geom_sf(aes(fill my_value), color white, size 0.2) scale_fill_viridis_c(option plasma, name My Index) theme_void() theme( plot.title element_text(face bold, size 16), legend.position bottom ) labs(title 全球指标空间分布示例)这个图的视觉效果几乎可以直接放进PPT里。theme_void()把经纬网格和坐标轴抹掉只剩地图本身scale_fill_viridis_c使用色盲友好的连续色带。如果你的数据是离散分类比如“高/中/低”就把scale_fill_viridis_c换成scale_fill_viridis_d否则会报错或颜色分不出来。3.4 解决中文乱码和主题细节中文标题和图例在Windows和macOS上最容易翻车显示成方块。我常用的解决办法是showtext包它能把字体嵌入到图形中避免在不同设备上“换了台电脑就乱码”。library(showtext) font_add(SimHei, simhei.ttf) # Windows一般自带macOS可换成PingFang.ttc showtext_auto()然后ggplot2里的标题、图例文字会自动使用SimHei字体。如果你不想引入字体包也可以直接用theme(text element_text(family SimHei))但跨设备兼容性没有showtext好。另外一个小细节是图例位置地图图例放在底部或者右侧更美观放在顶部会显得很局促。4. 地图不只是方块点、线、面的叠加与场景扩展4.1 在地图上叠加城市点数据业务场景里经常需要在一张地图上叠加多个图层比如底图是行政区域上面再加城市坐标点、物流中心和迁徙路线。我们的处理方式很简单构建一个sf点对象然后直接在ggplot2里叠加。# 构造几个城市点 cities - data.frame( lon c(-0.13, 116.41, 37.62, 139.69), lat c(51.51, 39.90, 55.75, 35.68), name c(London, Beijing, Moscow, Tokyo) ) %% st_as_sf(coords c(lon, lat), crs 4326) # 地图上叠加点 ggplot(world_data) geom_sf(color gray80) geom_sf(data cities, aes(size name), color red, alpha 0.7) theme_void()这里关键是st_as_sf(coords c(lon, lat), crs 4326)把普通数据框转成了带几何对象的sf数据框。叠加时ggplot2会自动识别两个sf对象的坐标系如果底图是4286点的crs也是4326就不会有偏移。要注意的是aes(size name)会让每个城市名字映射成点的大小如果名字不同尺寸会不同更合理的做法是size 常数然后单独用geom_text()标注城市名。4.2 基于geom_sf的连线与路径画线轨迹稍微有点绕因为你需要把一个多点的路径转成一个sf的线对象。具体方法是先构造点坐标再用st_linestring()变成线几何最后放在geom_sf()里画。实际业务里最常见的场景是“从A城市到B城市的航线/物流线”。我的经验是不要直接在ggplot2里硬画line而是先用sf包的st_sfc()构造一个线串再转成sf对象这样后续做缓冲区、空间计算都能继续用。4.3 组合使用ggspatial添加比例尺地图缺少比例尺和指北针会让人看不明白空间尺度。ggspatial包可以很方便地给ggplot2地图加这两个元素library(ggspatial) ggplot(world_data) geom_sf(fill lightyellow, color gray50) annotation_scale(location bl, width_hint 0.25) annotation_north_arrow(location tl, which_north true)which_north true表示按真北方向而不是网格北方向。比例尺的长度会自动根据当前地图范围调整不用你手动算。我一般在出正式报告图的时候才会加这些装饰元素如果是随手探索数据不加反而更清爽。5. 从静态到交互tmap与leaflet的实战选择5.1 tmap的交互模式静态图适合放在报告里但你自己探索数据或者给领导做展示交互地图常常更直观。R里最简单的交互式地图方案是tmap。先设置地图模式为“view”后面的语法几乎和ggplot2差不多但又包含了鼠标悬停提示、缩放等天然能力。library(tmap) tmap_mode(view) tm_shape(world_data) tm_polygons( col my_value, title My Index, palette Reds, popup.vars c(Country name) )tmap_mode(view)会把图形窗口变成交互式网络地图底图默认是OpenStreetMap。这里有一个新手常犯的错如果地图上只显示灰色没有底图多半是因为当前RStudio的Viewer面板或浏览器的网络请求没有发出去需要检查网络环境。实际项目中tmap也能直接输出HTML文件方便你共享出去。5.2 leaflet的弹窗、颜色分层和发布如果想要更精细的交互体验我推荐leaflet。虽然函数名比ggplot2长一些但功能更强。library(leaflet) pal - colorNumeric(viridis, domain world_data$my_value) leaflet(world_data) %% addTiles() %% addPolygons( fillColor ~pal(my_value), color white, weight 0.5, fillOpacity 0.7, label ~name, popup ~paste0(name, br/Value: , my_value) ) %% addLegend(pal pal, values ~my_value, position bottomright, title My Index)colorNumeric()是连续色带如果数值分布差异太大可以加trans log10做对数变换否则最大值会压掉其他区域的颜色层次。label和popup分别控制悬停和点击时显示的内容paste0()里可以写简单的HTML让弹窗显示多行信息。导出形式是HTML可以直接放进微信公众号或网页的iframe里。5.3 什么时候用静态什么时候用交互我的选择标准很直接图是用来打印、存PDF、嵌入Word或PPT就用ggplot2图是要在浏览器里点来点去、包含多个图层切换、你要给不懂R的同事看就做leaflet。tmap介于两者之间它其实可以同时做到“静态出图”和“交互预览”适合你还没想清楚到底要哪种的时候。项目交付场景下我一般先tmap快速看图层效果再决定用ggplot2精修还是用leaflet做交互版本。6. 我踩过的坑坐标偏移、数据合并和中文字体6.1 数据合并把sf对象搞坏有次做项目我直接从shp里取了一个省级数据然后用dplyr::mutate()加了一列数值画图一切正常。后来换了一台电脑重新运行同样的代码居然报错“Error: x must be a numeric vector”。排查半天发现是left_join()之后有一个地区的代码在业务数据里是空值合并后那一行几何信息还在但属性变成了NA导致geom_sf(aes(fill my_value))里遇到NA值直接画不出来。解决方法是合并前用anti_join()检查哪些区域在业务数据里缺失然后手动补齐如果只是临时探索也可以先filter(!is.na(my_value))再画图。另外千万小心不要用as.data.frame()把sf对象转成普通数据框几何列会被拆掉后期想再画图就麻烦了。6.2 中文字体在不同系统的坑中文字体绝对是我踩过最多的坑。在Windows上用WindowsFonts设置字体代码换到Linux服务器就完全失效。后来我用showtext解决但仍有一个细节如果你在RStudio里看到图形预览是正常的中文导出PDF或者PNG时中文又消失这一般是因为字体文件没有嵌进去。用ggsave()出图时最好指定device png或pdf并且配合showtext_auto()后再保存这样字体才会被渲染进文件。推荐在代码开头固定设置showtext_opts(dpi 300) ggsave(map.png, width 8, height 6, dpi 300)6.3 地图审图与发布规范地图很容易踩到一个看不见的坑边界数据不完整或者用了未经审核的地图发布出去可能变成“问题地图”。我在写这篇教程时特意用了全球公开数据不讲局部边界问题就是为了避免这种风险。但如果你确实要画某个国家的行政区域图请一定使用当地测绘部门发布的带正式审图号的标准地图服务并且不要擅自修改、拼接边界数据。这不仅是合规问题也是作为数据从业者的基本职业习惯。画图之前想一下“这张图会发给谁会不会被拿去公开传播”如果会那就多花几分钟检查边界来源。我个人的使用体验是R的地图绘制能力绝对够用只是官方文档和函数名有点让人劝退。可一旦你跑通首张图后面所有地图项目都会变成“换数据、换配色、换标题”的重复劳动。真正的高手并不是背下几十个函数而是清楚每一步在解决什么坑数据有没有对齐、坐标系有没有统一、字体能不能正常显示、发布来源合不合规。把这几个问题想在前面你也能用Rstudio画出一套让同事羡慕的地图。最后再分享一个小技巧保存地图时PNG适合快速预览但正式报告里我倾向于用ggsave()导出高分辨率PDF再插进文档这样文字边缘是矢量效果放大也不会糊。本文还有配套的精品资源点击获取