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