Rによる地図と空間データ:sf、ggplot2、Leafletの完全チュートリアル
地理空間データは、パンデミックマップ、Didiの配車サービス利用状況のヒートマップ、JD.comの配送対象エリアなど、実世界のプロジェクトにおいてますます重要になっています。このレッスンでは、Rを用いた地図の可視化について学びます。具体的には、sfパッケージを使ってベクトルデータを処理し、ggplot2で地図を作成し、Leafletを使ってインタラクティブな地図を構築する方法を解説します。
このレッスンを修了すると、R を使用して 4 種類の地図を作成できるようになります。具体的には、静的地図(ggplot2 + sf)、コロプレース地図、点密度地図、およびインタラクティブ地図(leaflet)です。
1. 学習内容
- 空間データの基礎(ベクトル/ラスター、シェープファイル、座標系)
- sf パッケージはシェープファイルを読み込み、処理します
- ggplot2 と geom_sf を使った地図の作成
- CRS 座標系(WGS84 / Web Mercator)
- コロプレット図
- リーフレットのインタラクティブマップ
- 実例:中国各都市の売上ヒートマップ
2. 物流ヒートマップにまつわるストーリー
(1) 課題:販売データに規模感が欠けている
アリスは全国販売を担当している。マネージャーが「どの州の売り上げが一番いい?」と尋ねた。アリスはスプレッドシートをしばらく眺めた後、マネージャーに「地図を見せて」と言った。
(2) Rを用いた解決策
library(sf)
library(ggplot2)
# 1. View a Map of China
china <- st_read("china.shp")
# 2. Consolidate Sales Data
china_sales <- china |>
left_join(sales_by_province, by = c("name" = "province"))
# 3. Line Chart of Categorical Statistics
ggplot(china_sales) +
geom_sf(aes(fill = sales)) +
scale_fill_gradient(low = "lightblue", high = "darkred") +
labs(title = "National Sales Distribution", fill = "Sales")
たった3行のコードで、中国における売上を可視化した出版物レベルの地図。
3. 空間データの基礎
(1) ベクターとラスター
graph LR
A[Spatial Data] --> B[Vector Data<br/>Vector]
A --> C[Raster Data<br/>Raster]
B --> D[pt Point<br/>Line<br/>Polygon]
C --> E[Pixel Pixel<br/>Satellite image/Temperature Map]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
このレッスンでは、ベクトルデータ(点、線、面)について解説します。
(2) シェープファイルとは何ですか?
シェープファイル = ESRIのベクトルデータ規格であり、以下の4つのファイルで構成されています:
data/
├── china.shp # Geometric Information (Coordinates)
├── china.shx # Shape Index
├── china.dbf # Property Information (Name, Population, etc.)
└── china.prj # Projection Information (Coordinate System)
GeoJSON
現代のウェブマップでは、一般的にGeoJSON(JSONをベースとした形式)が使用されています:
{
"type": "Feature",
"properties": {"name": "Beijing", "value": 1000},
"geometry": {
"type": "Point",
"coordinates": [116.4, 39.9]
}
}
4. sf パッケージ:Spatial Data Framework
(1) なぜSFを使うのか?
| バッグ | 特徴 | おすすめ |
|---|---|---|
| sf | モダン、Rのtidyverseスタイル、ggplot2との統合 | ⭐⭐⭐ |
| sp | 旧式、Rの基本構文 | ❌ 新規コードでは推奨されません |
| ラスター | ラスターデータのみ | ラスター |
(2) インストールと読み込み
install.packages("sf")
library(sf)
(3) シェープファイルを読み込む
library(sf)
# Read 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 Latitude and Longitude
# View Structure
print(china)
# It basically means data.frame More geometry col
# A tibble: 34 × 6
# name population ... geometry
# <chr> <int> <MULTIPOLYGON>
# Geometric Information
st_geometry(china)
st_crs(china) # Coordinate System
st_bbox(china) # Bounding Box
(4) GeoJSONの読み込み
# Read GeoJSON
cities <- st_read("data/cities.geojson")
# or from URL
cities <- st_read("https://example.com/cities.geojson")
(5) シェープファイル/GeoJSONの作成
# Write shapefile
st_write(china, "output/china_new.shp")
# Write GeoJSON
st_write(china, "output/china.geojson")
5. CRS座標系
(1) CRSとは何ですか?
CRS(座標参照系)—地球の「球面」を「平面」に投影すること:
graph LR
A[Earth's Surface] -->|WGS84 4326<br/>Latitude and Longitude| B[Cartesian coordinates]
A -->|Web Mercator 3857<br/>m| B
A -->|China 2000<br/>m| B
(2) 一般的なCRSコード
| EPSG | 名称 | 用途 |
|---|---|---|
| 4326 | WGS84 | GPS / 一般 (最も一般的) |
| 3857 | Web Mercator | Google マップ、OpenStreetMap |
| 4490 | CGCS2000 | 中国国家座標系 |
| 32650 | UTM 50N | 高精度ローカル |
(3) 座標系の変換
# View Current CRS
st_crs(china)
# Coordinate Reference System:
# EPSG: 4326
# proj4string: "+proj=longlat +datum=WGS84 ..."
# Convert to Web Mercator (3857)
china_3857 <- st_transform(china, 3857)
# Convert to China 2000
china_4490 <- st_transform(china, 4490)
(4) 緯度・経度の抽出
# Extracting Latitude and Longitude from Geometric Objects
coords <- st_coordinates(china)
# X = Longitude, Y = Latitude
6. ggplot2 と geom_sf を使った地図の作成
(1) ベースマップ
library(ggplot2)
library(sf)
ggplot(china) +
geom_sf() +
labs(title = "Map of China")
(2) 地図の着色(属性による着色)
# Color by Province
ggplot(china) +
geom_sf(aes(fill = name)) +
labs(title = "Provinces of China", fill = "Province") +
theme_minimal()
(3) 幾何学的簡略化(パフォーマンスの最適化)
# World Map (>100,000 coordinate points) use st_simplify to simplify
china_simple <- st_simplify(china, dTolerance = 1000)
# Drawing (10x faster)
ggplot(china_simple) + geom_sf()
(4) テーマのカスタマイズ
ggplot(china) +
geom_sf(fill = "lightblue", color = "white", linewidth = 0.3) +
labs(title = "Map of China") +
theme_void() + # Hide Axes
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "none"
)
7. コロプレット図
(1) データの結合 + カテゴリ別グラフの作成
# Simulated Sales Data
sales_by_province <- data.frame(
name = c("Beijing", "Shanghai", "Guangdong", "Jiangsu", "Zhejiang", "Sichuan", "Hubei", "Shaanxi"),
sales = c(5000, 4500, 4000, 3500, 3000, 2500, 2000, 1500)
)
# Merge into Map
china_sales <- china |>
left_join(sales_by_province, by = "name")
# Draw a categorical bar chart
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 = "National Sales Distribution", fill = "Sales (10,000 yuan)") +
theme_void() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
legend.position = "right"
)
(2) 分類(連続型 → 離散型)
# Use cut to divide continuous values into 5 levels
china_sales <- china_sales |>
mutate(sales_level = cut(sales,
breaks = c(0, 1000, 2000, 3000, 5000),
labels = c("Low", "Mid", "High", "Very high"),
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 = "National Sales Grades", fill = "Level") +
theme_void()
8. 点密度プロット
# City Points (Latitude and Longitude + Sales)
cities <- data.frame(
name = c("Beijing", "Shanghai", "Guangzhou", "Shenzhen"),
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)
# Draw a point on the map
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 = "Sales") +
labs(title = "Map of City Sales Locations") +
theme_void()
9. Leaflet インタラクティブマップ
(1) インストールと基本操作
install.packages("leaflet")
library(leaflet)
# Basic Interactive Map
m <- leaflet() |>
addTiles() |> # OpenStreetMap Base Map
setView(lng = 116.4, lat = 39.9, zoom = 4) # Beijing as the center
m # Show in RStudio Viewer
(2) マーク(ラベル)を追加する
# Add a city
leaflet(cities) |>
addTiles() |>
addMarkers(
lng = ~lon, lat = ~lat,
popup = ~paste0("<b>", name, "</b><br>Sales: ", sales, " 10,000 yuan"),
label = ~name
)
(3) 円を追加する(サイズ順)
leaflet(cities) |>
addTiles() |>
addCircles(
lng = ~lon, lat = ~lat,
radius = ~sqrt(sales) * 1000, # By Sales Radius
color = "red",
fillOpacity = 0.5,
popup = ~paste0(name, ": ", sales, "10,000 yuan")
)
(4) グラデーション着色
# Use sf Data
leaflet(china_sales) |>
addTiles() |>
addPolygons(
fillColor = ~colorNumeric("YlOrRd", sales)(sales),
fillOpacity = 0.7,
color = "white",
weight = 1,
popup = ~paste0("<b>", name, "</b><br>Sales: ", sales, " 10,000 yuan")
) |>
addLegend(pal = colorNumeric("YlOrRd", china_sales$sales),
values = ~sales, title = "Sales")
(5) HTMLとして保存
# Save as a standalone HTML file (Shareable)
library(htmlwidgets)
saveWidget(m, "interactive_map.html", selfcontained = FALSE)
10. 完全な例:全国の売上ヒートマップ
以下は、このレッスンで取り上げた地図関連の概念をすべて結びつけた完全なワークフローの例です。
▶ サンプル:4つの都市の販売マップ(静的+インタラクティブ)
# ============================================
# 4 City Sales Map
# Features: ggplot2 Static Map + leaflet Interactive Map
# ============================================
library(sf)
library(ggplot2)
library(dplyr)
library(leaflet)
library(htmlwidgets)
# 1. Prepare data
# 4 City Points (Latitude and Longitude)
cities <- data.frame(
name = c("Beijing", "Shanghai", "Guangzhou", "Shenzhen"),
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("Northern China", "East China", "South China", "South China")
) |>
st_as_sf(coords = c("lon", "lat"), crs = 4326)
# Simplified Polygons of China's Provinces (Sample data here)
china_provinces <- data.frame(
name = c("Beijing", "Shanghai", "Guangdong Province"),
lat = c(39.9, 31.2, 23.1),
lon = c(116.4, 121.5, 113.3)
)
# 2. Static Map: ggplot2 + sf
p_static <- ggplot() +
# Simplify: use geom_point Replace geom_sf (no 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 = "Sales (10,000 yuan)") +
scale_color_brewer(palette = "Set1", name = "Region") +
labs(title = "China 4 Sales Distribution in Major Cities",
subtitle = "Data: 2024 Q4",
caption = "Base Map: Natural Earth | Chart: 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. Save Static Image
ggsave("static_sales_map.png", p_static,
width = 10, height = 8, dpi = 300)
cat("=== The static map has been saved: static_sales_map.png ===\n")
# 4. Interactive Map: 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 == "Northern China" ~ "red",
region == "East China" ~ "blue",
region == "South China" ~ "green"
),
fillOpacity = 0.6,
stroke = TRUE,
weight = 2,
popup = ~paste0(
"<div style='font-family: Arial; font-size: 14px;'>",
"<b>", name, "</b><br>",
"Region: ", region, "<br>",
"Sales: ", sales, " 10,000 yuan<br>",
"Longitude: ", round(lon, 2), "<br>",
"Latitude: ", round(lat, 2),
"</div>"
),
label = ~paste0(name, ": ", sales, "10,000 yuan")
) |>
addLegend(
"bottomright",
colors = c("red", "blue", "green"),
labels = c("Northern China", "East China", "South China"),
title = "Region"
) |>
addScaleBar(position = "bottomleft")
# 5. Show (in RStudio Viewer)
m_interactive
# 6. Save as HTML
saveWidget(m_interactive, "interactive_sales_map.html",
selfcontained = TRUE)
cat("=== The interactive map has been saved: interactive_sales_map.html ===\n")
# 7. Output Statistics
cat("\n=== Sales Statistics ===\n")
print(cities_df |> select(name, region, sales))
期待される出力:
- 静的マップ(PNG、300 dpi、4種類のドットサイズ別の売上を表示)
- インタラクティブマップ(HTML、マウスを乗せるとツールチップが表示されます)
❓ よくある質問
Q: sf と sp、どちらを選べばいいですか? A: sf(Simple Features)は、Rの空間データの現代的な標準であり、tidyverseのスタイルに準拠し、dplyrやggplot2とシームレスに連携します。新しいコードにはsfを使用してください。旧式のspコードはもはやメンテナンスされていません。**
Q: 4326と3857のどちらを選べばいいですか? A: 4326(WGS84の緯度・経度)は一般的な用途やGPS用、3857(Web Mercator)は地図サービスやGoogle/OSM用です。ggplot2とsfでは通常4326が使用されますが、Leafletのデフォルトは3857です。**
Q: シェープファイルはどこからダウンロードできますか? A:
- 中国の地図:Alibaba Cloud DataV.GeoAtlas (https://datav.aliyun.com/portal/school/atlas/area_selector)
- 世界地図:Natural Earth (https://www.naturalearthdata.com/)
- 中国の詳細地図:https://github.com/longwosion/geojson-map-china
family = "SimHei" に theme() を追加してください。あるいは、borders(database = "world", regions = "China") にはデフォルトで中国語の名称がすでに含まれています。rayshader パッケージ(ggplot2のプロットを3Dに変換する)を使用するか、plotlyの3D散布図とマップの背景を組み合わせて使用してください。このレッスンでは、これについて詳しく説明しません。- ggplot2 を使用した レポート/論文(静的、出版に適した品質)
- Web/アプリ:Leaflet を使用(インタラクティブ、ズーム機能付き)
- 共有可能:Leaflet を使用して HTML をエクスポートする
📖 まとめ
- 空間データ=ベクトル(点・線・ポリゴン)+ラスター(ピクセル)。このレッスンではベクトルについて解説します。
- シェープファイルは4つのファイル(.shp、.shx、.dbf、.prj)で構成されています。GeoJSONは最新のWebフォーマットです。
- sf は、tidyverse スタイルの R 空間データの現代的な標準です
- 一般的に使用される座標系:4326(WGS84 緯度・経度)/3857(Web Mercator)
ggplot2 +
geom_sf()を使用して静的な地図を描画する。コロプレット地図にはaes(fill = value)+scale_fill_gradientを使用する。 - インタラクティブマップのリーフレット:addTiles(ベースマップ)+ addMarkers/addCircles(ポイント)+ addPolygons(領域)
borders(database = "world")世界地図を素早く描く(シェープファイル不要)- 保存:
ggsave()静的コンテンツはPNG/PDF形式、saveWidget()インタラクティブコンテンツはHTML形式 - Rの地図活用例:企業の地理的分布、パンデミック、物流、顧客分布、ヒートマップ
📝 練習問題
-
基本演習:Alibaba Cloud DataVから中国の省に関するGeoJSONファイルをダウンロードし、
st_read()を使用してインポートした後、ggplot() + geom_sf()を使って中国の基本地図を描いてください(色の塗りつぶしは行わないでください)。 -
基本問題:前の問題の地図を省ごとに色分けし(
aes(fill = name)を使用)、さらにtheme_void()とテーマに沿ったデザインを追加してください。 -
基本演習:
borders(database = "world", regions = "China")およびgeom_point(data = cities, aes(lon, lat, size = sales))を使用して、4つの都市について独自の緯度・経度座標を指定し、中国の都市地図を作成してください。 -
応用演習:5つの都市の販売データ(緯度・経度、売上高、カテゴリーを含む)をシミュレートし、Leaflet を使用してインタラクティブな地図を作成してください。① 売上高を円の大きさに反映させる;② カテゴリーを色で区別する;③ 詳細を表示するためのツールチップを追加する;④ 凡例とスケールを追加する。HTML 形式で保存してください。
-
課題:ワークフローの完成—中国の各省のGeoJSONデータをダウンロードし、31省の販売データをシミュレートする:① LEFT JOINを使用して地図に統合する ②
cut()を使用して5つのカテゴリーに分類する ③ ggplot2を使用してカテゴリー別の統計グラフを作成する ④ グラフの書式設定を行う(タイトル/サブタイトル/中国語フォント) ⑤ Leafletを使用してインタラクティブなバージョンを作成する (凡例を含む) ⑥ PNGおよびHTML形式で保存する。スクリーンショットを撮影し、保存する。



