如何使用GTFS列出与某路线相关的所有站点?

20
我正在使用一些GTFS数据,并希望能够创建一个包含所有与某个路线相关的停靠站的列表。我不太明白如何在GTFS数据中实现这一点。
Trips.txt 的格式如下:
route_id,service_id,trip_id,trip_headsign,direction_id,block_id,shape_id 1,A20120610WKD,A20120610WKD_000800_1..S03R,SOUTH FERRY,1,,1..S03R 1,A20120610WKD,A20120610WKD_002700_1..S03R,SOUTH FERRY,1,,1..S03R 1,A20120610WKD,A20120610WKD_004700_1..S03R,SOUTH FERRY,1,,1..S03R 1,A20120610WKD,A20120610WKD_006700_1..S03R,SOUTH FERRY,1,,1..S03R 1,A20120610WKD,A20120610WKD_008700_1..S03R,SOUTH FERRY,1,,1..S03R
我尝试使用形状ID读取匹配的形状,然后查找具有相同纬度和经度的站点,但这似乎不可靠。有人知道如何做到这一点吗?

1
这是一个非常适合于http://area51.stackexchange.com/proposals/49339/open-transportation-technology的问题。 - gcamp
6个回答

47

正如您所注意到的,GTFS 中的路线和站点之间没有直接关系。相反,站点与 行程 相关联,每个行程代表车辆沿特定路线的单次“运行”。这反映了一个事实:一条路线不一定在任何时候都服务于其所有站点——例如,在周末它可能会跳过高中外的站点。

因此,获取由路线服务的每个站点列表涉及组合几个模型:

  • routes.txt 为您提供所需路线的路线 ID。
  • trips.txt 为您提供该路线的一组行程 ID。
  • stop_times.txt 为您提供这些行程上服务的站点 ID 集。
  • stops.txt 为您提供有关这些站点的信息。

假设您正在使用 SQL 数据库来存储 GTFS 数据,则可以使用类似以下查询的查询(一旦获得了路线 ID):

SELECT stop_id, stop_name FROM stops WHERE stop_id IN (
  SELECT DISTINCT stop_id FROM stop_times WHERE trip_id IN (
    SELECT trip_id FROM trips WHERE route_id = <route_id>));

请记住,这将输出每个路线曾经服务过的站点记录。如果您正在为乘客生成时间表信息,则可能希望将查询限制为仅在今天运行的行程和在接下来的30分钟内出发的停靠时间。


更新:我之前写的SQL查询语句是为了尽可能简单地说明GTFS模型之间的关系,但btse在下面的回答中指出,这样的查询语句实际上永远不会在生产环境中使用。它太慢了。相反,您应该使用表连接和索引来保持查询时间合理。

这里是一个等效的查询语句,以更适合复制和粘贴到实际应用程序中的方式编写:

SELECT DISTINCT stops.stop_id, stops.stop_name
  FROM trips
  INNER JOIN stop_times ON stop_times.trip_id = trips.trip_id
  INNER JOIN stops ON stops.stop_id = stop_times.stop_id
  WHERE route_id = <route_id>;

通常情况下,您还需要为在JOINWHERE子句中使用的每个列创建一个索引,这意味着:
CREATE INDEX stop_times_trip_id_index ON stop_times(trip_id);

CREATE INDEX trips_route_id_index ON trips(route_id);

(请注意,关系型数据库通常会自动按照主键索引每个表,因此无需显式地在 stops.stop_id 上创建索引。)

根据使用的具体 DBMS 和您愿意为性能牺牲磁盘空间的程度,还可以进行许多进一步的优化。但是这些命令将在几乎任何 RDBMS 上产生良好的性能,而不会不必要地牺牲清晰度。


谢谢。我本应该自己能够解决这个问题,但是我可能写了大约100行代码才实现了这3行SQL的功能。 - Alex Muro
这个答案部分正确。是的,你会得到一条路线上的所有站点(这就是OP所问的),但许多路线有不同的分支,这个查询将同时返回所有分支。我仍在努力弄清如何拆分分支。 - Julian
2
很棒的JOIN查询展示了GTFS中表格之间的关系。然而,正如@Julian所指出的,它在确定分支方面存在不足。还需要以某种方式确定停靠序列。如果您可以附加一些有关如何最好拆分分支并确定序列的详细信息,那就太好了。谢谢! - AlexVPerl
这个回答很好,但我有一个问题:在考虑的地区(我的情况是东英格兰),从哪里获取routes.txttrips.txtstop_times.txtstops.txt文件? - mercury0114
这种解决方案的限制是,您可能没有按正确顺序停靠。 例如 第一次旅行 = A:8h00 | B:8h10 | C:8h20 ... 第二次旅行 = B:9h10 | E:9h20 ... 您知道E在B之后,但不知道E是在C之前还是之后。 - david CHOLLEZ

11
我在Google搜索中找到这篇文章,并想更新一下更好的答案,以防其他人遇到同样的问题。Simon给出的答案是100%正确的,但是他提供的查询对于大型GTFS数据源来说速度相当慢。以下是可执行相同操作但速度明显更快的查询语句。
仅为了提供一些个人经验,对于大约50mb的GTFS数据源,Simon的查询需要10-25秒才能完成。下面的语句始终少于0.2秒。
SELECT T3.stop_id, T3.stop_name 
FROM trips AS T1
JOIN
stop_times AS T2
ON T1.trip_id=T2.trip_id AND route_id = <routeid>
JOIN stops AS T3
ON T2.stop_id=T3.stop_id
GROUP BY T3.stop_id, T3.stop_name

更新:

我意识到之前没有提到,但是你当然需要在每个表被连接的地方创建索引。


请问一下,stops、stop_times和trips表的结构应该是怎样的?这两个查询执行起来都需要很长时间(大约20秒左右)。 - dargod
@dargod 表格的结构应该遵循谷歌在其开发者指南中概述的相同结构,该指南可以在此处找到。https://developers.google.com/transit/gtfs/reference。您还需要确保每个用于连接或选择的列都有索引。 - btse
非常感谢!所以根据您的查询,我应该在stop_id、stop_name、trip_id和route_id上建立索引? - dargod

4
如果在从trips选择时使用GROUP BY shape_id,您可以使查询速度更快。
使用@btse的查询获取两个路线的唯一站点需要1.147秒。
我的等效查询只需0.4秒。
SELECT unique_stops.route_id, unique_stops.stop_id, stop_name, stop_desc, stop_lat, stop_lon
FROM
  stops,
  (SELECT stop_id, route_id
   FROM
     stop_times,
     (SELECT trip_id, route_id
      FROM trips
      WHERE route_id IN (801, 803)
      GROUP BY shape_id
     ) AS unique_trips
   WHERE stop_times.trip_id = unique_trips.trip_id
   GROUP BY stop_id) AS unique_stops
WHERE stops.stop_id = unique_stops.stop_id

1
这比其他查询快得多! - devha

0
如果你正在使用R语言,你可以通过以下方式查找到停靠在目标站点X的路线:
require(dplyr)

routesX <- routes %>%
  left_join(trips %>% select(trip_id, route_id, shape_id)) %>%
  left_join(stop_times %>% select(trip_id, stop_id)) %>%
  semi_join(stops %>% filter(grepl('X', stop_name, ignore.case = T)), by = c('stop_id' = 'stop_code')) %>%
  select(names(routes), shape_id) %>%
  unique 

0
如果需要停止的方向,应该对Lukmaan的回答进行更改:
SELECT unique_stops.route_id, unique_stops.stop_id, stop_name, stop_desc, stop_lat, stop_lon, unique_stops.direction_id
FROM
  stops,
  (SELECT stop_id, route_id, direction_id
   FROM
     stop_times,
     (SELECT trip_id, route_id, direction_id
      FROM trips
      WHERE route_id IN (801, 803)
      GROUP BY direction_id
     ) AS unique_trips
   WHERE stop_times.trip_id = unique_trips.id
   GROUP BY stop_id, direction_id) AS unique_stops
WHERE stops.stop_id = unique_stops.stop_id

如果您也按照同样的方式添加stop_times.stop_sequence,并按方向和停靠顺序排序,则停靠点将按行程中的顺序排序。

-1

1
遗憾的是,MTA NYCT Subway(问题中的示例)不属于MTA Bus Time的一部分。 - Tony Laidig

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