尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

R语言地图绘制全攻略:从sf、ggplot2到交互式可视化

R语言地图绘制全攻略:从sf、ggplot2到交互式可视化 1. 项目概述为什么选择R来绘制地图在地理信息可视化的世界里工具选择往往让人眼花缭乱。从专业的ArcGIS、QGIS到Web开发常用的Leaflet、Mapbox再到Python生态的geopandas、folium每个工具都有其拥趸。那么作为一名数据分析师或研究者当你手头有一份带地理坐标的数据想快速、优雅且可复现地将其呈现在地图上时R语言会是一个极具竞争力的选择。我之所以在众多项目中坚持使用R来绘图核心原因在于其“优雅”二字——这不仅仅指最终图表的视觉效果更指整个工作流的流畅性、可编程性以及分析与可视化的无缝衔接。R在统计绘图领域的王者地位通过ggplot2扩展到了地图领域。你可以用处理普通数据框的思维来处理空间数据用叠加图层Layer的语法来构建复杂的地图整个过程就像在组装乐高积木逻辑清晰且高度可控。更重要的是整个流程可以完整地记录在R脚本或R Markdown文档中确保从数据清洗、空间计算到最终出图的每一步都可追溯、可重复。这对于需要频繁修改或应对审稿人质疑的学术研究以及需要定期更新的业务报告来说是无可替代的优势。接下来我将拆解如何使用R从基础到进阶一步步实现既专业又美观的地图绘制。2. 核心工具栈构建你的R地图绘制引擎工欲善其事必先利其器。R绘制地图的强大离不开其背后成熟且丰富的包Package生态系统。你不需要从零开始造轮子而是站在巨人的肩膀上组合使用这些工具。下面这张表梳理了最核心的几个包及其分工你可以根据自己的需求像搭积木一样选用。包名称核心用途关键特点/替代选择sf矢量空间数据操作现代R空间数据分析的事实标准替代旧的sp包。用于读取、处理、操作点、线、面等矢量数据。ggplot2图形语法与绘图绘图的核心引擎通过geom_sf()图层专门支持sf对象实现地图与其他统计图表的统一语法。raster/terra栅格空间数据操作处理卫星影像、高程模型等栅格数据。terra是更新更快的替代品。tmap专题地图快速绘制语法类似ggplot2但专为地图设计能快速制作出版级静态和交互地图非常适合探索性分析。leaflet交互式Web地图在R中创建Leaflet交互地图支持缩放、拖拽、弹出窗口易于嵌入网页或Shiny应用。maps/mapdata基础地图数据提供世界、国家、美国州界等基础地图数据适合快速绘制示意性地图。viridis/RColorBrewer配色方案提供科学、美观的色板尤其viridis系列对色盲友好且适合黑白打印。注意包的安装并非一劳永逸。sf包的安装可能是新手遇到的第一个坎因为它依赖系统级的GDAL、GEOS、PROJ等地理空间库。在Windows上建议直接安装RStudio推荐的预编译版本在Linux/macOS上可能需要通过系统包管理器如apt-get,brew先安装这些依赖。如果安装sf时遇到困难一个临时的替代方案是使用较旧的sp包配合ggplot2的geom_polygon()但这会牺牲很多现代便利性。我的工作流通常以sf和ggplot2为核心。sf将空间数据表示为带有“几何列”geometry的简单数据框data frame这意味着你可以用熟悉的dplyr动词如filter(),mutate(),group_by()来处理空间数据这种一致性极大地降低了学习成本。而ggplot2则负责将处理好的sf对象渲染成美观的静态图。3. 数据获取与处理地图绘制的基石没有数据地图只是空壳。地图数据主要分为两类底图数据Basemap和业务数据Your Data。底图数据定义了地理背景如国界、省界、河流、道路业务数据则是你想要在地图上展示的信息如各城市的销售额、监测点的污染指数。3.1 获取底图数据对于中国地区的底图需要特别注意数据源的合规性。公开可用的全球数据如来自rnaturalearth包通常精度较低且可能存在边界表示问题。因此对于正式报告或出版物建议使用官方或授权数据从国家或地方自然资源主管部门的官方渠道获取标准地图数据。这些数据通常以Shapefile或GeoJSON格式提供。利用R包获取rnaturalearth包可以方便地获取全球国家、省份的矢量数据适合教学和初步可视化。对于中国地图务必使用其提供的scale large的中国数据并注意检查其完整性。从开放街道地图OSM提取通过osmdata包你可以用R代码直接查询并下载OpenStreetMap中的道路、水系、绿地等矢量数据。这是一个非常强大的免费数据源但需遵守其版权协议。这里是一个使用sf读取本地Shapefile和从rnaturalearth获取数据的示例# 加载必要的包 library(sf) library(ggplot2) library(rnaturalearth) library(dplyr) # 方式1从rnaturalearth获取世界国家数据示意 world - ne_countries(scale medium, returnclass sf) # 筛选出中国 china_map - world %% filter(admin China) # 方式2读取本地Shapefile文件更常见 # 假设你有一个“省份.shp”文件及其附属文件.dbf, .shx等 province_map - st_read(path/to/your/省份.shp, quiet TRUE) # 查看数据结构 head(province_map) print(paste(该数据包含, nrow(province_map), 个要素如省份))3.2 准备与融合业务数据你的业务数据通常是一个包含地理标识如省份名、城市名、经纬度的普通表格。绘制地图的关键是将这个业务表格与底图的空间数据通过地理标识进行连接Join。# 假设你的业务数据框business_df包含两列province_name省份名称和gdpGDP数值 business_df - data.frame( province_name c(广东省, 江苏省, 山东省, 浙江省, 河南省), gdp c(124369.67, 116364.2, 83095.9, 73516.0, 58887.41) # 单位亿元 ) # 将业务数据与空间数据连接 # 前提province_map中有一列属性如NAME存储着与province_name对应的省份名 province_map_with_data - province_map %% left_join(business_df, by c(NAME province_name)) # 根据名称列进行左连接 # 检查连接结果查看是否有省份未能匹配上 unmatched - province_map_with_data %% filter(is.na(gdp)) if(nrow(unmatched) 0) { print(以下省份的业务数据缺失可能需要检查名称是否一致) print(unmatched$NAME) }实操心得数据连接失败十有八九是因为名称不匹配。比如底图数据里是“内蒙古自治区”你的业务数据里是“内蒙古”。建议在连接前统一使用stringr或base R的gsub()函数对名称进行清洗和标准化。也可以考虑使用编码如行政区划代码进行连接这比名称更可靠。如果你的业务数据是离散的点如商店位置则通常包含经纬度坐标。你需要将其转换为sf点对象# 假设有店铺数据包含经度(lon)、纬度(lat)和销售额(sales) store_data - data.frame( store_id 1:5, lon c(116.4, 121.5, 113.3, 120.2, 108.9), lat c(39.9, 31.2, 23.1, 30.3, 34.3), sales c(200, 500, 150, 400, 100) ) # 将普通数据框转换为sf点对象 # crs参数指定坐标系4326代表WGS84经纬度坐标系 store_sf - st_as_sf(store_data, coords c(lon, lat), crs 4326)4. 基础静态地图绘制从 choropleth 到点位图掌握了数据和工具我们就可以开始绘制最常见的地图类型了。ggplot2的geom_sf()函数是这一切的核心它能智能地根据sf对象的几何类型面、线、点进行绘制。4.1 分级统计图Choropleth Map这是最常用的地图类型用颜色深浅表示区域数值大小比如中国各省GDP分布。library(ggplot2) library(viridis) # 引入优雅的色板 # 假设 province_map_with_data 是已经连接好GDP数据的省份面数据 ggplot(data province_map_with_data) geom_sf(aes(fill gdp), color white, size 0.2) # fill基于gdp值着色color是边界线 scale_fill_viridis_c( option C, # 选择viridis色板变体 name GDP (亿元), # 图例标题 na.value grey90 # 为缺失值NA指定颜色 ) labs( title 中国部分省份GDP分布示意图, caption 数据来源示例数据 ) theme_void() # 使用空白主题只保留地图本身 theme( plot.title element_text(hjust 0.5, size 16, face bold), # 标题居中 legend.position right, # 图例放在右侧 legend.title element_text(size 10) )关键参数解析aes(fill gdp)将gdp列映射到填充色这是创建分级统计图的关键。scale_fill_viridis_c()这是一个连续色标函数c代表continuous。option参数可选“A”magma、“B”inferno、“C”plasma、“D”viridis、“E”cividis每种都有不同的视觉风格。theme_void()移除了坐标轴、网格线等所有非数据元素让地图更突出。这是绘制出版级地图的常用技巧。4.2 点位图与符号地图当你的数据是具体的点位时可以使用点几何对象来展示。# 继续使用之前转换的 store_sf 点数据 # 同时加载省份底图作为背景 province_base - st_read(path/to/china_provinces.shp) ggplot() # 首先绘制省份底图面 geom_sf(data province_base, fill lightblue, color gray60, size 0.3) # 然后在其上叠加店铺点点的大小映射销售额 geom_sf(data store_sf, aes(size sales, color sales), alpha 0.7) scale_size_continuous( range c(3, 10), # 点大小的范围 name 销售额 ) scale_color_viridis_c(option D, name 销售额) labs(title 店铺分布与销售额) theme_minimal() theme(legend.position bottom)图层叠加顺序在ggplot2中后添加的图层会绘制在先添加的图层之上。因此先画底图再画点才能确保点不被面覆盖。4.3 添加自定义元素指北针与比例尺专业地图常包含指北针和比例尺。ggspatial包让这个工作变得简单。library(ggspatial) ggplot(data province_map_with_data) geom_sf(aes(fill gdp)) scale_fill_viridis_c() # 添加指北针 annotation_north_arrow( location tr, # 位置top-right右上角 which_north true, # 指向真北 style north_arrow_fancy_orienteering # 箭头样式 ) # 添加比例尺 annotation_scale( location bl, # 位置bottom-left左下角 pad_x unit(0.5, cm), pad_y unit(0.5, cm) # 内边距 ) theme_void()5. 进阶技巧与专题地图制作基础地图能满足大部分需求但要让你的地图脱颖而出还需要一些进阶技巧。5.1 处理投影与坐标系地球是球体而屏幕是平面。将三维球面展平为二维地图的过程就是“投影”。不同的投影会带来不同的形状、面积或距离畸变。WGS84EPSG:4326是常用的经纬度坐标系但它不是投影坐标系直接绘图可能导致图形在高纬度地区被严重拉伸。对于中国全图阿尔伯斯等积投影Albers Equal Area Conic是一个不错的选择它能保持面积比例正确适合经济或人口数据的比较。# 定义中国常用的阿尔伯斯投影参数中央经线105E标准纬线25N和47N crs_albers_china - projaea lat_00 lon_0105 lat_125 lat_247 x_00 y_00 ellpsWGS84 unitsm no_defs # 将数据从WGS844326转换到阿尔伯斯投影 province_map_albers - st_transform(province_map_with_data, crs crs_albers_china) # 在投影后的数据上绘图 ggplot(data province_map_albers) geom_sf(aes(fill gdp)) scale_fill_viridis_c() # 注意添加指北针和比例尺时它们会自动适应当前坐标系 annotation_north_arrow(location tr) annotation_scale(location bl) theme_void()注意事项st_transform()是sf包中用于转换坐标系的核心函数。务必确保所有要叠加的图层如底图、点数据都转换到同一个投影坐标系下否则它们的位置将无法对齐。5.2 绘制带饼图或柱状图的地图子图映射有时我们想在一个区域内展示多个指标的构成比如一个省份内三大产业的占比。这可以通过在每个区域中心点绘制饼图或柱状图来实现。scatterpie或geom_sf_text结合ggplot2的拓展功能可以做到但更灵活的方法是使用ggplot2的annotation_custom()功能配合grid图形系统。不过这比较复杂。一个更实用的替代方案是使用cartography包或tmap包它们内置了更便捷的符号化功能。这里以tmap为例展示其简洁性library(tmap) library(tidyverse) # 准备数据假设每个省份有三大产业数据 industry_data - province_map_with_data %% mutate( industry_1 runif(n(), 10, 50), # 模拟第一产业 industry_2 runif(n(), 20, 70), # 模拟第二产业 industry_3 runif(n(), 30, 90) # 模拟第三产业 ) # 使用tmap的tm_symbols绘制比例符号图柱状 tm_shape(industry_data) tm_polygons(col grey90, border.col white) # 绘制底图 tm_symbols( size industry_2, # 符号大小映射第二产业值 col industry_3, # 符号颜色映射第三产业值 shape 21, # 形状填充圆 scale 1.5, # 大小缩放因子 border.col black, title.size 第二产业, title.col 第三产业 ) tm_layout(legend.outside TRUE) # 将图例放在外部tmap的语法非常直观tm_shape()指定数据后面接各种图形元素函数tm_polygons,tm_symbols等。它同样支持静态和交互模式通过tmap_mode(“view”)切换是快速进行地理数据探索的利器。5.3 绘制交互式地图静态图适合印刷和报告交互式地图则适合网页展示和深度探索。leaflet是R中创建交互地图最流行的包。library(leaflet) library(sf) library(viridis) # 确保数据是WGS84坐标系leaflet所需 province_map_wgs84 - st_transform(province_map_with_data, crs 4326) # 创建连续色板函数 pal - colorNumeric(palette viridis(10), domain province_map_wgs84$gdp) # 创建leaflet地图 leaflet(province_map_wgs84) %% addProviderTiles(providers$CartoDB.Positron) %% # 添加一个浅色底图 addPolygons( fillColor ~pal(gdp), # 填充色 fillOpacity 0.7, # 填充不透明度 color white, # 边界线颜色 weight 1, # 边界线宽度 dashArray 3, # 边界线为虚线 smoothFactor 0.5, # 平滑系数 highlightOptions highlightOptions( # 高亮选项 weight 3, color #666, dashArray , fillOpacity 0.9, bringToFront TRUE ), label ~paste0(NAME, : , round(gdp, 1), 亿元), # 鼠标悬停标签 labelOptions labelOptions(direction auto) ) %% addLegend( # 添加图例 pal pal, values ~gdp, opacity 0.7, title GDP (亿元), position bottomright )这段代码会生成一个可在浏览器中交互的地图可以缩放、拖拽鼠标悬停在省份上会显示名称和GDP点击会高亮。addProviderTiles可以切换不同的底图风格如OpenStreetMap,Esri.WorldImagery卫星图。6. 常见问题与排查技巧实录在实际操作中你一定会遇到各种“坑”。下面是我总结的一些典型问题及其解决方案。6.1 地图形状怪异或点线不显示问题描述使用geom_sf()绘图时地图形状被极度拉长或者点、线数据没有显示在正确位置。根本原因坐标系不匹配。这是最常见的问题。你的底图数据、业务点数据可能处于不同的坐标系CRS中。排查步骤检查CRS对每个sf对象使用st_crs(your_sf_object)查看其坐标系。统一CRS使用st_transform()将所有图层转换到同一个目标CRS。对于在ggplot2中叠加显示通常统一为底图数据的CRS。对于leaflet最终需要转换为WGS84EPSG:4326。注意经纬度顺序有些数据源的经纬度顺序可能是纬度经度而sf默认是经度纬度。在创建sf点对象时务必确认coords参数中c(“lon”, “lat”)的顺序。6.2 数据连接后地图区域缺失或颜色错误问题描述业务数据和空间数据连接后有些区域变成空白NA或者颜色映射完全不对。排查步骤检查连接键打印连接前后数据的行数。使用anti_join()查找未能匹配的行。unmatched_business - anti_join(business_df, province_map, by c(“province_name” “NAME”)) unmatched_map - anti_join(province_map, business_df, by c(“NAME” “province_name”))清洗名称行政区划名称常有全称、简称、别名之别。使用stringr::str_trim()去除空格stringr::str_replace()替换“省”、“市”、“自治区”等后缀确保一致性。检查编码如果数据包含行政区划代码如国家标准GB/T 2260优先使用代码进行连接这比名称更可靠。6.3 图形渲染缓慢或内存不足问题描述处理高精度地图数据如详细到乡镇边界时绘图速度极慢甚至R会话崩溃。解决方案简化几何图形使用sf::st_simplify()或rmapshaper::ms_simplify()函数降低几何图形的复杂度减少顶点数。这能极大提升渲染速度且在小比例尺地图上视觉损失很小。province_map_simple - st_simplify(province_map, dTolerance 1000) # dTolerance单位与数据CRS相同值越大越简化裁剪感兴趣区域如果只研究特定区域先用st_intersection()或st_crop()将数据裁剪到所需范围避免渲染无关区域。使用tmap或plot预览在精细调整ggplot2主题前先用tmap的qtm()快速函数或基础的plot()函数查看数据概貌这比反复渲染复杂的ggplot2对象要快得多。6.4 图例、标题或标签显示不正常问题描述图例标题是变量名而不是中文标签重叠标题位置不对。解决方案在美学映射aes()内部设置标签在labs()函数中明确设置title,subtitle,caption,fill,color,size等参数。labs(title “我的地图”, fill “GDP数值亿元”)调整图例使用guide_colorbar()或guide_legend()精细控制图例。theme()函数中的legend.position位置、legend.direction方向等参数也非常有用。处理重叠标签对于点数据的标签可以使用ggrepel包中的geom_text_repel()或geom_label_repel()来智能避让。对于面数据的标签通常需要计算几何中心st_centroid()后再添加。6.5 导出高清图片问题描述用RStudio的“Export”按钮导出的图片模糊或者字体不符合要求。解决方案始终使用ggsave()函数它能提供最高的控制度和输出质量。final_plot - ggplot(...) ... # 你的完整绘图代码 ggsave( filename “china_gdp_map.pdf”, # 推荐输出为PDF或SVG矢量格式 plot final_plot, width 10, # 宽度英寸 height 8, # 高度英寸 dpi 300 # 分辨率如果输出位图如PNG ) # 如果图形中有中文字体需要提前设置 # library(showtext) # font_add(“SimHei”, “simhei.ttf”) # 添加字体 # showtext_auto() # 然后在ggplot主题中设置 family “SimHei”矢量格式PDF/SVG可以无限缩放而不失真是印刷和出版的首选。如果必须使用位图PNG/JPEG请确保dpi设置足够高通常300或以上。绘制地图是一个将数据、几何和美学融合的过程。R提供的这套工具链从sf的数据操作到ggplot2/tmap的可视化再到leaflet的交互形成了一条完整、强大且优雅的工作流。它可能不是最简单的入门工具但一旦掌握其可重复性、可编程性和灵活性将让你在处理任何空间数据可视化任务时游刃有余。最关键的是从数据到成图的所有逻辑都清晰地记录在你的脚本中这才是“优雅”的真正含义——清晰、可控、可复现。
返回列表