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