1

用 R 软件绘制中国分省市地图

 2 years ago
source link: https://cosx.org/2009/07/drawing-china-map-using-r/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用 R 软件绘制中国分省市地图

关键词:GIS 数据; R 软件; 中国地图

【注】新版本的maptools包对很多函数进行了修改,对于修改的内容,文章中用粗体文字进行了说明。

鉴于最近有不少人在讨论用 R 软件绘制地图的问题,我也就跟着凑了凑热闹,对相应的方法学习了一番。下面的这篇文章是一个初步的介绍,还有很多内容仍在学习和探索中,如果大家有什么意见或建议,我将根据自己学习的情况对文章进行进一步的补充。

在 R 中绘制地图其实是十分方便的,最直接的办法大概就是安装mapsmapdata这两个包,然后输入下面的命令:

library(maps)
library(mapdata)
map("china")

其中map()函数还可以加上很多参数,在这里就不一一详述,具体的用法只需问号之。然而仔细看一看这张地图你会发现重庆市和四川省仍然是浑然一体,可见该地图的数据应该是有些年头了。

幸运的是,通过谢益辉的这篇博文我们已经可以大体知道该如何操作了,下面就为大家介绍一下具体的步骤。

首先,从这里下载中国地图的 GIS 数据,这是一个压缩包,完全解压后包含三个文件(bou2_4p.dbf、bou2_4p.shp 和 bou2_4p.shx),将这三个文件解压到同一个目录下,并在 R 中设好相应的工作空间,然后安装maptools包,运行如下程序:

library(maptools);
x=read.shape('bou2_4p.shp');#下文中会继续用到x这个变量,
                            #如果你用的是其它的名称,
                            #请在下文的程序中也进行相应的改动。
plot(x);

【修改】新版本的 maptools 包不再提供 read.shape() 函数,请用 readShapePoly() 代替。

这时一张完整的中国地图就已经画好了。但是在实际使用的过程中,我们往往会根据自己的需要对地图中的某些省份着以特定的颜色,这时就可以通过调节plot命令中的fg参数来予以实现。然而为了清楚地说明这部分的内容,我需要插播一段 R 绘制地图的原理。

====================== 传说中的分割线 =====================

在绘制地图时,每一个省市自治区或者岛屿都是用一个多边形来表示的。之前的 GIS 数据,其实就是提供了每一个行政区其多边形逐点的坐标,然后 R 软件通过顺次连接这些坐标,就绘制出了一个多边形区域。在上面的数据中,一共包含了 925 个多边形的信息,之所以有这么多是因为一些省份有很多小的附属岛屿。在这 925 个多边形中,每一个都对应一个唯一的 ID,编号分别从 1 到 925。

====================== 传说中的分割线 =====================

回到刚才的话题,plot命令中的fg参数在本例中应该是一个长度为 925 的向量,其第 i 个分量的取值就代表了地图中第 i 个多边形的颜色。一个简单的尝试是运行下面这个命令看看效果:

plot(x,fg=gray(924:0/924));

【修改】新版本的maptools包的绘图参数也有所改变,请将fg换成col

于是自然就产生了一个问题:如何获取某一个特定地区的 ID,进而设置我们想要的颜色?事实上,在变量 x 中,就已经存储了我们想要的信息。在 R 中输入 “x[[2]]” 或 “x$att.data”,会得到一个 925 行 7 列的数据框,这其实是 bou24p.dbf 这个文件中存储的信息,之前的read.shape()函数虽然读取的是 bou24p.shp 文件,但在默认情况下会把 dbf 文件的信息也放到变量之中。对于这个数据框,其行名就是每一个区域的 ID 编号,第一列和第二列分别是面积和周长,最后一列是该区域所属的行政区名,其它的列应该也是一些编号性质的变量。于是,通过查找相应的行政区对应的行名,就可以对fg参数进行赋值了。下面是我编的一个函数,用来生成所需的fg向量:

getColor = function(mapdata, provname, provcol, othercol){
	f = function(x, y) ifelse(x %in% y, which(y == x), 0);
	colIndex = sapply(mapdata$att.data$NAME, f, provname);
	fg = c(othercol, provcol)[colIndex + 1];
	return(fg);
}

【修改】地图数据的组织形式有所变化,上面函数中的mapdata$att.data$NAME需要替换为mapdata@data$NAME

其中mapdata是存放地图数据的变量,在上面的例子中就是 x,provname是需要改变颜色的地区的名称,provcol是对应于provname的代表颜色的向量(名称和数字均可),othercol是其它地区的颜色。举例如下:

provname = c("北京市", "天津市", "上海市", "重庆市");
provcol = c("red", "green", "yellow", "purple");
plot(x, fg = getColor(x, provname, provcol, "white"));

map00

注意provname一定要写地区的全称,写法可以参照下面这条命令生成的向量:

as.character(na.omit(unique(x$att.data$NAME)));

由此生成的向量有 33 个元素,少了澳门特别行政区,这是这个数据中的一块瑕疵。在x$att.data的第 899 行有一个NA,不知道它代表的是否就是澳门。

利用类似的方法就可以根据自己的需要对不同的区域进行着色,下面再举一例。从国家统计局获取 2007 年我国各地区的人口数据,然后根据人口的多少对各省份进行着色。程序如下:

provname = c("北京市", "天津市", "河北省", "山西省", "内蒙古自治区",
		"辽宁省", "吉林省", "黑龙江省", "上海市", "江苏省",
		"浙江省", "安徽省", "福建省", "江西省", "山东省",
		"河南省", "湖北省", "湖南省", "广东省",
		"广西壮族自治区", "海南省", "重庆市", "四川省", "贵州省",
		"云南省", "西藏自治区", "陕西省", "甘肃省", "青海省",
		"宁夏回族自治区", "新疆维吾尔自治区", "台湾省",
		"香港特别行政区");
pop = c(1633, 1115, 6943, 3393, 2405, 4298, 2730, 3824, 1858, 7625,
		5060, 6118, 3581, 4368, 9367, 9360, 5699, 6355, 9449,
		4768, 845, 2816, 8127, 3762, 4514, 284, 3748, 2617,
		552, 610, 2095, 2296, 693);
provcol = rgb(red = 1 - pop/max(pop)/2, green = 1-pop/max(pop)/2, blue = 0);
plot(x, fg = getColor(x, provname, provcol, "white"), xlab = "", ylab = "");

map01

其中颜色越深的地方代表人口数越多,反之为人口数越少。

此外,在绘制地图的过程中,还有一个比较有用的参数是recs,它是一个由多边形 ID 组成的向量,表示在地图中只画出这些 ID 所代表的区域。利用这个参数,就可以画出某一部分的地图,例如下面的例子是我国中部六省的地图:

getID = function(mapdata, provname){
	index = mapdata$att.data$NAME %in% provname;
	ids = rownames(mapdata$att.data[index, ]);
	return(as.numeric(ids));
}
midchina = c("河南省", "山西省", "湖北省", "安徽省", "湖南省", "江西省");
plot(x, recs = getID(x, midchina), fg = "green", ol = "white", xlab = "",
		ylab = "");

map02

上面的getID()是我编写的一个功能与getColor()类似的函数,用来返回指定省份的 ID。

【修改】新版本的maptools包的绘图函数已经取消了recs这个参数,现在要实现这个功能,可以在颜色上把不需要的省份变成白色,其中填充色用col参数,边界颜色用border参数。例如上面的例子可以用下面的函数来实现:

plot(x, col = getColor(x, midchina, rep("green", 6),
    "white"), border = "white", xlab = "", ylab = "")

最后要说的是,在画出的图上仍然可以用points()函数和text()函数加上点和文字,而maptools包中还提供了一个pointLabel()函数,用来解决文本标签的重叠问题。这部分内容请参阅博文:用 R 画中国地图并标注城市位置,以及避免文本标签重叠:maptools 中的 pointLabel()

从以上的内容来看,本文所述的都是一些最基本的绘图方法,还没有对地理信息数据进行更进一步的分析。如果有机会的话,这一主题的下一篇文章将为大家介绍地图数据的组成结构,并说明如何将不同格式的地理数据整合起来,例如如何在上面的地图上绘制出我国的铁路、水系分布等内容。

普渡大学统计系博士,现为卡耐基梅隆大学博士后,感兴趣的方向包括计算统计学、机器学习、大型数据处理等,参与翻译了《应用预测建模》《R 语言编程艺术》《ggplot2:数据分析与图形艺术》等经典书籍,是 showtext、RSpectra、recosystem、prettydoc 等流行 R 软件包的作者。邱怡轩

敬告各位友媒,如需转载,请与统计之都小编联系(直接留言或发至邮箱:[email protected]),获准转载的请在显著位置注明作者和出处(转载自:统计之都),并在文章结尾处附上统计之都微信二维码。

统计之都微信二维码

← R 中面向对象编程方法 WinBUGS 在统计分析中的应用(第四部分) →

发表 / 查看评论


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK