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