CrossSectionLink.java

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

  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.List;

  5. import org.djutils.event.EventType;
  6. import org.djutils.exceptions.Throw;
  7. import org.djutils.exceptions.Try;
  8. import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
  9. import org.opentrafficsim.core.geometry.OTSLine3D;
  10. import org.opentrafficsim.core.geometry.OTSPoint3D;
  11. import org.opentrafficsim.core.network.LinkType;
  12. import org.opentrafficsim.core.network.Network;
  13. import org.opentrafficsim.core.network.NetworkException;
  14. import org.opentrafficsim.core.network.OTSLink;
  15. import org.opentrafficsim.road.network.RoadNetwork;
  16. import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;

  17. import nl.tudelft.simulation.language.d3.DirectedPoint;

  18. /**
  19.  * A CrossSectionLink is a link with lanes where GTUs can possibly switch between lanes.
  20.  * <p>
  21.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  22.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  23.  * <p>
  24.  * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
  25.  * initial version Aug 19, 2014 <br>
  26.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  27.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  28.  * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
  29.  */
  30. public class CrossSectionLink extends OTSLink implements Serializable
  31. {
  32.     /** */
  33.     private static final long serialVersionUID = 20141015L;

  34.     /** List of cross-section elements. */
  35.     private final List<CrossSectionElement> crossSectionElementList = new ArrayList<>();

  36.     /** List of lanes. */
  37.     private final List<Lane> lanes = new ArrayList<>();

  38.     /** The policy to generally keep left, keep right, or keep lane. */
  39.     private final LaneKeepingPolicy laneKeepingPolicy;

  40.     /** Priority. */
  41.     // TODO per GTUDirectionality / LongitudinalDirectionality?
  42.     private Priority priority = Priority.NONE;

  43.     /** Fraction in range 0...1 to divide origin or destination flow over connectors. */
  44.     private Double demandWeight = null;

  45.     /** Line over which GTUs enter or leave the link at the start node. */
  46.     private OTSLine3D startLine;

  47.     /** Line over which GTUs enter or leave the link at the end node. */
  48.     private OTSLine3D endLine;

  49.     /**
  50.      * The (regular, not timed) event type for pub/sub indicating the addition of a Lane to a CrossSectionLink. <br>
  51.      * Payload: Object[] { String networkId, String linkId, String LaneId, Lane lane, int laneNumber } <br>
  52.      * TODO work in a different way with lane numbers to align to standard lane numbering.
  53.      */
  54.     public static final EventType LANE_ADD_EVENT = new EventType("LINK.LANE.ADD");

  55.     /**
  56.      * The (regular, not timed) event type for pub/sub indicating the removal of a Lane from a CrossSectionLink. <br>
  57.      * Payload: Object[] { String networkId, String linkId, String LaneId } <br>
  58.      * TODO allow for the removal of a Lane; currently this is not possible.
  59.      */
  60.     public static final EventType LANE_REMOVE_EVENT = new EventType("LINK.LANE.REMOVE");

  61.     /**
  62.      * Construction of a cross section link.
  63.      * @param network RoadNetwork; the network
  64.      * @param id String; the link id.
  65.      * @param startNode OTSRoadNode; the start node (directional).
  66.      * @param endNode OTSRoadNode; the end node (directional).
  67.      * @param linkType LinkType; the link type
  68.      * @param designLine OTSLine3D; the design line of the Link
  69.      * @param simulator OTSSimulatorInterface; the simulator on which events can be scheduled
  70.      * @param laneKeepingPolicy LaneKeepingPolicy; the policy to generally keep left, keep right, or keep lane
  71.      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
  72.      *             or the end node of the link are not registered in the network.
  73.      */
  74.     @SuppressWarnings("checkstyle:parameternumber")
  75.     public CrossSectionLink(final RoadNetwork network, final String id, final OTSRoadNode startNode, final OTSRoadNode endNode,
  76.             final LinkType linkType, final OTSLine3D designLine, final OTSSimulatorInterface simulator,
  77.             final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
  78.     {
  79.         super(network, id, startNode, endNode, linkType, designLine, simulator);
  80.         this.laneKeepingPolicy = laneKeepingPolicy;
  81.     }

  82.     /**
  83.      * Clone a CrossSectionLink for a new network.
  84.      * @param newNetwork Network; the new network to which the clone belongs
  85.      * @param newSimulator OTSSimulatorInterface; the new simulator for this network
  86.      * @param link CrossSectionLink; the link to clone from
  87.      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
  88.      *             or the end node of the link are not registered in the network.
  89.      */
  90.     protected CrossSectionLink(final RoadNetwork newNetwork, final OTSSimulatorInterface newSimulator,
  91.             final CrossSectionLink link) throws NetworkException
  92.     {
  93.         super(newNetwork, newSimulator, link);
  94.         this.laneKeepingPolicy = link.laneKeepingPolicy;
  95.         for (CrossSectionElement cse : link.crossSectionElementList)
  96.         {
  97.             cse.clone(this, newSimulator);
  98.             // the CrossSectionElement will add itself to the Link (OTS-237)
  99.         }
  100.     }

  101.     /** {@inheritDoc} */
  102.     @Override
  103.     public RoadNetwork getNetwork()
  104.     {
  105.         return (RoadNetwork) super.getNetwork();
  106.     }

  107.     /**
  108.      * Add a cross section element at the end of the list. <br>
  109.      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction.
  110.      * @param cse CrossSectionElement; the cross section element to add.
  111.      */
  112.     protected final void addCrossSectionElement(final CrossSectionElement cse)
  113.     {
  114.         this.crossSectionElementList.add(cse);
  115.         if (cse instanceof Lane)
  116.         {
  117.             this.lanes.add((Lane) cse);
  118.             fireEvent(LANE_ADD_EVENT,
  119.                     new Object[] { getNetwork().getId(), getId(), cse.getId(), (Lane) cse, this.lanes.indexOf(cse) });
  120.         }
  121.     }

  122.     /**
  123.      * Retrieve a safe copy of the cross section element list.
  124.      * @return List&lt;CrossSectionElement&gt;; the cross section element list.
  125.      */
  126.     public final List<CrossSectionElement> getCrossSectionElementList()
  127.     {
  128.         return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
  129.     }

  130.     /**
  131.      * Retrieve the lane keeping policy.
  132.      * @return LaneKeepingPolicy; the lane keeping policy on this CrossSectionLink
  133.      */
  134.     public final LaneKeepingPolicy getLaneKeepingPolicy()
  135.     {
  136.         return this.laneKeepingPolicy;
  137.     }

  138.     /**
  139.      * Find a cross section element with a specified id.
  140.      * @param id String; the id to search for
  141.      * @return CrossSectionElement; the cross section element with the given id, or null if not found
  142.      */
  143.     public final CrossSectionElement getCrossSectionElement(final String id)
  144.     {
  145.         for (CrossSectionElement cse : this.crossSectionElementList)
  146.         {
  147.             if (cse.getId().equals(id))
  148.             {
  149.                 return cse;
  150.             }
  151.         }
  152.         return null;
  153.     }

  154.     /**
  155.      * Return a safe copy of the list of lanes of this CrossSectionLink.
  156.      * @return List&lt;Lane&gt;; the list of lanes.
  157.      */
  158.     public final List<Lane> getLanes()
  159.     {
  160.         return this.lanes == null ? new ArrayList<>() : new ArrayList<>(this.lanes);
  161.     }

  162.     /**
  163.      * @return priority.
  164.      */
  165.     public final Priority getPriority()
  166.     {
  167.         return this.priority;
  168.     }

  169.     /**
  170.      * @param priority Priority; set priority.
  171.      */
  172.     public final void setPriority(final Priority priority)
  173.     {
  174.         this.priority = priority;
  175.     }

  176.     /**
  177.      * Sets the demand weight. This is only applicable to links of type CONNECTOR.
  178.      * @param demandWeight double; demand weight, which is any positive value
  179.      */
  180.     public final void setDemandWeight(final double demandWeight)
  181.     {
  182.         Throw.when(demandWeight < 0.0, IllegalArgumentException.class, "Demand weight should be positive.");
  183.         Throw.when(!getLinkType().isConnector(), IllegalArgumentException.class,
  184.                 "Demand weight can only be set on connectors.");
  185.         this.demandWeight = demandWeight;
  186.     }

  187.     /**
  188.      * Clears the demand weight. This is only applicable to links of type CONNECTOR.
  189.      */
  190.     public final void clearDemandWeight()
  191.     {
  192.         this.demandWeight = null;
  193.     }

  194.     /**
  195.      * Returns the demand weight. This is only applicable to links of type CONNECTOR.
  196.      * @return Double; demand weight, any positive value, or {@code null}
  197.      */
  198.     public final Double getDemandWeight()
  199.     {
  200.         return this.demandWeight;
  201.     }

  202.     /**
  203.      * Returns the line over which GTUs enter and leave the link at the start node.
  204.      * @return OTSLine3D; line over which GTUs enter and leave the link at the start node
  205.      */
  206.     public OTSLine3D getStartLine()
  207.     {
  208.         if (this.startLine == null)
  209.         {
  210.             double left = Double.NaN;
  211.             double right = Double.NaN;
  212.             for (Lane lane : this.lanes)
  213.             {
  214.                 double half = lane.getBeginWidth().si * .5;
  215.                 if (!Double.isNaN(left))
  216.                 {
  217.                     left = Math.max(left, lane.getDesignLineOffsetAtBegin().si + half);
  218.                     right = Math.min(right, lane.getDesignLineOffsetAtBegin().si - half);
  219.                 }
  220.                 else
  221.                 {
  222.                     left = lane.getDesignLineOffsetAtBegin().si + half;
  223.                     right = lane.getDesignLineOffsetAtBegin().si - half;
  224.                 }
  225.             }
  226.             OTSPoint3D start = getStartNode().getPoint();
  227.             double heading = getStartNode().getHeading() + .5 * Math.PI;
  228.             double cosHeading = Math.cos(heading);
  229.             double sinHeading = Math.sin(heading);
  230.             OTSPoint3D leftPoint = new OTSPoint3D(start.x + cosHeading * left, start.y + sinHeading * left);
  231.             OTSPoint3D rightPoint = new OTSPoint3D(start.x - cosHeading * right, start.y - sinHeading * right);
  232.             this.startLine = Try.assign(() -> new OTSLine3D(leftPoint, rightPoint), "Invalid startline on CrossSectionLink.");
  233.         }
  234.         return this.startLine;
  235.     }

  236.     /**
  237.      * Returns the line over which GTUs enter and leave the link at the end node.
  238.      * @return OTSLine3D; line over which GTUs enter and leave the link at the end node
  239.      */
  240.     public OTSLine3D getEndLine()
  241.     {
  242.         if (this.endLine == null)
  243.         {
  244.             double left = Double.NaN;
  245.             double right = Double.NaN;
  246.             for (Lane lane : this.lanes)
  247.             {
  248.                 double half = lane.getEndWidth().si * .5;
  249.                 if (!Double.isNaN(left))
  250.                 {
  251.                     left = Math.max(left, lane.getDesignLineOffsetAtEnd().si + half);
  252.                     right = Math.min(right, lane.getDesignLineOffsetAtEnd().si - half);
  253.                 }
  254.                 else
  255.                 {
  256.                     left = lane.getDesignLineOffsetAtEnd().si + half;
  257.                     right = lane.getDesignLineOffsetAtEnd().si - half;
  258.                 }
  259.             }
  260.             OTSPoint3D start = getEndNode().getPoint();
  261.             DirectedPoint p = Try.assign(() -> getEndNode().getLocation(), "Unexpected remote exception.");
  262.             double heading = p.getRotZ() + .5 * Math.PI;
  263.             double cosHeading = Math.cos(heading);
  264.             double sinHeading = Math.sin(heading);
  265.             OTSPoint3D leftPoint = new OTSPoint3D(start.x + cosHeading * left, start.y + sinHeading * left);
  266.             OTSPoint3D rightPoint = new OTSPoint3D(start.x + cosHeading * right, start.y + sinHeading * right);
  267.             this.endLine = Try.assign(() -> new OTSLine3D(leftPoint, rightPoint), "Invalid endline on CrossSectionLink.");
  268.         }
  269.         return this.endLine;
  270.     }

  271.     /** {@inheritDoc} */
  272.     @Override
  273.     public final String toString()
  274.     {
  275.         return "CrossSectionLink [name=" + this.getId() + ", nodes=" + getStartNode().getId() + "-" + getEndNode().getId()
  276.                 + ", crossSectionElementList=" + this.crossSectionElementList + ", lanes=" + this.lanes + ", laneKeepingPolicy="
  277.                 + this.laneKeepingPolicy + "]";
  278.     }

  279.     /** {@inheritDoc} */
  280.     @Override
  281.     @SuppressWarnings("checkstyle:designforextension")
  282.     public CrossSectionLink clone(final Network newNetwork, final OTSSimulatorInterface newSimulator) throws NetworkException
  283.     {
  284.         Throw.when(!(newNetwork instanceof RoadNetwork), NetworkException.class,
  285.                 "CrossSectionLink.clone. newNetwork not of the type Roadnetwork");
  286.         return new CrossSectionLink((RoadNetwork) newNetwork, newSimulator, this);
  287.     }

  288.     /**
  289.      * Priority of a link.
  290.      * <p>
  291.      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  292.      * <br>
  293.      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  294.      * <p>
  295.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 dec. 2016 <br>
  296.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  297.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  298.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  299.      */
  300.     public enum Priority
  301.     {
  302.         /** Traffic has priority. */
  303.         PRIORITY,

  304.         /** No priority. */
  305.         NONE,

  306.         /** Turn on red. */
  307.         TURN_ON_RED,

  308.         /** Yield. */
  309.         YIELD,

  310.         /** Need to stop. */
  311.         STOP,

  312.         /** Priority according to all-stop rules. */
  313.         ALL_STOP,

  314.         /** Priority at bus stop, i.e. bus has right of way if it wants to leave the bus stop. */
  315.         BUS_STOP;

  316.         /**
  317.          * Returns whether this is priority.
  318.          * @return whether this is priority
  319.          */
  320.         public boolean isPriority()
  321.         {
  322.             return this.equals(PRIORITY);
  323.         }

  324.         /**
  325.          * Returns whether this is none.
  326.          * @return whether this is none
  327.          */
  328.         public boolean isNone()
  329.         {
  330.             return this.equals(NONE);
  331.         }

  332.         /**
  333.          * Returns whether this is turn on red.
  334.          * @return whether this is turn on red
  335.          */
  336.         public boolean isTurnOnRed()
  337.         {
  338.             return this.equals(TURN_ON_RED);
  339.         }

  340.         /**
  341.          * Returns whether this is yield.
  342.          * @return whether this is yield
  343.          */
  344.         public boolean isYield()
  345.         {
  346.             return this.equals(YIELD);
  347.         }

  348.         /**
  349.          * Returns whether this is stop.
  350.          * @return whether this is stop
  351.          */
  352.         public boolean isStop()
  353.         {
  354.             return this.equals(STOP);
  355.         }

  356.         /**
  357.          * Returns whether this is all-stop.
  358.          * @return whether this is all-stop
  359.          */
  360.         public boolean isAllStop()
  361.         {
  362.             return this.equals(ALL_STOP);
  363.         }

  364.         /**
  365.          * Returns whether this is bus stop.
  366.          * @return whether this is bus stop
  367.          */
  368.         public boolean isBusStop()
  369.         {
  370.             return this.equals(BUS_STOP);
  371.         }

  372.     }

  373. }