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.djutils.exceptions.Throw;
17  import org.djutils.immutablecollections.ImmutableHashSet;
18  import org.djutils.immutablecollections.ImmutableSet;
19  import org.opentrafficsim.core.geometry.OTSPoint3D;
20  import org.opentrafficsim.core.gtu.GTUType;
21  
22  import nl.tudelft.simulation.dsol.animation.Locatable;
23  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
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-2019 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: 2019-01-06 01:35:05 +0100 (Sun, 06 Jan 2019) $, @version $Revision: 4831 $, 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 Network; the network.
75       * @param id String; the id of the Node.
76       * @param point OTSPoint3D; the point with usually an x and y setting.
77       * @param direction Direction; the 3D direction. "East" is 0 degrees. "North" is 90 degrees (1/2 pi radians).
78       * @param slope Angle; 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 Network; the network.
102      * @param id String; the id of the Node.
103      * @param point OTSPoint3D; 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 GTUType; the GTU type for which this connection is made
156      * @param incomingLink Link; the link that connects to this Node
157      * @param outgoingLink Link; 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 GTUType; the GTU type for which this connection is made
215      * @param incomingLink Link; the link that connects to this Node
216      * @param outgoingLinks Set&lt;Link&gt;; a set of links that the GTU can use to depart from this Node when coming from the
217      *            incoming link
218      * @throws NetworkException in case one of the links is not (correctly) connected to this Node
219      */
220     public final void addConnections(final GTUType gtuType, final Link incomingLink, final Set<Link> outgoingLinks)
221             throws NetworkException
222     {
223         // ------------------------------------------- check consistency
224         if (!this.links.contains(incomingLink))
225         {
226             throw new NetworkException(
227                     "addConnections: incoming link " + incomingLink + " for node " + this + " not in links set");
228         }
229 
230         if (!this.links.containsAll(outgoingLinks))
231         {
232             throw new NetworkException(
233                     "addConnections: outgoing links " + outgoingLinks + " for node " + this + " not all in links set");
234         }
235 
236         if (!((incomingLink.getEndNode().equals(this) && incomingLink.getDirectionality(gtuType).isForwardOrBoth())
237                 || (incomingLink.getStartNode().equals(this) && incomingLink.getDirectionality(gtuType).isBackwardOrBoth())))
238         {
239             throw new NetworkException("addConnections: incoming link " + incomingLink + " not connected to node " + this
240                     + " for GTU type " + gtuType);
241         }
242 
243         for (Link outgoingLink : outgoingLinks)
244         {
245             if (!((outgoingLink.getStartNode().equals(this) && outgoingLink.getDirectionality(gtuType).isForwardOrBoth())
246                     || (outgoingLink.getEndNode().equals(this) && outgoingLink.getDirectionality(gtuType).isBackwardOrBoth())))
247             {
248                 throw new NetworkException("addConnections: outgoing link " + outgoingLink + " not connected to node " + this
249                         + " for GTU type " + gtuType);
250             }
251         }
252 
253         // ------------------------------------------- make datasets if needed
254         if (this.connections == null)
255         {
256             this.connections = new HashMap<>();
257         }
258 
259         if (!this.connections.containsKey(gtuType))
260         {
261             this.connections.put(gtuType, new HashMap<>());
262         }
263 
264         Map<Link, Set<Link>> gtuMap = this.connections.get(gtuType);
265         if (!gtuMap.containsKey(incomingLink))
266         {
267             gtuMap.put(incomingLink, new HashSet<>());
268         }
269 
270         // ------------------------------------------- add the connections
271         gtuMap.get(incomingLink).addAll(outgoingLinks);
272     }
273 
274     /** {@inheritDoc} */
275     @Override
276     public final ImmutableSet<Link> getLinks()
277     {
278         if (this.cachedLinks == null)
279         {
280             this.cachedLinks = new ImmutableHashSet<>(this.links);
281         }
282         return this.cachedLinks;
283     }
284 
285     /** {@inheritDoc} */
286     @Override
287     public final Set<Link> nextLinks(final GTUType gtuType, final Link prevLink) throws NetworkException
288     {
289         // ------------------------------------------- check consistency
290         if (!this.links.contains(prevLink))
291         {
292             throw new NetworkException("nextLinks: incoming link " + prevLink + " for node " + this + " not in links set");
293         }
294 
295         if (!(prevLink.getEndNode().equals(this) && prevLink.getDirectionality(gtuType).isForwardOrBoth()
296                 || prevLink.getStartNode().equals(this) && prevLink.getDirectionality(gtuType).isBackwardOrBoth()))
297         {
298             throw new NetworkException(
299                     "nextLinks: incoming link " + prevLink + " not connected to node " + this + " for GTU type " + gtuType);
300         }
301 
302         Set<Link> result = new LinkedHashSet<>();
303 
304         // -------------------------------- check if explicit connections are present
305         if (this.connections != null)
306         {
307             if (!this.connections.containsKey(gtuType))
308             {
309                 return result;
310             }
311             if (!this.connections.get(gtuType).containsKey(prevLink))
312             {
313                 return result;
314             }
315             result.addAll(this.connections.get(gtuType).get(prevLink)); // defensive copy
316             return result;
317         }
318 
319         // ----------------------------- defensive copy of the connections for the gtuType
320         for (Link link : getLinks())
321         {
322             if ((link.getStartNode().equals(this) && link.getDirectionality(gtuType).isForwardOrBoth())
323                     || (link.getEndNode().equals(this) && link.getDirectionality(gtuType).isBackwardOrBoth()))
324             {
325                 if (!link.equals(prevLink)) // no U-turn
326                 {
327                     result.add(link);
328                 }
329             }
330         }
331         return result;
332     }
333 
334     /**
335      * Note: this method does not take into account explicitly defined connections, as the previous link is not given. <br>
336      * {@inheritDoc}
337      */
338     @Override
339     public final boolean isDirectionallyConnectedTo(final GTUType gtuType, final Node toNode)
340     {
341         for (Link link : getLinks())
342         {
343             if (toNode.equals(link.getEndNode()) && link.getDirectionality(gtuType).isForwardOrBoth())
344             {
345                 return true;
346             }
347             if (toNode.equals(link.getStartNode()) && link.getDirectionality(gtuType).isBackwardOrBoth())
348             {
349                 return true;
350             }
351         }
352         return false;
353     }
354 
355     /** {@inheritDoc} */
356     @Override
357     public final Direction getDirection()
358     {
359         return this.direction;
360     }
361 
362     /** {@inheritDoc} */
363     @Override
364     public final Angle getSlope()
365     {
366         return this.slope;
367     }
368 
369     /** {@inheritDoc} */
370     @Override
371     @SuppressWarnings("checkstyle:designforextension")
372     public DirectedPoint getLocation()
373     {
374         return this.point.getDirectedPoint();
375     }
376 
377     /** {@inheritDoc} */
378     @Override
379     @SuppressWarnings("checkstyle:designforextension")
380     public Bounds getBounds()
381     {
382         return new BoundingSphere(new Point3d(0.0d, 0.0d, 0.0d), 10.0d);
383     }
384 
385     /** {@inheritDoc} */
386     @Override
387     @SuppressWarnings("checkstyle:designforextension")
388     public String toString()
389     {
390         return "OTSNode [id=" + this.id + ", point=" + this.point + "]";
391     }
392 
393     /** {@inheritDoc} */
394     @Override
395     @SuppressWarnings("checkstyle:designforextension")
396     public int hashCode()
397     {
398         final int prime = 31;
399         int result = 1;
400         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
401         return result;
402     }
403 
404     /** {@inheritDoc} */
405     @Override
406     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
407     public boolean equals(final Object obj)
408     {
409         if (this == obj)
410             return true;
411         if (obj == null)
412             return false;
413         if (getClass() != obj.getClass())
414             return false;
415         OTSNode other = (OTSNode) obj;
416         if (this.id == null)
417         {
418             if (other.id != null)
419                 return false;
420         }
421         else if (!this.id.equals(other.id))
422             return false;
423         return true;
424     }
425 
426     /**
427      * Clone the OTSode for e.g., copying a network.
428      * @param newNetwork Network; the new network to which the clone belongs
429      * @param newSimulator SimulatorInterface.TimeDoubleUnit; the new simulator for this network
430      * @return a clone of this object
431      * @throws NetworkException in case the cloning fails
432      */
433     @SuppressWarnings("checkstyle:designforextension")
434     public OTSNode clone1(final Network newNetwork, final SimulatorInterface.TimeDoubleUnit newSimulator)
435             throws NetworkException
436     {
437         return new OTSNode(newNetwork, this.id, this.point, this.direction, this.slope);
438     }
439 
440     /**
441      * Complete the cloning of the OTSode for e.g., copying a network. Call this method after all the links have been
442      * constructed in the new network.
443      * @param newNetwork Network; the new network to which the clone belongs
444      * @param newSimulator SimulatorInterface.TimeDoubleUnit; the new simulator for this network
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             throws NetworkException
451     {
452         OTSNode clone = (OTSNode) newNetwork.getNode(this.id);
453         if (this.connections != null)
454         {
455             Map<GTUType, Map<Link, Set<Link>>> newConnections = new HashMap<>();
456             for (GTUType gtuType : this.connections.keySet())
457             {
458                 Map<Link, Set<Link>> newConnMap = new HashMap<>();
459                 for (Link link : this.connections.get(gtuType).keySet())
460                 {
461                     Set<Link> newLinkSet = new HashSet<>();
462                     for (Link setLink : this.connections.get(gtuType).get(link))
463                     {
464                         newLinkSet.add(newNetwork.getLink(setLink.getId()));
465                     }
466                     newConnMap.put(newNetwork.getLink(link.getId()), newLinkSet);
467                 }
468                 newConnections.put(gtuType, newConnMap);
469             }
470             clone.connections = newConnections;
471         }
472         return clone;
473     }
474 
475 }