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.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.OTSNode;
27 import org.opentrafficsim.road.network.OTSRoadNetwork;
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 /**
38 * <p>
39 * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
40 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
41 * <p>
42 * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
43 * initial version 30 okt. 2014 <br>
44 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
45 */
46 public final class LaneFactory
47 {
48
49 /** Stripe width. */
50 private static final Length STRIPE_WIDTH = Length.instantiateSI(0.2);
51
52 /** Angle above which a Bezier curve is used over a straight line. */
53 private static final double BEZIER_MARGIN = Math.toRadians(0.5);
54
55 /** Link. */
56 private final CrossSectionLink link;
57
58 /** Offset for next cross section elements. Left side of lane when building left to right, and vice versa. */
59 private Length offset;
60
61 /** Lane width to use (negative when building left to right). */
62 private Length laneWidth0;
63
64 /** Start offset. */
65 private Length offsetStart = Length.ZERO;
66
67 /** End offset. */
68 private Length offsetEnd = Length.ZERO;
69
70 /** Lane type to use. */
71 private LaneType laneType0;
72
73 /** Speed limit to use. */
74 private Speed speedLimit0;
75
76 /** created lanes. */
77 private final List<Lane> lanes = new ArrayList<>();
78
79 /**
80 * @param network OTSRoadNetwork; network
81 * @param from OTSRoadNode; from node
82 * @param to OTSRoadNode; to node
83 * @param type LinkType; link type
84 * @param simulator OTSSimulatorInterface; simulator
85 * @param policy LaneKeepingPolicy; lane keeping policy
86 * @throws OTSGeometryException if no valid line can be created
87 * @throws NetworkException if the link exists, or a node does not exist, in the network
88 */
89 public LaneFactory(final OTSRoadNetwork network, final OTSRoadNode from, final OTSRoadNode to, final LinkType type,
90 final OTSSimulatorInterface simulator, final LaneKeepingPolicy policy) throws OTSGeometryException, NetworkException
91 {
92 this(network, from, to, type, simulator, policy, makeLine(from, to));
93 }
94
95 /**
96 * @param network OTSRoadNetwork; network
97 * @param from OTSRoadNode; from node
98 * @param to OTSRoadNode; to node
99 * @param type LinkType; link type
100 * @param simulator OTSSimulatorInterface; simulator
101 * @param policy LaneKeepingPolicy; lane keeping policy
102 * @param line OTSLine3D; line
103 * @throws NetworkException if the link exists, or a node does not exist, in the network
104 */
105 public LaneFactory(final OTSRoadNetwork network, final OTSRoadNode from, final OTSRoadNode to, final LinkType type,
106 final OTSSimulatorInterface simulator, final LaneKeepingPolicy policy, final OTSLine3D line) throws NetworkException
107 {
108 this.link = new CrossSectionLink(network, from.getId() + to.getId(), from, to, type, line, policy);
109 }
110
111 /**
112 * Creates a line between two nodes. If the nodes and their directions are on a straight line, a straight line is created.
113 * Otherwise a default Bezier curve is created.
114 * @param from OTSNode; from node
115 * @param to OTSNode; to node
116 * @return OTSLine3D; line
117 * @throws OTSGeometryException if no valid line can be created
118 */
119 private static OTSLine3D makeLine(final OTSNode from, final OTSNode to) throws OTSGeometryException
120 {
121 // Straight or bezier?
122 double rotCrow = Math.atan2(to.getLocation().y - from.getLocation().y, to.getLocation().x - from.getLocation().x);
123 double dRot = from.getLocation().getRotZ() - rotCrow;
124 while (dRot < -Math.PI)
125 {
126 dRot += 2.0 * Math.PI;
127 }
128 while (dRot > Math.PI)
129 {
130 dRot -= 2.0 * Math.PI;
131 }
132 OTSLine3D line;
133 if (from.getLocation().getRotZ() != to.getLocation().getRotZ() || Math.abs(dRot) > BEZIER_MARGIN)
134 {
135 line = Bezier.cubic(from.getLocation(), to.getLocation());
136 }
137 else
138 {
139 line = new OTSLine3D(from.getPoint(), to.getPoint());
140 }
141 return line;
142 }
143
144 /**
145 * Prepare the factory to add lanes from left to right.
146 * @param leftLanes double; number of lanes left from the link design line
147 * @param laneWidth Length; lane width
148 * @param laneType LaneType; lane type
149 * @param speedLimit Speed; speed limit
150 * @return LaneFactory this lane factory for method chaining
151 */
152 public LaneFactory leftToRight(final double leftLanes, final Length laneWidth, final LaneType laneType,
153 final Speed speedLimit)
154 {
155 this.offset = laneWidth.times(leftLanes);
156 this.laneWidth0 = laneWidth.neg();
157 this.laneType0 = laneType;
158 this.speedLimit0 = speedLimit;
159 Try.execute(
160 () -> new Stripe(this.link, this.offset.plus(this.offsetStart), this.offset.plus(this.offsetEnd), STRIPE_WIDTH),
161 "Unexpected exception while building link.");
162 return this;
163 }
164
165 /**
166 * Prepare the factory to add lanes from right to left.
167 * @param rightLanes double; number of lanes right from the link design line
168 * @param laneWidth Length; lane width
169 * @param laneType LaneType; lane type
170 * @param speedLimit Speed; speed limit
171 * @return LaneFactory this lane factory for method chaining
172 */
173 public LaneFactory rightToLeft(final double rightLanes, final Length laneWidth, final LaneType laneType,
174 final Speed speedLimit)
175 {
176 this.offset = laneWidth.times(-rightLanes);
177 this.laneWidth0 = laneWidth;
178 this.laneType0 = laneType;
179 this.speedLimit0 = speedLimit;
180 Try.execute(() -> new Stripe(this.link, this.offset, this.offset, STRIPE_WIDTH),
181 "Unexpected exception while building link.");
182 return this;
183 }
184
185 /**
186 * Set start offset.
187 * @param startOffset Length; offset
188 * @return LaneFactory this lane factory for method chaining
189 */
190 public LaneFactory setOffsetStart(final Length startOffset)
191 {
192 this.offsetStart = startOffset;
193 return this;
194 }
195
196 /**
197 * Set end offset.
198 * @param endOffset Length; offset
199 * @return LaneFactory this lane factory for method chaining
200 */
201 public LaneFactory setOffsetEnd(final Length endOffset)
202 {
203 this.offsetEnd = endOffset;
204 return this;
205 }
206
207 /**
208 * Adds a lane pair for each permeable, where the permeable determines the right-hand side line when building from left to
209 * right and vice versa. The left-most line is created in {@code leftToRight()}, meaning that each permeable describes
210 * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). For no allowed
211 * lane changes use {@code null}. This method internally adds {@code null} to create the final continuous stripe.
212 * @param permeable Permeable...; permeable per lane pair, for N lanes N-1 should be provided
213 * @return this LaneFactory this lane factory for method chaining
214 */
215 public LaneFactory addLanes(final Permeable... permeable)
216 {
217 List<Permeable> list = new ArrayList<>(Arrays.asList(permeable));
218 list.add(null);
219 for (Permeable perm : list)
220 {
221 Length startOffset = this.offset.plus(this.laneWidth0.times(0.5)).plus(this.offsetStart);
222 Length endOffset = this.offset.plus(this.laneWidth0.times(0.5)).plus(this.offsetEnd);
223 this.lanes.add(Try.assign(
224 () -> new Lane(this.link, "Lane " + (this.lanes.size() + 1), startOffset, endOffset, this.laneWidth0.abs(),
225 this.laneWidth0.abs(), this.laneType0, this.speedLimit0),
226 "Unexpected exception while building link."));
227 this.offset = this.offset.plus(this.laneWidth0);
228 Stripe stripe = Try.assign(() -> new Stripe(this.link, this.offset.plus(this.offsetStart),
229 this.offset.plus(this.offsetEnd), STRIPE_WIDTH), "Unexpected exception while building link.");
230 if (perm != null)
231 {
232 stripe.addPermeability(this.link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), perm);
233 }
234 }
235 return this;
236 }
237
238 /**
239 * Adds 1 or 2 shoulders to the current set of lanes.
240 * @param width Length; width of the shoulder
241 * @param lat LateralDirectionality; side of shoulder, use {@code null} or {@code NONE} for both
242 * @return LaneFactory this lane factory for method chaining
243 * @throws IllegalStateException if no lanes are defined
244 */
245 public LaneFactory addShoulder(final Length width, final LateralDirectionality lat)
246 {
247 Throw.when(this.lanes.isEmpty(), IllegalStateException.class, "Lanes should be defined before adding shoulder(s).");
248 if (lat == null || lat.isNone() || lat.isLeft())
249 {
250 Length startOffset = null;
251 Length endOffset = null;
252 for (Lane lane : this.lanes)
253 {
254 if (startOffset == null
255 || lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().times(0.5)).gt(startOffset))
256 {
257 startOffset = lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().times(0.5));
258 }
259 if (endOffset == null || lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().times(0.5)).gt(endOffset))
260 {
261 endOffset = lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().times(0.5));
262 }
263 }
264 Length start = startOffset.plus(width.times(0.5));
265 Length end = endOffset.plus(width.times(0.5));
266 Try.assign(() -> new Shoulder(this.link, "Left shoulder", start, end, width, width),
267 "Unexpected exception while building link.");
268 }
269 if (lat == null || lat.isNone() || lat.isRight())
270 {
271 Length startOffset = null;
272 Length endOffset = null;
273 for (Lane lane : this.lanes)
274 {
275 if (startOffset == null
276 || lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().times(0.5)).lt(startOffset))
277 {
278 startOffset = lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().times(0.5));
279 }
280 if (endOffset == null
281 || lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().times(0.5)).lt(endOffset))
282 {
283 endOffset = lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().times(0.5));
284 }
285 }
286 Length start = startOffset.minus(width.times(0.5));
287 Length end = endOffset.minus(width.times(0.5));
288 Try.assign(() -> new Shoulder(this.link, "Right shoulder", start, end, width, width),
289 "Unexpected exception while building link.");
290 }
291 return this;
292 }
293
294 /**
295 * Returns the created lanes in build order.
296 * @return List<Lane> created lanes in build order
297 */
298 public List<Lane> getLanes()
299 {
300 return this.lanes;
301 }
302
303 /**
304 * Create a Link along intermediate coordinates from one Node to another.
305 * @param network RoadNetwork; the network
306 * @param name String; name of the new Link
307 * @param from OTSRoadNode; start Node of the new Link
308 * @param to OTSRoadNode; end Node of the new Link
309 * @param intermediatePoints OTSPoint3D[]; array of intermediate coordinates (may be null); the intermediate points may
310 * contain the coordinates of the from node and to node
311 * @param simulator OTSSimulatorInterface; the simulator for this network
312 * @return Link; the newly constructed Link
313 * @throws OTSGeometryException when the design line is degenerate (only one point or duplicate point)
314 * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
315 * or the end node of the link are not registered in the network.
316 */
317 public static CrossSectionLink makeLink(final OTSRoadNetwork network, final String name, final OTSRoadNode from,
318 final OTSRoadNode to, final OTSPoint3D[] intermediatePoints, final OTSSimulatorInterface simulator)
319 throws OTSGeometryException, NetworkException
320 {
321 List<OTSPoint3D> pointList =
322 intermediatePoints == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(intermediatePoints));
323 if (pointList.size() == 0 || !from.getPoint().equals(pointList.get(0)))
324 {
325 pointList.add(0, from.getPoint());
326 }
327 if (pointList.size() == 0 || !to.getPoint().equals(pointList.get(pointList.size() - 1)))
328 {
329 pointList.add(to.getPoint());
330 }
331
332 /*-
333 // see if an intermediate point needs to be created to the start of the link in the right direction
334 OTSPoint3D s1 = pointList.get(0);
335 OTSPoint3D s2 = pointList.get(1);
336 double dy = s2.y - s1.y;
337 double dx = s2.x - s1.x;
338 double a = from.getLocation().getRotZ();
339 if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
340 {
341 double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
342 OTSPoint3D extra = new OTSPoint3D(s1.x + r * Math.cos(a), s1.y + r * Math.sin(a), s1.z);
343 pointList.add(1, extra);
344 }
345
346 // see if an intermediate point needs to be created to the end of the link in the right direction
347 s1 = pointList.get(pointList.size() - 2);
348 s2 = pointList.get(pointList.size() - 1);
349 dy = s2.y - s1.y;
350 dx = s2.x - s1.x;
351 a = to.getLocation().getRotZ() - Math.PI;
352 if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
353 {
354 double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
355 OTSPoint3D extra = new OTSPoint3D(s2.x + r * Math.cos(a), s2.y + r * Math.sin(a), s2.z);
356 pointList.add(pointList.size() - 2, extra);
357 }
358 */
359
360 OTSLine3D designLine = new OTSLine3D(pointList);
361 CrossSectionLink link = new CrossSectionLink(network, name, from, to, network.getLinkType(LinkType.DEFAULTS.ROAD),
362 designLine, LaneKeepingPolicy.KEEPRIGHT);
363 return link;
364 }
365
366 /**
367 * Create one Lane.
368 * @param link CrossSectionLink; the link that owns the new Lane
369 * @param id String; the id of this lane, should be unique within the link
370 * @param laneType LaneType; the type of the new Lane
371 * @param latPosAtStart Length; the lateral position of the new Lane with respect to the design line of the link at the
372 * start of the link
373 * @param latPosAtEnd Length; the lateral position of the new Lane with respect to the design line of the link at the end of
374 * the link
375 * @param width Length; the width of the new Lane
376 * @param speedLimit Speed; the speed limit on the new Lane
377 * @param simulator OTSSimulatorInterface; the simulator
378 * @return Lane
379 * @throws NetworkException on network inconsistency
380 * @throws OTSGeometryException when creation of center line or contour fails
381 */
382 @SuppressWarnings("checkstyle:parameternumber")
383 private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType,
384 final Length latPosAtStart, final Length latPosAtEnd, final Length width, final Speed speedLimit,
385 final OTSSimulatorInterface simulator) throws NetworkException, OTSGeometryException
386 {
387 Map<GTUType, Speed> speedMap = new LinkedHashMap<>();
388 speedMap.put(link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), speedLimit);
389 Lane result = new Lane(link, id, latPosAtStart, latPosAtEnd, width, width, laneType, speedMap);
390 return result;
391 }
392
393 /**
394 * Create a simple Lane.
395 * @param network RoadNetwork; the network
396 * @param name String; name of the Lane (and also of the Link that owns it)
397 * @param from OTSRoadNode; starting node of the new Lane
398 * @param to OTSRoadNode; ending node of the new Lane
399 * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
400 * points may contain the coordinates of the from node and to node
401 * @param laneType LaneType; type of the new Lane
402 * @param speedLimit Speed; the speed limit on the new Lane
403 * @param simulator OTSSimulatorInterface; the simulator
404 * @return Lane; the new Lane
405 * @throws NetworkException on network inconsistency
406 * @throws OTSGeometryException when creation of center line or contour fails
407 */
408 public static Lane makeLane(final OTSRoadNetwork network, final String name, final OTSRoadNode from, final OTSRoadNode to,
409 final OTSPoint3D[] intermediatePoints, final LaneType laneType, final Speed speedLimit,
410 final OTSSimulatorInterface simulator) throws NetworkException, OTSGeometryException
411 {
412 Length width = new Length(4.0, LengthUnit.METER);
413 final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
414 Length latPos = new Length(0.0, LengthUnit.METER);
415 return makeLane(link, "lane", laneType, latPos, latPos, width, speedLimit, simulator);
416 }
417
418 /**
419 * Create a simple road with the specified number of Lanes.<br>
420 * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
421 * method of the Lane.
422 * @param network RoadNetwork; the network
423 * @param name String; name of the Link
424 * @param from OTSNode; starting node of the new Lane
425 * @param to OTSNode; ending node of the new Lane
426 * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
427 * points may contain the coordinates of the from node and to node
428 * @param laneCount int; number of lanes in the road
429 * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
430 * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
431 * @param laneType LaneType; type of the new Lanes
432 * @param speedLimit Speed; the speed limit on all lanes
433 * @param simulator OTSSimulatorInterface; the simulator
434 * @return Lane<String, String>[]; array containing the new Lanes
435 * @throws NetworkException on topological problems
436 * @throws OTSGeometryException when creation of center line or contour fails
437 */
438 @SuppressWarnings("checkstyle:parameternumber")
439 public static Lane[] makeMultiLane(final OTSRoadNetwork network, final String name, final OTSRoadNode from,
440 final OTSRoadNode to, final OTSPoint3D[] intermediatePoints, final int laneCount, final int laneOffsetAtStart,
441 final int laneOffsetAtEnd, final LaneType laneType, final Speed speedLimit, final OTSSimulatorInterface simulator)
442 throws NetworkException, OTSGeometryException
443 {
444 final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
445 Lane[] result = new Lane[laneCount];
446 Length width = new Length(4.0, LengthUnit.METER);
447 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
448 {
449 // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
450 Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
451 Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
452 result[laneIndex] =
453 makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit, simulator);
454 }
455 return result;
456 }
457
458 /**
459 * Create a simple road with the specified number of Lanes.<br>
460 * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
461 * method of the Lane.
462 * @param network RoadNetwork; the network
463 * @param name String; name of the Link
464 * @param from OTSRoadNode; starting node of the new Lane
465 * @param to OTSRoadNode; ending node of the new Lane
466 * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
467 * points may contain the coordinates of the from node and to node
468 * @param laneCount int; number of lanes in the road
469 * @param laneType LaneType; type of the new Lanes
470 * @param speedLimit Speed; Speed the speed limit (applies to all generated lanes)
471 * @param simulator OTSSimulatorInterface; the simulator
472 * @return Lane<String, String>[]; array containing the new Lanes
473 * @throws NamingException when names cannot be registered for animation
474 * @throws NetworkException on topological problems
475 * @throws OTSGeometryException when creation of center line or contour fails
476 */
477 @SuppressWarnings("checkstyle:parameternumber")
478 public static Lane[] makeMultiLane(final OTSRoadNetwork network, final String name, final OTSRoadNode from, final OTSRoadNode to,
479 final OTSPoint3D[] intermediatePoints, final int laneCount, final LaneType laneType, final Speed speedLimit,
480 final OTSSimulatorInterface simulator) throws NamingException, NetworkException, OTSGeometryException
481 {
482 return makeMultiLane(network, name, from, to, intermediatePoints, laneCount, 0, 0, laneType, speedLimit, simulator);
483 }
484
485 /**
486 * Create a simple road with the specified number of Lanes, based on a Bezier curve.<br>
487 * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
488 * method of the Lane.
489 * @param network RoadNetwork; the network
490 * @param name String; name of the Link
491 * @param n1 OTSRoadNode; control node for the start direction
492 * @param n2 OTSRoadNode; starting node of the new Lane
493 * @param n3 OTSRoadNode; ending node of the new Lane
494 * @param n4 OTSRoadNode; control node for the end direction
495 * @param laneCount int; number of lanes in the road
496 * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
497 * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
498 * @param laneType LaneType; type of the new Lanes
499 * @param speedLimit Speed; the speed limit on all lanes
500 * @param simulator OTSSimulatorInterface; the simulator
501 * @return Lane<String, String>[]; array containing the new Lanes
502 * @throws NamingException when names cannot be registered for animation
503 * @throws NetworkException on topological problems
504 * @throws OTSGeometryException when creation of center line or contour fails
505 */
506 @SuppressWarnings("checkstyle:parameternumber")
507 public static Lane[] makeMultiLaneBezier(final OTSRoadNetwork network, final String name, final OTSRoadNode n1,
508 final OTSRoadNode n2, final OTSRoadNode n3, final OTSRoadNode n4, final int laneCount, final int laneOffsetAtStart,
509 final int laneOffsetAtEnd, final LaneType laneType, final Speed speedLimit, final OTSSimulatorInterface simulator)
510 throws NamingException, NetworkException, OTSGeometryException
511 {
512 OTSLine3D bezier = makeBezier(n1, n2, n3, n4);
513 final CrossSectionLink link = makeLink(network, name, n2, n3, bezier.getPoints(), simulator);
514 Lane[] result = new Lane[laneCount];
515 Length width = new Length(4.0, LengthUnit.METER);
516 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
517 {
518 // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
519 Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
520 Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
521 result[laneIndex] =
522 makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit, simulator);
523 }
524 return result;
525 }
526
527 /**
528 * @param n1 OTSNode; node 1
529 * @param n2 OTSNode; node 2
530 * @param n3 OTSNode; node 3
531 * @param n4 OTSNode; node 4
532 * @return line between n2 and n3 with start-direction n1-->n2 and end-direction n3-->n4
533 * @throws OTSGeometryException on failure of Bezier curve creation
534 */
535 public static OTSLine3D makeBezier(final OTSNode n1, final OTSNode n2, final OTSNode n3, final OTSNode n4)
536 throws OTSGeometryException
537 {
538 OTSPoint3D p1 = n1.getPoint();
539 OTSPoint3D p2 = n2.getPoint();
540 OTSPoint3D p3 = n3.getPoint();
541 OTSPoint3D p4 = n4.getPoint();
542 DirectedPoint dp1 = new DirectedPoint(p2.x, p2.y, p2.z, 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
543 DirectedPoint dp2 = new DirectedPoint(p3.x, p3.y, p3.z, 0.0, 0.0, Math.atan2(p4.y - p3.y, p4.x - p3.x));
544 return Bezier.cubic(dp1, dp2);
545 }
546 }