View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.rmi.RemoteException;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.EnumMap;
8   import java.util.LinkedHashMap;
9   import java.util.LinkedHashSet;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Set;
13  
14  import org.djunits.unit.SpeedUnit;
15  import org.djunits.value.vdouble.scalar.Length;
16  import org.djunits.value.vdouble.scalar.Speed;
17  import org.djutils.draw.point.Point3d;
18  import org.djutils.exceptions.Throw;
19  import org.opentrafficsim.core.geometry.Bounds;
20  import org.opentrafficsim.core.geometry.DirectedPoint;
21  import org.opentrafficsim.core.geometry.OTSGeometryException;
22  import org.opentrafficsim.core.gtu.GTUDirectionality;
23  import org.opentrafficsim.core.gtu.GTUType;
24  import org.opentrafficsim.core.math.Draw;
25  import org.opentrafficsim.core.network.Link;
26  import org.opentrafficsim.core.network.LinkDirection;
27  import org.opentrafficsim.core.network.NetworkException;
28  import org.opentrafficsim.core.network.Node;
29  import org.opentrafficsim.core.network.route.Route;
30  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.BySpeed;
31  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.ByValue;
32  import org.opentrafficsim.road.gtu.strategical.route.RouteGeneratorOD;
33  import org.opentrafficsim.road.network.lane.CrossSectionLink;
34  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
35  import org.opentrafficsim.road.network.lane.Lane;
36  
37  import nl.tudelft.simulation.dsol.animation.Locatable;
38  import nl.tudelft.simulation.jstats.streams.StreamInterface;
39  
40  /**
41   * 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,
42   * it should be included in a queue. This class requires the number of unplaced GTU's per lane, in order to appropriately divide
43   * traffic over the lanes.
44   * <p>
45   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
46   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
47   * <p>
48   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 22 dec. 2017 <br>
49   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
50   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
51   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
52   */
53  public final class GeneratorPositions implements Locatable
54  {
55  
56      /** Underlying object representing the zone. */
57      private final GeneratorZonePosition position;
58  
59      /** Stream for random numbers. */
60      private final StreamInterface stream;
61  
62      /** Location. */
63      private final DirectedPoint location;
64  
65      /** Bounds. */
66      private final Bounds bounds;
67  
68      /** Set of all positions. */
69      private final Set<GeneratorLanePosition> allPositions = new LinkedHashSet<>();
70  
71      /**
72       * Constructor. Private to facilitate easier creation methods using static factories, and to hide underlying classes.
73       * @param position GeneratorZonePosition; underlying object representing the zone
74       * @param biases LaneBiases; lane biases for GTU types
75       * @param stream StreamInterface; stream for random numbers
76       */
77      @SuppressWarnings("synthetic-access")
78      private GeneratorPositions(final GeneratorZonePosition position, final LaneBiases biases, final StreamInterface stream)
79      {
80          this.position = position;
81          for (GeneratorLinkPosition glp : this.position.positions)
82          {
83              if (glp.getDirection().equals(GTUDirectionality.DIR_MINUS))
84              {
85                  System.out.println("Hmm... GTUDirectionality is DIR_MINUS: " + glp);
86              }
87          }
88          this.stream = stream;
89          double x = 0.0;
90          double y = 0.0;
91          double xMin = Double.POSITIVE_INFINITY;
92          double xMax = Double.NEGATIVE_INFINITY;
93          double yMin = Double.POSITIVE_INFINITY;
94          double yMax = Double.NEGATIVE_INFINITY;
95          int n = 0;
96          for (GeneratorLinkPosition linkPosition : position.positions)
97          {
98              for (GeneratorLanePosition lanePosition : linkPosition.positions)
99              {
100                 this.allPositions.add(lanePosition);
101                 for (DirectedLanePosition pos : lanePosition.getPosition())
102                 {
103                     DirectedPoint point;
104                     try
105                     {
106                         point = pos.getLane().getCenterLine().getLocation(pos.getPosition());
107                     }
108                     catch (OTSGeometryException exception)
109                     {
110                         point = new DirectedPoint(0, 0, 0);
111                     }
112                     x += point.x;
113                     y += point.y;
114                     xMin = xMin < point.x ? xMin : point.x;
115                     yMin = yMin < point.y ? yMin : point.y;
116                     xMax = xMax > point.x ? xMax : point.x;
117                     yMax = yMax > point.y ? yMax : point.y;
118                     n++;
119                 }
120             }
121         }
122         this.location = new DirectedPoint(x / n, y / n, 0);
123         this.bounds = new Bounds(new Point3d(xMin, yMin, 0.0), new Point3d(xMax, yMax, 0.0));
124     }
125 
126     /**
127      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Lanes are drawn
128      * without bias. Each link receives a weight equal to the number of lanes.
129      * @param positions Set&lt;DirectedLanePosition&gt;; all considered positions, each lane is considered separately
130      * @param stream StreamInterface; stream for random numbers
131      * @return GeneratorPositions; object to draw positions from
132      */
133     public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream)
134     {
135         return create(positions, stream, null, null, null);
136     }
137 
138     /**
139      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Each link receives a
140      * weight equal to the number of lanes.
141      * @param positions Set&lt;DirectedLanePosition&gt;; all considered positions, each lane is considered separately
142      * @param stream StreamInterface; stream for random numbers
143      * @param biases LaneBiases; lane biases for GTU types
144      * @return GeneratorPositions; object to draw positions from
145      */
146     public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream,
147             final LaneBiases biases)
148     {
149         return create(positions, stream, biases, null, null);
150     }
151 
152     /**
153      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Lanes are drawn
154      * without bias.
155      * @param positions Set&lt;DirectedLanePosition&gt;; all considered positions, each lane is considered separately
156      * @param stream StreamInterface; stream for random numbers
157      * @param linkWeights Map&lt;CrossSectionLink, Double&gt;; weight per link direction
158      * @param viaNodes Map&lt;CrossSectionLink, Node&gt;; nodes connectors feed to for each link where GTU's will be generated
159      * @return GeneratorPositions; object to draw positions from
160      */
161     public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream,
162             final Map<CrossSectionLink, Double> linkWeights, final Map<CrossSectionLink, Node> viaNodes)
163     {
164         return create(positions, stream, null, linkWeights, viaNodes);
165     }
166 
167     /**
168      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link.
169      * @param positions Set&lt;DirectedLanePosition&gt;; all considered positions, each lane is considered separately
170      * @param stream StreamInterface; stream for random numbers
171      * @param laneBiases LaneBiases; lane biases for GTU types
172      * @param linkWeights Map&lt;CrossSectionLink, Double&gt;; weight per link direction
173      * @param viaNodes Map&lt;CrossSectionLink, Node&gt;; nodes connectors feed to for each link where GTU's will be generated
174      * @return GeneratorPositions; object to draw positions from
175      */
176     public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream,
177             final LaneBiases laneBiases, final Map<CrossSectionLink, Double> linkWeights,
178             final Map<CrossSectionLink, Node> viaNodes)
179     {
180 
181         // group directions per link
182         Map<LinkDirection, Set<DirectedLanePosition>> linkSplit = new LinkedHashMap<>();
183         for (DirectedLanePosition position : positions)
184         {
185             if (!linkSplit.containsKey(position.getLinkDirection()))
186             {
187                 linkSplit.put(position.getLinkDirection(), new LinkedHashSet<>());
188             }
189             linkSplit.get(position.getLinkDirection()).add(position);
190         }
191 
192         // create list of GeneratorLinkPositions
193         List<GeneratorLinkPosition> linkPositions = new ArrayList<>();
194         for (LinkDirection linkDirection : linkSplit.keySet())
195         {
196             List<Lane> lanes = ((CrossSectionLink) linkDirection.getLink()).getLanes();
197             // let's sort the lanes by lateral position
198             Collections.sort(lanes, new Comparator<Lane>()
199             {
200                 /** {@inheritDoc} */
201                 @Override
202                 public int compare(final Lane lane1, final Lane lane2)
203                 {
204                     Length lat1 = linkDirection.getDirection().isPlus() ? lane1.getDesignLineOffsetAtBegin() : lane1
205                         .getDesignLineOffsetAtEnd().neg();
206                     Length lat2 = linkDirection.getDirection().isPlus() ? lane2.getDesignLineOffsetAtBegin() : lane2
207                         .getDesignLineOffsetAtEnd().neg();
208                     return lat1.compareTo(lat2);
209                 }
210             });
211             // create list of GeneratorLanePositions
212             List<GeneratorLanePosition> lanePositions = new ArrayList<>();
213             for (DirectedLanePosition lanePosition : linkSplit.get(linkDirection))
214             {
215                 Set<DirectedLanePosition> set = new LinkedHashSet<>();
216                 set.add(lanePosition);
217                 lanePositions.add(new GeneratorLanePosition(lanes.indexOf(lanePosition.getLane()) + 1, set,
218                     (CrossSectionLink) linkDirection.getLink()));
219             }
220             // create the GeneratorLinkPosition
221             CrossSectionLink link = (CrossSectionLink) linkDirection.getLink();
222             if (linkWeights == null)
223             {
224                 linkPositions.add(new GeneratorLinkPosition(lanePositions, link, stream, laneBiases));
225             }
226             else
227             {
228                 Double weight = linkWeights.get(link);
229                 Throw.whenNull(weight, "Using link weights for GTU generation, but no weight for link %s is defined.", link);
230                 linkPositions.add(new GeneratorLinkPosition(lanePositions, link, stream, laneBiases, weight, viaNodes.get(
231                     link)));
232             }
233         }
234 
235         // create the GeneratorZonePosition
236         return new GeneratorPositions(new GeneratorZonePosition(linkPositions), laneBiases, stream);
237 
238     }
239 
240     /**
241      * Draw a new position to generate a GTU. The link is drawn by giving each link a weight equal to the number of accessible
242      * lanes for the GTU type. Next, a lane is drawn using (optionally biased) weights.
243      * @param gtuType GTUType; GTU type
244      * @param destination Node; destination node
245      * @param route Route; route, may be {@code null}
246      * @return GeneratorLanePosition; new position to generate a GTU
247      */
248     public GeneratorLinkPosition draw(final GTUType gtuType, final Node destination, final Route route)
249     {
250         return this.position.draw(gtuType, this.stream, destination, route);
251     }
252 
253     /** {@inheritDoc} */
254     @Override
255     public DirectedPoint getLocation()
256     {
257         return this.location;
258     }
259 
260     /** {@inheritDoc} */
261     @Override
262     public Bounds getBounds() throws RemoteException
263     {
264         return this.bounds;
265     }
266 
267     /**
268      * Returns all underlying positions.
269      * @return all underlying positions
270      */
271     public Set<GeneratorLanePosition> getAllPositions()
272     {
273         return this.allPositions;
274     }
275 
276     /**
277      * Returns the speed limit for the given GTU type, prior to the GTU position being determined.
278      * @param gtuType GTUType; GTU type
279      * @return speed limit for the given GTU type, prior to the GTU position being determined
280      */
281     public Speed speedLimit(final GTUType gtuType)
282     {
283         Speed speedLimit = null;
284         for (GeneratorLanePosition pos : this.allPositions)
285         {
286             for (DirectedLanePosition lane : pos.getPosition())
287             {
288                 try
289                 {
290                     Speed limit = lane.getLane().getSpeedLimit(gtuType);
291                     if (speedLimit == null || limit.lt(speedLimit))
292                     {
293                         speedLimit = limit;
294                     }
295                 }
296                 catch (NetworkException exception)
297                 {
298                     // ignore
299                 }
300             }
301         }
302         Throw.when(speedLimit == null, IllegalStateException.class, "No speed limit could be determined for GTUType %s.",
303             gtuType);
304         return speedLimit;
305     }
306 
307     /**
308      * Class representing a vehicle generation lane, providing elementary information for randomly drawing links and lanes.
309      * <p>
310      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
311      * <br>
312      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
313      * <p>
314      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 23 dec. 2017 <br>
315      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
316      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
317      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
318      */
319     public static final class GeneratorLanePosition
320     {
321 
322         /** Lane number, where 1 is the right-most lane. */
323         private final int laneNumber;
324 
325         /** Position set, representing a single GTU position on the network. */
326         private final Set<DirectedLanePosition> position;
327 
328         /** Link. */
329         private final CrossSectionLink link;
330 
331         /**
332          * Constructor.
333          * @param laneNumber int; lane number, where 1 is the right-most lane
334          * @param position Set&lt;DirectedLanePosition&gt;; position set, representing a single GTU position on the network
335          * @param link CrossSectionLink; link
336          */
337         GeneratorLanePosition(final int laneNumber, final Set<DirectedLanePosition> position, final CrossSectionLink link)
338         {
339             this.laneNumber = laneNumber;
340             this.position = position;
341             this.link = link;
342         }
343 
344         /**
345          * Returns the lane number, where 1 is the right-most lane.
346          * @return lane number, where 1 is the right-most lane
347          */
348         int getLaneNumber()
349         {
350             return this.laneNumber;
351         }
352 
353         /**
354          * Returns whether this lane is accessible to the GTU type.
355          * @param gtuType GTUType; gtu type
356          * @return boolean; whether this lane is accessible to the GTU type
357          */
358         boolean allows(final GTUType gtuType)
359         {
360             for (DirectedLanePosition pos : this.position)
361             {
362                 if (pos.getLane().getLaneType().isCompatible(gtuType, pos.getGtuDirection()))
363                 {
364                     return true;
365                 }
366             }
367             return false;
368         }
369 
370         /**
371          * Returns the contained position set, representing a single GTU position on the network.
372          * @return Set&lt;DirectedLanePosition&gt;; contained position set, representing a single GTU position on the network
373          */
374         Set<DirectedLanePosition> getPosition()
375         {
376             return this.position;
377         }
378 
379         /**
380          * Returns the link.
381          * @return CrossSectionLink; link
382          */
383         CrossSectionLink getLink()
384         {
385             return this.link;
386         }
387 
388         /**
389          * Returns the direction of travel.
390          * @return GTUDirectionality; direction of travel
391          */
392         GTUDirectionality getDirection()
393         {
394             return this.position.iterator().next().getGtuDirection();
395         }
396 
397         /** {@inheritDoc} */
398         @Override
399         public String toString()
400         {
401             return "GeneratorLanePosition [laneNumber=" + this.laneNumber + ", position=" + this.position + ", link="
402                 + this.link + "]";
403         }
404 
405     }
406 
407     /**
408      * Class representing a vehicle generation link to provide individual generation positions.
409      * <p>
410      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
411      * <br>
412      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
413      * <p>
414      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 23 dec. 2017 <br>
415      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
416      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
417      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
418      */
419     public static final class GeneratorLinkPosition
420     {
421 
422         /** Contained lanes. */
423         private final List<GeneratorLanePosition> positions;
424 
425         /** The link. */
426         private final CrossSectionLink link;
427 
428         /** Random stream. */
429         private final StreamInterface stream;
430 
431         /** Lane bias. */
432         private final LaneBiases laneBiases;
433 
434         /** Weight for drawing this link. */
435         private final double weight;
436 
437         /** Node by which a connector connects, may be {@code null}. */
438         private final Node viaNode;
439 
440         /**
441          * Constructor.
442          * @param positions List&lt;GeneratorLanePosition&gt;; contained lanes
443          * @param link CrossSectionLink; the link
444          * @param stream StreamInterface; stream
445          * @param laneBiases LaneBiases; lane biases
446          */
447         GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final CrossSectionLink link,
448                 final StreamInterface stream, final LaneBiases laneBiases)
449         {
450             this.positions = positions;
451             this.link = link;
452             this.stream = stream;
453             this.laneBiases = laneBiases;
454             this.weight = -1;
455             this.viaNode = null;
456         }
457 
458         /**
459          * Constructor.
460          * @param positions List&lt;GeneratorLanePosition&gt;; contained lanes
461          * @param link CrossSectionLink; the link
462          * @param stream StreamInterface; stream
463          * @param laneBiases LaneBiases; lane biases
464          * @param weight double; weight for drawing this link
465          * @param viaNode Node; node by which a connector connects
466          */
467         GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final CrossSectionLink link,
468                 final StreamInterface stream, final LaneBiases laneBiases, final double weight, final Node viaNode)
469         {
470             this.positions = positions;
471             this.link = link;
472             this.stream = stream;
473             this.laneBiases = laneBiases;
474             this.weight = weight;
475             this.viaNode = viaNode;
476         }
477 
478         /**
479          * Return the link.
480          * @return CrossSectionLink; link
481          */
482         CrossSectionLink getLink()
483         {
484             return this.link;
485         }
486 
487         /**
488          * Returns the weight for this link. This is either a predefined weight, or the number of lanes for the GTU type.
489          * @param gtuType GTUType; GTU type
490          * @return double; weight for this link
491          */
492         double getWeight(final GTUType gtuType)
493         {
494             if (this.weight < 0.0)
495             {
496                 return getNumberOfLanes(gtuType);
497             }
498             return this.weight;
499         }
500 
501         /**
502          * Returns the node by which a connector connects.
503          * @return the node by which a connector connects
504          */
505         Node getViaNode()
506         {
507             return this.viaNode;
508         }
509 
510         /**
511          * Returns the number of accessible lanes for the GTU type.
512          * @param gtuType GTUType; GTU type
513          * @return int; number of accessible lanes for the GTU type
514          */
515         int getNumberOfLanes(final GTUType gtuType)
516         {
517             int numberOfLanes = 0;
518             for (GeneratorLanePosition lanePosition : this.positions)
519             {
520                 if (lanePosition.allows(gtuType))
521                 {
522                     numberOfLanes++;
523                 }
524             }
525             return numberOfLanes;
526         }
527 
528         /**
529          * Draws a specific GeneratorLanePosition utilizing lane biases of GTU types.
530          * @param gtuType GTUType; GTU type
531          * @param unplaced Map&lt;Integer, Integer&gt;; number of unplaced GTUs per lane. The lane number should match with
532          *            {@code GeneratorLanePosition.getLaneNumber()}, where 1 is the right-most lane. Missing lanes are assumed
533          *            to have no queue.
534          * @param desiredSpeed Speed; desired speed, possibly used to determine the biased road position
535          * @return GeneratorLanePosition; specific GeneratorLanePosition utilizing lane biases of GTU types
536          */
537         GeneratorLanePosition draw(final GTUType gtuType, final Map<Integer, Integer> unplaced, final Speed desiredSpeed)
538         {
539             Map<GeneratorLanePosition, Double> map = new LinkedHashMap<>();
540             for (int i = 0; i < this.positions.size(); i++)
541             {
542                 GeneratorLanePosition lanePosition = this.positions.get(i);
543                 if (lanePosition.allows(gtuType))
544                 {
545                     GTUType type = gtuType;
546                     boolean found = false;
547                     while (this.laneBiases != null && !found && type != null)
548                     {
549                         if (this.laneBiases.contains(type))
550                         {
551                             found = true;
552                             int laneNum = lanePosition.getLaneNumber();
553                             int unplacedTemplates = unplaced == null ? 0 : unplaced.getOrDefault(laneNum, 0);
554                             double w = this.laneBiases.getBias(type).calculateWeight(laneNum, getNumberOfLanes(gtuType),
555                                 unplacedTemplates, desiredSpeed);
556                             map.put(lanePosition, w);
557                         }
558                         type = type.getParent();
559                     }
560                     if (!found)
561                     {
562                         map.put(lanePosition, 1.0);
563                     }
564                 }
565             }
566             if (0 == map.size())
567             {
568                 System.err.println("This really, really can't work...");
569             }
570             return Draw.drawWeighted(map, this.stream);
571         }
572 
573         /**
574          * Returns the direction of travel.
575          * @return GTUDirectionality; direction of travel
576          */
577         GTUDirectionality getDirection()
578         {
579             return this.positions.get(0).getDirection();
580         }
581 
582         /** {@inheritDoc} */
583         @Override
584         public String toString()
585         {
586             return "GeneratorLinkPosition [positions=" + this.positions + "]";
587         }
588 
589         /**
590          * @param gtuType GTUType; GTU type
591          * @return Speed; speed limit
592          */
593         public Speed speedLimit(final GTUType gtuType)
594         {
595             Speed speedLimit = null;
596             for (GeneratorLanePosition pos : this.positions)
597             {
598                 for (DirectedLanePosition lane : pos.getPosition())
599                 {
600                     try
601                     {
602                         Speed limit = lane.getLane().getSpeedLimit(gtuType);
603                         if (speedLimit == null || limit.lt(speedLimit))
604                         {
605                             speedLimit = limit;
606                         }
607                     }
608                     catch (NetworkException exception)
609                     {
610                         // ignore
611                     }
612                 }
613             }
614             Throw.when(speedLimit == null, IllegalStateException.class, "No speed limit could be determined for GTUType %s.",
615                 gtuType);
616             return speedLimit;
617         }
618 
619     }
620 
621     /**
622      * Class representing a vehicle generation zone to provide individual generation positions.
623      * <p>
624      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
625      * <br>
626      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
627      * <p>
628      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 23 dec. 2017 <br>
629      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
630      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
631      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
632      */
633     private static final class GeneratorZonePosition
634     {
635 
636         /** Contained links. */
637         private final List<GeneratorLinkPosition> positions;
638 
639         /**
640          * Constructor.
641          * @param positions List&lt;GeneratorLinkPosition&gt;; contained links
642          */
643         GeneratorZonePosition(final List<GeneratorLinkPosition> positions)
644         {
645             this.positions = positions;
646         }
647 
648         /**
649          * Draws a GeneratorLinkPosition using number of accessible lanes for the GTUType as weight, and a GeneratorLanePosition
650          * from that.
651          * @param gtuType GTUType; GTU type
652          * @param stream StreamInterface; stream for random numbers
653          * @param destination Node; destination node
654          * @param route Route; route, may be {@code null}
655          * @return GeneratorLanePosition; draws a LinkPosition using number of accessible lanes for the GTUType as weight, and a
656          *         GeneratorLanePosition from that
657          */
658         GeneratorLinkPosition draw(final GTUType gtuType, final StreamInterface stream, final Node destination,
659                 final Route route)
660         {
661             Map<GeneratorLinkPosition, Double> map = new LinkedHashMap<>();
662             for (int i = 0; i < this.positions.size(); i++)
663             {
664                 GeneratorLinkPosition glp = this.positions.get(i);
665                 Link link = glp.getLink();
666                 GTUDirectionality direction = glp.getDirection();
667                 if (route != null)
668                 {
669                     int from = route.indexOf(direction.isPlus() ? link.getStartNode() : link.getEndNode());
670                     int to = route.indexOf(direction.isPlus() ? link.getEndNode() : link.getStartNode());
671                     if (from > -1 && to > -1 && to - from == 1)
672                     {
673                         map.put(glp, glp.getWeight(gtuType));
674                     }
675                 }
676                 else
677                 {
678                     // let's check whether any route is possible over this link
679                     if (glp.getViaNode() != null)
680                     {
681                         // this uses a shortest-path algorithm with caching
682                         Route r = RouteGeneratorOD.getDefaultRouteSupplier(stream).getRoute(glp.getViaNode(), destination,
683                             gtuType);
684                         if (r != null)
685                         {
686                             map.put(glp, glp.getWeight(gtuType));
687                         }
688                     }
689                     else
690                     {
691                         map.put(glp, glp.getWeight(gtuType));
692                     }
693                 }
694             }
695             return Draw.drawWeighted(map, stream);
696         }
697 
698         /** {@inheritDoc} */
699         @Override
700         public String toString()
701         {
702             return "GeneratorZonePosition [positions=" + this.positions + "]";
703         }
704 
705     }
706 
707     /**
708      * Set of lane biases per GTU type.
709      * <p>
710      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
711      * <br>
712      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
713      * <p>
714      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 24 dec. 2017 <br>
715      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
716      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
717      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
718      */
719     public static final class LaneBiases
720     {
721 
722         /** Biases per GTU type. */
723         private final Map<GTUType, LaneBias> biases = new LinkedHashMap<>();
724 
725         /**
726          * Adds a GTU bias for randomly drawing a lane.
727          * @param gtuType GTUType; gtu type
728          * @param bias LaneBias; bias
729          * @return LaneBiases; lane biases for method chaining
730          */
731         public LaneBiases addBias(final GTUType gtuType, final LaneBias bias)
732         {
733             Throw.whenNull(gtuType, "GTU type may not be null.");
734             Throw.whenNull(bias, "Bias may not be null.");
735             this.biases.put(gtuType, bias);
736             return this;
737         }
738 
739         /**
740          * Whether a bias is defined for the given type.
741          * @param gtuType GTUType; GTU type
742          * @return whether a bias is defined for the given type
743          */
744         public boolean contains(final GTUType gtuType)
745         {
746             return this.biases.containsKey(gtuType);
747         }
748 
749         /**
750          * Returns the bias of given GTU type, or {@code Bias.None} if none defined for the GTU type.
751          * @param gtuType GTUType; GTU type
752          * @return Bias; bias of the GTU type
753          */
754         public LaneBias getBias(final GTUType gtuType)
755         {
756             return this.biases.getOrDefault(gtuType, LaneBias.NONE);
757         }
758 
759         /** {@inheritDoc} */
760         @Override
761         public String toString()
762         {
763             return "LaneBiases [" + this.biases + "]";
764         }
765 
766     }
767 
768     /**
769      * Set of lane biases per GTU type enum, based on the GTU Types that are defined by default.
770      * <p>
771      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
772      * <br>
773      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
774      * <p>
775      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 24 dec. 2017 <br>
776      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
777      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
778      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
779      */
780     public static final class LaneBiasDefaults
781     {
782         /** Biases per GTU type. */
783         private final EnumMap<GTUType.DEFAULTS, LaneBias> biases = new EnumMap<>(GTUType.DEFAULTS.class);
784 
785         /**
786          * Adds a GTU bias for randomly drawing a lane.
787          * @param gtuEnum GTUType.DEFAULTS; gtu type
788          * @param bias LaneBias; bias
789          * @return LaneBiases; lane biases for method chaining
790          */
791         public LaneBiasDefaults addBias(final GTUType.DEFAULTS gtuEnum, final LaneBias bias)
792         {
793             Throw.whenNull(gtuEnum, "GTU type enum may not be null.");
794             Throw.whenNull(bias, "Bias may not be null.");
795             this.biases.put(gtuEnum, bias);
796             return this;
797         }
798 
799         /**
800          * Whether a bias is defined for the given type.
801          * @param gtuEnum GTUType; GTU type enum
802          * @return whether a bias is defined for the given type
803          */
804         public boolean contains(final GTUType.DEFAULTS gtuEnum)
805         {
806             return this.biases.containsKey(gtuEnum);
807         }
808 
809         /**
810          * Returns the bias of given GTU type, or {@code Bias.None} if none defined for the GTU type.
811          * @param gtuEnum GTUType.DEFAULTS; GTU type enum
812          * @return Bias; bias of the GTU type
813          */
814         public LaneBias getBias(final GTUType.DEFAULTS gtuEnum)
815         {
816             return this.biases.getOrDefault(gtuEnum, LaneBias.NONE);
817         }
818 
819         /** {@inheritDoc} */
820         @Override
821         public String toString()
822         {
823             return "LaneBiases [" + this.biases + "]";
824         }
825     }
826 
827     /**
828      * Vehicle generation lateral bias. Includes a lane maximum, e.g. trucks only on 2 right-hand lanes.
829      * <p>
830      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
831      * <br>
832      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
833      * <p>
834      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 22 dec. 2017 <br>
835      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
836      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
837      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
838      */
839     public static final class LaneBias
840     {
841 
842         /** No bias. */
843         public static final LaneBias NONE = new LaneBias(new ByValue(0.0), 0.0, Integer.MAX_VALUE);
844 
845         /** Weak left-hand bias, 2nd left lane contains 50% relative to left most lane, in free traffic. */
846         public static final LaneBias WEAK_LEFT = new LaneBias(new ByValue(1.0), 1.0, Integer.MAX_VALUE);
847 
848         /** Left-hand bias, 2nd left lane contains 25% relative to left most lane, in free traffic. */
849         public static final LaneBias LEFT = new LaneBias(new ByValue(1.0), 2.0, Integer.MAX_VALUE);
850 
851         /** Strong left-hand bias, 2nd left lane contains 3.125% relative to left most lane, in free traffic. */
852         public static final LaneBias STRONG_LEFT = new LaneBias(new ByValue(1.0), 5.0, Integer.MAX_VALUE);
853 
854         /** Weak middle bias, 2nd left lane contains 50% relative to left most lane, in free traffic. */
855         public static final LaneBias WEAK_MIDDLE = new LaneBias(new ByValue(0.5), 1.0, Integer.MAX_VALUE);
856 
857         /** Middle bias, 2nd left lane contains 25% relative to left most lane, in free traffic. */
858         public static final LaneBias MIDDLE = new LaneBias(new ByValue(0.5), 2.0, Integer.MAX_VALUE);
859 
860         /** Strong middle bias, 2nd left lane contains 3.125% relative to left most lane, in free traffic. */
861         public static final LaneBias STRONG_MIDDLE = new LaneBias(new ByValue(0.5), 5.0, Integer.MAX_VALUE);
862 
863         /** Weak right-hand bias, 2nd right lane contains 50% relative to right most lane, in free traffic. */
864         public static final LaneBias WEAK_RIGHT = new LaneBias(new ByValue(0.0), 1.0, Integer.MAX_VALUE);
865 
866         /** Right-hand bias, 2nd right lane contains 25% relative to right most lane, in free traffic. */
867         public static final LaneBias RIGHT = new LaneBias(new ByValue(0.0), 2.0, Integer.MAX_VALUE);
868 
869         /** Strong right-hand bias, 2nd right lane contains 3.125% relative to right most lane, in free traffic. */
870         public static final LaneBias STRONG_RIGHT = new LaneBias(new ByValue(0.0), 5.0, Integer.MAX_VALUE);
871 
872         /** Strong right-hand bias, limited to a maximum of 2 lanes. */
873         public static final LaneBias TRUCK_RIGHT = new LaneBias(new ByValue(0.0), 5.0, 2);
874 
875         /**
876          * Returns a bias by speed with normal extent.
877          * @param leftSpeed Speed; desired speed for full left bias
878          * @param rightSpeed Speed; desired speed for full right bias
879          * @return bias by speed with normal extent
880          */
881         public static LaneBias bySpeed(final Speed leftSpeed, final Speed rightSpeed)
882         {
883             return new LaneBias(new BySpeed(leftSpeed, rightSpeed), 2.0, Integer.MAX_VALUE);
884         }
885 
886         /**
887          * Returns a bias by speed with normal extent. Convenience km/h input.
888          * @param leftSpeedKm double; desired speed for full left bias
889          * @param rightSpeedKm double; desired speed for full right bias
890          * @return bias by speed with normal extent
891          */
892         public static LaneBias bySpeed(final double leftSpeedKm, final double rightSpeedKm)
893         {
894             return bySpeed(new Speed(leftSpeedKm, SpeedUnit.KM_PER_HOUR), new Speed(rightSpeedKm, SpeedUnit.KM_PER_HOUR));
895         }
896 
897         /** Provider of position on the road (0 = full left, 1 = full right). */
898         private final RoadPosition roadPosition;
899 
900         /** Bias extent. */
901         private final double bias;
902 
903         /** Number of lanes to consider in either direction, including the preferred lane. */
904         private final double stickyLanes;
905 
906         /**
907          * Constructor.
908          * @param roadPosition RoadPosition; lateral position on the road (0 = right, 0.5 = middle, 1 = left)
909          * @param bias double; bias extent, lower values create more spread traffic, 0.0 causes no lane preference
910          * @param stickyLanes double; number of lanes to consider in either direction, including the preferred lane
911          */
912         public LaneBias(final RoadPosition roadPosition, final double bias, final double stickyLanes)
913         {
914             Throw.when(bias < 0.0, IllegalArgumentException.class, "Bias should be positive or 0.");
915             Throw.when(stickyLanes < 1.0, IllegalArgumentException.class, "Sticky lanes should be 1.0 or larger.");
916             this.roadPosition = roadPosition;
917             this.bias = bias;
918             this.stickyLanes = stickyLanes;
919         }
920 
921         /**
922          * Returns a random draw weight for given lane. The weight is calculated as:
923          * 
924          * <pre>
925          * weight = { 0,                               d &gt;= number of sticky lanes
926          *          { 1 / ((d + 1)^bias * (m + 1)),    otherwise
927          * 
928          * where,
929          *      d:      lane deviation from lateral bias position
930          *      bias:   bias extent
931          *      m:      number of unplaced GTU's
932          * </pre>
933          * 
934          * The formula makes sure that all lanes have equal weight for <i>bias</i> &#61; 0, given an equal number of unplaced
935          * 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)
936          * 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
937          * <i>bias</i> &#61; 2, 1 vs. 8 for <i>bias</i> &#61; 3, etc.<br>
938          * <br>
939          * Division by <i>m</i> + 1 makes sure traffic distributes over the lanes in case of spillback, or otherwise too high
940          * demand on a particular lane. The weight for lanes with more unplaced GTU's simply reduces. This effect balances out
941          * with the bias, meaning that for a strong bias, GTU's are still likely to be generated on the biased lanes. Given a
942          * relatively strong bias of <i>bias</i> &#61; 5, the weight for the 1st and 2nd lane becomes equal if the 2nd lane has
943          * no unplaced GTU's, while the 1st lane has 31 unplaced GTU's.<br>
944          * <br>
945          * Lane deviation <i>d</i> is calculated as <i>d</i> &#61; abs(<i>latBiasLane</i> - <i>laneNumFromRight</i>). Here,
946          * <i>latBiasLane</i> &#61; 1 + <i>roadPosition</i>*(<i>numberOfLanes</i> - 1), i.e. ranging from 1 to 4 on a 4-lane
947          * road. For lanes that are beyond the number of sticky lanes, the weight is always 0.<br>
948          * <br>
949          * @param laneNumFromRight int; number of lane counted from right to left
950          * @param numberOfLanes int; total number of lanes
951          * @param numberOfUnplacedGTUs int; number of GTU's in the generation queue
952          * @param desiredSpeed Speed; desired speed, possibly used to determine the biased road position
953          * @return double; random draw weight for given lane
954          */
955         public double calculateWeight(final int laneNumFromRight, final int numberOfLanes, final int numberOfUnplacedGTUs,
956                 final Speed desiredSpeed)
957         {
958             double d = Math.abs((1.0 + this.roadPosition.getValue(desiredSpeed) * (numberOfLanes - 1.0)) - laneNumFromRight);
959             if (d >= this.stickyLanes)
960             {
961                 return 0.0;
962             }
963             return 1.0 / (Math.pow(d + 1.0, this.bias) * (numberOfUnplacedGTUs + 1.0));
964         }
965 
966         /** {@inheritDoc} */
967         @Override
968         public int hashCode()
969         {
970             final int prime = 31;
971             int result = 1;
972             long temp;
973             temp = Double.doubleToLongBits(this.bias);
974             result = prime * result + (int) (temp ^ (temp >>> 32));
975             result = prime * result + ((this.roadPosition == null) ? 0 : this.roadPosition.hashCode());
976             temp = Double.doubleToLongBits(this.stickyLanes);
977             result = prime * result + (int) (temp ^ (temp >>> 32));
978             return result;
979         }
980 
981         /** {@inheritDoc} */
982         @Override
983         public boolean equals(final Object obj)
984         {
985             if (this == obj)
986             {
987                 return true;
988             }
989             if (obj == null)
990             {
991                 return false;
992             }
993             if (getClass() != obj.getClass())
994             {
995                 return false;
996             }
997             LaneBias other = (LaneBias) obj;
998             if (Double.doubleToLongBits(this.bias) != Double.doubleToLongBits(other.bias))
999             {
1000                 return false;
1001             }
1002             if (this.roadPosition == null)
1003             {
1004                 if (other.roadPosition != null)
1005                 {
1006                     return false;
1007                 }
1008             }
1009             else if (!this.roadPosition.equals(other.roadPosition))
1010             {
1011                 return false;
1012             }
1013             if (Double.doubleToLongBits(this.stickyLanes) != Double.doubleToLongBits(other.stickyLanes))
1014             {
1015                 return false;
1016             }
1017             return true;
1018         }
1019 
1020         /** {@inheritDoc} */
1021         @Override
1022         public String toString()
1023         {
1024             return "Bias [roadPosition=" + this.roadPosition + ", bias=" + this.bias + ", stickyLanes=" + this.stickyLanes
1025                 + "]";
1026         }
1027 
1028     }
1029 
1030     /**
1031      * Interface for preferred road position for a lane bias.
1032      * <p>
1033      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
1034      * <br>
1035      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
1036      * <p>
1037      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 15 jan. 2018 <br>
1038      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
1039      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
1040      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
1041      */
1042     public interface RoadPosition
1043     {
1044 
1045         /**
1046          * Returns the road position (0.0 = right, 1.0 = left).
1047          * @param desiredSpeed Speed; desired speed at the generator
1048          * @return road position (0.0 = right, 1.0 = left)
1049          */
1050         double getValue(Speed desiredSpeed);
1051 
1052         /**
1053          * Fixed road position.
1054          * <p>
1055          * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
1056          * reserved. <br>
1057          * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
1058          * <p>
1059          * @version $Revision$, $LastChangedDate$, by $Author$, initial version 15 jan. 2018 <br>
1060          * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
1061          * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
1062          * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
1063          */
1064         public class ByValue implements RoadPosition
1065         {
1066 
1067             /** Road position. */
1068             private double value;
1069 
1070             /**
1071              * Constructor.
1072              * @param value double; road position
1073              */
1074             public ByValue(final double value)
1075             {
1076                 Throw.when(value < 0.0 || value > 1.0, IllegalArgumentException.class,
1077                     "Road position value should be in the range [0...1].");
1078                 this.value = value;
1079             }
1080 
1081             /** {@inheritDoc} */
1082             @Override
1083             public double getValue(final Speed desiredSpeed)
1084             {
1085                 return this.value;
1086             }
1087 
1088             /** {@inheritDoc} */
1089             @Override
1090             public int hashCode()
1091             {
1092                 final int prime = 31;
1093                 int result = 1;
1094                 long temp;
1095                 temp = Double.doubleToLongBits(this.value);
1096                 result = prime * result + (int) (temp ^ (temp >>> 32));
1097                 return result;
1098             }
1099 
1100             /** {@inheritDoc} */
1101             @Override
1102             public boolean equals(final Object obj)
1103             {
1104                 if (this == obj)
1105                 {
1106                     return true;
1107                 }
1108                 if (obj == null)
1109                 {
1110                     return false;
1111                 }
1112                 if (getClass() != obj.getClass())
1113                 {
1114                     return false;
1115                 }
1116                 ByValue other = (ByValue) obj;
1117                 if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value))
1118                 {
1119                     return false;
1120                 }
1121                 return true;
1122             }
1123 
1124         }
1125 
1126         /**
1127          * Road position based on desired speed.
1128          * <p>
1129          * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
1130          * reserved. <br>
1131          * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
1132          * <p>
1133          * @version $Revision$, $LastChangedDate$, by $Author$, initial version 15 jan. 2018 <br>
1134          * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
1135          * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
1136          * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
1137          */
1138         class BySpeed implements RoadPosition
1139         {
1140 
1141             /** Desired speed at left side of the road. */
1142             private Speed leftSpeed;
1143 
1144             /** Desired speed at the right side of the road. */
1145             private Speed rightSpeed;
1146 
1147             /**
1148              * Constructor.
1149              * @param leftSpeed Speed; desired speed at left side of the road
1150              * @param rightSpeed Speed; desired speed at right side of the road
1151              */
1152             public BySpeed(final Speed leftSpeed, final Speed rightSpeed)
1153             {
1154                 Throw.when(leftSpeed.eq(rightSpeed), IllegalArgumentException.class,
1155                     "Left speed and right speed may not be equal. Use LaneBias.NONE.");
1156                 this.leftSpeed = leftSpeed;
1157                 this.rightSpeed = rightSpeed;
1158             }
1159 
1160             /** {@inheritDoc} */
1161             @Override
1162             public double getValue(final Speed desiredSpeed)
1163             {
1164                 Throw.whenNull(desiredSpeed,
1165                     "Peeked desired speed from a strategical planner factory is null, while a lane bias depends on desired speed.");
1166                 double value = (desiredSpeed.si - this.rightSpeed.si) / (this.leftSpeed.si - this.rightSpeed.si);
1167                 return value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
1168             }
1169 
1170             /** {@inheritDoc} */
1171             @Override
1172             public int hashCode()
1173             {
1174                 final int prime = 31;
1175                 int result = 1;
1176                 result = prime * result + ((this.leftSpeed == null) ? 0 : this.leftSpeed.hashCode());
1177                 result = prime * result + ((this.rightSpeed == null) ? 0 : this.rightSpeed.hashCode());
1178                 return result;
1179             }
1180 
1181             /** {@inheritDoc} */
1182             @Override
1183             public boolean equals(final Object obj)
1184             {
1185                 if (this == obj)
1186                 {
1187                     return true;
1188                 }
1189                 if (obj == null)
1190                 {
1191                     return false;
1192                 }
1193                 if (getClass() != obj.getClass())
1194                 {
1195                     return false;
1196                 }
1197                 BySpeed other = (BySpeed) obj;
1198                 if (this.leftSpeed == null)
1199                 {
1200                     if (other.leftSpeed != null)
1201                     {
1202                         return false;
1203                     }
1204                 }
1205                 else if (!this.leftSpeed.equals(other.leftSpeed))
1206                 {
1207                     return false;
1208                 }
1209                 if (this.rightSpeed == null)
1210                 {
1211                     if (other.rightSpeed != null)
1212                     {
1213                         return false;
1214                     }
1215                 }
1216                 else if (!this.rightSpeed.equals(other.rightSpeed))
1217                 {
1218                     return false;
1219                 }
1220                 return true;
1221             }
1222 
1223         }
1224 
1225     }
1226 
1227 }