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.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   * <p>
42   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
43   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
44   * <p>
45   * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
46   * initial version 30 okt. 2014 <br>
47   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
48   */
49  public final class LaneFactory
50  {
51  
52      /** Stripe width. */
53      private static final Length STRIPE_WIDTH = Length.instantiateSI(0.2);
54  
55      /** Angle above which a Bezier curve is used over a straight line. */
56      private static final double BEZIER_MARGIN = Math.toRadians(0.5);
57  
58      /** Link. */
59      private final CrossSectionLink link;
60  
61      /** Offset for next cross section elements. Left side of lane when building left to right, and vice versa. */
62      private Length offset;
63  
64      /** Lane width to use (negative when building left to right). */
65      private Length laneWidth0;
66  
67      /** Start offset. */
68      private Length offsetStart = Length.ZERO;
69  
70      /** End offset. */
71      private Length offsetEnd = Length.ZERO;
72  
73      /** Lane type to use. */
74      private LaneType laneType0;
75  
76      /** Speed limit to use. */
77      private Speed speedLimit0;
78  
79      /** created lanes. */
80      private final List<Lane> lanes = new ArrayList<>();
81  
82      /**
83       * @param network OTSRoadNetwork; network
84       * @param from OTSRoadNode; from node
85       * @param to OTSRoadNode; to node
86       * @param type LinkType; link type
87       * @param simulator OTSSimulatorInterface; simulator
88       * @param policy LaneKeepingPolicy; lane keeping policy
89       * @throws OTSGeometryException if no valid line can be created
90       * @throws NetworkException if the link exists, or a node does not exist, in the network
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       * @param network OTSRoadNetwork; network
100      * @param from OTSRoadNode; from node
101      * @param to OTSRoadNode; to node
102      * @param type LinkType; link type
103      * @param simulator OTSSimulatorInterface; simulator
104      * @param policy LaneKeepingPolicy; lane keeping policy
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 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      * Creates a line between two nodes. If the nodes and their directions are on a straight line, a straight line is created.
116      * Otherwise a default Bezier curve is created.
117      * @param from OTSNode; from node
118      * @param to OTSNode; to node
119      * @return OTSLine3D; line
120      * @throws OTSGeometryException if no valid line can be created
121      */
122     private static OTSLine3D makeLine(final OTSNode from, final OTSNode to) throws OTSGeometryException
123     {
124         // Straight or bezier?
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      * Prepare the factory to add lanes from left to right.
149      * @param leftLanes double; number of lanes left from the link design line
150      * @param laneWidth Length; lane width
151      * @param laneType LaneType; lane type
152      * @param speedLimit Speed; speed limit
153      * @return LaneFactory this lane factory for method chaining
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      * Prepare the factory to add lanes from right to left.
170      * @param rightLanes double; number of lanes right from the link design line
171      * @param laneWidth Length; lane width
172      * @param laneType LaneType; lane type
173      * @param speedLimit Speed; speed limit
174      * @return LaneFactory this lane factory for method chaining
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      * Set start offset.
190      * @param startOffset Length; offset
191      * @return LaneFactory this lane factory for method chaining
192      */
193     public LaneFactory setOffsetStart(final Length startOffset)
194     {
195         this.offsetStart = startOffset;
196         return this;
197     }
198 
199     /**
200      * Set end offset.
201      * @param endOffset Length; offset
202      * @return LaneFactory this lane factory for method chaining
203      */
204     public LaneFactory setOffsetEnd(final Length endOffset)
205     {
206         this.offsetEnd = endOffset;
207         return this;
208     }
209 
210     /**
211      * Adds a lane pair for each permeable, where the permeable determines the right-hand side line when building from left to
212      * right and vice versa. The left-most line is created in {@code leftToRight()}, meaning that each permeable describes
213      * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). For no allowed
214      * lane changes use {@code null}. This method internally adds {@code null} to create the final continuous stripe.
215      * @param permeable Permeable...; permeable per lane pair, for N lanes N-1 should be provided
216      * @return this LaneFactory this lane factory for method chaining
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      * Adds 1 or 2 shoulders to the current set of lanes.
243      * @param width Length; width of the shoulder
244      * @param lat LateralDirectionality; side of shoulder, use {@code null} or {@code NONE} for both
245      * @return LaneFactory this lane factory for method chaining
246      * @throws IllegalStateException if no lanes are defined
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      * Returns the created lanes in build order.
299      * @return List&lt;Lane&gt; created lanes in build order
300      */
301     public List<Lane> getLanes()
302     {
303         return this.lanes;
304     }
305 
306     /**
307      * Create a Link along intermediate coordinates from one Node to another.
308      * @param network RoadNetwork; the network
309      * @param name String; name of the new Link
310      * @param from OTSRoadNode; start Node of the new Link
311      * @param to OTSRoadNode; end Node of the new Link
312      * @param intermediatePoints OTSPoint3D[]; array of intermediate coordinates (may be null); the intermediate points may
313      *            contain the coordinates of the from node and to node
314      * @param simulator OTSSimulatorInterface; the simulator for this network
315      * @return Link; the newly constructed Link
316      * @throws OTSGeometryException when the design line is degenerate (only one point or duplicate point)
317      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
318      *             or the end node of the link are not registered in the network.
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         // see if an intermediate point needs to be created to the start of the link in the right direction
337         OTSPoint3D s1 = pointList.get(0);
338         OTSPoint3D s2 = pointList.get(1);
339         double dy = s2.y - s1.y;
340         double dx = s2.x - s1.x;
341         double a = from.getLocation().getRotZ();
342         if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
343         {
344             double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0); 
345             OTSPoint3D extra = new OTSPoint3D(s1.x + r * Math.cos(a), s1.y + r * Math.sin(a), s1.z);
346             pointList.add(1, extra);
347         }
348         
349         // see if an intermediate point needs to be created to the end of the link in the right direction
350         s1 = pointList.get(pointList.size() - 2);
351         s2 = pointList.get(pointList.size() - 1);
352         dy = s2.y - s1.y;
353         dx = s2.x - s1.x;
354         a = to.getLocation().getRotZ() - Math.PI;
355         if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
356         {
357             double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0); 
358             OTSPoint3D extra = new OTSPoint3D(s2.x + r * Math.cos(a), s2.y + r * Math.sin(a), s2.z);
359             pointList.add(pointList.size() - 2, extra);
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      * Create one Lane.
371      * @param link CrossSectionLink; the link that owns the new Lane
372      * @param id String; the id of this lane, should be unique within the link
373      * @param laneType LaneType; the type of the new Lane
374      * @param latPosAtStart Length; the lateral position of the new Lane with respect to the design line of the link at the
375      *            start of the link
376      * @param latPosAtEnd Length; the lateral position of the new Lane with respect to the design line of the link at the end of
377      *            the link
378      * @param width Length; the width of the new Lane
379      * @param speedLimit Speed; the speed limit on the new Lane
380      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator
381      * @return Lane
382      * @throws NetworkException on network inconsistency
383      * @throws OTSGeometryException when creation of center line or contour fails
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      * Create a simple Lane.
398      * @param network RoadNetwork; the network
399      * @param name String; name of the Lane (and also of the Link that owns it)
400      * @param from OTSRoadNode; starting node of the new Lane
401      * @param to OTSRoadNode; ending node of the new Lane
402      * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
403      *            points may contain the coordinates of the from node and to node
404      * @param laneType LaneType; type of the new Lane
405      * @param speedLimit Speed; the speed limit on the new Lane
406      * @param simulator OTSSimulatorInterface; the simulator
407      * @return Lane; the new Lane
408      * @throws NetworkException on network inconsistency
409      * @throws OTSGeometryException when creation of center line or contour fails
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      * Create a simple road with the specified number of Lanes.<br>
423      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
424      * method of the Lane.
425      * @param network RoadNetwork; the network
426      * @param name String; name of the Link
427      * @param from OTSNode; starting node of the new Lane
428      * @param to OTSNode; ending node of the new Lane
429      * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
430      *            points may contain the coordinates of the from node and to node
431      * @param laneCount int; number of lanes in the road
432      * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
433      * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
434      * @param laneType LaneType; type of the new Lanes
435      * @param speedLimit Speed; the speed limit on all lanes
436      * @param simulator OTSSimulatorInterface; the simulator
437      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
438      * @throws NetworkException on topological problems
439      * @throws OTSGeometryException when creation of center line or contour fails
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             // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
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      * Create a simple road with the specified number of Lanes.<br>
463      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
464      * method of the Lane.
465      * @param network RoadNetwork; the network
466      * @param name String; name of the Link
467      * @param from OTSRoadNode; starting node of the new Lane
468      * @param to OTSRoadNode; ending node of the new Lane
469      * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
470      *            points may contain the coordinates of the from node and to node
471      * @param laneCount int; number of lanes in the road
472      * @param laneType LaneType; type of the new Lanes
473      * @param speedLimit Speed; Speed the speed limit (applies to all generated lanes)
474      * @param simulator OTSSimulatorInterface; the simulator
475      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
476      * @throws NamingException when names cannot be registered for animation
477      * @throws NetworkException on topological problems
478      * @throws OTSGeometryException when creation of center line or contour fails
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      * Create a simple road with the specified number of Lanes, based on a Bezier curve.<br>
490      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
491      * method of the Lane.
492      * @param network RoadNetwork; the network
493      * @param name String; name of the Link
494      * @param n1 OTSRoadNode; control node for the start direction
495      * @param n2 OTSRoadNode; starting node of the new Lane
496      * @param n3 OTSRoadNode; ending node of the new Lane
497      * @param n4 OTSRoadNode; control node for the end direction
498      * @param laneCount int; number of lanes in the road
499      * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
500      * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
501      * @param laneType LaneType; type of the new Lanes
502      * @param speedLimit Speed; the speed limit on all lanes
503      * @param simulator OTSSimulatorInterface; the simulator
504      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
505      * @throws NamingException when names cannot be registered for animation
506      * @throws NetworkException on topological problems
507      * @throws OTSGeometryException when creation of center line or contour fails
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             // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
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      * @param n1 OTSNode; node 1
532      * @param n2 OTSNode; node 2
533      * @param n3 OTSNode; node 3
534      * @param n4 OTSNode; node 4
535      * @return line between n2 and n3 with start-direction n1--&gt;n2 and end-direction n3--&gt;n4
536      * @throws OTSGeometryException on failure of Bezier curve creation
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 }