如何使用Leaflet在R中创建一个交互式的GTFS数据图?

9
我希望创建一个交互式地图,展示一座城市的公共交通线路。我正在尝试使用R中的Leaflet来完成这个任务(但我也愿意接受其他建议和方案)。
数据:交通系统的数据以GTFS格式组织在文本文件(.txt)中,我将其读入R作为数据框*。
问题:我找不到如何指示每个Poly线的id(变量shape_id),以便绘图实际上可以沿着每条运输线路进行。相反,它会以随机顺序连接这些点。
以下是我尝试过的方法,但都没有成功:
# Download GTFS data of the Victoria Regional Transit System
  tf <- tempfile() 
  td <- tempdir()
  ftp.path <- "http://www.gtfs-data-exchange.com/agency/bc-transit-victoria-regional-transit-system/latest.zip"
  download.file(ftp.path, tf) 

# Read text file to a data frame
  zipfile <- unzip( tf , exdir = td )
  shape <- read.csv(zipfile[9])

# Create base map
  basemap <- leaflet() %>% addTiles()


# Add transit layer
  basemap  %>% addPolylines(lng=shape$shape_pt_lon, lat=shape$shape_pt_lat, 
                            fill = FALSE,
                            layerId =shape$shape_id) 

我很高兴听到您的意见。
*我知道可以将此数据导入GIS软件(例如QGIS)创建shapefile,然后使用readOGR将shapefile读入R。Robin Lovelace已经展示了如何做到这一点。但是,我正在寻找纯R解决方案。 ;)
附注:Kyle Walker编写了一篇关于使用Leaflet在R中制作交互式地图的优秀文章。不幸的是,他在教程中未涉及折线。

1
你可以尝试为每条路线挑选出最长的形状,这样你就不会在每条路线上有重复的形状 - 这将使你需要绘制的数据变得更小。请参阅我关于此问题的演讲(第27页)https://drive.google.com/file/d/191Jq1awInmtXPXrVlywXXaQjxv0basHe/ (也可在YouTube上找到)。 - Belinda
1个回答

12

你的问题不在于方法,而在于数据:请注意你下载了8 MB,而你尝试通过shiny加载到Leaflet中的行文件只有5 MB。一般原则是,在扩大数据规模之前,应该先使用小数据集尝试新方法。接下来我将介绍我如何诊断并解决这个问题。

第一阶段:探索和子集数据

pkgs <- c("leaflet", "shiny" # packages we'll use
  , "maps" # to test antiquated 'maps' data type
  , "maptools" # to convert 'maps' data type to Spatial* data
  )
lapply(pkgs, "library", character.only = TRUE)


class(shape)
## [1] "data.frame"

head(shape)

##   shape_id shape_pt_lon shape_pt_lat shape_pt_sequence
## 1 1-39-220    -123.4194     48.49065                 0
## 2 1-39-220    -123.4195     48.49083                 1
## 3 1-39-220    -123.4195     48.49088                 2
## 4 1-39-220    -123.4196     48.49123                 3
## 5 1-39-220    -123.4197     48.49160                 4
## 6 1-39-220    -123.4196     48.49209                 5

object.size(shape) / 1000000 # 5 MB!!!

## 5.538232 bytes

summary(shape$shape_id)
shape$shape_id <- as.character(shape$shape_id)
ids <- unique(shape$shape_id)
shape_orig <- shape
shape <- shape[shape$shape_id == ids[1],] # subset the data

第二步:转换为空间对象

这类似于从地图中获得的data.frame对象吗?

state.map <- map("state", plot = FALSE, fill = TRUE)
str(state.map)

## List of 4
##  $ x    : num [1:15599] -87.5 -87.5 -87.5 -87.5 -87.6 ...
##  $ y    : num [1:15599] 30.4 30.4 30.4 30.3 30.3 ...
##  $ range: num [1:4] -124.7 -67 25.1 49.4
##  $ names: chr [1:63] "alabama" "arizona" "arkansas" "california" ...
##  - attr(*, "class")= chr "map"

是的,它很相似,因此我们可以使用map2Spatial*进行转换:

shape_map <- list(x = shape$shape_pt_lon, y = shape$shape_pt_lat)
shape_lines <- map2SpatialLines(shape_map, IDs = ids[1])
plot(shape_lines) # success - this plots a single line!

line

第三阶段:将所有线连接在一起

使用 for 循环可以轻松完成此任务。请注意,我们仅使用前10行。对于所有行,请使用2:length(ids)

for(i in 2:10){
  shape <- shape_orig[shape_orig$shape_id == ids[i],]
  shape_map <- list(x = shape$shape_pt_lon, y = shape$shape_pt_lat)
  shape_temp <- map2SpatialLines(shape_map, IDs = ids[i])
  shape_lines <- spRbind(shape_lines, shape_temp)
}

第四阶段:绘图

使用SpatialLines对象可以使代码变得更短——在这种情况下,它将绘制前10条线:

leaflet() %>% 
  addTiles() %>%
  addPolylines(data = shape_lines)

first-10

结论

在将数据转换为正确的 ID 并绘图前,需要对数据进行操作和处理,使其成为 Spatial* 数据类型。使用 maptools::map2Spatial*unique() 和聪明的 for 循环可以解决问题。


它很好用,Robin。谢谢你。不过我在想你提到的“数据问题”。因为最终当我将你的解决方案应用于映射所有公交路线时,你的代码也会加载一个5MB的对象(shape_lines)到Leaflet中。 - rafa.pereira
没错,但为了测试和可重复性,问题应该被简化到最小/最简单的形式。 - RobinLovelace
maptools现已弃用:https://www.rdocumentation.org/packages/maptools/versions/1.1-8 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接