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