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