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
42
43
44
45
46
47
48
49
50
51
52
53 public final class GeneratorPositions implements Locatable
54 {
55
56
57 private final GeneratorZonePosition position;
58
59
60 private final LaneBiases biases;
61
62
63 private final StreamInterface stream;
64
65
66 private final DirectedPoint location;
67
68
69 private final Bounds bounds;
70
71
72 private final Set<GeneratorLanePosition> allPositions = new LinkedHashSet<>();
73
74
75
76
77
78
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
125
126
127
128
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
137
138
139
140
141
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
151
152
153
154
155
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
165
166
167
168
169
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
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
187 List<GeneratorLinkPosition> linkPositions = new ArrayList<>();
188 for (LinkDirection linkDirection : linkSplit.keySet())
189 {
190 List<Lane> lanes = ((CrossSectionLink) linkDirection.getLink()).getLanes();
191
192 Collections.sort(lanes, new Comparator<Lane>()
193 {
194
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
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
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
229 return new GeneratorPositions(new GeneratorZonePosition(linkPositions), biases, stream);
230
231 }
232
233
234
235
236
237
238
239
240
241
242
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
251 @Override
252 public DirectedPoint getLocation() throws RemoteException
253 {
254 return this.location;
255 }
256
257
258 @Override
259 public Bounds getBounds() throws RemoteException
260 {
261 return this.bounds;
262 }
263
264
265
266
267
268 public Set<GeneratorLanePosition> getAllPositions()
269 {
270 return this.allPositions;
271 }
272
273
274
275
276
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
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
306
307
308
309
310
311
312
313
314
315
316 public static final class GeneratorLanePosition
317 {
318
319
320 private final int laneNumber;
321
322
323 private final Set<DirectedLanePosition> position;
324
325
326 private final CrossSectionLink link;
327
328
329
330
331
332
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
343
344
345 int getLaneNumber()
346 {
347 return this.laneNumber;
348 }
349
350
351
352
353
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
369
370
371 Set<DirectedLanePosition> getPosition()
372 {
373 return this.position;
374 }
375
376
377
378
379
380 CrossSectionLink getLink()
381 {
382 return this.link;
383 }
384
385
386
387
388
389 GTUDirectionality getDirection()
390 {
391 return this.position.iterator().next().getGtuDirection();
392 }
393
394
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
406
407
408
409
410
411
412
413
414
415
416 private static final class GeneratorLinkPosition
417 {
418
419
420 private final List<GeneratorLanePosition> positions;
421
422
423 private final CrossSectionLink link;
424
425
426 private final double weight;
427
428
429
430
431
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
442
443
444
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
455
456
457 CrossSectionLink getLink()
458 {
459 return this.link;
460 }
461
462
463
464
465
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
478
479
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
496
497
498
499
500
501
502
503
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
510
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
529 }
530 type = type.getParent();
531 }
532 if (!found)
533 {
534 map.put(lanePosition, 1.0);
535
536 }
537
538 }
539 }
540 return Draw.drawWeighted(map, stream);
541
542
543
544
545
546
547
548
549
550 }
551
552
553
554
555
556 GTUDirectionality getDirection()
557 {
558 return this.positions.get(0).getDirection();
559 }
560
561
562 @Override
563 public String toString()
564 {
565 return "GeneratorLinkPosition [positions=" + this.positions + "]";
566 }
567
568 }
569
570
571
572
573
574
575
576
577
578
579
580
581
582 private static final class GeneratorZonePosition
583 {
584
585
586 private final List<GeneratorLinkPosition> positions;
587
588
589
590
591
592 GeneratorZonePosition(final List<GeneratorLinkPosition> positions)
593 {
594 this.positions = positions;
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608
609
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
616
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
629 }
630 }
631 else
632 {
633 map.put(this.positions.get(i), this.positions.get(i).getWeight(gtuType));
634
635 }
636
637 }
638
639 GeneratorLinkPosition linkPosition = Draw.drawWeighted(map, stream);
640 return linkPosition.draw(gtuType, stream, biases, unplaced.get(linkPosition.getLink()), desiredSpeed);
641
642
643
644
645
646
647
648
649
650
651
652
653 }
654
655
656 @Override
657 public String toString()
658 {
659 return "GeneratorZonePosition [positions=" + this.positions + "]";
660 }
661
662 }
663
664
665
666
667
668
669
670
671
672
673
674
675
676 public static final class LaneBiases
677 {
678
679
680 private final Map<GTUType, LaneBias> biases = new LinkedHashMap<>();
681
682
683
684
685
686
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
698
699
700
701 public boolean contains(final GTUType gtuType)
702 {
703 return this.biases.containsKey(gtuType);
704 }
705
706
707
708
709
710
711 public LaneBias getBias(final GTUType gtuType)
712 {
713 return this.biases.getOrDefault(gtuType, LaneBias.NONE);
714 }
715
716
717 @Override
718 public String toString()
719 {
720 return "LaneBiases [" + this.biases + "]";
721 }
722
723 }
724
725
726
727
728
729
730
731
732
733
734
735
736
737 public static final class LaneBiasDefaults
738 {
739
740 private final EnumMap<GTUType.DEFAULTS, LaneBias> biases = new EnumMap<>(GTUType.DEFAULTS.class);
741
742
743
744
745
746
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
758
759
760
761 public boolean contains(final GTUType.DEFAULTS gtuEnum)
762 {
763 return this.biases.containsKey(gtuEnum);
764 }
765
766
767
768
769
770
771 public LaneBias getBias(final GTUType.DEFAULTS gtuEnum)
772 {
773 return this.biases.getOrDefault(gtuEnum, LaneBias.NONE);
774 }
775
776
777 @Override
778 public String toString()
779 {
780 return "LaneBiases [" + this.biases + "]";
781 }
782 }
783
784
785
786
787
788
789
790
791
792
793
794
795
796 public static final class LaneBias
797 {
798
799
800 public static final LaneBias NONE = new LaneBias(new ByValue(0.0), 0.0, Integer.MAX_VALUE);
801
802
803 public static final LaneBias WEAK_LEFT = new LaneBias(new ByValue(1.0), 1.0, Integer.MAX_VALUE);
804
805
806 public static final LaneBias LEFT = new LaneBias(new ByValue(1.0), 2.0, Integer.MAX_VALUE);
807
808
809 public static final LaneBias STRONG_LEFT = new LaneBias(new ByValue(1.0), 5.0, Integer.MAX_VALUE);
810
811
812 public static final LaneBias WEAK_MIDDLE = new LaneBias(new ByValue(0.5), 1.0, Integer.MAX_VALUE);
813
814
815 public static final LaneBias MIDDLE = new LaneBias(new ByValue(0.5), 2.0, Integer.MAX_VALUE);
816
817
818 public static final LaneBias STRONG_MIDDLE = new LaneBias(new ByValue(0.5), 5.0, Integer.MAX_VALUE);
819
820
821 public static final LaneBias WEAK_RIGHT = new LaneBias(new ByValue(0.0), 1.0, Integer.MAX_VALUE);
822
823
824 public static final LaneBias RIGHT = new LaneBias(new ByValue(0.0), 2.0, Integer.MAX_VALUE);
825
826
827 public static final LaneBias STRONG_RIGHT = new LaneBias(new ByValue(0.0), 5.0, Integer.MAX_VALUE);
828
829
830 public static final LaneBias TRUCK_RIGHT = new LaneBias(new ByValue(0.0), 5.0, 2);
831
832
833
834
835
836
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
845
846
847
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
855 private final RoadPosition roadPosition;
856
857
858 private final double bias;
859
860
861 private final double stickyLanes;
862
863
864
865
866
867
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
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
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
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
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
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
989
990
991
992
993
994
995
996
997
998
999 public interface RoadPosition
1000 {
1001
1002
1003
1004
1005
1006
1007 double getValue(Speed desiredSpeed);
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021 public class ByValue implements RoadPosition
1022 {
1023
1024
1025 private double value;
1026
1027
1028
1029
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
1039 @Override
1040 public double getValue(final Speed desiredSpeed)
1041 {
1042 return this.value;
1043 }
1044
1045
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
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
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095 public class BySpeed implements RoadPosition
1096 {
1097
1098
1099 private Speed leftSpeed;
1100
1101
1102 private Speed rightSpeed;
1103
1104
1105
1106
1107
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
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
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
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 }