1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import org.djutils.event.EventType;
8 import org.djutils.exceptions.Try;
9 import org.djutils.metadata.MetaData;
10 import org.djutils.metadata.ObjectDescriptor;
11 import org.opentrafficsim.core.geometry.DirectedPoint;
12 import org.opentrafficsim.core.geometry.OtsLine3d;
13 import org.opentrafficsim.core.geometry.OtsPoint3d;
14 import org.opentrafficsim.core.network.LinkType;
15 import org.opentrafficsim.core.network.NetworkException;
16 import org.opentrafficsim.core.network.Node;
17 import org.opentrafficsim.core.network.Link;
18 import org.opentrafficsim.road.network.RoadNetwork;
19 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
20
21
22
23
24
25
26
27
28
29
30
31 public class CrossSectionLink extends Link implements Serializable
32 {
33
34 private static final long serialVersionUID = 20141015L;
35
36
37 private final List<CrossSectionElement> crossSectionElementList = new ArrayList<>();
38
39
40 private final List<Lane> lanes = new ArrayList<>();
41
42
43 private final LaneKeepingPolicy laneKeepingPolicy;
44
45
46 private Priority priority = Priority.NONE;
47
48
49 private OtsLine3d startLine;
50
51
52 private OtsLine3d endLine;
53
54
55
56
57
58
59 public static final EventType LANE_ADD_EVENT = new EventType("LINK.LANE.ADD",
60 new MetaData("Lane data", "Lane data",
61 new ObjectDescriptor[] {new ObjectDescriptor("Network id", "Network id", String.class),
62 new ObjectDescriptor("Link id", "Link id", String.class),
63 new ObjectDescriptor("Lane id", "Lane id", String.class),
64 new ObjectDescriptor("Lane number", "Lane number", Integer.class)}));
65
66
67
68
69
70
71 public static final EventType LANE_REMOVE_EVENT = new EventType("LINK.LANE.REMOVE",
72 new MetaData("Lane data", "Lane data",
73 new ObjectDescriptor[] {new ObjectDescriptor("Network id", "Network id", String.class),
74 new ObjectDescriptor("Link id", "Link id", String.class),
75 new ObjectDescriptor("Lane id", "Lane id", String.class),
76 new ObjectDescriptor("Lane number", "Lane number", Integer.class)}));
77
78
79
80
81
82
83
84
85
86
87
88
89
90 @SuppressWarnings("checkstyle:parameternumber")
91 public CrossSectionLink(final RoadNetwork network, final String id, final Node startNode,
92 final Node endNode, final LinkType linkType, final OtsLine3d designLine,
93 final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
94 {
95 super(network, id, startNode, endNode, linkType, designLine);
96 this.laneKeepingPolicy = laneKeepingPolicy;
97 }
98
99
100 @Override
101 public RoadNetwork getNetwork()
102 {
103 return (RoadNetwork) super.getNetwork();
104 }
105
106
107
108
109
110
111 protected final void addCrossSectionElement(final CrossSectionElement cse)
112 {
113 this.crossSectionElementList.add(cse);
114 if (cse instanceof Lane)
115 {
116 this.lanes.add((Lane) cse);
117 fireTimedEvent(LANE_ADD_EVENT, new Object[] {getNetwork().getId(), getId(), cse.getId(), this.lanes.indexOf(cse)},
118 getSimulator().getSimulatorTime());
119 }
120 }
121
122
123
124
125
126 public final List<CrossSectionElement> getCrossSectionElementList()
127 {
128 return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
129 }
130
131
132
133
134
135 public final LaneKeepingPolicy getLaneKeepingPolicy()
136 {
137 return this.laneKeepingPolicy;
138 }
139
140
141
142
143
144
145 public final CrossSectionElement getCrossSectionElement(final String id)
146 {
147 for (CrossSectionElement cse : this.crossSectionElementList)
148 {
149 if (cse.getId().equals(id))
150 {
151 return cse;
152 }
153 }
154 return null;
155 }
156
157
158
159
160
161 public final List<Lane> getLanes()
162 {
163 return this.lanes == null ? new ArrayList<>() : new ArrayList<>(this.lanes);
164 }
165
166
167
168
169 public final Priority getPriority()
170 {
171 return this.priority;
172 }
173
174
175
176
177 public final void setPriority(final Priority priority)
178 {
179 this.priority = priority;
180 }
181
182
183
184
185
186 public OtsLine3d getStartLine()
187 {
188 if (this.startLine == null)
189 {
190 double left = Double.NaN;
191 double right = Double.NaN;
192 for (Lane lane : this.lanes)
193 {
194 double half = lane.getBeginWidth().si * .5;
195 if (!Double.isNaN(left))
196 {
197 left = Math.max(left, lane.getDesignLineOffsetAtBegin().si + half);
198 right = Math.min(right, lane.getDesignLineOffsetAtBegin().si - half);
199 }
200 else
201 {
202 left = lane.getDesignLineOffsetAtBegin().si + half;
203 right = lane.getDesignLineOffsetAtBegin().si - half;
204 }
205 }
206 OtsPoint3d start = getStartNode().getPoint();
207 double heading = getStartNode().getHeading().si + .5 * Math.PI;
208 double cosHeading = Math.cos(heading);
209 double sinHeading = Math.sin(heading);
210 OtsPoint3d leftPoint = new OtsPoint3d(start.x + cosHeading * left, start.y + sinHeading * left);
211 OtsPoint3d rightPoint = new OtsPoint3d(start.x - cosHeading * right, start.y - sinHeading * right);
212 this.startLine = Try.assign(() -> new OtsLine3d(leftPoint, rightPoint), "Invalid startline on CrossSectionLink.");
213 }
214 return this.startLine;
215 }
216
217
218
219
220
221 public OtsLine3d getEndLine()
222 {
223 if (this.endLine == null)
224 {
225 double left = Double.NaN;
226 double right = Double.NaN;
227 for (Lane lane : this.lanes)
228 {
229 double half = lane.getEndWidth().si * .5;
230 if (!Double.isNaN(left))
231 {
232 left = Math.max(left, lane.getDesignLineOffsetAtEnd().si + half);
233 right = Math.min(right, lane.getDesignLineOffsetAtEnd().si - half);
234 }
235 else
236 {
237 left = lane.getDesignLineOffsetAtEnd().si + half;
238 right = lane.getDesignLineOffsetAtEnd().si - half;
239 }
240 }
241 OtsPoint3d start = getEndNode().getPoint();
242 DirectedPoint p = Try.assign(() -> getEndNode().getLocation(), "Unexpected remote exception.");
243 double heading = p.getRotZ() + .5 * Math.PI;
244 double cosHeading = Math.cos(heading);
245 double sinHeading = Math.sin(heading);
246 OtsPoint3d leftPoint = new OtsPoint3d(start.x + cosHeading * left, start.y + sinHeading * left);
247 OtsPoint3d rightPoint = new OtsPoint3d(start.x + cosHeading * right, start.y + sinHeading * right);
248 this.endLine = Try.assign(() -> new OtsLine3d(leftPoint, rightPoint), "Invalid endline on CrossSectionLink.");
249 }
250 return this.endLine;
251 }
252
253
254 @Override
255 public final String toString()
256 {
257 return "CrossSectionLink [name=" + this.getId() + ", nodes=" + getStartNode().getId() + "-" + getEndNode().getId()
258 + ", crossSectionElementList=" + this.crossSectionElementList + ", lanes=" + this.lanes + ", laneKeepingPolicy="
259 + this.laneKeepingPolicy + "]";
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273 public enum Priority
274 {
275
276 PRIORITY,
277
278
279 NONE,
280
281
282 TURN_ON_RED,
283
284
285 YIELD,
286
287
288 STOP,
289
290
291 ALL_STOP,
292
293
294 BUS_STOP;
295
296
297
298
299
300 public boolean isPriority()
301 {
302 return this.equals(PRIORITY);
303 }
304
305
306
307
308
309 public boolean isNone()
310 {
311 return this.equals(NONE);
312 }
313
314
315
316
317
318 public boolean isTurnOnRed()
319 {
320 return this.equals(TURN_ON_RED);
321 }
322
323
324
325
326
327 public boolean isYield()
328 {
329 return this.equals(YIELD);
330 }
331
332
333
334
335
336 public boolean isStop()
337 {
338 return this.equals(STOP);
339 }
340
341
342
343
344
345 public boolean isAllStop()
346 {
347 return this.equals(ALL_STOP);
348 }
349
350
351
352
353
354 public boolean isBusStop()
355 {
356 return this.equals(BUS_STOP);
357 }
358
359 }
360
361 }