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