1   package org.opentrafficsim.road.gtu.strategical.od;
2   
3   import java.util.Iterator;
4   import java.util.LinkedHashMap;
5   import java.util.Map;
6   import java.util.TreeMap;
7   
8   import org.djunits.unit.DurationUnit;
9   import org.djunits.value.ValueRuntimeException;
10  import org.djunits.value.storage.StorageType;
11  import org.djunits.value.vdouble.scalar.Duration;
12  import org.djunits.value.vdouble.vector.DurationVector;
13  import org.djunits.value.vdouble.vector.base.DoubleVector;
14  import org.djutils.exceptions.Throw;
15  import org.opentrafficsim.core.gtu.GTUType;
16  import org.opentrafficsim.core.math.Draw;
17  import org.opentrafficsim.core.network.Link;
18  import org.opentrafficsim.core.network.Node;
19  import org.opentrafficsim.road.network.lane.CrossSectionLink;
20  
21  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
22  import nl.tudelft.simulation.jstats.streams.StreamInterface;
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  public class SplitFraction
36  {
37  
38      
39      private final Node node;
40  
41      
42      private final Interpolation interpolation;
43  
44      
45      private final StreamInterface random;
46  
47      
48      private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
49  
50      
51      private final Map<GTUType, Map<Link, Map<Duration, Double>>> fractions = new LinkedHashMap<>();
52  
53      
54  
55  
56  
57  
58  
59  
60      public SplitFraction(final Node node, final Interpolation interpolation, final StreamInterface random,
61              final DEVSSimulatorInterface.TimeDoubleUnit simulator)
62      {
63          this.node = node;
64          this.interpolation = interpolation;
65          this.random = random;
66          this.simulator = simulator;
67      }
68  
69      
70  
71  
72  
73  
74      public void addFraction(final Link link, final double fraction)
75      {
76          addFraction(link, link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), fraction);
77      }
78  
79      
80  
81  
82  
83  
84  
85      public void addFraction(final Link link, final GTUType gtuType, final double fraction)
86      {
87          double[] fracs = new double[2];
88          fracs[0] = fraction;
89          fracs[1] = fraction;
90          DurationVector time;
91          try
92          {
93              double[] t = new double[2];
94              t[1] = Double.MAX_VALUE;
95              time = DoubleVector.instantiate(t, DurationUnit.SI, StorageType.DENSE);
96          }
97          catch (ValueRuntimeException exception)
98          {
99              
100             throw new RuntimeException("Input null while creating duration vector.", exception);
101         }
102         addFraction(link, gtuType, time, fracs);
103     }
104 
105     
106 
107 
108 
109 
110 
111     public void addFraction(final Link link, final DurationVector time, final double[] fraction)
112     {
113         addFraction(link, link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), time, fraction);
114     }
115 
116     
117 
118 
119 
120 
121 
122 
123     public void addFraction(final Link link, final GTUType gtuType, final DurationVector time, final double[] fraction)
124     {
125         Throw.when(time.size() != fraction.length, IllegalArgumentException.class,
126                 "Time vector and fraction array require equal length.");
127         Throw.when(!this.node.getLinks().contains(link), IllegalArgumentException.class, "Link %s is not connected to node %s.",
128                 link, this.node);
129         for (double f : fraction)
130         {
131             Throw.when(f < 0.0, IllegalArgumentException.class, "Fraction should be larger than 0.0.");
132         }
133         if (this.fractions.containsKey(gtuType))
134         {
135             this.fractions.put(gtuType, new LinkedHashMap<>());
136         }
137         this.fractions.get(gtuType).put(link, new TreeMap<>());
138         for (int i = 0; i <= time.size(); i++)
139         {
140             try
141             {
142                 this.fractions.get(gtuType).get(link).put(time.get(i), fraction[i]);
143             }
144             catch (ValueRuntimeException exception)
145             {
146                 
147                 throw new RuntimeException("Index out of range.", exception);
148             }
149         }
150     }
151 
152     
153 
154 
155 
156 
157 
158     public Link draw(final GTUType gtuType)
159     {
160         for (GTUType gtu : this.fractions.keySet())
161         {
162             if (gtuType.isOfType(gtu))
163             {
164                 Map<Link, Double> currentFractions = new LinkedHashMap<>();
165                 double t = this.simulator.getSimulatorTime().si;
166                 for (Link link : this.fractions.get(gtu).keySet())
167                 {
168                     Iterator<Duration> iterator = this.fractions.get(gtu).get(link).keySet().iterator();
169                     Duration prev = iterator.next();
170                     while (iterator.hasNext())
171                     {
172                         Duration next = iterator.next();
173                         if (prev.si <= t && t < next.si)
174                         {
175                             
176                             double f;
177                             if (this.interpolation.equals(Interpolation.STEPWISE))
178                             {
179                                 f = this.fractions.get(gtuType).get(link).get(prev);
180                             }
181                             else
182                             {
183                                 double r = (t - prev.si) / (next.si - prev.si);
184                                 f = (1 - r) * this.fractions.get(gtuType).get(link).get(prev)
185                                         + r * this.fractions.get(gtuType).get(link).get(next);
186                             }
187                             currentFractions.put(link, f);
188                             break;
189                         }
190                     }
191                 }
192                 return Draw.drawWeighted(currentFractions, this.random);
193             }
194         }
195         
196         boolean fractionAdded = false;
197         for (Link link : this.node.getLinks())
198         {
199             if ((link.getStartNode().equals(this.node) && link.getDirectionality(gtuType).isForwardOrBoth())
200                     || (link.getEndNode().equals(this.node) && link.getDirectionality(gtuType).isBackwardOrBoth()))
201             {
202                 if (link instanceof CrossSectionLink)
203                 {
204                     int n = ((CrossSectionLink) link).getLanes().size();
205                     if (n > 0)
206                     {
207                         fractionAdded = true;
208                         addFraction(link, gtuType, n);
209                     }
210                 }
211                 else
212                 {
213                     fractionAdded = true;
214                     addFraction(link, gtuType, 1.0);
215                 }
216             }
217         }
218         Throw.when(!fractionAdded, UnsupportedOperationException.class,
219                 "Split fraction on node %s cannot be derived for gtuType %s as there are no outgoing links.", this.node,
220                 gtuType);
221         
222         return draw(gtuType);
223     }
224 
225     
226     @Override
227     public int hashCode()
228     {
229         final int prime = 31;
230         int result = 1;
231         result = prime * result + ((this.fractions == null) ? 0 : this.fractions.hashCode());
232         result = prime * result + ((this.node == null) ? 0 : this.node.hashCode());
233         return result;
234     }
235 
236     
237     @Override
238     public boolean equals(final Object obj)
239     {
240         if (this == obj)
241         {
242             return true;
243         }
244         if (obj == null)
245         {
246             return false;
247         }
248         if (getClass() != obj.getClass())
249         {
250             return false;
251         }
252         SplitFraction../../../../org/opentrafficsim/road/gtu/strategical/od/SplitFraction.html#SplitFraction">SplitFraction other = (SplitFraction) obj;
253         if (this.fractions == null)
254         {
255             if (other.fractions != null)
256             {
257                 return false;
258             }
259         }
260         else if (!this.fractions.equals(other.fractions))
261         {
262             return false;
263         }
264         if (this.node == null)
265         {
266             if (other.node != null)
267             {
268                 return false;
269             }
270         }
271         else if (!this.node.equals(other.node))
272         {
273             return false;
274         }
275         return true;
276     }
277 
278     
279     @Override
280     public String toString()
281     {
282         return "SplitFraction [node=" + this.node + ", fractions=" + this.fractions + "]";
283     }
284 
285 }