北京地铁出行线路规划系统项目总结

GitHub仓库地址:https://github.com/KeadinZhou/SE-Subway

Demo地址:http://10.66.2.161:8080/ (校内网)

项目需求

  • 实现一个帮助进行地铁出行路线规划的命令行程序
  • 地铁线路图数据需要与执行程序解耦
  • 支持查询单条线路的所有站点
  • 支持查询任意两站之间通过最少站数的路线

算法设计

项目中最主要的点在于:找出两个站点之间通过最少站数的路线。该点对应的经典模型是“在一个无向图中找出两点之间的最短路径”。各个站点即为无向图中的点,两站间的路径即为无向图中连接两个节点的边,在此需求下,所有边的长度均为1

地铁最短路的换乘,最贴近实际的情况是“一次初始化,多次查询”,即在地图确定下来之后可能会有若干次不同点对的查询。而本需求中地铁线路图数据需要与执行程序解耦,每次查询对于地图数据都可能是不同的,所以初始化的时间应该均摊到每次查询中去。由于地铁地图属于稀疏图,且该图中每条边权恒定,故可以直接使用BFS算法解决。

细节上,为了解决两条线路覆盖的情况下(见下图),连续换乘的问题,需要更好的换乘算法。

解决方法:通过判断站点和前继站点的所在线路是否有重叠来判断是否需要换乘。

存储设计

数据存储上,可以采用存点/存边两种形式。在本项目中,存边会明显优于存点。

  • 简洁易懂。数据中每个条目表示两个站点中的一条通路。
  • 易于扩展。添加站点方便;数据不需要有序。
  • 方便应用程度读取。数据中每个条目即为图中的一条边,方便建图。

具体到内容,整个数据文件可以被几条线路分成几个大的模块,每个模块存一条线路的数据,由于地铁站名不存在同名的情况,可以不存线路换乘站点的信息,通过同名同名判断即可。由于同一条边不可能同时属于两条线路,所以在边信息上不会存在冗余。每个模块由一个星号(*)开始,后面这个这条线的名称。接下来包含若干条边信息。每条边信息的格式为起点站名称 终点站名称。每条线的起点可以由建图后入度为 0 的点确定。

样例如下:

* 1号线
刘院 西横堤
果酒场 本溪路
...(省略)
咸水沽北 双桥河
* 9号线
天津站 大王庄
大王庄 十一经路
...(省略)

架构设计

系统整体的计算核心使用Java实现,通过命令行可以与之进行数据交互,通过指定地图数据和相关查询指令,可以实现所有的需求。同时,该核心可以通过外加另外的展示模块来实现人机交互的可视化。外加的数据显示模块采用B/S架构,后端使用Python/Flask实现,前端使用Node.js/Vue.js实现,前后端分离,两者使用接口进行数据交互。

实现细节

计算核心

数据初始化
private static void dataInit(String pathname) {
try(FileReader reader = new FileReader(pathname); BufferedReader br = new BufferedReader(reader) ) {
String line;
String lineName="";
boolean first=false;
while((line=br.readLine()) != null) {
String[] data = line.split(" ");
if(data[0].contains("*")){
lineName=data[1];
first=true;
continue;
}
int id1=getStationId(data[0]);
int id2=getStationId(data[1]);
if(first){
lineStart.put(lineName, id1);
first=false;
stations.get(id1).addLines(lineName);
}
stations.get(id2).addLines(lineName);
addEdge(id1, id2, lineName);
}
Station.count=stationCnt;
} catch (Exception e) {
e.printStackTrace();
}
}

通过约定的数据存储格式进行数据初始化,通过 * 星号来区别不同线路的数据。每条数据表示地铁图上的一条边,通过记录边数据和对应的线路名称来建立地铁图。

换乘检测
private static String checkSwitchLine(int id1, int id2, int id3){
String linea=edgeLines.get(getEdgeHash(id1,id2));
String lineb=edgeLines.get(getEdgeHash(id2,id3));
if(linea.equals(lineb)) return null;
return lineb;
}

通过检测相邻两条表是否同属于同一条线路来判断是否需要换乘。

路径查询
private static void getShortestPath(String start, String end) throws IOException {
int startID=getStationId(start);
if(startID>Station.count){
println("错误:没有 " + start + " 这个站");
return;
}
int endID=getStationId(end);
if(endID>Station.count){
println("错误:没有 " + end + " 这个站");
return;
}
Queue<Integer>q=new LinkedList<>();
q.offer(startID);
stations.get(startID).setVis(true);
while(!q.isEmpty()){
Station now=stations.get(q.poll());
for(int it:now.getEdges()){
Station tmp=stations.get(it);
if(tmp.isVis()) continue;
q.offer(it);
tmp.setVis(true);
tmp.setLastPoint(now.getId());
if(it==endID) break;
}
}
Stack<Integer> path=new Stack<>();
int tip=endID;
while(tip!=startID){
path.push(tip);
tip=stations.get(tip).getLastPoint();
} ArrayList<Integer> res=new ArrayList<>();
res.add(startID);
while(!path.empty()){
int now=path.pop();
res.add(now);
}
println(res.size());
int tmpa=-1,tmpb=-1;
for(int it:res){
if(tmpa!=-1){
String switchMsg=checkSwitchLine(tmpa,tmpb,it);
if(switchMsg!=null){
println(switchMsg);
}
}
println(stations.get(it).getName());
tmpa=tmpb;
tmpb=it;
}
}

通过BFS算法进行最短路的寻路。找到路径之后检测路径上的所有站点是否需要换乘。

后端接口

后端接口由Python/Flask实现。Flask是一个轻量级的web框架,在接口类后端应用上有很大的优势。

全局数据接口
@app.route('/data', methods=['GET'])
def data():
lines = []
sites = set()
file = open('subway.txt', 'r')
for item in file:
ll = item.strip().split()
if ll[0].find('*') != -1:
lines.append(ll[1])
else:
sites.add(ll[0])
sites.add(ll[1])
return jsonify({
'lines': lines,
'sites': list(sites)
})

该接口用于查询地铁图中出现的所有线路和所有站点的列表,通过直接读取数据文件实现。

查询接口
GET_ALL_CMD = 'java subway -map subway.txt -a %s -o out.txt'
GET_PATH_CMD = 'java subway -map subway.txt -b %s %s -o out.txt' @app.route('/query', methods=['POST'])
def query():
json_data = request.get_json()
all = json_data['isAll']
if all:
os.system(GET_ALL_CMD % json_data['query'])
else:
os.system(GET_PATH_CMD % (json_data['query'][0], json_data['query'][1]))
file = open('out.txt', 'r')
res = []
for item in file:
res.append(item.strip())
return jsonify({
'res': res
})

该接口通过前端提交的数据,选择查询线路和查询路径对应的计算核心命令,来使用计算核心来获取线路数据。

前端页面

前端页面是用户和程序交互的非常重要的手段。本项目的前端页面使用Vue.js配合ElementUI实现。

运行效果

计算核心

通过-a命令显示一条线上所有的站点。

通过-b命令查询两站之间的最短路径。

通过-o命令指定结果输出的文件。

计算核心的健壮性:不合法命令的检测。

WEB展示前端

查询主界面。(路径查询)

当查询时,输入关键字,系统将会自动匹配补全相关的站点供用户选择。

查询的结果。显示每一站的站名,并在需要换乘的站点后面标注换乘的线路。

线路站点查询,可以查询每条线路所包含的所有站点。

线路查询结果。

北京地铁出行线路规划系统项目总结(Java+Flask+Vue实现)的更多相关文章

  1. canves绘制北京地铁线路图,包括线路绘制,优先路线,单路径选择。

    canves绘制北京地铁线路图,包括线路绘制,优先路线,单路径选择. 即将推出,后台涵盖各种语言,php,C#,java,nodejs等.

  2. Dijkstra算法_北京地铁换乘_android实现-附带源码.apk

    Dijkstra算法_北京地铁换乘_android实现   android 2.2+ 源码下载    apk下载 直接上图片 如下: Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计 ...

  3. [转载]将别人的项目或JAVA文件导入到自己的Eclipse中时,常常会出现JAVA文件的中文注释变成乱码的情况,解决办法

    eclipse 代码中文注释乱码 求解决 将别人的项目或JAVA文件导入到自己的Eclipse中时,常常会出现JAVA文件的中文注释变成乱码的情况,主要原因就是别人的IDE编码格式和自己的Eclips ...

  4. Java web项目引用java项目,类型找不到

    Java web项目引用java项目,类型找不到 错误信息: java.lang.ClassNotFoundException: org.codehaus.jackson.map.ObjectMapp ...

  5. 北京地铁月度消费总金额计算(Python版)

    最近业余时间在学习Python,这是那天坐地铁时突发奇想,想看看我这一个月的地铁费共多少钱,所以简单的构思了下思路,就直接开写了,没想到用Python来实现还挺简单的. 设计思路: 每次乘车正常消费7 ...

  6. WPF简易北京地铁效果图

    这个是百度地图上北京地铁的地址http://map.baidu.com/?subwayShareId=beijing,131,我们先看下百度上面的效果图 我要实现的内容比较简单,就是绘制这些图,和在地 ...

  7. ubuntu下eclipse新建项目没有java project的解决办法

    装好了eclipse之后却发现新建项目没有java project的选项,大致搜索了一下,并没有发现很好的解决方案(大都是让你重新安装什么的),于是开始瞎鼓捣,并且找到了一个方案: 在终端切换到roo ...

  8. Maven项目中java类报错-Cannot resolve symbol

    电脑蓝屏了,强制重启之后再打开IDEA里面的项目,所有Java类文件都在报Cannot resolve symbo错误,可以确定所有依赖的包都有引用且jar包没有冲突. 经查询找到这个解决方法: 在I ...

  9. 【MyEcplise】导入项目报错:Errors running builder 'JavaScript Validator' on project '项目名'. java.lang.ClassCastException

    导入项目报错:Errors running builder 'JavaScript Validator' on project '项目名'. java.lang.ClassCastException ...

随机推荐

  1. SQL Server 完整备份遇到的一个不常见的错误

    1. 错误详情 有一次在手动执行数据库完整备份时遇到如下错误: 执行多次都是这个错误信息. 提示无法生成检查点,原因可能是由于系统资源(如磁盘或内存空间)不足或者有时是由于数据库损坏而造成的. 我们检 ...

  2. [PHP] Ubuntu快速安装起PHP7.4

    先安装一下这个命令 add-apt-repositoryapt-get install software-properties-common 添加第三方源:add-apt-repository ppa ...

  3. CodeForces - 1251E2 (思维+贪心)

    题意 https://vjudge.net/problem/CodeForces-1251E2 一共有 n 个选民,你可以付出 pi​ 的代价让第 i 个选民为你投票,或者,在为你投票的人数达到 mi ...

  4. I2C硬件与模拟的区别

    硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的I2C:一般也较为稳定,但是程序较为繁琐. 硬件(固件)I2C是直接调用内部寄存器进行 ...

  5. Python的3种执行方式

    1.Python源程序就是一个特殊格式的文本文件,可以使用任意文本编辑器软件做python的开发,python的文件扩展名为 .py   2.执行python程序的三种方式 解释器:用命令行输入:如输 ...

  6. CentOS 8安装体验

    这两天出来了,晚上爽一爽. 一,下载 http://ftp.sjtu.edu.cn/centos/8.0.1905/isos/x86_64/ 还是那7G左右的保险,没有minial版了,那个500m多 ...

  7. ubuntu下的mv命令

    移动文件 代码: mv file1 dir1 #因为dir1目录是存在的,可以不加 / 直接放入 mv file1 dir1/ #把temp文件移动到myfiles目录中 移动目录 代码: mv di ...

  8. LG3389 「模板」高斯消元法 高斯消元

    问题描述 LG3389 题解 高斯消元,是用来解\(n\)元一次方程组的算法,时间复杂度\(O(n^3)\) 这样就构造出了这个方程组的矩阵 目标就是把这个矩阵左边\(n \times n\)消为单位 ...

  9. CF92B-Binary Number-(思维)

    https://vjudge.net/problem/CodeForces-92B 题意:给一个长度为106的二进制数,有两种操作,第一种是除以2,第二种是末尾+1,以二进制运算,问这个二进制数最少几 ...

  10. Vue indent eslint缩进webstorm冲突解决

    参考教程 官方回复 ESlint设置 rules: { 'no-multiple-empty-lines': [1, {max: 3}], // 控制允许的最多的空行数量 'vue/script-in ...