1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.io.Serializable;
4 import java.util.Iterator;
5 import java.util.Map;
6 import java.util.SortedSet;
7 import java.util.TreeMap;
8
9 import org.djunits.value.vdouble.scalar.Length;
10 import org.djunits.value.vdouble.scalar.Time;
11 import org.opentrafficsim.core.Throw;
12 import org.opentrafficsim.core.gtu.GTUException;
13 import org.opentrafficsim.core.network.LateralDirectionality;
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class LaneStructure implements Serializable
83 {
84
85 private static final long serialVersionUID = 20160400L;
86
87
88 private Length length;
89
90
91 private LaneStructureRecord rootLSR;
92
93
94 private TreeMap<RelativeLane, LaneStructureRecord> crossSectionRecords;
95
96
97 private Time crossSectionUpdateTime;
98
99
100
101
102 public LaneStructure(final LaneStructureRecord initialRootLSR)
103 {
104 setRootLSR(initialRootLSR);
105 }
106
107
108
109
110 public final LaneStructureRecord getRootLSR()
111 {
112 return this.rootLSR;
113 }
114
115
116
117
118 public final void setRootLSR(final LaneStructureRecord rootLSR)
119 {
120 this.rootLSR = rootLSR;
121 }
122
123
124
125
126 public final Length getLength()
127 {
128 return this.length;
129 }
130
131
132
133
134
135
136 public final SortedSet<RelativeLane> getCrossSection(final Time now)
137 {
138 updateCrossSection(now);
139 return this.crossSectionRecords.navigableKeySet();
140 }
141
142
143
144
145 private void updateCrossSection(final Time now)
146 {
147 if (this.crossSectionRecords == null || now.gt(this.crossSectionUpdateTime))
148 {
149 this.crossSectionRecords = new TreeMap<>();
150
151 this.crossSectionRecords.put(RelativeLane.CURRENT, getRootLSR());
152
153 LaneStructureRecord lane = getRootLSR();
154 int left = 1;
155 while (lane.getLeft() != null)
156 {
157 RelativeLane relLane = new RelativeLane(LateralDirectionality.LEFT, left);
158 this.crossSectionRecords.put(relLane, lane.getLeft());
159 left++;
160 lane = lane.getLeft();
161 }
162 addFirstMergeToCrossSection(lane, LateralDirectionality.LEFT, left);
163
164 lane = getRootLSR();
165 int right = 1;
166 while (lane.getRight() != null)
167 {
168 RelativeLane relLane = new RelativeLane(LateralDirectionality.RIGHT, right);
169 this.crossSectionRecords.put(relLane, lane.getRight());
170 right++;
171 lane = lane.getRight();
172 }
173 addFirstMergeToCrossSection(lane, LateralDirectionality.RIGHT, right);
174 }
175 }
176
177
178
179
180
181
182
183 private void
184 addFirstMergeToCrossSection(final LaneStructureRecord farMost, final LateralDirectionality dir, final int n)
185 {
186 Length cumulLengthDown = farMost.getLane().getLength();
187 LaneStructureRecord next = getNextOnSide(farMost, dir);
188 LaneStructureRecord mergeRecord = null;
189 while (next != null)
190 {
191 if (next.isLinkMerge())
192 {
193 mergeRecord = next;
194 next = null;
195 }
196 else
197 {
198 cumulLengthDown = cumulLengthDown.plus(next.getLane().getLength());
199 next = getNextOnSide(next, dir);
200 }
201 }
202 if (mergeRecord != null)
203 {
204 LaneStructureRecord adjacentRecord =
205 dir.equals(LateralDirectionality.LEFT) ? mergeRecord.getLeft() : mergeRecord.getRight();
206 if (adjacentRecord == null)
207 {
208
209 return;
210 }
211 adjacentRecord = getPrevOnSide(adjacentRecord, dir);
212 Length cumulLengthUp = Length.ZERO;
213 while (adjacentRecord != null)
214 {
215 cumulLengthUp = cumulLengthUp.plus(adjacentRecord.getLane().getLength());
216 if (cumulLengthUp.ge(cumulLengthDown))
217 {
218 RelativeLane relLane = new RelativeLane(dir, n);
219 this.crossSectionRecords.put(relLane, adjacentRecord);
220 return;
221 }
222 adjacentRecord = getPrevOnSide(adjacentRecord, dir);
223 }
224 }
225 }
226
227
228
229
230
231
232
233 private LaneStructureRecord getNextOnSide(final LaneStructureRecord lane, final LateralDirectionality dir)
234 {
235 if (lane.getNext().size() == 1)
236 {
237 return lane.getNext().get(0);
238 }
239 for (LaneStructureRecord next : lane.getNext())
240 {
241 if ((dir.equals(LateralDirectionality.LEFT) && next.getLeft() == null)
242 || (dir.equals(LateralDirectionality.RIGHT) && next.getRight() == null))
243 {
244 return next;
245 }
246 }
247 return null;
248 }
249
250
251
252
253
254
255
256 private LaneStructureRecord getPrevOnSide(final LaneStructureRecord lane, final LateralDirectionality dir)
257 {
258 if (lane.getPrev().size() == 1)
259 {
260 return lane.getPrev().get(0);
261 }
262 for (LaneStructureRecord prev : lane.getPrev())
263 {
264
265 if ((dir.equals(LateralDirectionality.LEFT) && prev.getRight() == null)
266 || (dir.equals(LateralDirectionality.RIGHT) && prev.getLeft() == null))
267 {
268 return prev;
269 }
270 }
271 return null;
272 }
273
274
275
276
277
278
279
280 public final LaneStructureRecord getLaneLSR(final RelativeLane lane, final Time now) throws GTUException
281 {
282 updateCrossSection(now);
283 Throw.when(!this.crossSectionRecords.containsKey(lane), GTUException.class,
284 "The requeasted lane %s is not in the most recent cross section.", lane);
285 return this.crossSectionRecords.get(lane);
286 }
287
288
289
290
291
292
293 public final void removeInvalidMappings(final Map<RelativeLane, ?> map, final Time now)
294 {
295 updateCrossSection(now);
296 Iterator<RelativeLane> iterator = map.keySet().iterator();
297 while (iterator.hasNext())
298 {
299 RelativeLane lane = iterator.next();
300 if (!this.crossSectionRecords.containsKey(lane))
301 {
302 iterator.remove();
303 }
304 }
305 }
306
307
308 @Override
309 public final String toString()
310 {
311 return "LaneStructure [length=" + this.length + ", rootLSR=" + this.rootLSR + "]";
312 }
313
314 }