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