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