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 for (GeneratorLinkPosition glp : this.position.positions)
85 {
86 if (glp.getDirection().equals(GTUDirectionality.DIR_MINUS))
87 {
88 System.out.println("Hmm... GTUDirectionality is DIR_MINUS: " + glp);
89 }
90 }
91 this.biases = biases;
92 this.stream = stream;
93 double x = 0.0;
94 double y = 0.0;
95 double xMin = Double.POSITIVE_INFINITY;
96 double xMax = Double.NEGATIVE_INFINITY;
97 double yMin = Double.POSITIVE_INFINITY;
98 double yMax = Double.NEGATIVE_INFINITY;
99 int n = 0;
100 for (GeneratorLinkPosition linkPosition : position.positions)
101 {
102 for (GeneratorLanePosition lanePosition : linkPosition.positions)
103 {
104 this.allPositions.add(lanePosition);
105 for (DirectedLanePosition pos : lanePosition.getPosition())
106 {
107 DirectedPoint point;
108 try
109 {
110 point = pos.getLane().getCenterLine().getLocation(pos.getPosition());
111 }
112 catch (OTSGeometryException exception)
113 {
114 point = new DirectedPoint(0, 0, 0);
115 }
116 x += point.x;
117 y += point.y;
118 xMin = xMin < point.x ? xMin : point.x;
119 yMin = yMin < point.y ? yMin : point.y;
120 xMax = xMax > point.x ? xMax : point.x;
121 yMax = yMax > point.y ? yMax : point.y;
122 n++;
123 }
124 }
125 }
126 this.location = new DirectedPoint(x / n, y / n, 0);
127 this.bounds = new BoundingBox(new Point3d(xMin, yMin, 0.0), new Point3d(xMax, yMax, 0.0));
128 }
129
130
131
132
133
134
135
136
137 public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream)
138 {
139 return create(positions, stream, null, null);
140 }
141
142
143
144
145
146
147
148
149
150 public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream,
151 final LaneBiases biases)
152 {
153 return create(positions, stream, biases, null);
154 }
155
156
157
158
159
160
161
162
163
164 public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream,
165 final Map<CrossSectionLink, Double> linkWeights)
166 {
167 return create(positions, stream, null, linkWeights);
168 }
169
170
171
172
173
174
175
176
177
178 public static GeneratorPositions create(final Set<DirectedLanePosition> positions, final StreamInterface stream,
179 final LaneBiases biases, final Map<CrossSectionLink, Double> linkWeights)
180 {
181
182
183 Map<LinkDirection, Set<DirectedLanePosition>> linkSplit = new LinkedHashMap<>();
184 for (DirectedLanePosition position : positions)
185 {
186 if (!linkSplit.containsKey(position.getLinkDirection()))
187 {
188 linkSplit.put(position.getLinkDirection(), new LinkedHashSet<>());
189 }
190 linkSplit.get(position.getLinkDirection()).add(position);
191 }
192
193
194 List<GeneratorLinkPosition> linkPositions = new ArrayList<>();
195 for (LinkDirection linkDirection : linkSplit.keySet())
196 {
197 List<Lane> lanes = ((CrossSectionLink) linkDirection.getLink()).getLanes();
198
199 Collections.sort(lanes, new Comparator<Lane>()
200 {
201
202 @Override
203 public int compare(final Lane/network/lane/Lane.html#Lane">Lane lane1, final Lane lane2)
204 {
205 Length lat1 = linkDirection.getDirection().isPlus() ? lane1.getDesignLineOffsetAtBegin()
206 : lane1.getDesignLineOffsetAtEnd().neg();
207 Length lat2 = linkDirection.getDirection().isPlus() ? lane2.getDesignLineOffsetAtBegin()
208 : lane2.getDesignLineOffsetAtEnd().neg();
209 return lat1.compareTo(lat2);
210 }
211 });
212
213 List<GeneratorLanePosition> lanePositions = new ArrayList<>();
214 for (DirectedLanePosition lanePosition : linkSplit.get(linkDirection))
215 {
216 Set<DirectedLanePosition> set = new LinkedHashSet<>();
217 set.add(lanePosition);
218 lanePositions.add(new GeneratorLanePosition(lanes.indexOf(lanePosition.getLane()) + 1, set,
219 (CrossSectionLink) linkDirection.getLink()));
220 }
221
222 CrossSectionLink/../../org/opentrafficsim/road/network/lane/CrossSectionLink.html#CrossSectionLink">CrossSectionLink link = (CrossSectionLink) linkDirection.getLink();
223 if (linkWeights == null)
224 {
225 linkPositions.add(new GeneratorLinkPosition(lanePositions, link));
226 }
227 else
228 {
229 Double weight = linkWeights.get(link);
230 Throw.whenNull(weight, "Using link weights for GTU generation, but no weight for link %s is defined.", link);
231 linkPositions.add(new GeneratorLinkPosition(lanePositions, link, weight));
232 }
233 }
234
235
236 return new GeneratorPositions(new GeneratorZonePosition(linkPositions), biases, stream);
237
238 }
239
240
241
242
243
244
245
246
247
248
249
250
251 public GeneratorLanePosition draw(final GTUType gtuType, final Map<CrossSectionLink, Map<Integer, Integer>> unplaced,
252 final Speed desiredSpeed, final Route route)
253 {
254 return this.position.draw(gtuType, this.stream, this.biases, unplaced, desiredSpeed, route);
255 }
256
257
258 @Override
259 public DirectedPoint getLocation() throws RemoteException
260 {
261 return this.location;
262 }
263
264
265 @Override
266 public Bounds getBounds() throws RemoteException
267 {
268 return this.bounds;
269 }
270
271
272
273
274
275 public Set<GeneratorLanePosition> getAllPositions()
276 {
277 return this.allPositions;
278 }
279
280
281
282
283
284
285 public Speed speedLimit(final GTUType gtuType)
286 {
287 Speed speedLimit = null;
288 for (GeneratorLanePosition pos : this.allPositions)
289 {
290 for (DirectedLanePosition lane : pos.getPosition())
291 {
292 try
293 {
294 Speed limit = lane.getLane().getSpeedLimit(gtuType);
295 if (speedLimit == null || limit.lt(speedLimit))
296 {
297 speedLimit = limit;
298 }
299 }
300 catch (NetworkException exception)
301 {
302
303 }
304 }
305 }
306 Throw.when(speedLimit == null, IllegalStateException.class, "No speed limit could be determined for GTUType %s.",
307 gtuType);
308 return speedLimit;
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322
323 public static final class GeneratorLanePosition
324 {
325
326
327 private final int laneNumber;
328
329
330 private final Set<DirectedLanePosition> position;
331
332
333 private final CrossSectionLink link;
334
335
336
337
338
339
340
341 GeneratorLanePosition(final int laneNumber, final Set<DirectedLanePosition> position, final CrossSectionLink link)
342 {
343 this.laneNumber = laneNumber;
344 this.position = position;
345 this.link = link;
346 }
347
348
349
350
351
352 int getLaneNumber()
353 {
354 return this.laneNumber;
355 }
356
357
358
359
360
361
362 boolean allows(final GTUType gtuType)
363 {
364 for (DirectedLanePosition pos : this.position)
365 {
366 if (pos.getLane().getLaneType().isCompatible(gtuType, pos.getGtuDirection()))
367 {
368 return true;
369 }
370 }
371 return false;
372 }
373
374
375
376
377
378 Set<DirectedLanePosition> getPosition()
379 {
380 return this.position;
381 }
382
383
384
385
386
387 CrossSectionLink getLink()
388 {
389 return this.link;
390 }
391
392
393
394
395
396 GTUDirectionality getDirection()
397 {
398 return this.position.iterator().next().getGtuDirection();
399 }
400
401
402 @Override
403 public String toString()
404 {
405 return "GeneratorLanePosition [laneNumber=" + this.laneNumber + ", position=" + this.position + ", link="
406 + this.link + "]";
407 }
408
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422
423 private static final class GeneratorLinkPosition
424 {
425
426
427 private final List<GeneratorLanePosition> positions;
428
429
430 private final CrossSectionLink link;
431
432
433 private final double weight;
434
435
436
437
438
439
440 GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final CrossSectionLink link)
441 {
442 this.positions = positions;
443 this.link = link;
444 this.weight = -1;
445 }
446
447
448
449
450
451
452
453 GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final CrossSectionLink link, final double weight)
454 {
455 this.positions = positions;
456 this.link = link;
457 this.weight = weight;
458 }
459
460
461
462
463
464 CrossSectionLink getLink()
465 {
466 return this.link;
467 }
468
469
470
471
472
473
474 double getWeight(final GTUType gtuType)
475 {
476 if (this.weight < 0.0)
477 {
478 return getNumberOfLanes(gtuType);
479 }
480 return this.weight;
481 }
482
483
484
485
486
487
488 int getNumberOfLanes(final GTUType gtuType)
489 {
490 int numberOfLanes = 0;
491 for (GeneratorLanePosition lanePosition : this.positions)
492 {
493 if (lanePosition.allows(gtuType))
494 {
495 numberOfLanes++;
496 }
497 }
498 return numberOfLanes;
499 }
500
501
502
503
504
505
506
507
508
509
510
511
512 GeneratorLanePosition draw(final GTUType gtuType, final StreamInterface stream, final LaneBiases biases,
513 final Map<Integer, Integer> unplaced, final Speed desiredSpeed)
514 {
515 Map<GeneratorLanePosition, Double> map = new LinkedHashMap<>();
516
517
518 for (int i = 0; i < this.positions.size(); i++)
519 {
520 GeneratorLanePosition lanePosition = this.positions.get(i);
521 if (lanePosition.allows(gtuType))
522 {
523 GTUType type = gtuType;
524 boolean found = false;
525 while (biases != null && !found && type != null)
526 {
527 if (biases.contains(type))
528 {
529 found = true;
530 int laneNum = lanePosition.getLaneNumber();
531 int unplacedTemplates = unplaced == null ? 0 : unplaced.getOrDefault(laneNum, 0);
532 double w = biases.getBias(type).calculateWeight(laneNum, getNumberOfLanes(gtuType),
533 unplacedTemplates, desiredSpeed);
534 map.put(lanePosition, w);
535
536 }
537 type = type.getParent();
538 }
539 if (!found)
540 {
541 map.put(lanePosition, 1.0);
542
543 }
544
545 }
546 }
547 if (0 == map.size())
548 {
549 System.err.println("This really, really can't work...");
550 }
551 return Draw.drawWeighted(map, stream);
552
553
554
555
556
557
558
559
560
561 }
562
563
564
565
566
567 GTUDirectionality getDirection()
568 {
569 return this.positions.get(0).getDirection();
570 }
571
572
573 @Override
574 public String toString()
575 {
576 return "GeneratorLinkPosition [positions=" + this.positions + "]";
577 }
578
579 }
580
581
582
583
584
585
586
587
588
589
590
591
592
593 private static final class GeneratorZonePosition
594 {
595
596
597 private final List<GeneratorLinkPosition> positions;
598
599
600
601
602
603 GeneratorZonePosition(final List<GeneratorLinkPosition> positions)
604 {
605 this.positions = positions;
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622 GeneratorLanePosition draw(final GTUType gtuType, final StreamInterface stream, final LaneBiases biases,
623 final Map<CrossSectionLink, Map<Integer, Integer>> unplaced, final Speed desiredSpeed, final Route route)
624 {
625 Map<GeneratorLinkPosition, Double> map = new LinkedHashMap<>();
626
627
628 for (int i = 0; i < this.positions.size(); i++)
629 {
630 Link link = this.positions.get(i).getLink();
631 GTUDirectionality direction = this.positions.get(i).getDirection();
632 if (route != null)
633 {
634 int from = route.indexOf(direction.isPlus() ? link.getStartNode() : link.getEndNode());
635 int to = route.indexOf(direction.isPlus() ? link.getEndNode() : link.getStartNode());
636 if (from > -1 && to > -1 && to - from == 1)
637 {
638 map.put(this.positions.get(i), this.positions.get(i).getWeight(gtuType));
639
640 }
641 }
642 else
643 {
644 map.put(this.positions.get(i), this.positions.get(i).getWeight(gtuType));
645
646 }
647
648 }
649
650 GeneratorLinkPosition linkPosition = Draw.drawWeighted(map, stream);
651 return linkPosition.draw(gtuType, stream, biases, unplaced.get(linkPosition.getLink()), desiredSpeed);
652
653
654
655
656
657
658
659
660
661
662
663
664 }
665
666
667 @Override
668 public String toString()
669 {
670 return "GeneratorZonePosition [positions=" + this.positions + "]";
671 }
672
673 }
674
675
676
677
678
679
680
681
682
683
684
685
686
687 public static final class LaneBiases
688 {
689
690
691 private final Map<GTUType, LaneBias> biases = new LinkedHashMap<>();
692
693
694
695
696
697
698
699 public LaneBiases addBias(final GTUType gtuType, final LaneBias bias)
700 {
701 Throw.whenNull(gtuType, "GTU type may not be null.");
702 Throw.whenNull(bias, "Bias may not be null.");
703 this.biases.put(gtuType, bias);
704 return this;
705 }
706
707
708
709
710
711
712 public boolean contains(final GTUType gtuType)
713 {
714 return this.biases.containsKey(gtuType);
715 }
716
717
718
719
720
721
722 public LaneBias getBias(final GTUType gtuType)
723 {
724 return this.biases.getOrDefault(gtuType, LaneBias.NONE);
725 }
726
727
728 @Override
729 public String toString()
730 {
731 return "LaneBiases [" + this.biases + "]";
732 }
733
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747
748 public static final class LaneBiasDefaults
749 {
750
751 private final EnumMap<GTUType.DEFAULTS, LaneBias> biases = new EnumMap<>(GTUType.DEFAULTS.class);
752
753
754
755
756
757
758
759 public LaneBiasDefaults addBias(final GTUType.DEFAULTS gtuEnum, final LaneBias bias)
760 {
761 Throw.whenNull(gtuEnum, "GTU type enum may not be null.");
762 Throw.whenNull(bias, "Bias may not be null.");
763 this.biases.put(gtuEnum, bias);
764 return this;
765 }
766
767
768
769
770
771
772 public boolean contains(final GTUType.DEFAULTS gtuEnum)
773 {
774 return this.biases.containsKey(gtuEnum);
775 }
776
777
778
779
780
781
782 public LaneBias getBias(final GTUType.DEFAULTS gtuEnum)
783 {
784 return this.biases.getOrDefault(gtuEnum, LaneBias.NONE);
785 }
786
787
788 @Override
789 public String toString()
790 {
791 return "LaneBiases [" + this.biases + "]";
792 }
793 }
794
795
796
797
798
799
800
801
802
803
804
805
806
807 public static final class LaneBias
808 {
809
810
811 public static final LaneBias NONE = new LaneBias(new ByValue(0.0), 0.0, Integer.MAX_VALUE);
812
813
814 public static final LaneBias WEAK_LEFT = new LaneBias(new ByValue(1.0), 1.0, Integer.MAX_VALUE);
815
816
817 public static final LaneBias LEFT = new LaneBias(new ByValue(1.0), 2.0, Integer.MAX_VALUE);
818
819
820 public static final LaneBias STRONG_LEFT = new LaneBias(new ByValue(1.0), 5.0, Integer.MAX_VALUE);
821
822
823 public static final LaneBias WEAK_MIDDLE = new LaneBias(new ByValue(0.5), 1.0, Integer.MAX_VALUE);
824
825
826 public static final LaneBias MIDDLE = new LaneBias(new ByValue(0.5), 2.0, Integer.MAX_VALUE);
827
828
829 public static final LaneBias STRONG_MIDDLE = new LaneBias(new ByValue(0.5), 5.0, Integer.MAX_VALUE);
830
831
832 public static final LaneBias WEAK_RIGHT = new LaneBias(new ByValue(0.0), 1.0, Integer.MAX_VALUE);
833
834
835 public static final LaneBias RIGHT = new LaneBias(new ByValue(0.0), 2.0, Integer.MAX_VALUE);
836
837
838 public static final LaneBias STRONG_RIGHT = new LaneBias(new ByValue(0.0), 5.0, Integer.MAX_VALUE);
839
840
841 public static final LaneBias TRUCK_RIGHT = new LaneBias(new ByValue(0.0), 5.0, 2);
842
843
844
845
846
847
848
849 public static LaneBias bySpeed(final Speed leftSpeed, final Speed rightSpeed)
850 {
851 return new LaneBias(new BySpeed(leftSpeed, rightSpeed), 2.0, Integer.MAX_VALUE);
852 }
853
854
855
856
857
858
859
860 public static LaneBias bySpeed(final double leftSpeedKm, final double rightSpeedKm)
861 {
862 return bySpeed(new Speed(leftSpeedKm, SpeedUnit.KM_PER_HOUR), new Speed(rightSpeedKm, SpeedUnit.KM_PER_HOUR));
863 }
864
865
866 private final RoadPosition roadPosition;
867
868
869 private final double bias;
870
871
872 private final double stickyLanes;
873
874
875
876
877
878
879
880 public LaneBias(final RoadPosition roadPosition, final double bias, final double stickyLanes)
881 {
882 Throw.when(bias < 0.0, IllegalArgumentException.class, "Bias should be positive or 0.");
883 Throw.when(stickyLanes < 1.0, IllegalArgumentException.class, "Sticky lanes should be 1.0 or larger.");
884 this.roadPosition = roadPosition;
885 this.bias = bias;
886 this.stickyLanes = stickyLanes;
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
913
914
915
916
917
918
919
920
921
922
923 public double calculateWeight(final int laneNumFromRight, final int numberOfLanes, final int numberOfUnplacedGTUs,
924 final Speed desiredSpeed)
925 {
926 double d = Math.abs((1.0 + this.roadPosition.getValue(desiredSpeed) * (numberOfLanes - 1.0)) - laneNumFromRight);
927 if (d >= this.stickyLanes)
928 {
929 return 0.0;
930 }
931 return 1.0 / (Math.pow(d + 1.0, this.bias) * (numberOfUnplacedGTUs + 1.0));
932 }
933
934
935 @Override
936 public int hashCode()
937 {
938 final int prime = 31;
939 int result = 1;
940 long temp;
941 temp = Double.doubleToLongBits(this.bias);
942 result = prime * result + (int) (temp ^ (temp >>> 32));
943 result = prime * result + ((this.roadPosition == null) ? 0 : this.roadPosition.hashCode());
944 temp = Double.doubleToLongBits(this.stickyLanes);
945 result = prime * result + (int) (temp ^ (temp >>> 32));
946 return result;
947 }
948
949
950 @Override
951 public boolean equals(final Object obj)
952 {
953 if (this == obj)
954 {
955 return true;
956 }
957 if (obj == null)
958 {
959 return false;
960 }
961 if (getClass() != obj.getClass())
962 {
963 return false;
964 }
965 LaneBias other = (LaneBias) obj;
966 if (Double.doubleToLongBits(this.bias) != Double.doubleToLongBits(other.bias))
967 {
968 return false;
969 }
970 if (this.roadPosition == null)
971 {
972 if (other.roadPosition != null)
973 {
974 return false;
975 }
976 }
977 else if (!this.roadPosition.equals(other.roadPosition))
978 {
979 return false;
980 }
981 if (Double.doubleToLongBits(this.stickyLanes) != Double.doubleToLongBits(other.stickyLanes))
982 {
983 return false;
984 }
985 return true;
986 }
987
988
989 @Override
990 public String toString()
991 {
992 return "Bias [roadPosition=" + this.roadPosition + ", bias=" + this.bias + ", stickyLanes=" + this.stickyLanes
993 + "]";
994 }
995
996 }
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 public interface RoadPosition
1011 {
1012
1013
1014
1015
1016
1017
1018 double getValue(Speed desiredSpeed);
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032 public class ByValue implements RoadPosition
1033 {
1034
1035
1036 private double value;
1037
1038
1039
1040
1041
1042 public ByValue(final double value)
1043 {
1044 Throw.when(value < 0.0 || value > 1.0, IllegalArgumentException.class,
1045 "Road position value should be in the range [0...1].");
1046 this.value = value;
1047 }
1048
1049
1050 @Override
1051 public double getValue(final Speed desiredSpeed)
1052 {
1053 return this.value;
1054 }
1055
1056
1057 @Override
1058 public int hashCode()
1059 {
1060 final int prime = 31;
1061 int result = 1;
1062 long temp;
1063 temp = Double.doubleToLongBits(this.value);
1064 result = prime * result + (int) (temp ^ (temp >>> 32));
1065 return result;
1066 }
1067
1068
1069 @Override
1070 public boolean equals(final Object obj)
1071 {
1072 if (this == obj)
1073 {
1074 return true;
1075 }
1076 if (obj == null)
1077 {
1078 return false;
1079 }
1080 if (getClass() != obj.getClass())
1081 {
1082 return false;
1083 }
1084 ByValue other = (ByValue) obj;
1085 if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value))
1086 {
1087 return false;
1088 }
1089 return true;
1090 }
1091
1092 }
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106 public class BySpeed implements RoadPosition
1107 {
1108
1109
1110 private Speed leftSpeed;
1111
1112
1113 private Speed rightSpeed;
1114
1115
1116
1117
1118
1119
1120 public BySpeed(final Speed leftSpeed, final Speed rightSpeed)
1121 {
1122 Throw.when(leftSpeed.eq(rightSpeed), IllegalArgumentException.class,
1123 "Left speed and right speed may not be equal. Use LaneBias.NONE.");
1124 this.leftSpeed = leftSpeed;
1125 this.rightSpeed = rightSpeed;
1126 }
1127
1128
1129 @Override
1130 public double getValue(final Speed desiredSpeed)
1131 {
1132 Throw.whenNull(desiredSpeed,
1133 "Peeked desired speed from a strategical planner factory is null, while a lane bias depends on desired speed.");
1134 double value = (desiredSpeed.si - this.rightSpeed.si) / (this.leftSpeed.si - this.rightSpeed.si);
1135 return value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
1136 }
1137
1138
1139 @Override
1140 public int hashCode()
1141 {
1142 final int prime = 31;
1143 int result = 1;
1144 result = prime * result + ((this.leftSpeed == null) ? 0 : this.leftSpeed.hashCode());
1145 result = prime * result + ((this.rightSpeed == null) ? 0 : this.rightSpeed.hashCode());
1146 return result;
1147 }
1148
1149
1150 @Override
1151 public boolean equals(final Object obj)
1152 {
1153 if (this == obj)
1154 {
1155 return true;
1156 }
1157 if (obj == null)
1158 {
1159 return false;
1160 }
1161 if (getClass() != obj.getClass())
1162 {
1163 return false;
1164 }
1165 BySpeed other = (BySpeed) obj;
1166 if (this.leftSpeed == null)
1167 {
1168 if (other.leftSpeed != null)
1169 {
1170 return false;
1171 }
1172 }
1173 else if (!this.leftSpeed.equals(other.leftSpeed))
1174 {
1175 return false;
1176 }
1177 if (this.rightSpeed == null)
1178 {
1179 if (other.rightSpeed != null)
1180 {
1181 return false;
1182 }
1183 }
1184 else if (!this.rightSpeed.equals(other.rightSpeed))
1185 {
1186 return false;
1187 }
1188 return true;
1189 }
1190
1191 }
1192
1193 }
1194
1195 }