绘制使用 read.shp 和 ggplot2 加载的形状文件
Plotting shape files loaded using read.shp with ggplot2
我想绘制一个使用 fastshp
包中的 read.shp 加载的形状文件。但是,read.shp
函数是 returns 列表而不是 data.frame 列表。我不确定我需要提取列表的哪一部分才能获得格式正确的 data.frame 对象。 stack overflow 上已经有人问过这个确切的问题,但是,该解决方案似乎不再有效(解决方案来自 > 7 年前)。非常感谢任何帮助。
remotes::install_github("s-u/fastshp") #fastshp not on CRAN
library(ggplot2);library(fastshp)
temp <- tempfile()
temp2 <- tempfile()
download.file("https://www2.census.gov/geo/tiger/TIGER2017/COUNTY/tl_2017_us_county.zip",temp)
unzip(zipfile = temp, exdir = temp2)
shp <- list.files(temp2, pattern = ".shp$",full.names=TRUE) %>% read.shp(.)
shp
是包含大量信息的列表列表。我尝试了之前发布的 SO 中的以下解决方案,但无济于事:
shp.list <- sapply(shp, FUN = function(x) Polygon(cbind(lon = x$x, lat = x$y))) #throws an error here cbind(lon = x$x, lat = x$y) returns NULL
shp.poly <- Polygons(shp.list, "area")
shp.df <- fortify(shp.poly, region = "area")
我还尝试了以下方法:
shp.list <- sapply(shp, FUN = function(x) do.call(cbind, x[c("id","x","y")])) #returns NULL value here...
shp.df <- as.data.frame(do.call(rbind, shp.list))
已更新:仍然没有运气但更接近:
file_shp<-list.files(temp2, pattern = ".shp$",full.names=TRUE) %>%
read.shp(., format = c("table"))
ggplot() +
geom_polygon(data = file_shp, aes(x = x, y = y, group = part),
colour = "black", fill = NA)
投影似乎关闭了。我不确定如何命令数据正确映射,也不确定如何读取 CRS 数据。尝试了以下无济于事:
file_prj<-list.files(temp2, pattern = ".prj$",full.names=TRUE) %>%
proj4string(.)
我尝试使用您脚本中的人口普查数据。但是,当我将 read.shp()
应用于多边形数据时,R Studio 不知何故一直崩溃。因此,我决定使用 read.shp()
帮助页面中的示例,该示例也是人口普查数据。希望你不会介意。花了一些时间弄清楚如何用 class shp 绘制地图。让我一步一步解释一下我经历了什么。
这部分来自帮助页面。我基本上是在获取 shapefile 并将其作为 shp 对象导入。
# Census 2010 TIGER/Line(TM) state shapefile
library(fastshp)
fn <- system.file("shp", "tl_2010_us_state10.shp.xz", package="fastshp")
s <- read.shp(xzfile(fn, "rb"))
让我们检查一下这个对象,s
是什么样的。它包含 52 个列表。在每个列表中,有六个向量。 ID
是一个唯一的整数来表示一个状态。 x
是经度,y
是纬度。最糟糕的部分是 parts
。在下面的这个例子中,只有一个数字,这意味着在这种状态下只有一个多边形。但是其他一些列表(状态)有多个数字。这些数字基本上是指示数据中新多边形开始位置的索引。
#> str(s)
#List of 52
# $ :List of 6
# ..$ id : int 1
# ..$ type : int 5
# ..$ box : num [1:4] -111 41 -104 45
# ..$ parts: int 0
# ..$ x : num [1:9145] -109 -109 -109 -109 -109 ...
# ..$ y : num [1:9145] 45 45 45 45 45 ...
这是阿拉斯加的。如您所见,parts
中有一些数字,这些数字表示新多边形数据的开始位置。阿拉克萨有许多小岛。因此,他们需要使用此信息指示数据中的不同多边形。稍后我们将在创建数据框时回到这一点。
#List of 6
# $ id : int 18
# $ type : int 5
# $ box : num [1:4] -179.2 51.2 179.9 71.4
# $ parts: int [1:50] 0 52 88 127 175 207 244 306 341 375 ...
# $ x : num [1:14033] 177 177 177 177 177 ...
# $ y : num [1:14033] 52.1 52.1 52.1 52.1 52.1 ...
我们需要的是以下内容。对于每个列表,我们需要提取经度(即 x
)、纬度(即 y
)和 id
,以便为一个州创建数据名。此外,我们需要使用 parts
以便我们可以用唯一 ID 表示所有多边形。我们需要创建一个新的组变量,其中包含每个多边形的唯一 ID 值。我使用 findInterval()
它采用索引来创建组变量。一个棘手的部分是我们需要在 findInterval()
中使用 left.open = TRUE
来创建一个组变量。 (这让我很难弄清楚发生了什么。)这个 map_dfr()
部分处理我刚才描述的工作。
library(tidyverse)
map_dfr(.x = s,
.f = function(mylist){
temp <- data.frame(id = mylist$id,
lon = mylist$x,
lat = mylist$y)
ind <- mylist$parts
out <- mutate(temp,
subgroup = findInterval(x = 1:n(), vec = ind, left.open = TRUE),
group = paste(id, subgroup, sep = "_"))
return(out)
}) -> test
一旦我们有了 test
,我们就有了另一份工作。阿拉斯加的一些经度点保持正数(例如,179.85)。只要我们有这样的数字,ggplot2 就会画出有趣的长线,即使在您的示例中也可以看到。我们需要的是将这些正数转换为负数,以便ggplot2可以绘制出合适的地图。
mutate(test,
lon = if_else(lon > 0, lon * -1, lon)) -> out
此时,out
看起来像这样。
id lon lat subgroup group
1 1 -108.6213 45.00028 1 1_1
2 1 -108.6197 45.00028 1 1_1
3 1 -108.6150 45.00031 1 1_1
4 1 -108.6134 45.00032 1 1_1
5 1 -108.6133 45.00032 1 1_1
6 1 -108.6130 45.00032 1 1_1
现在我们准备绘制地图。
ggplot() +
geom_polygon(data = out, aes(x = lon, y = lat, group = group))
我想绘制一个使用 fastshp
包中的 read.shp 加载的形状文件。但是,read.shp
函数是 returns 列表而不是 data.frame 列表。我不确定我需要提取列表的哪一部分才能获得格式正确的 data.frame 对象。 stack overflow 上已经有人问过这个确切的问题,但是,该解决方案似乎不再有效(解决方案来自 > 7 年前)。非常感谢任何帮助。
remotes::install_github("s-u/fastshp") #fastshp not on CRAN
library(ggplot2);library(fastshp)
temp <- tempfile()
temp2 <- tempfile()
download.file("https://www2.census.gov/geo/tiger/TIGER2017/COUNTY/tl_2017_us_county.zip",temp)
unzip(zipfile = temp, exdir = temp2)
shp <- list.files(temp2, pattern = ".shp$",full.names=TRUE) %>% read.shp(.)
shp
是包含大量信息的列表列表。我尝试了之前发布的 SO 中的以下解决方案,但无济于事:
shp.list <- sapply(shp, FUN = function(x) Polygon(cbind(lon = x$x, lat = x$y))) #throws an error here cbind(lon = x$x, lat = x$y) returns NULL
shp.poly <- Polygons(shp.list, "area")
shp.df <- fortify(shp.poly, region = "area")
我还尝试了以下方法:
shp.list <- sapply(shp, FUN = function(x) do.call(cbind, x[c("id","x","y")])) #returns NULL value here...
shp.df <- as.data.frame(do.call(rbind, shp.list))
已更新:仍然没有运气但更接近:
file_shp<-list.files(temp2, pattern = ".shp$",full.names=TRUE) %>%
read.shp(., format = c("table"))
ggplot() +
geom_polygon(data = file_shp, aes(x = x, y = y, group = part),
colour = "black", fill = NA)
投影似乎关闭了。我不确定如何命令数据正确映射,也不确定如何读取 CRS 数据。尝试了以下无济于事:
file_prj<-list.files(temp2, pattern = ".prj$",full.names=TRUE) %>%
proj4string(.)
我尝试使用您脚本中的人口普查数据。但是,当我将 read.shp()
应用于多边形数据时,R Studio 不知何故一直崩溃。因此,我决定使用 read.shp()
帮助页面中的示例,该示例也是人口普查数据。希望你不会介意。花了一些时间弄清楚如何用 class shp 绘制地图。让我一步一步解释一下我经历了什么。
这部分来自帮助页面。我基本上是在获取 shapefile 并将其作为 shp 对象导入。
# Census 2010 TIGER/Line(TM) state shapefile
library(fastshp)
fn <- system.file("shp", "tl_2010_us_state10.shp.xz", package="fastshp")
s <- read.shp(xzfile(fn, "rb"))
让我们检查一下这个对象,s
是什么样的。它包含 52 个列表。在每个列表中,有六个向量。 ID
是一个唯一的整数来表示一个状态。 x
是经度,y
是纬度。最糟糕的部分是 parts
。在下面的这个例子中,只有一个数字,这意味着在这种状态下只有一个多边形。但是其他一些列表(状态)有多个数字。这些数字基本上是指示数据中新多边形开始位置的索引。
#> str(s)
#List of 52
# $ :List of 6
# ..$ id : int 1
# ..$ type : int 5
# ..$ box : num [1:4] -111 41 -104 45
# ..$ parts: int 0
# ..$ x : num [1:9145] -109 -109 -109 -109 -109 ...
# ..$ y : num [1:9145] 45 45 45 45 45 ...
这是阿拉斯加的。如您所见,parts
中有一些数字,这些数字表示新多边形数据的开始位置。阿拉克萨有许多小岛。因此,他们需要使用此信息指示数据中的不同多边形。稍后我们将在创建数据框时回到这一点。
#List of 6
# $ id : int 18
# $ type : int 5
# $ box : num [1:4] -179.2 51.2 179.9 71.4
# $ parts: int [1:50] 0 52 88 127 175 207 244 306 341 375 ...
# $ x : num [1:14033] 177 177 177 177 177 ...
# $ y : num [1:14033] 52.1 52.1 52.1 52.1 52.1 ...
我们需要的是以下内容。对于每个列表,我们需要提取经度(即 x
)、纬度(即 y
)和 id
,以便为一个州创建数据名。此外,我们需要使用 parts
以便我们可以用唯一 ID 表示所有多边形。我们需要创建一个新的组变量,其中包含每个多边形的唯一 ID 值。我使用 findInterval()
它采用索引来创建组变量。一个棘手的部分是我们需要在 findInterval()
中使用 left.open = TRUE
来创建一个组变量。 (这让我很难弄清楚发生了什么。)这个 map_dfr()
部分处理我刚才描述的工作。
library(tidyverse)
map_dfr(.x = s,
.f = function(mylist){
temp <- data.frame(id = mylist$id,
lon = mylist$x,
lat = mylist$y)
ind <- mylist$parts
out <- mutate(temp,
subgroup = findInterval(x = 1:n(), vec = ind, left.open = TRUE),
group = paste(id, subgroup, sep = "_"))
return(out)
}) -> test
一旦我们有了 test
,我们就有了另一份工作。阿拉斯加的一些经度点保持正数(例如,179.85)。只要我们有这样的数字,ggplot2 就会画出有趣的长线,即使在您的示例中也可以看到。我们需要的是将这些正数转换为负数,以便ggplot2可以绘制出合适的地图。
mutate(test,
lon = if_else(lon > 0, lon * -1, lon)) -> out
此时,out
看起来像这样。
id lon lat subgroup group
1 1 -108.6213 45.00028 1 1_1
2 1 -108.6197 45.00028 1 1_1
3 1 -108.6150 45.00031 1 1_1
4 1 -108.6134 45.00032 1 1_1
5 1 -108.6133 45.00032 1 1_1
6 1 -108.6130 45.00032 1 1_1
现在我们准备绘制地图。
ggplot() +
geom_polygon(data = out, aes(x = lon, y = lat, group = group))