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