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