北京地铁线路查询

北京地铁线路查询

项目概述

  项目目的为开发一个北京地铁系统的路径查询系统,用户通过输入出发地和目的地,得到最优的出行方案。源代码:github

  本人选择使用网页形式向用户呈现结果,采用的web框架为:tornado。参考链接:Tornado

  算法上选择了Dijkstra算法,由于站点间的实际距离数据难以获得,构建图时将临近站点距离视为1个单位。参考链接:Dijkstra

运行截图

  出发地和目的地的选择:

   北京地铁线路查询_第1张图片

  返回查找路径结果:

      北京地铁线路查询_第2张图片

 

 

 

 

 

 

 

 

 

 

 

后端实现

  根据需求,所有站点和线路的信息存放在station.txt文件。为方便读取和数据处理,station.txt文件中的格式修改为:

总线路数
线路1 数目1
站点1-1 是否换乘
站点1-2 是否换乘
……
线路2 数目2
站点2-1 是否换乘
站点2-2 是否换乘
……
线路n 数目n
站点n-1 是否换乘
站点n-2 是否换乘
……

  以北京目前拥有的24条地铁线路为例,station.txt的部分截图(0表示为不可换乘站,1表示可换乘站)如下图所示:

   北京地铁线路查询_第3张图片

  处理后的数据存放在字典中:

   北京地铁线路查询_第4张图片

  得到所有站点数据后就需要创建图。在创建图时会发现,若以领接矩阵形式存放数据,会有大量空间浪费,因为换乘站占总站数的比例很小。所以考虑使用领接表效率更高。

  

class GraphAL(Graph):
    def __init__(self,mat=[],unconn=0):
        vnum=len(mat)
        for x in mat:
            if len(x)!=vnum:
                raise ValueError("Argument for 'GraphAL'.")
        self._mat=[Graph._out_edges(mat[i],unconn) for i in range(vnum)]
        self._vnum=vnum
        self._unconn=unconn
    def add_vertex(self):#增加新节点时安排一个新编号
        self._mat.append([])
        self._vnum+=1
        return self._vnum-1
    def add_edge(self,vi,vj,val=1):
        if self._vnum==0:
            raise GraphError("cannot add edge to empty graph")
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        row=self._mat[vi]
        i=0
        while ivj:#原来如果没有到vj的边,退出循环,加入边
                break
            i+=1
        self._mat[vi].insert(i,(vj,val))
    def get_edge(self,vi,vj):
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        for i,val in self._mat[vi]:
            if i==vj:
                return val
        return self._unconn
    def out_edges(self,vi):
        if self._invalid(vi):
            raise GraphError(str(vi)+" is not a valid vertex.")
        return self._mat[vi]

  在后端核心的寻找路径代码上,选择了Dijkstra算法。Dijkstra用于求图中指定两点之间的最短路径,或者是指定一点到其它所有点之间的最短路径,实质上是贪心算法。

  寻找最短路径代码:

def dijkstra_shortest_pathS(graph,v0,endpos):
    vnum=0
    for i in pathss.keys():
        vnum+=1
    assert 0<=v0#长为vnum的表记录路径
    count=0
    cands=PrioQueue([(0,v0,v0)])#求解最短路径的候选边集记录在优先队列cands中(p,v,v')v0经过v到v'的最短路径长度为p,根据p的大小排序,保证选到最近的未知距离顶点
    while count#取路径最短顶点
        if paths[vmin]:#如果这个点的最短路径已知,则跳过
            continue
        paths[vmin]=(u,plen)#新确定最短路径并记录
        for v in graph[vmin]:#遍历经过新顶点组的路径
            if not paths[v]:#如果还不知道最短路径的顶点的路径,则记录
                cands.enqueue((plen+1,vmin,v))
        count+=1
    return paths

前端实现

  前端上选择了轻量级的tornado框架,该框架的优点有:

  • 抗负载性强
  • 异步非阻塞IO处理方式
  • 优异的处理性能,不依赖多进程/多线程,一定程度上解决C10K问题
  • WSGI全栈替代产品,推荐同时使用其web框架和HTTP服务器

  该项目设计了两个页面,一个是主页面,即查询页面(testUI.html),另一个是结果展示页面(index.html)。

  前后端交互的整体思路为:

  1. 后端向前端发送线路及站点信息,前端通过多选框形式展示给用户
  2. 前端将用户选择的出发地和目的地发送给后端处理
  3. 后端将得到的线路查询结果发送给前端,前端渲染后返回给用户

  框架上开启了8080端口,使用者可运行app.py后在chrome输入127.0.0.1:8080 进行操作。代码如下:

from tornado import web,ioloop,httpserver
import findPath
data=findPath.data
RESULT='test'

class MainPageHandler(web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('testUI.html',data=data,result='')
    def post(self, *args, **kwargs):
        startPos = self.get_argument('startPos')
        endPos = self.get_argument('endPos')
        if startPos and endPos:
            global RESULT
            RESULT = findPath.getPath(startPos, endPos)
            self.redirect('/index')
        else:
            self.write('内容不能为空')

class ShowResultHandler(web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('index.html',result=RESULT)
    def post(self, *args, **kwargs):
        self.redirect('/')

settings = {
    'template_path':'templates',
    'static_path':'static'
}

application = web.Application([
            (r"/", MainPageHandler),
            (r"/index", ShowResultHandler),
        ], **settings)

if __name__ == '__main__':
    http_server = httpserver.HTTPServer(application)
    http_server.listen(8080)
    ioloop.IOLoop.current().start()

  testUI.html代码如下:





    
    
    
    
    
    
    Beijing Metro
    
    
    
    
    
    
    
    
    
    
    
  
  
    

  index.html代码如下:




    
    
    
    
    
    
    Beijing Metro
    
    
    
    
    
    
    
    
    
    
    
  

    

总结

  本项目的后端用python实现,核心代码采用Dijkstra算法,web框架选择了tornado。

  该项目让我简单复习了图的构建和Dijkstra算法,感受到了在开发一个项目时,程序封装对后期维护和调试的重要性。

  前端页面上使用了下拉框以避免用户的非法输入,同时也提高了使用体验。由于之前没有web前端开发的经验,通过自学后受益匪浅。

你可能感兴趣的:(北京地铁线路查询)