1 package org.opentrafficsim.road.gtu.generator;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
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.Objects;
11 import java.util.Set;
12
13 import org.djunits.unit.SpeedUnit;
14 import org.djunits.value.vdouble.scalar.Length;
15 import org.djunits.value.vdouble.scalar.Speed;
16 import org.djutils.exceptions.Throw;
17 import org.opentrafficsim.core.gtu.GtuException;
18 import org.opentrafficsim.core.gtu.GtuType;
19 import org.opentrafficsim.core.math.Draw;
20 import org.opentrafficsim.core.network.Link;
21 import org.opentrafficsim.core.network.NetworkException;
22 import org.opentrafficsim.core.network.Node;
23 import org.opentrafficsim.core.network.route.Route;
24 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.BySpeed;
25 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.ByValue;
26 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
27 import org.opentrafficsim.road.network.lane.CrossSectionLink;
28 import org.opentrafficsim.road.network.lane.Lane;
29 import org.opentrafficsim.road.network.lane.LanePosition;
30
31 import nl.tudelft.simulation.jstats.streams.StreamInterface;
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public interface GeneratorPositions
46 {
47
48
49
50
51
52
53
54
55
56
57 GeneratorLanePosition draw(GtuType gtuType, LaneBasedGtuCharacteristics characteristics,
58 Map<CrossSectionLink, Map<Integer, Integer>> unplaced) throws GtuException;
59
60
61
62
63
64 Set<GeneratorLanePosition> getAllPositions();
65
66
67
68
69
70
71
72
73 static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream)
74 {
75 return create(positions, stream, null, null, null);
76 }
77
78
79
80
81
82
83
84
85
86 static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream, final LaneBiases biases)
87 {
88 return create(positions, stream, biases, null, null);
89 }
90
91
92
93
94
95
96
97
98
99
100 static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream,
101 final Map<CrossSectionLink, Double> linkWeights, final Map<CrossSectionLink, Node> viaNodes)
102 {
103 return create(positions, stream, null, linkWeights, viaNodes);
104 }
105
106
107
108
109
110
111
112
113
114
115 static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream,
116 final LaneBiases laneBiases, final Map<CrossSectionLink, Double> linkWeights,
117 final Map<CrossSectionLink, Node> viaNodes)
118 {
119
120
121 Map<Link, Set<LanePosition>> linkSplit = new LinkedHashMap<>();
122 for (LanePosition position : positions)
123 {
124 linkSplit.computeIfAbsent(position.getLane().getParentLink(), (link) -> new LinkedHashSet<>()).add(position);
125 }
126
127
128 List<GeneratorLinkPosition> linkPositions = new ArrayList<>();
129 Set<GeneratorLanePosition> allLanePositions = new LinkedHashSet<>();
130 for (Link splitLink : linkSplit.keySet())
131 {
132 List<Lane> lanes = ((CrossSectionLink) splitLink).getLanes();
133
134 Collections.sort(lanes, new Comparator<Lane>()
135 {
136
137 @Override
138 public int compare(final Lane lane1, final Lane lane2)
139 {
140 Length lat1 = lane1.getDesignLineOffsetAtBegin();
141 Length lat2 = lane2.getDesignLineOffsetAtBegin();
142 return lat1.compareTo(lat2);
143 }
144 });
145
146 List<GeneratorLanePosition> lanePositions = new ArrayList<>();
147 for (LanePosition lanePosition : linkSplit.get(splitLink))
148 {
149 Set<LanePosition> set = new LinkedHashSet<>();
150 set.add(lanePosition);
151 lanePositions.add(new GeneratorLanePosition(lanes.indexOf(lanePosition.getLane()) + 1, set,
152 (CrossSectionLink) splitLink));
153 }
154 allLanePositions.addAll(lanePositions);
155
156 if (linkWeights == null)
157 {
158 linkPositions.add(new GeneratorLinkPosition(lanePositions, splitLink, stream, laneBiases));
159 }
160 else
161 {
162 Double weight = linkWeights.get(splitLink);
163 Throw.whenNull(weight, "Using link weights for GTU generation, but no weight for link %s is defined.",
164 splitLink);
165 linkPositions.add(new GeneratorLinkPosition(lanePositions, splitLink, stream, laneBiases, weight,
166 viaNodes.get(splitLink)));
167 }
168 }
169
170
171 GeneratorZonePosition position = new GeneratorZonePosition(linkPositions);
172 return new GeneratorPositions()
173 {
174
175 @Override
176 public GeneratorLanePosition draw(final GtuType gtuType, final LaneBasedGtuCharacteristics characteristics,
177 final Map<CrossSectionLink, Map<Integer, Integer>> unplaced) throws GtuException
178 {
179 GeneratorLinkPosition linkPosition =
180 position.draw(gtuType, stream, characteristics.getDestination(), characteristics.getRoute());
181 Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
182 linkPosition.speedLimit(gtuType), characteristics.getMaximumSpeed());
183 return linkPosition.draw(gtuType, unplaced.get(linkPosition.getLink()), desiredSpeed);
184 }
185
186
187 @Override
188 public Set<GeneratorLanePosition> getAllPositions()
189 {
190 return allLanePositions;
191 }
192 };
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206 final class GeneratorLanePosition
207 {
208
209
210 private final int laneNumber;
211
212
213 private final Set<LanePosition> position;
214
215
216 private final CrossSectionLink link;
217
218
219
220
221
222
223
224 GeneratorLanePosition(final int laneNumber, final Set<LanePosition> position, final CrossSectionLink link)
225 {
226 this.laneNumber = laneNumber;
227 this.position = position;
228 this.link = link;
229 }
230
231
232
233
234
235 int getLaneNumber()
236 {
237 return this.laneNumber;
238 }
239
240
241
242
243
244
245 boolean allows(final GtuType gtuType)
246 {
247 for (LanePosition pos : this.position)
248 {
249 if (pos.getLane().getType().isCompatible(gtuType))
250 {
251 return true;
252 }
253 }
254 return false;
255 }
256
257
258
259
260
261 Set<LanePosition> getPosition()
262 {
263 return this.position;
264 }
265
266
267
268
269
270 CrossSectionLink getLink()
271 {
272 return this.link;
273 }
274
275
276 @Override
277 public int hashCode()
278 {
279 return Objects.hash(this.laneNumber, this.link, this.position);
280 }
281
282
283 @Override
284 public boolean equals(final Object obj)
285 {
286 if (this == obj)
287 {
288 return true;
289 }
290 if (obj == null)
291 {
292 return false;
293 }
294 if (getClass() != obj.getClass())
295 {
296 return false;
297 }
298 GeneratorLanePosition other = (GeneratorLanePosition) obj;
299 return this.laneNumber == other.laneNumber && Objects.equals(this.link, other.link)
300 && Objects.equals(this.position, other.position);
301 }
302
303
304 @Override
305 public String toString()
306 {
307 return "GeneratorLanePosition [laneNumber=" + this.laneNumber + ", position=" + this.position + ", link="
308 + this.link + "]";
309 }
310
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324 final class GeneratorLinkPosition
325 {
326
327
328 private final List<GeneratorLanePosition> positions;
329
330
331 private final Link link;
332
333
334 private final StreamInterface stream;
335
336
337 private final LaneBiases laneBiases;
338
339
340 private final double weight;
341
342
343 private final Node viaNode;
344
345
346
347
348
349
350
351
352 GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final Link link, final StreamInterface stream,
353 final LaneBiases laneBiases)
354 {
355 this.positions = positions;
356 this.link = link;
357 this.stream = stream;
358 this.laneBiases = laneBiases;
359 this.weight = -1;
360 this.viaNode = null;
361 }
362
363
364
365
366
367
368
369
370
371
372 GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final Link link, final StreamInterface stream,
373 final LaneBiases laneBiases, final double weight, final Node viaNode)
374 {
375 this.positions = positions;
376 this.link = link;
377 this.stream = stream;
378 this.laneBiases = laneBiases;
379 this.weight = weight;
380 this.viaNode = viaNode;
381 }
382
383
384
385
386
387 Link getLink()
388 {
389 return this.link;
390 }
391
392
393
394
395
396
397 double getWeight(final GtuType gtuType)
398 {
399 if (this.weight < 0.0)
400 {
401 return getNumberOfLanes(gtuType);
402 }
403 return this.weight;
404 }
405
406
407
408
409
410 Node getViaNode()
411 {
412 return this.viaNode;
413 }
414
415
416
417
418
419
420 int getNumberOfLanes(final GtuType gtuType)
421 {
422 int numberOfLanes = 0;
423 for (GeneratorLanePosition lanePosition : this.positions)
424 {
425 if (lanePosition.allows(gtuType))
426 {
427 numberOfLanes++;
428 }
429 }
430 return numberOfLanes;
431 }
432
433
434
435
436
437
438
439
440
441
442 GeneratorLanePosition draw(final GtuType gtuType, final Map<Integer, Integer> unplaced, final Speed desiredSpeed)
443 {
444 Map<GeneratorLanePosition, Double> map = new LinkedHashMap<>();
445 for (int i = 0; i < this.positions.size(); i++)
446 {
447 GeneratorLanePosition lanePosition = this.positions.get(i);
448 if (lanePosition.allows(gtuType))
449 {
450 GtuType type = gtuType;
451 boolean found = false;
452 while (this.laneBiases != null && !found && type != null)
453 {
454 if (this.laneBiases.contains(type))
455 {
456 found = true;
457 int laneNum = lanePosition.getLaneNumber();
458 int unplacedTemplates = unplaced == null ? 0 : unplaced.getOrDefault(laneNum, 0);
459 double w = this.laneBiases.getBias(type).calculateWeight(laneNum, getNumberOfLanes(gtuType),
460 unplacedTemplates, desiredSpeed);
461 map.put(lanePosition, w);
462 }
463 type = type.getParent();
464 }
465 if (!found)
466 {
467 map.put(lanePosition, 1.0);
468 }
469 }
470 }
471 if (0 == map.size())
472 {
473 System.err.println("This really, really can't work...");
474 }
475 return Draw.drawWeighted(map, this.stream);
476 }
477
478
479 @Override
480 public String toString()
481 {
482 return "GeneratorLinkPosition [positions=" + this.positions + "]";
483 }
484
485
486
487
488
489 public Speed speedLimit(final GtuType gtuType)
490 {
491 Speed speedLimit = null;
492 for (GeneratorLanePosition pos : this.positions)
493 {
494 for (LanePosition lane : pos.getPosition())
495 {
496 try
497 {
498 Speed limit = lane.getLane().getSpeedLimit(gtuType);
499 if (speedLimit == null || limit.lt(speedLimit))
500 {
501 speedLimit = limit;
502 }
503 }
504 catch (NetworkException exception)
505 {
506
507 }
508 }
509 }
510 Throw.when(speedLimit == null, IllegalStateException.class, "No speed limit could be determined for GtuType %s.",
511 gtuType);
512 return speedLimit;
513 }
514
515 }
516
517
518
519
520
521
522
523
524
525
526
527
528 final class GeneratorZonePosition
529 {
530
531
532 private final List<GeneratorLinkPosition> positions;
533
534
535
536
537
538 GeneratorZonePosition(final List<GeneratorLinkPosition> positions)
539 {
540 this.positions = positions;
541 }
542
543
544
545
546
547
548
549
550
551
552
553 GeneratorLinkPosition draw(final GtuType gtuType, final StreamInterface stream, final Node destination,
554 final Route route)
555 {
556 Map<GeneratorLinkPosition, Double> map = new LinkedHashMap<>();
557 for (int i = 0; i < this.positions.size(); i++)
558 {
559 GeneratorLinkPosition glp = this.positions.get(i);
560 Link link = glp.getLink();
561 if (route != null)
562 {
563 int from = route.indexOf(link.getStartNode());
564 int to = route.indexOf(link.getEndNode());
565 if (from > -1 && to > -1 && to - from == 1)
566 {
567 map.put(glp, glp.getWeight(gtuType));
568 }
569 }
570 else
571 {
572
573 if (glp.getViaNode() != null)
574 {
575 Route r;
576 try
577 {
578 r = glp.getViaNode().getNetwork().getShortestRouteBetween(gtuType, glp.getViaNode(), destination);
579 }
580 catch (NetworkException exception)
581 {
582 r = null;
583 }
584 if (r != null)
585 {
586 map.put(glp, glp.getWeight(gtuType));
587 }
588 }
589 else
590 {
591 map.put(glp, glp.getWeight(gtuType));
592 }
593 }
594 }
595 return Draw.drawWeighted(map, stream);
596 }
597
598
599 @Override
600 public String toString()
601 {
602 return "GeneratorZonePosition [positions=" + this.positions + "]";
603 }
604
605 }
606
607
608
609
610
611
612
613
614
615
616
617
618 final class LaneBiases
619 {
620
621
622 private final Map<GtuType, LaneBias> biases = new LinkedHashMap<>();
623
624
625
626
627
628
629
630 public LaneBiases addBias(final GtuType gtuType, final LaneBias bias)
631 {
632 Throw.whenNull(gtuType, "GTU type may not be null.");
633 Throw.whenNull(bias, "Bias may not be null.");
634 this.biases.put(gtuType, bias);
635 return this;
636 }
637
638
639
640
641
642
643 public boolean contains(final GtuType gtuType)
644 {
645 return this.biases.containsKey(gtuType);
646 }
647
648
649
650
651
652
653 public LaneBias getBias(final GtuType gtuType)
654 {
655 return this.biases.getOrDefault(gtuType, LaneBias.NONE);
656 }
657
658
659 @Override
660 public String toString()
661 {
662 return "LaneBiases [" + this.biases + "]";
663 }
664
665 }
666
667
668
669
670
671
672
673
674
675
676
677
678 final class LaneBias
679 {
680
681
682 public static final LaneBias NONE = new LaneBias(new ByValue(0.0), 0.0, Integer.MAX_VALUE);
683
684
685 public static final LaneBias WEAK_LEFT = new LaneBias(new ByValue(1.0), 1.0, Integer.MAX_VALUE);
686
687
688 public static final LaneBias LEFT = new LaneBias(new ByValue(1.0), 2.0, Integer.MAX_VALUE);
689
690
691 public static final LaneBias STRONG_LEFT = new LaneBias(new ByValue(1.0), 5.0, Integer.MAX_VALUE);
692
693
694 public static final LaneBias WEAK_MIDDLE = new LaneBias(new ByValue(0.5), 1.0, Integer.MAX_VALUE);
695
696
697 public static final LaneBias MIDDLE = new LaneBias(new ByValue(0.5), 2.0, Integer.MAX_VALUE);
698
699
700 public static final LaneBias STRONG_MIDDLE = new LaneBias(new ByValue(0.5), 5.0, Integer.MAX_VALUE);
701
702
703 public static final LaneBias WEAK_RIGHT = new LaneBias(new ByValue(0.0), 1.0, Integer.MAX_VALUE);
704
705
706 public static final LaneBias RIGHT = new LaneBias(new ByValue(0.0), 2.0, Integer.MAX_VALUE);
707
708
709 public static final LaneBias STRONG_RIGHT = new LaneBias(new ByValue(0.0), 5.0, Integer.MAX_VALUE);
710
711
712 public static final LaneBias TRUCK_RIGHT = new LaneBias(new ByValue(0.0), 5.0, 2);
713
714
715
716
717
718
719
720 public static LaneBias bySpeed(final Speed leftSpeed, final Speed rightSpeed)
721 {
722 return new LaneBias(new BySpeed(leftSpeed, rightSpeed), 2.0, Integer.MAX_VALUE);
723 }
724
725
726
727
728
729
730
731 public static LaneBias bySpeed(final double leftSpeedKm, final double rightSpeedKm)
732 {
733 return bySpeed(new Speed(leftSpeedKm, SpeedUnit.KM_PER_HOUR), new Speed(rightSpeedKm, SpeedUnit.KM_PER_HOUR));
734 }
735
736
737 private final RoadPosition roadPosition;
738
739
740 private final double bias;
741
742
743 private final double stickyLanes;
744
745
746
747
748
749
750
751 public LaneBias(final RoadPosition roadPosition, final double bias, final double stickyLanes)
752 {
753 Throw.when(bias < 0.0, IllegalArgumentException.class, "Bias should be positive or 0.");
754 Throw.when(stickyLanes < 1.0, IllegalArgumentException.class, "Sticky lanes should be 1.0 or larger.");
755 this.roadPosition = roadPosition;
756 this.bias = bias;
757 this.stickyLanes = stickyLanes;
758 }
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794 public double calculateWeight(final int laneNumFromRight, final int numberOfLanes, final int numberOfUnplacedGTUs,
795 final Speed desiredSpeed)
796 {
797 double d = Math.abs((1.0 + this.roadPosition.getValue(desiredSpeed) * (numberOfLanes - 1.0)) - laneNumFromRight);
798 if (d >= this.stickyLanes)
799 {
800 return 0.0;
801 }
802 return 1.0 / (Math.pow(d + 1.0, this.bias) * (numberOfUnplacedGTUs + 1.0));
803 }
804
805
806 @Override
807 public int hashCode()
808 {
809 final int prime = 31;
810 int result = 1;
811 long temp;
812 temp = Double.doubleToLongBits(this.bias);
813 result = prime * result + (int) (temp ^ (temp >>> 32));
814 result = prime * result + ((this.roadPosition == null) ? 0 : this.roadPosition.hashCode());
815 temp = Double.doubleToLongBits(this.stickyLanes);
816 result = prime * result + (int) (temp ^ (temp >>> 32));
817 return result;
818 }
819
820
821 @Override
822 public boolean equals(final Object obj)
823 {
824 if (this == obj)
825 {
826 return true;
827 }
828 if (obj == null)
829 {
830 return false;
831 }
832 if (getClass() != obj.getClass())
833 {
834 return false;
835 }
836 LaneBias other = (LaneBias) obj;
837 if (Double.doubleToLongBits(this.bias) != Double.doubleToLongBits(other.bias))
838 {
839 return false;
840 }
841 if (this.roadPosition == null)
842 {
843 if (other.roadPosition != null)
844 {
845 return false;
846 }
847 }
848 else if (!this.roadPosition.equals(other.roadPosition))
849 {
850 return false;
851 }
852 if (Double.doubleToLongBits(this.stickyLanes) != Double.doubleToLongBits(other.stickyLanes))
853 {
854 return false;
855 }
856 return true;
857 }
858
859
860 @Override
861 public String toString()
862 {
863 return "Bias [roadPosition=" + this.roadPosition + ", bias=" + this.bias + ", stickyLanes=" + this.stickyLanes
864 + "]";
865 }
866
867 }
868
869
870
871
872
873
874
875
876
877
878
879
880 public interface RoadPosition
881 {
882
883
884
885
886
887
888 double getValue(Speed desiredSpeed);
889
890
891
892
893
894
895
896
897
898
899
900
901 class ByValue implements RoadPosition
902 {
903
904
905 private double value;
906
907
908
909
910
911 public ByValue(final double value)
912 {
913 Throw.when(value < 0.0 || value > 1.0, IllegalArgumentException.class,
914 "Road position value should be in the range [0...1].");
915 this.value = value;
916 }
917
918
919 @Override
920 public double getValue(final Speed desiredSpeed)
921 {
922 return this.value;
923 }
924
925
926 @Override
927 public int hashCode()
928 {
929 final int prime = 31;
930 int result = 1;
931 long temp;
932 temp = Double.doubleToLongBits(this.value);
933 result = prime * result + (int) (temp ^ (temp >>> 32));
934 return result;
935 }
936
937
938 @Override
939 public boolean equals(final Object obj)
940 {
941 if (this == obj)
942 {
943 return true;
944 }
945 if (obj == null)
946 {
947 return false;
948 }
949 if (getClass() != obj.getClass())
950 {
951 return false;
952 }
953 ByValue other = (ByValue) obj;
954 if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value))
955 {
956 return false;
957 }
958 return true;
959 }
960
961 }
962
963
964
965
966
967
968
969
970
971
972
973
974 class BySpeed implements RoadPosition
975 {
976
977
978 private Speed leftSpeed;
979
980
981 private Speed rightSpeed;
982
983
984
985
986
987
988 public BySpeed(final Speed leftSpeed, final Speed rightSpeed)
989 {
990 Throw.when(leftSpeed.eq(rightSpeed), IllegalArgumentException.class,
991 "Left speed and right speed may not be equal. Use LaneBias.NONE.");
992 this.leftSpeed = leftSpeed;
993 this.rightSpeed = rightSpeed;
994 }
995
996
997 @Override
998 public double getValue(final Speed desiredSpeed)
999 {
1000 Throw.whenNull(desiredSpeed, "Peeked desired speed from a strategical planner factory is null, "
1001 + "while a lane bias depends on desired speed.");
1002 double value = (desiredSpeed.si - this.rightSpeed.si) / (this.leftSpeed.si - this.rightSpeed.si);
1003 return value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
1004 }
1005
1006
1007 @Override
1008 public int hashCode()
1009 {
1010 final int prime = 31;
1011 int result = 1;
1012 result = prime * result + ((this.leftSpeed == null) ? 0 : this.leftSpeed.hashCode());
1013 result = prime * result + ((this.rightSpeed == null) ? 0 : this.rightSpeed.hashCode());
1014 return result;
1015 }
1016
1017
1018 @Override
1019 public boolean equals(final Object obj)
1020 {
1021 if (this == obj)
1022 {
1023 return true;
1024 }
1025 if (obj == null)
1026 {
1027 return false;
1028 }
1029 if (getClass() != obj.getClass())
1030 {
1031 return false;
1032 }
1033 BySpeed other = (BySpeed) obj;
1034 if (this.leftSpeed == null)
1035 {
1036 if (other.leftSpeed != null)
1037 {
1038 return false;
1039 }
1040 }
1041 else if (!this.leftSpeed.equals(other.leftSpeed))
1042 {
1043 return false;
1044 }
1045 if (this.rightSpeed == null)
1046 {
1047 if (other.rightSpeed != null)
1048 {
1049 return false;
1050 }
1051 }
1052 else if (!this.rightSpeed.equals(other.rightSpeed))
1053 {
1054 return false;
1055 }
1056 return true;
1057 }
1058
1059 }
1060
1061 }
1062
1063 }