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