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.Set;

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

  13. /**
  14.  * A Route consists of a list of Nodes. A route does not have to be complete. As long as all 'splitting' nodes are part of the
  15.  * route and have a valid successor node (connected by a Link), the strategical planner is able to make a plan. An extension of
  16.  * the Route class exists that contains a complete route, where all nodes on the route have to be present and connected.
  17.  * <p>
  18.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  19.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  20.  * <p>
  21.  * $LastChangedDate: 2020-05-14 13:12:03 +0200 (Thu, 14 May 2020) $, @version $Revision: 6470 $, by $Author: pknoppers $,
  22.  * initial version Jan 1, 2015 <br>
  23.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  24.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  25.  */
  26. public class Route implements Serializable, Identifiable
  27. {
  28.     /** */
  29.     private static final long serialVersionUID = 20150101L;

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

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

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

  36.     /**
  37.      * Create an empty route.
  38.      * @param id String; the name of the route.
  39.      */
  40.     public Route(final String id)
  41.     {
  42.         this.nodes = new ArrayList<>();
  43.         this.id = id;
  44.     }

  45.     /**
  46.      * Create a route based on an initial list of nodes. <br>
  47.      * @param nodes List&lt;Node&gt;; the initial list of nodes.
  48.      * @param id String; the name of the route.
  49.      */
  50.     public Route(final String id, final List<Node> nodes)
  51.     {
  52.         this.id = id;
  53.         this.nodes = new ArrayList<>(nodes); // defensive copy
  54.         this.nodeSet.addAll(nodes);
  55.         verify();
  56.     }

  57.     /**
  58.      * Verify that there are normal (non Connectors) between adjacent nodes, except at start and end (where Connectors are OK.
  59.      */
  60.     public void verify()
  61.     {
  62.         // XXX Sanity check - there should be no Connectors (except at start and end)
  63.         for (int index = 0; index < this.nodes.size() - 1; index++)
  64.         {
  65.             Node from = this.nodes.get(index);
  66.             Node to = this.nodes.get(index + 1);
  67.             boolean normalLinkFound = false;
  68.             boolean connectorFound = false;
  69.             for (Link link : from.getLinks())
  70.             {
  71.                 if (link.getStartNode().equals(to) || link.getEndNode().equals(to))
  72.                 {
  73.                     if (link.getLinkType().isConnector())
  74.                     {
  75.                         connectorFound = true;
  76.                     }
  77.                     else
  78.                     {
  79.                         normalLinkFound = true;
  80.                     }
  81.                 }
  82.             }
  83.             if ((!normalLinkFound) && (!connectorFound))
  84.             {
  85.                 CategoryLogger.always()
  86.                         .error(String.format("Unlike this route, the network has no link from %s (index %d of %d) to %s", from,
  87.                                 index, this.nodes.size(), to));
  88.             }
  89.             else if ((!normalLinkFound) && index > 0 && index < this.nodes.size() - 2)
  90.             {
  91.                 CategoryLogger.always()
  92.                         .error(String.format(
  93.                                 "Route (from node %s to node %s) includes connector along the way (index %d; node %s and %d; "
  94.                                         + "node %s of %d)",
  95.                                 this.nodes.get(0).getId(), this.nodes.get(this.nodes.size() - 1).getId(), index, from,
  96.                                 index + 1, to, this.nodes.size()));
  97.             }
  98.         }
  99.     }

  100.     /**
  101.      * Add a node to the end of the node list.
  102.      * @param node Node; the node to add.
  103.      * @return Route; this route for method chaining
  104.      * @throws NetworkException in case node could not be added to the route.
  105.      */
  106.     @SuppressWarnings("checkstyle:designforextension")
  107.     public Route addNode(final Node node) throws NetworkException
  108.     {
  109.         this.nodes.add(node);
  110.         this.nodeSet.add(node);
  111.         verify();
  112.         return this;
  113.     }

  114.     /**
  115.      * @return nodes.
  116.      */
  117.     public final List<Node> getNodes()
  118.     {
  119.         return this.nodes;
  120.     }

  121.     /**
  122.      * @param i int; the index of the node to obtain
  123.      * @return node i.
  124.      * @throws NetworkException if i &lt; 0 or i &gt; size
  125.      */
  126.     public final Node getNode(final int i) throws NetworkException
  127.     {
  128.         if (i < 0 || i >= this.nodes.size())
  129.         {
  130.             throw new NetworkException("Route.getNode(i=" + i + "); i<0 or i>size=" + size());
  131.         }
  132.         return this.nodes.get(i);
  133.     }

  134.     /**
  135.      * @return the first node of the route.
  136.      * @throws NetworkException when the list has no nodes.
  137.      */
  138.     public final Node originNode() throws NetworkException
  139.     {
  140.         if (this.nodes.size() == 0)
  141.         {
  142.             throw new NetworkException("Route.getOrigin() called, but node list has no nodes");
  143.         }
  144.         return this.nodes.get(0);
  145.     }

  146.     /**
  147.      * @return the number of nodes in the list. If the list contains more than Integer.MAX_VALUE elements, returns
  148.      *         Integer.MAX_VALUE.
  149.      */
  150.     public final int size()
  151.     {
  152.         return this.nodes.size();
  153.     }

  154.     /**
  155.      * @return the last node of the route.
  156.      * @throws NetworkException when the list has no nodes.
  157.      */
  158.     public final Node destinationNode() throws NetworkException
  159.     {
  160.         if (this.nodes.size() == 0)
  161.         {
  162.             throw new NetworkException("Route.getDestination() called, but node list has no nodes");
  163.         }
  164.         return this.nodes.get(size() - 1);
  165.     }

  166.     /**
  167.      * Return the index of a Node in this Route, or -1 if this Route does not contain the specified Node. <br>
  168.      * If this route contains the Node more than once, the index of the first is returned.
  169.      * @param node Node; the Node to find
  170.      * @return int;
  171.      */
  172.     public final int indexOf(final Node node)
  173.     {
  174.         return this.nodes.indexOf(node);
  175.     }

  176.     /**
  177.      * @param node Node; the Node to find
  178.      * @return whether the route contains this node (quick using LinkedHashSet);
  179.      */
  180.     public final boolean contains(final Node node)
  181.     {
  182.         return this.nodeSet.contains(node);
  183.     }

  184.     /**
  185.      * @return name.
  186.      */
  187.     @Override
  188.     public final String getId()
  189.     {
  190.         return this.id;
  191.     }

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public int hashCode()
  195.     {
  196.         final int prime = 31;
  197.         int result = 1;
  198.         result = prime * result + ((id == null) ? 0 : id.hashCode());
  199.         result = prime * result + ((nodeSet == null) ? 0 : nodeSet.hashCode());
  200.         result = prime * result + ((nodes == null) ? 0 : nodes.hashCode());
  201.         return result;
  202.     }

  203.     /** {@inheritDoc} */
  204.     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
  205.     @Override
  206.     public boolean equals(final Object obj)
  207.     {
  208.         if (this == obj)
  209.             return true;
  210.         if (obj == null)
  211.             return false;
  212.         if (getClass() != obj.getClass())
  213.             return false;
  214.         Route other = (Route) obj;
  215.         if (id == null)
  216.         {
  217.             if (other.id != null)
  218.                 return false;
  219.         }
  220.         else if (!id.equals(other.id))
  221.             return false;
  222.         if (nodeSet == null)
  223.         {
  224.             if (other.nodeSet != null)
  225.                 return false;
  226.         }
  227.         else if (!nodeSet.equals(other.nodeSet))
  228.             return false;
  229.         if (nodes == null)
  230.         {
  231.             if (other.nodes != null)
  232.                 return false;
  233.         }
  234.         else if (!nodes.equals(other.nodes))
  235.             return false;
  236.         return true;
  237.     }

  238.     /** {@inheritDoc} */
  239.     @Override
  240.     @SuppressWarnings("checkstyle:designforextension")
  241.     public String toString()
  242.     {
  243.         return "Route [id=" + this.id + ", nodes=" + this.nodes + "]";
  244.     }

  245.     /**
  246.      * Clone the Route.
  247.      * @param newNetwork Network; the new network
  248.      * @return a clone of this route
  249.      * @throws NetworkException in case the cloning fails
  250.      */
  251.     @SuppressWarnings("checkstyle:designforextension")
  252.     public Route clone(final Network newNetwork) throws NetworkException
  253.     {
  254.         Route newRoute = new Route(this.id);
  255.         for (Node node : this.nodes)
  256.         {
  257.             newRoute.addNode(newNetwork.getNode(node.getId()));
  258.         }
  259.         return newRoute;
  260.     }
  261. }