eclipse-sumo / sumo

Eclipse SUMO is an open source, highly portable, microscopic and continuous traffic simulation package designed to handle large networks. It allows for intermodal simulation including pedestrians and comes with a large set of tools for scenario creation.
https://eclipse.dev/sumo
Eclipse Public License 2.0
2.49k stars 1.41k forks source link

🐛 Trip IDs Not Retained in Routes Generated by RouteSampler #15373

Closed dominique-AR closed 2 weeks ago

dominique-AR commented 3 weeks ago

Hi everyone! 😊 @namdre @bcoueraud87 🐛 ### Issue: Trip IDs Not Retained in Routes Generated by RouteSampler

We are currently working on modeling traffic in Antananarivo and need to export data on duration, route length, and mean speed by TAZ (Traffic Assignment Zone, which corresponds to administrative limits). However, we've encountered an issue that requires assistance.

Steps We've Taken So Far:

CODE: python "C:\Program Files (x86)\Eclipse\Sumo\tools\output\tripinfoByType.py" -t out\statistics\all-tripinfo.xml -a vType -o out\statistics\test.tripinfo.xml

We utilized RouteSampler to integrate count data into the itineraries generated by Duarouter with OD matrix data.

You can view the generated trip info by TAZ:

  1. Working:
<tripinfosByTAZ attribute="duration">
    <odInfo fromTaz="IX" toTaz="VII" count="12" min="778.0" minVeh="4" max="1551.0" maxVeh="13" mean="1122.9166666666667" Q1="979.0" median="1054.0" Q3="1277.0"/>
    <odInfo fromTaz="IX" toTaz="VIII" count="11" min="267.0" minVeh="29" max="1967.0" maxVeh="16" mean="975.4545454545455" Q1="426.0" median="994.0" Q3="1567.0"/>
    <odInfo fromTaz="IX" toTaz="X" count="4" min="834.0" minVeh="37" max="1314.0" maxVeh="33" mean="1039.5" Q1="893.0" median="1117.0" Q3="1314.0"/>
    <!-- more data -->
</tripinfosByTAZ>
  1. Not working:
<tripinfosByTAZ attribute="routeLength">
    <odInfo fromTaz="?" toTaz="?" count="13847" min="537.35" minVeh="m843" max="27479.87" maxVeh="b2083" mean="9864.40448544811" Q1="6418.24" median="9584.33" Q3="12866.71"/>
</tripinfosByTAZ>

🛑 The Problem: The trip IDs are not retained in the routes generated by RouteSampler (e.g., car.rou.xml). This creates a significant issue because when we attempt to generate trip info and perform aggregation with trip info by TAZ, we cannot retrieve the aggregated data by TAZ.

❓ Our Question: Is it possible to modify RouteSampler so that the generated routes retain the trip IDs stored in itineraries (car.trips.xml) and can be used later for generating trip info output and aggregation by assignment zone in trip info by TAZ?

Alternatively, can you recommend other tools or methods to address this issue? 🛠️

Your assistance would be greatly appreciated! We look forward to your guidance on how to resolve this issue.

Thank you for your support! 🙏

dominique-AR commented 3 weeks ago

Additional Information:

`

<trip id="7402" depart="1.27" from="312430385" to="566343980#4" type="car" fromTaz="ZoneCentrale" toTaz="XII" departLane="free" departSpeed="max"/>
...

` >>

  • The format of the route:

-`

</vehicle>`

Chatgpt options 👉

  1. Step 1: Modify the Routes Class to Map Trip IDs

`class Routes: def init(self, routefiles, tripfile=None): self.all = [] self.edgeProbs = defaultdict(zero) self.edgeIDs = {} # This will map route edges to their trip IDs self.withProb = 0

    # Parse the trips.xml file and store trip IDs
    if tripfile:
        for trip in sumolib.xml.parse(tripfile, 'trip'):
            trip_id = trip.getAttribute("id")
            from_edge = trip.getAttribute("from")
            to_edge = trip.getAttribute("to")
            edges = (from_edge, to_edge)
            self.edgeIDs[edges] = trip_id

    for routefile in routefiles:
        warned = False
        for r in sumolib.xml.parse(routefile, ['route', 'walk'], heterogeneous=True):
            if r.edges is None:
                if not warned:
                    print(f"Warning: Ignoring walk in file '{routefile}' because it does not contain edges.", file=sys.stderr)
                    warned = True
                continue
            edges = tuple(r.edges.split())
            self.all.append(edges)
            prob = float(r.getAttributeSecure("probability", 1))
            if r.hasAttribute("probability"):
                self.withProb += 1
                prob = float(r.probability)
            else:
                prob = 1
            if prob <= 0:
                print(f"Warning: route probability must be positive (edges={r.edges})", file=sys.stderr)
                prob = 0
            self.edgeProbs[edges] += prob

    self.unique = sorted(list(self.edgeProbs.keys()))
    self.number = len(self.unique)
    self.edges2index = dict([(e, i) for i, e in enumerate(self.unique)])
    if len(self.unique) == 0:
        print("Error: no input routes loaded", file=sys.stderr)
        sys.exit()
    self.probabilities = np.array([self.edgeProbs[e] for e in self.unique], dtype=np.float64)

`

  1. Step 2: Modify the solveInterval Function to Include Trip IDs `def solveInterval(options, routes, begin, end, intervalPrefix, outf, mismatchf, rng):

    ... (rest of the function remains the same)

    if usedRoutes: outf.write('\n' % (begin, end)) period = (end - begin) / len(usedRoutes) depart = begin routeCounts = getRouteCounts(routes, usedRoutes)

    if options.writeRouteIDs:
        for routeIndex in sorted(set(usedRoutes)):
            edges = routes.unique[routeIndex]
            tripID = routes.edgeIDs.get(edges, None)  # Get the corresponding trip ID
            routeIDComment = f" tripID='{tripID}'" if tripID else ""
            outf.write('    <route id="%s%s%s" edges="%s"/> <!-- %s%s -->\n' % (
                options.prefix, intervalPrefix, routeIndex, ' '.join(edges),
                routeCounts[routeIndex], routeIDComment))
        outf.write('\n')
    
    for i, routeIndex in enumerate(usedRoutes):
        tripID = routes.edgeIDs.get(routes.unique[routeIndex], None)  # Get the corresponding trip ID
        vehID = options.prefix + intervalPrefix + str(i)
        if tripID is not None:
            outf.write('    <vehicle id="%s" depart="%.2f" type="car" tripID="%s">\n' % (
                vehID, depart, tripID))
        else:
            outf.write('    <vehicle id="%s" depart="%.2f" type="car">\n' % (
                vehID, depart))
        outf.write('        <route edges="%s"/>\n' % ' '.join(routes.unique[routeIndex]))
        outf.write('    </vehicle>\n')
        depart += period

    `

  2. Step 3: Ensure trips.xml is Loaded in main `def main(options): rng = np.random.RandomState(options.seed)

    tripfile = "path_to_trips.xml" # Include the correct path to the trips.xml file

    routes = Routes(options.routeFiles, tripfile) `

namdre commented 2 weeks ago

There is no general schema that would preserve all input vehicle ids given to routeSampler.py since the route of any input vehicle may be re-used multiple times (input only gives candidate routes).

However, the core problem of using tripInfoByTAZ.py with meso comes from the fact that tripinfo output in meso does not include information about the departLane and arrivalLane (as it does for a micro simulation). This problem has been solved just now via #7680

If you update to the latest development version (soon to be released as version 1.21.0), you will get more information in meso tripinfo. You can then use tripInfoByTAZ with option --taz-files and it will derive the zone information from the departLane / arrivalLane attribute.