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