LaneFactory.java

  1. package org.opentrafficsim.road.network.factory;

  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. import java.util.LinkedHashMap;
  5. import java.util.List;
  6. import java.util.Map;

  7. import javax.naming.NamingException;

  8. import org.djunits.unit.LengthUnit;
  9. import org.djunits.value.vdouble.scalar.Length;
  10. import org.djunits.value.vdouble.scalar.Speed;
  11. import org.djutils.exceptions.Throw;
  12. import org.djutils.exceptions.Try;
  13. import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
  14. import org.opentrafficsim.core.geometry.Bezier;
  15. import org.opentrafficsim.core.geometry.OTSGeometryException;
  16. import org.opentrafficsim.core.geometry.OTSLine3D;
  17. import org.opentrafficsim.core.geometry.OTSPoint3D;
  18. import org.opentrafficsim.core.gtu.GTUType;
  19. import org.opentrafficsim.core.network.LateralDirectionality;
  20. import org.opentrafficsim.core.network.LinkType;
  21. import org.opentrafficsim.core.network.NetworkException;
  22. import org.opentrafficsim.core.network.OTSNode;
  23. import org.opentrafficsim.road.network.OTSRoadNetwork;
  24. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  25. import org.opentrafficsim.road.network.lane.Lane;
  26. import org.opentrafficsim.road.network.lane.LaneType;
  27. import org.opentrafficsim.road.network.lane.OTSRoadNode;
  28. import org.opentrafficsim.road.network.lane.Shoulder;
  29. import org.opentrafficsim.road.network.lane.Stripe;
  30. import org.opentrafficsim.road.network.lane.Stripe.Permeable;
  31. import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;

  32. import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
  33. import nl.tudelft.simulation.language.d3.DirectedPoint;

  34. /**
  35.  * <p>
  36.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  37.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  38.  * <p>
  39.  * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
  40.  * initial version 30 okt. 2014 <br>
  41.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  42.  */
  43. public final class LaneFactory
  44. {

  45.     /** Stripe width. */
  46.     private static final Length STRIPE_WIDTH = Length.instantiateSI(0.2);

  47.     /** Angle above which a Bezier curve is used over a straight line. */
  48.     private static final double BEZIER_MARGIN = Math.toRadians(0.5);

  49.     /** Link. */
  50.     private final CrossSectionLink link;

  51.     /** Offset for next cross section elements. Left side of lane when building left to right, and vice versa. */
  52.     private Length offset;

  53.     /** Lane width to use (negative when building left to right). */
  54.     private Length laneWidth0;

  55.     /** Start offset. */
  56.     private Length offsetStart = Length.ZERO;

  57.     /** End offset. */
  58.     private Length offsetEnd = Length.ZERO;

  59.     /** Lane type to use. */
  60.     private LaneType laneType0;

  61.     /** Speed limit to use. */
  62.     private Speed speedLimit0;

  63.     /** created lanes. */
  64.     private final List<Lane> lanes = new ArrayList<>();

  65.     /**
  66.      * @param network OTSRoadNetwork; network
  67.      * @param from OTSRoadNode; from node
  68.      * @param to OTSRoadNode; to node
  69.      * @param type LinkType; link type
  70.      * @param simulator OTSSimulatorInterface; simulator
  71.      * @param policy LaneKeepingPolicy; lane keeping policy
  72.      * @throws OTSGeometryException if no valid line can be created
  73.      * @throws NetworkException if the link exists, or a node does not exist, in the network
  74.      */
  75.     public LaneFactory(final OTSRoadNetwork network, final OTSRoadNode from, final OTSRoadNode to, final LinkType type,
  76.             final OTSSimulatorInterface simulator, final LaneKeepingPolicy policy) throws OTSGeometryException, NetworkException
  77.     {
  78.         this(network, from, to, type, simulator, policy, makeLine(from, to));
  79.     }

  80.     /**
  81.      * @param network OTSRoadNetwork; network
  82.      * @param from OTSRoadNode; from node
  83.      * @param to OTSRoadNode; to node
  84.      * @param type LinkType; link type
  85.      * @param simulator OTSSimulatorInterface; simulator
  86.      * @param policy LaneKeepingPolicy; lane keeping policy
  87.      * @param line OTSLine3D; line
  88.      * @throws NetworkException if the link exists, or a node does not exist, in the network
  89.      */
  90.     public LaneFactory(final OTSRoadNetwork network, final OTSRoadNode from, final OTSRoadNode to, final LinkType type,
  91.             final OTSSimulatorInterface simulator, final LaneKeepingPolicy policy, final OTSLine3D line) throws NetworkException
  92.     {
  93.         this.link = new CrossSectionLink(network, from.getId() + to.getId(), from, to, type, line, policy);
  94.     }

  95.     /**
  96.      * Creates a line between two nodes. If the nodes and their directions are on a straight line, a straight line is created.
  97.      * Otherwise a default Bezier curve is created.
  98.      * @param from OTSNode; from node
  99.      * @param to OTSNode; to node
  100.      * @return OTSLine3D; line
  101.      * @throws OTSGeometryException if no valid line can be created
  102.      */
  103.     private static OTSLine3D makeLine(final OTSNode from, final OTSNode to) throws OTSGeometryException
  104.     {
  105.         // Straight or bezier?
  106.         double rotCrow = Math.atan2(to.getLocation().y - from.getLocation().y, to.getLocation().x - from.getLocation().x);
  107.         double dRot = from.getLocation().getRotZ() - rotCrow;
  108.         while (dRot < -Math.PI)
  109.         {
  110.             dRot += 2.0 * Math.PI;
  111.         }
  112.         while (dRot > Math.PI)
  113.         {
  114.             dRot -= 2.0 * Math.PI;
  115.         }
  116.         OTSLine3D line;
  117.         if (from.getLocation().getRotZ() != to.getLocation().getRotZ() || Math.abs(dRot) > BEZIER_MARGIN)
  118.         {
  119.             line = Bezier.cubic(from.getLocation(), to.getLocation());
  120.         }
  121.         else
  122.         {
  123.             line = new OTSLine3D(from.getPoint(), to.getPoint());
  124.         }
  125.         return line;
  126.     }

  127.     /**
  128.      * Prepare the factory to add lanes from left to right.
  129.      * @param leftLanes double; number of lanes left from the link design line
  130.      * @param laneWidth Length; lane width
  131.      * @param laneType LaneType; lane type
  132.      * @param speedLimit Speed; speed limit
  133.      * @return LaneFactory this lane factory for method chaining
  134.      */
  135.     public LaneFactory leftToRight(final double leftLanes, final Length laneWidth, final LaneType laneType,
  136.             final Speed speedLimit)
  137.     {
  138.         this.offset = laneWidth.times(leftLanes);
  139.         this.laneWidth0 = laneWidth.neg();
  140.         this.laneType0 = laneType;
  141.         this.speedLimit0 = speedLimit;
  142.         Try.execute(
  143.                 () -> new Stripe(this.link, this.offset.plus(this.offsetStart), this.offset.plus(this.offsetEnd), STRIPE_WIDTH),
  144.                 "Unexpected exception while building link.");
  145.         return this;
  146.     }

  147.     /**
  148.      * Prepare the factory to add lanes from right to left.
  149.      * @param rightLanes double; number of lanes right 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 rightToLeft(final double rightLanes, final Length laneWidth, final LaneType laneType,
  156.             final Speed speedLimit)
  157.     {
  158.         this.offset = laneWidth.times(-rightLanes);
  159.         this.laneWidth0 = laneWidth;
  160.         this.laneType0 = laneType;
  161.         this.speedLimit0 = speedLimit;
  162.         Try.execute(() -> new Stripe(this.link, this.offset, this.offset, STRIPE_WIDTH),
  163.                 "Unexpected exception while building link.");
  164.         return this;
  165.     }

  166.     /**
  167.      * Set start offset.
  168.      * @param startOffset Length; offset
  169.      * @return LaneFactory this lane factory for method chaining
  170.      */
  171.     public LaneFactory setOffsetStart(final Length startOffset)
  172.     {
  173.         this.offsetStart = startOffset;
  174.         return this;
  175.     }

  176.     /**
  177.      * Set end offset.
  178.      * @param endOffset Length; offset
  179.      * @return LaneFactory this lane factory for method chaining
  180.      */
  181.     public LaneFactory setOffsetEnd(final Length endOffset)
  182.     {
  183.         this.offsetEnd = endOffset;
  184.         return this;
  185.     }

  186.     /**
  187.      * Adds a lane pair for each permeable, where the permeable determines the right-hand side line when building from left to
  188.      * right and vice versa. The left-most line is created in {@code leftToRight()}, meaning that each permeable describes
  189.      * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). For no allowed
  190.      * lane changes use {@code null}. This method internally adds {@code null} to create the final continuous stripe.
  191.      * @param permeable Permeable...; permeable per lane pair, for N lanes N-1 should be provided
  192.      * @return this LaneFactory this lane factory for method chaining
  193.      */
  194.     public LaneFactory addLanes(final Permeable... permeable)
  195.     {
  196.         List<Permeable> list = new ArrayList<>(Arrays.asList(permeable));
  197.         list.add(null);
  198.         for (Permeable perm : list)
  199.         {
  200.             Length startOffset = this.offset.plus(this.laneWidth0.times(0.5)).plus(this.offsetStart);
  201.             Length endOffset = this.offset.plus(this.laneWidth0.times(0.5)).plus(this.offsetEnd);
  202.             this.lanes.add(Try.assign(
  203.                     () -> new Lane(this.link, "Lane " + (this.lanes.size() + 1), startOffset, endOffset, this.laneWidth0.abs(),
  204.                             this.laneWidth0.abs(), this.laneType0, this.speedLimit0),
  205.                     "Unexpected exception while building link."));
  206.             this.offset = this.offset.plus(this.laneWidth0);
  207.             Stripe stripe = Try.assign(() -> new Stripe(this.link, this.offset.plus(this.offsetStart),
  208.                     this.offset.plus(this.offsetEnd), STRIPE_WIDTH), "Unexpected exception while building link.");
  209.             if (perm != null)
  210.             {
  211.                 stripe.addPermeability(this.link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), perm);
  212.             }
  213.         }
  214.         return this;
  215.     }

  216.     /**
  217.      * Adds 1 or 2 shoulders to the current set of lanes.
  218.      * @param width Length; width of the shoulder
  219.      * @param lat LateralDirectionality; side of shoulder, use {@code null} or {@code NONE} for both
  220.      * @return LaneFactory this lane factory for method chaining
  221.      * @throws IllegalStateException if no lanes are defined
  222.      */
  223.     public LaneFactory addShoulder(final Length width, final LateralDirectionality lat)
  224.     {
  225.         Throw.when(this.lanes.isEmpty(), IllegalStateException.class, "Lanes should be defined before adding shoulder(s).");
  226.         if (lat == null || lat.isNone() || lat.isLeft())
  227.         {
  228.             Length startOffset = null;
  229.             Length endOffset = null;
  230.             for (Lane lane : this.lanes)
  231.             {
  232.                 if (startOffset == null
  233.                         || lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().times(0.5)).gt(startOffset))
  234.                 {
  235.                     startOffset = lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().times(0.5));
  236.                 }
  237.                 if (endOffset == null || lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().times(0.5)).gt(endOffset))
  238.                 {
  239.                     endOffset = lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().times(0.5));
  240.                 }
  241.             }
  242.             Length start = startOffset.plus(width.times(0.5));
  243.             Length end = endOffset.plus(width.times(0.5));
  244.             Try.assign(() -> new Shoulder(this.link, "Left shoulder", start, end, width, width),
  245.                     "Unexpected exception while building link.");
  246.         }
  247.         if (lat == null || lat.isNone() || lat.isRight())
  248.         {
  249.             Length startOffset = null;
  250.             Length endOffset = null;
  251.             for (Lane lane : this.lanes)
  252.             {
  253.                 if (startOffset == null
  254.                         || lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().times(0.5)).lt(startOffset))
  255.                 {
  256.                     startOffset = lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().times(0.5));
  257.                 }
  258.                 if (endOffset == null
  259.                         || lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().times(0.5)).lt(endOffset))
  260.                 {
  261.                     endOffset = lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().times(0.5));
  262.                 }
  263.             }
  264.             Length start = startOffset.minus(width.times(0.5));
  265.             Length end = endOffset.minus(width.times(0.5));
  266.             Try.assign(() -> new Shoulder(this.link, "Right shoulder", start, end, width, width),
  267.                     "Unexpected exception while building link.");
  268.         }
  269.         return this;
  270.     }

  271.     /**
  272.      * Returns the created lanes in build order.
  273.      * @return List&lt;Lane&gt; created lanes in build order
  274.      */
  275.     public List<Lane> getLanes()
  276.     {
  277.         return this.lanes;
  278.     }

  279.     /**
  280.      * Create a Link along intermediate coordinates from one Node to another.
  281.      * @param network RoadNetwork; the network
  282.      * @param name String; name of the new Link
  283.      * @param from OTSRoadNode; start Node of the new Link
  284.      * @param to OTSRoadNode; end Node of the new Link
  285.      * @param intermediatePoints OTSPoint3D[]; array of intermediate coordinates (may be null); the intermediate points may
  286.      *            contain the coordinates of the from node and to node
  287.      * @param simulator OTSSimulatorInterface; the simulator for this network
  288.      * @return Link; the newly constructed Link
  289.      * @throws OTSGeometryException when the design line is degenerate (only one point or duplicate point)
  290.      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
  291.      *             or the end node of the link are not registered in the network.
  292.      */
  293.     public static CrossSectionLink makeLink(final OTSRoadNetwork network, final String name, final OTSRoadNode from,
  294.             final OTSRoadNode to, final OTSPoint3D[] intermediatePoints, final OTSSimulatorInterface simulator)
  295.             throws OTSGeometryException, NetworkException
  296.     {
  297.         List<OTSPoint3D> pointList =
  298.                 intermediatePoints == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(intermediatePoints));
  299.         if (pointList.size() == 0 || !from.getPoint().equals(pointList.get(0)))
  300.         {
  301.             pointList.add(0, from.getPoint());
  302.         }
  303.         if (pointList.size() == 0 || !to.getPoint().equals(pointList.get(pointList.size() - 1)))
  304.         {
  305.             pointList.add(to.getPoint());
  306.         }

  307.         /*-
  308.         // see if an intermediate point needs to be created to the start of the link in the right direction
  309.         OTSPoint3D s1 = pointList.get(0);
  310.         OTSPoint3D s2 = pointList.get(1);
  311.         double dy = s2.y - s1.y;
  312.         double dx = s2.x - s1.x;
  313.         double a = from.getLocation().getRotZ();
  314.         if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
  315.         {
  316.             double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
  317.             OTSPoint3D extra = new OTSPoint3D(s1.x + r * Math.cos(a), s1.y + r * Math.sin(a), s1.z);
  318.             pointList.add(1, extra);
  319.         }
  320.        
  321.         // see if an intermediate point needs to be created to the end of the link in the right direction
  322.         s1 = pointList.get(pointList.size() - 2);
  323.         s2 = pointList.get(pointList.size() - 1);
  324.         dy = s2.y - s1.y;
  325.         dx = s2.x - s1.x;
  326.         a = to.getLocation().getRotZ() - Math.PI;
  327.         if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
  328.         {
  329.             double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
  330.             OTSPoint3D extra = new OTSPoint3D(s2.x + r * Math.cos(a), s2.y + r * Math.sin(a), s2.z);
  331.             pointList.add(pointList.size() - 2, extra);
  332.         }
  333.          */

  334.         OTSLine3D designLine = new OTSLine3D(pointList);
  335.         CrossSectionLink link = new CrossSectionLink(network, name, from, to, network.getLinkType(LinkType.DEFAULTS.ROAD),
  336.                 designLine, LaneKeepingPolicy.KEEPRIGHT);
  337.         return link;
  338.     }

  339.     /**
  340.      * Create one Lane.
  341.      * @param link CrossSectionLink; the link that owns the new Lane
  342.      * @param id String; the id of this lane, should be unique within the link
  343.      * @param laneType LaneType; the type of the new Lane
  344.      * @param latPosAtStart Length; the lateral position of the new Lane with respect to the design line of the link at the
  345.      *            start of the link
  346.      * @param latPosAtEnd Length; the lateral position of the new Lane with respect to the design line of the link at the end of
  347.      *            the link
  348.      * @param width Length; the width of the new Lane
  349.      * @param speedLimit Speed; the speed limit on the new Lane
  350.      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator
  351.      * @return Lane
  352.      * @throws NetworkException on network inconsistency
  353.      * @throws OTSGeometryException when creation of center line or contour fails
  354.      */
  355.     @SuppressWarnings("checkstyle:parameternumber")
  356.     private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType,
  357.             final Length latPosAtStart, final Length latPosAtEnd, final Length width, final Speed speedLimit,
  358.             final DEVSSimulatorInterface.TimeDoubleUnit simulator) throws NetworkException, OTSGeometryException
  359.     {
  360.         Map<GTUType, Speed> speedMap = new LinkedHashMap<>();
  361.         speedMap.put(link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), speedLimit);
  362.         Lane result = new Lane(link, id, latPosAtStart, latPosAtEnd, width, width, laneType, speedMap);
  363.         return result;
  364.     }

  365.     /**
  366.      * Create a simple Lane.
  367.      * @param network RoadNetwork; the network
  368.      * @param name String; name of the Lane (and also of the Link that owns it)
  369.      * @param from OTSRoadNode; starting node of the new Lane
  370.      * @param to OTSRoadNode; ending node of the new Lane
  371.      * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
  372.      *            points may contain the coordinates of the from node and to node
  373.      * @param laneType LaneType; type of the new Lane
  374.      * @param speedLimit Speed; the speed limit on the new Lane
  375.      * @param simulator OTSSimulatorInterface; the simulator
  376.      * @return Lane; the new Lane
  377.      * @throws NetworkException on network inconsistency
  378.      * @throws OTSGeometryException when creation of center line or contour fails
  379.      */
  380.     public static Lane makeLane(final OTSRoadNetwork network, final String name, final OTSRoadNode from, final OTSRoadNode to,
  381.             final OTSPoint3D[] intermediatePoints, final LaneType laneType, final Speed speedLimit,
  382.             final OTSSimulatorInterface simulator) throws NetworkException, OTSGeometryException
  383.     {
  384.         Length width = new Length(4.0, LengthUnit.METER);
  385.         final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
  386.         Length latPos = new Length(0.0, LengthUnit.METER);
  387.         return makeLane(link, "lane", laneType, latPos, latPos, width, speedLimit, simulator);
  388.     }

  389.     /**
  390.      * Create a simple road with the specified number of Lanes.<br>
  391.      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
  392.      * method of the Lane.
  393.      * @param network RoadNetwork; the network
  394.      * @param name String; name of the Link
  395.      * @param from OTSNode; starting node of the new Lane
  396.      * @param to OTSNode; ending node of the new Lane
  397.      * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
  398.      *            points may contain the coordinates of the from node and to node
  399.      * @param laneCount int; number of lanes in the road
  400.      * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
  401.      * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
  402.      * @param laneType LaneType; type of the new Lanes
  403.      * @param speedLimit Speed; the speed limit on all lanes
  404.      * @param simulator OTSSimulatorInterface; the simulator
  405.      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
  406.      * @throws NetworkException on topological problems
  407.      * @throws OTSGeometryException when creation of center line or contour fails
  408.      */
  409.     @SuppressWarnings("checkstyle:parameternumber")
  410.     public static Lane[] makeMultiLane(final OTSRoadNetwork network, final String name, final OTSRoadNode from,
  411.             final OTSRoadNode to, final OTSPoint3D[] intermediatePoints, final int laneCount, final int laneOffsetAtStart,
  412.             final int laneOffsetAtEnd, final LaneType laneType, final Speed speedLimit, final OTSSimulatorInterface simulator)
  413.             throws NetworkException, OTSGeometryException
  414.     {
  415.         final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
  416.         Lane[] result = new Lane[laneCount];
  417.         Length width = new Length(4.0, LengthUnit.METER);
  418.         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
  419.         {
  420.             // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
  421.             Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
  422.             Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
  423.             result[laneIndex] =
  424.                     makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit, simulator);
  425.         }
  426.         return result;
  427.     }

  428.     /**
  429.      * Create a simple road with the specified number of Lanes.<br>
  430.      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
  431.      * method of the Lane.
  432.      * @param network RoadNetwork; the network
  433.      * @param name String; name of the Link
  434.      * @param from OTSRoadNode; starting node of the new Lane
  435.      * @param to OTSRoadNode; ending node of the new Lane
  436.      * @param intermediatePoints OTSPoint3D[]; intermediate coordinates or null to create a straight road; the intermediate
  437.      *            points may contain the coordinates of the from node and to node
  438.      * @param laneCount int; number of lanes in the road
  439.      * @param laneType LaneType; type of the new Lanes
  440.      * @param speedLimit Speed; Speed the speed limit (applies to all generated lanes)
  441.      * @param simulator OTSSimulatorInterface; the simulator
  442.      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
  443.      * @throws NamingException when names cannot be registered for animation
  444.      * @throws NetworkException on topological problems
  445.      * @throws OTSGeometryException when creation of center line or contour fails
  446.      */
  447.     @SuppressWarnings("checkstyle:parameternumber")
  448.     public static Lane[] makeMultiLane(final OTSRoadNetwork network, final String name, final OTSRoadNode from, final OTSRoadNode to,
  449.             final OTSPoint3D[] intermediatePoints, final int laneCount, final LaneType laneType, final Speed speedLimit,
  450.             final OTSSimulatorInterface simulator) throws NamingException, NetworkException, OTSGeometryException
  451.     {
  452.         return makeMultiLane(network, name, from, to, intermediatePoints, laneCount, 0, 0, laneType, speedLimit, simulator);
  453.     }

  454.     /**
  455.      * Create a simple road with the specified number of Lanes, based on a Bezier curve.<br>
  456.      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
  457.      * method of the Lane.
  458.      * @param network RoadNetwork; the network
  459.      * @param name String; name of the Link
  460.      * @param n1 OTSRoadNode; control node for the start direction
  461.      * @param n2 OTSRoadNode; starting node of the new Lane
  462.      * @param n3 OTSRoadNode; ending node of the new Lane
  463.      * @param n4 OTSRoadNode; control node for the end direction
  464.      * @param laneCount int; number of lanes in the road
  465.      * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
  466.      * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
  467.      * @param laneType LaneType; type of the new Lanes
  468.      * @param speedLimit Speed; the speed limit on all lanes
  469.      * @param simulator OTSSimulatorInterface; the simulator
  470.      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
  471.      * @throws NamingException when names cannot be registered for animation
  472.      * @throws NetworkException on topological problems
  473.      * @throws OTSGeometryException when creation of center line or contour fails
  474.      */
  475.     @SuppressWarnings("checkstyle:parameternumber")
  476.     public static Lane[] makeMultiLaneBezier(final OTSRoadNetwork network, final String name, final OTSRoadNode n1,
  477.             final OTSRoadNode n2, final OTSRoadNode n3, final OTSRoadNode n4, final int laneCount, final int laneOffsetAtStart,
  478.             final int laneOffsetAtEnd, final LaneType laneType, final Speed speedLimit, final OTSSimulatorInterface simulator)
  479.             throws NamingException, NetworkException, OTSGeometryException
  480.     {
  481.         OTSLine3D bezier = makeBezier(n1, n2, n3, n4);
  482.         final CrossSectionLink link = makeLink(network, name, n2, n3, bezier.getPoints(), simulator);
  483.         Lane[] result = new Lane[laneCount];
  484.         Length width = new Length(4.0, LengthUnit.METER);
  485.         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
  486.         {
  487.             // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
  488.             Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
  489.             Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
  490.             result[laneIndex] =
  491.                     makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit, simulator);
  492.         }
  493.         return result;
  494.     }

  495.     /**
  496.      * @param n1 OTSNode; node 1
  497.      * @param n2 OTSNode; node 2
  498.      * @param n3 OTSNode; node 3
  499.      * @param n4 OTSNode; node 4
  500.      * @return line between n2 and n3 with start-direction n1--&gt;n2 and end-direction n3--&gt;n4
  501.      * @throws OTSGeometryException on failure of Bezier curve creation
  502.      */
  503.     public static OTSLine3D makeBezier(final OTSNode n1, final OTSNode n2, final OTSNode n3, final OTSNode n4)
  504.             throws OTSGeometryException
  505.     {
  506.         OTSPoint3D p1 = n1.getPoint();
  507.         OTSPoint3D p2 = n2.getPoint();
  508.         OTSPoint3D p3 = n3.getPoint();
  509.         OTSPoint3D p4 = n4.getPoint();
  510.         DirectedPoint dp1 = new DirectedPoint(p2.x, p2.y, p2.z, 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
  511.         DirectedPoint dp2 = new DirectedPoint(p3.x, p3.y, p3.z, 0.0, 0.0, Math.atan2(p4.y - p3.y, p4.x - p3.x));
  512.         return Bezier.cubic(dp1, dp2);
  513.     }
  514. }