這裡介紹如何在 R 中使用 Leaflet 繪製互動式的地圖,並將資料呈現在地圖上。
Leaflet 是一套相當熱門的網頁互動式地圖繪製工具,在 R 中我們也可以利用這套工具來繪製地圖,並將各種資料標示在地圖上,讓使用者以互動式的方式瀏覽資料。
R 的 leaflet 套件讓使用者在 R 的環境中(標準 R 執行環境或 RStudio)直接畫出網頁互動式的地圖,使用者可以使用滑鼠操作地圖(放大、移動等),另外 Leaflet 的地圖也可以內坎在 R Markdown 文件或 Shiny 應用程式中,與既有的文件或應用程式整合在一起。
基本用法
R 的 leaflet 套件已經被收錄在官方的 CRAN 網站上,在 R 中使用 install.packages 即可安裝:
install.packages("leaflet")
接著載入 leaflet 套件,即可開始使用:
library(leaflet)
leaflet 可以配合 magrittr 的 %>% 管線運算子使用:
map <- leaflet() %>%
addTiles() %>% # 加上預設的地圖資料
addMarkers(lng=120.239, lat=22.992, popup="訊息方塊的文字說明")
map # 繪製地圖

若不熟悉 magrittr 的 %>% 管線運算子語法,也可以使用傳統的 R 語法,兩種寫法功能都一樣:
map <- leaflet()
map <- addTiles(map)
map <- addMarkers(map, lng=120.239, lat=22.992, popup="訊息方塊的文字說明")
map
Leaflet 地圖物件
leaflet 函數會傳回一個 Leaflet 的地圖物件(map widget object),此物件中儲存了許多地圖繪製的相關資訊,可於後續持續修改與更新,而大部分的 leaflet 套件中的函數,第一個參數都是指定地圖物件,因此可以非常容易與 magrittr 的管線運算子配合使用。
地圖屬性
在建立 Leaflet 地圖物件之後,可以利用以下幾個函數來修改地圖物件的屬性:
| 函數名稱 | 說明 |
|---|---|
setView | 設定地圖的中心座標位置以及縮放比例。 |
fitBounds | 設定地圖的位置與縮放比例,讓畫面剛好可呈現指定的方形區域。 |
clearBounds | 清除指定的方形區域,顯示最大的區域(全球地圖)。 |
以下示範這幾個函數的使用方式,通常在建立地圖時都會使用 setView 設定地圖的位置與縮放比例:
m <- leaflet() %>%
addTiles() %>%
setView(-71.0382679, 42.3489054, zoom = 18)
m

若要顯示特定的矩形區域,則可使用 fitBounds 來變更地圖的顯示區域:
m %>% fitBounds(-72, 40, -70, 43)

清除了指定的方形區域之後,就會顯示最大的區域,亦即全球地圖:
m %>% clearBounds()

資料來源
leaflet 與各種地圖圖層函數都有一個 data 參數,可用來指定空間資料(spatial data)的來源,其可接受的資料格式有很多種:
| 類型 | 資料格式 |
|---|---|
| 基本類型 | 包含經緯度的 R 矩陣 |
| 包含經緯度的 data frame | |
| sp 套件 | SpatialPoints[DataFrame] |
Line/Lines | |
SpatialLines[DataFrame] | |
Polygon/Polygons | |
SpatialPolygons[DataFrame] | |
| maps 套件 | 由 map 函數所產生的 data frame |
當資料來源為一般的 data frame 時,leaflet 會自動尋找 data frame 中名稱類似 lng、longitude 與 lat、latitude 作為經緯度座標,以下是一個以圓圈標示資料的範例:
set.seed(3)
point.df <- data.frame(
Lat = 22.992 + rnorm(10)/800,
Long = 120.239 + rnorm(10)/800
)
m <- leaflet(point.df) %>%
addTiles() %>%
setView(lng = 120.239, lat = 22.992, zoom = 17)
m %>% addCircles()

亦可使用公式的方式明確指定經緯度座標的變數名稱:
m %>% addCircles(lng = ~Long, lat = ~Lat)
也可以針對特定的圖層個別指定不同的資料來源:
m <- leaflet() %>%
addTiles() %>%
setView(lng = 120.239, lat = 22.992, zoom = 17)
m %>% addCircles(data = point.df, lng = ~Long, lat = ~Lat)
關於 sp 與 maps 套件的資料來源範例,可參考 R Leaflet 套件官方網站的範例。
公式介面
leaflet 套件中所有圖層函數的參數都可以接受一般的 R 向量(例如以數值向量指定經緯度等),也可以使用單邊的公式指定 data 資料來源中的變數,例如 ~ x 就是指 data 中的 x 變數,另外也可以結合各種的數學轉換,例如 ~ sqrt(x + 1)。
以下是一個使用公式的範例:
set.seed(3)
point.df <- data.frame(
lat = 22.97 + rnorm(100)/800,
long = 120.23 + rnorm(100)/800,
size = runif(100, 5, 20),
color = sample(colors(), 100)
)
m <- leaflet(point.df) %>%
addTiles() %>%
setView(lng = 120.23, lat = 22.97, zoom = 17)
m %>% addCircleMarkers(radius = ~size, color = ~color, fill = FALSE)

這是使用一般 R 向量的例子:
m %>% addCircleMarkers(radius = runif(100, 4, 10), color = c('red'))

地圖資料來源
Leaflet 使用圖磚(map tiles)的方式繪製基本地圖,目前網路上的各種地圖服務大部分也都是使用這種方式。
要在 leaflet 地圖物件上加上基本的地圖,可以使用 addTiles:
m <- leaflet() %>% setView(lng=120.239, lat=22.992, zoom = 12)
m %>% addTiles()

Leaflet 預設會使用 OpenStreetMap 的地圖資料,除此之外也可以使用 addProviderTiles 指定不同的地圖資料來源:
m %>% addProviderTiles("Stamen.Toner")

addTiles 也可以允許使用者自定地圖資料來源的網址樣板。
WMS 圖磚
addWMSTiles 函數可以在地圖上加入 WMS(Web Map Service)圖磚,下面這個是顯示雷達回波圖的例子(資料來源為 Iowa Environmental Mesonet):
leaflet() %>% addTiles() %>% setView(-93.65, 42.0285, zoom = 5) %>%
addWMSTiles(
"http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi",
layers = "nexrad-n0r-900913",
options = WMSTileOptions(format = "image/png", transparent = TRUE),
attribution = "Weather data © 2012 IEM Nexrad"
)

多個地圖資料來源
Leaflet 允許在一個地圖上同時加入多個地圖資料來源,通常可以利用透明度的設定,套疊多種地圖:
m %>% addProviderTiles("MtbMap") %>%
addProviderTiles("Stamen.TonerLines",
options = providerTileOptions(opacity = 0.35)
) %>%
addProviderTiles("Stamen.TonerLabels")

地圖標示
地圖標示可以用來呈現地圖上的經緯度座標位置,我們可以使用 SpatialPoints、SpatialPointsDataFrame 的方式表示經緯度座標,或是直接使用兩個行(column)的矩陣來表示(第一行為經度,第二行為緯度)。另外也可以使用一般的 data frame 配合公式指定經緯度座標欄位,以下是一些使用範例。
圖示標示
普通的圖示標示可以使用 addMarker 來加入,而其 popup 參數可以指定點擊標示時要出現訊息內容:
# 顯示 quakes 的前 20 筆資料
leaflet(data = quakes[1:20,]) %>% addTiles() %>%
addMarkers(~long, ~lat, popup = ~as.character(mag))

自訂標示
標示用的圖示也可以自行指定,例如利用 makeIcon 配合 URL 網址的方式指定圖檔:
greenLeafIcon <- makeIcon(
iconUrl = "http://leafletjs.com/docs/images/leaf-green.png",
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94,
shadowUrl = "http://leafletjs.com/docs/images/leaf-shadow.png",
shadowWidth = 50, shadowHeight = 64,
shadowAnchorX = 4, shadowAnchorY = 62
)
leaflet(data = quakes[1:4,]) %>% addTiles() %>%
addMarkers(~long, ~lat, icon = greenLeafIcon)

如果有多個同一系列的圖示,大小都相同,只是網址不同,則可以使用 icons 一次建立多個圖示:
quakes1 <- quakes[1:10,]
leafIcons <- icons(
iconUrl = ifelse(quakes1$mag < 4.6,
"http://leafletjs.com/docs/images/leaf-green.png",
"http://leafletjs.com/docs/images/leaf-red.png"
),
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94,
shadowUrl = "http://leafletjs.com/docs/images/leaf-shadow.png",
shadowWidth = 50, shadowHeight = 64,
shadowAnchorX = 4, shadowAnchorY = 62
)
leaflet(data = quakes1) %>% addTiles() %>%
addMarkers(~long, ~lat, icon = leafIcons)

對於樣式大小都不同的多個圖示,則可使用 iconList 來處理:
# 建立多個圖示
oceanIcons <- iconList(
ship = makeIcon("ferry_36.png", 36, 36),
pirate = makeIcon("danger_48.png", 48, 48)
)
# 建立測試用資料
set.seed(5)
df <- sp::SpatialPointsDataFrame(
cbind(
(runif(10) - .5) * 10 - 90.620130, # lng
(runif(10) - .5) * 3.8 + 25.638077 # lat
),
data.frame(type = factor(
ifelse(runif(10) > 0.75, "pirate", "ship"),
c("ship", "pirate")
))
)
leaflet(df) %>% addTiles() %>%
# 根據 df$type 選擇圖示
addMarkers(icon = ~oceanIcons[type])

如果想要自己畫出上面這個地圖,請先下載兩個圖示檔 [ferry_36.png][9] 與 [danger_48.png][10]。
叢集標示
如果有大量的標示集中在同一個小區域,可以使用 Leaflet.markercluster 這個外掛工具,在 R 中可以使用 clusterOptions 參數來啟用這個外掛工具:
leaflet(quakes) %>% addTiles() %>% addMarkers(
clusterOptions = markerClusterOptions()
)

圓圈標示
addCircleMarkers 可以在地圖上加上圓圈的標示,而這種圓圈的大小是固定的,不會因為縮放地圖而改變:
leaflet(df) %>% addTiles() %>% addCircleMarkers()

自訂圓圈的大小、顏色、樣式、透明度等屬性:
# 將因子對應至顏色
pal <- colorFactor(c("navy", "red"), domain = c("ship", "pirate"))
leaflet(df) %>% addTiles() %>%
addCircleMarkers(
radius = ~ifelse(type == "ship", 6, 10),
color = ~pal(type),
stroke = FALSE, fillOpacity = 0.5
)

訊息方塊
addPopups 可以在地圖上加入訊息方塊,其內容可以包涵任何的 HTML 程式碼:
content <- paste(sep = "<br/>",
"<b><a href='http://www.samurainoodle.com'>Samurai Noodle</a></b>",
"606 5th Ave. S",
"Seattle, WA 98138"
)
leaflet() %>% addTiles() %>%
addPopups(-122.327298, 47.597131, content,
options = popupOptions(closeButton = FALSE)
)

訊息方塊最常見的用法就是配合地圖上的標示一同使用,當使用者點擊標示時,跳出對應的訊息:
library(htmltools)
df <- read.csv(textConnection(
"Name,Lat,Long
Samurai Noodle,47.597131,-122.327298
Kukai Ramen,47.6154,-122.327157
Tsukushinbo,47.59987,-122.326726"
))
leaflet(df) %>% addTiles() %>%
addMarkers(~Long, ~Lat, popup = ~htmlEscape(Name))

上面的 htmlEscape 是用來處理一些 HTML 的特殊字元的函數,可讓資料可以依照原本的樣子呈現在地圖上,在這個例子中可以將其省略,但在一般的狀況下最好都加入這個處理步驟,確保資料可以正確呈現。
線條與形狀
R 的 Leaflet 套件可以讓使用者在地圖上加入各種線條或形狀,其可接受的資料格式包含:
sp套件的SpatialPolygons、SpatialPolygonsDataFrame、Polygon與Polygons。sp套件的SpatialLines、SpatialLinesDataFrame、Lines與Line。maps套件的map物件(map(fill = TRUE)代表多邊形,map(fill = FALSE)代表折線)。- 兩個行(column)的矩陣,第一行為經度,第二行為緯度。若要包含多個多邊形,可以用
(NA, NA)分隔。
多邊形
addPolygons 可加入多邊形:
Lat <- c(22.992, 22.982, 22.970, 22.990)
Long <- c(120.289, 120.299, 120.267, 120.267)
leaflet() %>% addTiles() %>% addPolygons(lng = Long, lat = Lat)

以下是繪製美國幾個州範例,資料來源為美國人口普查局:
library(rgdal)
states <- readOGR("census/cb_2015_us_state_20m.shp",
layer = "cb_2015_us_state_20m", verbose = FALSE)
neStates <- subset(states, states$STUSPS %in% c(
"CT","ME","MA","NH","RI","VT","NY","NJ","PA"
))
leaflet(neStates) %>%
addPolygons(
stroke = FALSE, fillOpacity = 0.5, smoothFactor = 0.5,
color = ~colorQuantile("YlOrRd", states$AWATER)(AWATER)
)

圓圈
addCircles 可以在地圖上加上圓圈,其與 addCircleMarkers 類似,只是 addCircles 的半徑單位是實際的公尺,而 addCircleMarkers 的單位則是螢幕上的像素,所以 addCircles 的圓圈會依據地圖的縮放比例自動調整大小,而 addCircleMarkers 的圓圈大小則是固定的。
cities <- read.csv(textConnection("
City,Lat,Long,Pop
Boston,42.3601,-71.0589,645966
Hartford,41.7627,-72.6743,125017
New York City,40.7127,-74.0059,8406000
Philadelphia,39.9500,-75.1667,1553000
Pittsburgh,40.4397,-79.9764,305841
Providence,41.8236,-71.4222,177994
"))
leaflet(cities) %>% addTiles() %>%
addCircles(lng = ~Long, lat = ~Lat, weight = 1,
radius = ~sqrt(Pop) * 30, popup = ~City
)

矩形
addRectangles 可以在地圖上標示矩形的區域:
leaflet() %>% addTiles() %>%
addRectangles(
lng1=-118.456554, lat1=34.078039,
lng2=-118.436383, lat2=34.062717,
fillColor = "transparent"
)

