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