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