Node.java

  1. package org.opentrafficsim.core.network;

  2. import java.io.Serializable;
  3. import java.util.LinkedHashMap;
  4. import java.util.LinkedHashSet;
  5. import java.util.Map;
  6. import java.util.Set;

  7. import org.djunits.value.vdouble.scalar.Direction;
  8. import org.djutils.base.Identifiable;
  9. import org.djutils.draw.bounds.Bounds2d;
  10. import org.djutils.draw.line.Polygon2d;
  11. import org.djutils.draw.point.OrientedPoint2d;
  12. import org.djutils.draw.point.Point2d;
  13. import org.djutils.exceptions.Throw;
  14. import org.djutils.immutablecollections.ImmutableHashSet;
  15. import org.djutils.immutablecollections.ImmutableSet;
  16. import org.opentrafficsim.base.HierarchicallyTyped;
  17. import org.opentrafficsim.base.geometry.OtsLocatable;
  18. import org.opentrafficsim.base.geometry.OtsShape;
  19. import org.opentrafficsim.base.geometry.PolygonShape;
  20. import org.opentrafficsim.base.geometry.SpatialObject;
  21. import org.opentrafficsim.core.gtu.GtuType;

  22. /**
  23.  * The Node is a point with an id. It is used in the network to connect Links.
  24.  * <p>
  25.  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  26.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  27.  * <p>
  28.  * $LastChangedDate$, @version $Revision$, by $Author$, initial version Aug 19, 2014 <br>
  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://www.citg.tudelft.nl">Guus Tamminga</a>
  32.  */
  33. public class Node
  34.         implements HierarchicallyTyped<NodeType, Node>, OtsLocatable, Serializable, Identifiable
  35. {
  36.     /** */
  37.     private static final long serialVersionUID = 20150722L;

  38.     /** The Network. */
  39.     private final Network network;

  40.     /** The node id. */
  41.     private final String id;

  42.     /** The point. */
  43.     private final OrientedPoint2d point;

  44.     /** The contour. */
  45.     private final Polygon2d contour;

  46.     /** Shape. */
  47.     private final OtsShape shape;

  48.     /** The links connected to the Node. */
  49.     private final Set<Link> links = new LinkedHashSet<>();

  50.     /** The cached immutable set of links to return. */
  51.     private ImmutableSet<Link> cachedLinks = null;

  52.     /**
  53.      * Map with connections per GTU type. When this map is null, the all connections that are possible for the GTU type will be
  54.      * included, with the exception of the U-turn. When exceptions are taken into account, the map has to be completely filled
  55.      * as it replaces the default. The map gives per GTU type a map of incoming links that are connected to outgoing links,
  56.      * which are stored in a Set.
  57.      */
  58.     private Map<GtuType, Map<Link, Set<Link>>> connections = null;

  59.     /**
  60.      * Construction of a Node. Direction will be 0.0.
  61.      * @param network the network.
  62.      * @param id the id of the Node.
  63.      * @param point the point with usually an x and y setting.
  64.      * @throws NetworkException if node already exists in the network, or if name of the node is not unique.
  65.      */
  66.     public Node(final Network network, final String id, final Point2d point) throws NetworkException
  67.     {
  68.         this(network, id, new OrientedPoint2d(point.x, point.y, 0.0));
  69.     }

  70.     /**
  71.      * Construction of a Node.
  72.      * @param network the network.
  73.      * @param id the id of the Node.
  74.      * @param point the point with usually an x and y setting.
  75.      * @param heading heading
  76.      * @throws NetworkException if node already exists in the network, or if name of the node is not unique.
  77.      */
  78.     public Node(final Network network, final String id, final Point2d point, final Direction heading) throws NetworkException
  79.     {
  80.         this(network, id, new OrientedPoint2d(point.x, point.y, heading.si));
  81.     }

  82.     /**
  83.      * Construction of a Node.
  84.      * @param network the network.
  85.      * @param id the id of the Node.
  86.      * @param point the point with usually an x and y setting.
  87.      * @throws NetworkException if node already exists in the network, or if name of the node is not unique.
  88.      */
  89.     public Node(final Network network, final String id, final OrientedPoint2d point) throws NetworkException
  90.     {
  91.         Throw.whenNull(network, "network cannot be null");
  92.         Throw.whenNull(id, "id cannot be null");
  93.         Throw.whenNull(point, "point cannot be null");

  94.         this.network = network;
  95.         this.id = id;
  96.         this.point = point;

  97.         double x = this.point.x;
  98.         double y = this.point.y;
  99.         this.contour = new Polygon2d(new Point2d(x - 0.5, y - 0.5), new Point2d(x - 0.5, y + 0.5),
  100.                 new Point2d(x + 0.5, y + 0.5), new Point2d(x + 0.5, y - 0.5));
  101.         this.shape = new PolygonShape(OtsLocatable.relativeContour(this));

  102.         this.network.addNode(this);
  103.     }

  104.     /**
  105.      * Return the network in which this link is registered. Cannot be null.
  106.      * @return the network in which this link is registered
  107.      */
  108.     public Network getNetwork()
  109.     {
  110.         return this.network;
  111.     }

  112.     @Override
  113.     public String getId()
  114.     {
  115.         return this.id;
  116.     }

  117.     /**
  118.      * Returns the point without direction.
  119.      * @return point.
  120.      */
  121.     public OrientedPoint2d getPoint()
  122.     {
  123.         return this.point;
  124.     }

  125.     @Override
  126.     public Polygon2d getContour()
  127.     {
  128.         return this.contour;
  129.     }

  130.     @Override
  131.     public OtsShape getShape()
  132.     {
  133.         return this.shape;
  134.     }

  135.     /**
  136.      * Returns the heading.
  137.      * @return heading.
  138.      */
  139.     public Direction getHeading()
  140.     {
  141.         return Direction.instantiateSI(this.point.dirZ);
  142.     }

  143.     /**
  144.      * Add a link to this Node.
  145.      * @param link the link to add.
  146.      */
  147.     public void addLink(final Link link)
  148.     {
  149.         this.links.add(link);
  150.         this.cachedLinks = null; // invalidate the cache
  151.     }

  152.     /**
  153.      * Remove a link from this Node.
  154.      * @param link the link to remove.
  155.      */
  156.     public void removeLink(final Link link)
  157.     {
  158.         this.links.remove(link);
  159.         this.cachedLinks = null; // invalidate the cache
  160.     }

  161.     /**
  162.      * Add a single connection for a GTU type to the connections map. The data structures will be created if it does not exist
  163.      * yet.
  164.      * @param gtuType the GTU type for which this connection is made
  165.      * @param incomingLink the link that connects to this Node
  166.      * @param outgoingLink the link that the GTU can use to depart from this Node when coming from the incoming link
  167.      * @throws NetworkException in case one of the links is not (correctly) connected to this Node
  168.      */
  169.     public void addConnection(final GtuType gtuType, final Link incomingLink, final Link outgoingLink) throws NetworkException
  170.     {
  171.         // ------------------------------------------- check consistency
  172.         if (!this.links.contains(incomingLink))
  173.         {
  174.             throw new NetworkException(
  175.                     "addConnection: incoming link " + incomingLink + " for node " + this + " not in links set");
  176.         }

  177.         if (!this.links.contains(outgoingLink))
  178.         {
  179.             throw new NetworkException(
  180.                     "addConnection: outgoing link " + outgoingLink + " for node " + this + " not in links set");
  181.         }

  182.         if (!(incomingLink.getEndNode().equals(this) && incomingLink.getType().isCompatible(gtuType)))
  183.         {
  184.             throw new NetworkException("addConnection: incoming link " + incomingLink + " not connected to node " + this
  185.                     + " for GTU type " + gtuType);
  186.         }

  187.         if (!(outgoingLink.getStartNode().equals(this) && outgoingLink.getType().isCompatible(gtuType)))
  188.         {
  189.             throw new NetworkException("addConnection: outgoing link " + outgoingLink + " not connected to node " + this
  190.                     + " for GTU type " + gtuType);
  191.         }

  192.         // ------------------------------------------- make datasets if needed
  193.         if (this.connections == null)
  194.         {
  195.             this.connections = new LinkedHashMap<>();
  196.         }

  197.         if (!this.connections.containsKey(gtuType))
  198.         {
  199.             this.connections.put(gtuType, new LinkedHashMap<>());
  200.         }

  201.         Map<Link, Set<Link>> gtuMap = this.connections.get(gtuType);
  202.         if (!gtuMap.containsKey(incomingLink))
  203.         {
  204.             gtuMap.put(incomingLink, new LinkedHashSet<>());
  205.         }

  206.         // ------------------------------------------- add the connection
  207.         gtuMap.get(incomingLink).add(outgoingLink);
  208.     }

  209.     /**
  210.      * Add a set of connections for a GTU type to the connections map. The data structures will be created if it does not exist
  211.      * yet.
  212.      * @param gtuType the GTU type for which this connection is made
  213.      * @param incomingLink the link that connects to this Node
  214.      * @param outgoingLinks a set of links that the GTU can use to depart from this Node when coming from the incoming link
  215.      * @throws NetworkException in case one of the links is not (correctly) connected to this Node
  216.      */
  217.     public void addConnections(final GtuType gtuType, final Link incomingLink, final Set<Link> outgoingLinks)
  218.             throws NetworkException
  219.     {
  220.         // ------------------------------------------- check consistency
  221.         if (!this.links.contains(incomingLink))
  222.         {
  223.             throw new NetworkException(
  224.                     "addConnections: incoming link " + incomingLink + " for node " + this + " not in links set");
  225.         }

  226.         if (!this.links.containsAll(outgoingLinks))
  227.         {
  228.             throw new NetworkException(
  229.                     "addConnections: outgoing links " + outgoingLinks + " for node " + this + " not all in links set");
  230.         }

  231.         if (!(incomingLink.getEndNode().equals(this) && incomingLink.getType().isCompatible(gtuType)))
  232.         {
  233.             throw new NetworkException("addConnections: incoming link " + incomingLink + " not connected to node " + this
  234.                     + " for GTU type " + gtuType);
  235.         }

  236.         for (Link outgoingLink : outgoingLinks)
  237.         {
  238.             if (!(outgoingLink.getStartNode().equals(this) && outgoingLink.getType().isCompatible(gtuType)))
  239.             {
  240.                 throw new NetworkException("addConnections: outgoing link " + outgoingLink + " not connected to node " + this
  241.                         + " for GTU type " + gtuType);
  242.             }
  243.         }

  244.         // ------------------------------------------- make datasets if needed
  245.         if (this.connections == null)
  246.         {
  247.             this.connections = new LinkedHashMap<>();
  248.         }

  249.         if (!this.connections.containsKey(gtuType))
  250.         {
  251.             this.connections.put(gtuType, new LinkedHashMap<>());
  252.         }

  253.         Map<Link, Set<Link>> gtuMap = this.connections.get(gtuType);
  254.         if (!gtuMap.containsKey(incomingLink))
  255.         {
  256.             gtuMap.put(incomingLink, new LinkedHashSet<>());
  257.         }

  258.         // ------------------------------------------- add the connections
  259.         gtuMap.get(incomingLink).addAll(outgoingLinks);
  260.     }

  261.     /**
  262.      * Returns the links connected to this node.
  263.      * @return links.
  264.      */
  265.     public ImmutableSet<Link> getLinks()
  266.     {
  267.         if (this.cachedLinks == null)
  268.         {
  269.             this.cachedLinks = new ImmutableHashSet<>(this.links);
  270.         }
  271.         return this.cachedLinks;
  272.     }

  273.     /**
  274.      * Determine the links connecting from the previous link via this Node for the given GTU type.
  275.      * @param gtuType the GTU type to determine the next links for
  276.      * @param prevLink the incoming link to the Node
  277.      * @return a set of links connecting from the previous link via this Node for the given GTU type
  278.      * @throws NetworkException if the incoming link is not connected to this node for the given GTU type
  279.      */
  280.     public Set<Link> nextLinks(final GtuType gtuType, final Link prevLink) throws NetworkException
  281.     {
  282.         // ------------------------------------------- check consistency
  283.         if (!this.links.contains(prevLink))
  284.         {
  285.             throw new NetworkException("nextLinks: incoming link " + prevLink + " for node " + this + " not in links set");
  286.         }

  287.         if (!(prevLink.getEndNode().equals(this) && prevLink.getType().isCompatible(gtuType)))
  288.         {
  289.             throw new NetworkException(
  290.                     "nextLinks: incoming link " + prevLink + " not connected to node " + this + " for GTU type " + gtuType);
  291.         }

  292.         Set<Link> result = new LinkedHashSet<>();

  293.         // -------------------------------- check if explicit connections are present
  294.         if (this.connections != null)
  295.         {
  296.             if (!this.connections.containsKey(gtuType))
  297.             {
  298.                 return result;
  299.             }
  300.             if (!this.connections.get(gtuType).containsKey(prevLink))
  301.             {
  302.                 return result;
  303.             }
  304.             result.addAll(this.connections.get(gtuType).get(prevLink)); // defensive copy
  305.             return result;
  306.         }

  307.         // ----------------------------- defensive copy of the connections for the gtuType
  308.         for (Link link : getLinks())
  309.         {
  310.             if ((link.getStartNode().equals(this) && link.getType().isCompatible(gtuType)))
  311.             {
  312.                 if (!link.equals(prevLink)) // no U-turn
  313.                 {
  314.                     result.add(link);
  315.                 }
  316.             }
  317.         }
  318.         return result;
  319.     }

  320.     /**
  321.      * Check if the current node is linked to the given Node for the given GtuType. This means there is a direct Link from this
  322.      * node to toNode for the provided GtuType.
  323.      * @param gtuType the GTU type to check the connection for.
  324.      * @param toNode the to node
  325.      * @return whether two nodes are linked for the GTU type.
  326.      */
  327.     public boolean isConnectedTo(final GtuType gtuType, final Node toNode)
  328.     {
  329.         for (Link link : getLinks())
  330.         {
  331.             if (toNode.equals(link.getEndNode()) && link.getType().isCompatible(gtuType))
  332.             {
  333.                 return true;
  334.             }
  335.         }
  336.         return false;
  337.     }

  338.     /**
  339.      * Checks whether the node has only connector links going in and/or out, and no other links.
  340.      * @return whether the node is a centroid, i.e. it <b>only</b> has connector links going in and out
  341.      */
  342.     public boolean isCentroid()
  343.     {
  344.         return false;
  345.     }

  346.     @Override
  347.     public NodeType getType()
  348.     {
  349.         return NodeType.NODE;
  350.     }

  351.     @Override
  352.     @SuppressWarnings("checkstyle:designforextension")
  353.     public OrientedPoint2d getLocation()
  354.     {
  355.         return this.point;
  356.     }

  357.     @Override
  358.     @SuppressWarnings("checkstyle:designforextension")
  359.     public Bounds2d getBounds()
  360.     {
  361.         return new Bounds2d(-1.0, 1.0, -1.0, 1.0);
  362.     }

  363.     @Override
  364.     @SuppressWarnings("checkstyle:designforextension")
  365.     public String toString()
  366.     {
  367.         return "Node [id=" + this.id + ", point=" + this.point + "]";
  368.     }

  369.     @Override
  370.     @SuppressWarnings("checkstyle:designforextension")
  371.     public int hashCode()
  372.     {
  373.         final int prime = 31;
  374.         int result = 1;
  375.         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
  376.         return result;
  377.     }

  378.     @Override
  379.     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
  380.     public boolean equals(final Object obj)
  381.     {
  382.         if (this == obj)
  383.             return true;
  384.         if (obj == null)
  385.             return false;
  386.         if (getClass() != obj.getClass())
  387.             return false;
  388.         Node other = (Node) obj;
  389.         if (this.id == null)
  390.         {
  391.             if (other.id != null)
  392.                 return false;
  393.         }
  394.         else if (!this.id.equals(other.id))
  395.             return false;
  396.         return true;
  397.     }

  398. }