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