View Javadoc
1   package org.opentrafficsim.core.network;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.HashSet;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  
13  import javax.naming.Binding;
14  import javax.naming.NamingEnumeration;
15  import javax.naming.NamingException;
16  import javax.naming.event.EventContext;
17  
18  import org.jgrapht.GraphPath;
19  import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
20  import org.jgrapht.graph.SimpleDirectedWeightedGraph;
21  import org.opentrafficsim.core.animation.ClonableRenderable2DInterface;
22  import org.opentrafficsim.core.gtu.GTU;
23  import org.opentrafficsim.core.gtu.GTUType;
24  import org.opentrafficsim.core.network.route.CompleteRoute;
25  import org.opentrafficsim.core.network.route.Route;
26  import org.opentrafficsim.core.object.InvisibleObjectInterface;
27  import org.opentrafficsim.core.object.ObjectInterface;
28  import org.opentrafficsim.core.perception.PerceivableContext;
29  
30  import nl.tudelft.simulation.dsol.animation.Locatable;
31  import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
32  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
33  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
34  import nl.tudelft.simulation.event.EventProducer;
35  import nl.tudelft.simulation.immutablecollections.Immutable;
36  import nl.tudelft.simulation.immutablecollections.ImmutableHashMap;
37  import nl.tudelft.simulation.immutablecollections.ImmutableMap;
38  import nl.tudelft.simulation.naming.context.ContextUtil;
39  
40  /**
41   * A Network consists of a set of links. Each link has, in its turn, a start node and an end node.
42   * <p>
43   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
44   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
45   * <p>
46   * $LastChangedDate: 2018-09-19 13:55:45 +0200 (Wed, 19 Sep 2018) $, @version $Revision: 4006 $, by $Author: averbraeck $,
47   * initial version Jul 22, 2015 <br>
48   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
49   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
50   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
51   */
52  public class OTSNetwork extends EventProducer implements Network, PerceivableContext, Serializable
53  {
54      /** */
55      private static final long serialVersionUID = 20150722;
56  
57      /** Id of this network. */
58      private final String id;
59  
60      /** Map of Nodes. */
61      private Map<String, Node> nodeMap = new HashMap<>();
62  
63      /** Map of Links. */
64      private Map<String, Link> linkMap = new HashMap<>();
65  
66      /** Map of ObjectInterface. */
67      private Map<String, ObjectInterface> objectMap = new HashMap<>();
68  
69      /** Map of InvisibleObjects. */
70      private Map<String, InvisibleObjectInterface> invisibleObjectMap = new HashMap<>();
71  
72      /** Map of Routes. */
73      private Map<GTUType, Map<String, Route>> routeMap = new HashMap<>();
74  
75      /** Graphs to calculate shortest paths per GTUType. */
76      private Map<GTUType, SimpleDirectedWeightedGraph<Node, LinkEdge<Link>>> linkGraphs = new HashMap<>();
77  
78      /** GTUs registered in this network. */
79      private Map<String, GTU> gtuMap = new HashMap<>();
80  
81      /**
82       * Construction of an empty network.
83       * @param id the network id.
84       */
85      public OTSNetwork(final String id)
86      {
87          this.id = id;
88      }
89  
90      /** {@inheritDoc} */
91      @Override
92      public final String getId()
93      {
94          return this.id;
95      }
96  
97      /***************************************************************************************/
98      /**************************************** NODES ****************************************/
99      /***************************************************************************************/
100 
101     /** {@inheritDoc} */
102     @Override
103     public final ImmutableMap<String, Node> getNodeMap()
104     {
105         return new ImmutableHashMap<>(this.nodeMap, Immutable.WRAP);
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public final void addNode(final Node node) throws NetworkException
111     {
112         if (containsNode(node))
113         {
114             throw new NetworkException("Node " + node + " already registered in network " + this.id);
115         }
116         if (this.nodeMap.keySet().contains(node.getId()))
117         {
118             throw new NetworkException("Node with name " + node.getId() + " already registered in network " + this.id);
119         }
120         this.nodeMap.put(node.getId(), node);
121         fireEvent(Network.NODE_ADD_EVENT, node.getId());
122     }
123 
124     /** {@inheritDoc} */
125     @Override
126     public final void removeNode(final Node node) throws NetworkException
127     {
128         if (!containsNode(node))
129         {
130             throw new NetworkException("Node " + node + " not registered in network " + this.id);
131         }
132         fireEvent(Network.NODE_REMOVE_EVENT, node.getId());
133         this.nodeMap.remove(node.getId());
134     }
135 
136     /** {@inheritDoc} */
137     @Override
138     public final boolean containsNode(final Node node)
139     {
140         // System.out.println(node);
141         return this.nodeMap.keySet().contains(node.getId());
142     }
143 
144     /** {@inheritDoc} */
145     @Override
146     public final boolean containsNode(final String nodeId)
147     {
148         return this.nodeMap.keySet().contains(nodeId);
149     }
150 
151     /** {@inheritDoc} */
152     @Override
153     public final Node getNode(final String nodeId)
154     {
155         return this.nodeMap.get(nodeId);
156     }
157 
158     /***************************************************************************************/
159     /**************************************** LINKS ****************************************/
160     /***************************************************************************************/
161 
162     /** {@inheritDoc} */
163     @Override
164     public final ImmutableMap<String, Link> getLinkMap()
165     {
166         return new ImmutableHashMap<>(this.linkMap, Immutable.WRAP);
167     }
168 
169     /** {@inheritDoc} */
170     @Override
171     public final void addLink(final Link link) throws NetworkException
172     {
173         if (containsLink(link))
174         {
175             throw new NetworkException("Link " + link + " already registered in network " + this.id);
176         }
177         if (this.linkMap.keySet().contains(link.getId()))
178         {
179             throw new NetworkException("Link with name " + link.getId() + " already registered in network " + this.id);
180         }
181         if (!containsNode(link.getStartNode()) || !containsNode(link.getEndNode()))
182         {
183             throw new NetworkException(
184                     "Start node or end node of Link " + link.getId() + " not registered in network " + this.id);
185         }
186         this.linkMap.put(link.getId(), link);
187         fireEvent(Network.LINK_ADD_EVENT, link.getId());
188     }
189 
190     /** {@inheritDoc} */
191     @Override
192     public final void removeLink(final Link link) throws NetworkException
193     {
194         if (!containsLink(link))
195         {
196             throw new NetworkException("Link " + link + " not registered in network " + this.id);
197         }
198         fireEvent(Network.LINK_REMOVE_EVENT, link.getId());
199         this.linkMap.remove(link.getId());
200     }
201 
202     /** {@inheritDoc} */
203     @Override
204     public final Link getLink(final Node node1, final Node node2)
205     {
206         for (Link link : this.linkMap.values())
207         {
208             if (link.getStartNode().equals(node1) && link.getEndNode().equals(node2))
209             {
210                 return link;
211             }
212         }
213         return null;
214     }
215 
216     /** {@inheritDoc} */
217     @Override
218     public final Link getLink(final String nodeId1, final String nodeId2) throws NetworkException
219     {
220         if (!containsNode(nodeId1))
221         {
222             throw new NetworkException("Node " + nodeId1 + " not in network " + this.id);
223         }
224         if (!containsNode(nodeId2))
225         {
226             throw new NetworkException("Node " + nodeId2 + " not in network " + this.id);
227         }
228         return getLink(getNode(nodeId1), getNode(nodeId2));
229     }
230 
231     /** {@inheritDoc} */
232     @Override
233     public final boolean containsLink(final Link link)
234     {
235         return this.linkMap.keySet().contains(link.getId());
236     }
237 
238     /** {@inheritDoc} */
239     @Override
240     public final boolean containsLink(final String linkId)
241     {
242         return this.linkMap.keySet().contains(linkId);
243     }
244 
245     /** {@inheritDoc} */
246     @Override
247     public final Link getLink(final String linkId)
248     {
249         return this.linkMap.get(linkId);
250     }
251 
252     /***************************************************************************************/
253     /************************ OBJECT INTERFACE IMPLEMENTING OBJECTS ************************/
254     /***************************************************************************************/
255 
256     /** {@inheritDoc} */
257     @Override
258     public final ImmutableMap<String, ObjectInterface> getObjectMap()
259     {
260         return new ImmutableHashMap<>(this.objectMap, Immutable.WRAP);
261     }
262 
263     /** {@inheritDoc} */
264     @SuppressWarnings("unchecked")
265     @Override
266     public final <T extends ObjectInterface> ImmutableMap<String, T> getObjectMap(final Class<T> objectType)
267     {
268         Map<String, T> result = new HashMap<>();
269         for (String key : this.objectMap.keySet())
270         {
271             ObjectInterface o = this.objectMap.get(key);
272             if (objectType.isInstance(o))
273             {
274                 result.put(key, (T) o);
275             }
276         }
277         return new ImmutableHashMap<>(result, Immutable.WRAP);
278     }
279 
280     /** {@inheritDoc} */
281     @Override
282     public final void addObject(final ObjectInterface object) throws NetworkException
283     {
284         if (containsObject(object))
285         {
286             throw new NetworkException("Object " + object + " already registered in network " + this.id);
287         }
288         if (containsObject(object.getFullId()))
289         {
290             throw new NetworkException("Object with name " + object.getFullId() + " already registered in network " + this.id);
291         }
292         this.objectMap.put(object.getFullId(), object);
293         fireEvent(Network.OBJECT_ADD_EVENT, object.getFullId());
294     }
295 
296     /** {@inheritDoc} */
297     @Override
298     public final void removeObject(final ObjectInterface object) throws NetworkException
299     {
300         if (!containsObject(object))
301         {
302             throw new NetworkException("Object " + object + " not registered in network " + this.id);
303         }
304         fireEvent(Network.OBJECT_REMOVE_EVENT, object.getFullId());
305         this.objectMap.remove(object.getFullId());
306     }
307 
308     /** {@inheritDoc} */
309     @Override
310     public final boolean containsObject(final ObjectInterface object)
311     {
312         return this.objectMap.containsKey(object.getFullId());
313     }
314 
315     /**
316      * {@inheritDoc}
317      * <p>
318      * Note that the objectId should be the <b>fullId</b> of the object, including any additions such as lane ids, link ids,
319      * etc.
320      */
321     @Override
322     public final boolean containsObject(final String objectId)
323     {
324         return this.objectMap.containsKey(objectId);
325     }
326 
327     /***************************************************************************************/
328     /********************************* INVISIBLE OBJECTS ***********************************/
329     /***************************************************************************************/
330 
331     /** {@inheritDoc} */
332     @Override
333     public final ImmutableMap<String, InvisibleObjectInterface> getInvisibleObjectMap()
334     {
335         return new ImmutableHashMap<>(this.invisibleObjectMap, Immutable.WRAP);
336     }
337 
338     /** {@inheritDoc} */
339     @Override
340     public final ImmutableMap<String, InvisibleObjectInterface> getInvisibleObjectMap(
341             final Class<InvisibleObjectInterface> objectType)
342     {
343         Map<String, InvisibleObjectInterface> result = new HashMap<>();
344         for (String key : this.objectMap.keySet())
345         {
346             InvisibleObjectInterface o = this.invisibleObjectMap.get(key);
347             if (objectType.isInstance(o))
348             {
349                 result.put(key, o);
350             }
351         }
352         return new ImmutableHashMap<>(result, Immutable.WRAP);
353     }
354 
355     /** {@inheritDoc} */
356     @Override
357     public final void addInvisibleObject(final InvisibleObjectInterface object) throws NetworkException
358     {
359         if (containsInvisibleObject(object))
360         {
361             throw new NetworkException("InvisibleObject " + object + " already registered in network " + this.id);
362         }
363         if (containsInvisibleObject(object.getFullId()))
364         {
365             throw new NetworkException(
366                     "InvisibleObject with name " + object.getFullId() + " already registered in network " + this.id);
367         }
368         this.invisibleObjectMap.put(object.getFullId(), object);
369         fireEvent(Network.INVISIBLE_OBJECT_ADD_EVENT, object.getFullId());
370     }
371 
372     /** {@inheritDoc} */
373     @Override
374     public final void removeInvisibleObject(final InvisibleObjectInterface object) throws NetworkException
375     {
376         if (!containsInvisibleObject(object))
377         {
378             throw new NetworkException("InvisibleObject " + object + " not registered in network " + this.id);
379         }
380         fireEvent(Network.INVISIBLE_OBJECT_REMOVE_EVENT, object.getFullId());
381         this.objectMap.remove(object.getFullId());
382     }
383 
384     /** {@inheritDoc} */
385     @Override
386     public final boolean containsInvisibleObject(final InvisibleObjectInterface object)
387     {
388         return this.invisibleObjectMap.containsKey(object.getFullId());
389     }
390 
391     /**
392      * {@inheritDoc}
393      * <p>
394      * Note that the objectId should be the <b>fullId</b> of the object, including any additions such as lane ids, link ids,
395      * etc.
396      */
397     @Override
398     public final boolean containsInvisibleObject(final String objectId)
399     {
400         return this.invisibleObjectMap.containsKey(objectId);
401     }
402 
403     /***************************************************************************************/
404     /*************************************** ROUTES ****************************************/
405     /***************************************************************************************/
406 
407     /** {@inheritDoc} */
408     @Override
409     public final ImmutableMap<String, Route> getDefinedRouteMap(final GTUType gtuType)
410     {
411         Map<String, Route> routes = new HashMap<>();
412         if (this.routeMap.containsKey(gtuType))
413         {
414             routes.putAll(this.routeMap.get(gtuType));
415         }
416         return new ImmutableHashMap<>(routes, Immutable.WRAP);
417     }
418 
419     /** {@inheritDoc} */
420     @Override
421     public final void addRoute(final GTUType gtuType, final Route route) throws NetworkException
422     {
423         if (containsRoute(gtuType, route))
424         {
425             throw new NetworkException(
426                     "Route " + route + " for GTUType " + gtuType + " already registered in network " + this.id);
427         }
428         if (this.routeMap.containsKey(gtuType) && this.routeMap.get(gtuType).keySet().contains(route.getId()))
429         {
430             throw new NetworkException("Route with name " + route.getId() + " for GTUType " + gtuType
431                     + " already registered in network " + this.id);
432         }
433         for (Node node : route.getNodes())
434         {
435             if (!containsNode(node))
436             {
437                 throw new NetworkException("Node " + node.getId() + " of route " + route.getId() + " for GTUType " + gtuType
438                         + " not registered in network " + this.id);
439             }
440         }
441         if (!this.routeMap.containsKey(gtuType))
442         {
443             this.routeMap.put(gtuType, new HashMap<String, Route>());
444         }
445         this.routeMap.get(gtuType).put(route.getId(), route);
446         fireEvent(Network.ROUTE_ADD_EVENT, new Object[] { gtuType.getId(), route.getId() });
447     }
448 
449     /** {@inheritDoc} */
450     @Override
451     public final void removeRoute(final GTUType gtuType, final Route route) throws NetworkException
452     {
453         if (!containsRoute(gtuType, route))
454         {
455             throw new NetworkException("Route " + route + " for GTUType " + gtuType + " not registered in network " + this.id);
456         }
457         fireEvent(Network.ROUTE_REMOVE_EVENT, new Object[] { gtuType.getId(), route.getId() });
458         this.routeMap.get(gtuType).remove(route.getId());
459     }
460 
461     /** {@inheritDoc} */
462     @Override
463     public final boolean containsRoute(final GTUType gtuType, final Route route)
464     {
465         if (this.routeMap.containsKey(gtuType))
466         {
467             return this.routeMap.get(gtuType).values().contains(route);
468         }
469         return false;
470     }
471 
472     /** {@inheritDoc} */
473     @Override
474     public final boolean containsRoute(final GTUType gtuType, final String routeId)
475     {
476         if (this.routeMap.containsKey(gtuType))
477         {
478             return this.routeMap.get(gtuType).keySet().contains(routeId);
479         }
480         return false;
481     }
482 
483     /**
484      * Returns the route with given id or {@code null} if no such route is available.
485      * @param routeId String; route id
486      * @return route with given id or {@code null} if no such route is available
487      */
488     public final Route getRoute(final String routeId)
489     {
490         for (GTUType gtuType : this.routeMap.keySet())
491         {
492             Route route = this.routeMap.get(gtuType).get(routeId);
493             if (route != null)
494             {
495                 return route;
496             }
497         }
498         return null;
499     }
500 
501     /** {@inheritDoc} */
502     @Override
503     public final Route getRoute(final GTUType gtuType, final String routeId)
504     {
505         if (this.routeMap.containsKey(gtuType))
506         {
507             return this.routeMap.get(gtuType).get(routeId);
508         }
509         return null;
510     }
511 
512     /** {@inheritDoc} */
513     @Override
514     public final Set<Route> getRoutesBetween(final GTUType gtuType, final Node nodeFrom, final Node nodeTo)
515     {
516         Set<Route> routes = new LinkedHashSet<>();
517         if (this.routeMap.containsKey(gtuType))
518         {
519             for (Route route : this.routeMap.get(gtuType).values())
520             {
521                 try
522                 {
523                     if (route.originNode().equals(nodeFrom) && route.destinationNode().equals(nodeTo))
524                     {
525                         routes.add(route);
526                     }
527                 }
528                 catch (@SuppressWarnings("unused") NetworkException ne)
529                 {
530                     // thrown if no nodes exist in the route. Do not add the route in that case.
531                 }
532             }
533         }
534         return routes;
535     }
536 
537     /** {@inheritDoc} */
538     @Override
539     public final void buildGraph(final GTUType gtuType)
540     {
541         SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> graph = buildGraph(gtuType, LinkWeight.LENGTH);
542         this.linkGraphs.put(gtuType, graph);
543     }
544 
545     /**
546      * Builds a graph using the specified link weight.
547      * @param gtuType GTUType; GTU type
548      * @param linkWeight LinkWeight; link weight
549      * @return SimpleDirectedWeightedGraph graph
550      */
551     private SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> buildGraph(final GTUType gtuType, final LinkWeight linkWeight)
552     {
553         // TODO: take connections into account, and possibly do node expansion to build the graph
554         @SuppressWarnings({ "unchecked" })
555         // TODO: the next line with .class has problems compiling... So used a dirty hack instead for now...
556         Class<LinkEdge<Link>> linkEdgeClass = (Class<LinkEdge<Link>>) new LinkEdge<OTSLink>(null).getClass();
557         SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> graph = new SimpleDirectedWeightedGraph<>(linkEdgeClass);
558         for (Node node : this.nodeMap.values())
559         {
560             graph.addVertex(node);
561         }
562         for (Link link : this.linkMap.values())
563         {
564             // determine if the link is accessible for the GTUType , and in which direction(s)
565             LongitudinalDirectionality directionality = link.getDirectionality(gtuType);
566             if (directionality.isForwardOrBoth())
567             {
568                 LinkEdge<Link> linkEdge = new LinkEdge<>(link);
569                 graph.addEdge(link.getStartNode(), link.getEndNode(), linkEdge);
570                 graph.setEdgeWeight(linkEdge, linkWeight.getWeight(link));
571             }
572             if (directionality.isBackwardOrBoth())
573             {
574                 LinkEdge<Link> linkEdge = new LinkEdge<>(link);
575                 graph.addEdge(link.getEndNode(), link.getStartNode(), linkEdge);
576                 graph.setEdgeWeight(linkEdge, linkWeight.getWeight(link));
577             }
578         }
579         return graph;
580     }
581 
582     /** {@inheritDoc} */
583     @Override
584     public final CompleteRoute getShortestRouteBetween(final GTUType gtuType, final Node nodeFrom, final Node nodeTo,
585             final LinkWeight linkWeight) throws NetworkException
586     {
587         CompleteRoute route = new CompleteRoute("Route for " + gtuType + " from " + nodeFrom + "to " + nodeTo, gtuType);
588         SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> graph = getGraph(gtuType, linkWeight);
589         // DijkstraShortestPath<Node, LinkEdge<Link>> dijkstra = new DijkstraShortestPath<>(graph);
590         // GraphPath<Node, LinkEdge<Link>> path = dijkstra.getPath(nodeFrom, nodeTo);
591         GraphPath<Node, LinkEdge<Link>> path = DijkstraShortestPath.findPathBetween(graph, nodeFrom, nodeTo);
592         if (path == null)
593         {
594             return null;
595         }
596         route.addNode(nodeFrom);
597         for (LinkEdge<Link> link : path.getEdgeList())
598         {
599             if (!link.getLink().getEndNode().equals(route.destinationNode())
600                     && route.destinationNode().isDirectionallyConnectedTo(gtuType, link.getLink().getEndNode()))
601             {
602                 route.addNode(link.getLink().getEndNode());
603             }
604             else if (!link.getLink().getStartNode().equals(route.destinationNode())
605                     && route.destinationNode().isDirectionallyConnectedTo(gtuType, link.getLink().getStartNode()))
606             {
607                 route.addNode(link.getLink().getStartNode());
608             }
609             else
610             {
611                 throw new NetworkException("Cannot connect two links when calculating shortest route");
612             }
613         }
614         return route;
615     }
616 
617     /** {@inheritDoc} */
618     @Override
619     public final CompleteRoute getShortestRouteBetween(final GTUType gtuType, final Node nodeFrom, final Node nodeTo,
620             final List<Node> nodesVia) throws NetworkException
621     {
622         return getShortestRouteBetween(gtuType, nodeFrom, nodeTo, nodesVia, LinkWeight.LENGTH);
623     }
624 
625     /** {@inheritDoc} */
626     @Override
627     public final CompleteRoute getShortestRouteBetween(final GTUType gtuType, final Node nodeFrom, final Node nodeTo,
628             final List<Node> nodesVia, final LinkWeight linkWeight) throws NetworkException
629     {
630         CompleteRoute route = new CompleteRoute(
631                 "Route for " + gtuType + " from " + nodeFrom + "to " + nodeTo + " via " + nodesVia.toString(), gtuType);
632         SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> graph = getGraph(gtuType, linkWeight);
633         List<Node> nodes = new ArrayList<>();
634         nodes.add(nodeFrom);
635         nodes.addAll(nodesVia);
636         nodes.add(nodeTo);
637         Node from = nodeFrom;
638         route.addNode(nodeFrom);
639         for (int i = 1; i < nodes.size(); i++)
640         {
641             Node to = nodes.get(i);
642             DijkstraShortestPath<Node, LinkEdge<Link>> dijkstra = new DijkstraShortestPath<>(graph);
643             GraphPath<Node, LinkEdge<Link>> path = dijkstra.getPath(from, to);
644             if (path == null)
645             {
646                 return null;
647             }
648             for (LinkEdge<Link> link : path.getEdgeList())
649             {
650                 if (!link.getLink().getEndNode().equals(route.destinationNode())
651                         && route.destinationNode().isDirectionallyConnectedTo(gtuType, link.getLink().getEndNode()))
652                 {
653                     route.addNode(link.getLink().getEndNode());
654                 }
655                 else if (!link.getLink().getStartNode().equals(route.destinationNode())
656                         && route.destinationNode().isDirectionallyConnectedTo(gtuType, link.getLink().getStartNode()))
657                 {
658                     route.addNode(link.getLink().getStartNode());
659                 }
660                 else
661                 {
662                     throw new NetworkException(
663                             "Cannot connect two links when calculating shortest route with intermediate nodes");
664                 }
665             }
666             from = to;
667         }
668         return route;
669     }
670 
671     /**
672      * Returns the graph, possibly a stored one.
673      * @param gtuType GTUType; GTU type
674      * @param linkWeight LinkWeight; link weight
675      * @return SimpleDirectedWeightedGraph
676      */
677     private SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> getGraph(final GTUType gtuType, final LinkWeight linkWeight)
678     {
679         SimpleDirectedWeightedGraph<Node, LinkEdge<Link>> graph;
680         if (linkWeight.equals(LinkWeight.LENGTH))
681         {
682             // stored default
683             if (!this.linkGraphs.containsKey(gtuType))
684             {
685                 buildGraph(gtuType);
686             }
687             graph = this.linkGraphs.get(gtuType);
688         }
689         else
690         {
691             graph = buildGraph(gtuType, linkWeight);
692         }
693         return graph;
694     }
695 
696     /***************************************************************************************/
697     /**************************************** GTUs *****************************************/
698     /***************************************************************************************/
699 
700     /** {@inheritDoc} */
701     @Override
702     public final void addGTU(final GTU gtu)
703     {
704         this.gtuMap.put(gtu.getId(), gtu);
705         fireTimedEvent(Network.GTU_ADD_EVENT, gtu.getId(), gtu.getSimulator().getSimulatorTime());
706     }
707 
708     /** {@inheritDoc} */
709     @Override
710     public final void removeGTU(final GTU gtu)
711     {
712         fireTimedEvent(Network.GTU_REMOVE_EVENT, gtu.getId(), gtu.getSimulator().getSimulatorTime());
713         this.gtuMap.remove(gtu.getId());
714     }
715 
716     /** {@inheritDoc} */
717     @Override
718     public final boolean containsGTU(final GTU gtu)
719     {
720         return this.gtuMap.containsValue(gtu);
721     }
722 
723     /** {@inheritDoc} */
724     @Override
725     public final GTU getGTU(final String gtuId)
726     {
727         return this.gtuMap.get(gtuId);
728     }
729 
730     /** {@inheritDoc} */
731     @Override
732     public final Set<GTU> getGTUs()
733     {
734         // defensive copy
735         return new HashSet<>(this.gtuMap.values());
736     }
737 
738     /** {@inheritDoc} */
739     @Override
740     public final boolean containsGtuId(final String gtuId)
741     {
742         return this.gtuMap.containsKey(gtuId);
743     }
744 
745     /** {@inheritDoc} */
746     @Override
747     public final String toString()
748     {
749         return "OTSNetwork [id=" + this.id + ", nodeMapSize=" + this.nodeMap.size() + ", linkMapSize=" + this.linkMap.size()
750                 + ", objectMapSize=" + this.objectMap.size() + ", routeMapSize=" + this.routeMap.size() + ", gtuMapSize="
751                 + this.gtuMap.size() + "]";
752     }
753 
754     /***************************************************************************************/
755     /*************************************** CLONE *****************************************/
756     /***************************************************************************************/
757 
758     /**
759      * Clone the OTSNetwork.
760      * @param newId the new id of the network
761      * @param oldSimulator the old simulator for this network
762      * @param newSimulator the new simulator for this network
763      * @param animation whether to (re)create animation or not
764      * @return a clone of this network
765      * @throws NetworkException in case the cloning fails
766      */
767     @SuppressWarnings("checkstyle:designforextension")
768     public OTSNetwork clone(final String newId, final SimulatorInterface.TimeDoubleUnit oldSimulator,
769             final SimulatorInterface.TimeDoubleUnit newSimulator, final boolean animation) throws NetworkException
770     {
771         OTSNetwork newNetwork = new OTSNetwork(newId);
772 
773         // clone the nodes
774         for (Node node : this.nodeMap.values())
775         {
776             ((OTSNode) node).clone1(newNetwork, newSimulator);
777         }
778 
779         // clone the links
780         for (Link oldLink : this.linkMap.values())
781         {
782             OTSLink newLink = ((OTSLink) oldLink).clone(newNetwork, newSimulator, animation);
783             if (animation)
784             {
785                 cloneAnimation(oldLink, newLink, oldSimulator, newSimulator);
786             }
787         }
788 
789         // make the link-connections for the cloned nodes
790         for (Node oldNode : this.nodeMap.values())
791         {
792             OTSNode newNode = ((OTSNode) oldNode).clone2(newNetwork, newSimulator, animation);
793             if (animation)
794             {
795                 cloneAnimation(oldNode, newNode, oldSimulator, newSimulator);
796             }
797         }
798 
799         // clone the graphs that had been created for the old network
800         for (GTUType gtuType : this.linkGraphs.keySet())
801         {
802             newNetwork.buildGraph(gtuType);
803         }
804 
805         // clone the routes
806         Map<GTUType, Map<String, Route>> newRouteMap = new HashMap<>();
807         for (GTUType gtuType : this.routeMap.keySet())
808         {
809             Map<String, Route> newRoutes = new HashMap<>();
810             for (Route route : this.routeMap.get(gtuType).values())
811             {
812                 newRoutes.put(route.getId(), route.clone(newNetwork, newSimulator, animation));
813             }
814             newRouteMap.put(gtuType, newRoutes);
815         }
816         newNetwork.routeMap = newRouteMap;
817         // clone the traffic lights
818         for (InvisibleObjectInterface io : getInvisibleObjectMap().values())
819         {
820             InvisibleObjectInterface clonedIO = io.clone(newSimulator, newNetwork);
821             newNetwork.addInvisibleObject(clonedIO);
822         }
823         return newNetwork;
824     }
825 
826     /**
827      * Clone all animation objects for the given class. The given class is the <b>source</b> of the animation objects, as it is
828      * not known on beforehand which objects need to be cloned. It is important for cloning that the animation objects implement
829      * the CloneableRenderable2DInterface, so they can be cloned with their properties. If not, they will not be taken into
830      * account for cloning by this method.
831      * @param oldSource the old source object that might have one or more animation objects attached to it
832      * @param newSource the new source object to attach the cloned animation objects to
833      * @param oldSimulator the old simulator when the old objects can be found
834      * @param newSimulator the new simulator where the new simulation objects need to be registered
835      * @param <T> locatable type
836      */
837     @SuppressWarnings("checkstyle:designforextension")
838     public static <T extends Locatable> void cloneAnimation(final Locatable oldSource, final T newSource,
839             final SimulatorInterface.TimeDoubleUnit oldSimulator, final SimulatorInterface.TimeDoubleUnit newSimulator)
840     {
841         if (!(oldSimulator instanceof AnimatorInterface) || !(newSimulator instanceof AnimatorInterface))
842         {
843             return;
844         }
845 
846         try
847         {
848             EventContext context =
849                     (EventContext) ContextUtil.lookup(oldSimulator.getReplication().getContext(), "/animation/2D");
850             NamingEnumeration<Binding> list = context.listBindings("");
851             while (list.hasMore())
852             {
853                 Binding binding = list.next();
854                 @SuppressWarnings("unchecked")
855                 Renderable2DInterface<T> animationObject = (Renderable2DInterface<T>) binding.getObject();
856                 T locatable = animationObject.getSource();
857                 if (oldSource.equals(locatable) && animationObject instanceof ClonableRenderable2DInterface)
858                 {
859                     ((ClonableRenderable2DInterface<T>) animationObject).clone(newSource, newSimulator);
860                 }
861             }
862         }
863         catch (@SuppressWarnings("unused") NamingException | RemoteException exception)
864         {
865             System.err.println("Error when cloning animation objects for object " + oldSource);
866         }
867     }
868 
869     /**
870      * Remove all objects and animation in the network.
871      * @param simulator the simulator of the old network
872      */
873     @SuppressWarnings("checkstyle:designforextension")
874     public void destroy(final SimulatorInterface.TimeDoubleUnit simulator)
875     {
876         for (GTU gtu : this.getGTUs())
877         {
878             gtu.destroy();
879         }
880 
881         Set<Renderable2DInterface<?>> animationObjects = new HashSet<>();
882         try
883         {
884             EventContext context = (EventContext) ContextUtil.lookup(simulator.getReplication().getContext(), "/animation/2D");
885             NamingEnumeration<Binding> list = context.listBindings("");
886             while (list.hasMore())
887             {
888                 Binding binding = list.next();
889                 Renderable2DInterface<?> animationObject = (Renderable2DInterface<?>) binding.getObject();
890                 animationObjects.add(animationObject);
891             }
892 
893             for (Renderable2DInterface<?> ao : animationObjects)
894             {
895                 try
896                 {
897                     ao.destroy();
898                 }
899                 catch (Exception e)
900                 {
901                     //
902                 }
903             }
904         }
905         catch (NamingException exception)
906         {
907             System.err.println("Error when destroying animation objects");
908         }
909 
910         this.nodeMap.clear();
911         this.linkMap.clear();
912         this.linkGraphs.clear();
913         this.routeMap.clear();
914     }
915 
916     /**
917      * Remove all animation objects of the given class.
918      * @param clazz the class to remove the animation objects for
919      * @param oldSimulator the old simulator
920      */
921     @SuppressWarnings("checkstyle:designforextension")
922     public void removeAnimation(final Class<?> clazz, final SimulatorInterface.TimeDoubleUnit oldSimulator)
923     {
924         if (!(oldSimulator instanceof AnimatorInterface))
925         {
926             return;
927         }
928 
929         try
930         {
931             EventContext context =
932                     (EventContext) ContextUtil.lookup(oldSimulator.getReplication().getContext(), "/animation/2D");
933             NamingEnumeration<Binding> list = context.listBindings("");
934             while (list.hasMore())
935             {
936                 Binding binding = list.next();
937                 Renderable2DInterface<?> animationObject = (Renderable2DInterface<?>) binding.getObject();
938                 Locatable locatable = animationObject.getSource();
939                 if (clazz.isAssignableFrom(locatable.getClass()))
940                 {
941                     animationObject.destroy();
942                 }
943             }
944         }
945         catch (@SuppressWarnings("unused") NamingException | RemoteException exception)
946         {
947             System.err.println("Error when destroying animation objects for class " + clazz.getSimpleName());
948         }
949     }
950 
951 }