R中的画地图的思路有三种,一种是利用包里GIS方面的数据,在R中直接画出来,第二种是从其他地方拿到数据,在R中通过某些包解析后再展现成,第三种就是直接把别人的图拿过来,自己再添加或去掉自己需要或不需要的东西。这三种方法只是数据来源不同,具体的画图以及美化方法无异。
第一种,最早应该是从maps包开始的,将地图数据封装在包里,这个包里应该用map函数为主,美中不足的是包里面的数据是在是太少了,连张中国地图都画不全,好在后来有了mapdata等一系列的包,CRAN上maps包后面那一串全是,情况有所好转,具体的内容看一下帮助文档就可以了。
可是问题又来了,R包里的数据总是不够用的,而且R包更新的速度远远赶不上开发商的脚步,这个时候就可以考虑第二种思路,自己下载实时的地图数据信息,可以在世界各国的地理信息系统里下载最官方的地图数据,然后通过包调用这些下载到本地的数据;或者更简单些,规规矩矩的做个伸手党,那么就是第三种思路,通过某些包直接引用百度或谷歌地图的地图数据,光明正大的白嫖。
地理信息数据毕竟是一门专业的领域,我们使用的时候不需知其所以然,但至少要知其然。下载的地图数据一般有三个文件:.dbf,.shp,.shx。这三个文件即可以映射出一幅地图。ESRI Shapefile(shp),或简称shapefile,是美国环境系统研究所公司(ESRI)开发的一种空间数据开放格式,是一种文件存储的方法,实际上该种文件格式是由多个文件组成的。这种文件方法用于描述几何体对象:点,折线与多边形。例如,Shapefile文件可以存储井、河流、湖泊等空间对象的几何位置。除了几何位置,shp文件也可以存储这些空间对象的属性,例如一条河流的名字,一个城市的温度等等。目前,许多自由的程序或商业的程序都可以读取Shapefile。要组成一个Shapefile,有三个文件是必不可少的,它们分别是".shp", ".shx"与 ".dbf"文件。表示同一数据的一组文件其文件名前缀应该相同。例如,存储一个关于湖的几何与属性数据,就必须有lake.shp,lake.shx与lake.dbf三个文件。而其中“真正”的Shapefile的后缀为shp,然而仅有这个文件数据是不完整的,必须要把其他两个附带上才能构成一组完整的地理数据。除了这三个必须的文件以外,还有八个可选的文件,使用它们可以增强空间数据的表达能力。具体哪几个、怎么命名、文件里面是什么就无需再深究,只需要知道有一种数据格式叫Shapefile,我们绘制地图就是要找这种数据。
接下来就按照这三种思路一步步走,不求美观,先求能画出来图。
有一些R包中存储着常见地图的数据,比如maps包中存有世界地图、美国地图、美国各州郡地图、法国地图以及加拿大城市地图等,加载了这个包,就可以轻松愉快地绘制世界地图。
library(maps)
map("world", fill = TRUE, col = rainbow(200),
ylim = c(-60, 90), mar = c(0, 0, 0, 0))
title("世界地图")
上图参数解释:‘fill=’是否填充各个地区颜色,‘col=’为填充或边界的颜色,‘ylim=’为所取地图上下长度,‘mar=’是图片边界宽度,具体参数可参考:“?map()”
加载maps和mapdata包,使用包中中国地图的数据直接作图
map('china')
但数据比较老了(重庆仍然在四川省内),获取比较新的,下载中国GIS数据(http://cos.name/wp-content/uploads/2009/07/chinaprovinceborderdata_tar_gz.zip),解压到自己R的工作空间(“getwd()”和“setwd()”可查看和设置工作空间),然后下载maptools包:install.packages(“maptools”),加载到R的工作空间:library(maptools),然后绘制地图(这个属于接下来第二部分手动下载地图数据绘制,奈何包中数据过于古老,涉及地理问题不太好展示,为了逻辑完整,只能投机取巧先糊弄过去,可忽略不看):
x <- readShapePoly('bou2_4p.shp')
plot(x)
通过调节plot命令中的col(旧版为fg)参数来根据自己的需要对地图中的省份着以特定的颜色。
GIS数据包含925个多边形(各地区形状),col参数应该时一个长度为925的向量,第i个分量的取值就代表了地图中第i个多边形的颜色。
plot(x,col=gray(924:0/924))
因为包自带地图数据做不到实时更新,所以就不再使用包中的数据做上述多种类型的地图,主要介绍如何使用自己下载的地图数据以及第三方平台绘图。
手动绘制中国地图,第一步要下载中国地图的经纬度数据,也就是shapefile格式的数据,数据来源有很多,一般直接查找“中国地图的GIS数据”或者点这里下载。如想做全球范围的地图,可到GADM“全球航空数据管理”网下载。常用的第三方包为:maptools包用来导入并将数据映射为地图图像,ggplot2包用来在地图图像上添加各种需要的图层。
接下来为具体步骤:
这是一个压缩包,完全解压后一般包含三个文件(bou2_4p.dbf、bou2_4p.shp和bou2_4p.shx),将这三个文件解压到R的工作空间下。
R的版本为3.6.2,但是在安装maptools后无法用readShapePoly()函数读取.shp文件,提示:
readShapePoly is deprecated; use rgdal::readOGR or sf::st_read
那这样的话就按他说的使用新的包“rgdal”,然后使用包中的readOGR()函数读取.shp文件:
china_map <- readOGR("bou2_4p.shp")
china_map变量类型是"SpatialPolygonsDataFrame",他是属于S4类型的变量。关于S4有更深入的介绍,不过对使用没什么影响,唯一的影响就是在读取数据框或其他数据类型的数据时,$要用@替代。因为ggplot()函数只接受数据框类型的数据,因此需要将"SpatialPolygonsDataFrame"类型做一下转换,ggplot2包中提供了相应的转换函数fortify():
china_map1 <- fortify(china_map)
使用ggplot2包,将读取的数据框china_map1映射到坐标轴上:
ggplot(china_map1,aes(x=long,y=lat,group=group))+
geom_polygon(fill="white",colour="black")+
coord_map("polyconic")
其中coord_map(“polyconic”)为调整地图的投影显示方式,因为地球不是平面,所以根据经纬度做的图会有相应的扭曲,使用coord_map(“polyconic”)即可将地图平铺。注:上述代码中数据框china_map1可以直接使用china_map来代替,或许因为ggplot()函数在识别数据框时会自动将其转换成数据框吧。
做热图时,最重要的是确定各个地区所对应的的一个数值,可以是连续的也可以是分类的,总之在ggplot()的“fill=”参数要有个值,要获得这个值就需要在原来的china_map这个东西里添加上你自己的对应值的数据(my_data)。
我们现在的目的是将自己的数据跟GIS地图数据参照某个变量连接在一起。比较奇怪的是本来就是china_map跟my_data的连接,只要一步即可,但因为现在还不清楚fortify()的作用,而在代码中fortify(china_map)得到的数据不全,所以要再将china_map中的其他需要的数据跟fortify(china_map)所得到的数据连接起来,组成一个完整的“china_map数据框”,然后再将这个完整的数据框再与my_data连接在一起。总结起来就是两步走,根据这个目的再写相应的代码:
x<-china_map@data
china_map1<-fortify(china_map)
xs<-data.frame(x,id=seq(0:924)-1)#过渡用数据框
china_map_data<-join(china_map1,xs,type="full")#至此得到了完整的完整的“china_map数据框”
mydata<-read.csv("data_dt.csv",header=T,as.is=T)
china_data <- join(china_map_data, mydata, type="full")#最终合并
第一步:除fortify(china_map)得到的数据外,我们还需要china_map中的data数据,这里用@因为上面说过的china_map变量类型是"SpatialPolygonsDataFrame",他是属于S4类型的变量。我们将china_map@data与fortify(china_map)合并的时候,因为缺乏一个能够参照的公共变量,所以我们就自己创一个,也就是xs,他在china_map@data中添加了id变量,使其能够和fortify(china_map)连接在一起,得到china_map_data。
第二步:将上一步连接得到的数据框与my_data连接起来。注意my_data里的数据形式,应该是有两个变量,一个是应该和china_map_data中的某个变量对应起来,可用NAME命名变量,包括各个省的名称;另外的变量就是热图“fill=”参数所需要的值。最终将他们连接起来。
这里使用join()函数,他是plyer包中的一个整合函数,其中参数type=“full”,这样可以实现一对多的连接。
地图的绘制仍和第三步的一样,只是需要添加一个fill参数和一个scale_fill_gradient()图层,这些都是最基础的:
ggplot(china_data,aes(x=long,y=lat,group=group,fill=ratio))+
geom_polygon(colour="grey40")+
scale_fill_gradient(low="white",high="red")+
coord_map("polyconic")
其中的ratio就是my_data中的另一个变量,用来映射热图的颜色。
如果想在图中的省份上添加省的名字,则按照ggplot2语法的规则,就是在现在做的图上再加一个图层,这个新图层就是geom_text()函数,这个函数需要一个数据框,这个数据框的里应该有各省名称的信息,并且这些名称需要显示在新的位置,所以这个数据框里还应该有这些省名称所对应的经纬度信息,这些信息都可以在网上查到。同样是两步走:
第一步:
读取省份名称和经纬度信息:
province_city<-read.csv("pcity.csv",header=T,as.is=T)#获取省会城市坐标
pcity.csv中有三个变量,省份名称、经度、维度。
第二步;在原图上加上新的名称图层:
ggplot(china_data,aes(long,lat))+
geom_polygon(aes(group=group,fill=ratio),colour="grey",size=0.01)+
scale_fill_gradient(low="white",high="red")+
coord_map("polyconic")+
geom_text(aes(x=jd,y=wd,label=name),data=province_city,colour="black",size=2.5)
基本的地图热图完成,他就长这样:如何还需要调整例如图例、坐标轴、背景、标签等细节,具体可参考ggplot2语法。
上面一套流程走完,他就长这样,标签、图例、坐标轴、省份名称位置大小、配色什么的都很粗糙,不过终于是把他画出来了。美化的事情在我看来属于高级别的绘图,现在先不搞他,一步步走,下一步是直接用百度或谷歌地图,规规矩矩的拿来主义。
这里的拿来主义思想就是通过某个包,连接到第三方平台的公开数据库,可以直接使用和调用其中的信息,国外的地图平台就是谷歌地图,奈何网络信号不好,太太慢慢了,所以就使用国内的百度地图好了,至于有没有高德地图的连接包,想要就自己做一个呗~~~
这里用到的连接百度地图的包是baidumap,这个包目前是在GitHub上维护着,不在R语言的官方包数据库里,所以安装的话不能一键install了,一键解决不了,那就两键:
install.packages('devtools')
library(devtools)
install_github('badbye/baidumap')
因为他在GitHub上,所以使用一个devtools包中的install_github()函数就可以安装了。
这个包在使用之前,需要一个百度地图认证过程,如果想获取到信息,就得有一把钥匙打开这个信息的大门,所以在使用这个包之前,要先给R一个钥匙:
options(baidumap.key = 'nSxiPohfziUaCuONe4ViUP2N')
接下来就能使用这个包获得地图了,但是这个钥匙不一定一直有用,可能因为各种不可抗力他会过期,解决办法有两种,一是自己注册申请百度地图开发人员,为百度地图的完善尽一份力,自然就会得到钥匙了;而是找其他人分享的,多试几个就行了。
安装导入后,里面有几个函数,可以实现不同的功能。
getBaiduMap(location, width = 400, height = 400, zoom = 10, scale = 2,
color = "color", messaging = TRUE)
参数:
location:包含经度和维度的向量或者是一个矩阵,或者可以是一个字符串表示地址;经纬度和地址将作为地图的中心点
width,height:所下载地图的宽和高
zoom:map的缩放比例,是一个整数,从3(大洲)到21(建筑),默认值是10
scale:像素数
color:"color" or "bw",表示有色或者是黑白
messaging:逻辑语句,决定是否输出下载数据的信息
参数:
location:包含经度和维度的向量或者是一个矩阵,或者可以是一个字符串表示地址;经纬度和地址将作为地图的中心点
width,height:所下载地图的宽和高
zoom:map的缩放比例,是一个整数,从3(大洲)到21(建筑),默认值是10
scale:像素数
color:“color” or “bw”,表示有色或者是黑白
messaging:逻辑语句,决定是否输出下载数据的信息
例子:
q <- getBaiduMap('北京大学', width=600, height=600, zoom=18, scale = 2, messaging=FALSE)
ggmap(q)
就可以按照上面参数的设定的大小把以“北京大学”为中心的百度地图绘制出来了,长得和百度地图一样。
getCoordinate(address, city = NULL, output = "json", formatted = F)
参数:
address:地址
city:可选项,地质所在的城市
output:json或者xml格式
formatted:F返回原有的json或者xml格式,而T返回的是经纬度的矩阵
例子:
getCoordinate('北京大学',output='xml') #xml格式
getCoordinate('北京大学',output='json') #json格式
getCoordinate('北京大学',output='xml',formatted = T) #矩阵形式
#也可以同时多个地点,放在一个向量里
getCoordinate(c('北京大学', '清华大学', '人民大学'), formatted = T)
多用矩阵形式,不然在xml或者jon里找到坐标还是很费眼的,不过也也可以通过包解析这两种格式的数据,可以但没必要。
getLocation(location, output = "json", formatted = F, pois = 0)
参数:
location:经纬度
output:json或者xml格式
formatted:F返回原有的json或者xml格式,而T返回的是地名的矩阵
pois:是否返回这个位置周围的PIO
例子:
getLocation(c(118.12845, 24.57232),formatted = T)
#同样可以返回多个位置
getLocation(c(118.12845, 24.57232,116.31234,40.56125),formatted = T)
这里的两个坐标,就是““福建省厦门市集美区集美大桥” “北京市延庆县S323(延琉路)””的坐标,返回这两个地名。
getPlace(place = NULL, city = "北京")
参数:
place:你想要搜索的地方
city:城市
返回值:数据框dataframe:包含名字、经纬度、地址等
例子:
getPlace('大学','北京')
就像是在百度地图搜索框输入这两个参数一样,返回的东西就是查找到的东西,我觉得这个函数就是一个爬虫。
getRoute(...)
参数:
origin:起点
destination:终点
mode:出行方式,'walk','transit'
region:起点和终点所在区域,若不在同一地区,分别用origin_region和destination_region
tactics:10(不走高速), 11(默认, 最短时间), 12(最短路径).
coord_type:'bd09ll'(default), 'gcj02'(which Google map and Soso map are using), 'wgs84' for GPS devices.
返回值:dataframe:包含经纬度
例子:
bjMap =getBaiduMap('北京',color = 'bw')
df = getRoute('首都国际机场', '北京南苑机场')
ggmap(bjMap) + geom_path(data = df, aes(lon, lat), alpha = 0.5, col = 'red')
可以画出从“首都国际机场”到“北京南苑机场”的路线图。
主要用的函数就这几个,这个包事实上是在R中调用百度的API,不过与ggmap和REmap结合起来使用会有较好的效果;
上面的一块把基础的地图绘制方法和核心交代清楚了,但是仅仅会画出来图是不够的,我的目的是通过地图展示我的数据,所以会有很多种地图数据的展示形式,也就是很多种图。现在也有越来越多的包能绘制越来越精致的地图了,不论用哪个包,用哪个形式的地图,最终能实现我数据展示的目的就是好方法。刚开始接触也没必要一口吃下是所有的包,见招拆招,遇到好看的就去实现,实现的多了,自然就能自己创造了。