Route.java

  1. package org.opentrafficsim.core.network.route;

  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.LinkedHashSet;
  5. import java.util.List;
  6. import java.util.Objects;
  7. import java.util.Set;

  8. import org.djutils.base.Identifiable;
  9. import org.djutils.logger.CategoryLogger;
  10. import org.opentrafficsim.core.gtu.GtuType;
  11. import org.opentrafficsim.core.network.Link;
  12. import org.opentrafficsim.core.network.NetworkException;
  13. import org.opentrafficsim.core.network.Node;

  14. /**
  15.  * A route is defined as a series of nodes. Each pair of consecutive nodes should have a link valid for the given direction and
  16.  * GTU type.
  17.  * <p>
  18.  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  19.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  20.  * </p>
  21.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  22.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  23.  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  24.  */
  25. public class Route implements Serializable, Identifiable
  26. {
  27.     /** */
  28.     private static final long serialVersionUID = 20221910L;

  29.     /** The nodes of the route. */
  30.     private final List<Node> nodes;

  31.     /** The nodes of the route as a Set for quick containsNode() method. */
  32.     private final Set<Node> nodeSet = new LinkedHashSet<>();

  33.     /** Name of the route. */
  34.     private final String id;

  35.     /** The GtuType for which this is a route. */
  36.     private final GtuType gtuType;

  37.     /**
  38.      * Create an empty route for the given GtuType.
  39.      * @param id String; the name of the route
  40.      * @param gtuType GtuType; the GtuType for which this is a route
  41.      */
  42.     public Route(final String id, final GtuType gtuType)
  43.     {
  44.         this.nodes = new ArrayList<>();
  45.         this.id = id;
  46.         this.gtuType = gtuType;
  47.     }

  48.     /**
  49.      * Create a route based on an initial list of nodes. <br>
  50.      * This constructor makes a defensive copy of the provided List.
  51.      * @param id String; the name of the route.
  52.      * @param gtuType GtuType; the GtuType for which this is a route
  53.      * @param nodes List&lt;Node&gt;; the initial list of nodes.
  54.      * @throws NetworkException if intermediate nodes are missing in the route.
  55.      */
  56.     public Route(final String id, final GtuType gtuType, final List<Node> nodes) throws NetworkException
  57.     {
  58.         this.id = id;
  59.         this.nodes = new ArrayList<>(nodes); // defensive copy
  60.         this.nodeSet.addAll(nodes);
  61.         verify();
  62.         this.gtuType = gtuType;
  63.         Node fromNode = null;
  64.         for (Node toNode : getNodes())
  65.         {
  66.             if (null != fromNode)
  67.             {
  68.                 if (!fromNode.isConnectedTo(this.gtuType, toNode))
  69.                 {
  70.                     throw new NetworkException("Route: node " + fromNode
  71.                             + " not directly or not directionally connected to node " + toNode);
  72.                 }
  73.             }
  74.             fromNode = toNode;
  75.         }
  76.     }

  77.     /**
  78.      * Verify that there are normal (non Connectors) between adjacent nodes, except at start and end (where Connectors are OK.
  79.      */
  80.     public void verify()
  81.     {
  82.         // XXX Sanity check - there should be no Connectors (except at start and end)
  83.         for (int index = 0; index < this.nodes.size() - 1; index++)
  84.         {
  85.             Node from = this.nodes.get(index);
  86.             Node to = this.nodes.get(index + 1);
  87.             boolean normalLinkFound = false;
  88.             boolean connectorFound = false;
  89.             for (Link link : from.getLinks())
  90.             {
  91.                 if (link.getStartNode().equals(to) || link.getEndNode().equals(to))
  92.                 {
  93.                     if (link.isConnector())
  94.                     {
  95.                         connectorFound = true;
  96.                     }
  97.                     else
  98.                     {
  99.                         normalLinkFound = true;
  100.                     }
  101.                 }
  102.             }
  103.             if ((!normalLinkFound) && (!connectorFound))
  104.             {
  105.                 CategoryLogger.always()
  106.                         .error(String.format("Unlike this route, the network has no link from %s (index %d of %d) to %s", from,
  107.                                 index, this.nodes.size(), to));
  108.             }
  109.             else if ((!normalLinkFound) && index > 0 && index < this.nodes.size() - 2)
  110.             {
  111.                 CategoryLogger.always()
  112.                         .error(String.format(
  113.                                 "Route (from node %s to node %s) includes connector along the way (index %d; node %s and %d; "
  114.                                         + "node %s of %d)",
  115.                                 this.nodes.get(0).getId(), this.nodes.get(this.nodes.size() - 1).getId(), index, from,
  116.                                 index + 1, to, this.nodes.size()));
  117.             }
  118.         }
  119.     }

  120.     /**
  121.      * Add a node to the end of the node list.
  122.      * @param node Node; the node to add.
  123.      * @return Route; this route for method chaining
  124.      * @throws NetworkException in case node could not be added to the route.
  125.      */
  126.     public final Route addNode(final Node node) throws NetworkException
  127.     {
  128.         if (getNodes().size() > 0)
  129.         {
  130.             Node lastNode = getNodes().get(getNodes().size() - 1);
  131.             if (!lastNode.isConnectedTo(this.gtuType, node))
  132.             {
  133.                 throw new NetworkException("Route: last node " + lastNode
  134.                         + " not directly or not directionally connected to node " + node);
  135.             }
  136.         }
  137.         this.nodes.add(node);
  138.         this.nodeSet.add(node);
  139.         verify();
  140.         return this;
  141.     }

  142.     /**
  143.      * @return nodes.
  144.      */
  145.     public final List<Node> getNodes()
  146.     {
  147.         return this.nodes;
  148.     }

  149.     /**
  150.      * @param i int; the index of the node to obtain
  151.      * @return node i.
  152.      * @throws NetworkException if i &lt; 0 or i &gt; size
  153.      */
  154.     public final Node getNode(final int i) throws NetworkException
  155.     {
  156.         if (i < 0 || i >= this.nodes.size())
  157.         {
  158.             throw new NetworkException("Route.getNode(i=" + i + "); i<0 or i>size=" + size());
  159.         }
  160.         return this.nodes.get(i);
  161.     }

  162.     /**
  163.      * @return the first node of the route.
  164.      * @throws NetworkException when the list has no nodes.
  165.      */
  166.     public final Node originNode() throws NetworkException
  167.     {
  168.         if (this.nodes.size() == 0)
  169.         {
  170.             throw new NetworkException("Route.getOrigin() called, but node list has no nodes");
  171.         }
  172.         return this.nodes.get(0);
  173.     }

  174.     /**
  175.      * @return the number of nodes in the list. If the list contains more than Integer.MAX_VALUE elements, returns
  176.      *         Integer.MAX_VALUE.
  177.      */
  178.     public final int size()
  179.     {
  180.         return this.nodes.size();
  181.     }

  182.     /**
  183.      * @return the last node of the route.
  184.      * @throws NetworkException when the list has no nodes.
  185.      */
  186.     public final Node destinationNode() throws NetworkException
  187.     {
  188.         if (this.nodes.size() == 0)
  189.         {
  190.             throw new NetworkException("Route.getDestination() called, but node list has no nodes");
  191.         }
  192.         return this.nodes.get(size() - 1);
  193.     }

  194.     /**
  195.      * Return the index of a Node in this Route, or -1 if this Route does not contain the specified Node. <br>
  196.      * If this route contains the Node more than once, the index of the first is returned.
  197.      * @param node Node; the Node to find
  198.      * @return int;
  199.      */
  200.     public final int indexOf(final Node node)
  201.     {
  202.         return this.nodes.indexOf(node);
  203.     }

  204.     /**
  205.      * @param node Node; the Node to find
  206.      * @return whether the route contains this node (quick using LinkedHashSet);
  207.      */
  208.     public final boolean contains(final Node node)
  209.     {
  210.         return this.nodeSet.contains(node);
  211.     }

  212.     /**
  213.      * @return name.
  214.      */
  215.     @Override
  216.     public final String getId()
  217.     {
  218.         return this.id;
  219.     }

  220.     /**
  221.      * Determine if this Route contains the specified Link.
  222.      * @param link Link; the link to check in the route.
  223.      * @return whether the link is part of the route or not.
  224.      */
  225.     public final boolean containsLink(final Link link)
  226.     {
  227.         int index1 = getNodes().indexOf(link.getStartNode());
  228.         int index2 = getNodes().indexOf(link.getEndNode());
  229.         return index1 >= 0 && index2 >= 0 && Math.abs(index2 - index1) == 1;
  230.     }

  231.     /** {@inheritDoc} */
  232.     @Override
  233.     public int hashCode()
  234.     {
  235.         return Objects.hash(this.gtuType, this.id, this.nodes);
  236.     }

  237.     /** {@inheritDoc} */
  238.     @Override
  239.     public boolean equals(final Object obj)
  240.     {
  241.         if (this == obj)
  242.         {
  243.             return true;
  244.         }
  245.         if (obj == null)
  246.         {
  247.             return false;
  248.         }
  249.         if (getClass() != obj.getClass())
  250.         {
  251.             return false;
  252.         }
  253.         Route other = (Route) obj;
  254.         return Objects.equals(this.gtuType, other.gtuType) && Objects.equals(this.id, other.id)
  255.                 && Objects.equals(this.nodes, other.nodes);
  256.     }

  257.     /** {@inheritDoc} */
  258.     @Override
  259.     public String toString()
  260.     {
  261.         return "Route [id=" + this.id + ", gtuType=" + this.gtuType + ", nodes=" + this.nodes + "]";
  262.     }

  263. }