R: R 地图与空间数据

最后更新:2026-08-26

真实项目里地理空间数据越来越重要——疫情地图、滴滴打车热力图、京东配送范围。这一课我们学 R 地图可视化:sf 处理矢量数据、ggplot2 画地图、leaflet 做交互地图。

读完这一课你能用 R 画 4 类地图:静态地图(ggplot2 + sf)、分级统计图(choropleth)、点密度图、交互地图(leaflet)。

1. 你将学到



2. 一个物流热力图的故事

(1) 痛点:销售数据没有空间感

Alice负责全国销售,manager问:"哪个省卖得最好?"表格看了半天,manager说:"给我看地图。"

(2) R 的解法

R
library(sf)
library(ggplot2)

# 1. 读取中国地图
china <- st_read("china.shp")

# 2. 合并销售数据
china_sales <- china |>
  left_join(sales_by_province, by = c("name" = "province"))

# 3. 一行画分级统计图
ggplot(china_sales) +
  geom_sf(aes(fill = sales)) +
  scale_fill_gradient(low = "lightblue", high = "darkred") +
  labs(title = "全国销售分布", fill = "销售额")

3 行代码 → 出版级中国销售地图



3. 空间数据基础

(1) 矢量 vs 栅格

100%
graph LR
    A[空间数据] --> B[矢量数据<br/>Vector]
    A --> C[栅格数据<br/>Raster]
    B --> D[点 Point<br/>线 Line<br/>面 Polygon]
    C --> E[像素 Pixel<br/>卫星图/温度图]
    
    style A fill:#fff3cd
    style B fill:#cce5ff
    style C fill:#d4edda

本课讲矢量数据(点/线/面)。

(2) shapefile 是什么?

Shapefile = ESRI 公司的矢量数据标准,由 4 个文件组成:

TEXT 📖 仅展示
data/
├── china.shp         # 几何信息(坐标)
├── china.shx         # 形状索引
├── china.dbf         # 属性信息(名称、人口等)
└── china.prj         # 投影信息(坐标系)
⚠️ 注意:4 个文件必须放同一目录。shp 是"主文件",其他是辅助。

(3) GeoJSON

现代 Web 地图常用 GeoJSON(基于 JSON):

JSON
{
  "type": "Feature",
  "properties": {"name": "北京", "value": 1000},
  "geometry": {
    "type": "Point",
    "coordinates": [116.4, 39.9]
  }
}


4. sf 包:空间数据框架

(1) 为什么用 sf?

特点 推荐度
sf 现代、R tidyverse 风格、ggplot2 整合 ⭐⭐⭐
sp 老式、R 基础语法 ❌ 新代码不推荐
raster 栅格数据专用 用于栅格

(2) 安装和加载

R
install.packages("sf")
library(sf)

(3) 读取 shapefile

R
library(sf)

# 读取 shapefile
china <- st_read("data/china.shp")
# Reading layer `china' from data source `data/china.shp' using driver `ESRI Shapefile'
# Simple feature collection with 34 features and 5 fields
# Geometry type: MULTIPOLYGON
# Dimension:     XY
# Bounding box:  xmin: 73.5 ymin: 3.5 xmax: 135 ymax: 53.5
# CRS:           4326  ← WGS84 经纬度

# 查看结构
print(china)
# 实际就是 data.frame 多了 geometry 列
# A tibble: 34 × 6
#   name        population   ...  geometry
#   <chr>            <int>        <MULTIPOLYGON>

# 几何信息
st_geometry(china)
st_crs(china)              # 坐标系
st_bbox(china)             # 边界框

(4) 读取 GeoJSON

R
# 读取 GeoJSON
cities <- st_read("data/cities.geojson")
# 或从 URL
cities <- st_read("https://example.com/cities.geojson")

(5) 写 shapefile / GeoJSON

R
# 写 shapefile
st_write(china, "output/china_new.shp")

# 写 GeoJSON
st_write(china, "output/china.geojson")


5. CRS 坐标系

(1) 什么是 CRS?

CRS (Coordinate Reference System) 坐标系——把地球的"球面"映射到"平面":

100%
graph LR
    A[地球球面] -->|WGS84 4326<br/>经纬度| B[平面坐标]
    A -->|Web Mercator 3857<br/>米| B
    A -->|中国 2000<br/>米| B

(2) 常用 CRS 代码

EPSG 名称 用途
4326 WGS84 GPS / 通用(最常用
3857 Web Mercator 谷歌地图、OpenStreetMap
4490 CGCS2000 中国国家坐标系
32650 UTM 50N 局部高精度

(3) 转换坐标系

R
# 查看当前 CRS
st_crs(china)
# Coordinate Reference System:
#   EPSG: 4326
#   proj4string: "+proj=longlat +datum=WGS84 ..."

# 转换为 Web Mercator(3857)
china_3857 <- st_transform(china, 3857)

# 转换为中国 2000
china_4490 <- st_transform(china, 4490)

(4) 经纬度提取

R
# 从几何对象提取经纬度
coords <- st_coordinates(china)
# X = 经度, Y = 纬度


6. ggplot2 + geom_sf 画地图

(1) 基础地图

R
library(ggplot2)
library(sf)

ggplot(china) +
  geom_sf() +
  labs(title = "中国地图")

(2) 填色地图(按属性填色)

R
# 按省份填色
ggplot(china) +
  geom_sf(aes(fill = name)) +
  labs(title = "中国省份", fill = "省份") +
  theme_minimal()

(3) 简化几何(性能优化)

R
# 大地图(>10 万坐标点)用 st_simplify 简化
china_simple <- st_simplify(china, dTolerance = 1000)

# 画图(快 10 倍)
ggplot(china_simple) + geom_sf()

(4) 主题美化

R
ggplot(china) +
  geom_sf(fill = "lightblue", color = "white", linewidth = 0.3) +
  labs(title = "中国地图") +
  theme_void() +              # 去坐标轴
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "none"
  )


7. choropleth 分级统计图

(1) 合并数据 + 画分级图

R
# 模拟销售数据
sales_by_province <- data.frame(
  name = c("北京", "上海", "广东", "江苏", "浙江", "四川", "湖北", "陕西"),
  sales = c(5000, 4500, 4000, 3500, 3000, 2500, 2000, 1500)
)

# 合并到地图
china_sales <- china |>
  left_join(sales_by_province, by = "name")

# 画分级统计图
ggplot(china_sales) +
  geom_sf(aes(fill = sales), color = "white", linewidth = 0.3) +
  scale_fill_gradient(
    low = "lightyellow",
    high = "darkred",
    na.value = "gray90"
  ) +
  labs(title = "全国销售分布", fill = "销售额(万元)") +
  theme_void() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
    legend.position = "right"
  )

(2) 分级(连续 → 离散)

R
# 用 cut 把连续值分成 5 档
china_sales <- china_sales |>
  mutate(sales_level = cut(sales,
                            breaks = c(0, 1000, 2000, 3000, 5000),
                            labels = c("低", "中", "高", "很高"),
                            include.lowest = TRUE))

ggplot(china_sales) +
  geom_sf(aes(fill = sales_level), color = "white", linewidth = 0.3) +
  scale_fill_brewer(palette = "YlOrRd", na.value = "gray90") +
  labs(title = "全国销售等级", fill = "等级") +
  theme_void()


8. 点密度图

R
# 城市点(经纬度 + 销售)
cities <- data.frame(
  name = c("北京", "上海", "广州", "深圳"),
  lon = c(116.4, 121.5, 113.3, 114.1),
  lat = c(39.9, 31.2, 23.1, 22.5),
  sales = c(5000, 4500, 4000, 3000)
) |>
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

# 在地图上画点
ggplot() +
  geom_sf(data = china, fill = "lightgray", color = "white") +
  geom_sf(data = cities, aes(size = sales), color = "red", alpha = 0.7) +
  scale_size_continuous(range = c(2, 12), name = "销售额") +
  labs(title = "城市销售点图") +
  theme_void()


9. leaflet 交互地图

(1) 安装和基础

R
install.packages("leaflet")
library(leaflet)

# 基础交互地图
m <- leaflet() |>
  addTiles() |>  # OpenStreetMap 底图
  setView(lng = 116.4, lat = 39.9, zoom = 4)  # 北京为中心

m  # 在 RStudio Viewer 显示

(2) 加点(标记)

R
# 加城市点
leaflet(cities) |>
  addTiles() |>
  addMarkers(
    lng = ~lon, lat = ~lat,
    popup = ~paste0("<b>", name, "</b><br>销售:", sales, "万元"),
    label = ~name
  )

(3) 加圆圈(按大小)

R
leaflet(cities) |>
  addTiles() |>
  addCircles(
    lng = ~lon, lat = ~lat,
    radius = ~sqrt(sales) * 1000,  # 半径按销售
    color = "red",
    fillOpacity = 0.5,
    popup = ~paste0(name, ": ", sales, "万元")
  )

(4) 加分级填色

R
# 用 sf 数据
leaflet(china_sales) |>
  addTiles() |>
  addPolygons(
    fillColor = ~colorNumeric("YlOrRd", sales)(sales),
    fillOpacity = 0.7,
    color = "white",
    weight = 1,
    popup = ~paste0("<b>", name, "</b><br>销售:", sales, " 万元")
  ) |>
  addLegend(pal = colorNumeric("YlOrRd", china_sales$sales),
            values = ~sales, title = "销售额")

(5) 保存为 HTML

R
# 保存为独立 HTML(可分享)
library(htmlwidgets)
saveWidget(m, "interactive_map.html", selfcontained = FALSE)


10. 完整示例:全国销售热力图

下面是一个完整工作流示例,把本课所有地图知识串起来。

▶ 示例:4 城市销售地图(静态 + 交互)

R 📖 仅展示
# ============================================
# 4 城市销售地图
# 功能:ggplot2 静态图 + leaflet 交互图
# ============================================

library(sf)
library(ggplot2)
library(dplyr)
library(leaflet)
library(htmlwidgets)

# 1. Prepare data
# 4 城市点(经纬度)
cities <- data.frame(
  name = c("北京", "上海", "广州", "深圳"),
  lon = c(116.4074, 121.4737, 113.2644, 114.0579),
  lat = c(39.9042, 31.2304, 23.1291, 22.5431),
  sales = c(5000, 4500, 4000, 3000),
  region = c("华北", "华东", "华南", "华南")
) |>
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

# 中国省级简化多边形(这里模拟数据)
china_provinces <- data.frame(
  name = c("北京市", "上海市", "广东省"),
  lat = c(39.9, 31.2, 23.1),
  lon = c(116.4, 121.5, 113.3)
)

# 2. 静态地图:ggplot2 + sf
p_static <- ggplot() +
  # 简化:用 geom_point 替代 geom_sf(无 shapefile)
  borders(database = "world", regions = "China",
          fill = "lightgray", color = "white") +
  geom_sf(data = cities, aes(size = sales, color = region),
          alpha = 0.7) +
  geom_text(data = as.data.frame(st_coordinates(cities)),
            aes(X, Y, label = cities$name),
            vjust = -1.5, size = 4, fontface = "bold") +
  scale_size_continuous(range = c(3, 12), name = "销售额(万元)") +
  scale_color_brewer(palette = "Set1", name = "区域") +
  labs(title = "中国 4 大城市销售分布",
       subtitle = "数据:2024 年 Q4",
       caption = "底图:Natural Earth | 制图:R + ggplot2") +
  coord_sf(xlim = c(100, 125), ylim = c(20, 45)) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
    plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
    legend.position = "bottom"
  )

print(p_static)

# 3. 保存静态图
ggsave("static_sales_map.png", p_static,
       width = 10, height = 8, dpi = 300)
cat("=== 静态地图已保存:static_sales_map.png ===\n")

# 4. 交互地图:leaflet
cities_df <- as.data.frame(st_coordinates(cities)) |>
  rename(lon = X, lat = Y) |>
  cbind(name = cities$name, sales = cities$sales, region = cities$region)

m_interactive <- leaflet(cities_df) |>
  addTiles() |>
  setView(lng = 114, lat = 33, zoom = 4) |>
  addCircleMarkers(
    lng = ~lon, lat = ~lat,
    radius = ~sqrt(sales) / 5,
    color = ~case_when(
      region == "华北" ~ "red",
      region == "华东" ~ "blue",
      region == "华南" ~ "green"
    ),
    fillOpacity = 0.6,
    stroke = TRUE,
    weight = 2,
    popup = ~paste0(
      "<div style='font-family: Arial; font-size: 14px;'>",
      "<b>", name, "</b><br>",
      "区域:", region, "<br>",
      "销售:", sales, " 万元<br>",
      "经度:", round(lon, 2), "<br>",
      "纬度:", round(lat, 2),
      "</div>"
    ),
    label = ~paste0(name, ": ", sales, "万元")
  ) |>
  addLegend(
    "bottomright",
    colors = c("red", "blue", "green"),
    labels = c("华北", "华东", "华南"),
    title = "区域"
  ) |>
  addScaleBar(position = "bottomleft")

# 5. 显示(在 RStudio Viewer)
m_interactive

# 6. 保存为 HTML
saveWidget(m_interactive, "interactive_sales_map.html",
           selfcontained = TRUE)
cat("=== 交互地图已保存:interactive_sales_map.html ===\n")

# 7. 输出统计
cat("\n=== 销售统计 ===\n")
print(cities_df |> select(name, region, sales))
逻辑代码 83 行(超过 40 行限制,仅展示)

预期输出:


❓ 常见问题

Q:sf 和 sp 选哪个? A:sf(Simple Features)是 R 空间数据现代标准,tidyverse 风格,与 dplyr/ggplot2 无缝整合。新代码用 sf,老 sp 代码已停止维护。**

Q:4326 和 3857 怎么选? A:4326(WGS84 经纬度)通用 + GPS;3857(Web Mercator)地图服务 + 谷歌/OSM。ggplot2 + sf 一般用 4326,leaflet 默认 3857。**

Q:shapefile 从哪下载? A:

Q 地图中文乱码怎么办?
A theme() 加 family = "SimHei"。或者用 borders(database = "world", regions = "China") 时已内置中文名。
Q 怎么画 3D 地图?
Arayshader 包(基于 ggplot2 转 3D)或 plotly 的 3D 散点 + 地图底图。本课不深入。

📖 小节


📝 作业

  1. 基础题:从阿里云 DataV 下载中国省级 GeoJSON,用 st_read() 读取,用 ggplot() + geom_sf() 画基础中国地图(不填色)。

  2. 基础题:在上题地图上按省份填色(用 aes(fill = name)),加 theme_void() + 主题美化。

  3. 基础题:用 borders(database = "world", regions = "China") + geom_point(data = cities, aes(lon, lat, size = sales)) 画中国城市点图,4 个城市自定义经纬度。

  4. 进阶题:模拟 5 个城市销售数据(含经纬度/销售/类别),用 leaflet 画交互地图:① 圆圈大小映射销售 ② 颜色映射类别 ③ 加弹窗显示详情 ④ 加图例 + 比例尺。保存为 HTML。

  5. 挑战题:完整工作流——下载中国省级 GeoJSON,模拟 31 省销售数据:① left_join 合并到地图 ② 用 cut() 分成 5 档 ③ ggplot2 画分级统计图 ④ 美化(标题/副标题/中文字体) ⑤ leaflet 画交互版(含图例)⑥ 保存 PNG + HTML。截图保存。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏