def intervalRunStayTime(self,graph,start,end): """ 不算起点和终点 """ started = False running = 0 stay = 0 former = None for st in self.timetable: if stationEqual(st["zhanming"],start): started = True if not started: continue if former is None: former = st continue running += (st["ddsj"] - former["cfsj"]).seconds thisStay=(st["cfsj"] - st["ddsj"]).seconds if st["zhanming"] not in (start,end): stay += thisStay former = st if stationEqual(st["zhanming"],end): break # print(running, stay) return running, stay
def jointTrain(self,train,former:bool,graph): """ 将train连接到本车次上。 """ if former: for st in reversed(train.timetable): if not graph.stationInLine(st["zhanming"]): continue #非本线站点不处理,以免出错 find = False for node in self.timetable: if stationEqual(st["zhanming"],node["zhanming"],strict=True): find = True break if find: continue self.timetable.insert(0,st) else: for st in train.timetable: if not graph.stationInLine(st["zhanming"]): continue #非本线站点不处理,以免出错 find = False for node in self.timetable: if stationEqual(st["zhanming"],node["zhanming"],strict=True): find = True break if find: continue self.timetable.append(st)
def getInfo(self, fazhan, daozhan) -> dict: for node in self._nodes: if stationEqual(node["fazhan"], fazhan) and stationEqual( node["daozhan"], daozhan): return node if not self._different: for node in self._nodes: if stationEqual(node["daozhan"], fazhan) and stationEqual( node["fazhan"], daozhan): return node return None
def _find_neighbors(self, name: str): neighbors = [] for st in self._nodes: if stationEqual(st["fazhan"], name): neighbors.append(st["daozhan"]) if not self._different: for st in self._nodes: if stationEqual(st['daozhan'], name): neighbors.append(st["fazhan"]) return neighbors
def intervalCount(self,graph,start,end): count = 0 started=False for st in self.timetable: name = st["zhanming"] if stationEqual(name,start): started=True if not started: continue if graph.stationInLine(name): count+=1 if stationEqual(name,end): break return count
def stationBefore(self,st1,st2): """ 返回st1是否在st2之前。 """ findStart = False for st_dict in self.timetable: if stationEqual(st1,st_dict['zhanming']): findStart = True if stationEqual(st2,st_dict['zhanming']): if findStart: return True else: return False return False
def checkStartEnd(self, train): # 返回False,True保留到最后 if self.useStart: for st in self.startStations: if stationEqual(train.sfz, st): break else: return False if self.useEnd: for st in self.endStations: if stationEqual(train.zdz, st): break else: return False return True
def intervalStopCount(self,graph,start,end): count = 0 started = False for st in self.timetable: name = st["zhanming"] if stationEqual(name,start): started = True if not started: continue if graph.stationInLine(name) and (st["cfsj"]-st["ddsj"]).seconds!=0 and\ name not in (start,end): count+=1 if stationEqual(name,end): break return count
def localRunStayTime(self, graph) -> (int, int): """ 计算本线纯运行时间的总和、本线停站时间总和。算法是从本线入图点开始,累加所有区间时分。 2.0版本修改:计算所有【运行线铺画区段】的上述数值。区段包括首末站。不铺画运行线的区段不累计。 """ started = False n = 0 # 即将开始的铺画区段 running = 0 stay = 0 former = None bounds = [] for dct in self.itemInfo(): bounds.append((dct['start'], dct['end'])) if not bounds: return 0, 0 # if self.fullCheci() == 'K1156/7': # print(bounds) for st in self.timetable: if not started and stationEqual( st['zhanming'], bounds[n][0], strict=True): # if self.fullCheci() == 'K8361/4/1': # print("start seted to True",st['zhanming']) started = True if not started: continue if former is None: former = st stay += (st["cfsj"] - st["ddsj"]).seconds continue running += (st["ddsj"] - former["cfsj"]).seconds stay += (st["cfsj"] - st["ddsj"]).seconds # if self.fullCheci() == 'K8361/4/1': # print("train.runStayTime",running,stay,former['zhanming'],st['zhanming'],n) former = st if stationEqual(st['zhanming'], bounds[n][1], strict=True): if n < len(bounds) - 1 and stationEqual( st['zhanming'], bounds[n + 1][0]): # if self.fullCheci() == 'K1156/7': # print("end but go on",st['zhanming']) pass else: started = False former = None n += 1 if n >= len(bounds): break return running, stay
def updateLocalFirst(self,graph): for st in self.timetable: name = st["zhanming"] for station in graph.line.stations: if stationEqual(name,station["zhanming"]): self._localFirst = name return name
def postorderLinked(self,train)->tuple: """ [Train,datetime] or [None,None] 返回后续连接的车次。如果没有后续或者后续没有勾选link,返回None. """ found = False postTrain = None for node in self.nodes(): if node.train() is train: found=True continue if found: if not node.link: return None,None else: postTrain = node.train() break if postTrain is None: return None,None thisEnd = train.destination() postStart = postTrain.departure() if thisEnd is None or postStart is None: return None,None if stationEqual(thisEnd['zhanming'],postStart['zhanming']) and \ self.graph.stationInLine(thisEnd['zhanming']): return postTrain,postStart['cfsj'] return None,None
def preorderLinked(self,train)->tuple: """ [Train,datetime] or [None,None] 返回有Link的前一个车次及其终到时间。如果本车次没有Link,则返回None。 返回的充要条件是符合连线条件。 关于本交路长度的线性算法。 """ preNode = None preTrain = None for node in self.nodes(): if node.train() is train: if preNode is None: return None,None elif not node.link: return None,None else: preTrain = preNode.train() break preNode = node if preTrain is None: return None,None preEnd = preTrain.destination() thisStart = train.departure() if preEnd is None or thisStart is None: return None,None if stationEqual(preEnd['zhanming'],thisStart['zhanming']) and \ self.graph.stationInLine(preEnd['zhanming']): return preTrain,preEnd['ddsj'] return None,None
def stationIndexByName(self,name,strict=False)->int: """ 2.0新增。线性算法。 """ for i,st in enumerate(self.timetable): if stationEqual(st['zhanming'],name): return i return -1
def stationIndex_bf(self,name:str): """ 原来的暴力方法查找序号。分离此函数是为了尝试统计有多少次使用暴力方法。 """ for i, st in enumerate(self.stations): if stationEqual(st["zhanming"], name): return i raise StationNotInLineException(name)
def stationDict(self,name,strict=False): """ 线性算法 """ for st in self.timetable: if stationEqual(st["zhanming"],name,strict): return st return None
def isDownGap(self, st1: str, st2: str): """ 判断给定的区间是否为下行区间 """ s1 = None s2 = None for st in self.stations: if stationEqual(st["zhanming"], st1): s1 = st elif stationEqual(st["zhanming"], st2): s2 = st if s1 is not None and s2 is not None: break try: if s1["licheng"] - s2["licheng"] > 0.0: return False except: pass return True
def onStationDirectionSplited(self, old_name: str, down_name: str, up_name: str): """ 2019.11.28新增。 在将一个站拆成上下行两个站且没有动Line数据时调用。 """ if not self.different(): self.setDifferent(True) for node in self._nodes: if stationEqual(node['fazhan'], old_name, strict=True): if self._line.isDownGap(node['fazhan'], node['daozhan']): node['fazhan'] = down_name else: node['fazhan'] = up_name elif stationEqual(node['daozhan'], old_name, strict=True): if self._line.isDownGap(node['fazhan'], node['daozhan']): node['daozhan'] = down_name else: node['daozhan'] = up_name
def updateLocalLast(self,graph): """ 2019.02.03修改:时间换空间,计算并维护好数据。原函数名: localLast """ for st in reversed(self.timetable): name = st["zhanming"] for station in graph.line.stations: if stationEqual(name,station["zhanming"]): self._localLast = name return name
def gapBetweenStation(self,st1,st2,graph=None)->int: """ 返回两站间的运行时间 :param graph:依赖的线路。不为None表示允许向前后推断邻近站。 :return: seconds:int """ st_dict1,st_dict2 = None,None for dict in self.timetable: if stationEqual(st1,dict['zhanming']): st_dict1 = dict elif stationEqual(st2,dict['zhanming']): st_dict2 = dict print("detect",self.fullCheci(),st1,st2) if st_dict1 is None or st_dict2 is None: if graph is None: raise Exception("No such station gap.",st1,st2) else: ignore_f = [st1,st2] station = st1 while st_dict1 is None: station = graph.adjacentStation(station,ignore_f) print("adjacent found",station,ignore_f) ignore_f.append(station) if station is None: break st_dict1 = self.stationDict(station) print("st_dict1 found",st_dict1) ignore_l = [st1,st2] station = st2 while st_dict2 is None: station = graph.adjacentStation(station,ignore_l) ignore_l.append(station) if station is None: break st_dict2 = self.stationDict(station) if st_dict1 is None or st_dict2 is None: return -1 # no such gap dt = st_dict2["ddsj"]-st_dict1["cfsj"] return dt.seconds
def destination(self) -> dict: """ 如果时刻表最后一个站是终到站,返回。否则返回None。 """ if not self.timetable: return None if not self.zdz: return None dct = self.timetable[-1] if stationEqual(dct['zhanming'], self.zdz): return dct return None
def getInfo(self, fazhan: str, daozhan: str, allow_multi=False): """ allow_multi:是否允许跨区间。 """ for node in self._nodes: if stationEqual(node['fazhan'],fazhan,strict=True) and \ stationEqual(node['daozhan'],daozhan,strict=True): return node for node in self._nodes: if stationEqual(node['fazhan'], fazhan) and stationEqual( node['daozhan'], daozhan): return node if not self._different: for dict in self._nodes: if dict["fazhan"] == daozhan and dict["daozhan"] == fazhan: return dict for node in self._nodes: if stationEqual(node['daozhan'], fazhan) and stationEqual( node['fazhan'], daozhan): return node if allow_multi: return self._multiBFS(fazhan, daozhan) return None
def setStationDeltaTime(self,name:str,ds_int): st_dict = None for st in self.timetable: if stationEqual(st["zhanming"],name): st_dict = st break if st_dict is None: raise Exception("No such station",name) dt = timedelta(days=0,seconds=ds_int) st_dict["ddsj"] += dt st_dict["cfsj"] += dt
def departure(self) -> dict: """ 如果时刻表第一个站是始发站,返回它。否则返回None。 适用于严格要求判断始发站的场景,如交路连接。 """ if not self.timetable: return None if not self.sfz: return None dct = self.timetable[0] if stationEqual(dct['zhanming'], self.sfz): return dct return None
def onStationDirectionSplited(self, old_name: str, down_name: str, up_name: str): """ 2019.11.28添加。 当站名被按上下行拆分时调用。 precondition: Line中站名没有修改。 只需要改名字,不需要插入新的结点。 """ self.resetAllPassed() if not self.different(): self.setDifferent(True, True) print("Ruler::onStationDirectionSplited: 自动设为上下行分设标尺。") for node in self._nodes: if stationEqual(node['fazhan'], old_name, strict=True): if self._line.isDownGap(node['fazhan'], node['daozhan']): node['fazhan'] = down_name else: node['fazhan'] = up_name elif stationEqual(node['daozhan'], old_name, strict=True): if self._line.isDownGap(node['fazhan'], node['daozhan']): node['daozhan'] = down_name else: node['daozhan'] = up_name
def _multiBFS(self, start, end): """ BFS框架搜索从start到end的路径 """ length_dict = {} last_dict = {} #记住每个站路径中的上一个站,方便回溯 queue = [] for w in self._find_neighbors(start): length_dict[w] = self.getInfo(start, w, allow_multi=False)['interval'] last_dict[w] = start queue.append(w) while queue: v = queue.pop(0) if stationEqual(v, end): #找到要找的结点,直接返回 interval = length_dict[v] #区间运行时分 t = last_dict[v] stop = self.getInfo(t, v)['stop'] while last_dict[v] != start: v = last_dict[v] # 现在v的last就是start start = self.getInfo(start, v)['start'] return { 'interval': interval, 'start': start, 'stop': stop, } for w in self._find_neighbors(v): if w not in length_dict.keys(): # w is unreached length_dict[w] = length_dict[v] + self.getInfo( v, w, False)['interval'] last_dict[w] = v queue.append(w) return None
def stationInTimetable(self,name:str,strict=False): return bool(filter(lambda x:stationEqual(name,x,strict), map(lambda x:x['zhanming'],self.timetable)))