1 package org.opentrafficsim.road.gtu.strategical.od;
2
3 import java.util.HashMap;
4 import java.util.Iterator;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7 import java.util.TreeMap;
8
9 import org.djunits.unit.DurationUnit;
10 import org.djunits.value.StorageType;
11 import org.djunits.value.ValueException;
12 import org.djunits.value.vdouble.scalar.Duration;
13 import org.djunits.value.vdouble.vector.DurationVector;
14 import org.opentrafficsim.core.gtu.GTUType;
15 import org.opentrafficsim.core.math.Draw;
16 import org.opentrafficsim.core.network.Link;
17 import org.opentrafficsim.core.network.Node;
18 import org.opentrafficsim.road.network.lane.CrossSectionLink;
19
20 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
21 import nl.tudelft.simulation.jstats.streams.StreamInterface;
22 import nl.tudelft.simulation.language.Throw;
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 HashMap<>();
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, GTUType.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 = new DurationVector(t, DurationUnit.SI, StorageType.DENSE);
96 }
97 catch (ValueException 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, GTUType.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 HashMap<>());
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 (ValueException 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.",
220 this.node, 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 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 }