Path: blob/main/tools/contributed/sumopy/coremodules/demand/turnflows.py
169689 views
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo1# Copyright (C) 2016-2025 German Aerospace Center (DLR) and others.2# SUMOPy module3# Copyright (C) 2012-2021 University of Bologna - DICAM4# This program and the accompanying materials are made available under the5# terms of the Eclipse Public License 2.0 which is available at6# https://www.eclipse.org/legal/epl-2.0/7# This Source Code may also be made available under the following Secondary8# Licenses when the conditions for such availability set forth in the Eclipse9# Public License 2.0 are satisfied: GNU General Public License, version 210# or later which is available at11# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html12# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later1314# @file turnflows.py15# @author Joerg Schweizer16# @date 20121718import os19import string20import numpy as np21from numpy import random22import agilepy.lib_base.classman as cm23import agilepy.lib_base.arrayman as am24import agilepy.lib_base.xmlman as xm25from agilepy.lib_base.geometry import *26#from coremodules.modules_common import *27from coremodules.network.network import SumoIdsConf, MODES28from agilepy.lib_base.processes import Process, P, call, CmlMixin29import demandbase as db303132class Flows(am.ArrayObjman):33def __init__(self, ident, parent, edges, **kwargs):34self._init_objman(ident, parent=parent,35name='Flows',36info='Contains the number of vehicles which start on the given edge during a certain time interval.',37version=0.2,38xmltag=('flows', 'flow', None), **kwargs)3940self._init_attributes(edges)4142def _init_attributes(self, edges=None):43if edges is None:44# recover edges from already initialized45edges = self.ids_edge.get_linktab()4647if self.get_version() < 0.1:48# update attrs from previous49# IdsArrayConf not yet modifiable interactively, despite perm = 'rw',!!!50self.ids_edge.set_perm('rw')51if hasattr(self, 'func_delete_row'):52self.func_make_row._is_returnval = False53self.func_delete_row._is_returnval = False5455self.add_col(am.IdsArrayConf('ids_edge', edges,56groupnames=['state'],57name='Edge ID',58perm='rw',59info='Edge ID of flow.',60xmltag='from',61))6263self.add_col(am.ArrayConf('flows', 0,64dtype=np.int32,65groupnames=['state'],66perm='rw',67name='Flow',68info='Absolute number of vehicles which start on the given edge during a certain time interval.',69xmltag='number',70))7172self.add(cm.FuncConf('func_make_row', 'on_add_row', None,73groupnames=['rowfunctions', '_private'],74name='New flow.',75info='Add a new flow.',76is_returnval=False,77))7879self.add(cm.FuncConf('func_delete_row', 'on_del_row', None,80groupnames=['rowfunctions', '_private'],81name='Del flow',82info='Delete flow.',83is_returnval=False,84))8586def on_del_row(self, id_row=None):87if id_row is not None:88# print 'on_del_row', id_row89self.del_row(id_row)9091def on_add_row(self, id_row=None):92if len(self) > 0:93# copy previous94flow_last = self.get_row(self.get_ids()[-1])95self.add_row(**flow_last)96else:97self.add_row(self.suggest_id())9899def _init_constants(self):100#self.edgeweights_orig = None101#self.edgeweights_dest = None102pass103104def add_flow(self, id_edge, flow):105# print 'Flows.add_flows'106return self.add_row(ids_edge=id_edge,107flows=flow)108109def get_edges(self):110return self.ids_edge.get_linktab()111112def get_demand(self):113return self.parent.parent.parent114115def count_left(self, counter):116"""117Counts the number of vehicles leaving the edge.118The counter is an array where the index equals the edge ID119and the value represens the number of entered vehicles per edge.120"""121ids_flow = self.get_ids()122counter[self.ids_edge[ids_flow]] += self.flows[ids_flow]123124def export_xml(self, fd, vtype, id_flow, share=1.0, indent=2):125"""126Generates a line for each edge with a flow.127128id_flow is the flow count and is used to generate a unique flow id129130131"""132# TODO: better handling of mode and vtypes, distributions133# DONE with share134# <flow id="0" from="edge0" to="edge1" type= "vType" number="100"/>135ids_eges = []136ids_sumoeges = self.get_edges().ids_sumo137i = 0138# print 'Flows.export_xml vtypes,id_flow,len(self)',vtype,id_flow,len(self)139for id_edge, flow in zip(self.ids_edge.get_value(), share*self.flows.get_value()):140# print ' id_edge,flow',id_edge,flow141ids_eges.append(id_edge)142id_flow += 1143fd.write(xm.start('flow'+xm.num('id', id_flow), indent))144fd.write(xm.num('from', ids_sumoeges[id_edge]))145fd.write(xm.num('type', vtype))146fd.write(xm.num('number', int(flow)))147fd.write(xm.stopit())148i += 1149# print ' return ids_eges, id_flow',ids_eges, id_flow150return ids_eges, id_flow151152153class Turns(am.ArrayObjman):154def __init__(self, ident, parent, edges, **kwargs):155self._init_objman(ident, parent=parent,156name='Turn flows',157info='The table contains turn probabilities between two edges during a given time interval.',158version=0.1,159xmltag=('odtrips', 'odtrip', None), **kwargs)160161self._init_attributes(edges)162163def _init_attributes(self, edges=None):164if edges is None:165# recover edges from already initialized166edges = self.ids_fromedge.get_linktab()167168if self.get_version() < 0.1:169# update attrs from previous170# IdsArrayConf not yet modifiable interactively, despite perm = 'rw',!!!171self.ids_fromedge.set_perm('rw')172self.ids_toedge.set_perm('rw')173if hasattr(self, 'func_delete_row'):174self.func_make_row._is_returnval = False175self.func_delete_row._is_returnval = False176177self.add_col(am.IdsArrayConf('ids_fromedge', edges,178groupnames=['state'],179name='Edge ID from',180info='Edge ID where turn starts.',181xmltag='fromEdge',182))183184self.add_col(am.IdsArrayConf('ids_toedge', edges,185groupnames=['state'],186name='Edge ID to',187info='Edge ID where turn ends.',188xmltag='toEdge',189))190191self.add_col(am.ArrayConf('flows', 0,192dtype=np.int32,193groupnames=['state'],194perm='rw',195name='Flow',196info='Absolute number of vehicles which pass from "fromedge" to "toedge" during a certain time interval.',197xmltag='number',198))199200self.add_col(am.ArrayConf('probabilities', 0.0,201dtype=np.float32,202groupnames=['state'],203perm='rw',204name='Probab.',205info='Probability to make a turn between "Edge ID from" and "Edge ID to" and .',206xmltag='probability',207))208209self.add(cm.FuncConf('func_make_row', 'on_add_row', None,210groupnames=['rowfunctions', '_private'],211name='New turns',212info='Add a new turnflow.',213is_returnval=False,214))215216self.add(cm.FuncConf('func_delete_row', 'on_del_row', None,217groupnames=['rowfunctions', '_private'],218name='Del turns',219info='Delete turns.',220is_returnval=False,221))222223def on_del_row(self, id_row=None):224if id_row is not None:225# print 'on_del_row', id_row226self.del_row(id_row)227228def on_add_row(self, id_row=None):229if len(self) > 0:230# copy previous231flow_last = self.get_row(self.get_ids()[-1])232self.add_row(**flow_last)233else:234self.add_row(self.suggest_id())235236def _init_constants(self):237#self.edgeweights_orig = None238#self.edgeweights_dest = None239pass240241def normalize_turnprobabilities(self):242"""243Makes sure that sum of turn probabilities from an edge equals 1.244"""245# TODO: there is a quicker way with picking a set and then select246# and sum with vectors247#ids_source = set(self.ids_fromedge.get_values())248249flows_total = {}250for _id in self.get_ids():251id_fromedge = self.ids_fromedge[_id]252if not flows_total.has_key(id_fromedge):253flows_total[id_fromedge] = 0.0254flows_total[id_fromedge] += self.flows[_id]255256for _id in self.get_ids():257if flows_total[self.ids_fromedge[_id]] > 0:258self.probabilities[_id] = self.flows[_id] / flows_total[self.ids_fromedge[_id]]259260def add_turn(self, id_fromedge, id_toedge, flow):261print 'Turns.add_turn'262return self.add_row(ids_fromedge=id_fromedge,263ids_toedge=id_toedge,264flows=flow,265)266267def get_edges(self):268return self.ids_fromedge.get_linktab()269270def count_entered(self, counter):271"""272Counts the number of vehicles entered in an edge.273The counter is an array where the index equals the edge ID274and the value represens the number of entered vehicles per edge.275"""276ids_turns = self.get_ids()277counter[self.ids_toedge[ids_turns]] += self.flows[ids_turns]278279def export_xml(self, fd, indent=0):280# <edgeRelations>281# <interval begin="0" end="3600">282# <edgeRelation from="myEdge0" to="myEdge1" probability="0.2"/>283# <edgeRelation from="myEdge0" to="myEdge2" probability="0.7"/>284# <edgeRelation from="myEdge0" to="myEdge3" probability="0.1"/>285286# ... any other edges ...287288# </interval>289290# ... some further intervals ...291292# </edgeRelations>293294fromedge_to_turnprobs = {}295for _id in self.get_ids():296id_fromedge = self.ids_fromedge[_id]297if not fromedge_to_turnprobs.has_key(id_fromedge):298fromedge_to_turnprobs[id_fromedge] = []299fromedge_to_turnprobs[id_fromedge].append((self.ids_toedge[_id], self.probabilities[_id]))300301ids_sumoeges = self.get_edges().ids_sumo302303fd.write(xm.begin('edgeRelations', indent))304for id_fromedge in fromedge_to_turnprobs.keys():305306for id_toedge, turnprob in fromedge_to_turnprobs[id_fromedge]:307fd.write(xm.start('edgeRelation', indent+2))308fd.write(xm.num('from', ids_sumoeges[id_fromedge]))309fd.write(xm.num('to', ids_sumoeges[id_toedge]))310fd.write(xm.num('probability', turnprob))311fd.write(xm.stopit())312313fd.write(xm.end('edgeRelations', indent))314315316class TurnflowModes(am.ArrayObjman):317def __init__(self, ident, parent, modes, edges, **kwargs):318self._init_objman(ident, parent=parent,319name='Mode OD tables',320info='Contains for each transport mode an OD trip table.',321xmltag=('modesods', 'modeods', 'ids_mode'), **kwargs)322323print 'TurnflowModes.__init__', modes324self.add_col(am.IdsArrayConf('ids_mode', modes,325groupnames=['state'],326choices=MODES,327name='ID mode',328is_index=True,329#xmltag = 'vClass',330info='ID of transport mode.',331))332print ' self.ids_mode.is_index', self.ids_mode.is_index()333334self.add_col(cm.ObjsConf('flowtables',335groupnames=['state'],336name='Flows',337info='Flow generation per edge for a specific mode.',338))339340self.add_col(cm.ObjsConf('turntables',341groupnames=['state'],342name='Turns',343info='Turn probabilities between edges for a specific mode.',344))345346self.add(cm.ObjConf(edges, is_child=False, groups=['_private']))347348# def generate_trips(self, demand, time_start, time_end,**kwargs):349# for id_od_mode in self.get_ids():350# self.odtrips[id_od_mode].generate_trips( demand, time_start, time_end, self.ids_mode[id_od_mode],**kwargs)351352# def generate_odflows(self, odflowtab, time_start, time_end,**kwargs):353# for id_od_mode in self.get_ids():354# self.odtrips[id_od_mode].generate_odflows( odflowtab, time_start, time_end, self.ids_mode[id_od_mode],**kwargs)355356def get_demand(self):357return self.parent.parent358359def normalize_turnprobabilities(self):360"""361Makes sure that sum of turn probabilities from an edge equals 1.362"""363for _id in self.get_ids():364self.turntables[_id].normalize_turnprobabilities()365366def add_mode(self, id_mode):367id_tf_modes = self.add_row(ids_mode=id_mode)368print ' add_mode', id_mode, id_tf_modes369370flows = Flows((self.flowtables.attrname, id_tf_modes), self, self.edges.get_value())371self.flowtables[id_tf_modes] = flows372373turns = Turns((self.turntables.attrname, id_tf_modes), self, self.edges.get_value())374self.turntables[id_tf_modes] = turns375376return id_tf_modes377378def add_flow(self, id_mode, id_edge, flow):379"""380Sets a demand flows between from-Edge and toEdge pairs for mode where flows is a dictionary381with (fromEdgeID,toEdgeID) pair as key and number of trips as values.382"""383print 'TurnflowModes.add_turnflows', id_mode # ,flows,kwargs384print ' self.ids_mode.is_index()', self.ids_mode.is_index()385if self.ids_mode.has_index(id_mode):386id_tf_modes = self.ids_mode.get_id_from_index(id_mode)387else:388id_tf_modes = self.add_mode(id_mode)389self.flowtables[id_tf_modes].add_flow(id_edge, flow)390return self.flowtables[id_tf_modes]391392def add_turn(self, id_mode, id_fromedge, id_toedge, turnflow):393"""394Sets turn probability between from-edge and to-edge.395"""396print 'TurnflowModes.add_turn', id_mode # ,turnflow,kwargs397# if scale!=1.0:398# for od in odm.iterkeys():399# odm[od] *= scale400# if not self.contains_key(mode):401# self._initTurnflowsMode(mode)402if self.ids_mode.has_index(id_mode):403id_tf_modes = self.ids_mode.get_id_from_index(id_mode)404else:405id_tf_modes = self.add_mode(id_mode)406407self.turntables[id_tf_modes].add_turn(id_fromedge, id_toedge, turnflow)408return self.turntables[id_tf_modes]409410def export_flows_xml(self, fd, id_mode, id_flow=0, indent=0):411"""412Export flow data of desired mode to xml file.413Returns list with edge IDs with non zero flows and a flow ID counter.414"""415print 'TurnflowModes.export_flows_xml id_mode, id_flow', id_mode, id_flow, self.ids_mode.has_index(id_mode)416ids_sourceedge = []417418if not self.ids_mode.has_index(id_mode):419return ids_sourceedge, id_flow420421# get vtypes for specified mode422vtypes, shares = self.get_demand().vtypes.select_by_mode(423id_mode, is_sumoid=True, is_share=True)424425# for vtype in vtypes:426# print ' test vtype',vtype427# print ' _index_to_id', self.get_demand().vtypes.ids_sumo._index_to_id428# print ' id_vtype',self.get_demand().vtypes.ids_sumo.get_id_from_index(vtype)429430# if len(vtypes) > 0:# any vehicles found for this mode?431# # TODO: can we put some distributen here?432# vtype = vtypes[0]433for vtype, share in zip(vtypes, shares):434print ' write flows for vtype', vtype, share435if self.ids_mode.has_index(id_mode):436id_tf_modes = self.ids_mode.get_id_from_index(id_mode)437ids_sourceedge, id_flow = self.flowtables[id_tf_modes].export_xml(438fd, vtype, id_flow, share=share, indent=indent)439440# print ' return ids_sourceedge, id_flow',ids_sourceedge, id_flow441return ids_sourceedge, id_flow442443def export_turns_xml(self, fd, id_mode, indent=0):444"""445Export flow data of desired mode to xml file.446Returns list with edge IDs with non zero flows and a flow ID counter.447"""448print 'TurnflowModes.export_turns_xml'449450if self.ids_mode.has_index(id_mode):451id_tf_modes = self.ids_mode.get_id_from_index(id_mode)452self.turntables[id_tf_modes].export_xml(fd, indent=indent)453454def count_entered(self, counter):455"""456Counts the number of vehicles entered in an edge.457The counter is an array where the index equals the edge ID458and the value represens the number of entered vehicles per edge.459"""460for id_tf_modes in self.get_ids():461self.turntables[id_tf_modes].count_entered(counter)462463464class Turnflows(am.ArrayObjman):465def __init__(self, ident, demand, net, **kwargs):466self._init_objman(ident, parent=demand, # = demand467name='Turnflow Demand',468info='Contains flows and turn probailities for different modes and time intervals. Here demand data is ordered by time intervals.',469xmltag=('turnflows', 'interval', None), **kwargs)470471self.add_col(am.ArrayConf('times_start', 0,472groupnames=['state'],473perm='rw',474name='Start time',475unit='s',476info='Start time of interval in seconds (no fractional seconds).',477xmltag='t_start',478))479480self.add_col(am.ArrayConf('times_end', 3600,481groupnames=['state'],482perm='rw',483name='End time',484unit='s',485info='End time of interval in seconds (no fractional seconds).',486xmltag='t_end',487))488489self.add_col(cm.ObjsConf('turnflowmodes',490groupnames=['state'],491is_save=True,492name='Turnflows by modes',493info='Turnflow transport demand for all transport modes within the respective time interval.',494))495#self.add( cm.ObjConf( Sinkzones('sinkzones', self, demand.get_scenario().net.edges) ))496497def get_demand(self):498return self.parent499500def normalize_turnprobabilities(self):501"""502Makes sure that sum of turn probabilities from an edge equals 1.503"""504print 'Turnflows.normalize_turnprobabilities'505# for turnflowmode in self.turnflowmodes.get_value():506# turnflowmode.normalize_turnprobabilities() # no! it's a dict!!507# print ' ',self.turnflowmodes.get_value()508for _id in self.get_ids():509self.turnflowmodes[_id].normalize_turnprobabilities()510511def clear_turnflows(self):512self.clear()513514def add_flow(self, t_start, t_end, id_mode, id_edge, flow):515516# print 'turnflows.add_flow', t_start, t_end, id_mode, id_edge, flow517ids_inter = self.select_ids((self.times_start.get_value() == t_start) & (self.times_end.get_value() == t_end))518if len(ids_inter) == 0:519520id_inter = self.add_row(times_start=t_start, times_end=t_end,)521# print ' create new',id_inter522tfmodes = TurnflowModes((self.turnflowmodes.attrname, id_inter),523self, self.get_net().modes, self.get_net().edges)524525# NO!! odmodes = OdModes( ('ODMs for modes', id_inter), parent = self, modes = self.get_net().modes, zones = self.get_zones())526self.turnflowmodes[id_inter] = tfmodes527528flows = tfmodes.add_flow(id_mode, id_edge, flow)529530else:531532# there should be only one demand table found for a certain interval533id_inter = ids_inter[0]534# print ' use',id_inter535flows = self.turnflowmodes[id_inter].add_flow(id_mode, id_edge, flow)536return flows537538def add_turn(self, t_start, t_end, id_mode, id_fromedge, id_toedge, turnflow):539540# print 'turnflows.add_turnflows',t_start, t_end,id_mode,id_fromedge, id_toedge, turnprob541ids_inter = self.select_ids((self.times_start.get_value() == t_start) & (self.times_end.get_value() == t_end))542if len(ids_inter) == 0:543544id_inter = self.add_row(times_start=t_start, times_end=t_end,)545# print ' create new',id_inter546tfmodes = TurnflowModes((self.turnflowmodes.attrname, id_inter),547self, self.get_net().modes, self.get_net().edges)548549# NO!! odmodes = OdModes( ('ODMs for modes', id_inter), parent = self, modes = self.get_net().modes, zones = self.get_zones())550self.turnflowmodes[id_inter] = tfmodes551552turns = tfmodes.add_turn(id_mode, id_fromedge, id_toedge, turnflow)553554else:555556# there should be only one demand table found for a certain interval557id_inter = ids_inter[0]558# print ' use',id_inter559turns = self.turnflowmodes[id_inter].add_turn(id_mode, id_fromedge, id_toedge, turnflow)560return turns561562def get_net(self):563return self.parent.get_scenario().net564565def get_edges(self):566return self.get_net().edges567568def get_modes(self):569ids_mode = set()570for id_inter in self.get_ids():571ids_mode.update(self.turnflowmodes[id_inter].ids_mode.value)572return list(ids_mode) # self.get_net().modes573574def get_sinkedges(self):575zones = self.parent.get_scenario().landuse.zones576ids_sinkedges = set()577ids_sinkzone = zones.select_ids(zones.ids_landusetype.get_value() == 7)578for ids_edge in zones.ids_edges_inside[ids_sinkzone]:579ids_sinkedges.update(ids_edge)580#sinkedges = zones.ids_edges_orig.get_value().tolist()581# print 'get_sinkedges',sinkedges582# print ' sinkedges',np.array(sinkedges,np.object)583return ids_sinkedges584585def export_flows_and_turns(self, flowfilepath, turnsfilepath, id_mode, indent=0):586"""587Create the flow file and turn ratios file for a specific mode.588In the SUMOpy tunflow data structure, each mode has its own589flow and turnratio data.590"""591print '\n\n'+79*'_'592print 'export_flows_and_turns id_mode=', id_mode, 'ids_vtype=', self.parent.vtypes.select_by_mode(id_mode)593print ' write flows', flowfilepath594fd = open(flowfilepath, 'w')595fd.write(xm.begin('flows', indent))596597# write all possible vtypes for this mode598self.parent.vtypes.write_xml(fd, indent=indent,599ids=self.parent.vtypes.select_by_mode(id_mode),600is_print_begin_end=False)601602id_flow = 0603ids_allsourceedges = []604time_start_min = +np.inf605time_end_max = -np.inf606for id_inter in self.get_ids():607time_start = self.times_start[id_inter]608time_end = self.times_end[id_inter]609fd.write(xm.begin('interval'+xm.num('begin', time_start)+xm.num('end', time_end), indent+2))610ids_sourceedge, id_flow = self.turnflowmodes[id_inter].export_flows_xml(fd, id_mode, id_flow, indent+4)611# print ' got ids_sourceedge, id_flow',ids_sourceedge, id_flow612ids_allsourceedges += ids_sourceedge613614if len(ids_sourceedge) > 0:615# print ' extend total time interval only for intervals with flow'616if time_start < time_start_min:617time_start_min = time_start618619if time_end > time_end_max:620time_end_max = time_end621622fd.write(xm.end('interval', indent+2))623624fd.write(xm.end('flows', indent))625fd.close()626627# print ' write turndefs', turnsfilepath628fd = open(turnsfilepath, 'w')629fd.write(xm.begin('turns', indent))630631for id_inter in self.get_ids():632time_start = self.times_start[id_inter]633time_end = self.times_end[id_inter]634fd.write(xm.begin('interval'+xm.num('begin', time_start)+xm.num('end', time_end), indent+2))635self.turnflowmodes[id_inter].export_turns_xml(fd, id_mode, indent+4)636fd.write(xm.end('interval', indent+2))637638# take sink edges from sink zones639ids_sinkedge = self.get_sinkedges() # it's a set640# ...and remove source edges, otherwise vehicle will be inserted and641# immediately removed642# print ' ids_sinkedge',ids_sinkedge643# print ' ids_allsourceedges',ids_allsourceedges644ids_sinkedge = ids_sinkedge.difference(ids_allsourceedges)645646ids_sumoedge = self.get_edges().ids_sumo647# print ' determined sink edges',list(ids_sinkedge)648if len(ids_sinkedge) > 0:649fd.write(xm.start('sink'))650fd.write(xm.arr('edges', ids_sumoedge[list(ids_sinkedge)]))651fd.write(xm.stopit())652653fd.write(xm.end('turns', indent))654fd.close()655if len(ids_allsourceedges) == 0:656time_start_min = 0657time_end_max = 0658659return time_start_min, time_end_max660661def estimate_entered(self):662"""663Estimates the entered number of vehicles for each edge664generated by turnflow definitions. Bases are the only the665turnflows, not the generated flows (which are not entering an edge).666667returns ids_edge and entered_vec668"""669670counter = np.zeros(np.max(self.get_edges().get_ids())+1, int)671672for id_inter in self.get_ids():673self.turnflowmodes[id_inter].count_entered(counter)674675ids_edge = np.flatnonzero(counter)676entered_vec = counter[ids_edge].copy()677return ids_edge, entered_vec678679def turnflows_to_routes(self, is_clear_trips=True, is_export_network=True,680is_make_probabilities=True, cmloptions=None,):681# jtrrouter --flow-files=<FLOW_DEFS>682# --turn-ratio-files=<TURN_DEFINITIONS> --net-file=<SUMO_NET> \683# --output-file=MySUMORoutes.rou.xml --begin <UINT> --end <UINT>684685if is_make_probabilities:686self.normalize_turnprobabilities()687688scenario = self.parent.get_scenario()689if cmloptions is None:690cmloptions = '-v --max-edges-factor 1 --seed 23423 --repair --ignore-vclasses false --ignore-errors --turn-defaults 5,90,5'691692trips = scenario.demand.trips693if is_clear_trips:694# clear all current trips = routes695trips.clear_trips()696697rootfilepath = scenario.get_rootfilepath()698netfilepath = scenario.net.get_filepath()699flowfilepath = rootfilepath+'.flow.xml'700turnfilepath = rootfilepath+'.turn.xml'701702routefilepath = trips.get_routefilepath()703704# first generate xml for net705if is_export_network:706scenario.net.export_netxml()707708ids_mode = self.get_modes()709print 'turnflows_to_routes', ids_mode # scenario.net.modes.get_ids()710print ' cmloptions', cmloptions711712# route for all modes and read in routes713for id_mode in ids_mode:714# write flow and turns xml file for this mode715time_start, time_end = self.export_flows_and_turns(flowfilepath, turnfilepath, id_mode)716print ' time_start, time_end =', time_start, time_end717718if time_end > time_start: # means there exist some flows for this mode719cmd = 'jtrrouter --route-files=%s --turn-ratio-files=%s --net-file=%s --output-file=%s --begin %s --end %s %s'\720% (P+flowfilepath+P,721P+turnfilepath+P,722P+netfilepath+P,723P+routefilepath+P,724time_start,725time_end,726cmloptions,727)728# print '\n Starting command:',cmd729if call(cmd):730if os.path.isfile(routefilepath):731trips.import_routes_xml(routefilepath, is_generate_ids=True)732os.remove(routefilepath)733734else:735print 'jtrroute: no flows generated for id_mode', id_mode736737# self.simfiles.set_modified_data('rou',True)738# self.simfiles.set_modified_data('trip',True)739# trips and routes are not yet saved!!740741742class TurnflowRouter(db.TripoptionMixin, CmlMixin, Process):743def __init__(self, turnflows, logger=None, **kwargs):744745self._init_common('turnflowrouter', name='Turnflow Router',746parent=turnflows,747logger=logger,748info='Generates routes from turnflow database using the JTR router.',749)750751self.init_cml('') # pass no commad to generate options only752753attrsman = self.get_attrsman()754755self.add_option('turnratio_defaults', '30,50,20',756groupnames=['options'],757cml='--turn-defaults',758name='Default turn ratios',759info='Default turn definition. Comma separated string means: "percent right, percent straight, percent left".',760)761762self.add_option('max-edges-factor', 2.0,763groupnames=['options'],764cml='--max-edges-factor',765name='Maximum edge factor',766info='Routes are cut off when the route edges to net edges ratio is larger than this factor.',767)768769self.add_option('is_internal_links', False,770groupnames=['options'],771cml='--no-internal-links',772name='Disable internal links',773info='Disable (junction) internal links.',774)775776self.add_option('is_randomize_flows', True,777groupnames=['options'],778cml='--randomize-flows',779name='Randomize flows',780info='generate random departure times for flow input.',781)782783self.add_option('is_ignore_vclasses', False,784groupnames=['options'],785cml='--ignore-vclasses',786name='Ignore mode restrictions',787info='Ignore mode restrictions of network edges.',788)789790self.add_option('is_remove_loops', True,791groupnames=['options'],792cml='--remove-loops',793name='Remove loops',794info='emove loops within the route; Remove turnarounds at start and end of the route.',795)796797self.add_option('is_remove_loops', True,798groupnames=['options'],799cml='--remove-loops',800name='Remove loops',801info='Remove loops within the route; Remove turnarounds at start and end of the route.',802)803804self.add_option('is_weights_interpolate', True,805groupnames=['options'],806cml='--weights.interpolate',807name='Interpolate edge weights',808info='Interpolate edge weights at interval boundaries.',809)810811self.add_option('weights_minor_penalty', 1.5,812groupnames=['options'],813cml='--weights.minor-penalty',814name='Minor link panelty',815info='Apply the given time penalty when computing routing costs for minor-link internal lanes.',816)817818self.add_option('n_routing_threads', 0,819groupnames=['options'],820cml='--routing-threads',821name='Number of parallel threads',822info='ATTENTION: Numbers greater than 0 may cause errors!. The number of parallel execution threads used for routing.',823)824self.add_option('seed', 1,825groupnames=['options'],826cml='--seed',827name='Random seed',828info='Initialises the random number generator with the given value.',829)830831self.add_option('is_repair', True,832groupnames=['options'],833cml='--repair',834name='Repair routes',835info='Tries to correct a false route.',836)837838self.add_option('is_ignore_errors', True,839groupnames=['options'],840cml='--ignore-errors',841name='Ignore errors',842info="""Continue routing, even if errors occur.843This option is recommended to avoid abortion if no sink zones are.""",844)845846self.is_export_network = attrsman.add(am.AttrConf('is_export_network', True,847groupnames=['options'],848perm='rw',849name='Export network',850info='Export network before routing.',851))852853self.is_clear_trips = attrsman.add(am.AttrConf('is_clear_trips', True,854groupnames=['options'],855perm='rw',856name='Clear trips',857info='Clear all trips in current trips database before routing.',858))859860self.add_posoptions()861self.add_laneoptions()862self.add_speedoptions()863# self.add_option('turnratiofilepath', turnratiofilepath,864# groupnames = ['_private'],#865# cml = '--turn-ratio-files',866# perm='r',867# name = 'Net file',868# wildcards = 'Net XML files (*.net.xml)|*.net.xml',869# metatype = 'filepath',870# info = 'SUMO Net file in XML format.',871# )872873def do(self):874print 'do'875cml = self.get_cml(is_without_command=True) # only options, not the command #876print ' cml=', cml877self.parent.turnflows_to_routes(is_clear_trips=self.is_clear_trips,878is_export_network=self.is_export_network,879is_make_probabilities=True,880cmloptions=cml)881return True882883884class TurnflowImporter(Process):885def __init__(self, turnflows, rootname=None, rootdirpath=None, tffilepath=None,886logger=None, **kwargs):887888self._init_common('turnflowimporter', name='Turnflow Importer',889parent=turnflows,890logger=logger,891info='Reads and imports turnflow data from different file formates.',892)893894self._edges = turnflows.get_edges()895self._net = self._edges.get_parent()896897if rootname is None:898rootname = self._net.parent.get_rootfilename()899900if rootdirpath is None:901if self._net.parent is not None:902rootdirpath = self._net.parent.get_workdirpath()903else:904rootdirpath = os.getcwd()905906if tffilepath is None:907tffilepath = os.path.join(rootdirpath, rootname+'.net.xml')908909attrsman = self.get_attrsman()910911self.t_start = attrsman.add(am.AttrConf('t_start', 0,912groupnames=['options'],913perm='rw',914name='Start time',915unit='s',916info='Start time of interval',917))918919self.t_end = attrsman.add(am.AttrConf('t_end', 3600,920groupnames=['options'],921perm='rw',922name='End time',923unit='s',924info='End time of interval',925))926927# here we get currently available vehicle classes not vehicle type928# specific vehicle type within a class will be generated later929self.id_mode = attrsman.add(am.AttrConf('id_mode', MODES['passenger'],930groupnames=['options'],931choices=turnflows.parent.vtypes.get_modechoices(),932name='ID mode',933info='ID of transport mode.',934))935936self.tffilepath = attrsman.add(am.AttrConf('tffilepath', tffilepath,937groupnames=['options'], # this will make it show up in the dialog938perm='rw',939name='Turnflow file',940wildcards="Turnflows CSV files (*.csv)|*.csv|CSV files (*.txt)|*.txt|All files (*.*)|*.*",941metatype='filepath',942info='CSV file with turnflow information for the specific mode and time interval.',943))944945def update_params(self):946"""947Make all parameters consistent.948example: used by import OSM to calculate/update number of tiles949from process dialog950"""951pass952#self.workdirpath = os.path.dirname(self.netfilepath)953#bn = os.path.basename(self.netfilepath).split('.')954# if len(bn)>0:955# self.rootname = bn[0]956957def do(self):958# self.update_params()959if os.path.isfile(self.tffilepath):960ids_sumoedge_notexist, pairs_sumoedge_unconnected = self.import_pat_csv()961return (len(ids_sumoedge_notexist) == 0) & (len(pairs_sumoedge_unconnected) == 0)962963def import_pat_csv(self, sep=","):964965f = open(self.tffilepath, 'r')966# self.attrs.print_attrs()967turnflows = self.parent968edges = turnflows.get_edges()969ids_edge_sumo = edges.ids_sumo970971ids_sumoedge_notexist = []972pairs_sumoedge_unconnected = []973974print 'import_pat_csv', self.tffilepath975i_line = 1976for line in f.readlines():977cols = line.split(sep)978# print ' cols=',cols979if len(cols) >= 2:980id_fromedge_sumo = cols[0].strip()981if not ids_edge_sumo.has_index(id_fromedge_sumo):982ids_sumoedge_notexist.append(id_fromedge_sumo)983else:984id_fromedge = ids_edge_sumo.get_id_from_index(id_fromedge_sumo)985986if cols[1].strip() != '':987flow = int(string.atof(cols[1].strip()))988# print ' id_fromedge,flow',id_fromedge,flow989if flow > 0:990turnflows.add_flow(self.t_start, self.t_end, self.id_mode, id_fromedge, flow)991992if len(cols) >= 4:993for i in range(2, len(cols), 2):994id_toedge_sumo = cols[i].strip()995if not ids_edge_sumo.has_index(id_toedge_sumo):996ids_sumoedge_notexist.append(id_toedge_sumo)997else:998id_toedge = ids_edge_sumo.get_id_from_index(id_toedge_sumo)999if not (id_toedge in edges.get_outgoing(id_fromedge)):1000pairs_sumoedge_unconnected.append((id_fromedge_sumo, id_toedge_sumo))1001else:1002if cols[i+1].strip() != '':1003turnflow = int(string.atof(cols[i+1].strip()))1004turnflows.add_turn(self.t_start, self.t_end, self.id_mode,1005id_fromedge, id_toedge, turnflow)10061007else:1008print 'WARNING: inconsistent row in line %d, file %s' % (i_line, self.tffilepath)1009i_line += 11010f.close()1011if len(ids_sumoedge_notexist) > 0:1012print 'WARNING: inexistant edge IDs:', ids_sumoedge_notexist1013if len(pairs_sumoedge_unconnected) > 0:1014print 'WARNING: unconnected edge pairs:', pairs_sumoedge_unconnected10151016return ids_sumoedge_notexist, pairs_sumoedge_unconnected101710181019