前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Leaflet 与高德继续碰撞火花!

Leaflet 与高德继续碰撞火花!

作者头像
庄闪闪
发布2021-10-08 10:23:02
2.7K0
发布2021-10-08 10:23:02
举报

本文来自读者厦门大学的李康国研究生投稿,讲述高德和 Leaflet 结合绘制地图。也欢迎其他小伙伴来分享你们的经验!

前言

我们对 leaflet 包做了一期简单的入门教程:空间地理数据可视化之 leaflet 包及其拓展。之后,又将高德和该包相结合,介绍了前期需要准备的工作,见:Leaflet 与高德合并会擦出怎么样的火花?。这一期就到了绘制地图环节,下面将分享三类数据的绘制教程。

本文框架

这篇推文讲的是第三节内容

3. 绘制地图

3.1 散点地图绘制

  1. 将高德地图替换 leaflet 自带的底图

由于 leaflet 自带的底图不是很合规,所以我们使用高德地图进行替换。

代码语言:javascript
复制
library(leaflet)
geo_map <- leaflet(width = '100%') %>% 
  addTiles(
    urlTemplate = 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layerId = tileOptions(minZoom = 3, maxZoom = 17),
    attribution = NULL
  )
  1. 在地图上增加标记

使用addMarkers()geo_map上增加散点图。

代码语言:javascript
复制
geo_map %>% 
  addMarkers(
    data = datafile,  ## 存放有坐标的文件
    lat = ~lat,       ## 变量纬度(latitude)所在列
    lng = ~lng,       ## 变量经度(longitude)所在列
    label = ~address  ## 变量标签所在列(这里设置了详细的地址作为标签)
    )
  1. 批量下载 api 地址

高德地图地理编码的 api 每天可以查询 30 万次,所以可以将上述查询过程写成一个 function 来进行批量查询。代码如下,代码中的注释已做详细解释。

代码语言:javascript
复制
## 加载packages
library(dplyr)
library(tidyr)
library(leaflet)

## 设置文件
datafile <- data.frame(
  id = 1:9,
  address = c(
    "北京市东城区天安门广场人民英雄纪念碑",
    "上海市浦东新区东方明珠广播电视塔",
    "台湾省台北市南京东路台北体育馆",
    "海南省海口市海口站(出站口)",
    "香港特别行政区湾仔区岛湾仔博览道1号会议展览中心香港回归祖国纪念碑",
    "澳门特别行政区花王堂区澳门博物馆大炮台(北入口)",
    "西藏拉萨市城关区布达拉宫广场西藏和平解放纪念碑",
    "新疆克拉玛依市平南八路克拉玛依北站",
    "黑龙江省哈尔滨市道里区斯大林街20号哈尔滨市人民防洪胜利纪念塔"
  )
)

## 自定义function
gaodemap <- function(x){
  address <- stringr::str_replace_all(datafile$address[x], "[^[:alnum:]]", "") ## 去除特殊字符,部分特殊字符会导致查询失败,所以在最开始直接去除
  url <- paste0(
    'https://restapi.amap.com/v3/geocode/geo?',
    '&key=', key, ## key
    '&address=', address, ## 详细地址
    '&output=', 'JSON'
  )
  temp_geo <- fromJSON(paste(readLines(url,warn = F, encoding = 'UTF-8'), collapse = ""))
  status <- temp_geo$status ## 判断查询是否成功
  if (status == 1 & length(temp_geo$geocodes) != 0){ ## 有时候成功了但是没有收到数据,所以用了一个“且”进行判定
    temp <- paste(datafile$address[x], temp_geo$geocodes$location, sep = ",")
  } else {
    temp <- paste(datafile$address[x], 'NULL,NULL', sep = ",")
  }
  return(temp)
}

## 地理编码

df <- data.frame(
  geo = sapply(datafile$id, gaodemap, simplify = T)
) %>% 
  separate(col = geo, into = c('address', 'lng', 'lat'), sep = ',') ## 分割function的结果

df$lng <- as.numeric(df$lng)
df$lat <- as.numeric(df$lat)

df
##                                                             address       lng
## 1                              北京市东城区天安门广场人民英雄纪念碑 116.39768
## 2                                  上海市浦东新区东方明珠广播电视塔 121.50003
## 3                                    台湾省台北市南京东路台北体育馆 121.54067
## 4                                        海南省海口市海口站(出站口) 110.16203
## 5 香港特别行政区湾仔区岛湾仔博览道1号会议展览中心香港回归祖国纪念碑 114.17738
## 6                    澳门特别行政区花王堂区澳门博物馆大炮台(北入口) 113.54620
## 7                    西藏拉萨市城关区布达拉宫广场西藏和平解放纪念碑  91.11885
## 8                                新疆克拉玛依市平南八路克拉玛依北站  85.08215
## 9      黑龙江省哈尔滨市道里区斯大林街20号哈尔滨市人民防洪胜利纪念塔 126.61724
##        lat
## 1 39.90462
## 2 31.23925
## 3 25.05213
## 4 20.02724
## 5 22.28094
## 6 22.19486
## 7 29.65127
## 8 45.56794
## 9 45.78065
  1. 绘图

这里我们将上面获得的df数据进行绘制,可以得到以下图形

代码语言:javascript
复制
geo_map <- leaflet(width = '100%') %>% 
  addTiles(
    urlTemplate = 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layerId = tileOptions(minZoom = 3, maxZoom = 17),
    attribution = NULL
  )

geo_map %>% 
  addMarkers(
    data = df,
    lat = ~lat, 
    lng = ~lng, 
    label = ~address
    )

补充:如果数据量较大,可以考虑使用 parallel 和 foreach 等来实现并行访问和解析 api,因为 R 语言默认是单核运行的,所以会出现**“一核有难,多核围观”**的情形,使用并行运算可以使电脑发挥出多核的优势,提升数据处理速度。

您可能会发现高德限制每秒 api 访问量是 200 次,多核并行会超限,就我的经验而言每次访问和解析大概需要 0.1 秒,16 线程并行查询 api,一秒钟也就160次,更何况还存在网络波动,所以基本上不用担心超限问题。

3.2 路径地图绘制

  1. 按照画线图的经验,平面直角坐标系中的一条线的位置由两个点决定,而两个点位置由它们分别的坐标 (X, Y) 决定,同理路径地图上的线由起点和终点决定,起点和终点由它们对应的经纬度决定,这样子我们就可以知道绘制路径地图的数据至少需要 4 个值,分别表示起点的经纬度和终点的经纬度。
  2. 知道了绘图需要的基本数据,后面的就简单了,只要分别查询两个点的经纬度把他们合并到一个表就好了,这里就不赘述了。有数据的朋友也可以直接把数据整理下就行,下面的例子使用上面绘制散点地图的数据。
  3. 先把数据整理下,假如说希望画从北京到另外 8 个点的直线,我只要在上表中新增一个点作为线段起点就好
代码语言:javascript
复制
df_line <- df %>%  
  mutate(              
    lat_start = lat[1], ## 新增起点纬度
    lng_start = lng[1]  ## 新增起点经度
  )

df_line
##                                                             address       lng
## 1                       北京市东城区天安门广场人民英雄纪念碑 116.39768
## 2                                  上海市浦东新区东方明珠广播电视塔 121.50003
## 3                                    台湾省台北市南京东路台北体育馆 121.54067
## 4                                        海南省海口市海口站(出站口) 110.16203
## 5 香港特别行政区湾仔区岛湾仔博览道1号会议展览中心香港回归祖国纪念碑 114.17738
## 6                    澳门特别行政区花王堂区澳门博物馆大炮台(北入口) 113.54620
## 7                    西藏拉萨市城关区布达拉宫广场西藏和平解放纪念碑  91.11885
## 8                                新疆克拉玛依市平南八路克拉玛依北站  85.08215
## 9      黑龙江省哈尔滨市道里区斯大林街20号哈尔滨市人民防洪胜利纪念塔 126.61724
##        lat lat_start lng_start
## 1 39.90462  39.90462  116.3977
## 2 31.23925  39.90462  116.3977
## 3 25.05213  39.90462  116.3977
## 4 20.02724  39.90462  116.3977
## 5 22.28094  39.90462  116.3977
## 6 22.19486  39.90462  116.3977
## 7 29.65127  39.90462  116.3977
## 8 45.56794  39.90462  116.3977
## 9 45.78065  39.90462  116.3977
  1. 分组合并下数据
代码语言:javascript
复制
df_line <- data.frame(
  group = rownames(df_line),
  lat = c(df_line$lat, df_line$lat_start),
  lng = c(df_line$lng, df_line$lng_start)
) %>% 
  group_by(group) %>% 
  arrange(.by_group = TRUE)

df_line
## # A tibble: 18 x 3
## # Groups:   group [9]
##    group   lat   lng
##    <chr> <dbl> <dbl>
##  1 1      39.9 116. 
##  2 1      39.9 116. 
##  3 2      31.2 122. 
##  4 2      39.9 116. 
##  5 3      25.1 122. 
##  6 3      39.9 116. 
##  7 4      20.0 110. 
##  8 4      39.9 116. 
##  9 5      22.3 114. 
## 10 5      39.9 116. 
## 11 6      22.2 114. 
## 12 6      39.9 116. 
## 13 7      29.7  91.1
## 14 7      39.9 116. 
## 15 8      45.6  85.1
## 16 8      39.9 116. 
## 17 9      45.8 127. 
## 18 9      39.9 116.
  1. 绘图
代码语言:javascript
复制
geo_map <- leaflet(width = '100%') %>% 
  addTiles(
    urlTemplate = 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layerId = tileOptions(minZoom = 3, maxZoom = 17),
    attribution = NULL
  )

geo_map %>% 
  addPolylines(
    data = df_line,  ## 存放有坐标文件 
    lng = ~lng,  ## 变量经度(longitude)所在列
    lat = ~lat,  ## 变量纬度(latitude)所在列
    group = ~group,  ## 线条分组
    weight = 1,  ## 线条粗细
    color = "red" ##线条颜色
  )

3.3 导航路径图

这个是看高德地图的时候无意中发现的,高德还提供 api 来查询导航路径,详细可见:官方说明文档[1]

  1. 通过地理编码获得起点和终点的经纬度,方法同上不再赘述了。
代码语言:javascript
复制
address1 <- '福建省厦门市厦门高崎国际机场' ## 起点详细地址
address2 <- '福建省厦门市厦门大学思明校区' ## 终点详细地址

url1 <- paste0(  ## 查询起点的url
  "https://restapi.amap.com/v3/geocode/geo?address=", address1,
  "&output=JSON&key=", key
) 
url2 <- paste0(  ## 查询终点的url
  "https://restapi.amap.com/v3/geocode/geo?address=", address2,
  "&output=JSON&key=", key
)
address1 <- fromJSON(paste(readLines(url1,warn = F, encoding = 'UTF-8'), collapse = "")) ## 起点坐标

address2 <- fromJSON(paste(readLines(url2,warn = F, encoding = 'UTF-8'), collapse = "")) ## 终点坐标

print(address1$geocodes) 
##                formatted_address country province citycode   city district
## 1 福建省厦门市湖里区高崎国际机场    中国   福建省     0592 厦门市   湖里区
##   township neighborhood.name neighborhood.type building.name building.type
## 1     NULL              NULL              NULL          NULL          NULL
##   adcode street number             location  level
## 1 350206   NULL   NULL 118.137053,24.539473 兴趣点
print(address2$geocodes)
##                    formatted_address country province citycode   city district
## 1 福建省厦门市思明区厦门大学思明校区    中国   福建省     0592 厦门市   思明区
##   township neighborhood.name neighborhood.type building.name building.type
## 1     NULL              NULL              NULL          NULL          NULL
##   adcode street number             location  level
## 1 350203   NULL   NULL 118.093470,24.437154 兴趣点
  1. 按照说明文档要求,生成url。
代码语言:javascript
复制
url <- paste0(
  "https://restapi.amap.com/v5/direction/bicycling?", 
  ## 使用骑行路线的api,如果是步行或者驾车,需要按照说明文档修改即可
  "&output=", "JSON",
  "&key=", key,
  '&origin=', address1$geocodes$location, ## 设置起点经纬度
  '&destination=', address2$geocodes$location,  ## 设置终点经纬度
  '&show_fields=', 'polyline' ## 设置显示坐标点
)

## 查询api
address <- fromJSON(paste(readLines(url,warn = F, encoding = 'UTF-8'), collapse = ""))
  1. 导航路径的经纬度信息分成不同段的储存在“route-paths-steps-polyline”中。
代码语言:javascript
复制
head(address$route$paths$steps[[1]]$polyline)
## [1] "118.13766;....                                                                   
  1. 整理数据
代码语言:javascript
复制
## 经纬度分割开
datafile <- lapply(1:length(address[["route"]][["paths"]][["steps"]][[1]][["polyline"]]), function(x){
    df <- strsplit(address[["route"]][["paths"]][["steps"]][[1]][["polyline"]][[x]], ";")
    return(df)
})

## 转化成表格
df <- data.frame(
  id = matrix(unlist(datafile))
) %>% 
  separate(id, c('lng', 'lat'), ",") %>% 
  mutate_if(is.character, as.numeric)
  1. 绘制地图
代码语言:javascript
复制
leaflet(df, width = '100%') %>% 
  addTiles(
    urlTemplate = 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layerId = tileOptions(minZoom = 3, maxZoom = 17),
    attribution = NULL
  ) %>% 
  addPolylines(  ## 绘制路线
    lat = ~lat, 
    lng = ~lng
    ) %>% 
  addMarkers(  ## 绘制起点所在点
    lat = ~lat[1], 
    lng = ~lng[1], 
    label = paste0('起点:', address1$geocodes$formatted_address)
    )%>% 
  addMarkers( ## 绘制终点所在点
    lat = ~lat[nrow(df)], 
    lng = ~lng[nrow(df)], 
    label = paste0('终点:', address2$geocodes$formatted_address)
    )

小编有话说

受限于微信平台问题,所有的 leaflet 画的图都是以截图方式呈现,但是实际上 leaflet 生成的是交互式的地图,也就是你可以像导航软件里一样放大和缩小地图,不用受限于分辨率问题;上述代码中设置的label都是鼠标悬停显示。

另外,很多教程都没有提一件事:

根据《中华人民共和国测绘法》等有关法律、法规规定:在中华人民共和国境内公开出版地图、引进地图、展示、登载地图以及在生产加工的产品上附加的地图图形都需要经审核,审核通过之后编发审图号。

本教程使用的是高德的底图,所以可以直接使用高德提供的审图号。如果是来历不明的地图数据,无法提供审图号可能会引来一些不必要的麻烦。

参考资料

[1]

官方说明文档: https://lbs.amap.com/api/webservice/guide/api/newroute

推荐: 可以保存以下照片,在 b 站扫该二维码,或者 b 站搜索【庄闪闪】观看 Rmarkdown 系列的视频教程。Rmarkdown 视频新增两节视频(写轮眼幻灯片制作)需要视频内的文档,可在公众号回复【rmarkdown

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 庄闪闪的R语言手册 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 本文框架
  • 3. 绘制地图
    • 3.1 散点地图绘制
      • 3.2 路径地图绘制
        • 3.3 导航路径图
        • 小编有话说
          • 参考资料
          相关产品与服务
          图数据库 KonisGraph
          图数据库 KonisGraph(TencentDB for KonisGraph)是一种云端图数据库服务,基于腾讯在海量图数据上的实践经验,提供一站式海量图数据存储、管理、实时查询、计算、可视化分析能力;KonisGraph 支持属性图模型和 TinkerPop Gremlin 查询语言,能够帮助用户快速完成对图数据的建模、查询和可视化分析。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档