LaneFactory.java

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

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

  6. import javax.naming.NamingException;

  7. import org.djunits.unit.LengthUnit;
  8. import org.djunits.value.vdouble.scalar.Length;
  9. import org.djunits.value.vdouble.scalar.Speed;
  10. import org.djutils.exceptions.Throw;
  11. import org.djutils.exceptions.Try;
  12. import org.opentrafficsim.core.definitions.DefaultsNl;
  13. import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
  14. import org.opentrafficsim.core.geometry.Bezier;
  15. import org.opentrafficsim.core.geometry.DirectedPoint;
  16. import org.opentrafficsim.core.geometry.OtsGeometryException;
  17. import org.opentrafficsim.core.geometry.OtsLine3d;
  18. import org.opentrafficsim.core.geometry.OtsPoint3d;
  19. import org.opentrafficsim.core.gtu.GtuType;
  20. import org.opentrafficsim.core.network.LateralDirectionality;
  21. import org.opentrafficsim.core.network.LinkType;
  22. import org.opentrafficsim.core.network.NetworkException;
  23. import org.opentrafficsim.core.network.Node;
  24. import org.opentrafficsim.road.network.RoadNetwork;
  25. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  26. import org.opentrafficsim.road.network.lane.Lane;
  27. import org.opentrafficsim.road.network.lane.LaneType;
  28. import org.opentrafficsim.road.network.lane.Shoulder;
  29. import org.opentrafficsim.road.network.lane.Stripe;
  30. import org.opentrafficsim.road.network.lane.Stripe.Type;
  31. import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;

  32. /**
  33.  * <p>
  34.  * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  35.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  36.  * </p>
  37.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  38.  */
  39. public final class LaneFactory
  40. {

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

  43.     /** Link. */
  44.     private final CrossSectionLink link;

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

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

  49.     /** Start offset. */
  50.     private Length offsetStart = Length.ZERO;

  51.     /** End offset. */
  52.     private Length offsetEnd = Length.ZERO;

  53.     /** Lane type to use. */
  54.     private LaneType laneType0;

  55.     /** Speed limit to use. */
  56.     private Speed speedLimit0;

  57.     /** Parent GTU type of relevant GTUs. */
  58.     private GtuType gtuType;

  59.     /** Created lanes. */
  60.     private final List<Lane> lanes = new ArrayList<>();

  61.     /** Stored stripes, so we can return it to the user on the addLanes() call. */
  62.     private Stripe firstStripe;

  63.     /**
  64.      * @param network RoadNetwork; network
  65.      * @param from Node; from node
  66.      * @param to Node; to node
  67.      * @param type LinkType; link type
  68.      * @param simulator OtsSimulatorInterface; simulator
  69.      * @param policy LaneKeepingPolicy; lane keeping policy
  70.      * @param gtuType GtuType; parent GTU type of relevant GTUs.
  71.      * @throws OtsGeometryException if no valid line can be created
  72.      * @throws NetworkException if the link exists, or a node does not exist, in the network
  73.      */
  74.     public LaneFactory(final RoadNetwork network, final Node from, final Node to, final LinkType type,
  75.             final OtsSimulatorInterface simulator, final LaneKeepingPolicy policy, final GtuType gtuType)
  76.             throws OtsGeometryException, NetworkException
  77.     {
  78.         this(network, from, to, type, simulator, policy, gtuType, makeLine(from, to));
  79.     }

  80.     /**
  81.      * @param network RoadNetwork; network
  82.      * @param from Node; from node
  83.      * @param to Node; to node
  84.      * @param type LinkType; link type
  85.      * @param simulator OtsSimulatorInterface; simulator
  86.      * @param policy LaneKeepingPolicy; lane keeping policy
  87.      * @param gtuType GtuType; parent GTU type of relevant GTUs.
  88.      * @param line OtsLine3d; line
  89.      * @throws NetworkException if the link exists, or a node does not exist, in the network
  90.      */
  91.     public LaneFactory(final RoadNetwork network, final Node from, final Node to, final LinkType type,
  92.             final OtsSimulatorInterface simulator, final LaneKeepingPolicy policy, final GtuType gtuType, final OtsLine3d line)
  93.             throws NetworkException
  94.     {
  95.         this.link = new CrossSectionLink(network, from.getId() + to.getId(), from, to, type, line, policy);
  96.         this.gtuType = gtuType;
  97.     }

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

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

  153.     /**
  154.      * Prepare the factory to add lanes from right to left.
  155.      * @param rightLanes double; number of lanes right from the link design line
  156.      * @param laneWidth Length; lane width
  157.      * @param laneType LaneType; lane type
  158.      * @param speedLimit Speed; speed limit
  159.      * @return LaneFactory this lane factory for method chaining
  160.      */
  161.     public LaneFactory rightToLeft(final double rightLanes, final Length laneWidth, final LaneType laneType,
  162.             final Speed speedLimit)
  163.     {
  164.         this.offset = laneWidth.times(-rightLanes);
  165.         this.laneWidth0 = laneWidth;
  166.         this.laneType0 = laneType;
  167.         this.speedLimit0 = speedLimit;
  168.         this.firstStripe = Try.assign(() -> new Stripe(Type.SOLID, this.link, this.offset, getWidth(Type.SOLID)),
  169.                 "Unexpected exception while building link.");
  170.         return this;
  171.     }

  172.     /**
  173.      * Set start offset.
  174.      * @param startOffset Length; offset
  175.      * @return LaneFactory this lane factory for method chaining
  176.      */
  177.     public LaneFactory setOffsetStart(final Length startOffset)
  178.     {
  179.         this.offsetStart = startOffset;
  180.         return this;
  181.     }

  182.     /**
  183.      * Set end offset.
  184.      * @param endOffset Length; offset
  185.      * @return LaneFactory this lane factory for method chaining
  186.      */
  187.     public LaneFactory setOffsetEnd(final Length endOffset)
  188.     {
  189.         this.offsetEnd = endOffset;
  190.         return this;
  191.     }

  192.     /**
  193.      * Adds a lane pair for each stripe type, where the type determines the right-hand side stripe when building from left to
  194.      * right and vice versa. The left-most stripe is created in {@code leftToRight()}, meaning that each type describes
  195.      * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). This method
  196.      * internally adds {@code SOLID} to create the final continuous stripe.
  197.      * @param types Type...; type per lane pair, for N lanes N-1 should be provided
  198.      * @return this LaneFactory this lane factory for method chaining
  199.      */
  200.     public LaneFactory addLanes(final Type... types)
  201.     {
  202.         return addLanes(new ArrayList<>(), types);
  203.     }

  204.     /**
  205.      * Adds a lane pair for each stripe type, where the type determines the right-hand side stripe when building from left to
  206.      * right and vice versa. The left-most stripe is created in {@code leftToRight()}, meaning that each type describes
  207.      * permeablility between a lane and it's right-hand neighbor, when building left to right (and vice versa). This method
  208.      * internally adds {@code SOLID} to create the final continuous stripe. All generated stripes, including the one generated
  209.      * in leftToRight() or rightToLeft(), is returned in the provided list for custom permeability.
  210.      * @param stripeList List&lt;? super Stripe&gt;; list in to which the generated stripes are placed.
  211.      * @param types Type...; type per lane pair, for N lanes N-1 should be provided
  212.      * @return this LaneFactory this lane factory for method chaining
  213.      */
  214.     public LaneFactory addLanes(final List<? super Stripe> stripeList, final Type... types)
  215.     {
  216.         stripeList.add(this.firstStripe);
  217.         List<Type> typeList = new ArrayList<>(Arrays.asList(types));
  218.         typeList.add(Type.SOLID);
  219.         for (Type type : typeList)
  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, Map.of(this.gtuType, this.speedLimit0), false),
  226.                     "Unexpected exception while building link."));
  227.             this.offset = this.offset.plus(this.laneWidth0);
  228.             Length width = getWidth(type);
  229.             stripeList
  230.                     .add(Try.assign(
  231.                             () -> new Stripe(type, this.link, this.offset.plus(this.offsetStart),
  232.                                     this.offset.plus(this.offsetEnd), width, width, false),
  233.                             "Unexpected exception while building link."));
  234.         }
  235.         return this;
  236.     }

  237.     /**
  238.      * Return width to use for different stripe types.
  239.      * @param type Type; stripe type.
  240.      * @return Length; width.
  241.      */
  242.     private Length getWidth(final Type type)
  243.     {
  244.         switch (type)
  245.         {
  246.             case DASHED:
  247.             case SOLID:
  248.                 return Length.instantiateSI(0.2);
  249.             case LEFT:
  250.             case RIGHT:
  251.             case DOUBLE:
  252.                 return Length.instantiateSI(0.6);
  253.             case BLOCK:
  254.                 return Length.instantiateSI(0.45);
  255.             default:
  256.                 return Length.instantiateSI(0.2);
  257.         }
  258.     }

  259.     /**
  260.      * Adds 1 or 2 shoulders to the current set of lanes.
  261.      * @param width Length; width of the shoulder
  262.      * @param lat LateralDirectionality; side of shoulder, use {@code null} or {@code NONE} for both
  263.      * @return LaneFactory this lane factory for method chaining
  264.      * @throws IllegalStateException if no lanes are defined
  265.      */
  266.     public LaneFactory addShoulder(final Length width, final LateralDirectionality lat)
  267.     {
  268.         Throw.when(this.lanes.isEmpty(), IllegalStateException.class, "Lanes should be defined before adding shoulder(s).");
  269.         if (lat == null || lat.isNone() || lat.isLeft())
  270.         {
  271.             Length startOffset = null;
  272.             Length endOffset = null;
  273.             for (Lane lane : this.lanes)
  274.             {
  275.                 if (startOffset == null
  276.                         || lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().times(0.5)).gt(startOffset))
  277.                 {
  278.                     startOffset = lane.getDesignLineOffsetAtBegin().plus(lane.getBeginWidth().times(0.5));
  279.                 }
  280.                 if (endOffset == null || lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().times(0.5)).gt(endOffset))
  281.                 {
  282.                     endOffset = lane.getDesignLineOffsetAtEnd().plus(lane.getEndWidth().times(0.5));
  283.                 }
  284.             }
  285.             Length start = startOffset.plus(width.times(0.5));
  286.             Length end = endOffset.plus(width.times(0.5));
  287.             Try.assign(() -> new Shoulder(this.link, "Left shoulder", start, end, width, width, false),
  288.                     "Unexpected exception while building link.");
  289.         }
  290.         if (lat == null || lat.isNone() || lat.isRight())
  291.         {
  292.             Length startOffset = null;
  293.             Length endOffset = null;
  294.             for (Lane lane : this.lanes)
  295.             {
  296.                 if (startOffset == null
  297.                         || lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().times(0.5)).lt(startOffset))
  298.                 {
  299.                     startOffset = lane.getDesignLineOffsetAtBegin().minus(lane.getBeginWidth().times(0.5));
  300.                 }
  301.                 if (endOffset == null || lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().times(0.5)).lt(endOffset))
  302.                 {
  303.                     endOffset = lane.getDesignLineOffsetAtEnd().minus(lane.getEndWidth().times(0.5));
  304.                 }
  305.             }
  306.             Length start = startOffset.minus(width.times(0.5));
  307.             Length end = endOffset.minus(width.times(0.5));
  308.             Try.assign(() -> new Shoulder(this.link, "Right shoulder", start, end, width, width, false),
  309.                     "Unexpected exception while building link.");
  310.         }
  311.         return this;
  312.     }

  313.     /**
  314.      * Returns the created lanes in build order.
  315.      * @return List&lt;Lane&gt; created lanes in build order
  316.      */
  317.     public List<Lane> getLanes()
  318.     {
  319.         return this.lanes;
  320.     }

  321.     /**
  322.      * Create a Link along intermediate coordinates from one Node to another.
  323.      * @param network RoadNetwork; the network
  324.      * @param name String; name of the new Link
  325.      * @param from Node; start Node of the new Link
  326.      * @param to Node; end Node of the new Link
  327.      * @param intermediatePoints OtsPoint3d[]; array of intermediate coordinates (may be null); the intermediate points may
  328.      *            contain the coordinates of the from node and to node
  329.      * @param simulator OtsSimulatorInterface; the simulator for this network
  330.      * @return Link; the newly constructed Link
  331.      * @throws OtsGeometryException when the design line is degenerate (only one point or duplicate point)
  332.      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
  333.      *             or the end node of the link are not registered in the network.
  334.      */
  335.     public static CrossSectionLink makeLink(final RoadNetwork network, final String name, final Node from, final Node to,
  336.             final OtsPoint3d[] intermediatePoints, final OtsSimulatorInterface simulator)
  337.             throws OtsGeometryException, NetworkException
  338.     {
  339.         List<OtsPoint3d> pointList =
  340.                 intermediatePoints == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(intermediatePoints));
  341.         if (pointList.size() == 0 || !from.getPoint().equals(pointList.get(0)))
  342.         {
  343.             pointList.add(0, from.getPoint());
  344.         }
  345.         if (pointList.size() == 0 || !to.getPoint().equals(pointList.get(pointList.size() - 1)))
  346.         {
  347.             pointList.add(to.getPoint());
  348.         }

  349.         /*-
  350.         // see if an intermediate point needs to be created to the start of the link in the right direction
  351.         OtsPoint3d s1 = pointList.get(0);
  352.         OtsPoint3d s2 = pointList.get(1);
  353.         double dy = s2.y - s1.y;
  354.         double dx = s2.x - s1.x;
  355.         double a = from.getLocation().getRotZ();
  356.         if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
  357.         {
  358.             double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
  359.             OtsPoint3d extra = new OtsPoint3d(s1.x + r * Math.cos(a), s1.y + r * Math.sin(a), s1.z);
  360.             pointList.add(1, extra);
  361.         }
  362.        
  363.         // see if an intermediate point needs to be created to the end of the link in the right direction
  364.         s1 = pointList.get(pointList.size() - 2);
  365.         s2 = pointList.get(pointList.size() - 1);
  366.         dy = s2.y - s1.y;
  367.         dx = s2.x - s1.x;
  368.         a = to.getLocation().getRotZ() - Math.PI;
  369.         if (Math.abs(a - Math.atan2(dy, dx)) > 1E-6)
  370.         {
  371.             double r = Math.min(1.0, Math.sqrt(dy * dy + dx * dx) / 4.0);
  372.             OtsPoint3d extra = new OtsPoint3d(s2.x + r * Math.cos(a), s2.y + r * Math.sin(a), s2.z);
  373.             pointList.add(pointList.size() - 2, extra);
  374.         }
  375.          */

  376.         OtsLine3d designLine = new OtsLine3d(pointList);
  377.         CrossSectionLink link =
  378.                 new CrossSectionLink(network, name, from, to, DefaultsNl.ROAD, designLine, LaneKeepingPolicy.KEEPRIGHT);
  379.         return link;
  380.     }

  381.     /**
  382.      * Create one Lane.
  383.      * @param link CrossSectionLink; the link that owns the new Lane
  384.      * @param id String; the id of this lane, should be unique within the link
  385.      * @param laneType LaneType; the type of the new Lane
  386.      * @param latPosAtStart Length; the lateral position of the new Lane with respect to the design line of the link at the
  387.      *            start of the link
  388.      * @param latPosAtEnd Length; the lateral position of the new Lane with respect to the design line of the link at the end of
  389.      *            the link
  390.      * @param width Length; the width of the new Lane
  391.      * @param speedLimit Speed; the speed limit on the new Lane
  392.      * @param simulator OtsSimulatorInterface; the simulator
  393.      * @param gtuType GtuType; parent GTU type of relevant GTUs
  394.      * @return Lane
  395.      * @throws NetworkException on network inconsistency
  396.      * @throws OtsGeometryException when creation of center line or contour fails
  397.      */
  398.     @SuppressWarnings("checkstyle:parameternumber")
  399.     private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType,
  400.             final Length latPosAtStart, final Length latPosAtEnd, final Length width, final Speed speedLimit,
  401.             final OtsSimulatorInterface simulator, final GtuType gtuType) throws NetworkException, OtsGeometryException
  402.     {
  403.         Lane result =
  404.                 new Lane(link, id, latPosAtStart, latPosAtEnd, width, width, laneType, Map.of(gtuType, speedLimit), false);
  405.         return result;
  406.     }

  407.     /**
  408.      * Create a simple Lane.
  409.      * @param network RoadNetwork; the network
  410.      * @param name String; name of the Lane (and also of the Link that owns it)
  411.      * @param from Node; starting node of the new Lane
  412.      * @param to Node; ending node of the new Lane
  413.      * @param intermediatePoints OtsPoint3d[]; intermediate coordinates or null to create a straight road; the intermediate
  414.      *            points may contain the coordinates of the from node and to node
  415.      * @param laneType LaneType; type of the new Lane
  416.      * @param speedLimit Speed; the speed limit on the new Lane
  417.      * @param simulator OtsSimulatorInterface; the simulator
  418.      * @param gtuType GtuType; parent GTU type of relevant GTUs
  419.      * @return Lane; the new Lane
  420.      * @throws NetworkException on network inconsistency
  421.      * @throws OtsGeometryException when creation of center line or contour fails
  422.      */
  423.     @SuppressWarnings("checkstyle:parameternumber")
  424.     public static Lane makeLane(final RoadNetwork network, final String name, final Node from, final Node to,
  425.             final OtsPoint3d[] intermediatePoints, final LaneType laneType, final Speed speedLimit,
  426.             final OtsSimulatorInterface simulator, final GtuType gtuType) throws NetworkException, OtsGeometryException
  427.     {
  428.         Length width = new Length(4.0, LengthUnit.METER);
  429.         final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
  430.         Length latPos = new Length(0.0, LengthUnit.METER);
  431.         return makeLane(link, "lane", laneType, latPos, latPos, width, speedLimit, simulator, gtuType);
  432.     }

  433.     /**
  434.      * Create a simple road with the specified number of Lanes.<br>
  435.      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
  436.      * method of the Lane.
  437.      * @param network RoadNetwork; the network
  438.      * @param name String; name of the Link
  439.      * @param from Node; starting node of the new Lane
  440.      * @param to Node; ending node of the new Lane
  441.      * @param intermediatePoints OtsPoint3d[]; intermediate coordinates or null to create a straight road; the intermediate
  442.      *            points may contain the coordinates of the from node and to node
  443.      * @param laneCount int; number of lanes in the road
  444.      * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
  445.      * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
  446.      * @param laneType LaneType; type of the new Lanes
  447.      * @param speedLimit Speed; the speed limit on all lanes
  448.      * @param simulator OtsSimulatorInterface; the simulator
  449.      * @param gtuType GtuType; parent GTU type of relevant GTUs
  450.      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
  451.      * @throws NetworkException on topological problems
  452.      * @throws OtsGeometryException when creation of center line or contour fails
  453.      */
  454.     @SuppressWarnings("checkstyle:parameternumber")
  455.     public static Lane[] makeMultiLane(final RoadNetwork network, final String name, final Node from, final Node to,
  456.             final OtsPoint3d[] intermediatePoints, final int laneCount, final int laneOffsetAtStart, final int laneOffsetAtEnd,
  457.             final LaneType laneType, final Speed speedLimit, final OtsSimulatorInterface simulator, final GtuType gtuType)
  458.             throws NetworkException, OtsGeometryException
  459.     {
  460.         final CrossSectionLink link = makeLink(network, name, from, to, intermediatePoints, simulator);
  461.         Lane[] result = new Lane[laneCount];
  462.         Length width = new Length(4.0, LengthUnit.METER);
  463.         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
  464.         {
  465.             // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
  466.             Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
  467.             Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
  468.             result[laneIndex] = makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit,
  469.                     simulator, gtuType);
  470.         }
  471.         return result;
  472.     }

  473.     /**
  474.      * Create a simple road with the specified number of Lanes.<br>
  475.      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
  476.      * method of the Lane.
  477.      * @param network RoadNetwork; the network
  478.      * @param name String; name of the Link
  479.      * @param from Node; starting node of the new Lane
  480.      * @param to Node; ending node of the new Lane
  481.      * @param intermediatePoints OtsPoint3d[]; intermediate coordinates or null to create a straight road; the intermediate
  482.      *            points may contain the coordinates of the from node and to node
  483.      * @param laneCount int; number of lanes in the road
  484.      * @param laneType LaneType; type of the new Lanes
  485.      * @param speedLimit Speed; Speed the speed limit (applies to all generated lanes)
  486.      * @param simulator OtsSimulatorInterface; the simulator
  487.      * @param gtuType GtuType; parent GTU type of relevant GTUs
  488.      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
  489.      * @throws NamingException when names cannot be registered for animation
  490.      * @throws NetworkException on topological problems
  491.      * @throws OtsGeometryException when creation of center line or contour fails
  492.      */
  493.     @SuppressWarnings("checkstyle:parameternumber")
  494.     public static Lane[] makeMultiLane(final RoadNetwork network, final String name, final Node from, final Node to,
  495.             final OtsPoint3d[] intermediatePoints, final int laneCount, final LaneType laneType, final Speed speedLimit,
  496.             final OtsSimulatorInterface simulator, final GtuType gtuType)
  497.             throws NamingException, NetworkException, OtsGeometryException
  498.     {
  499.         return makeMultiLane(network, name, from, to, intermediatePoints, laneCount, 0, 0, laneType, speedLimit, simulator,
  500.                 gtuType);
  501.     }

  502.     /**
  503.      * Create a simple road with the specified number of Lanes, based on a Bezier curve.<br>
  504.      * This method returns an array of Lane. These lanes are embedded in a Link that can be accessed through the getParentLink
  505.      * method of the Lane.
  506.      * @param network RoadNetwork; the network
  507.      * @param name String; name of the Link
  508.      * @param n1 Node; control node for the start direction
  509.      * @param n2 Node; starting node of the new Lane
  510.      * @param n3 Node; ending node of the new Lane
  511.      * @param n4 Node; control node for the end direction
  512.      * @param laneCount int; number of lanes in the road
  513.      * @param laneOffsetAtStart int; extra offset from design line in lane widths at start of link
  514.      * @param laneOffsetAtEnd int; extra offset from design line in lane widths at end of link
  515.      * @param laneType LaneType; type of the new Lanes
  516.      * @param speedLimit Speed; the speed limit on all lanes
  517.      * @param simulator OtsSimulatorInterface; the simulator
  518.      * @param gtuType GtuType; parent GTU type of relevant GTUs
  519.      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
  520.      * @throws NamingException when names cannot be registered for animation
  521.      * @throws NetworkException on topological problems
  522.      * @throws OtsGeometryException when creation of center line or contour fails
  523.      */
  524.     @SuppressWarnings("checkstyle:parameternumber")
  525.     public static Lane[] makeMultiLaneBezier(final RoadNetwork network, final String name, final Node n1, final Node n2,
  526.             final Node n3, final Node n4, final int laneCount, final int laneOffsetAtStart, final int laneOffsetAtEnd,
  527.             final LaneType laneType, final Speed speedLimit, final OtsSimulatorInterface simulator, final GtuType gtuType)
  528.             throws NamingException, NetworkException, OtsGeometryException
  529.     {
  530.         OtsLine3d bezier = makeBezier(n1, n2, n3, n4);
  531.         final CrossSectionLink link = makeLink(network, name, n2, n3, bezier.getPoints(), simulator);
  532.         Lane[] result = new Lane[laneCount];
  533.         Length width = new Length(4.0, LengthUnit.METER);
  534.         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
  535.         {
  536.             // Be ware! LEFT is lateral positive, RIGHT is lateral negative.
  537.             Length latPosAtStart = new Length((-0.5 - laneIndex - laneOffsetAtStart) * width.getSI(), LengthUnit.SI);
  538.             Length latPosAtEnd = new Length((-0.5 - laneIndex - laneOffsetAtEnd) * width.getSI(), LengthUnit.SI);
  539.             result[laneIndex] = makeLane(link, "lane." + laneIndex, laneType, latPosAtStart, latPosAtEnd, width, speedLimit,
  540.                     simulator, gtuType);
  541.         }
  542.         return result;
  543.     }

  544.     /**
  545.      * @param n1 Node; node 1
  546.      * @param n2 Node; node 2
  547.      * @param n3 Node; node 3
  548.      * @param n4 Node; node 4
  549.      * @return line between n2 and n3 with start-direction n1--&gt;n2 and end-direction n3--&gt;n4
  550.      * @throws OtsGeometryException on failure of Bezier curve creation
  551.      */
  552.     public static OtsLine3d makeBezier(final Node n1, final Node n2, final Node n3, final Node n4) throws OtsGeometryException
  553.     {
  554.         OtsPoint3d p1 = n1.getPoint();
  555.         OtsPoint3d p2 = n2.getPoint();
  556.         OtsPoint3d p3 = n3.getPoint();
  557.         OtsPoint3d p4 = n4.getPoint();
  558.         DirectedPoint dp1 = new DirectedPoint(p2.x, p2.y, p2.z, 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
  559.         DirectedPoint dp2 = new DirectedPoint(p3.x, p3.y, p3.z, 0.0, 0.0, Math.atan2(p4.y - p3.y, p4.x - p3.x));
  560.         return Bezier.cubic(dp1, dp2);
  561.     }
  562. }