1 package org.opentrafficsim.road.gtu.generator.od;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Comparator;
6 import java.util.LinkedHashMap;
7 import java.util.LinkedHashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Map.Entry;
11 import java.util.Set;
12 import java.util.stream.Collectors;
13
14 import org.djunits.unit.FrequencyUnit;
15 import org.djunits.value.vdouble.scalar.Duration;
16 import org.djunits.value.vdouble.scalar.Frequency;
17 import org.djunits.value.vdouble.scalar.Length;
18 import org.djunits.value.vdouble.scalar.Time;
19 import org.djutils.exceptions.Throw;
20 import org.opentrafficsim.base.parameters.ParameterException;
21 import org.opentrafficsim.core.distributions.Generator;
22 import org.opentrafficsim.core.distributions.ProbabilityException;
23 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
24 import org.opentrafficsim.core.gtu.GTUDirectionality;
25 import org.opentrafficsim.core.gtu.GTUException;
26 import org.opentrafficsim.core.gtu.GTUType;
27 import org.opentrafficsim.core.idgenerator.IdGenerator;
28 import org.opentrafficsim.core.math.Draw;
29 import org.opentrafficsim.core.network.Link;
30 import org.opentrafficsim.core.network.LinkType;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.core.network.Node;
33 import org.opentrafficsim.road.gtu.generator.GeneratorPositions;
34 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBiases;
35 import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator;
36 import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator.RoomChecker;
37 import org.opentrafficsim.road.gtu.generator.MarkovCorrelation;
38 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
39 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
40 import org.opentrafficsim.road.gtu.generator.headway.Arrivals;
41 import org.opentrafficsim.road.gtu.generator.headway.ArrivalsHeadwayGenerator;
42 import org.opentrafficsim.road.gtu.generator.headway.ArrivalsHeadwayGenerator.HeadwayDistribution;
43 import org.opentrafficsim.road.gtu.generator.headway.DemandPattern;
44 import org.opentrafficsim.road.gtu.strategical.od.Categorization;
45 import org.opentrafficsim.road.gtu.strategical.od.Category;
46 import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
47 import org.opentrafficsim.road.network.OTSRoadNetwork;
48 import org.opentrafficsim.road.network.lane.CrossSectionLink;
49 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
50 import org.opentrafficsim.road.network.lane.Lane;
51 import org.opentrafficsim.road.network.lane.object.sensor.Sensor;
52 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
53
54 import nl.tudelft.simulation.dsol.SimRuntimeException;
55 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
56 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
57 import nl.tudelft.simulation.jstats.streams.StreamInterface;
58
59
60
61
62
63
64
65
66
67
68
69
70 public final class ODApplier
71 {
72
73
74
75
76 private ODApplier()
77 {
78
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 @SuppressWarnings("checkstyle:methodlength")
123 public static Map<String, GeneratorObjects> applyOD(final OTSRoadNetwork network, final ODMatrix od,
124 final OTSSimulatorInterface simulator, final ODOptions odOptions) throws ParameterException, SimRuntimeException
125 {
126 Throw.whenNull(network, "Network may not be null.");
127 Throw.whenNull(od, "OD matrix may not be null.");
128 Throw.whenNull(simulator, "Simulator may not be null.");
129 Throw.whenNull(odOptions, "OD options may not be null.");
130 Throw.when(!simulator.getSimulatorTime().eq0(), SimRuntimeException.class,
131 "Method ODApplier.applyOD() should be invoked at simulation time 0.");
132
133
134 for (Node destination : od.getDestinations())
135 {
136 createSinksAtDestination(destination, simulator);
137 }
138
139 final Categorization categorization = od.getCategorization();
140 final boolean laneBased = categorization.entails(Lane.class);
141 boolean markovian = od.getCategorization().entails(GTUType.class);
142
143
144 StreamInterface stream = simulator.getReplication().getStream("generation");
145 if (stream == null)
146 {
147 stream = simulator.getReplication().getStream("default");
148 if (stream == null)
149 {
150 System.out
151 .println("Using locally created stream (not from the simulator) for vehicle generation, with seed 1.");
152 stream = new MersenneTwister(1L);
153 }
154 else
155 {
156 System.out.println("Using stream 'default' for vehicle generation.");
157 }
158 }
159
160 Map<String, GeneratorObjects> output = new LinkedHashMap<>();
161 for (Node origin : od.getOrigins())
162 {
163
164 DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> rootNode = null;
165
166
167
168
169
170
171
172
173 Map<Lane, DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>> originNodePerLane = new LinkedHashMap<>();
174 MarkovChain markovChain = null;
175 if (!laneBased)
176 {
177 rootNode = new DemandNode<>(origin, stream, null);
178 LinkType linkType = getLinkTypeFromNode(origin);
179 if (markovian)
180 {
181 MarkovCorrelation<GTUType, Frequency> correlation = odOptions.get(ODOptions.MARKOV, null, origin, linkType);
182 if (correlation != null)
183 {
184 Throw.when(!od.getCategorization().entails(GTUType.class), IllegalArgumentException.class,
185 "Markov correlation can only be used on OD categorization entailing GTU type.");
186 markovChain = new MarkovChain(correlation);
187 }
188 }
189 }
190 for (Node destination : od.getDestinations())
191 {
192 Set<Category> categories = od.getCategories(origin, destination);
193 if (!categories.isEmpty())
194 {
195 DemandNode<Node, DemandNode<Category, ?>> destinationNode = null;
196 if (!laneBased)
197 {
198 destinationNode = new DemandNode<>(destination, stream, markovChain);
199 rootNode.addChild(destinationNode);
200 }
201 for (Category category : categories)
202 {
203 if (laneBased)
204 {
205
206 Lane lane = category.get(Lane.class);
207 rootNode = originNodePerLane.get(lane);
208 if (rootNode == null)
209 {
210 rootNode = new DemandNode<>(origin, stream, null);
211 originNodePerLane.put(lane, rootNode);
212 }
213 destinationNode = rootNode.getChild(destination);
214 if (destinationNode == null)
215 {
216 markovChain = null;
217 if (markovian)
218 {
219 MarkovCorrelation<GTUType, Frequency> correlation =
220 odOptions.get(ODOptions.MARKOV, lane, origin, lane.getParentLink().getLinkType());
221 if (correlation != null)
222 {
223 Throw.when(!od.getCategorization().entails(GTUType.class),
224 IllegalArgumentException.class,
225 "Markov correlation can only be used on OD categorization entailing GTU type.");
226 markovChain = new MarkovChain(correlation);
227 }
228 }
229 destinationNode = new DemandNode<>(destination, stream, markovChain);
230 rootNode.addChild(destinationNode);
231 }
232 }
233 DemandNode<Category, ?> categoryNode =
234 new DemandNode<>(category, od.getDemandPattern(origin, destination, category));
235 if (markovian)
236 {
237 destinationNode.addLeaf(categoryNode, category.get(GTUType.class));
238 }
239 else
240 {
241 destinationNode.addChild(categoryNode);
242 }
243 }
244 }
245 }
246
247
248 Map<DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>, Set<DirectedLanePosition>> initialPositions =
249 new LinkedHashMap<>();
250 Map<CrossSectionLink, Double> linkWeights = null;
251 if (laneBased)
252 {
253 for (Lane lane : originNodePerLane.keySet())
254 {
255 DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> demandNode = originNodePerLane.get(lane);
256 Set<DirectedLanePosition> initialPosition = new LinkedHashSet<>();
257 try
258 {
259 initialPosition.add(lane.getParentLink().getStartNode().equals(demandNode.getObject())
260 ? new DirectedLanePosition(lane, Length.ZERO, GTUDirectionality.DIR_PLUS)
261 : new DirectedLanePosition(lane, lane.getLength(), GTUDirectionality.DIR_MINUS));
262 }
263 catch (GTUException ge)
264 {
265 throw new RuntimeException(ge);
266 }
267 initialPositions.put(demandNode, initialPosition);
268 }
269 }
270 else
271 {
272 Set<DirectedLanePosition> positionSet = new LinkedHashSet<>();
273 for (Link link : origin.getLinks())
274 {
275 if (link.getLinkType().isConnector())
276 {
277 if (link.getStartNode().equals(origin))
278 {
279 Node connectedNode = link.getEndNode();
280
281 int served = 0;
282 for (Link connectedLink : connectedNode.getLinks())
283 {
284 if (connectedLink instanceof CrossSectionLink && !connectedLink.getLinkType().isConnector())
285 {
286 served++;
287 }
288 }
289 for (Link connectedLink : connectedNode.getLinks())
290 {
291 if (connectedLink instanceof CrossSectionLink)
292 {
293 if (link instanceof CrossSectionLink./../org/opentrafficsim/road/network/lane/CrossSectionLink.html#CrossSectionLink">CrossSectionLink && ((CrossSectionLink) link).getDemandWeight() != null)
294 {
295 if (linkWeights == null)
296 {
297 linkWeights = new LinkedHashMap<>();
298 }
299
300 linkWeights.put(((CrossSectionLink) connectedLink),
301 ((CrossSectionLink) link).getDemandWeight() / served);
302 }
303 setDirectedLanePosition((CrossSectionLink) connectedLink, connectedNode, positionSet);
304 }
305 }
306 }
307 }
308 else if (link instanceof CrossSectionLink)
309 {
310 setDirectedLanePosition((CrossSectionLink) link, origin, positionSet);
311 }
312 }
313 initialPositions.put(rootNode, positionSet);
314 }
315
316
317 initialPositions = sortByValue(initialPositions);
318 Map<Node, Integer> originGeneratorCounts = new LinkedHashMap<>();
319 for (DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root : initialPositions.keySet())
320 {
321 Set<DirectedLanePosition> initialPosition = initialPositions.get(root);
322
323 Node o = root.getObject();
324 String id = o.getId();
325 if (laneBased)
326 {
327 Integer count = originGeneratorCounts.get(o);
328 if (count == null)
329 {
330 count = 0;
331 }
332 count++;
333 id += count;
334 originGeneratorCounts.put(o, count);
335 }
336
337 Lane lane;
338 LinkType linkType;
339 if (laneBased)
340 {
341 lane = initialPosition.iterator().next().getLane();
342 linkType = lane.getParentLink().getLinkType();
343 }
344 else
345 {
346 lane = null;
347 linkType = getLinkTypeFromNode(o);
348 }
349 HeadwayDistribution randomization = odOptions.get(ODOptions.HEADWAY_DIST, lane, o, linkType);
350 ArrivalsHeadwayGenerator headwayGenerator =
351 new ArrivalsHeadwayGenerator(root, simulator, stream, randomization);
352 GTUCharacteristicsGeneratorODWrapper characteristicsGenerator = new GTUCharacteristicsGeneratorODWrapper(root,
353 simulator, odOptions.get(ODOptions.GTU_TYPE, lane, o, linkType), stream);
354 RoomChecker roomChecker = odOptions.get(ODOptions.ROOM_CHECKER, lane, o, linkType);
355 IdGenerator idGenerator = odOptions.get(ODOptions.GTU_ID, lane, o, linkType);
356 LaneBiases biases = odOptions.get(ODOptions.getLaneBiasOption(network), lane, o, linkType);
357
358 try
359 {
360 LaneBasedGTUGeneratorneBasedGTUGenerator.html#LaneBasedGTUGenerator">LaneBasedGTUGenerator generator = new LaneBasedGTUGenerator(id, headwayGenerator, characteristicsGenerator,
361 GeneratorPositions.create(initialPosition, stream, biases, linkWeights), network, simulator,
362 roomChecker, idGenerator);
363 generator.setNoLaneChangeDistance(odOptions.get(ODOptions.NO_LC_DIST, lane, o, linkType));
364 generator.setInstantaneousLaneChange(odOptions.get(ODOptions.INSTANT_LC, lane, o, linkType));
365 generator.setErrorHandler(odOptions.get(ODOptions.ERROR_HANDLER, lane, o, linkType));
366 output.put(id, new GeneratorObjects(generator, headwayGenerator, characteristicsGenerator));
367 }
368 catch (SimRuntimeException exception)
369 {
370
371 throw new RuntimeException(exception);
372 }
373 catch (ProbabilityException exception)
374 {
375
376 throw new RuntimeException(exception);
377 }
378 }
379 }
380 return output;
381 }
382
383
384
385
386
387
388 private static void createSinksAtDestination(final Node destination, final OTSSimulatorInterface simulator)
389 {
390 for (Link link : destination.getLinks())
391 {
392 if (link.getLinkType().isConnector() && !link.getStartNode().equals(destination))
393 {
394 createSinksAtDestination(link.getStartNode(), simulator);
395 }
396 if (link instanceof CrossSectionLink)
397 {
398 for (Lane lane : ((CrossSectionLink) link).getLanes())
399 {
400 try
401 {
402
403 boolean sinkSensorExists = false;
404 for (Sensor sensor : lane.getSensors())
405 {
406 if (sensor instanceof SinkSensor)
407 {
408 sinkSensorExists = true;
409 }
410 }
411 if (!sinkSensorExists)
412 {
413 if (link.getEndNode().equals(destination))
414 {
415 new SinkSensor(lane, lane.getLength(), GTUDirectionality.DIR_PLUS, simulator);
416 }
417 else if (link.getStartNode().equals(destination))
418 {
419 new SinkSensor(lane, Length.ZERO, GTUDirectionality.DIR_MINUS, simulator);
420 }
421 }
422 }
423 catch (NetworkException exception)
424 {
425
426 throw new RuntimeException(exception);
427 }
428 }
429 }
430 }
431 }
432
433
434
435
436
437
438 private static LinkType getLinkTypeFromNode(final Node node)
439 {
440 return getLinkTypeFromNode0(node, false);
441 }
442
443
444
445
446
447
448
449 private static LinkType getLinkTypeFromNode0(final Node node, final boolean ignoreConnectors)
450 {
451 LinkType linkType = null;
452 for (Link link : node.getLinks())
453 {
454 LinkType next = link.getLinkType();
455 if (!ignoreConnectors && next.isConnector())
456 {
457 Node otherNode = link.getStartNode().equals(node) ? link.getEndNode() : link.getStartNode();
458 next = getLinkTypeFromNode0(otherNode, true);
459 }
460 if (next != null && !next.isConnector())
461 {
462 if (linkType == null)
463 {
464 linkType = next;
465 }
466 else
467 {
468 linkType = linkType.commonAncestor(next);
469 if (linkType == null)
470 {
471
472 return null;
473 }
474 }
475 }
476 }
477 return linkType;
478 }
479
480
481
482
483
484
485
486
487 private static <K, V extends Set<DirectedLanePosition>> Map<K, V> sortByValue(final Map<K, V> map)
488 {
489 return map.entrySet().stream().sorted(new Comparator<Map.Entry<K, V>>()
490 {
491 @Override
492 public int compare(final Entry<K, V> o1, final Entry<K, V> o2)
493 {
494 DirectedLanePosition lanePos1 = o1.getValue().iterator().next();
495 String linkId1 = lanePos1.getLane().getParentLink().getId();
496 DirectedLanePosition lanePos2 = o2.getValue().iterator().next();
497 String linkId2 = lanePos2.getLane().getParentLink().getId();
498 int c = linkId1.compareToIgnoreCase(linkId2);
499 if (c == 0)
500 {
501 Length pos1 = lanePos1.getGtuDirection().isPlus() ? Length.ZERO : lanePos1.getLane().getLength();
502 Length lat1 = lanePos1.getLane().getLateralCenterPosition(pos1);
503 Length pos2 = lanePos2.getGtuDirection().isPlus() ? Length.ZERO : lanePos2.getLane().getLength();
504 Length lat2 = lanePos2.getLane().getLateralCenterPosition(pos2);
505 return lat1.compareTo(lat2);
506 }
507 return c;
508 }
509 }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
510 }
511
512
513
514
515
516
517
518
519 private static void setDirectedLanePosition(final CrossSectionLink link, final Node node,
520 final Set<DirectedLanePosition> positionSet)
521 {
522 for (Lane lane : link.getLanes())
523 {
524 try
525 {
526 positionSet.add(lane.getParentLink().getStartNode().equals(node)
527 ? new DirectedLanePosition(lane, Length.ZERO, GTUDirectionality.DIR_PLUS)
528 : new DirectedLanePosition(lane, lane.getLength(), GTUDirectionality.DIR_MINUS));
529 }
530 catch (GTUException ge)
531 {
532 throw new RuntimeException(ge);
533 }
534 }
535 }
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559 private static class DemandNode<T, K extends DemandNode<?, ?>> implements Arrivals
560 {
561
562
563 private final T object;
564
565
566 private final StreamInterface stream;
567
568
569 private final List<K> children = new ArrayList<>();
570
571
572 private final DemandPattern demandPattern;
573
574
575 private final List<GTUType> gtuTypes = new ArrayList<>();
576
577
578 private final List<Integer> gtuTypeCounts = new ArrayList<>();
579
580
581 private final Map<K, GTUType> gtuTypesPerChild = new LinkedHashMap<>();
582
583
584 private final MarkovChain markov;
585
586
587
588
589
590
591
592 DemandNode(final T object, final StreamInterface stream, final MarkovChain markov)
593 {
594 this.object = object;
595 this.stream = stream;
596 this.demandPattern = null;
597 this.markov = markov;
598 }
599
600
601
602
603
604
605 DemandNode(final T object, final DemandPattern demandPattern)
606 {
607 this.object = object;
608 this.stream = null;
609 this.demandPattern = demandPattern;
610 this.markov = null;
611 }
612
613
614
615
616
617 public void addChild(final K child)
618 {
619 this.children.add(child);
620 }
621
622
623
624
625
626
627 public void addLeaf(final K child, final GTUType gtuType)
628 {
629 Throw.when(this.gtuTypes == null, IllegalStateException.class,
630 "Adding leaf with GTUType in not possible on a non-Markov node.");
631 addChild(child);
632 this.gtuTypesPerChild.put(child, gtuType);
633 if (!this.gtuTypes.contains(gtuType))
634 {
635 this.gtuTypes.add(gtuType);
636 this.gtuTypeCounts.add(1);
637 }
638 else
639 {
640 int index = this.gtuTypes.indexOf(gtuType);
641 this.gtuTypeCounts.set(index, this.gtuTypeCounts.get(index) + 1);
642 }
643 }
644
645
646
647
648
649
650 public K draw(final Time time)
651 {
652 Throw.when(this.children.isEmpty(), RuntimeException.class, "Calling draw on a leaf node in the demand tree.");
653 Map<K, Double> weightMap = new LinkedHashMap<>();
654 if (this.markov == null)
655 {
656
657 for (K child : this.children)
658 {
659 double f = child.getFrequency(time, true).si;
660 weightMap.put(child, f);
661 }
662 }
663 else
664 {
665
666 GTUType[] gtuTypeArray = new GTUType[this.gtuTypes.size()];
667 gtuTypeArray = this.gtuTypes.toArray(gtuTypeArray);
668 Frequency[] steadyState = new Frequency[this.gtuTypes.size()];
669 Arrays.fill(steadyState, Frequency.ZERO);
670 Map<K, Frequency> frequencies = new LinkedHashMap<>();
671 for (K child : this.children)
672 {
673 GTUType gtuType = this.gtuTypesPerChild.get(child);
674 int index = this.gtuTypes.indexOf(gtuType);
675 Frequency f = child.getFrequency(time, true);
676 frequencies.put(child, f);
677 steadyState[index] = steadyState[index].plus(f);
678 }
679 GTUType nextGtuType = this.markov.draw(gtuTypeArray, steadyState, this.stream);
680
681 for (K child : this.children)
682 {
683 if (this.gtuTypesPerChild.get(child).equals(nextGtuType))
684 {
685 double f = frequencies.get(child).si;
686 weightMap.put(child, f);
687 }
688 }
689 }
690 return Draw.drawWeighted(weightMap, this.stream);
691 }
692
693
694
695
696
697 public T getObject()
698 {
699 return this.object;
700 }
701
702
703
704
705
706
707 public K getChild(final Object obj)
708 {
709 for (K child : this.children)
710 {
711 if (child.getObject().equals(obj))
712 {
713 return child;
714 }
715 }
716 return null;
717 }
718
719
720 @Override
721 public Frequency getFrequency(final Time time, final boolean sliceStart)
722 {
723 if (this.demandPattern != null)
724 {
725 return this.demandPattern.getFrequency(time, sliceStart);
726 }
727 Frequency f = new Frequency(0.0, FrequencyUnit.PER_HOUR);
728 for (K child : this.children)
729 {
730 f = f.plus(child.getFrequency(time, sliceStart));
731 }
732 return f;
733 }
734
735
736 @Override
737 public Time nextTimeSlice(final Time time)
738 {
739 if (this.demandPattern != null)
740 {
741 return this.demandPattern.nextTimeSlice(time);
742 }
743 Time out = null;
744 for (K child : this.children)
745 {
746 Time childSlice = child.nextTimeSlice(time);
747 out = out == null || (childSlice != null && childSlice.lt(out)) ? childSlice : out;
748 }
749 return out;
750 }
751
752
753 @Override
754 public String toString()
755 {
756 return "DemandNode [object=" + this.object + ", stream=" + this.stream + ", children=" + this.children
757 + ", demandPattern=" + this.demandPattern + ", gtuTypes=" + this.gtuTypes + ", gtuTypeCounts="
758 + this.gtuTypeCounts + ", gtuTypesPerChild=" + this.gtuTypesPerChild + ", markov=" + this.markov + "]";
759 }
760
761 }
762
763
764
765
766
767 private static class MarkovChain
768 {
769
770 private final MarkovCorrelation<GTUType, Frequency> markov;
771
772
773 private GTUType previousGtuType = null;
774
775
776
777
778
779 MarkovChain(final MarkovCorrelation<GTUType, Frequency> markov)
780 {
781 this.markov = markov;
782 }
783
784
785
786
787
788
789
790
791 public GTUType draw(final GTUType[] gtuTypes, final Frequency[] intensities, final StreamInterface stream)
792 {
793 this.previousGtuType = this.markov.drawState(this.previousGtuType, gtuTypes, intensities, stream);
794 return this.previousGtuType;
795 }
796 }
797
798
799
800
801
802
803
804
805
806
807
808
809
810 private static class GTUCharacteristicsGeneratorODWrapper implements LaneBasedGTUCharacteristicsGenerator
811 {
812
813
814 private final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root;
815
816
817 private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
818
819
820 private final GTUCharacteristicsGeneratorOD charachteristicsGenerator;
821
822
823 private final StreamInterface randomStream;
824
825
826
827
828
829
830
831 GTUCharacteristicsGeneratorODWrapper(final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root,
832 final DEVSSimulatorInterface.TimeDoubleUnit simulator,
833 final GTUCharacteristicsGeneratorOD charachteristicsGenerator, final StreamInterface randomStream)
834 {
835 this.root = root;
836 this.simulator = simulator;
837 this.charachteristicsGenerator = charachteristicsGenerator;
838 this.randomStream = randomStream;
839 }
840
841
842 @Override
843 public LaneBasedGTUCharacteristics draw() throws ProbabilityException, ParameterException, GTUException
844 {
845
846 Time time = this.simulator.getSimulatorTime();
847 Node origin = this.root.getObject();
848 DemandNode<Node, DemandNode<Category, ?>> destinationNode = this.root.draw(time);
849 Node destination = destinationNode.getObject();
850 Category category = destinationNode.draw(time).getObject();
851
852 return this.charachteristicsGenerator.draw(origin, destination, category, this.randomStream);
853 }
854
855
856 @Override
857 public String toString()
858 {
859 return "GTUCharacteristicsGeneratorODWrapper [root=" + this.root + ", simulator=" + this.simulator
860 + ", charachteristicsGenerator=" + this.charachteristicsGenerator + ", randomStream=" + this.randomStream
861 + "]";
862 }
863
864 }
865
866
867
868
869
870
871
872
873
874
875
876
877
878 public static class GeneratorObjects
879 {
880
881
882 private final LaneBasedGTUGenerator generator;
883
884
885 private final Generator<Duration> headwayGenerator;
886
887
888 private final LaneBasedGTUCharacteristicsGenerator charachteristicsGenerator;
889
890
891
892
893
894
895 public GeneratorObjects(final LaneBasedGTUGenerator generator, final Generator<Duration> headwayGenerator,
896 final LaneBasedGTUCharacteristicsGenerator charachteristicsGenerator)
897 {
898 this.generator = generator;
899 this.headwayGenerator = headwayGenerator;
900 this.charachteristicsGenerator = charachteristicsGenerator;
901 }
902
903
904
905
906
907 public LaneBasedGTUGenerator getGenerator()
908 {
909 return this.generator;
910 }
911
912
913
914
915
916 public Generator<Duration> getHeadwayGenerator()
917 {
918 return this.headwayGenerator;
919 }
920
921
922
923
924
925 public LaneBasedGTUCharacteristicsGenerator getCharachteristicsGenerator()
926 {
927 return this.charachteristicsGenerator;
928 }
929
930 }
931
932 }