1 package org.opentrafficsim.road.network.factory;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Map;
8
9 import javax.naming.NamingException;
10
11 import org.djunits.unit.LengthUnit;
12 import org.djunits.value.vdouble.scalar.Length;
13 import org.djunits.value.vdouble.scalar.Speed;
14 import org.djutils.exceptions.Throw;
15 import org.djutils.exceptions.Try;
16 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
17 import org.opentrafficsim.core.geometry.Bezier;
18 import org.opentrafficsim.core.geometry.OTSGeometryException;
19 import org.opentrafficsim.core.geometry.OTSLine3D;
20 import org.opentrafficsim.core.geometry.OTSPoint3D;
21 import org.opentrafficsim.core.gtu.GTUType;
22 import org.opentrafficsim.core.network.LateralDirectionality;
23 import org.opentrafficsim.core.network.LinkType;
24 import org.opentrafficsim.core.network.Network;
25 import org.opentrafficsim.core.network.NetworkException;
26 import org.opentrafficsim.core.network.Node;
27 import org.opentrafficsim.core.network.OTSNetwork;
28 import org.opentrafficsim.core.network.OTSNode;
29 import org.opentrafficsim.road.network.lane.CrossSectionLink;
30 import org.opentrafficsim.road.network.lane.Lane;
31 import org.opentrafficsim.road.network.lane.LaneType;
32 import org.opentrafficsim.road.network.lane.Shoulder;
33 import org.opentrafficsim.road.network.lane.Stripe;
34 import org.opentrafficsim.road.network.lane.Stripe.Permeable;
35 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
36 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
37
38 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
39 import nl.tudelft.simulation.language.d3.DirectedPoint;
40
41
42
43
44
45
46
47
48
49
50 public final class LaneFactory
51 {
52
53
54 private static final Length STRIPE_WIDTH = Length.createSI(0.2);
55
56
57 private static final double BEZIER_MARGIN = Math.toRadians(0.5);
58
59
60 private final CrossSectionLink link;
61
62
63 private Length offset;
64
65
66 private Length laneWidth0;
67
68
69 private LaneType laneType0;
70
71
72 private Speed speedLimit0;
73
74
75 private final List<Lane> lanes = new ArrayList<>();
76
77
78
79
80
81
82
83
84
85
86
87 public LaneFactory(final OTSNetwork network, final OTSNode from, final OTSNode to, final LinkType type,
88 final OTSSimulatorInterface simulator, final LaneKeepingPolicy policy) throws OTSGeometryException, NetworkException
89 {
90 this(network, from, to, type, simulator, policy, makeLine(from, to));
91 }
92
93
94
95
96
97
98
99
100
101
102
103 public LaneFactory(final OTSNetwork network, final OTSNode from, final OTSNode to, final LinkType type,
104 final OTSSimulatorInterface simulator, final LaneKeepingPolicy policy, final OTSLine3D line) throws NetworkException
105 {
106 this.link = new CrossSectionLink(network, from.getId() + to.getId(), from, to, type, line, simulator, policy);
107 }
108
109
110
111
112
113
114
115
116
117 private static OTSLine3D makeLine(final OTSNode from, final OTSNode to) throws OTSGeometryException
118 {
119
120 double rotCrow = Math.atan2(to.getLocation().y - from.getLocation().y, to.getLocation().x - from.getLocation().x);
121 double dRot = from.getLocation().getRotZ() - rotCrow;
122 while (dRot < -Math.PI)
123 {
124 dRot += 2.0 * Math.PI;
125 }
126 while (dRot > Math.PI)
127 {
128 dRot -= 2.0 * Math.PI;
129 }
130 OTSLine3D line;
131 if (from.getLocation().getRotZ() != to.getLocation().getRotZ() || Math.abs(dRot) > BEZIER_MARGIN)
132 {
133 line = Bezier.cubic(from.getLocation(), to.getLocation());
134 }
135 else
136 {
137 line = new OTSLine3D(from.getPoint(), to.getPoint());
138 }
139 return line;
140 }
141
142
143
144
145
146
147
148
149
150 public LaneFactory leftToRight(final double leftLanes, final Length laneWidth, final LaneType laneType,
151 final Speed speedLimit)
152 {
153 this.offset = laneWidth.multiplyBy(leftLanes);
154 this.laneWidth0 = laneWidth.neg();
155 this.laneType0 = laneType;
156 this.speedLimit0 = speedLimit;
157 Try.execute(() -> new Stripe(this.link, this.offset, this.offset, STRIPE_WIDTH),
158 "Unexpected exception while building link.");
159 return this;
160 }
161
162
163
164
165
166
167
168
169
170 public LaneFactory rightToLeft(final double rightLanes, final Length laneWidth, final LaneType laneType,
171 final Speed speedLimit)
172 {
173 this.offset = laneWidth.multiplyBy(-rightLanes);
174 this.laneWidth0 = laneWidth;
175 this.laneType0 = laneType;
176 this.speedLimit0 = speedLimit;
177 Try.execute(() -> new Stripe(this.link, this.offset, this.offset, STRIPE_WIDTH),
178 "Unexpected exception while building link.");
179 return this;
180 }
181
182
183
184
185
186
187
188
189
190 public LaneFactory addLanes(final Permeable... permeable)
191 {
192 List<Permeable> list = new ArrayList<>(Arrays.asList(permeable));
193 list.add(null);
194 for (Permeable perm : list)
195 {
196 this.lanes.add(Try.assign(() -> new Lane(this.link, "Lane " + (this.lanes.size() + 1),
197 this.offset.plus(this.laneWidth0.multiplyBy(0.5)), this.laneWidth0.abs(), this.laneType0, this.speedLimit0,
198 null), "Unexpected exception while building link."));
199 this.offset = this.offset.plus(this.laneWidth0);
200 Stripe stripe = Try.assign(() -> new Stripe(this.link, this.offset, this.offset, STRIPE_WIDTH),
201 "Unexpected exception while building link.");
202 if (perm != null)
203 {
204 stripe.addPermeability(GTUType.VEHICLE, perm);
205 }
206 }
207 return this;
208 }
209
210
211
212
213
214
215
216
217 public LaneFactory addShoulder(final Length width, final LateralDirectionality lat)
218 {
219 Throw.when(this.lanes.isEmpty(), IllegalStateException.class, "Lanes should be defined before adding shoulder(s).");
220 if (lat == null || lat.isNone() || lat.isLeft())
221 {
222 Length startOffset = null;
223 Length endOffset = null;
224 for (Lane lane : this.lanes)
225 {
226 if (startOffset == null
227 || lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().multiplyBy(0.5)).gt(startOffset))
228 {
229 startOffset = lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().multiplyBy(0.5));
230 }
231 if (endOffset == null || lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().multiplyBy(0.5)).gt(endOffset))
232 {
233 endOffset = lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().multiplyBy(0.5));
234 }
235 }
236 Length start = startOffset.plus(width.multiplyBy(0.5));
237 Length end = endOffset.plus(width.multiplyBy(0.5));
238 Try.assign(() -> new Shoulder(this.link, "Left shoulder", start, end, width, width),
239 "Unexpected exception while building link.");
240 }
241 if (lat == null || lat.isNone() || lat.isRight())
242 {
243 Length startOffset = null;
244 Length endOffset = null;
245 for (Lane lane : this.lanes)
246 {
247 if (startOffset == null
248 || lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().multiplyBy(0.5)).lt(startOffset))
249 {
250 startOffset = lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().multiplyBy(0.5));
251 }
252 if (endOffset == null
253 || lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().multiplyBy(0.5)).lt(endOffset))
254 {
255 endOffset = lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().multiplyBy(0.5));
256 }
257 }
258 Length start = startOffset.minus(width.multiplyBy(0.5));
259 Length end = endOffset.minus(width.multiplyBy(0.5));
260 Try.assign(() -> new Shoulder(this.link, "Right shoulder", start, end, width, width),
261 "Unexpected exception while building link.");
262 }
263 return this;
264 }
265
266
267
268
269
270 public List<Lane> getLanes()
271 {
272 return this.lanes;
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 public static CrossSectionLink makeLink(final Network network, final String name, final Node from, final Node to,
290 final OTSPoint3D[] intermediatePoints, final OTSSimulatorInterface simulator)
291 throws OTSGeometryException, NetworkException
292 {
293 List<OTSPoint3D> pointList =
294 intermediatePoints == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(intermediatePoints));
295 if (pointList.size() == 0 || !from.getPoint().equals(pointList.get(0)))
296 {
297 pointList.add(0, from.getPoint());
298 }
299 if (pointList.size() == 0 || !to.getPoint().equals(pointList.get(pointList.size() - 1)))
300 {
301 pointList.add(to.getPoint());
302 }
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 OTSLine3D designLine = new OTSLine3D(pointList);
333 CrossSectionLink link = new CrossSectionLink(network, name, from, to, LinkType.ROAD, designLine, simulator,
334 LaneKeepingPolicy.KEEP_RIGHT);
335 return link;
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354 @SuppressWarnings("checkstyle:parameternumber")
355 private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType,
356 final Length latPosAtStart, final Length latPosAtEnd, final Length width, final Speed speedLimit,
357 final DEVSSimulatorInterface.TimeDoubleUnit simulator) throws NetworkException, OTSGeometryException
358 {
359 Map<GTUType, Speed> speedMap = new LinkedHashMap<>();
360 speedMap.put(GTUType.VEHICLE, speedLimit);
361 Lane result = new Lane(link, id, latPosAtStart, latPosAtEnd, width, width, laneType, speedMap,
362 new OvertakingConditions.LeftAndRight());
363 return result;
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381 public static Lane makeLane(final Network network, final String name, final OTSNode from, final OTSNode to,
382 final OTSPoint3D[] intermediatePoints, final LaneType laneType, final Speed speedLimit,
383 final OTSSimulatorInterface simulator) throws NetworkException, OTSGeometryException
384 {
385 Length width = new Length(4.0, LengthUnit.METER);
386 final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
387 Length latPos = new Length(0.0, LengthUnit.METER);
388 return makeLane(link, "lane", laneType, latPos, latPos, width, speedLimit, simulator);
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411 @SuppressWarnings("checkstyle:parameternumber")
412 public static Lane[] makeMultiLane(final Network network, final String name, final OTSNode from, final OTSNode to,
413 final OTSPoint3D[] intermediatePoints, final int laneCount, final int laneOffsetAtStart, final int laneOffsetAtEnd,
414 final LaneType laneType, final Speed speedLimit, final OTSSimulatorInterface simulator)
415 throws NetworkException, OTSGeometryException
416 {
417 final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
418 Lane[] result = new Lane[laneCount];
419 Length width = new Length(4.0, LengthUnit.METER);
420 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
421 {
422
423 Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
424 Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
425 result[laneIndex] =
426 makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit, simulator);
427 }
428 return result;
429 }
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 @SuppressWarnings("checkstyle:parameternumber")
451 public static Lane[] makeMultiLane(final Network network, final String name, final OTSNode from, final OTSNode to,
452 final OTSPoint3D[] intermediatePoints, final int laneCount, final LaneType laneType, final Speed speedLimit,
453 final OTSSimulatorInterface simulator) throws NamingException, NetworkException, OTSGeometryException
454 {
455 return makeMultiLane(network, name, from, to, intermediatePoints, laneCount, 0, 0, laneType, speedLimit, simulator);
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479 @SuppressWarnings("checkstyle:parameternumber")
480 public static Lane[] makeMultiLaneBezier(final Network network, final String name, final OTSNode n1, final OTSNode n2,
481 final OTSNode n3, final OTSNode n4, final int laneCount, final int laneOffsetAtStart, final int laneOffsetAtEnd,
482 final LaneType laneType, final Speed speedLimit, final OTSSimulatorInterface simulator)
483 throws NamingException, NetworkException, OTSGeometryException
484 {
485 OTSLine3D bezier = makeBezier(n1, n2, n3, n4);
486 final CrossSectionLink link = makeLink(network, name, n2, n3, bezier.getPoints(), simulator);
487 Lane[] result = new Lane[laneCount];
488 Length width = new Length(4.0, LengthUnit.METER);
489 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
490 {
491
492 Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
493 Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
494 result[laneIndex] =
495 makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit, simulator);
496 }
497 return result;
498 }
499
500
501
502
503
504
505
506
507
508 public static OTSLine3D makeBezier(final OTSNode n1, final OTSNode n2, final OTSNode n3, final OTSNode n4)
509 throws OTSGeometryException
510 {
511 OTSPoint3D p1 = n1.getPoint();
512 OTSPoint3D p2 = n2.getPoint();
513 OTSPoint3D p3 = n3.getPoint();
514 OTSPoint3D p4 = n4.getPoint();
515 DirectedPoint dp1 = new DirectedPoint(p2.x, p2.y, p2.z, 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
516 DirectedPoint dp2 = new DirectedPoint(p3.x, p3.y, p3.z, 0.0, 0.0, Math.atan2(p4.y - p3.y, p4.x - p3.x));
517 return Bezier.cubic(dp1, dp2);
518 }
519 }