View Javadoc
1   package org.opentrafficsim.road.gtu.strategical;
2   
3   import java.util.ArrayList;
4   import java.util.LinkedHashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import org.djutils.exceptions.Throw;
9   import org.djutils.exceptions.Try;
10  import org.djutils.multikeymap.MultiKeyMap;
11  import org.opentrafficsim.base.OtsRuntimeException;
12  import org.opentrafficsim.core.gtu.GtuType;
13  import org.opentrafficsim.core.math.Draw;
14  import org.opentrafficsim.core.network.Connector;
15  import org.opentrafficsim.core.network.Link;
16  import org.opentrafficsim.core.network.LinkWeight;
17  import org.opentrafficsim.core.network.NetworkException;
18  import org.opentrafficsim.core.network.Node;
19  import org.opentrafficsim.core.network.route.Route;
20  
21  import nl.tudelft.simulation.jstats.streams.StreamInterface;
22  
23  /**
24   * Generates a route by determining one. This class is different from {@code Generator<Route>} in that it has the origin,
25   * destination and GTU type as input.
26   * <p>
27   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
29   * </p>
30   * @author Alexander Verbraeck
31   * @author Peter Knoppers
32   * @author Wouter Schakel
33   */
34  public interface RouteGenerator
35  {
36      /** No route route generator. */
37      RouteGenerator NULL = new RouteGenerator()
38      {
39          @Override
40          public Route getRoute(final Node origin, final Node destination, final GtuType gtuType)
41          {
42              return null;
43          }
44      };
45  
46      /** Cache of default route generators per stream and link-weight. */
47      MultiKeyMap<RouteGenerator> DEFAULT_SUPPLIERS = new MultiKeyMap<>(StreamInterface.class, LinkWeight.class);
48  
49      /**
50       * Returns a default route generator for shortest routes based on the given stream.
51       * @param stream random number stream
52       * @param linkWeight link weight.
53       * @return default route generator for shortest routes based on the given stream
54       */
55      static RouteGenerator getDefaultRouteSupplier(final StreamInterface stream, final LinkWeight linkWeight)
56      {
57          RouteGenerator def = DEFAULT_SUPPLIERS.get(stream, linkWeight);
58          if (def == null)
59          {
60              def = new DefaultRouteGenerator(stream, linkWeight);
61              DEFAULT_SUPPLIERS.put(def, stream, linkWeight);
62          }
63          return def;
64      }
65  
66      /** Shortest route route generator. */
67      class DefaultRouteGenerator implements RouteGenerator
68      {
69          /** Shortest route cache. */
70          private final MultiKeyMap<Route> shortestRouteCache =
71                  new MultiKeyMap<>(GtuType.class, Node.class, Node.class, List.class);
72  
73          /** Link weight. */
74          private final LinkWeight linkWeight;
75  
76          /** Stream of random numbers. */
77          private final StreamInterface stream;
78  
79          /**
80           * Constructor.
81           * @param stream stream of random numbers
82           * @param linkWeight link weight.
83           */
84          public DefaultRouteGenerator(final StreamInterface stream, final LinkWeight linkWeight)
85          {
86              Throw.whenNull(stream, "Stream may not be null.");
87              Throw.whenNull(linkWeight, "Link weight may not be null.");
88              this.stream = stream;
89              this.linkWeight = linkWeight;
90          }
91  
92          @Override
93          public Route getRoute(final Node origin, final Node destination, final GtuType gtuType)
94          {
95              List<Node> viaNodes = new ArrayList<>();
96              double cumulWeight = 0.0;
97              Map<Link, Double> links = new LinkedHashMap<>();
98              boolean directLinkExists = false;
99              for (Link link : destination.getLinks())
100             {
101                 if (link.isConnector() && link instanceof Connector && ((Connector) link).getDemandWeight() > 0.0)
102                 {
103                     // Verify there is a route from origin to this link
104                     List<Node> testViaNode = new ArrayList<>();
105                     Node linkEntryNode = link.getStartNode();
106                     testViaNode.add(linkEntryNode);
107                     try
108                     {
109                         if (origin.getNetwork().getShortestRouteBetween(gtuType, origin, destination, viaNodes,
110                                 this.linkWeight) != null)
111                         {
112                             Double weight = ((Connector) link).getDemandWeight();
113                             links.put(link, weight);
114                             cumulWeight += weight;
115                         }
116                     }
117                     catch (NetworkException e)
118                     {
119                         // ignore this link
120                     }
121                 }
122                 if (link.getStartNode().equals(origin) || link.getEndNode().equals(origin))
123                 {
124                     directLinkExists = true;
125                 }
126             }
127             if (cumulWeight > 0.0 && links.size() > 1 && (!directLinkExists))
128             {
129                 Link via = Draw.drawWeighted(links, this.stream);
130                 if (via.getEndNode().equals(destination))
131                 {
132                     viaNodes.add(via.getStartNode());
133                 }
134                 else if (via.getStartNode().equals(destination))
135                 {
136                     viaNodes.add(via.getEndNode());
137                 }
138                 else
139                 {
140                     viaNodes.add(via.getEndNode());
141                 }
142             }
143             if (!this.linkWeight.isStatic())
144             {
145                 return Try.assign(
146                         () -> origin.getNetwork().getShortestRouteBetween(gtuType, origin, destination, viaNodes,
147                                 this.linkWeight),
148                         OtsRuntimeException.class, "Could not determine the shortest route from %s to %s via %s.", origin,
149                         destination, viaNodes);
150             }
151             return this.shortestRouteCache.get(
152                     () -> Try.assign(
153                             () -> origin.getNetwork().getShortestRouteBetween(gtuType, origin, destination, viaNodes,
154                                     this.linkWeight),
155                             OtsRuntimeException.class, "Could not determine the shortest route from %s to %s via %s.", origin,
156                             destination, viaNodes),
157                     gtuType, origin, destination, viaNodes);
158         }
159 
160         @Override
161         public String toString()
162         {
163             return "DefaultRouteGenerator [linkWeight=" + this.linkWeight + "shortestRouteCache=" + this.shortestRouteCache
164                     + "]";
165         }
166     };
167 
168     /**
169      * Returns a route.
170      * @param origin origin
171      * @param destination destination
172      * @param gtuType gtu type
173      * @return route
174      */
175     Route getRoute(Node origin, Node destination, GtuType gtuType);
176 }