R 语言空间地图实战:从城市热力图到地理分布图,一篇吃透
地图可视化从来不只是画几条线、填几种颜色那么简单。当你面对一份城市人口数据,或者一长串省份的经济指标时,如何让这些冰冷的数字在空间维度上"活"起来?R 语言的空间生态,正是为此而生。
本文不是入门指南,而是面向实战的完整方案------城市热力图、地理分布图、专业出版级地图,代码可直接复用。
一、搭建工作环境:核心工具链
先把"画板"和"颜料"准备好。打开 RStudio 控制台,一键安装:
less
r
install.packages(c("sf", "ggplot2", "rnaturalearth", "rnaturalearthdata",
"dplyr", "viridis", "leaflet", "leaflet.extras",
"tmap", "ggspatial"))
各包分工明确:
| 包 | 角色 |
|---|---|
| sf | 现代空间数据处理基石,统一管理矢量几何与属性 |
| ggplot2 | 可视化语法核心,与 sf 无缝集成 |
| rnaturalearth | 高质量全球地图数据,免去四处找底图的苦 |
| leaflet | 交互式热力图,网页端探索利器 |
| tmap | 几行代码画出出版级区域热图 |
| ggspatial | 比例尺、指北针等专业元素一键添加 |
加载:
scss
r
library(sf)
library(ggplot2)
library(rnaturalearth)
library(dplyr)
library(viridis)
library(leaflet)
library(leaflet.extras)
library(tmap)
library(ggspatial)
⚠️ 关键习惯:项目开始时统一坐标参考系统(CRS)。全球可视化用 WGS84(EPSG:4326);中国区域分析建议用等面积投影如 Albers。
二、实战一:城市热力图(ggplot2 + 地理底图)
2.1 经纬度散点热力图
假设有一份城市数据,包含经纬度和某项指标(如人口):
ini
r
# 模拟数据
set.seed(123)
city_data <- data.frame(
city = c("北京", "上海", "广州", "深圳", "成都", "杭州", "武汉", "西安"),
lon = c(116.407, 121.473, 113.264, 114.057, 104.066, 120.153, 114.305, 108.940),
lat = c(39.904, 31.230, 23.129, 22.543, 30.572, 30.287, 30.593, 34.341),
population = c(2189, 2487, 1868, 1756, 2094, 1220, 1365, 1295) # 万人
)
# 转换为 sf 对象
city_sf <- st_as_sf(city_data, coords = c("lon", "lat"), crs = 4326)
# 加载中国底图
china <- ne_countries(scale = "medium", country = "China", returnclass = "sf")
# 绘图
ggplot() +
geom_sf(data = china, fill = "grey95", color = "grey70") +
geom_sf(data = city_sf, aes(size = population, color = population), alpha = 0.8) +
scale_size_continuous(range = c(3, 12), name = "人口(万人)") +
scale_color_viridis(option = "plasma", name = "人口(万人)") +
theme_minimal() +
labs(title = "中国主要城市人口热力图", x = "经度", y = "纬度")
点的大小和颜色双重编码人口规模,一目了然。
2.2 密度热力图层(ggmap 方案)
如果想要更"热"的效果------连续的热力渐变:
ini
r
library(ggmap)
# 获取底图
base_map <- get_map(location = c(lon = 110, lat = 35), zoom = 5, maptype = "terrain")
ggmap(base_map) +
geom_point(data = city_data, aes(x = lon, y = lat, size = population, color = population),
alpha = 0.7) +
scale_color_gradient(low = "blue", high = "red") +
scale_size_continuous(range = c(2, 10)) +
labs(title = "城市人口密度热力图") +
theme_void()
三、实战二:交互式地图热力图(leaflet)
静态图不够过瘾?leaflet 让你在浏览器里缩放、悬停查看数值:
ini
r
library(leaflet)
library(leaflet.extras)
leaflet(city_data) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lon, lat = ~lat,
radius = ~population / 80, # 半径映射人口
color = ~viridis::viridis(100)[cut(population, breaks = 100)],
fillOpacity = 0.7,
popup = ~paste0("<b>", city, "</b><br>人口: ", population, "万人")
) %>%
addLegend("bottomright",
pal = colorNumeric(viridis(100), domain = city_data$population),
values = ~population,
title = "人口(万人)")
真正的热力图(连续密度,非散点):
ini
r
leaflet() %>%
addTiles() %>%
addHeatmap(
data = city_data,
lng = ~lon, lat = ~lat,
intensity = ~population,
radius = 15, blur = 20, max = 0.5,
gradient = c("blue", "cyan", "lime", "red")
)
radius 控制热斑半径,blur 控制模糊程度,max 控制强度上限------三个参数调好,效果立现。
四、实战三:区域数据热力图(tmap,几行搞定)
这是最省力的方案,尤其适合省级/市级 GDP、PM2.5 等面数据。
ini
r
library(tmap)
# 模拟省级数据
province_data <- data.frame(
name = c("广东", "江苏", "山东", "浙江", "河南", "四川"),
GDP = c(135673, 128222, 92069, 77715, 61345, 56749) # 亿元
)
# 读取中国省级边界(需提前准备 GeoJSON 或 Shapefile)
# china_provinces <- st_read("china_provinces.geojson")
# 这里用 rnaturalearth 的中国边界做演示
china_provinces <- ne_states(country = "China", returnclass = "sf")
# 合并数据
map_data <- left_join(china_provinces, province_data, by = c("name" = "name"))
# 彩色热图
tm_shape(map_data) +
tm_polygons("GDP", title = "GDP(亿元)", palette = "YlOrRd") +
tm_layout(title = "2023年中国部分省份GDP分布", legend.outside = TRUE)
# 灰度热图(适合印刷)
tm_shape(map_data) +
tm_polygons("GDP", title = "GDP(亿元)", palette = "Greys") +
tm_layout(title = "2023年中国部分省份GDP分布(灰度版)")
tm_style("bw") 是灰度风格,tm_style("beaver") 是另一种配色------换一行代码,风格即变。
五、实战四:出版级专业地图(ggplot2 + ggspatial)
学术论文需要比例尺、指北针?ggspatial 一键搞定:
ini
r
library(ggspatial)
ggplot() +
geom_sf(data = china_provinces, fill = "white", color = "grey60", size = 0.3) +
geom_sf(data = map_data, aes(fill = GDP), color = "grey40") +
scale_fill_viridis(option = "plasma", name = "GDP(亿元)") +
annotation_scale(location = "bl", width_hint = 0.4) + # 比例尺
annotation_north_arrow(location = "tl", style = north_arrow_fancy_orienteering) + # 指北针
theme_minimal() +
labs(title = "中国省级GDP分布", x = "经度", y = "纬度")
这张图直接丢进论文,审稿人挑不出毛病。
六、避坑清单:实战中最容易翻车的点
| 坑 | 解决方案 |
|---|---|
| 国家名称对不上 | 数据里是 "United States",地图里是 "United States of America"------用 name_mapping 手动映射,或 rwcvp_match_names() 自动匹配 |
| CRS 不统一导致地图扭曲 | 开工第一件事:st_crs(your_data) <- 4326,所有数据统一 WGS84 |
| 点太多导致图形卡顿 | 用 geom_hex() 或 stat_summary_2d() 做密度聚合,别硬画几万个点 |
| 中文乱码 | 读 CSV 时加 fileEncoding = "GBK",或统一转 UTF-8 |
| 图例太丑 | viridis 配色对色盲友好,RColorBrewer 的 YlOrRd 是经典热力图色系 |
速查:该用哪个方案?
| 需求 | 推荐方案 | 核心函数 |
|---|---|---|
| 城市散点热力图 | ggplot2 + sf | geom_sf() |
| 连续密度热力图 | leaflet | addHeatmap() |
| 省级/区域面数据热图 | tmap | tm_polygons() |
| 学术出版级地图 | ggplot2 + ggspatial | annotation_scale() |
| 物种/植物全球分布 | rwcvp | wcvp_distribution_map() |
代码全在这里了。从几行代码的城市热力图,到带比例尺指北针的出版级地图,R 的空间可视化能力远比你想象的强大。挑一个场景,复制粘贴,跑起来。