View Javadoc
1   package org.opentrafficsim.core.network;
2   
3   import java.io.Serializable;
4   import java.util.HashMap;
5   import java.util.HashSet;
6   import java.util.LinkedHashSet;
7   import java.util.Map;
8   import java.util.Set;
9   
10  import javax.media.j3d.BoundingSphere;
11  import javax.media.j3d.Bounds;
12  import javax.vecmath.Point3d;
13  
14  import org.djunits.value.vdouble.scalar.Angle;
15  import org.djunits.value.vdouble.scalar.Direction;
16  import org.opentrafficsim.core.geometry.OTSPoint3D;
17  import org.opentrafficsim.core.gtu.GTUType;
18  
19  import nl.tudelft.simulation.dsol.animation.Locatable;
20  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
21  import nl.tudelft.simulation.immutablecollections.ImmutableHashSet;
22  import nl.tudelft.simulation.immutablecollections.ImmutableSet;
23  import nl.tudelft.simulation.language.Throw;
24  import nl.tudelft.simulation.language.d3.DirectedPoint;
25  
26  /**
27   * The Node is a point with an id. It is used in the network to connect Links.
28   * <p>
29   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
30   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
31   * <p>
32   * $LastChangedDate: 2018-09-19 13:55:45 +0200 (Wed, 19 Sep 2018) $, @version $Revision: 4006 $, by $Author: averbraeck $,
33   * initial version Aug 19, 2014 <br>
34   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
35   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
36   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
37   */
38  public class OTSNode implements Node, Locatable, Serializable
39  {
40      /** */
41      private static final long serialVersionUID = 20150722L;
42  
43      /** the Network. */
44      private final Network network;
45  
46      /** The node id. */
47      private final String id;
48  
49      /** The point. */
50      private final OTSPoint3D point;
51  
52      /** The 3D direction. "East" is 0 degrees. "North" is 90 degrees (1/2 pi radians). */
53      private final Direction direction;
54  
55      /** The slope as an angle. Horizontal is 0 degrees. */
56      private final Angle slope;
57  
58      /** The links connected to the Node. */
59      private final Set<Link> links = new LinkedHashSet<>();
60  
61      /** The cached immutable set of links to return. */
62      private ImmutableSet<Link> cachedLinks = null;
63  
64      /**
65       * Map with connections per GTU type. When this map is null, the all connections that are possible for the GTU type will be
66       * included, with the exception of the U-turn. When exceptions are taken into account, the map has to be completely filled
67       * as it replaces the default. The map gives per GTU type a map of incoming links that are connected to outgoing links,
68       * which are stored in a Set.
69       */
70      private Map<GTUType, Map<Link, Set<Link>>> connections = null;
71  
72      /**
73       * Construction of a Node.
74       * @param network the network.
75       * @param id the id of the Node.
76       * @param point the point with usually an x and y setting.
77       * @param direction the 3D direction. "East" is 0 degrees. "North" is 90 degrees (1/2 pi radians).
78       * @param slope the slope as an angle. Horizontal is 0 degrees.
79       * @throws NetworkException if node already exists in the network, or if name of the node is not unique.
80       */
81      public OTSNode(final Network network, final String id, final OTSPoint3D point, final Direction direction, final Angle slope)
82              throws NetworkException
83      {
84          Throw.whenNull(network, "network cannot be null");
85          Throw.whenNull(id, "id cannot be null");
86          Throw.whenNull(point, "point cannot be null");
87          Throw.whenNull(direction, "direction cannot be null");
88          Throw.whenNull(slope, "slope cannot be null");
89  
90          this.network = network;
91          this.id = id;
92          this.point = point;
93          this.direction = direction;
94          this.slope = slope;
95  
96          this.network.addNode(this);
97      }
98  
99      /**
100      * Construction of a Node.
101      * @param network the network.
102      * @param id the id of the Node.
103      * @param point the point with usually an x and y setting.
104      * @throws NetworkException if node already exists in the network, or if name of the node is not unique.
105      */
106     public OTSNode(final Network network, final String id, final OTSPoint3D point) throws NetworkException
107     {
108         this(network, id, point, Direction.ZERO, Angle.ZERO);
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public final Network getNetwork()
114     {
115         return this.network;
116     }
117 
118     /**
119      * @return node id.
120      */
121     @Override
122     public final String getId()
123     {
124         return this.id;
125     }
126 
127     /**
128      * @return point.
129      */
130     @Override
131     public final OTSPoint3D getPoint()
132     {
133         return this.point;
134     }
135 
136     /** {@inheritDoc} */
137     @Override
138     public final void addLink(final Link link)
139     {
140         this.links.add(link);
141         this.cachedLinks = null; // invalidate the cache
142     }
143 
144     /** {@inheritDoc} */
145     @Override
146     public final void removeLink(final Link link)
147     {
148         this.links.remove(link);
149         this.cachedLinks = null; // invalidate the cache
150     }
151 
152     /**
153      * Add a single connection for a GTU type to the connections map. The data structures will be created is it does not exist
154      * yet.
155      * @param gtuType the GTU type for which this connection is made
156      * @param incomingLink the link that connects to this Node
157      * @param outgoingLink the link that the GTU can use to depart from this Node when coming from the incoming link
158      * @throws NetworkException in case one of the links is not (correctly) connected to this Node
159      */
160     public final void addConnection(final GTUType gtuType, final Link incomingLink, final Link outgoingLink)
161             throws NetworkException
162     {
163         // ------------------------------------------- check consistency
164         if (!this.links.contains(incomingLink))
165         {
166             throw new NetworkException(
167                     "addConnection: incoming link " + incomingLink + " for node " + this + " not in links set");
168         }
169 
170         if (!this.links.contains(outgoingLink))
171         {
172             throw new NetworkException(
173                     "addConnection: outgoing link " + outgoingLink + " for node " + this + " not in links set");
174         }
175 
176         if (!(incomingLink.getEndNode().equals(this) && incomingLink.getDirectionality(gtuType).isForwardOrBoth()
177                 || incomingLink.getStartNode().equals(this) && incomingLink.getDirectionality(gtuType).isBackwardOrBoth()))
178         {
179             throw new NetworkException("addConnection: incoming link " + incomingLink + " not connected to node " + this
180                     + " for GTU type " + gtuType);
181         }
182 
183         if (!(outgoingLink.getStartNode().equals(this) && outgoingLink.getDirectionality(gtuType).isForwardOrBoth()
184                 || outgoingLink.getEndNode().equals(this) && outgoingLink.getDirectionality(gtuType).isBackwardOrBoth()))
185         {
186             throw new NetworkException("addConnection: outgoing link " + outgoingLink + " not connected to node " + this
187                     + " for GTU type " + gtuType);
188         }
189 
190         // ------------------------------------------- make datasets if needed
191         if (this.connections == null)
192         {
193             this.connections = new HashMap<>();
194         }
195 
196         if (!this.connections.containsKey(gtuType))
197         {
198             this.connections.put(gtuType, new HashMap<>());
199         }
200 
201         Map<Link, Set<Link>> gtuMap = this.connections.get(gtuType);
202         if (!gtuMap.containsKey(incomingLink))
203         {
204             gtuMap.put(incomingLink, new HashSet<>());
205         }
206 
207         // ------------------------------------------- add the connection
208         gtuMap.get(incomingLink).add(outgoingLink);
209     }
210 
211     /**
212      * Add a set of connections for a GTU type to the connections map. The data structures will be created if it does not exist
213      * yet.
214      * @param gtuType the GTU type for which this connection is made
215      * @param incomingLink the link that connects to this Node
216      * @param outgoingLinks a set of links that the GTU can use to depart from this Node when coming from the incoming link
217      * @throws NetworkException in case one of the links is not (correctly) connected to this Node
218      */
219     public final void addConnections(final GTUType gtuType, final Link incomingLink, final Set<Link> outgoingLinks)
220             throws NetworkException
221     {
222         // ------------------------------------------- check consistency
223         if (!this.links.contains(incomingLink))
224         {
225             throw new NetworkException(
226                     "addConnections: incoming link " + incomingLink + " for node " + this + " not in links set");
227         }
228 
229         if (!this.links.containsAll(outgoingLinks))
230         {
231             throw new NetworkException(
232                     "addConnections: outgoing links " + outgoingLinks + " for node " + this + " not all in links set");
233         }
234 
235         if (!((incomingLink.getEndNode().equals(this) && incomingLink.getDirectionality(gtuType).isForwardOrBoth())
236                 || (incomingLink.getStartNode().equals(this) && incomingLink.getDirectionality(gtuType).isBackwardOrBoth())))
237         {
238             throw new NetworkException("addConnections: incoming link " + incomingLink + " not connected to node " + this
239                     + " for GTU type " + gtuType);
240         }
241 
242         for (Link outgoingLink : outgoingLinks)
243         {
244             if (!((outgoingLink.getStartNode().equals(this) && outgoingLink.getDirectionality(gtuType).isForwardOrBoth())
245                     || (outgoingLink.getEndNode().equals(this) && outgoingLink.getDirectionality(gtuType).isBackwardOrBoth())))
246             {
247                 throw new NetworkException("addConnections: outgoing link " + outgoingLink + " not connected to node " + this
248                         + " for GTU type " + gtuType);
249             }
250         }
251 
252         // ------------------------------------------- make datasets if needed
253         if (this.connections == null)
254         {
255             this.connections = new HashMap<>();
256         }
257 
258         if (!this.connections.containsKey(gtuType))
259         {
260             this.connections.put(gtuType, new HashMap<>());
261         }
262 
263         Map<Link, Set<Link>> gtuMap = this.connections.get(gtuType);
264         if (!gtuMap.containsKey(incomingLink))
265         {
266             gtuMap.put(incomingLink, new HashSet<>());
267         }
268 
269         // ------------------------------------------- add the connections
270         gtuMap.get(incomingLink).addAll(outgoingLinks);
271     }
272 
273     /** {@inheritDoc} */
274     @Override
275     public final ImmutableSet<Link> getLinks()
276     {
277         if (this.cachedLinks == null)
278         {
279             this.cachedLinks = new ImmutableHashSet<>(this.links);
280         }
281         return this.cachedLinks;
282     }
283 
284     /** {@inheritDoc} */
285     @Override
286     public final Set<Link> nextLinks(final GTUType gtuType, final Link prevLink) throws NetworkException
287     {
288         // ------------------------------------------- check consistency
289         if (!this.links.contains(prevLink))
290         {
291             throw new NetworkException("nextLinks: incoming link " + prevLink + " for node " + this + " not in links set");
292         }
293 
294         if (!(prevLink.getEndNode().equals(this) && prevLink.getDirectionality(gtuType).isForwardOrBoth()
295                 || prevLink.getStartNode().equals(this) && prevLink.getDirectionality(gtuType).isBackwardOrBoth()))
296         {
297             throw new NetworkException(
298                     "nextLinks: incoming link " + prevLink + " not connected to node " + this + " for GTU type " + gtuType);
299         }
300 
301         Set<Link> result = new LinkedHashSet<>();
302 
303         // -------------------------------- check if explicit connections are present
304         if (this.connections != null)
305         {
306             if (!this.connections.containsKey(gtuType))
307             {
308                 return result;
309             }
310             if (!this.connections.get(gtuType).containsKey(prevLink))
311             {
312                 return result;
313             }
314             result.addAll(this.connections.get(gtuType).get(prevLink)); // defensive copy
315             return result;
316         }
317 
318         // ----------------------------- defensive copy of the connections for the gtuType
319         for (Link link : getLinks())
320         {
321             if ((link.getStartNode().equals(this) && link.getDirectionality(gtuType).isForwardOrBoth())
322                     || (link.getEndNode().equals(this) && link.getDirectionality(gtuType).isBackwardOrBoth()))
323             {
324                 if (!link.equals(prevLink)) // no U-turn
325                 {
326                     result.add(link);
327                 }
328             }
329         }
330         return result;
331     }
332 
333     /**
334      * Note: this method does not take into account explicitly defined connections, as the previous link is not given. <br>
335      * {@inheritDoc}
336      */
337     @Override
338     public final boolean isDirectionallyConnectedTo(final GTUType gtuType, final Node toNode)
339     {
340         for (Link link : getLinks())
341         {
342             if (toNode.equals(link.getEndNode()) && link.getDirectionality(gtuType).isForwardOrBoth())
343             {
344                 return true;
345             }
346             if (toNode.equals(link.getStartNode()) && link.getDirectionality(gtuType).isBackwardOrBoth())
347             {
348                 return true;
349             }
350         }
351         return false;
352     }
353 
354     /** {@inheritDoc} */
355     @Override
356     public final Direction getDirection()
357     {
358         return this.direction;
359     }
360 
361     /** {@inheritDoc} */
362     @Override
363     public final Angle getSlope()
364     {
365         return this.slope;
366     }
367 
368     /** {@inheritDoc} */
369     @Override
370     @SuppressWarnings("checkstyle:designforextension")
371     public DirectedPoint getLocation()
372     {
373         return this.point.getDirectedPoint();
374     }
375 
376     /** {@inheritDoc} */
377     @Override
378     @SuppressWarnings("checkstyle:designforextension")
379     public Bounds getBounds()
380     {
381         return new BoundingSphere(new Point3d(0.0d, 0.0d, 0.0d), 10.0d);
382     }
383 
384     /** {@inheritDoc} */
385     @Override
386     @SuppressWarnings("checkstyle:designforextension")
387     public String toString()
388     {
389         return "OTSNode [id=" + this.id + ", point=" + this.point + "]";
390     }
391 
392     /** {@inheritDoc} */
393     @Override
394     @SuppressWarnings("checkstyle:designforextension")
395     public int hashCode()
396     {
397         final int prime = 31;
398         int result = 1;
399         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
400         return result;
401     }
402 
403     /** {@inheritDoc} */
404     @Override
405     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
406     public boolean equals(final Object obj)
407     {
408         if (this == obj)
409             return true;
410         if (obj == null)
411             return false;
412         if (getClass() != obj.getClass())
413             return false;
414         OTSNode other = (OTSNode) obj;
415         if (this.id == null)
416         {
417             if (other.id != null)
418                 return false;
419         }
420         else if (!this.id.equals(other.id))
421             return false;
422         return true;
423     }
424 
425     /**
426      * Clone the OTSode for e.g., copying a network.
427      * @param newNetwork the new network to which the clone belongs
428      * @param newSimulator the new simulator for this network
429      * @return a clone of this object
430      * @throws NetworkException in case the cloning fails
431      */
432     @SuppressWarnings("checkstyle:designforextension")
433     public OTSNode clone1(final Network newNetwork, final SimulatorInterface.TimeDoubleUnit newSimulator)
434             throws NetworkException
435     {
436         return new OTSNode(newNetwork, this.id, this.point, this.direction, this.slope);
437     }
438 
439     /**
440      * Complete the cloning of the OTSode for e.g., copying a network. Call this method after all the links have been
441      * constructed in the new network.
442      * @param newNetwork the new network to which the clone belongs
443      * @param newSimulator the new simulator for this network
444      * @param animation whether to clone the animation or not (could be used in subclasses)
445      * @return the completed clone
446      * @throws NetworkException in case the cloning fails
447      */
448     @SuppressWarnings("checkstyle:designforextension")
449     public OTSNode clone2(final Network newNetwork, final SimulatorInterface.TimeDoubleUnit newSimulator,
450             final boolean animation) throws NetworkException
451     {
452         @SuppressWarnings("unchecked")
453         OTSNode clone = (OTSNode) newNetwork.getNode(this.id);
454         if (this.connections != null)
455         {
456             Map<GTUType, Map<Link, Set<Link>>> newConnections = new HashMap<>();
457             for (GTUType gtuType : this.connections.keySet())
458             {
459                 Map<Link, Set<Link>> newConnMap = new HashMap<>();
460                 for (Link link : this.connections.get(gtuType).keySet())
461                 {
462                     Set<Link> newLinkSet = new HashSet<>();
463                     for (Link setLink : this.connections.get(gtuType).get(link))
464                     {
465                         newLinkSet.add(newNetwork.getLink(setLink.getId()));
466                     }
467                     newConnMap.put(newNetwork.getLink(link.getId()), newLinkSet);
468                 }
469                 newConnections.put(gtuType, newConnMap);
470             }
471             clone.connections = newConnections;
472         }
473         return clone;
474     }
475 
476 }