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