View Javadoc
1   package org.opentrafficsim.road.gtu.strategical.route;
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.GTUDirectionality;
12  import org.opentrafficsim.core.gtu.GTUType;
13  import org.opentrafficsim.core.math.Draw;
14  import org.opentrafficsim.core.network.Link;
15  import org.opentrafficsim.core.network.NetworkException;
16  import org.opentrafficsim.core.network.Node;
17  import org.opentrafficsim.core.network.route.Route;
18  import org.opentrafficsim.road.network.lane.CrossSectionLink;
19  
20  import nl.tudelft.simulation.jstats.streams.StreamInterface;
21  
22  /**
23   * Generates a route by determining one. This class is different from {@code RouteGenerator} in that it has the origin,
24   * destination and GTU type as input.
25   * <p>
26   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
28   * <p>
29   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 24 mrt. 2018 <br>
30   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
32   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
33   */
34  public interface RouteGeneratorOD
35  {
36      /** No route route generator. */
37      RouteGeneratorODrategical/route/RouteGeneratorOD.html#RouteGeneratorOD">RouteGeneratorOD NULL = new RouteGeneratorOD()
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. */
47      Map<StreamInterface, RouteGeneratorOD> DEFAULT_MAP = new LinkedHashMap<>();
48  
49      /**
50       * Returns a default route generator for shortest routes based on the given stream.
51       * @param stream StreamInterface; random number stream
52       * @return RouteSupplier; default route generator for shortest routes based on the given stream
53       */
54      static RouteGeneratorOD getDefaultRouteSupplier(final StreamInterface stream)
55      {
56          RouteGeneratorOD def = DEFAULT_MAP.get(stream);
57          if (def == null)
58          {
59              def = new DefaultRouteGenerator(stream);
60              DEFAULT_MAP.put(stream, def);
61          }
62          return def;
63      }
64  
65      /** Shortest route route generator. */
66      class DefaultRouteGenerator implements RouteGeneratorOD
67      {
68          /** Shortest route cache. */
69          private MultiKeyMap<Route> shortestRouteCache = new MultiKeyMap<>(GTUType.class, Node.class, Node.class, List.class);
70  
71          /** Stream of random numbers. */
72          private final StreamInterface stream;
73  
74          /**
75           * Constructor.
76           * @param stream StreamInterface; stream of random numbers
77           */
78          public DefaultRouteGenerator(final StreamInterface stream)
79          {
80              Throw.whenNull(stream, "Stream may not be null.");
81              this.stream = stream;
82          }
83  
84          @Override
85          public Route getRoute(final Node origin, final Node destination, final GTUType gtuType)
86          {
87              List<Node> viaNodes = new ArrayList<>();
88              double cumulWeight = 0.0;
89              List<Double> weights = new ArrayList<>();
90              Map<Link, Double> links = new LinkedHashMap<>();
91              boolean directLinkExists = false;
92              for (Link link : destination.getLinks())
93              {
94                  GTUDirectionality direction =
95                          link.getEndNode().equals(destination) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
96                  if (link.getLinkType().isConnector() && link.getDirectionality(gtuType).permits(direction)
97                          && link instanceof CrossSectionLink./../org/opentrafficsim/road/network/lane/CrossSectionLink.html#CrossSectionLink">CrossSectionLink && ((CrossSectionLink) link).getDemandWeight() != null)
98                  {
99                      // Verify there is a route from origin to this link
100                     List<Node> testViaNode = new ArrayList<>();
101                     Node linkEntryNode = direction.isPlus() ? link.getStartNode() : link.getEndNode();
102                     testViaNode.add(linkEntryNode);
103                     try
104                     {
105                         if (origin.getNetwork().getShortestRouteBetween(gtuType, origin, destination, viaNodes) != null)
106                         {
107                             Double weight = ((CrossSectionLink) link).getDemandWeight();
108                             weights.add(weight);
109                             links.put(link, weight);
110                             cumulWeight += weight;
111                         }
112                         else
113                         {
114                             System.out.println("No route from origin to link; NOT including link " + link);
115                         }
116                     }
117                     catch (NetworkException e)
118                     {
119                         e.printStackTrace();
120                     }
121                 }
122                 if (link.getDirectionality(gtuType).permits(direction)
123                         && (link.getStartNode().equals(origin) || link.getEndNode().equals(origin)))
124                 {
125                     directLinkExists = true;
126                 }
127             }
128             if (cumulWeight > 0.0 && links.size() > 1 && (!directLinkExists))
129             {
130                 System.out.println("Need to select access point to destination from " + links.size() + " options:");
131                 for (Link link : links.keySet())
132                 {
133                     System.out.println(" " + link);
134                 }
135                 Link via = Draw.drawWeighted(links, this.stream);
136                 // System.out.println("selected via " + via);
137                 if (via.getEndNode().equals(destination))
138                 {
139                     // System.out
140                     // .println("using start node to force use of randomly selected access point to destination centroid");
141                     viaNodes.add(via.getStartNode());
142                 }
143                 else if (via.getStartNode().equals(destination))
144                 {
145                     // System.out.println("using end node to force use of randomly selected access point to destination
146                     // centroid");
147                     viaNodes.add(via.getEndNode());
148                 }
149                 else
150                 {
151                     // System.out.println("using end node (could also have used start node) to force use of randomly "
152                     // + "selected access point to destination centroid");
153                     viaNodes.add(via.getEndNode());
154                 }
155                 if (viaNodes.size() > 0 && viaNodes.get(0).getId().startsWith("Centroid "))
156                 {
157                     System.out.println("oops:   via node is a centroid");
158                 }
159                 System.out.println("Selected via node(s) " + viaNodes);
160             }
161             // XXX make silent, as the higher level method should draw another destination if the route does not exist 
162             return this.shortestRouteCache.get(
163                     () -> Try.assign(() -> origin.getNetwork().getShortestRouteBetween(gtuType, origin, destination, viaNodes),
164                             "Could not determine the shortest route from %s to %s via %s.", origin, destination, viaNodes),
165                     gtuType, origin, destination, viaNodes);
166         }
167 
168         /** {@inheritDoc} */
169         @Override
170         public String toString()
171         {
172             return "ShortestRouteGTUCharacteristicsGeneratorOD [shortestRouteCache=" + this.shortestRouteCache + "]";
173         }
174     };
175 
176     /**
177      * Returns a route.
178      * @param origin Node; origin
179      * @param destination Node; destination
180      * @param gtuType GTUType; gtu type
181      * @return Route; route
182      */
183     Route getRoute(Node origin, Node destination, GTUType gtuType);
184 }