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.draw.line.PolyLine2d;
8 import org.djutils.draw.point.Point2d;
9 import org.djutils.event.EventType;
10 import org.djutils.metadata.MetaData;
11 import org.djutils.metadata.ObjectDescriptor;
12 import org.opentrafficsim.core.geometry.FractionalLengthData;
13 import org.opentrafficsim.core.geometry.OtsLine2d;
14 import org.opentrafficsim.core.network.Link;
15 import org.opentrafficsim.core.network.LinkType;
16 import org.opentrafficsim.core.network.NetworkException;
17 import org.opentrafficsim.core.network.Node;
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 PolyLine2d startLine;
50
51
52 private PolyLine2d 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
91 @SuppressWarnings("checkstyle:parameternumber")
92 public CrossSectionLink(final RoadNetwork network, final String id, final Node startNode, final Node endNode,
93 final LinkType linkType, final OtsLine2d designLine, final FractionalLengthData elevation,
94 final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
95 {
96 super(network, id, startNode, endNode, linkType, designLine, elevation);
97 this.laneKeepingPolicy = laneKeepingPolicy;
98 }
99
100
101 @Override
102 public RoadNetwork getNetwork()
103 {
104 return (RoadNetwork) super.getNetwork();
105 }
106
107
108
109
110
111
112 protected final void addCrossSectionElement(final CrossSectionElement cse)
113 {
114 this.crossSectionElementList.add(cse);
115 if (cse instanceof Lane)
116 {
117 this.lanes.add((Lane) cse);
118 fireTimedEvent(LANE_ADD_EVENT, new Object[] {getNetwork().getId(), getId(), cse.getId(), this.lanes.indexOf(cse)},
119 getSimulator().getSimulatorTime());
120 }
121 }
122
123
124
125
126
127 public final List<CrossSectionElement> getCrossSectionElementList()
128 {
129 return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
130 }
131
132
133
134
135
136 public final LaneKeepingPolicy getLaneKeepingPolicy()
137 {
138 return this.laneKeepingPolicy;
139 }
140
141
142
143
144
145
146 public final CrossSectionElement getCrossSectionElement(final String id)
147 {
148 for (CrossSectionElement cse : this.crossSectionElementList)
149 {
150 if (cse.getId().equals(id))
151 {
152 return cse;
153 }
154 }
155 return null;
156 }
157
158
159
160
161
162 public final List<Lane> getLanes()
163 {
164 return this.lanes == null ? new ArrayList<>() : new ArrayList<>(this.lanes);
165 }
166
167
168
169
170 public final Priority getPriority()
171 {
172 return this.priority;
173 }
174
175
176
177
178 public final void setPriority(final Priority priority)
179 {
180 this.priority = priority;
181 }
182
183
184
185
186
187 public PolyLine2d getStartLine()
188 {
189 if (this.startLine == null)
190 {
191 double left = Double.NaN;
192 double right = Double.NaN;
193 for (Lane lane : this.lanes)
194 {
195 double half = lane.getBeginWidth().si * .5;
196 if (!Double.isNaN(left))
197 {
198 left = Math.max(left, lane.getOffsetAtBegin().si + half);
199 right = Math.min(right, lane.getOffsetAtBegin().si - half);
200 }
201 else
202 {
203 left = lane.getOffsetAtBegin().si + half;
204 right = lane.getOffsetAtBegin().si - half;
205 }
206 }
207 Point2d start = getDesignLine().getFirst();
208 double heading = getStartNode().getHeading().si + .5 * Math.PI;
209 double cosHeading = Math.cos(heading);
210 double sinHeading = Math.sin(heading);
211 Point2d leftPoint = new Point2d(start.x + cosHeading * left, start.y + sinHeading * left);
212 Point2d rightPoint = new Point2d(start.x - cosHeading * right, start.y - sinHeading * right);
213 this.startLine = new PolyLine2d(leftPoint, rightPoint);
214 }
215 return this.startLine;
216 }
217
218
219
220
221
222 public PolyLine2d getEndLine()
223 {
224 if (this.endLine == null)
225 {
226 double left = Double.NaN;
227 double right = Double.NaN;
228 for (Lane lane : this.lanes)
229 {
230 double half = lane.getEndWidth().si * .5;
231 if (!Double.isNaN(left))
232 {
233 left = Math.max(left, lane.getOffsetAtEnd().si + half);
234 right = Math.min(right, lane.getOffsetAtEnd().si - half);
235 }
236 else
237 {
238 left = lane.getOffsetAtEnd().si + half;
239 right = lane.getOffsetAtEnd().si - half;
240 }
241 }
242 Point2d start = getDesignLine().getLast();
243 double heading = getEndNode().getHeading().si + .5 * Math.PI;
244 double cosHeading = Math.cos(heading);
245 double sinHeading = Math.sin(heading);
246 Point2d leftPoint = new Point2d(start.x + cosHeading * left, start.y + sinHeading * left);
247 Point2d rightPoint = new Point2d(start.x + cosHeading * right, start.y + sinHeading * right);
248 this.endLine = new PolyLine2d(leftPoint, rightPoint);
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 YIELD,
283
284
285 STOP,
286
287
288 ALL_STOP,
289
290
291 BUS_STOP;
292
293
294
295
296
297 public boolean isPriority()
298 {
299 return this.equals(PRIORITY);
300 }
301
302
303
304
305
306 public boolean isNone()
307 {
308 return this.equals(NONE);
309 }
310
311
312
313
314
315 public boolean isYield()
316 {
317 return this.equals(YIELD);
318 }
319
320
321
322
323
324 public boolean isStop()
325 {
326 return this.equals(STOP);
327 }
328
329
330
331
332
333 public boolean isAllStop()
334 {
335 return this.equals(ALL_STOP);
336 }
337
338
339
340
341
342 public boolean isBusStop()
343 {
344 return this.equals(BUS_STOP);
345 }
346
347 }
348
349 }