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