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