作者:李誉辉
四川大学在读研究生
最近一直在寻找方便上手的地图包,实验了google地图包,百度地图包,发现还是有很多限制。
百度地图包搭配的REmap总是崩溃,
google地图包ggmap现在需要国际信用卡注册,总之,都不甚满意。
虽然ggplot2地图功能非常丰富,但是还是不够简单快捷。
尤其是当我们仅仅需要绘制一些简单的地图时。
经过圈子大佬的推荐,开始尝试leaflet包,确实给我很多惊喜。
leaflet包相对其它地图包,有很多优点和缺点,
首先,绘制地图简单快捷,因为都是基于供应商的tiles,一行代码就可以render出基本widget地图。
支持管道传参,一个图层一个图层进行添加,代码结构更加清晰。
其次,有很多tiles供应商可以选择,包括高德、google、Stamen, Esri, OpenWeatherMap,NASA,
等好几十个tiles供应商。当然其中一些需要注册。其中的google可以绕过注册,已经很难得了。
对于在地图上添加markers图标,shapes形状,线条等,异常方便快捷,这在ggplot2中很难做到的。
支持栅格数据,rasters栅格数据是基于像素点的地图。可以看出,leaflet具有很强的包容性。
支持多种投影坐标系,甚至可以自定义坐标系,这在某些特殊场景非常重要。
当然还有更重要的是,其具有一定交互能力,可以缩放拖拽,
简单的图层切换也不需要使用Shiny。使得更容易上手。
其它特点,首先tiles是基于供应商的,必须联网,
其次对颜色支持不一样,只支持HEX颜色空间和colors()中的颜色名称。 当然内置的几个palette函数,非常特别。
总之,笔者认为leaflet包与ggplot2包的互补性非常好,刚好弥补了ggplot2中画地图的不足。
对于大量数据需要呈现在地图上的场景,还是`ggplot2, leaflet中插入其它plots不便也没有必要。
下面是笔者总结的中文教程,使得新人能更快上手。
说明:以下图片皆为截图,限于技术问题,不能完全展现它的交互式功能。
Widget地图框的设置,就是确定Widget的基本参数,
包括CRS坐标系,widget的中心坐标,zoom level(缩放)的范围, widget边界坐标,data数据等。
leaflet()中有个options参数,用leafletOptions()函数来指定,可以控制widget缩放范围。
语法:
1leafletOptions(minZoom = NULL, maxZoom = NULL, crs = leafletCRS(),
2  worldCopyJump = NULL, preferCanvas = NULL, ...)
关键参数:
minZoom,表示最低缩小倍数,作用于所有地图层。
maxZoom,表示最高放大倍数,作用于所有地图层。
crs, 表示指定坐标系统,
preferCanvas, 表示是否将leaflet.js路径呈现在地图上。
1library(leaflet)
2
3leaflet(options = leafletOptions(minZoom = 0, maxZoom = 18))
关键函数:
setView() ,设定地图的view(包括center位置和zoom level)
flyTo() ,切换到一个指定的location或zoom-level,使用光滑的pan-zoom
fitBounds() ,设定地图矩形区域边界。view将限制在[lng1, lat1] - [lng2, lat2]
flyToBounds() ,切换到一个指定的地图矩形区域边界,使用光滑的pan/zoom
setMaxBounds() ,限定地图矩形区域最大边界
clearBounds() ,清除地图矩形区域边界, 然后view将只会受地图层的经度和纬度数据限制。
语法:
setView(map, lng, lat, zoom, options = list())
flyTo(map, lng, lat, zoom, options = list())
fitBounds(map, lng1, lat1, lng2, lat2, options = list())
flyToBounds(map, lng1, lat1, lng2, lat2, options = list())
setMaxBounds(map, lng1, lat1, lng2, lat2)
clearBounds(map)
参数解释:
map,表示leaflet()创建的map widget
lng, 表示map center的经度,东经为正
lat, 表示map center的纬度,北纬为正
zoom, 表示zoom level
options, 列表传参,传递zoom或pan参数。
lngl, latl, lng2, lat2, 表示widget边界的坐标。
 1library(leaflet)
 2
 3# 设定中心坐标和zoom level
 4m <- leaflet() %>% addTiles() %>% setView(-71.0382679, 42.3489054, zoom = 18)
 5
 6# 显示第一个view
 7m %>% fitBounds(-72, 40, -70, 43)  # 设定view边界
 8
 9# 显示第二个view
10m %>% clearBounds()  # 清除边界限制, leaflet()默认为世界地图
这里的Data不仅仅是画地图上行政区域的数据,而且包括要在地图上呈现的数据。
大多数图层添加函数都有data参数,通常使用%>%管道符逐渐传递data参数。leaflet()通常支持下列几种形式的数据。
矩阵数据(由经度和纬度构成)。
数据框(由经度和纬度构成)。
从sp包传递的数据,包括:
SpatialPoints(数据框类型)
Line()/Lines()
SpatialLines()(数据框类型)
Polygon()/Polygons()
SpatialPolygons()(数据框类型)
从maps包传递的数据,主要是map()函数传递的数据框。
对于经度和纬度组成矩阵或数据框类型数据,在调动data添加图层时,会根据变量名进行猜测匹配:
若变量名称为lat,或latitude等,则猜测为纬度,猜测时,不区分大小写。
若变量名称为lng, long或 longitude等,则猜测为经度,猜测时,不区分大小写。
也可以手动指定经度和纬度变量,使用~语法。
在参数传递过程中,默认后面的data参数覆盖前面data参数。
 1library(leaflet)
 2
 3# 自动猜测匹配
 4set.seed(123)
 5df <- data.frame(Lat = 1:10, Long = rnorm(10))
 6leaflet(df) %>% addCircles()
 7
 8# 手动指定经度和纬度变量,结果一样 leaflet(df) %>% addCircles(lng = ~Long,
 9# lat = ~Lat)
10
11# 在add_xxx()函数中重新指定参数进行覆盖,结果一样 leaflet() %>%
12# addCircles(data = df) leaflet() %>% addCircles(data = df, lat = ~Lat, lng
13# = ~Long)
sp对象的data 1library(leaflet)
 2library(sp)
 3library(RColorBrewer)
 4
 5Sr1 <- Polygon(cbind(c(2, 4, 4, 1, 2), c(2, 3, 5, 4, 2)))  # 4对非重复点坐标,首尾相连
 6Sr2 <- Polygon(cbind(c(5, 4, 2, 5), c(2, 3, 2, 2)))  # 3对非重复点坐标,画三角形
 7Sr3 <- Polygon(cbind(c(4, 4, 5, 10, 4), c(5, 3, 2, 5, 5)))  # 4对非重复点坐标,画四边形
 8Sr4 <- Polygon(cbind(c(5, 6, 6, 5, 5), c(4, 4, 3, 3, 4)), hole = TRUE)  # hole = TRUE,表示中空
 9
10
11Srs1 <- Polygons(list(Sr1), "s1")  # 's1'指定ID参数,多个多边形才有ID参数,Polygon()没有ID参数
12Srs2 <- Polygons(list(Sr2), "s2")
13Srs3 <- Polygons(list(Sr4, Sr3), "s3/4")  # 合并Sr3和Sr4多边形
14
15# 列表传参,传递多个多边形参数
16SpP <- SpatialPolygons(list(Srs1, Srs2, Srs3), 1:3)
17
18leaflet(height = "300px") %>% addPolygons(data = SpP, fillColor = brewer.pal(3, 
19    name = "Set1"))
maps包中获取data1library(leaflet)
2library(maps)
3library(RColorBrewer)
4
5mapStates <- map("state", fill = TRUE, plot = FALSE)
6leaflet(data = mapStates) %>% addTiles() %>% addPolygons(fillColor = brewer.pal(10, 
7    name = "Paired"), stroke = FALSE)
其它绘图参数支持R自带的数据类型,如:向量,颜色向量,数据框,同样支持用~指定。
 1library(leaflet)
 2
 3# 随便编一个数据
 4m <- leaflet() %>% addTiles()
 5df <- data.frame(lat = rnorm(100), lng = rnorm(100), size = runif(100, 5, 20), 
 6    color = sample(colors(), 100))
 7
 8m <- leaflet(df) %>% addTiles()
 9# circle图标的半径不随zoom变化:
10m %>% addCircleMarkers(radius = ~size, color = ~color, fill = FALSE)
11# circle图标的半径不随zoom变化:
12m %>% addCircleMarkers(radius = runif(100, 4, 10), color = c("red"))
leaflet支持Tilemap类型的底图,leaflet支持多种免费第三方providers的tiles, 包括Stamen, Esri, OpenWeatherMap等,
用names(providers)可以查看所有的providers。
使用addTiles()函数添加tiles并使用默认参数,默认即是OpenStreetMap,即街区Tiles。
1library(leaflet)
2
3m <- leaflet() %>% setView(lng = -71.0589, lat = 42.3601, zoom = 12)
4m %>% addTiles()  # 显示街区图
2.2
调用addProviderTiles()函数,在参数providers$后面加tiles供应商的名字就行了。 需要注意:部分第三方tiles需要注册。
通过options参数调用providerTileOptions()函数可以规避部分tiles的注册。
如果有定制的tiles模板的URL链接,可以在addTiles()函数中调用。
1library(leaflet)
2
3m <- leaflet() %>% setView(lng = -71.0589, lat = 42.3601, zoom = 12)
4
5# 使用Stamen.Toner 的 tiles
6m %>% addProviderTiles(providers$Stamen.Toner)
2.3
巧妙的数据转换
通过调用函数addWMSTiles()可以添加WMS(Web Map Service)的tiles。
Web地图服务(WMS)是一种标准协议,描述如何通过Internet提供任何地理配准的地图图像,
这通常由使用来自地理信息系统数据库的数据的地图服务器生成。
协议标准由Open Geospatial Consortium(OGC)开发,
并于1999年首次发布.WMS提供了一种使用HTTP接口请求地理注册地图图像的简单方法。
WMS供应商(https://en.wikipedia.org/wiki/Web_Map_Service)
下图是调用WMS的tiles,显示降雨量的图数据来源(http://mesonet.agron.iastate.edu/)。
1library(leaflet)
2
3leaflet() %>% addTiles() %>% setView(-93.65, 42.0285, zoom = 4) %>% # 叠加一个WMS的tiles图层
4addWMSTiles("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", layers = "nexrad-n0r-900913", 
5    options = WMSTileOptions(format = "image/png", transparent = TRUE), attribution = "Weather data <U+00A9> 2012 IEM Nexrad")
2.4
多个Tiles图层也可以叠加, 但是这种情况通常仅用于表层tiles是半透明的情况下,
或在options参数中手动指定不透明度opacity。opacity从0(完全透明)到1(完全不透明)。
1library(leaflet)
2
3m <- leaflet() %>% setView(lng = -71.0589, lat = 42.3601, zoom = 12)
4
5m %>% addProviderTiles(providers$MtbMap) %>% # 底层tiles
6  addProviderTiles(providers$Stamen.TonerLines,  # 叠加一层tiles,显示公路和街道
7    options = providerTileOptions(opacity = 0.35)) %>% # opacity设定非透明度
8  addProviderTiles(providers$Stamen.TonerLabels) # 叠加tiles,显示公路名,街道名,机场图标。
使用图标用于标记地图上特殊的点。图标的位置通过经纬度指定。
leaflet中,图标可以分为:icon,circle图标,clusters(簇),共3类。
Markers坐标获取途径:
SpatialPoints()/SpatialPointsDataFrame() (sf包)
POINT, sfc_POINT或sf对象(sf包), 只有x,y维度的数据才能被使用。
只有2列的矩阵(第1列为经度,第2列为纬度)
含经度列和纬度列的数据框,
同样默认猜测匹配,也可以手动指定,如: addMarkers(lng = ~Longitude, lat = ~Latitude)。
用1个数字向量指定lng或lat参数。
注意:不支持sf包中的 MULTIPOINT对象。
3.1
icon图标的添加分为几类:
addMarkers()函数,添加leaflet中内置一种icon。
makeIcon()指定addMarkers()函数内的icon参数,
添加URL链接(包括file path)中的一种icon。
icons()指定addMarkers()内的icon参数,同时添加几种icon。
iconList()与addMarkers()函数联用,同时添加很多种icon。
addAwesomeMarkers()函数,添加awesome icons。
addMarkers())默认Icon为雨滴状图标(dropped pin)。
与大多数图层绘制函数一样,popup参数可以用来指定一个message,当点击该Icon则显示该message;label参数可以用来指定一个文本标签,可以是浮动的或静止的。
1library(leaflet)
2data(quakes)
3
4# 索引quakes前20行数据进行显示。
5leaflet(data = quakes[1:20, ]) %>% addTiles() %>% 
6# 增加图标图层,标签显示为quakes中的mag变量
7addMarkers(~long, ~lat, popup = ~as.character(mag), label = ~as.character(mag))
makeIcon())自定义Icon图标,可以通过URL链接或文件路径来指定。
对于单独一个Icon,若要映射到多个数据点,则可以使用makeIcon()函数制作一组Icon图标,
然后再调用addMarkers()函数增加Marker图层。 
免费Icon下载(https://icons8.com/icons)
 1library(leaflet)
 2data(quakes)
 3
 4# 制作一组Icon
 5greenLeafIcon <- makeIcon(
 6  iconUrl = "E:/R_input_output/images_input/leaf-green.png", # 使用文件路径指定
 7  iconWidth = 38, iconHeight = 95, # 设定当个Icon的相对长宽。 
 8  iconAnchorX = 22, iconAnchorY = 94, # 定位点相对位置(在IconWidth和IconHeight内)
 9  shadowUrl = "E:/R_input_output/images_input/leaf-shadow.png", # 阴影Icon
10  shadowWidth = 50, shadowHeight = 64,
11  shadowAnchorX = 4, shadowAnchorY = 62
12)
13
14leaflet(data = quakes[1:4,]) %>% addTiles() %>%
15  addMarkers(~long, ~lat, icon = greenLeafIcon) # 增加Icon图层
icons()(多种icons)若多个Icons图标尺寸等参数一致,仅仅URLs不一样,使用icons()制作Icon图标组合非常方便,icons()语法与data.frame()类似,参数都可以用向量指定,短的参数将会循环补齐。
 1library(leaflet)
 2data(quakes)
 3
 4quakes1 <- quakes[1:10,] # 索引前10行数据
 5
 6leafIcons <- icons(
 7  iconUrl = ifelse(quakes1$mag < 4.6, # 若mag < 4.6为真
 8    "E:/R_input_output/images_input/leaf-green.png", # test = TRUE
 9    "E:/R_input_output/images_input/leaf-red.png" # test = FALSE
10  ),
11  iconWidth = 38, iconHeight = 95, # length = 1, 循环补齐
12  iconAnchorX = 22, iconAnchorY = 94, # length = 1, 循环补齐
13  shadowUrl = "E:/R_input_output/images_input/leaf-shadow.png", # length = 1, 循环补齐
14  shadowWidth = 50, shadowHeight = 64, # length = 1, 循环补齐
15  shadowAnchorX = 4, shadowAnchorY = 62 # length = 1, 循环补齐
16)
17
18leaflet(data = quakes1) %>% addTiles() %>%
19  addMarkers(~long, ~lat, icon = leafIcons) # 传递制作的Icons参数
20
iconList())若一些Icons的参数相差很大,此时使用iconList()函数更加方便,
相当于创建一个列表将makeIcon()创建icons包裹起来。然后整体传递给addMarkers()。
 1library(leaflet)
 2
 3# iconList()制作icons组成的列表
 4oceanIcons <- iconList(
 5  ship = makeIcon(iconUrl = "E:/R_input_output/images_input/cargo-ship.png", # 使用文件路径指定
 6    iconWidth = 30, iconHeight = 20), # 设定当个Icon的相对长宽。 
 7  pirate = makeIcon(iconUrl = "E:/R_input_output/images_input/pirates-caribbean.png", # 使用文件路径指定
 8    iconWidth = 30, iconHeight = 30) # 设定当个Icon的相对长宽。 
 9)
10
11# 编造一个数据
12df <- sp::SpatialPointsDataFrame(
13  cbind( # 列合并后为一个4列20行的数据框
14    (runif(20) - .5) * 10 - 90.620130,  # 经度
15    (runif(20) - .5) * 3.8 + 25.638077  # 纬度
16  ),
17  data.frame(type = factor(
18    ifelse(runif(20) > 0.75, "pirate", "ship"), # test > 7.5为真,则type = "pirate"
19    c("ship", "pirate") # length = 2 < 20, 循环补齐
20  ))
21)
22
23leaflet(df) %>% addTiles() %>%
24  addMarkers(icon = ~oceanIcons[type]) # 使用type中的元素匹配oceanIcons中的列名。
addAwesomeMarkers())leaflet中可以使用定制Icons的颜色,
使用addAwesomeMarkers()添加awesome icons, 用法与addMarkers()类似。
awesome icons库:fontawesome,glyphicon,ionicons。
在addAwesomeMarkers()中可以用library参数指定awesome icons库。
默认为library = "glyphico", library = "fa"表示fontawesome库, library = "ion"表示ioncons库,
与icons()和iconList()函数可以用于addMarkers()类似,awesomeIcons()和 awesomeIconList()函数可以用于 addAwesomeMarkers()以添加多个icons。
 1library(leaflet)
 2data(quakes)
 3
 4df.20 <- quakes[1:20,] # 索引前20行数据
 5
 6# 创建颜色向量函数,mag <= 4 为绿色,mag = 5为橙色, mag > 5为红色
 7getColor <- function(quakes) {
 8  sapply(quakes$mag, function(mag) {
 9  if(mag <= 4) {
10    "green"
11  } else if(mag <= 5) {
12    "orange"
13  } else {
14    "red"
15  } })
16}
17
18icons <- awesomeIcons(
19  icon = 'flash', # icon名字为"flash"(闪电),名字可以从前面库的网站上查看
20  iconColor = 'black', # 指定icon颜色
21  library = 'ion', # 指定icons库为ionicons
22  markerColor = getColor(df.20) # 指定markers颜色
23)
24
25leaflet(df.20) %>% addTiles() %>%
26  addAwesomeMarkers(~long, ~lat, icon=icons, label=~as.character(mag))
3.2
当地图上存在大量的图标,可以通过设置clusterOptions参数以簇的形式插入。
好处是,缩小地图后,图标聚集成簇,放大地图后,簇散开为多个图标。 簇上可以显示该簇包含图标的数目。
clusterOptions参数)使用markerOptions()设定单个簇的参数。
对于多个簇,同样可以用列表传递多个簇参数,使用markerClusterOptions()函数。
1library(leaflet)
2
3# cache = TRUE使用上个代码块的数据
4leaflet(quakes) %>% addTiles() %>% addAwesomeMarkers(~long, ~lat, icon = icons, 
5    label = ~as.character(mag), clusterOptions = markerClusterOptions()  # 设置簇
6)
freezeAtZoom参数)使用markerClusterOptions()中的freezeAtZoom参数可以设定簇冻结在一个zoom level。
这样缩放地图时,簇是否散开取决于冻结时的zoom level高低。
例如下图中,设置freezeAtZoom = 5
1library(leaflet)
2
3leaflet(quakes) %>% addTiles() %>% addAwesomeMarkers(~long, ~lat, icon = icons, 
4    label = ~as.character(mag), clusterOptions = markerClusterOptions(freezeAtZoom = 5)  # 冻结簇在zoom level = 5
5)
3.3
circle图标与circle形状不一样:
circle Markers 属于图标,其尺寸是像素尺寸,不随zoom level变化而变化。
而circles 属于形状,与多边形是一类,其尺寸是以“米”为单位,随zoom level 变化而变化。
使用addCircleMarkers()函数添加圆圈图标。 语法:
1addCircleMarkers(map, lng = NULL, lat = NULL, radius = 10,
2  layerId = NULL, group = NULL, stroke = TRUE, color = "#03F",
3  weight = 5, opacity = 0.5, fill = TRUE, fillColor = color,
4  fillOpacity = 0.2, dashArray = NULL, popup = NULL,
5  popupOptions = NULL, label = NULL, labelOptions = NULL,
6  options = pathOptions(), clusterOptions = NULL, clusterId = NULL,
7  data = getMapData(map))
关键参数:
radius, 表示指定circles的半径,为数字向量或~引导的表达式。
stroke, 表示是否显示路径阴影, 默认stroke = TRUE显示路径阴影,这样圆环就出现了阴影边框。
color, 表示指定stroke的颜色。
weight, 表示指定stroke像素宽度。
opacity, 表示指定stroke透明度,opacity从0(完全透明)到1(完全不透明)。
fill, 表示是否填充路径颜色(如填充多边形和园环)
fillColor, 表示指定fill颜色。
fillOpacity, 表示指定fill头透明度,opacity从0(完全透明)到1(完全不透明)。
dashArray, 表示指定stroke线型,为数字类型字符串。原理与ggplot2中自定义线型类似。
如dasharray = "4 1 2"表示:先闭合4个像素长度,然后缺口1,再闭合2,再缺口4,闭合1,缺口2。
 1library(leaflet)
 2
 3# 默认circles参数
 4leaflet(df) %>% addTiles() %>% addCircleMarkers()
 5
 6# stroke = FALSE去掉路径阴影
 7leaflet(df) %>% addTiles() %>% addCircleMarkers(radius = 15, stroke = FALSE) %>% 
 8
 9# fill = FALSE空心圆环
10addCircleMarkers(radius = 15, color = "magenta", weight = 8, opacity = 0.2, 
11    fill = FALSE) %>% 
12# 分别指定stroke和fill不同的颜色。并调整透明度
13addCircleMarkers(radius = 60, color = "lime", weight = 8, opacity = 0.8, fillColor = "cyan", 
14    fillOpacity = 0.3, dashArray = "5 2")
不同半径的circles: addCircleMarkers()中的参数同样可以用向量指定,如下图:
 1library(leaflet)
 2
 3# 定义一个色板函数
 4pal <- colorFactor(c("blue", "magenta"), domain = c("ship", "pirate")) 
 5
 6leaflet(df) %>% addTiles() %>%
 7  addCircleMarkers(
 8    radius = ~ifelse(type == "ship", 6, 10), # 分别设置半径,ship半径为6,pirate半径为10
 9    color = ~pal(type), # 色板函数匹配
10    stroke = FALSE, fillOpacity = 0.5
11  )
给标记点增加信息提示框有4种方式:
使用addPopups()在地图中添加单个popup。
在图标或形状添加函数中,指定popup参数。
在图标或形状添加函数中,指定labelOptions参数。
使用addLabelOnlyMarkers()函数创建无图标labels。
popups是包含HTML链接的提示框,用于显示标记点的文本信息。
popup最常见的用法是,当点击图标时才出现在地图上。
 1library(leaflet)
 2
 3# 生成popup框内信息
 4content <- paste(sep = "<br/>", # 分隔符为html语法的换行符
 5  # 单引号内为HTML链接,单引号后为HTML显示标签                 
 6  "<b><a href='http://www.samurainoodle.com'>Samurai Noodle</a></b>", 
 7  "606 5th Ave. S",
 8  "Seattle, WA 98138"
 9)
10
11leaflet() %>% addTiles() %>%
12  addPopups(-122.327298, 47.597131, popup = content, # 将HTML语法支持的文本内容传递给popup
13    options = popupOptions(closeButton = FALSE) # popupOptions()中设定popups的尺寸和样式
14  )
4.2
图标和形状的添加函数中都有一个popup参数。
当存在大量不含HTML链接的popups时,可以使用htmltools::htmlEscape()函数,
以避免将字符串作为html语言进行转义,直接输出字符串到popup框。
 1library(leaflet)
 2library(htmltools)  # 为了调用htmlEscape()函数,
 3
 4# 创建一个数据框
 5df <- read.csv(textConnection("Name,Lat,Long
 6Samurai Noodle,47.597131,-122.327298
 7Kukai Ramen,47.6154,-122.327157
 8Tsukushinbo,47.59987,-122.326726"))
 9df
10
11leaflet(df) %>% addTiles() %>% addMarkers(~Long, ~Lat, popup = ~htmlEscape(Name))  # 默认点击Marker才出现popup
4.3
labels内容包括文本和HTML链接内容。 用labelOptions()函数指定labelOptions参数。
关键参数:
noHide, 为逻辑值,FALSE表示仅当鼠标指针悬浮到marker上,才显示label;TRUE,则一直显示label。
textOnly,为逻辑值, TRUE表示只显示文本,不显示提示框。
style,表示设定信息框的样式,列表传参。
可以设定label的颜色,字体,字型,字体大小,边框线颜色,边框阴影等。
 1library(leaflet)
 2
 3leaflet() %>% addTiles() %>% setView(-118.456554, 34.09, 13) %>%
 4
 5  addMarkers(
 6    lng = -118.456554, lat = 34.105,
 7    label = "Default Label",
 8    labelOptions = labelOptions(noHide = T)) %>% # 不隐藏的popups
 9
10  addMarkers(
11    lng = -118.456554, lat = 34.095,
12    label = "Label w/o surrounding box",
13    labelOptions = labelOptions(noHide = T, textOnly = TRUE)) %>% # 只显示文字,不显示提示框
14
15  addMarkers(
16    lng = -118.456554, lat = 34.085,
17    label = "label w/ textsize 15px",
18    labelOptions = labelOptions(noHide = T, textsize = "15px")) %>% # 修改提示框大小,文本大小随之变化
19
20  addMarkers(
21    lng = -118.456554, lat = 34.075,
22    label = "Label w/ custom CSS style",
23    labelOptions = labelOptions(noHide = T, direction = "bottom", # 修改提示框相对位置
24      style = list( # 修改提示框文本样式,列表传参
25        "color" = "red", # 设定文本颜色为红色
26        "font-family" = "serif", # 设置字体
27        "font-style" = "italic", # 设置字型为斜体
28        "box-shadow" = "3px 3px yellow", # 设置提示框边框线阴影尺寸和边框阴影颜色
29        "font-size" = "12px", # 设置字体大小为12个像素点
30        "border-color" = "cyan" # 设置提示框边框线颜色
31      ))
32    )
在leaflet中,添加形状和线条非常容易。
多边形和折线
线条和多边形数据源可以从以下途径获取:
SpatialPolygons(), SpatialPolygonsDataFrame(),Polygons()和Polygon()对象(来自sp包)
SpatialLines(), SpatialLinesDataFrame(), Lines(), 和Line()对象(来自sp包)
MULTIPOLYGON(), POLYGON(), MULTILINESTRING()和LINESTRING()对象(来自sp包)
map()对象(来自maps包)。
使用map(fill = TRUE)得到多边形数据,使用map(fill = FALSE)得到折线段数据
2列的矩阵,第1列为经度,第2列为纬度。多边形之间用(NA, NA)行分开。
但是对于多边形内部存在空洞,这种方法失效,须使用SpatialPolygons()。
5.1
使用addCircles()添加circle形状。
绘制circle形状时,仅仅需要圆心坐标和半径。使用radius参数指定半径。
 1library(leaflet)
 2
 3cities <- read.csv(textConnection("
 4City,Lat,Long,Pop
 5Boston,42.3601,-71.0589,645966
 6Hartford,41.7627,-72.6743,125017
 7New York City,40.7127,-74.0059,8406000
 8Philadelphia,39.9500,-75.1667,1553000
 9Pittsburgh,40.4397,-79.9764,305841
10Providence,41.8236,-71.4222,177994
11"))
12
13leaflet(cities) %>% addTiles() %>% addCircles(lng = ~Long, lat = ~Lat, weight = 1, 
14    radius = ~sqrt(Pop) * 30, popup = ~City, fillColor = "magenta", fillOpacity = 0.2)
5.2
一个矩形仅仅需要2组经纬度数据(对应矩形2个对角点),
分别为lng1, lng2, lat1, lat2共4个数字向量
矩形坐标数据不能像其它多边形一样从sp,maps包或2列矩阵,或2列数据框中得到。
因为其它多边形数据都是1组点的坐标数据(分组连线成多边形),而矩形数据是2组点的坐标数据。
使用addRectangles()函数添加矩形图层。
1library(leaflet)
2
3leaflet() %>% addTiles() %>% addRectangles(lng1 = -118.456554, lat1 = 34.078039, 
4    lng2 = -118.436383, lat2 = 34.062717, fillColor = "transparent"  # fillColor = 'transparent'与fillColor = NULL结果不一样
5)
5.3
语法:
1addPolygons(map, lng = NULL, lat = NULL, layerId = NULL,
2  group = NULL, stroke = TRUE, color = "#03F", weight = 5,
3  opacity = 0.5, fill = TRUE, fillColor = color, fillOpacity = 0.2,
4  dashArray = NULL, smoothFactor = 1, noClip = FALSE, popup = NULL,
5  popupOptions = NULL, label = NULL, labelOptions = NULL,
6  options = pathOptions(), highlightOptions = NULL,
7  data = getMapData(map))
参数解释:
stroke, 表示是否显示多边形的路径阴影。
color, 表示指定stroke的颜色。
weight, 表示指定stroke像素宽度。
smoothFactor, 表示指定多边形的polyLines(边线)简化因子。越大,则边缘约平滑。
opacity, 表示指定stroke透明度,opacity从0(完全透明)到1(完全不透明)
fillOpacity, 表示指定多边形fill透明度,opacity从0(完全透明)到1(完全不透明)
fillColor, 表示指定多边形fill填充颜色,
highlightOptions, 表示高亮鼠标所在区域的多边形。
指定高亮参数用highlightOptions()函数。
highlightOptions()高亮多边形高亮多边形,就是改变鼠标所在区域多边形的颜色和透明度等参数。
语法:
1highlightOptions(stroke = NULL, color = NULL, weight = NULL,
2  opacity = NULL, fill = NULL, fillColor = NULL,
3  fillOpacity = NULL, dashArray = NULL, bringToFront = NULL,
4  sendToBack = NULL)
参数解释:
dashArray, 表示指定多边形stroke的线型,与Popup中用法一致。
bringToFront, 表示当鼠标悬浮在所在多边形区域时,
是否将该shape置于表层。这对于高亮shape是必须的。
sendToBack, 表示当鼠标移走时,是否将高亮shape置于底层。
下图为美国人口数据图,数据来源(https://www.census.gov/geo/maps-data/data/cbf/cbf_state.html)需要科学上网。
 1library(leaflet)
 2
 3path <- "E:/R_input_output/data_input/cb_2013_us_state_20m/cb_2013_us_state_20m.shp"
 4states <- rgdal::readOGR(path,layer = "cb_2013_us_state_20m", 
 5    GDAL1_integer64_policy = TRUE)# 读取数据
 6
 7neStates <- subset(states, states$STUSPS %in% c( 
 8  "CT","ME","MA","NH","RI","VT","NY","NJ","PA"
 9))
10
11leaflet(neStates) %>%
12  addPolygons(color = "cyan", weight = 1, smoothFactor = 0.5,
13    opacity = 1.0, fillOpacity = 0.5,
14    fillColor = ~colorQuantile("YlOrRd", ALAND)(ALAND),
15    highlightOptions = highlightOptions(color = "white", weight = 2, # 高亮形状区域,置于表层。
16      bringToFront = TRUE))
1## OGR data source with driver: ESRI Shapefile 
2## Source: "E:\R_input_output\data_input\cb_2013_us_state_20m\cb_2013_us_state_20m.shp", layer: "cb_2013_us_state_20m"
3## with 52 features
4## It has 9 fields
5## Integer64 fields read as doubles:  ALAND AWATER
对于形状复杂的地图多边形和折线,直接使用leaflet绘图容易出现一些问题,
并且运算量大, 因为数据点太多了。
很多时候,对数据进行简化是必要的,
简化后的图形与没有简化的在普通分辨率下根本分辨不出来差别。
使用rmapshaper::ms_simplify()简化很方便,
其基于拓扑学原理进行简化,数据存储量小,形状失真小。
albersusa包安装方式:devtools::install_github("hrbrmstr/albersusa")
1library(albersusa)
2
3fullsize <- usa_sf()  # albersusa包自带数据
4object.size(fullsize)  # object.size属于utils包,R自带
5
6simplified <- rmapshaper::ms_simplify(fullsize)
7object.size(simplified)  # 可以看出,这种简化方式存储量只有原来1/8
1## 933016 bytes
2## 123920 bytes
对于GeoJSON和TopoJSON类型数据,有2种处理方式:
要么读取为sp对象;要么使用addGeoJSON()和addTopoJSON()函数。
6.1
GeoJSON格式与TopoJSON格式是不一样的。
**GeoJSON格式文件后缀是”.geojson“。
TopoJSONg格式文件后缀是”.json“**。
地图数据集都可以在线转换,推荐使用GiS Map Converter
支持多种格式免费转换,邮箱注册非常方便。
也可以使用MyGeodata Converter(https://mygeodata.cloud/converter/)
使用addGeoJSON()或addTopoJSON()函数可以直接读取JSON文件,
但是很多参数不能修改,还是转换成sp对象最好。
addGeoJSON()下面用到的地图数据来自于中国省级地图下载(https://pan.baidu.com/share/init?surl=f5344XfW7X1wqXuTsNONSw)0,密码:vhk6
直接给geojson参数指定文件路径。
注意:使用本地JSON文件或leafletCN绘图,在markdown中码代码,不能直接knitr到html.
Script中码代码可以输出为本地html文件。但是只能在一个html中输出一张图。
所以笔者接下来的图形均使用截图。
1library(leaflet)
2
3filepath <- "E:/R_input_output/data_input/China.geojson"
4
5leaflet() %>% 
6  setView(lng = 106.33, lat = 29.35, zoom = 3.5) %>% # 以重庆城区经纬度为中心
7  addTiles() %>% 
8    addGeoJSON(geojson = filepath, 
9               weight = 1, color = "magenta", fill = FALSE)  # 修改color失败
addTopoJSON()直接给topojson参数指定文件路径。
1library(leaflet)
2
3filepath <- "E:/R_input_output/data_input/JSON/TopoJson/China.json"
4
5leaflet() %>% 
6  setView(lng = 106.33, lat = 29.35, zoom = 3.5) %>% # 以重庆城区经纬度为中心
7  addTiles() %>% 
8    addTopoJSON(topojson = filepath, weight = 1, 
9                color = "magenta", fill = FALSE)  # 修改color失败
6.2
使用geojsonio或rgdal包,读取GeoJSON/TopoJSON对象为sp对象。
然后就能使用sp对象作图。
此方法设置或修改地图区域的特征属性方面更加方便,如colors, labels等。
对于GeoJSON文件,可以用记事本打开看一下数据结构,然后再读取。
也可以用JSON在线解析器(http://geojson.io/#map=2/20.0/0.0)查看,更加方便。
读取GeoJSON还是TopoJson格式,函数的使用方法是一样的。
 1library(leaflet)
 2library(rgdal)
 3
 4filepath <- "E:/R_input_output/data_input/JSON/TopoJson/China.json"
 5# filepath <- "E:/R_input_output/data_input/JSON/GeoJSON/China.geojson" # 结果一样
 6China_map <- readOGR(filepath, stringsAsFactors = FALSE)
 7# China_map <-  geojsonio::geojson_read(filepath,what = "sp") # 结果一样
 8
 9Encoding(China_map@data$name) <- "UTF-8" # 纠正中文字符乱码
10leaflet(China_map) %>% 
11  setView(lng = 106.33, lat = 29.35, zoom = 3.5) %>% # 以重庆城区经纬度为中心
12  addPolygons(stroke = TRUE, color = "magenta", smoothFactor = 0.3, 
13              fillOpacity = 0.5,fillColor = "lime") %>%
14  addMarkers(lng = 114.3,lat = 30.6 ) # 增加一个图标
(图片太大,没截全)
6.3
对于GeoJSON/TopoJSO格式数据中的特征属性来说,
有2种方法进行修改(目前仅仅修改paths和shapes, 不支持markers):
其一是在addGeoJSON()和addTopoJSON()函数中设置参数。
另一种方法是对JSON对象(字符串),重新编码styling信息,
通过直接在JSON数据的top level的JSON 对象下面直接插入1个style对象。
下一个代码块是关于设置全局styles和在JSON数据中插入style对象的例子。
在R中不推荐使用这种方法设置styles, 通常还是首先将数据转换成sp对象,再进行绘图更加方便。
但是,如果你掌控了GeoJSON/TopoJSON数据的生成过程,直接插入style对象是可取的。
 1library(leaflet)
 2library(jsonlite)
 3
 4filepath <- "E:/R_input_output/data_input/JSON/TopoJson/China.json"
 5# filepath <- "E:/R_input_output/data_input/JSON/GeoJSON/China.geojson" # 结果一样
 6
 7# 首先解析JSON格式为stringified格式,再转化为多级列表
 8China_map_list <- readLines(filepath, warn = FALSE) %>%
 9  paste(collapse = "\n") %>%
10  fromJSON(simplifyVector = FALSE) # 将JSON格式转化为多级列表
11
12# 插入style对象,增加子列表
13China_map_list$style <- list(
14  weight = 1,
15  stroke = "TRUE",
16  color = "magenta",
17  fillColor = "lime",
18  opacity = 0.5,
19  fillOpacity = 0.8
20)
21
22
23# 使用修改了style的JSON数据绘图
24leaflet() %>% setView(lng = 106.33, lat = 29.35, zoom = 3.5) %>% # 以重庆城区经纬度为中心
25  addTopoJSON(China_map_list) %>%
26  # addGeoJSON(China_map_list) %>% # 若读取China.geojson文件,
27  addMarkers(lng = 114.3,lat = 30.6 ) # 增加一个图标
(图片太大,没截全)
往期精彩:
公众号后台回复关键字即可学习
回复 爬虫 爬虫三大案例实战
回复 Python 1小时破冰入门
回复 数据挖掘 R语言入门及数据挖掘
回复 人工智能 三个月入门人工智能
回复 数据分析师 数据分析师成长之路
回复 机器学习 机器学习的商业应用
回复 数据科学 数据科学实战
回复 常用算法 常用数据挖掘算法
这一路
一直为你加油的那个人
是你自己↓