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<DirectedLanePosition>; 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<DirectedLanePosition>; 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<DirectedLanePosition>; all considered positions, each lane is considered separately
158 * @param stream StreamInterface; stream for random numbers
159 * @param linkWeights Map<CrossSectionLink, Double>; weight per link direction
160 * @param viaNodes Map<CrossSectionLink, Node>; 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<DirectedLanePosition>; 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<CrossSectionLink, Double>; weight per link direction
175 * @param viaNodes Map<CrossSectionLink, Node>; 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<DirectedLanePosition>; 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<DirectedLanePosition>; 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<GeneratorLanePosition>; 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<GeneratorLanePosition>; 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<Integer, Integer>; 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<GeneratorLinkPosition>; 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 >= 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> = 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> = 0, 1 vs. 2 for <i>bias</i> = 1, 1 vs. 4 for
939 * <i>bias</i> = 2, 1 vs. 8 for <i>bias</i> = 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> = 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> = abs(<i>latBiasLane</i> - <i>laneNumFromRight</i>). Here,
948 * <i>latBiasLane</i> = 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 }