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://tudelft.nl/staff/p.knoppers-1">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 StreamInterface; random number stream
51       * @param linkWeight LinkWeight; link weight.
52       * @return RouteSupplier; 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 StreamInterface; stream of random numbers
81           * @param linkWeight 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          /** {@inheritDoc} */
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                         "Could not determine the shortest route from %s to %s via %s.", origin, destination, viaNodes);
149             }
150             return this.shortestRouteCache.get(() -> Try.assign(
151                     () -> origin.getNetwork().getShortestRouteBetween(gtuType, origin, destination, viaNodes, this.linkWeight),
152                     "Could not determine the shortest route from %s to %s via %s.", origin, destination, viaNodes), gtuType,
153                     origin, destination, viaNodes);
154         }
155 
156         /** {@inheritDoc} */
157         @Override
158         public String toString()
159         {
160             return "DefaultRouteGenerator [linkWeight=" + this.linkWeight + "shortestRouteCache=" + this.shortestRouteCache
161                     + "]";
162         }
163     };
164 
165     /**
166      * Returns a route.
167      * @param origin Node; origin
168      * @param destination Node; destination
169      * @param gtuType GtuType; gtu type
170      * @return Route; route
171      */
172     Route getRoute(Node origin, Node destination, GtuType gtuType);
173 }