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