View Javadoc
1   package org.opentrafficsim.core.network;
2   
3   import java.util.Collection;
4   import java.util.HashSet;
5   import java.util.Set;
6   
7   import org.opentrafficsim.core.unit.LengthUnit;
8   import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
9   
10  /**
11   * A Network consists of a set of links. Each link has, in its turn, a start node and an end node. An expandable network can be
12   * an (expanded) node as well. An example is shown below:
13   * 
14   * <pre>
15   *            |
16   *     -------O--------
17   *            |
18   * </pre>
19   * 
20   * can be expanded into:
21   * 
22   * <pre>
23   *            |
24   *            A
25   *           /|\
26   *          / | \
27   *    -----B--C--D-----
28   *          \ | /
29   *           \|/
30   *            E
31   *            |
32   * </pre>
33   * 
34   * Node O in the example is expanded into the subnetwork consisting of nodes A, B, C, D, and E, and links AB, AC, AD, BC, CD,
35   * BE, CE, and DE. It also means that when node expansion takes place, the links to node O have to be replaced. In the example
36   * below:
37   * 
38   * <pre>
39   *            X
40   *            |
41   *     Y------O-------Z
42   *            |
43   *            W
44   * </pre>
45   * 
46   * can be expanded into:
47   * 
48   * <pre>
49   *            X
50   *            |
51   *            A
52   *           /|\
53   *          / | \
54   *    Y----B--C--D----Z
55   *          \ | /
56   *           \|/
57   *            E
58   *            |
59   *            W
60   * </pre>
61   * 
62   * the node XO is replaced by XA, YO is replaced by YB, OZ is replaced by DZ, and OW is replaced by EW in the network. The
63   * reverse takes place when we do node collapse.
64   * <p>
65   * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
66   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
67   * <p>
68   * @version Aug 19, 2014 <br>
69   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
70   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
71   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
72   * @param <ID> the ID type of the network.
73   * @param <L>
74   */
75  public class ExpansionNetwork<ID, L extends Link<?, ?>> extends Network<ID, L>
76  {
77      /** */
78      private static final long serialVersionUID = 20150104L;
79  
80      /** Node of which this network is an expansion. */
81      private Node<?, ?> expansionOfNode = null;
82  
83      /**
84       * Construction of an empty network.
85       * @param id the network id.
86       */
87      public ExpansionNetwork(final ID id)
88      {
89          super(id);
90      }
91  
92      /**
93       * Construction of a network with an initial set of links.
94       * @param id the network id.
95       * @param collection the initial collection of links.
96       */
97      public ExpansionNetwork(final ID id, final Collection<? extends L> collection)
98      {
99          super(id, collection);
100     }
101 
102     /**
103      * Construction of a network with an initial set of links, and an expansion node.
104      * @param id the network id.
105      * @param collection the initial collection of links.
106      * @param expansionNode Node of which this network is an expansion.
107      * @throws NetworkException when expansion node is part of the initial collection.
108      */
109     public ExpansionNetwork(final ID id, final Collection<? extends L> collection, final Node<?, ?> expansionNode)
110         throws NetworkException
111     {
112         super(id, collection);
113         if (collection.contains(expansionNode))
114         {
115             throw new NetworkException("Creating Network " + id + " with initial collection. Expansion node "
116                 + expansionNode.toString() + " is part of the initial collection");
117         }
118         this.expansionOfNode = expansionNode;
119     }
120 
121     /**
122      * @return expansionOfNode
123      */
124     public final Node<?, ?> getExpansionOfNode()
125     {
126         return this.expansionOfNode;
127     }
128 
129     /**
130      * @param expansionOfNode set expansionOfNode
131      * @throws NetworkException when expansion node is part of the node collection.
132      */
133     public final void setExpansionOfNode(final Node<?, ?> expansionOfNode) throws NetworkException
134     {
135         this.expansionOfNode = expansionOfNode;
136     }
137 
138     /**
139      * Determine if a node is part of this Network.
140      * @param node Node&lt;?, ?&gt;; the node
141      * @param recurse boolean; if true also search sub-networks
142      * @return true or false
143      */
144     public final boolean isInNetwork(final Node<?, ?> node, final boolean recurse)
145     {
146         if (isInNetwork(node))
147         {
148             return true;
149         }
150         else if (recurse)
151         {
152             for (Node<?, ?> n : getNodeSet())
153             {
154                 // FIXME if (n instanceof AbstractExpansionNode
155                 // && ((AbstractExpansionNode<?, ?>) n).getNetwork().isInNetwork(node, true))
156                 {
157                     return true;
158                 }
159             }
160         }
161         return false;
162     }
163 
164     /**
165      * Return the sub network that directly owns a specified node.
166      * @param node Node&lt;?, ?&gt;; the node
167      * @return Network
168      * @throws NetworkException if the specified node is not contained in this Network or any of its sub Networks
169      */
170     public final ExpansionNetwork<?, ?> getSubNetworkConsistNode(final Node<?, ?> node) throws NetworkException
171     {
172         if (isInNetwork(node, true))
173         {
174             // FIXME going through the tree once more is inefficient
175             if (isInNetwork(node, false))
176             {
177                 return this;
178             }
179             else
180             {
181                 for (Node<?, ?> n : getNodeSet())
182                 {
183                     // FIXME if (n instanceof AbstractExpansionNode
184                     // && ((AbstractExpansionNode<?, ?>) n).getNetwork().isInNetwork(node, false))
185                     {
186                         return getSubNetworkConsistNode(node);
187                     }
188                 }
189             }
190         }
191         throw new NetworkException("The network does not contain the Node" + node.getId().toString() + ".");
192     }
193 
194     /**
195      * Delete a node from this network (or a sub-network of this network).
196      * @param deleteThis Node&lt;?, ?&gt;; the node that must be deleted
197      * @return boolean
198      * @throws NetworkException on network inconsistency
199      */
200     public final boolean deleteNode(final Node<?, ?> deleteThis) throws NetworkException
201     {
202         // TODO ensure that no links are orphaned due to removal of the node
203         if (isInNetwork(deleteThis, true))
204         {
205             // FIXME inefficient (searches once more)
206             if (isInNetwork(deleteThis, false))
207             {
208                 getNodeSet().remove(deleteThis);
209                 return true;
210             }
211             else
212             {
213                 ExpansionNetwork<?, ?> n = getSubNetworkConsistNode(deleteThis);
214                 n.remove(deleteThis);
215                 return true;
216             }
217         }
218         throw new NetworkException("Deleting" + deleteThis.getId().toString() + "is failed. Possible cause:"
219             + " node is not a member of the given Network");
220     }
221 
222     /**
223      * Collapse a nodes into a sub network.
224      * @param nodesOfSubNetwork HashSet&lt;Node&lt;?, ?&gt;&gt;; the nodes that go into the new sub network
225      * @return boolean; Currently always true (because some checks have not been implemented...)
226      */
227     public final boolean collapseToNode(final HashSet<Node<?, ?>> nodesOfSubNetwork)
228     {
229         Link<?, Node<?, ?>>[] setOfLinks = (Link<?, Node<?, ?>>[]) super.toArray();
230         Set<L> insideLinks = new HashSet<L>();
231         Set<L> neighbourLinks = new HashSet<L>();
232 
233         for (Node<?, ?> node : nodesOfSubNetwork)
234         {
235             for (Link<?, Node<?, ?>> link : setOfLinks)
236             {
237                 if (nodesOfSubNetwork.contains(link.getStartNode()) && nodesOfSubNetwork.contains(link.getEndNode()))
238                 // wrong: if (node.equals(link.getStartNode()) && node.equals(link.getEndNode()))
239                 {
240                     // This link is internal to the collapsing area
241                     insideLinks.add((L) link);
242                     // Subnetwork add link
243                     super.remove(link);
244                     // Still wrong; attempts to remove the link N times and adds it N times.
245                     getNodeSet().remove(node);
246                 }
247                 else if (node.equals(link.getStartNode()) || node.equals(link.getEndNode()))
248                 {
249                     // Link connects an internal node to an external node
250                     neighbourLinks.add((L) link);
251 
252                     super.remove(link);
253                     getNodeSet().remove(node);
254                 }
255                 // else This link is not part of the collapsed area;
256             }
257         }
258 
259         // add nodes to subnetwork
260         // add links to subnetwork
261         // constructor call new node with new links
262         // TODO add replacement links for each outgoing or incoming link (these are listed in neighborLinks).
263         // TODO create a Node that owns the sub network (includes figuring out the location for it)
264 
265         return true;
266     }
267 
268     /**
269      * Collapse all links between two given nodes into one forward and one reverse link.
270      * @param node1 Node; the first node
271      * @param node2 Node; the second node
272      * @return true if successful, false there are no links between the nodes
273      */
274     public final boolean collapseLinks(final Node<?, ?> node1, final Node<?, ?> node2)
275     {
276         Link<?, Node<?, ?>>[] setOfLinks = (Link<?, Node<?, ?>>[]) super.toArray();
277         float forwardCapacity = 0.0f; // One direction
278         float reverseCapacity = 0.0f; // Other direction
279         DoubleScalar.Rel<LengthUnit> shortestLengthFrom1 =
280             new DoubleScalar.Rel<LengthUnit>(Double.MAX_VALUE, LengthUnit.METER);
281         DoubleScalar.Rel<LengthUnit> shortestLengthFrom2 =
282             new DoubleScalar.Rel<LengthUnit>(Double.MAX_VALUE, LengthUnit.METER);
283 
284         int forwardLinksFound = 0;
285         int reverseLinksFound = 0;
286         for (Link<?, Node<?, ?>> link : setOfLinks)
287         {
288             if (node1.equals(link.getStartNode()) && node2.equals(link.getEndNode()))
289             {
290                 super.remove(link);
291 
292                 forwardCapacity += link.getCapacity().floatValue();
293 
294                 if (shortestLengthFrom1.floatValue() > link.getLength().floatValue())
295                 {
296                     shortestLengthFrom1 = link.getLength();
297                 }
298                 forwardLinksFound++;
299             }
300             else if (node2.equals(link.getStartNode()) && node1.equals(link.getEndNode()))
301             {
302                 super.remove(link);
303 
304                 reverseCapacity += link.getCapacity().floatValue();
305 
306                 if (shortestLengthFrom2.floatValue() > link.getLength().floatValue())
307                 {
308                     shortestLengthFrom2 = link.getLength();
309                 }
310                 reverseLinksFound++;
311             }
312         }
313         if (0 == forwardLinksFound && 0 == reverseLinksFound)
314         {
315             return false;
316         }
317 
318         if (forwardLinksFound > 0)
319         {
320             // FIXME Link<?, ?> newLinkFrom1 = new Link(node1.getId(), node1, node2, shortestLengthFrom1);
321             // newLinkFrom1.setCapacity(new DoubleScalar.Abs<FrequencyUnit>(forwardCapacity, FrequencyUnit.PER_SECOND));
322             // super.add((L) newLinkFrom1);
323         }
324         if (reverseLinksFound > 0)
325         {
326             // FIXME Link<?, ?> newLinkFrom2 = new Link(node2.getId(), node2, node1, shortestLengthFrom2);
327             // newLinkFrom2.setCapacity(new DoubleScalar.Abs<FrequencyUnit>(reverseCapacity, FrequencyUnit.PER_SECOND));
328             // super.add((L) newLinkFrom2);
329         }
330         return true;
331     }
332 
333     /**
334      * Find all links that have a hierarchy level not exceeding the specified value. <br>
335      * Highest hierarchy value is 0; 1 is next lower level, etc.
336      * @param hierarchyLevel int; the maximum hierarchy level of the returned links
337      * @return Set&lt;L&gt;
338      * @throws NetworkException on network inconsistency
339      */
340     public final Set<L> findLinkHierarchyBelow(final int hierarchyLevel) throws NetworkException
341     {
342         Link<?, Node<?, ?>>[] setOfLinks = (Link<?, Node<?, ?>>[]) super.toArray();
343         Set<L> linksAboveLevel = new HashSet<L>();
344 
345         for (Link<?, Node<?, ?>> link : setOfLinks)
346         {
347             // if (link.getHierarchy() > hierarchyLevel)
348             // {
349             // linksAboveLevel.add((L) link);
350             // }
351         }
352         return linksAboveLevel;
353     }
354 
355 }