這裡介紹如何在 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  # 繪製地圖

RStudio 使用 Leftlet 繪製地圖

若不熟悉 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

setView 設定地圖顯示區域

若要顯示特定的矩形區域,則可使用 fitBounds 來變更地圖的顯示區域:

m %>% fitBounds(-72, 40, -70, 43)

fitBounds 變更地圖的顯示區域

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

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 中名稱類似 lnglongitudelatlatitude 作為經緯度座標,以下是一個以圓圈標示資料的範例:

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)

關於 spmaps 套件的資料來源範例,可參考 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")

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")

套疊多種地圖

地圖標示

地圖標示可以用來呈現地圖上的經緯度座標位置,我們可以使用 SpatialPointsSpatialPointsDataFrame 的方式表示經緯度座標,或是直接使用兩個行(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 套件的 SpatialPolygonsSpatialPolygonsDataFramePolygonPolygons
  • sp 套件的 SpatialLinesSpatialLinesDataFrameLinesLine
  • 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"
  )

矩形