Path: blob/main/tools/import/visum/visum_convertRoutes.py
169679 views
#!/usr/bin/env python1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2# Copyright (C) 2009-2025 German Aerospace Center (DLR) and others.3# This program and the accompanying materials are made available under the4# terms of the Eclipse Public License 2.0 which is available at5# https://www.eclipse.org/legal/epl-2.0/6# This Source Code may also be made available under the following Secondary7# Licenses when the conditions for such availability set forth in the Eclipse8# Public License 2.0 are satisfied: GNU General Public License, version 29# or later which is available at10# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later1213# @file visum_convertRoutes.py14# @author Daniel Krajzewicz15# @author Michael Behrisch16# @date 2007-02-21171819from __future__ import print_function20from __future__ import absolute_import21import os22import sys23import random24if "SUMO_HOME" in os.environ:25sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))26import sumolib # noqa272829class Statistics:30found = 031foundN = 032missing = 033missingN = 0343536stats = Statistics()37routes = []383940def addRouteChecking(route, id, count, ok):41route = route.strip()42if route != "":43if ok:44if options.distribution and routes:45distID = routes[0][0][:routes[0][0].rfind("_")]46if distID != id[:id.rfind("_")]:47sum = 048r_max = (None, 0, None)49for r in routes:50sum += r[1]51if r_max[1] < r[1]:52r_max = r53if sum < options.cutoff:54del routes[:]55routes.append((r_max[0], sum, r_max[2]))56if len(routes) > 1:57fdo.write(' <routeDistribution id="%s">\n' % distID)58else:59routes[0] = (distID, routes[0][1], routes[0][2])60for r in routes:61if net2:62trace = []63for e in route.split():64edge = net.getEdge(e)65numSteps = int(edge.getLength() / options.step)66for p in range(numSteps):67trace.append(68sumolib.geomhelper.positionAtShapeOffset(edge.getShape(), p * options.step))69path = sumolib.route.mapTrace(70trace, net2, options.delta)71r = (r[0], r[1], " ".join(path))72fdo.write(73' <route id="%s" probability="%s" edges="%s"/>\n' % r)74if len(routes) > 1:75fdo.write(' </routeDistribution>\n')76del routes[:]77routes.append((id, count, route))78stats.found += 179stats.foundN += count80else:81stats.missing += 182stats.missingN += count838485def sorter(idx):86def t(i, j):87if i[idx] < j[idx]:88return -189elif i[idx] > j[idx]:90return 191else:92return 0939495# initialise96op = sumolib.options.ArgumentParser()97op.add_option("-n", "--net-file", dest="netfile", category="input", required=True, type=op.net_file,98help="SUMO net file to work with")99op.add_option("-r", "--visum-routes", dest="routes", category="input", required=True, type=op.file,100help="The VISUM-routes files to parse")101op.add_option("-o", "--output", category="output", type=op.file, required=True,102help="Name of the file to write")103op.add_option("-b", "--begin", category="time", type=op.time,104help="The begin time of the routes to generate", default=0)105op.add_option("-e", "--end", category="time", type=op.time,106help="The end time (+1) of the routes to generate", default=3600)107op.add_option("-p", "--prefix", category="input",108help="ID prefix to use")109op.add_option("-t", "--type", category="input",110help="The type to use for vehicles")111op.add_option("-u", "--uniform",112help="Whether departures shall be distributed uniform in each interval", action="store_true",113default=False)114op.add_option("-l", "--timeline", category="input",115help="Percentages over a day")116op.add_option("-a", "--tabs", action="store_true",117default=False, help="tab separated route file")118op.add_option("-v", "--verbose", action="store_true",119default=False, help="tell me what you are doing")120op.add_option("-2", "--net2", category="input", type=op.net_file,121help="immediately match routes to a second network", metavar="FILE")122op.add_option("-s", "--step", default=10, category="input",123type=float, help="distance between successive trace points")124op.add_option("-d", "--delta", default=1, category="input",125type=float, help="maximum distance between edge and trace points when matching " +126" to the second net")127op.add_option("-i", "--distribution", action="store_true",128default=False, help="write route distributions only")129op.add_option("--cutoff", category="input",130help="Keep only one route when less than CUTOFF vehicles drive the OD", type=int,131default=0)132133# parse options134options = op.parse_args()135136if options.verbose:137print("Reading net...")138net = sumolib.net.readNet(options.netfile)139net2 = None140if options.net2:141net.move(-net.getLocationOffset()[0], -net.getLocationOffset()[1])142net2 = sumolib.net.readNet(options.net2)143net2.move(-net2.getLocationOffset()[0], -net2.getLocationOffset()[1])144145# initialise nodes/edge map146emap = {}147for e in net._edges:148if e._from._id not in emap:149emap[e._from._id] = {}150if e._to._id not in emap[e._from._id]:151emap[e._from._id][e._to._id] = e._id152153# fill with read values154if options.verbose:155print("Reading routes...")156separator = "\t" if options.tabs else ";"157parse = False158ok = True159route = ""160id = ""161attributes = []162count = 0163no = 0164fd = open(options.routes)165fdo = open(options.output, "w")166fdo.write("<routes>\n")167for idx, line in enumerate(fd):168if options.verbose and idx % 10000 == 0:169sys.stdout.write("%s lines read\r" % "{:,}".format(idx))170if line.find("$") == 0 or line.find("*") == 0 or line.find(separator) < 0:171parse = False172addRouteChecking(route, id, count, ok)173if parse:174values = line.strip('\n\r').split(separator)175amap = {}176for i in range(0, len(attributes)):177amap[attributes[i]] = values[i]178if amap["origzoneno"] != "":179# route begin (not the route)180addRouteChecking(route, id, count, ok)181id = amap["origzoneno"] + "_" + \182amap["destzoneno"] + "_" + amap["pathindex"]183count = float(amap["prtpath\\vol(ap)"])184route = " "185ok = True186else:187if not ok:188continue189fromnode = amap["fromnodeno"]190tonode = amap["tonodeno"]191link = amap["linkno"]192if fromnode not in emap:193if no != 0:194print("Missing from-node '" + fromnode + "'; skipping")195ok = False196continue197if tonode not in emap[fromnode]:198if no != 0:199print("No connection between from-node '" +200fromnode + "' and to-node '" + tonode + "'; skipping")201ok = False202continue203edge = emap[fromnode][tonode]204if link != edge and link != edge[1:]:205if no != 0:206print("Mismatching edge '" + link + "' (from '" +207fromnode + "', to '" + tonode + "'); skipping")208ok = False209continue210route = route + edge + " "211212if line.find("$PRTPATHLINK:") == 0 or line.find("$IVTEILWEG:") == 0:213attributes = line[line.find(":") + 1:].strip().lower().split(separator)214for i in range(0, len(attributes)):215if attributes[i] == "qbeznr":216attributes[i] = "origzoneno"217if attributes[i] == "zbeznr":218attributes[i] = "destzoneno"219if attributes[i] == "iv-weg\\bel(ap)":220attributes[i] = "prtpath\\vol(ap)"221if attributes[i] == "wegind":222attributes[i] = "pathindex"223if attributes[i] == "vonknotnr":224attributes[i] = "fromnodeno"225if attributes[i] == "nachknotnr":226attributes[i] = "tonodeno"227if attributes[i] == "strnr":228attributes[i] = "linkno"229parse = True230231addRouteChecking(route, id, count, ok)232fd.close()233234if options.verbose:235print(" %s routes found (%s vehs)" % (stats.found, stats.foundN))236print(" %s routes missing (%s vehs)" % (stats.missing, stats.missingN))237238if options.distribution:239if routes:240distID = routes[0][0][:routes[0][0].rfind("_")]241fdo.write(' <routeDistribution id="%s">\n' % distID)242for r in routes:243fdo.write(244' <route id="%s" probability="%s" edges="%s"/>\n' % r)245fdo.write(' </routeDistribution>\n')246fdo.write("</routes>\n")247fdo.close()248exit()249250timeline = None251# apply timeline252if options.timeline:253timeline = []254nNo = 0255vals = options.timeline.split(",")256sum = 0257for v in vals:258timeline.append(float(v))259sum += float(v)260if len(timeline) != 24:261print("The timeline must have 24 entries")262sys.exit()263nRoutes = []264265# convert to vehicles266if options.verbose:267print("Generating vehicles...")268emissions = []269begin = int(options.begin)270end = int(options.end)271272if not timeline:273for r in routes:274for i in range(0, int(r[1])):275if options.uniform:276t = float(begin) + float(end - begin) / float(r[1]) * float(i)277else:278t = float(begin) + float(end - begin) * random.random()279emissions.append((int(t), r[0] + "__" + str(i), r[2]))280else:281for r in routes:282left = 0.283tbeg = 0284j = 0285for t in timeline:286fno = (float(r[1]) + left) * t / 100.287no = int(fno)288left += fno - no289if left >= 1.:290left -= 1291no += 1292for i in range(0, no):293if options.uniform:294t = tbeg + float(3600) / float(r[1]) * float(i)295else:296t = tbeg + float(3600) * random.random()297emissions.append((int(t), r[0] + "__" + str(j), r[2]))298j = j + 1299nNo += no300tbeg += 3600301if options.verbose:302print(" %s vehicles after applying timeline" % nNo)303304305# sort emissions306if options.verbose:307print("Sorting routes...")308emissions.sort(sorter(0))309310# save emissions311if options.verbose:312print("Writing routes...")313for emission in emissions:314fdo.write(' <vehicle id="')315if options.prefix:316fdo.write(options.prefix + "_")317fdo.write(emission[1] + '" depart="' + str(emission[0]) + '"')318if options.type:319fdo.write(' type="' + options.type + '"')320fdo.write('><route edges="' + emission[2] + '"/></vehicle>\n')321fdo.write("</routes>\n")322fdo.close()323if options.verbose:324print(" %s vehicles written" % len(emissions))325326327