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