北京地铁线路规划系统——总结

北京地铁线路规划系统——总结,第1张

概述目录 项目概况 数据流分析 文件结构与存储结构 文件结构 存储结构 业务逻辑实现(subwayFunction.py) 数据预处理 dijkstra算法 输出文件 异常处理 前端搭建 结果呈现 本地测试 web端测试 项目概况 Github项目源代码地址:https://github.com/NewWesternCEO/beijing_subway/ web端直接访问地址:http://54.16

目录

项目概况 数据流分析 文件结构与存储结构 文件结构 存储结构 业务逻辑实现(subwayFunction.py) 数据预处理 dijkstra算法 输出文件 异常处理 前端搭建 结果呈现 本地测试 web端测试 项目概况

Github项目源代码地址:https://github.com/NewWesternCEO/beijing_subway/
web端直接访问地址:http://54.162.90.89:8010

该项目在python3.6环境下开发
若不通过web端进行访问,在下载源码后运行 app.py 代码即可
代码的运行需要先安装好Flask库与numpy库,可以在以下方式中二选一进行安装

numpy库可以通过pip install Numpy进行安装
Flask库可以通过pip install Flask进行安装

在虚拟环境下建议使用pip install -r requirements.txt命令从requirements.txt文件中自动安装所有所需库

数据流分析

由于该工程所需要处理的数据较少,且数据处理过程为实时处理,因此不采用数据库进行存储。


文件结构与存储结构 文件结构

└── SubwayApp
│ └── pycache
│ └── static //项目资源
│ │ └── CSS
│ │ └── images
│ │ └── Js
│ │—— subway.txt //存储的站点信息
│ │—— routine.txt //存储的规划结果
│ │—— names.txt //站点编码对应的名称
│ │—— app.py //Flask 程序
│ │—— manager.py
│ │—— requirements.txt //程序运行所需要的环境
│ │—— subwayFunction.py //必要的业务逻辑模块(diJsktra算法等)

存储结构

在对线路的观察中发现北京市地铁线路的命名并不完全按数字来进行,存在特有的命名,导致不适宜直接在程序中进行处理。

因此首先对各条线路进行编码,将编码后的线路信息存放在names.txt文件中。编码后的结果为

1 地铁一号线2 地铁二号线3 地铁八通线...17 地铁昌平线18 地铁亦庄线19 地铁燕房线20 西郊线21 S1线22 机场线


对于各个站点,则通过

苹果园 1古城 1八角游乐园 1...2号航站楼 22三元桥 22


的形式存储在subway.txt文件中,并不需要通过额外的方式对换乘站点进行标注。

业务逻辑实现(subwayFunction.py)

全局声明

import numpy as npERROR = -1inf = 1e+8
数据预处理


数据预处理主要通过 readData()readnames()initGraph()几个功能模块来实现。
其具体功能为:

readData() 读取目录下的subway.txt文件,返回站点个数、线路数组、以字典的形式存储了不同线路所在的站点(若一个站点为换乘站,即属于多个线路,则在字典中它的键值对数量大于1),同时初始化了collect数组
readnames() 读取目录下的names.txt文件,返回一个存储了编码后的线路与线路名称键值对的字典 initGraph() 根据subway.txt文件构建一个无向图,相邻站点之间的距离均置为1

具体实现:
readData()

'''读取地铁站点信息'''def readData(path):    file = open(path,'r',enCoding='GB2312')    N = 0    lineNum = []    lineDict = {}    nodes = []    collected = []    for each in file:        # print(each)        node,line = each.split(' ')        if not node in nodes:            N += 1            nodes.append(node)            collected.append(False)            # 将线路分门别类        line = eval(line)        if not line in lineNum:            lineDict[line] = []            lineNum.append(line)        lineDict[line].append(node)            print('\n共有 %d 个站点' % N)    file.close()    return N,nodes,collected,lineNum,lineDict

readnames()

def readnames(path):    file = open(path,enCoding='GB2312')    names = {}    for each in file:        line,name = each.split(' ')        name = name.replace('\n','')        names[eval(line)] = name    return names

initGraph()

'''初始化'''def initGraph(path,N,nodes):    graph = np.ones([N,N]) * inf #不可达    preIDx = 0 #IDx表示结点编号    preline = ERROR    file = open(path,enCoding='GB2312')    for each in file:        node,line = each.split(' ')                if preline == ERROR:            preline = eval(line)                        curIDx = nodes.index(node)        if curIDx != preIDx and preline == eval(line):            graph[preIDx][curIDx] = graph[curIDx][preIDx] = 1                preIDx = curIDx                preline = eval(line)                return graph
dijkstra算法

典型的dijkstra算法实现,包含findNextMin()Dijkstra()两个模块。
其思想及具体实现可以参考此处:最短路径:Dijkstra算法

具体实现:
dijkstra

'''Dijkstra算法'''def findNextMin(graph,dist,N):    minNode,mindist = ERROR,inf        for i in range(0,N):        if dist[i] < mindist and collected[i] == False:            mindist = dist[i]            minNode = i        if mindist < inf:        return minNode    else:        return ERROR    def Dijkstra(nodes,startNode,graph,lineDict):    startIDx = nodes.index(startNode)    #endIDx = nodes.index(endNode)        collected[startIDx] = True    dist = np.ones(N) * inf    path = np.ones(N)    for i in range(0,N):        dist[i] = graph[startIDx][i]        if dist[i] < inf:            path[i] = startIDx        else:            path[i] = ERROR                while True:        nextNodeIDx = findNextMin(graph=graph,dist=dist,collected=collected,N=N)        lines1 = getline(nextNodeIDx,lineDict)        if nextNodeIDx == ERROR:            break        collected[nextNodeIDx] = True        for i in range(0,N):            if collected[i] == False and graph[nextNodeIDx][i] < inf:                if dist[nextNodeIDx] + graph[nextNodeIDx][i] < dist[i]:                    dist[i] = dist[nextNodeIDx] + graph[nextNodeIDx][i]                    path[i] = nextNodeIDx    return dist,path
输出文件

输出文件通过 getline()output()两个模块来实现。
其具体功能为:

getline() 输入一个站点,返回一个列表,包含该站点所在的所有线路 output() 根据要求进行输出,并将最终结果写入routine.txt文件中。为便于调试,同时在控制台中进行输出。
对于线路换乘的处理分成了四种情况,造成代码非常冗余且不宜阅读,是可以改进的方向
'''获取站点所在的线路号'''def getline(node,lineDict):    lines = []    for key in lineDict.keys():        if node in lineDict[key]:            lines.append(key)    return lines    '''整理结果并输出文件'''def output(nodes,startline,endNode,path,lineDict,names):    ListOut = []    outputPath = r'./routine.txt'    outputfile = open(outputPath,'w')    tracePath = []    tracePath.append(nodes.index(endNode))    pos = int(path[nodes.index(endNode)])    while pos != ERROR:        tracePath.append(pos)        pos = int(path[pos])    tracePath.reverse()    curline = []    # curline.append(eval(startline)    curline.append(startline)    temp = startline    first = True    print(len(tracePath))    ListOut.append(str(len(tracePath)))    outputfile.write(str(len(tracePath)))    outputfile.write('\r\n')    for each in tracePath:        if first == False:            lines = getline(nodes[each],lineDict)            if len(curline) == 1:                if len(lines) == 1:                    if lines[0] != curline[0]:                        curline[0] = lines[0]                        name = names[curline[0]]                        print(name)                        ListOut.append(name)                        outputfile.write(name)                        outputfile.write('\r\n')                        temp = curline[0]                elif len(lines) >= 2:                    curline = lines            elif len(curline) >= 2:                if len(lines) == 1:                    if lines[0] != temp:                        curline = []                        curline.append(lines[0])                        name = names[curline[0]]                        print(name)                        ListOut.append(name)                        outputfile.write(name)                        outputfile.write('\r\n')                        temp = curline[0]                elif len(lines) >= 2:                    newline = List(set(curline).intersection(lines))[0]                    if newline != temp:                        curline = []                        curline.append(newline)                        name = names[curline[0]]                        print(name)                        ListOut.append(name)                        outputfile.write(name)                        outputfile.write('\r\n')                        temp = curline[0]                    else:                        curline = lines        print(nodes[each])        ListOut.append(nodes[each])        outputfile.write(nodes[each])        outputfile.write('\r\n')        first = False        outputfile.close()    return ListOut
异常处理

用户给与的输入信息在web的限制下已经变得非常有限。

因此可以在调用业务函数之前通过判断来处理异常,在通过所有检查的情况下才将请求交给后台处理。

if startNode == endNode:        flash('出发站与终点站不能相同')        startNode = "error"    elif startline == '' or not eval(startline) in lineNum:        flash("请选择正确的出发线路")        startNode = "error"    elif endline == '' or not eval(endline) in lineNum:        flash("请选择正确的终点线路")        startNode = "error"    elif not startNode in lineDict[eval(startline)]:        flash("请选择正确的出发站")        startNode = "error"    elif not endNode in lineDict[eval(endline)]:        flash("请选择正确的终点站")        startNode = "error"    else:        return redirect(url_for('loadResult'))

在用户尝试提交非法的请求时,会产生如下的提示:



前端搭建

通过Flask与bootstrap结合的方式搭建web页面。

Flask实现对前后端信息的传递与路由的转发 bootstrap实现HTML5中样式的调用 JavaScript实现对线路的监听,从而动态加载某个线路所对应的站点信息
Js具体实现
<script type="text/JavaScript">    //1、用户选哪条线    var startline = document.getElementByID("startline");    var endline = document.getElementByID("endline");    //2、定位到对应的线路集合    var startNode = document.getElementByID("startNode");    var endNode = document.getElementByID("endNode");    //动态-改进    var nodes = {{ lineDict|toJson }}        $(".lineDict").HTML(nodes)    //3、动态的添加标签    function showStart() {        startNode.INNERHTML = "--选择一个起始站--";        var line = startline.value        for (var i in nodes[line]) {            startNode.INNERHTML += "<option>" + nodes[line][i] + "</option>";        }    }    function showEnd() {        endNode.INNERHTML = "--选择一个起始站--";        var line = endline.value        for (var i in nodes[line]) {            endNode.INNERHTML += "<option>" + nodes[line][i] + "</option>";        }    }</script>


随后将页面挂载到 AWS 的 EC2 实例上。
具体的挂载方法可以参考如下两篇博客,虽然博客中使用的是阿里云的服务器,但配置思路与AWS类似。

如何将Flask项目部署在Ubuntu系统的阿里云主机中(详细完整版:上) 如何将Flask项目部署在Ubuntu系统的阿里云主机中(详细完整版:下) 结果呈现 本地测试

输入

'''sample'''startNode = '西直门'endNode = '北京南站'startline = 13

运行结果

@H_416_403@web端测试

输入


输出


同时在本地也会产生routine.txt文件

总结

以上是内存溢出为你收集整理的北京地铁线路规划系统——总结全部内容,希望文章能够帮你解决北京地铁线路规划系统——总结所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1190619.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存