GeneratorPositions.java

  1. package org.opentrafficsim.road.gtu.generator;

  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.Comparator;
  5. import java.util.LinkedHashMap;
  6. import java.util.LinkedHashSet;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Objects;
  10. import java.util.Set;

  11. import org.djunits.unit.SpeedUnit;
  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.opentrafficsim.core.gtu.GtuException;
  16. import org.opentrafficsim.core.gtu.GtuType;
  17. import org.opentrafficsim.core.math.Draw;
  18. import org.opentrafficsim.core.network.Link;
  19. import org.opentrafficsim.core.network.NetworkException;
  20. import org.opentrafficsim.core.network.Node;
  21. import org.opentrafficsim.core.network.route.Route;
  22. import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.BySpeed;
  23. import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.ByValue;
  24. import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
  25. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  26. import org.opentrafficsim.road.network.lane.Lane;
  27. import org.opentrafficsim.road.network.lane.LanePosition;

  28. import nl.tudelft.simulation.jstats.streams.StreamInterface;

  29. /**
  30.  * Helper class for vehicle generation which can draw the next GTU position to try to place a GTU. If the GTU can not be placed,
  31.  * it should be included in a queue. This class requires the number of unplaced GTU's per lane, in order to appropriately divide
  32.  * traffic over the lanes.
  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://github.com/averbraeck">Alexander Verbraeck</a>
  38.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  39.  * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  40.  */
  41. public interface GeneratorPositions
  42. {

  43.     /**
  44.      * Draw a new position to generate a GTU.
  45.      * @param gtuType GtuType; GTU type.
  46.      * @param characteristics LaneBasedGtuCharacteristics; characteristics of the generated GTU.
  47.      * @param unplaced Map&lt;CrossSectionLink, Map&lt;Integer, Integer&gt;&gt;; number of unplaced GTUs per lane, counting from
  48.      *            the right and starting at 1.
  49.      * @return GeneratorLanePosition; new position to generate a GTU.
  50.      * @throws GtuException when the underlying structure is inconsistent for drawing
  51.      */
  52.     GeneratorLanePosition draw(GtuType gtuType, LaneBasedGtuCharacteristics characteristics,
  53.             Map<CrossSectionLink, Map<Integer, Integer>> unplaced) throws GtuException;

  54.     /**
  55.      * Returns all underlying positions.
  56.      * @return all underlying positions.
  57.      */
  58.     Set<GeneratorLanePosition> getAllPositions();

  59.     /**
  60.      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Lanes are drawn
  61.      * without bias. Each link receives a weight equal to the number of lanes.
  62.      * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
  63.      * @param stream StreamInterface; stream for random numbers
  64.      * @return GeneratorPositions; object to draw positions from
  65.      */
  66.     static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream)
  67.     {
  68.         return create(positions, stream, null, null, null);
  69.     }

  70.     /**
  71.      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Each link receives a
  72.      * weight equal to the number of lanes.
  73.      * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
  74.      * @param stream StreamInterface; stream for random numbers
  75.      * @param biases LaneBiases; lane biases for GTU types
  76.      * @return GeneratorPositions; object to draw positions from
  77.      */
  78.     static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream, final LaneBiases biases)
  79.     {
  80.         return create(positions, stream, biases, null, null);
  81.     }

  82.     /**
  83.      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Lanes are drawn
  84.      * without bias.
  85.      * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
  86.      * @param stream StreamInterface; stream for random numbers
  87.      * @param linkWeights Map&lt;CrossSectionLink, Double&gt;; weight per link direction
  88.      * @param viaNodes Map&lt;CrossSectionLink, Node&gt;; nodes connectors feed to for each link where GTU's will be generated
  89.      * @return GeneratorPositions; object to draw positions from
  90.      */
  91.     static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream,
  92.             final Map<CrossSectionLink, Double> linkWeights, final Map<CrossSectionLink, Node> viaNodes)
  93.     {
  94.         return create(positions, stream, null, linkWeights, viaNodes);
  95.     }

  96.     /**
  97.      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link.
  98.      * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
  99.      * @param stream StreamInterface; stream for random numbers
  100.      * @param laneBiases LaneBiases; lane biases for GTU types
  101.      * @param linkWeights Map&lt;CrossSectionLink, Double&gt;; weight per link
  102.      * @param viaNodes Map&lt;CrossSectionLink, Node&gt;; nodes connectors feed to for each link where GTU's will be generated
  103.      * @return GeneratorPositions; object to draw positions from
  104.      */
  105.     static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream,
  106.             final LaneBiases laneBiases, final Map<CrossSectionLink, Double> linkWeights,
  107.             final Map<CrossSectionLink, Node> viaNodes)
  108.     {

  109.         // group directions per link
  110.         Map<Link, Set<LanePosition>> linkSplit = new LinkedHashMap<>();
  111.         for (LanePosition position : positions)
  112.         {
  113.             linkSplit.computeIfAbsent(position.getLane().getParentLink(), (link) -> new LinkedHashSet<>()).add(position);
  114.         }

  115.         // create list of GeneratorLinkPositions
  116.         List<GeneratorLinkPosition> linkPositions = new ArrayList<>();
  117.         Set<GeneratorLanePosition> allLanePositions = new LinkedHashSet<>();
  118.         for (Link splitLink : linkSplit.keySet())
  119.         {
  120.             List<Lane> lanes = ((CrossSectionLink) splitLink).getLanes();
  121.             // let's sort the lanes by lateral position
  122.             Collections.sort(lanes, new Comparator<Lane>()
  123.             {
  124.                 /** {@inheritDoc} */
  125.                 @Override
  126.                 public int compare(final Lane lane1, final Lane lane2)
  127.                 {
  128.                     Length lat1 = lane1.getDesignLineOffsetAtBegin();
  129.                     Length lat2 = lane2.getDesignLineOffsetAtBegin();
  130.                     return lat1.compareTo(lat2);
  131.                 }
  132.             });
  133.             // create list of GeneratorLanePositions
  134.             List<GeneratorLanePosition> lanePositions = new ArrayList<>();
  135.             for (LanePosition lanePosition : linkSplit.get(splitLink))
  136.             {
  137.                 Set<LanePosition> set = new LinkedHashSet<>();
  138.                 set.add(lanePosition);
  139.                 lanePositions.add(new GeneratorLanePosition(lanes.indexOf(lanePosition.getLane()) + 1, set,
  140.                         (CrossSectionLink) splitLink));
  141.             }
  142.             allLanePositions.addAll(lanePositions);
  143.             // create the GeneratorLinkPosition
  144.             if (linkWeights == null)
  145.             {
  146.                 linkPositions.add(new GeneratorLinkPosition(lanePositions, splitLink, stream, laneBiases));
  147.             }
  148.             else
  149.             {
  150.                 Double weight = linkWeights.get(splitLink);
  151.                 Throw.whenNull(weight, "Using link weights for GTU generation, but no weight for link %s is defined.",
  152.                         splitLink);
  153.                 linkPositions.add(new GeneratorLinkPosition(lanePositions, splitLink, stream, laneBiases, weight,
  154.                         viaNodes.get(splitLink)));
  155.             }
  156.         }

  157.         // create the GeneratorZonePosition
  158.         GeneratorZonePosition position = new GeneratorZonePosition(linkPositions);
  159.         return new GeneratorPositions()
  160.         {
  161.             /** {@inheritDoc} */
  162.             @Override
  163.             public GeneratorLanePosition draw(final GtuType gtuType, final LaneBasedGtuCharacteristics characteristics,
  164.                     final Map<CrossSectionLink, Map<Integer, Integer>> unplaced) throws GtuException
  165.             {
  166.                 GeneratorLinkPosition linkPosition =
  167.                         position.draw(gtuType, stream, characteristics.getDestination(), characteristics.getRoute());
  168.                 Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
  169.                         linkPosition.speedLimit(gtuType), characteristics.getMaximumSpeed());
  170.                 return linkPosition.draw(gtuType, unplaced.get(linkPosition.getLink()), desiredSpeed);
  171.             }

  172.             /** {@inheritDoc} */
  173.             @Override
  174.             public Set<GeneratorLanePosition> getAllPositions()
  175.             {
  176.                 return allLanePositions;
  177.             }
  178.         };
  179.     }

  180.     /**
  181.      * Class representing a vehicle generation lane, providing elementary information for randomly drawing links and lanes.
  182.      * <p>
  183.      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  184.      * <br>
  185.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  186.      * </p>
  187.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  188.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  189.      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  190.      */
  191.     final class GeneratorLanePosition
  192.     {

  193.         /** Lane number, where 1 is the right-most lane. */
  194.         private final int laneNumber;

  195.         /** Position set, representing a single GTU position on the network. */
  196.         private final Set<LanePosition> position;

  197.         /** Link. */
  198.         private final CrossSectionLink link;

  199.         /**
  200.          * Constructor.
  201.          * @param laneNumber int; lane number, where 1 is the right-most lane
  202.          * @param position Set&lt;LanePosition&gt;; position set, representing a single GTU position on the network
  203.          * @param link CrossSectionLink; link
  204.          */
  205.         GeneratorLanePosition(final int laneNumber, final Set<LanePosition> position, final CrossSectionLink link)
  206.         {
  207.             this.laneNumber = laneNumber;
  208.             this.position = position;
  209.             this.link = link;
  210.         }

  211.         /**
  212.          * Returns the lane number, where 1 is the right-most lane.
  213.          * @return lane number, where 1 is the right-most lane
  214.          */
  215.         int getLaneNumber()
  216.         {
  217.             return this.laneNumber;
  218.         }

  219.         /**
  220.          * Returns whether this lane is accessible to the GTU type.
  221.          * @param gtuType GtuType; gtu type
  222.          * @return boolean; whether this lane is accessible to the GTU type
  223.          */
  224.         boolean allows(final GtuType gtuType)
  225.         {
  226.             for (LanePosition pos : this.position)
  227.             {
  228.                 if (pos.getLane().getType().isCompatible(gtuType))
  229.                 {
  230.                     return true;
  231.                 }
  232.             }
  233.             return false;
  234.         }

  235.         /**
  236.          * Returns the contained position set, representing a single GTU position on the network.
  237.          * @return Set&lt;LanePosition&gt;; contained position set, representing a single GTU position on the network
  238.          */
  239.         Set<LanePosition> getPosition()
  240.         {
  241.             return this.position;
  242.         }

  243.         /**
  244.          * Returns the link.
  245.          * @return CrossSectionLink; link
  246.          */
  247.         CrossSectionLink getLink()
  248.         {
  249.             return this.link;
  250.         }

  251.         /** {@inheritDoc} */
  252.         @Override
  253.         public int hashCode()
  254.         {
  255.             return Objects.hash(this.laneNumber, this.link, this.position);
  256.         }

  257.         /** {@inheritDoc} */
  258.         @Override
  259.         public boolean equals(final Object obj)
  260.         {
  261.             if (this == obj)
  262.             {
  263.                 return true;
  264.             }
  265.             if (obj == null)
  266.             {
  267.                 return false;
  268.             }
  269.             if (getClass() != obj.getClass())
  270.             {
  271.                 return false;
  272.             }
  273.             GeneratorLanePosition other = (GeneratorLanePosition) obj;
  274.             return this.laneNumber == other.laneNumber && Objects.equals(this.link, other.link)
  275.                     && Objects.equals(this.position, other.position);
  276.         }

  277.         /** {@inheritDoc} */
  278.         @Override
  279.         public String toString()
  280.         {
  281.             return "GeneratorLanePosition [laneNumber=" + this.laneNumber + ", position=" + this.position + ", link="
  282.                     + this.link + "]";
  283.         }

  284.     }

  285.     /**
  286.      * Class representing a vehicle generation link to provide individual generation positions.
  287.      * <p>
  288.      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  289.      * <br>
  290.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  291.      * </p>
  292.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  293.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  294.      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  295.      */
  296.     final class GeneratorLinkPosition
  297.     {

  298.         /** Contained lanes. */
  299.         private final List<GeneratorLanePosition> positions;

  300.         /** The link. */
  301.         private final Link link;

  302.         /** Random stream. */
  303.         private final StreamInterface stream;

  304.         /** Lane bias. */
  305.         private final LaneBiases laneBiases;

  306.         /** Weight for drawing this link. */
  307.         private final double weight;

  308.         /** Node by which a connector connects, may be {@code null}. */
  309.         private final Node viaNode;

  310.         /**
  311.          * Constructor.
  312.          * @param positions List&lt;GeneratorLanePosition&gt;; contained lanes
  313.          * @param link Link; the link
  314.          * @param stream StreamInterface; stream
  315.          * @param laneBiases LaneBiases; lane biases
  316.          */
  317.         GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final Link link, final StreamInterface stream,
  318.                 final LaneBiases laneBiases)
  319.         {
  320.             this.positions = positions;
  321.             this.link = link;
  322.             this.stream = stream;
  323.             this.laneBiases = laneBiases;
  324.             this.weight = -1;
  325.             this.viaNode = null;
  326.         }

  327.         /**
  328.          * Constructor.
  329.          * @param positions List&lt;GeneratorLanePosition&gt;; contained lanes
  330.          * @param link Link; the link
  331.          * @param stream StreamInterface; stream
  332.          * @param laneBiases LaneBiases; lane biases
  333.          * @param weight double; weight for drawing this link
  334.          * @param viaNode Node; node by which a connector connects
  335.          */
  336.         GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final Link link, final StreamInterface stream,
  337.                 final LaneBiases laneBiases, final double weight, final Node viaNode)
  338.         {
  339.             this.positions = positions;
  340.             this.link = link;
  341.             this.stream = stream;
  342.             this.laneBiases = laneBiases;
  343.             this.weight = weight;
  344.             this.viaNode = viaNode;
  345.         }

  346.         /**
  347.          * Return the link.
  348.          * @return CrossSectionLink; link
  349.          */
  350.         Link getLink()
  351.         {
  352.             return this.link;
  353.         }

  354.         /**
  355.          * Returns the weight for this link. This is either a predefined weight, or the number of lanes for the GTU type.
  356.          * @param gtuType GtuType; GTU type
  357.          * @return double; weight for this link
  358.          */
  359.         double getWeight(final GtuType gtuType)
  360.         {
  361.             if (this.weight < 0.0)
  362.             {
  363.                 return getNumberOfLanes(gtuType);
  364.             }
  365.             return this.weight;
  366.         }

  367.         /**
  368.          * Returns the node by which a connector connects.
  369.          * @return the node by which a connector connects
  370.          */
  371.         Node getViaNode()
  372.         {
  373.             return this.viaNode;
  374.         }

  375.         /**
  376.          * Returns the number of accessible lanes for the GTU type.
  377.          * @param gtuType GtuType; GTU type
  378.          * @return int; number of accessible lanes for the GTU type
  379.          */
  380.         int getNumberOfLanes(final GtuType gtuType)
  381.         {
  382.             int numberOfLanes = 0;
  383.             for (GeneratorLanePosition lanePosition : this.positions)
  384.             {
  385.                 if (lanePosition.allows(gtuType))
  386.                 {
  387.                     numberOfLanes++;
  388.                 }
  389.             }
  390.             return numberOfLanes;
  391.         }

  392.         /**
  393.          * Draws a specific GeneratorLanePosition utilizing lane biases of GTU types.
  394.          * @param gtuType GtuType; GTU type
  395.          * @param unplaced Map&lt;Integer, Integer&gt;; number of unplaced GTUs per lane. The lane number should match with
  396.          *            {@code GeneratorLanePosition.getLaneNumber()}, where 1 is the right-most lane. Missing lanes are assumed
  397.          *            to have no queue.
  398.          * @param desiredSpeed Speed; desired speed, possibly used to determine the biased road position
  399.          * @return GeneratorLanePosition; specific GeneratorLanePosition utilizing lane biases of GTU types
  400.          */
  401.         GeneratorLanePosition draw(final GtuType gtuType, final Map<Integer, Integer> unplaced, final Speed desiredSpeed)
  402.         {
  403.             Map<GeneratorLanePosition, Double> map = new LinkedHashMap<>();
  404.             for (int i = 0; i < this.positions.size(); i++)
  405.             {
  406.                 GeneratorLanePosition lanePosition = this.positions.get(i);
  407.                 if (lanePosition.allows(gtuType))
  408.                 {
  409.                     GtuType type = gtuType;
  410.                     boolean found = false;
  411.                     while (this.laneBiases != null && !found && type != null)
  412.                     {
  413.                         if (this.laneBiases.contains(type))
  414.                         {
  415.                             found = true;
  416.                             int laneNum = lanePosition.getLaneNumber();
  417.                             int unplacedTemplates = unplaced == null ? 0 : unplaced.getOrDefault(laneNum, 0);
  418.                             double w = this.laneBiases.getBias(type).calculateWeight(laneNum, getNumberOfLanes(gtuType),
  419.                                     unplacedTemplates, desiredSpeed);
  420.                             map.put(lanePosition, w);
  421.                         }
  422.                         type = type.getParent();
  423.                     }
  424.                     if (!found)
  425.                     {
  426.                         map.put(lanePosition, 1.0);
  427.                     }
  428.                 }
  429.             }
  430.             if (0 == map.size())
  431.             {
  432.                 System.err.println("This really, really can't work...");
  433.             }
  434.             return Draw.drawWeighted(map, this.stream);
  435.         }

  436.         /** {@inheritDoc} */
  437.         @Override
  438.         public String toString()
  439.         {
  440.             return "GeneratorLinkPosition [positions=" + this.positions + "]";
  441.         }

  442.         /**
  443.          * @param gtuType GtuType; GTU type
  444.          * @return Speed; speed limit
  445.          */
  446.         public Speed speedLimit(final GtuType gtuType)
  447.         {
  448.             Speed speedLimit = null;
  449.             for (GeneratorLanePosition pos : this.positions)
  450.             {
  451.                 for (LanePosition lane : pos.getPosition())
  452.                 {
  453.                     try
  454.                     {
  455.                         Speed limit = lane.getLane().getSpeedLimit(gtuType);
  456.                         if (speedLimit == null || limit.lt(speedLimit))
  457.                         {
  458.                             speedLimit = limit;
  459.                         }
  460.                     }
  461.                     catch (NetworkException exception)
  462.                     {
  463.                         // ignore
  464.                     }
  465.                 }
  466.             }
  467.             Throw.when(speedLimit == null, IllegalStateException.class, "No speed limit could be determined for GtuType %s.",
  468.                     gtuType);
  469.             return speedLimit;
  470.         }

  471.     }

  472.     /**
  473.      * Class representing a vehicle generation zone to provide individual generation positions.
  474.      * <p>
  475.      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  476.      * <br>
  477.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  478.      * </p>
  479.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  480.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  481.      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  482.      */
  483.     final class GeneratorZonePosition
  484.     {

  485.         /** Contained links. */
  486.         private final List<GeneratorLinkPosition> positions;

  487.         /**
  488.          * Constructor.
  489.          * @param positions List&lt;GeneratorLinkPosition&gt;; contained links
  490.          */
  491.         GeneratorZonePosition(final List<GeneratorLinkPosition> positions)
  492.         {
  493.             this.positions = positions;
  494.         }

  495.         /**
  496.          * Draws a GeneratorLinkPosition using number of accessible lanes for the GtuType as weight, and a GeneratorLanePosition
  497.          * from that.
  498.          * @param gtuType GtuType; GTU type
  499.          * @param stream StreamInterface; stream for random numbers
  500.          * @param destination Node; destination node
  501.          * @param route Route; route, may be {@code null}
  502.          * @return GeneratorLanePosition; draws a LinkPosition using number of accessible lanes for the GtuType as weight, and a
  503.          *         GeneratorLanePosition from that
  504.          */
  505.         GeneratorLinkPosition draw(final GtuType gtuType, final StreamInterface stream, final Node destination,
  506.                 final Route route)
  507.         {
  508.             Map<GeneratorLinkPosition, Double> map = new LinkedHashMap<>();
  509.             for (int i = 0; i < this.positions.size(); i++)
  510.             {
  511.                 GeneratorLinkPosition glp = this.positions.get(i);
  512.                 Link link = glp.getLink();
  513.                 if (route != null)
  514.                 {
  515.                     int from = route.indexOf(link.getStartNode());
  516.                     int to = route.indexOf(link.getEndNode());
  517.                     if (from > -1 && to > -1 && to - from == 1)
  518.                     {
  519.                         map.put(glp, glp.getWeight(gtuType));
  520.                     }
  521.                 }
  522.                 else
  523.                 {
  524.                     // let's check whether any route is possible over this link
  525.                     if (glp.getViaNode() != null)
  526.                     {
  527.                         Route r;
  528.                         try
  529.                         {
  530.                             r = glp.getViaNode().getNetwork().getShortestRouteBetween(gtuType, glp.getViaNode(), destination);
  531.                         }
  532.                         catch (NetworkException exception)
  533.                         {
  534.                             r = null;
  535.                         }
  536.                         if (r != null)
  537.                         {
  538.                             map.put(glp, glp.getWeight(gtuType));
  539.                         }
  540.                     }
  541.                     else
  542.                     {
  543.                         map.put(glp, glp.getWeight(gtuType));
  544.                     }
  545.                 }
  546.             }
  547.             return Draw.drawWeighted(map, stream);
  548.         }

  549.         /** {@inheritDoc} */
  550.         @Override
  551.         public String toString()
  552.         {
  553.             return "GeneratorZonePosition [positions=" + this.positions + "]";
  554.         }

  555.     }

  556.     /**
  557.      * Set of lane biases per GTU type.
  558.      * <p>
  559.      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  560.      * <br>
  561.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  562.      * </p>
  563.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  564.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  565.      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  566.      */
  567.     final class LaneBiases
  568.     {

  569.         /** Biases per GTU type. */
  570.         private final Map<GtuType, LaneBias> biases = new LinkedHashMap<>();

  571.         /**
  572.          * Adds a GTU bias for randomly drawing a lane.
  573.          * @param gtuType GtuType; gtu type
  574.          * @param bias LaneBias; bias
  575.          * @return LaneBiases; lane biases for method chaining
  576.          */
  577.         public LaneBiases addBias(final GtuType gtuType, final LaneBias bias)
  578.         {
  579.             Throw.whenNull(gtuType, "GTU type may not be null.");
  580.             Throw.whenNull(bias, "Bias may not be null.");
  581.             this.biases.put(gtuType, bias);
  582.             return this;
  583.         }

  584.         /**
  585.          * Whether a bias is defined for the given type.
  586.          * @param gtuType GtuType; GTU type
  587.          * @return whether a bias is defined for the given type
  588.          */
  589.         public boolean contains(final GtuType gtuType)
  590.         {
  591.             return this.biases.containsKey(gtuType);
  592.         }

  593.         /**
  594.          * Returns the bias of given GTU type, or {@code Bias.None} if none defined for the GTU type.
  595.          * @param gtuType GtuType; GTU type
  596.          * @return Bias; bias of the GTU type
  597.          */
  598.         public LaneBias getBias(final GtuType gtuType)
  599.         {
  600.             return this.biases.getOrDefault(gtuType, LaneBias.NONE);
  601.         }

  602.         /** {@inheritDoc} */
  603.         @Override
  604.         public String toString()
  605.         {
  606.             return "LaneBiases [" + this.biases + "]";
  607.         }

  608.     }

  609.     /**
  610.      * Vehicle generation lateral bias. Includes a lane maximum, e.g. trucks only on 2 right-hand lanes.
  611.      * <p>
  612.      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  613.      * <br>
  614.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  615.      * </p>
  616.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  617.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  618.      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  619.      */
  620.     final class LaneBias
  621.     {

  622.         /** No bias. */
  623.         public static final LaneBias NONE = new LaneBias(new ByValue(0.0), 0.0, Integer.MAX_VALUE);

  624.         /** Weak left-hand bias, 2nd left lane contains 50% relative to left most lane, in free traffic. */
  625.         public static final LaneBias WEAK_LEFT = new LaneBias(new ByValue(1.0), 1.0, Integer.MAX_VALUE);

  626.         /** Left-hand bias, 2nd left lane contains 25% relative to left most lane, in free traffic. */
  627.         public static final LaneBias LEFT = new LaneBias(new ByValue(1.0), 2.0, Integer.MAX_VALUE);

  628.         /** Strong left-hand bias, 2nd left lane contains 3.125% relative to left most lane, in free traffic. */
  629.         public static final LaneBias STRONG_LEFT = new LaneBias(new ByValue(1.0), 5.0, Integer.MAX_VALUE);

  630.         /** Weak middle bias, 2nd left lane contains 50% relative to left most lane, in free traffic. */
  631.         public static final LaneBias WEAK_MIDDLE = new LaneBias(new ByValue(0.5), 1.0, Integer.MAX_VALUE);

  632.         /** Middle bias, 2nd left lane contains 25% relative to left most lane, in free traffic. */
  633.         public static final LaneBias MIDDLE = new LaneBias(new ByValue(0.5), 2.0, Integer.MAX_VALUE);

  634.         /** Strong middle bias, 2nd left lane contains 3.125% relative to left most lane, in free traffic. */
  635.         public static final LaneBias STRONG_MIDDLE = new LaneBias(new ByValue(0.5), 5.0, Integer.MAX_VALUE);

  636.         /** Weak right-hand bias, 2nd right lane contains 50% relative to right most lane, in free traffic. */
  637.         public static final LaneBias WEAK_RIGHT = new LaneBias(new ByValue(0.0), 1.0, Integer.MAX_VALUE);

  638.         /** Right-hand bias, 2nd right lane contains 25% relative to right most lane, in free traffic. */
  639.         public static final LaneBias RIGHT = new LaneBias(new ByValue(0.0), 2.0, Integer.MAX_VALUE);

  640.         /** Strong right-hand bias, 2nd right lane contains 3.125% relative to right most lane, in free traffic. */
  641.         public static final LaneBias STRONG_RIGHT = new LaneBias(new ByValue(0.0), 5.0, Integer.MAX_VALUE);

  642.         /** Strong right-hand bias, limited to a maximum of 2 lanes. */
  643.         public static final LaneBias TRUCK_RIGHT = new LaneBias(new ByValue(0.0), 5.0, 2);

  644.         /**
  645.          * Returns a bias by speed with normal extent.
  646.          * @param leftSpeed Speed; desired speed for full left bias
  647.          * @param rightSpeed Speed; desired speed for full right bias
  648.          * @return bias by speed with normal extent
  649.          */
  650.         public static LaneBias bySpeed(final Speed leftSpeed, final Speed rightSpeed)
  651.         {
  652.             return new LaneBias(new BySpeed(leftSpeed, rightSpeed), 2.0, Integer.MAX_VALUE);
  653.         }

  654.         /**
  655.          * Returns a bias by speed with normal extent. Convenience km/h input.
  656.          * @param leftSpeedKm double; desired speed for full left bias
  657.          * @param rightSpeedKm double; desired speed for full right bias
  658.          * @return bias by speed with normal extent
  659.          */
  660.         public static LaneBias bySpeed(final double leftSpeedKm, final double rightSpeedKm)
  661.         {
  662.             return bySpeed(new Speed(leftSpeedKm, SpeedUnit.KM_PER_HOUR), new Speed(rightSpeedKm, SpeedUnit.KM_PER_HOUR));
  663.         }

  664.         /** Provider of position on the road (0 = full left, 1 = full right). */
  665.         private final RoadPosition roadPosition;

  666.         /** Bias extent. */
  667.         private final double bias;

  668.         /** Number of lanes to consider in either direction, including the preferred lane. */
  669.         private final double stickyLanes;

  670.         /**
  671.          * Constructor.
  672.          * @param roadPosition RoadPosition; lateral position on the road (0 = right, 0.5 = middle, 1 = left)
  673.          * @param bias double; bias extent, lower values create more spread traffic, 0.0 causes no lane preference
  674.          * @param stickyLanes double; number of lanes to consider in either direction, including the preferred lane
  675.          */
  676.         public LaneBias(final RoadPosition roadPosition, final double bias, final double stickyLanes)
  677.         {
  678.             Throw.when(bias < 0.0, IllegalArgumentException.class, "Bias should be positive or 0.");
  679.             Throw.when(stickyLanes < 1.0, IllegalArgumentException.class, "Sticky lanes should be 1.0 or larger.");
  680.             this.roadPosition = roadPosition;
  681.             this.bias = bias;
  682.             this.stickyLanes = stickyLanes;
  683.         }

  684.         /**
  685.          * Returns a random draw weight for given lane. The weight is calculated as:
  686.          *
  687.          * <pre>
  688.          * weight = { 0,                               d &gt;= number of sticky lanes
  689.          *          { 1 / ((d + 1)^bias * (m + 1)),    otherwise
  690.          *
  691.          * where,
  692.          *      d:      lane deviation from lateral bias position
  693.          *      bias:   bias extent
  694.          *      m:      number of unplaced GTU's
  695.          * </pre>
  696.          *
  697.          * The formula makes sure that all lanes have equal weight for <i>bias</i> &#61; 0, given an equal number of unplaced
  698.          * GTU's <i>m</i>. The bias can be seen to result in this: for each GTU on the 2nd lane, there are 2^(<i>bias</i> - 1)
  699.          * GTU's on the 1st lane. In numbers: 1 vs. 1 for <i>bias</i> &#61; 0, 1 vs. 2 for <i>bias</i> &#61; 1, 1 vs. 4 for
  700.          * <i>bias</i> &#61; 2, 1 vs. 8 for <i>bias</i> &#61; 3, etc.<br>
  701.          * <br>
  702.          * Division by <i>m</i> + 1 makes sure traffic distributes over the lanes in case of spillback, or otherwise too high
  703.          * demand on a particular lane. The weight for lanes with more unplaced GTU's simply reduces. This effect balances out
  704.          * with the bias, meaning that for a strong bias, GTU's are still likely to be generated on the biased lanes. Given a
  705.          * relatively strong bias of <i>bias</i> &#61; 5, the weight for the 1st and 2nd lane becomes equal if the 2nd lane has
  706.          * no unplaced GTU's, while the 1st lane has 31 unplaced GTU's.<br>
  707.          * <br>
  708.          * Lane deviation <i>d</i> is calculated as <i>d</i> &#61; abs(<i>latBiasLane</i> - <i>laneNumFromRight</i>). Here,
  709.          * <i>latBiasLane</i> &#61; 1 + <i>roadPosition</i>*(<i>numberOfLanes</i> - 1), i.e. ranging from 1 to 4 on a 4-lane
  710.          * road. For lanes that are beyond the number of sticky lanes, the weight is always 0.<br>
  711.          * <br>
  712.          * @param laneNumFromRight int; number of lane counted from right to left
  713.          * @param numberOfLanes int; total number of lanes
  714.          * @param numberOfUnplacedGTUs int; number of GTU's in the generation queue
  715.          * @param desiredSpeed Speed; desired speed, possibly used to determine the biased road position
  716.          * @return double; random draw weight for given lane
  717.          */
  718.         public double calculateWeight(final int laneNumFromRight, final int numberOfLanes, final int numberOfUnplacedGTUs,
  719.                 final Speed desiredSpeed)
  720.         {
  721.             double d = Math.abs((1.0 + this.roadPosition.getValue(desiredSpeed) * (numberOfLanes - 1.0)) - laneNumFromRight);
  722.             if (d >= this.stickyLanes)
  723.             {
  724.                 return 0.0;
  725.             }
  726.             return 1.0 / (Math.pow(d + 1.0, this.bias) * (numberOfUnplacedGTUs + 1.0));
  727.         }

  728.         /** {@inheritDoc} */
  729.         @Override
  730.         public int hashCode()
  731.         {
  732.             final int prime = 31;
  733.             int result = 1;
  734.             long temp;
  735.             temp = Double.doubleToLongBits(this.bias);
  736.             result = prime * result + (int) (temp ^ (temp >>> 32));
  737.             result = prime * result + ((this.roadPosition == null) ? 0 : this.roadPosition.hashCode());
  738.             temp = Double.doubleToLongBits(this.stickyLanes);
  739.             result = prime * result + (int) (temp ^ (temp >>> 32));
  740.             return result;
  741.         }

  742.         /** {@inheritDoc} */
  743.         @Override
  744.         public boolean equals(final Object obj)
  745.         {
  746.             if (this == obj)
  747.             {
  748.                 return true;
  749.             }
  750.             if (obj == null)
  751.             {
  752.                 return false;
  753.             }
  754.             if (getClass() != obj.getClass())
  755.             {
  756.                 return false;
  757.             }
  758.             LaneBias other = (LaneBias) obj;
  759.             if (Double.doubleToLongBits(this.bias) != Double.doubleToLongBits(other.bias))
  760.             {
  761.                 return false;
  762.             }
  763.             if (this.roadPosition == null)
  764.             {
  765.                 if (other.roadPosition != null)
  766.                 {
  767.                     return false;
  768.                 }
  769.             }
  770.             else if (!this.roadPosition.equals(other.roadPosition))
  771.             {
  772.                 return false;
  773.             }
  774.             if (Double.doubleToLongBits(this.stickyLanes) != Double.doubleToLongBits(other.stickyLanes))
  775.             {
  776.                 return false;
  777.             }
  778.             return true;
  779.         }

  780.         /** {@inheritDoc} */
  781.         @Override
  782.         public String toString()
  783.         {
  784.             return "Bias [roadPosition=" + this.roadPosition + ", bias=" + this.bias + ", stickyLanes=" + this.stickyLanes
  785.                     + "]";
  786.         }

  787.     }

  788.     /**
  789.      * Interface for preferred road position for a lane bias.
  790.      * <p>
  791.      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  792.      * <br>
  793.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  794.      * </p>
  795.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  796.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  797.      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  798.      */
  799.     public interface RoadPosition
  800.     {

  801.         /**
  802.          * Returns the road position (0.0 = right, 1.0 = left).
  803.          * @param desiredSpeed Speed; desired speed at the generator
  804.          * @return road position (0.0 = right, 1.0 = left)
  805.          */
  806.         double getValue(Speed desiredSpeed);

  807.         /**
  808.          * Fixed road position.
  809.          * <p>
  810.          * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
  811.          * reserved. <br>
  812.          * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  813.          * <p>
  814.          * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  815.          * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  816.          * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  817.          */
  818.         class ByValue implements RoadPosition
  819.         {

  820.             /** Road position. */
  821.             private double value;

  822.             /**
  823.              * Constructor.
  824.              * @param value double; road position
  825.              */
  826.             public ByValue(final double value)
  827.             {
  828.                 Throw.when(value < 0.0 || value > 1.0, IllegalArgumentException.class,
  829.                         "Road position value should be in the range [0...1].");
  830.                 this.value = value;
  831.             }

  832.             /** {@inheritDoc} */
  833.             @Override
  834.             public double getValue(final Speed desiredSpeed)
  835.             {
  836.                 return this.value;
  837.             }

  838.             /** {@inheritDoc} */
  839.             @Override
  840.             public int hashCode()
  841.             {
  842.                 final int prime = 31;
  843.                 int result = 1;
  844.                 long temp;
  845.                 temp = Double.doubleToLongBits(this.value);
  846.                 result = prime * result + (int) (temp ^ (temp >>> 32));
  847.                 return result;
  848.             }

  849.             /** {@inheritDoc} */
  850.             @Override
  851.             public boolean equals(final Object obj)
  852.             {
  853.                 if (this == obj)
  854.                 {
  855.                     return true;
  856.                 }
  857.                 if (obj == null)
  858.                 {
  859.                     return false;
  860.                 }
  861.                 if (getClass() != obj.getClass())
  862.                 {
  863.                     return false;
  864.                 }
  865.                 ByValue other = (ByValue) obj;
  866.                 if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value))
  867.                 {
  868.                     return false;
  869.                 }
  870.                 return true;
  871.             }

  872.         }

  873.         /**
  874.          * Road position based on desired speed.
  875.          * <p>
  876.          * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
  877.          * reserved. <br>
  878.          * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  879.          * <p>
  880.          * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  881.          * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  882.          * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  883.          */
  884.         class BySpeed implements RoadPosition
  885.         {

  886.             /** Desired speed at left side of the road. */
  887.             private Speed leftSpeed;

  888.             /** Desired speed at the right side of the road. */
  889.             private Speed rightSpeed;

  890.             /**
  891.              * Constructor.
  892.              * @param leftSpeed Speed; desired speed at left side of the road
  893.              * @param rightSpeed Speed; desired speed at right side of the road
  894.              */
  895.             public BySpeed(final Speed leftSpeed, final Speed rightSpeed)
  896.             {
  897.                 Throw.when(leftSpeed.eq(rightSpeed), IllegalArgumentException.class,
  898.                         "Left speed and right speed may not be equal. Use LaneBias.NONE.");
  899.                 this.leftSpeed = leftSpeed;
  900.                 this.rightSpeed = rightSpeed;
  901.             }

  902.             /** {@inheritDoc} */
  903.             @Override
  904.             public double getValue(final Speed desiredSpeed)
  905.             {
  906.                 Throw.whenNull(desiredSpeed, "Peeked desired speed from a strategical planner factory is null, "
  907.                         + "while a lane bias depends on desired speed.");
  908.                 double value = (desiredSpeed.si - this.rightSpeed.si) / (this.leftSpeed.si - this.rightSpeed.si);
  909.                 return value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
  910.             }

  911.             /** {@inheritDoc} */
  912.             @Override
  913.             public int hashCode()
  914.             {
  915.                 final int prime = 31;
  916.                 int result = 1;
  917.                 result = prime * result + ((this.leftSpeed == null) ? 0 : this.leftSpeed.hashCode());
  918.                 result = prime * result + ((this.rightSpeed == null) ? 0 : this.rightSpeed.hashCode());
  919.                 return result;
  920.             }

  921.             /** {@inheritDoc} */
  922.             @Override
  923.             public boolean equals(final Object obj)
  924.             {
  925.                 if (this == obj)
  926.                 {
  927.                     return true;
  928.                 }
  929.                 if (obj == null)
  930.                 {
  931.                     return false;
  932.                 }
  933.                 if (getClass() != obj.getClass())
  934.                 {
  935.                     return false;
  936.                 }
  937.                 BySpeed other = (BySpeed) obj;
  938.                 if (this.leftSpeed == null)
  939.                 {
  940.                     if (other.leftSpeed != null)
  941.                     {
  942.                         return false;
  943.                     }
  944.                 }
  945.                 else if (!this.leftSpeed.equals(other.leftSpeed))
  946.                 {
  947.                     return false;
  948.                 }
  949.                 if (this.rightSpeed == null)
  950.                 {
  951.                     if (other.rightSpeed != null)
  952.                     {
  953.                         return false;
  954.                     }
  955.                 }
  956.                 else if (!this.rightSpeed.equals(other.rightSpeed))
  957.                 {
  958.                     return false;
  959.                 }
  960.                 return true;
  961.             }

  962.         }

  963.     }

  964. }