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