Path: blob/main/tools/import/matsim/matsim_importPlans.py
169679 views
#!/usr/bin/env python1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2# Copyright (C) 2010-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 matsim_importPlans.py14# @author Jakob Erdmann15# @author Camillo Fillinger16# @date 2019-09-271718"""19Import person plans from MATSim20"""21from __future__ import absolute_import22from __future__ import print_function2324import os25import sys26import subprocess27from xml.sax import saxutils2829try:30from StringIO import StringIO31except ImportError:32from io import StringIO3334if 'SUMO_HOME' in os.environ:35sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))36sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools', 'route'))37import sumolib # noqa38import sort_routes394041def get_options(args=None):42op = sumolib.options.ArgumentParser()43op.add_argument("-p", "--plan-file", category="input", required=True,44type=op.data_file, help="MATSim plan file (mandatory)")45op.add_argument("-o", "--output-file", category="input", required=True,46type=op.route_file, help="SUMO output route file (mandatory)")47op.add_argument("-n", "--net-file", category="input", type=op.net_file,48help="SUMO net file (mandatory for --repair or --remove-loops)")49op.add_argument("--vehicles-only", action="store_true", category="processing",50default=False, help="Import only vehicles instead of persons")51op.add_argument("--no-bikes", action="store_true", category="processing",52default=False, help="do not import bike trips")53op.add_argument("--no-rides", action="store_true", category="processing",54default=False, help="do not import ride trips")55op.add_argument("--repair", action="store_true", category="processing",56default=False, help="Repair routes after import (needs a SUMO net)")57op.add_argument("--remove-loops", action="store_true", category="processing",58default=False, help="Remove loops in routes after import (needs a SUMO net)")59op.add_argument("--prefer-coordinate", action="store_true", category="processing",60default=False, help="Use coordinates instead of link ids if both are given")61op.add_argument("--default-start", metavar="TIME", default="0:0:0", category="time",62help="default start time for the first activity")63op.add_argument("--default-end", metavar="TIME", default="24:0:0", category="time",64help="default end time for the last activity")65op.add_argument("--default-dur", metavar="TIME", default="1:0:0", category="time",66help="default duration for activities")67op.add_argument("-v", "--verbose", action="store_true", category="processing",68default=False, help="tell me what you are doing")69options = op.parse_args(args)7071if not options.net_file and (options.repair or options.remove_loops):72op.print_help()73sys.exit()7475return options767778def getLocation(options, activity, attr, prj=None):79if activity.link and (not options.prefer_coordinate or not activity.x):80return '%s="%s"' % (attr, activity.link)81elif activity.x and activity.y:82if prj:83lon, lat = prj(activity.x, activity.y, inverse=True)84if attr == "edge":85return 'lon="%s" lat="%s"' % (lon, lat)86else:87return '%sLonLat="%s,%s"' % (attr, lon, lat)88else:89if attr == "edge":90return 'x="%s" y="%s"' % (activity.x, activity.y)91else:92return '%sXY="%s,%s"' % (attr, activity.x, activity.y)93else:94return None959697def skipLeg(options, leg):98# walk and public transport are not relevant99if leg.mode.endswith("walk") or leg.mode == "pt":100return True101if options.no_rides and leg.mode == "ride":102return True103if options.no_bikes and leg.mode in ("bike", "bicycle"):104return True105return False106107108def writeLeg(outf, options, idveh, leg, start, end, types):109""" Write the vehicles and trips. """110if skipLeg(options, leg):111return112depart = leg.dep_time if options.vehicles_only else "triggered"113mode = ' type="%s"' % leg.mode if leg.mode else ""114if leg.mode:115types.add(leg.mode)116if leg.route is None or leg.route[0].distance == "NaN" or leg.mode in ("bike", "bicycle"):117outf.write(' <trip id="%s" depart="%s" %s %s%s/>\n'118% (idveh, depart, start, end, mode))119else:120outf.write(' <vehicle id="%s" depart="%s"%s>\n' % (idveh, depart, mode))121outf.write(' <route edges="%s"/>\n' % (leg.route[0].getText()))122outf.write(' </vehicle>\n')123124125def main(options):126127prj = None128for attributes in sumolib.xml.parse(options.plan_file, 'attributes'):129for attribute in attributes.attribute:130if attribute.attr_name == "coordinateReferenceSystem":131projName = attribute.getText().strip()132try:133import pyproj134try:135prj = pyproj.Proj("EPSG:31468" if projName == "GK4" else projName)136except pyproj.exceptions.CRSError as e:137print("Warning: Could not interpret coordinate system '%s' (%s)" % (138projName, e), file=sys.stderr)139except ImportError:140print("Warning: install pyproj to support input with coordinates", file=sys.stderr)141142persons = [] # (depart, xmlsnippet)143types = set()144for index, person in enumerate(sumolib.xml.parse(options.plan_file, 'person')):145outf = StringIO()146vehIndex = 0147plan = person.plan[0]148if len(plan.getChildList()) == 0:149continue150firstAct = plan.getChildList()[0]151depart = firstAct.start_time152if depart is None:153depart = options.default_start154attributes = person.attributes[0] if person.attributes else None155156# write vehicles157vehicleslist = []158untillist = []159durations = []160lastAct = None161lastLeg = None162for item in plan.getChildList():163leg = None164idveh = "%s_%s" % (person.id, vehIndex)165if "act" in item.name: # act or activity166if lastLeg is not None:167leg = lastLeg168leg.dep_time = lastAct.end_time169writeLeg(outf, options, idveh, leg,170getLocation(options, lastAct, "from", prj),171getLocation(options, item, "to", prj), types)172lastLeg = None173# set missing end_time:174if not item.end_time:175if item.start_time and item.max_dur:176item.end_time = sumolib.miscutils.humanReadableTime(177sumolib.miscutils.parseTime(item.start_time) +178sumolib.miscutils.parseTime(item.max_dur)179)180elif item.start_time:181item.end_time = sumolib.miscutils.humanReadableTime(182sumolib.miscutils.parseTime(item.start_time) +183sumolib.miscutils.parseTime(options.default_dur)184)185elif item.max_dur and leg:186item.end_time = sumolib.miscutils.humanReadableTime(187sumolib.miscutils.parseTime(leg.dep_time) +188sumolib.miscutils.parseTime(options.default_dur) +189sumolib.miscutils.parseTime(item.max_dur)190)191lastAct = item192durations.append(item.max_dur if item.max_dur else options.default_dur)193if item.name == "leg":194if item.route is None:195lastLeg = item196else:197leg = item198start = 'from="%s"' % leg.route[0].start_link199end = 'to="%s"' % leg.route[0].end_link200writeLeg(outf, options, idveh, leg, start, end, types)201if leg:202untillist.append(leg.dep_time)203vehicleslist.append(idveh if leg.mode != "pt" else "pt")204vehIndex += 1205untillist.append(lastAct.end_time if lastAct.end_time else options.default_end)206207# write person208if not options.vehicles_only:209vehIndex = 0210actIndex = 0211outf.write(' <person id="%s" depart="%s">\n' % (person.id, depart))212if attributes is not None:213for attr in attributes.attribute:214outf.write(' <param key="%s" value=%s/>\n' % (215attr.attr_name, saxutils.quoteattr(attr.getText())))216217lastLeg = None218for item in plan.getChildList():219if "act" in item.name: # act or activity220end = getLocation(options, item, "to", prj)221if lastLeg is not None:222if lastLeg.mode == "non_network_walk":223pass224# outf.write(' <transship to="%s"/>\n' % item.link)225elif lastLeg.mode in ("walk", "transit_walk"):226outf.write(' <walk %s/>\n' % end)227elif lastLeg.mode == "pt":228outf.write(' <personTrip modes="public" %s/>\n' % end)229elif skipLeg(options, lastLeg):230outf.write(' <!-- ride lines="%s" %s mode="%s"/-->\n' % (231vehicleslist[vehIndex], end, lastLeg.mode))232else:233outf.write(' <ride lines="%s" %s/>\n' % (vehicleslist[vehIndex], end))234vehIndex += 1235until = untillist[vehIndex]236if until:237timing = 'until="%s"' % until238else:239timing = 'duration="%s"' % durations[actIndex]240outf.write(' <stop %s %s actType="%s"/>\n' %241(getLocation(options, item, "edge", prj), timing, item.type))242actIndex += 1243if item.name == "leg":244lastLeg = item245outf.write(' </person>\n')246persons.append((sumolib.miscutils.parseTime(depart), index, outf.getvalue()))247248persons.sort()249tmpout = options.output_file + ".tmp"250with open(tmpout, 'w') as outf:251sumolib.writeXMLHeader(outf, root="routes")252for t in sorted(types):253vClass = ""254if t in ("bike", "bicycle"):255vClass = ' vClass="bicycle"'256outf.write(' <vType id="%s"%s/>\n' % (t, vClass))257for depart, index, xml in persons:258outf.write(xml)259outf.write('</routes>\n')260outf.close()261# use duarouter for sorting when --vehicles-only is set262if options.repair or options.remove_loops:263args = ["-n", options.net_file, "-a", tmpout, "-o", options.output_file]264args += ["--repair"] if options.repair else []265args += ["--remove-loops"] if options.remove_loops else []266args += ["--write-trips", "--write-trips.geo"] if options.prefer_coordinate else []267subprocess.call([sumolib.checkBinary("duarouter")] + args)268elif options.vehicles_only:269sort_routes.main([tmpout, '-o', options.output_file])270else:271os.rename(tmpout, options.output_file)272273274if __name__ == "__main__":275main(get_options())276277278