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 * <p>
38 * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
39 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
40 * </p>
41 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
42 */
43 public final class LaneFactory
44 {
45
46 /** Angle above which a Bezier curve is used over a straight line. */
47 private static final double BEZIER_MARGIN = Math.toRadians(0.5);
48
49 /** Link. */
50 private final CrossSectionLink link;
51
52 /** Offset for next cross section elements. Left side of lane when building left to right, and vice versa. */
53 private Length offset;
54
55 /** Lane width to use (negative when building left to right). */
56 private Length laneWidth0;
57
58 /** Start offset. */
59 private Length offsetStart = Length.ZERO;
60
61 /** End offset. */
62 private Length offsetEnd = Length.ZERO;
63
64 /** Lane type to use. */
65 private LaneType laneType0;
66
67 /** Speed limit to use. */
68 private Speed speedLimit0;
69
70 /** Parent GTU type of relevant GTUs. */
71 private GtuType gtuType;
72
73 /** Created lanes. */
74 private final List<Lane> lanes = new ArrayList<>();
75
76 /** Stored stripes, so we can return it to the user on the addLanes() call. */
77 private Stripe firstStripe;
78
79 /**
80 * @param network RoadNetwork; network
81 * @param from Node; from node
82 * @param to Node; to node
83 * @param type LinkType; link type
84 * @param simulator OtsSimulatorInterface; simulator
85 * @param policy LaneKeepingPolicy; lane keeping policy
86 * @param gtuType GtuType; parent GTU type of relevant GTUs.
87 * @throws OtsGeometryException if no valid line can be created
88 * @throws NetworkException if the link exists, or a node does not exist, in the network
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 * @param network RoadNetwork; network
99 * @param from Node; from node
100 * @param to Node; to node
101 * @param type LinkType; link type
102 * @param simulator OtsSimulatorInterface; simulator
103 * @param policy LaneKeepingPolicy; lane keeping policy
104 * @param gtuType GtuType; parent GTU type of relevant GTUs.
105 * @param line OtsLine3d; line
106 * @throws NetworkException if the link exists, or a node does not exist, in the network
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 * Creates a line between two nodes. If the nodes and their directions are on a straight line, a straight line is created.
118 * Otherwise a default Bezier curve is created.
119 * @param from Node; from node
120 * @param to Node; to node
121 * @return OtsLine3d; line
122 * @throws OtsGeometryException if no valid line can be created
123 */
124 private static OtsLine3d makeLine(final Node from, final Node to) throws OtsGeometryException
125 {
126 // Straight or bezier?
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 * Prepare the factory to add lanes from left to right.
151 * @param leftLanes double; number of lanes left from the link design line
152 * @param laneWidth Length; lane width
153 * @param laneType LaneType; lane type
154 * @param speedLimit Speed; speed limit
155 * @return LaneFactory this lane factory for method chaining
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 * Prepare the factory to add lanes from right to left.
175 * @param rightLanes double; number of lanes right from the link design line
176 * @param laneWidth Length; lane width
177 * @param laneType LaneType; lane type
178 * @param speedLimit Speed; speed limit
179 * @return LaneFactory this lane factory for method chaining
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 * Set start offset.
195 * @param startOffset Length; offset
196 * @return LaneFactory this lane factory for method chaining
197 */
198 public LaneFactory setOffsetStart(final Length startOffset)
199 {
200 this.offsetStart = startOffset;
201 return this;
202 }
203
204 /**
205 * Set end offset.
206 * @param endOffset Length; offset
207 * @return LaneFactory this lane factory for method chaining
208 */
209 public LaneFactory setOffsetEnd(final Length endOffset)
210 {
211 this.offsetEnd = endOffset;
212 return this;
213 }
214
215 /**
216 * Adds a lane pair for each stripe type, where the type determines the right-hand side stripe when building from left to
217 * right and vice versa. The left-most stripe is created in {@code leftToRight()}, meaning that each type describes
218 * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). This method
219 * internally adds {@code SOLID} to create the final continuous stripe.
220 * @param types Type...; type per lane pair, for N lanes N-1 should be provided
221 * @return this LaneFactory this lane factory for method chaining
222 */
223 public LaneFactory addLanes(final Type... types)
224 {
225 return addLanes(new ArrayList<>(), types);
226 }
227
228 /**
229 * Adds a lane pair for each stripe type, where the type determines the right-hand side stripe when building from left to
230 * right and vice versa. The left-most stripe is created in {@code leftToRight()}, meaning that each type describes
231 * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). This method
232 * internally adds {@code SOLID} to create the final continuous stripe. All generated stripes, including the one generated
233 * in leftToRight() or rightToLeft(), is returned in the provided list for custom permeability.
234 * @param stripeList List<? super Stripe>; list in to which the generated stripes are placed.
235 * @param types Type...; type per lane pair, for N lanes N-1 should be provided
236 * @return this LaneFactory this lane factory for method chaining
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 * Return width to use for different stripe types.
265 * @param type Type; stripe type.
266 * @return Length; width.
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 * Adds 1 or 2 shoulders to the current set of lanes.
288 * @param width Length; width of the shoulder
289 * @param lat LateralDirectionality; side of shoulder, use {@code null} or {@code NONE} for both
290 * @return LaneFactory this lane factory for method chaining
291 * @throws IllegalStateException if no lanes are defined
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 * Returns the created lanes in build order.
343 * @return List<Lane> created lanes in build order
344 */
345 public List<Lane> getLanes()
346 {
347 return this.lanes;
348 }
349
350 /**
351 * Create a Link along intermediate coordinates from one Node to another.
352 * @param network RoadNetwork; the network
353 * @param name String; name of the new Link
354 * @param from Node; start Node of the new Link
355 * @param to Node; end Node of the new Link
356 * @param intermediatePoints OtsPoint3d[]; array of intermediate coordinates (may be null); the intermediate points may
357 * contain the coordinates of the from node and to node
358 * @param simulator OtsSimulatorInterface; the simulator for this network
359 * @return Link; the newly constructed Link
360 * @throws OtsGeometryException when the design line is degenerate (only one point or duplicate point)
361 * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
362 * or the end node of the link are not registered in the network.
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 // see if an intermediate point needs to be created to the start of the link in the right direction
381 OtsPoint3d s1 = pointList.get(0);
382 OtsPoint3d s2 = pointList.get(1);
383 double dy = s2.y - s1.y;
384 double dx = s2.x - s1.x;
385 double a = from.getLocation().getRotZ();
386 if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
387 {
388 double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
389 OtsPoint3d extra = new OtsPoint3d(s1.x + r * Math.cos(a), s1.y + r * Math.sin(a), s1.z);
390 pointList.add(1, extra);
391 }
392
393 // see if an intermediate point needs to be created to the end of the link in the right direction
394 s1 = pointList.get(pointList.size() - 2);
395 s2 = pointList.get(pointList.size() - 1);
396 dy = s2.y - s1.y;
397 dx = s2.x - s1.x;
398 a = to.getLocation().getRotZ() - Math.PI;
399 if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
400 {
401 double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
402 OtsPoint3d extra = new OtsPoint3d(s2.x + r * Math.cos(a), s2.y + r * Math.sin(a), s2.z);
403 pointList.add(pointList.size() - 2, extra);
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 * Create one Lane.
415 * @param link CrossSectionLink; the link that owns the new Lane
416 * @param id String; the id of this lane, should be unique within the link
417 * @param laneType LaneType; the type of the new Lane
418 * @param latPosAtStart Length; the lateral position of the new Lane with respect to the design line of the link at the
419 * start of the link
420 * @param latPosAtEnd Length; the lateral position of the new Lane with respect to the design line of the link at the end of
421 * the link
422 * @param width Length; the width of the new Lane
423 * @param speedLimit Speed; the speed limit on the new Lane
424 * @param simulator OtsSimulatorInterface; the simulator
425 * @param gtuType GtuType; parent GTU type of relevant GTUs
426 * @return Lane
427 * @throws NetworkException on network inconsistency
428 * @throws OtsGeometryException when creation of center line or contour fails
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 * Create a simple Lane.
442 * @param network RoadNetwork; the network
443 * @param name String; name of the Lane (and also of the Link that owns it)
444 * @param from Node; starting node of the new Lane
445 * @param to Node; ending node of the new Lane
446 * @param intermediatePoints OtsPoint3d[]; intermediate coordinates or null to create a straight road; the intermediate
447 * points may contain the coordinates of the from node and to node
448 * @param laneType LaneType; type of the new Lane
449 * @param speedLimit Speed; the speed limit on the new Lane
450 * @param simulator OtsSimulatorInterface; the simulator
451 * @param gtuType GtuType; parent GTU type of relevant GTUs
452 * @return Lane; the new Lane
453 * @throws NetworkException on network inconsistency
454 * @throws OtsGeometryException when creation of center line or contour fails
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 * Create a simple road with the specified number of Lanes.<br>
469 * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
470 * method of the Lane.
471 * @param network RoadNetwork; the network
472 * @param name String; name of the Link
473 * @param from Node; starting node of the new Lane
474 * @param to Node; ending node of the new Lane
475 * @param intermediatePoints OtsPoint3d[]; intermediate coordinates or null to create a straight road; the intermediate
476 * points may contain the coordinates of the from node and to node
477 * @param laneCount int; number of lanes in the road
478 * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
479 * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
480 * @param laneType LaneType; type of the new Lanes
481 * @param speedLimit Speed; the speed limit on all lanes
482 * @param simulator OtsSimulatorInterface; the simulator
483 * @param gtuType GtuType; parent GTU type of relevant GTUs
484 * @return Lane<String, String>[]; array containing the new Lanes
485 * @throws NetworkException on topological problems
486 * @throws OtsGeometryException when creation of center line or contour fails
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 // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
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 * Create a simple road with the specified number of Lanes.<br>
510 * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
511 * method of the Lane.
512 * @param network RoadNetwork; the network
513 * @param name String; name of the Link
514 * @param from Node; starting node of the new Lane
515 * @param to Node; ending node of the new Lane
516 * @param intermediatePoints OtsPoint3d[]; intermediate coordinates or null to create a straight road; the intermediate
517 * points may contain the coordinates of the from node and to node
518 * @param laneCount int; number of lanes in the road
519 * @param laneType LaneType; type of the new Lanes
520 * @param speedLimit Speed; Speed the speed limit (applies to all generated lanes)
521 * @param simulator OtsSimulatorInterface; the simulator
522 * @param gtuType GtuType; parent GTU type of relevant GTUs
523 * @return Lane<String, String>[]; array containing the new Lanes
524 * @throws NamingException when names cannot be registered for animation
525 * @throws NetworkException on topological problems
526 * @throws OtsGeometryException when creation of center line or contour fails
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 * Create a simple road with the specified number of Lanes, based on a Bezier curve.<br>
540 * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
541 * method of the Lane.
542 * @param network RoadNetwork; the network
543 * @param name String; name of the Link
544 * @param n1 Node; control node for the start direction
545 * @param n2 Node; starting node of the new Lane
546 * @param n3 Node; ending node of the new Lane
547 * @param n4 Node; control node for the end direction
548 * @param laneCount int; number of lanes in the road
549 * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
550 * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
551 * @param laneType LaneType; type of the new Lanes
552 * @param speedLimit Speed; the speed limit on all lanes
553 * @param simulator OtsSimulatorInterface; the simulator
554 * @param gtuType GtuType; parent GTU type of relevant GTUs
555 * @return Lane<String, String>[]; array containing the new Lanes
556 * @throws NamingException when names cannot be registered for animation
557 * @throws NetworkException on topological problems
558 * @throws OtsGeometryException when creation of center line or contour fails
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 // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
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 * @param n1 Node; node 1
583 * @param n2 Node; node 2
584 * @param n3 Node; node 3
585 * @param n4 Node; node 4
586 * @return line between n2 and n3 with start-direction n1-->n2 and end-direction n3-->n4
587 * @throws OtsGeometryException on failure of Bezier curve creation
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 }