1.5 综合实战:地铁路径规划系统
本项目是一个用于帮助乘客快速找到地铁乘车路线的应用程序,该系统基于地铁站点之间的邻接矩阵和线路信息,通过用户输入的起点和终点站,计算出最短的乘车路线,并提供详细的乘车指南,包括乘车线路、换乘站点和总耗时。用户可以方便地在系统中输入起点和终点站,获取到达目的地最快的地铁乘车路线,大大提高了乘客的出行效率和体验。
实例1-5:简易上海市地铁路径规划系统(codes/1/SubWay)
1.5.1 项目介绍
随着城市的发展和人口的增加,城市交通拥堵问题日益突出,尤其是在大城市中。地铁系统因其高效、快速和环保等特点,成为了城市居民出行的重要方式之一。然而,对于新来的游客或者长时间未使用地铁的居民来说,熟悉地铁线路并找到最优的乘车路径可能会成为一项挑战。
1. 需求分析
- 快速规划最优路径:用户需要能够输入起点和终点站,系统能够迅速计算出最优的乘车路线。
- 详细的乘车指南:系统应该提供详细的乘车指南,包括乘车线路、换乘站点和总耗时,以方便用户理解和跟随。
- 友好的用户界面:系统界面应该简洁、直观,易于操作,让用户能够方便地输入起点和终点,并查看乘车指南。
- 实时更新:系统应该能够实时更新地铁线路和站点信息,确保用户获取的信息是最新的。
- 可靠性和准确性:系统计算的乘车路线应该准确可靠,用户能够依赖系统提供的指引顺利到达目的地。
2. 功能模块分析
(1)数据加载模块
- 功能描述:从地铁线路数据源加载地铁线路、站点和换乘时间等信息。
- 实现细节:读取地铁线路数据文件,解析数据并存储为相应的数据结构,如字典、列表等。
(2)路径规划模块
- 功能描述:根据用户输入的起点和终点站点,计算出最优的乘车路径。
- 实现细节:使用图搜索算法(如Dijkstra算法、A*算法等)在地铁网络图上进行搜索,找到起点到终点的最短路径。
1.5.2 地铁数据文件
文件station_new_data.json提供的是一份地铁站点数据文件,数据内容的格式如下所示。
1号线 富锦路 友谊西路 2
1号线 友谊西路 宝安公路 2
1号线 宝安公路 共富新村 3
1号线 共富新村 呼兰路 3
1号线 呼兰路 通河新村 2
1号线 通河新村 共康路 3
1号线 共康路 彭浦新村 2
1号线 彭浦新村 汶水路 2
1号线 汶水路 上海马戏城 2
1号线 上海马戏城 延长路 2
1号线 延长路 中山北路 3
1号线 中山北路 上海火车站 3
1号线 上海火车站 汉中路 1
1号线 汉中路 新闸路 2
1号线 新闸路 人民广场 3
1号线 人民广场 黄陂南路 2
1号线 黄陂南路 陕西南路 3
1号线 陕西南路 常熟路 2
1号线 常熟路 衡山路 2
1号线 衡山路 徐家汇 3
1号线 徐家汇 上海体育馆 3
1号线 上海体育馆 漕宝路 2
1号线 漕宝路 上海南站 3
1号线 上海南站 锦江乐园 2
1号线 锦江乐园 莲花路 3
1号线 莲花路 外环路 2
1号线 外环路 莘庄 2
2号线 徐泾东 虹桥火车站 2
2号线 虹桥火车站 虹桥2号航站楼 2
2号线 虹桥2号航站楼 淞虹路 7
2号线 淞虹路 北新泾 3
2号线 北新泾 威宁路 2
2号线 威宁路 娄山关路 3
#####省略后面的地铁数据
文件station_new_data.json包含了上海地铁各条线路的站点信息,每条线路的站点信息按照起始站到终点站的顺序排列。具体来说,上面每行包含了以下信息:
- 地铁线路名称
- 起始站点
- 终点站点
- 两站之间的距离
可以用这些数据于建立地铁线路的图结构,方便进行路径搜索和规划。需要注意的是,有些站点可能在多条地铁线路上出现,而每个站点的名称应该是唯一的。在实际应用中,这些数据可以被用于地铁线路规划、乘客路径查询等方面。
1.5.3 找出两个站点的最短路径
文件main.py实现了一个地铁路径规划系统,通过读取地铁站点信息和线路信息,构建了地铁站点之间的邻接矩阵。然后,利用 Dijkstra 算法计算从起点到终点的最短路径,并考虑了换乘时间。最终,打印输出了起点到终点的最短路径信息,包括总时间以及经过的线路和站点。
(1)定义了一个名为StageNode的类,表示两个站点之间的线路节点。这个类有四个属性:起点(start)、终点(end)、所用时间(time)和所在线路(line)。这个类的目的是在地铁路线规划等应用中存储和处理站点之间的相关信息。
import json
from collections import defaultdict
class StageNode(object):
"""
两个站点之间的线路节点类
"""
# 起点
start = ''
# 终点
end = ''
# 所用时间,不是同一条线的话,time为999,line为0
time = 999
# 所在线路
line = 0
(2)方法get_matrix用于获取地铁站点之间的邻接矩阵及相关信息。首先从文件中读取地铁站点数据,并根据站点之间的关系构建邻接矩阵。然后,该方法会返回如下4个主要的数据结构:
- matrix_li:表示地铁站点之间的邻接矩阵,交点为StageNode类的实例对象,具有四个属性:起点、终点、所用时间和所在线路。
- stations_index_li:一个字典,用于通过站名获取其在邻接矩阵中的索引。
- line_station_di:一个字典,用于存储每条线路上的所有站点。
- line_time_di:一个字典,用于存储每条线路上各站点之间的所需时间。
由此可见,方法get_matrix是地铁路径规划系统中的关键组成部分,用于构建地铁网络图以及存储相关站点和线路信息。
def get_matrix():
"""
获取邻接矩阵
:return:
"""
with open('./data/station_new_data.json', encoding='utf-8') as f:
lines_di = defaultdict(list)
# 所有站点
stations = set()
# key:线路,value:所有站点
line_station_di = defaultdict(list)
# key:线路,value:所有站点的时间
line_time_di = defaultdict(list)
for i in f:
lines = i.strip().split('\t')[0]
start = i.strip().split('\t')[1]
end = i.strip().split('\t')[2]
times = int(i.strip().split('\t')[3])
stations.add(start)
stations.add(end)
lines_di[start].append(lines)
lines_di[end].append(lines)
if start not in line_station_di[lines]:
line_station_di[lines].append(start)
if end not in line_station_di[lines]:
line_station_di[lines].append(end)
line_time_di[lines].append(times)
stations_li = list(stations)
stations_li.sort(reverse=True)
# 站点的index字典,通过站名获取其index
stations_index_li = {i: ind for ind, i in enumerate(stations_li)}
# 站点和站点之间的邻接矩阵,交点为StageNode实例对象,具有四个属性
matrix_li = list()
for m in stations_li:
line_li = list()
for n in stations_li:
stage_node = StageNode()
stage_node.start = m
stage_node.end = n
# 是否有相同的线路
same_line_li = list(set([i for i in lines_di.get(m, []) if i in lines_di.get(n, [])]))
if same_line_li:
# 如果有共同的线路,则获取共同线路的所需时间
same_time_li = list()
for s in same_line_li:
index_one = line_station_di[s].index(m)
index_two = line_station_di[s].index(n)
if index_one > index_two:
index_one, index_two = index_two, index_one
time_li = line_time_di[s][index_one:index_two]
time_num = sum(time_li)
same_time_li.append(time_num)
li = list(zip(same_time_li, same_line_li))
li = sorted(li, key=lambda x: x[0], reverse=False)
new_time = li[0][0]
new_line = li[0][1]
stage_node.time = new_time
stage_node.line = new_line
else:
stage_node.time = 999
stage_node.line = 0
line_li.append((stage_node.time, [stage_node]))
matrix_li.append(line_li)
return matrix_li, stations_index_li, line_station_di, line_time_di
(3)方法get_line的功能是根据给定的起点和终点获取乘车线路及相关信息。该方法首先确定起点和终点在邻接矩阵中的索引,然后利用 Dijkstra 算法计算从起点到终点的最短路径。在计算过程中考虑了换乘时间,并在路径中添加了换乘节点信息。最后,该方法返回一个字典,其中包含了最短路径的总时间和经过的线路及站点信息。
def get_line(matrix_li, stations_index_li, line_station_di, line_time_di, source, target):
"""获取乘车线路"""
start_index = stations_index_li[source]
end_index = stations_index_li[target]
start_li = matrix_li[start_index]
path_li = [0 for i in range(len(stations_index_li))]
path_li[start_index] = 1
transfer_time = 3
transfer_node = StageNode()
transfer_node.start = '换乘'
transfer_node.end = '换乘'
transfer_node.time = transfer_time
transfer_node.line = '换乘'
for i in range(len(stations_index_li)):
short_time = 1000
short_index = 0
for ind, a in enumerate(start_li):
if path_li[ind] == 0 and a[0] < short_time:
short_time = a[0]
short_index = ind
path_li[short_index] = 1
for j in range(len(stations_index_li)):
if start_li[short_index][0] + transfer_time + matrix_li[short_index][j][0] < start_li[j][0]:
a = [i for i in start_li[short_index][1]]
a.append(transfer_node)
for new_a in matrix_li[short_index][j][1]:
a.append(new_a)
total_time = sum([i.time for i in a])
start_li[j] = (total_time, a)
ret_li = []
ret_di = {'time': start_li[end_index][0], 'line_li': ret_li}
for route in start_li[end_index][1]:
start = route.start
end = route.end
lines = route.line
times = route.time
if start != '换乘':
line_station_di_reverse = line_station_di[lines][::-1]
line_time_di_reverse = line_time_di[lines][::-1]
index_one = line_station_di[lines].index(start)
index_two = line_station_di[lines].index(end)
if index_one > index_two:
index_one = line_station_di_reverse.index(start)
index_two = line_station_di_reverse.index(end)
station_li = line_station_di_reverse[index_one:index_two + 1]
time_li = line_time_di_reverse[index_one:index_two]
else:
station_li = line_station_di[lines][index_one:index_two + 1]
time_li = line_time_di[lines][index_one:index_two]
one_line_di = {'line': lines, 'time': times, 'station': []}
for ind, st in enumerate(station_li):
if ind != station_li.__len__() - 1:
start_station = station_li[ind]
end_station = station_li[ind + 1]
use_time = time_li[ind]
one_li = (start_station, end_station, use_time)
one_line_di['station'].append(one_li)
ret_li.append(one_line_di)
return ret_di
(4)下面这段代码是程序的入口,用于执行地铁路径规划系统。首先调用get_matrix方法获取邻接矩阵和相关的站点信息,然后通过用户输入获取起点和终点站点名称,接着调用get_line方法计算并获取起点到终点的乘车线路及相关信息,最后打印输出计算结果。整个过程实现了用户与系统的交互,用户可以通过输入起点和终点来获取地铁乘车线路及相关信息。
if __name__ == '__main__':
matrix_li, stations_index_li, line_station_di, line_time_di = get_matrix()
source = input("请输入起点站:")
target = input("请输入终点站:")
transfer_li = get_line(matrix_li, stations_index_li, line_station_di, line_time_di, source, target)
print(transfer_li)
执行后需要输入起点和终点,例如分别输入“莘庄”和“静安寺”后会输出下面的计算结果:
请输入起点站:莘庄
请输入终点站:静安寺
{'time': 27, 'line_li': [{'line': '1号线', 'time': 22, 'station': [('莘庄', '外环路', 2), ('外环路', '莲花路', 2), ('莲花路', '锦江乐园', 3), ('锦江乐园', '上海南站', 2), ('上海南站', '漕宝路', 3), ('漕宝路', '上海体育馆', 2), ('上海体育馆', '徐家汇', 3), ('徐家汇', '衡山路', 3), ('衡山路', '常熟路', 2)]}, {'line': '7号线', 'time': 2, 'station': [('常熟路', '静安寺', 2)]}]}
上面的输出结果包括了总时间和乘车线路信息,总时间为27分钟。乘车线路分为两段,第一段是乘坐1号线,从莘庄站到常熟路站,共计22分钟,经过了多个站点;第二段是乘坐7号线,从常熟路站到静安寺站,共计2分钟。