1 package org.opentrafficsim.road.network.lane.conflict;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Map.Entry;
8 import java.util.SortedSet;
9 import java.util.TreeSet;
10
11 import org.djunits.value.vdouble.scalar.Length;
12 import org.opentrafficsim.core.geometry.OTSGeometryException;
13 import org.opentrafficsim.core.geometry.OTSLine3D;
14 import org.opentrafficsim.core.geometry.OTSPoint3D;
15 import org.opentrafficsim.core.gtu.GTUDirectionality;
16 import org.opentrafficsim.core.gtu.GTUType;
17 import org.opentrafficsim.core.network.Link;
18 import org.opentrafficsim.core.network.NetworkException;
19 import org.opentrafficsim.core.network.OTSNetwork;
20 import org.opentrafficsim.road.network.lane.CrossSectionElement;
21 import org.opentrafficsim.road.network.lane.CrossSectionLink;
22 import org.opentrafficsim.road.network.lane.Lane;
23
24 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
25 import nl.tudelft.simulation.immutablecollections.ImmutableMap;
26 import nl.tudelft.simulation.language.Throw;
27
28
29
30
31
32
33
34
35
36
37
38
39 public final class ConflictBuilder
40 {
41
42
43 public static final WidthGenerator DEFAULT_WIDTH_GENERATOR = new RelativeWidthGenerator(0.8);
44
45
46
47
48 private ConflictBuilder()
49 {
50
51 }
52
53
54
55
56
57
58
59
60
61 public static void buildConflicts(final OTSNetwork network, final GTUType gtuType,
62 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator) throws OTSGeometryException
63 {
64 buildConflicts(network, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
65 }
66
67
68
69
70
71
72
73
74
75
76
77 public static void buildConflicts(final OTSNetwork network, final GTUType gtuType,
78 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
79 final LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
80 {
81
82 ImmutableMap<String, Link> links = network.getLinkMap();
83 List<Lane> lanes = new ArrayList<>();
84 for (String linkId : links.keySet())
85 {
86 Link link = links.get(linkId);
87 if (link instanceof CrossSectionLink)
88 {
89 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
90 {
91 if (element instanceof Lane)
92 {
93 lanes.add((Lane) element);
94 }
95 }
96 }
97 }
98 buildConflicts(lanes, gtuType, simulator, widthGenerator, ignoreList, permittedList);
99 }
100
101
102
103
104
105
106
107
108
109 public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
110 final WidthGenerator widthGenerator) throws OTSGeometryException
111 {
112 buildConflicts(lanes, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
113 }
114
115
116
117
118
119
120
121
122
123
124
125 public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
126 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
127 throws OTSGeometryException
128 {
129
130 for (int i = 0; i < lanes.size(); i++)
131 {
132 Lane lane1 = lanes.get(i);
133 for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
134 {
135 Map<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
136 Map<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
137
138 for (int j = i + 1; j < lanes.size(); j++)
139 {
140 Lane lane2 = lanes.get(j);
141 if (ignoreList.contains(lane1, lane2))
142 {
143 continue;
144 }
145 boolean permitted = permittedList.contains(lane1, lane2);
146
147 for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(gtuType).getDirectionalities())
148 {
149 Map<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
150 Map<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
151
152 try
153 {
154 buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator,
155 widthGenerator);
156 }
157 catch (NetworkException ne)
158 {
159 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
160 }
161 }
162 }
163 }
164 }
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178 @SuppressWarnings("checkstyle:parameternumber")
179 public static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Lane lane2,
180 final GTUDirectionality dir2, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
181 final WidthGenerator widthGenerator) throws OTSGeometryException
182 {
183 buildConflicts(lane1, dir1, lane2, dir2, gtuType, simulator, widthGenerator, false);
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198 @SuppressWarnings("checkstyle:parameternumber")
199 public static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Lane lane2,
200 final GTUDirectionality dir2, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
201 final WidthGenerator widthGenerator, final boolean permitted) throws OTSGeometryException
202 {
203 Map<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
204 Map<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
205 Map<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
206 Map<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
207 try
208 {
209 buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator, widthGenerator);
210 }
211 catch (NetworkException ne)
212 {
213 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
214 }
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 @SuppressWarnings("checkstyle:parameternumber")
235 private static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Map<Lane, GTUDirectionality> down1,
236 final Map<Lane, GTUDirectionality> up1, final Lane lane2, final GTUDirectionality dir2,
237 final Map<Lane, GTUDirectionality> down2, final Map<Lane, GTUDirectionality> up2, final GTUType gtuType,
238 final boolean permitted, final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
239 throws OTSGeometryException, NetworkException
240 {
241
242
243 if (!lane1.getContour().intersects(lane2.getContour()))
244 {
245 return;
246 }
247
248
249 OTSLine3D line1 = lane1.getCenterLine();
250 OTSLine3D line2 = lane2.getCenterLine();
251 OTSLine3D left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
252 OTSLine3D right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
253 OTSLine3D left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
254 OTSLine3D right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
255
256
257 SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
258 intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
259 intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
260 intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
261
262
263 Iterator<Entry<Lane, GTUDirectionality>> iterator1 = down1.entrySet().iterator();
264 Iterator<Entry<Lane, GTUDirectionality>> iterator2 = down2.entrySet().iterator();
265 boolean merge = false;
266 while (iterator1.hasNext() && !merge)
267 {
268 Entry<Lane, GTUDirectionality> next1 = iterator1.next();
269 while (iterator2.hasNext() && !merge)
270 {
271 Entry<Lane, GTUDirectionality> next2 = iterator2.next();
272 if (next1.equals(next2))
273 {
274
275 double fraction1 = Double.NaN;
276 double fraction2 = Double.NaN;
277 for (Intersection intersection : intersections)
278 {
279
280 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
281 {
282 fraction1 = intersection.getFraction1();
283 fraction2 = intersection.getFraction2();
284 }
285 }
286
287 Iterator<Intersection> iterator = intersections.iterator();
288 while (iterator.hasNext())
289 {
290 if (iterator.next().getFraction1() >= fraction1)
291 {
292 iterator.remove();
293 }
294 }
295
296 buildMergeConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator,
297 permitted);
298
299 merge = true;
300 }
301 }
302 }
303
304
305 iterator1 = up1.entrySet().iterator();
306 iterator2 = up2.entrySet().iterator();
307 boolean split = false;
308 while (iterator1.hasNext() && !split)
309 {
310 Entry<Lane, GTUDirectionality> prev1 = iterator1.next();
311 while (iterator2.hasNext() && !split)
312 {
313 Entry<Lane, GTUDirectionality> prev2 = iterator2.next();
314 if (prev1.equals(prev2))
315 {
316
317 double fraction1 = Double.NaN;
318 double fraction2 = Double.NaN;
319 for (Intersection intersection : intersections)
320 {
321
322 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
323 {
324 fraction1 = intersection.getFraction1();
325 fraction2 = intersection.getFraction2();
326 break;
327 }
328 }
329
330 Iterator<Intersection> iterator = intersections.iterator();
331 while (iterator.hasNext())
332 {
333 if (iterator.next().getFraction1() <= fraction1)
334 {
335 iterator.remove();
336 }
337 else
338 {
339
340 break;
341 }
342 }
343
344 buildSplitConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator);
345
346 split = true;
347 }
348 }
349 }
350
351
352 boolean[] crossed = new boolean[4];
353 Iterator<Intersection> iterator = intersections.iterator();
354 double f1Start = Double.NaN;
355 double f2Start = Double.NaN;
356 double f2End = Double.NaN;
357 while (iterator.hasNext())
358 {
359 Intersection intersection = iterator.next();
360
361 if (Double.isNaN(f1Start))
362 {
363 f1Start = intersection.getFraction1();
364 }
365 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
366 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
367
368 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
369
370 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
371 || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
372 {
373 if (dir2.isMinus())
374 {
375 double f2Temp = f2Start;
376 f2Start = f2End;
377 f2End = f2Temp;
378 }
379 buildCrossingConflict(lane1, dir1, f1Start, intersection.getFraction1(), lane2, dir2, f2Start,
380 f2End, gtuType, simulator, widthGenerator, permitted);
381 f1Start = Double.NaN;
382 f2Start = Double.NaN;
383 f2End = Double.NaN;
384 }
385 }
386
387 }
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404 @SuppressWarnings("checkstyle:parameternumber")
405 private static void buildMergeConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
406 final Lane lane2, final GTUDirectionality dir2, final double f2start, final GTUType gtuType,
407 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator, final boolean permitted)
408 throws NetworkException, OTSGeometryException
409 {
410
411
412 double f1end = dir1.isPlus() ? 1.0 : 0.0;
413 double f2end = dir2.isPlus() ? 1.0 : 0.0;
414
415
416 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1start);
417 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2start);
418 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
419 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
420
421
422 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
423 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
424
425
426 ConflictRule conflictRule;
427 if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
428 {
429 Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
430 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
431 conflictRule = new BusStopConflictRule(simulator);
432 }
433 else
434 {
435 conflictRule = new DefaultConflictRule();
436 }
437
438
439 Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1, dir1,
440 geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
441 }
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457 @SuppressWarnings("checkstyle:parameternumber")
458 private static void buildSplitConflict(final Lane lane1, final GTUDirectionality dir1, final double f1end, final Lane lane2,
459 final GTUDirectionality dir2, final double f2end, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
460 final WidthGenerator widthGenerator) throws NetworkException, OTSGeometryException
461 {
462
463
464 double f1start = dir1.isPlus() ? 0.0 : 1.0;
465 double f2start = dir2.isPlus() ? 0.0 : 1.0;
466
467
468 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1start);
469 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2start);
470 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
471 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
472
473
474 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
475 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
476
477
478 Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
479 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499 @SuppressWarnings("checkstyle:parameternumber")
500 private static void buildCrossingConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
501 final double f1end, final Lane lane2, final GTUDirectionality dir2, final double f2start, final double f2end,
502 final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
503 final boolean permitted) throws NetworkException, OTSGeometryException
504 {
505
506
507
508 double f1startDirected;
509 double f2startDirected;
510 if ((dir1.isPlus() && f1end < f1start) || (dir1.isMinus() && f1end > f1start))
511 {
512 f1startDirected = f1end;
513 }
514 else
515 {
516 f1startDirected = f1start;
517 }
518 if ((dir2.isPlus() && f2end < f2start) || (dir2.isMinus() && f2end > f2start))
519 {
520 f2startDirected = f2end;
521 }
522 else
523 {
524 f2startDirected = f2start;
525 }
526
527
528 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1startDirected);
529 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2startDirected);
530 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
531 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
532
533
534 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
535 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
536
537
538 ConflictRule conflictRule;
539 if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
540 {
541 Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
542 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
543 conflictRule = new BusStopConflictRule(simulator);
544 }
545 else
546 {
547 conflictRule = new DefaultConflictRule();
548 }
549
550
551 Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
552 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
553 }
554
555
556
557
558
559
560
561
562
563
564 private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
565 final WidthGenerator widthGenerator) throws OTSGeometryException
566 {
567
568 double f1;
569 double f2;
570 if (fEnd > fStart)
571 {
572 f1 = fStart;
573 f2 = fEnd;
574 }
575 else
576 {
577 f1 = fEnd;
578 f2 = fStart;
579 }
580 OTSLine3D centerLine = lane.getCenterLine().extractFractional(f1, f2);
581 OTSLine3D left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
582 OTSLine3D right =
583 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
584 OTSPoint3D[] points = new OTSPoint3D[left.size() + right.size()];
585 System.arraycopy(left.getPoints(), 0, points, 0, left.size());
586 System.arraycopy(right.getPoints(), 0, points, left.size(), right.size());
587 return new OTSLine3D(points);
588 }
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603 private static class Intersection implements Comparable<Intersection>
604 {
605
606
607 private final double fraction1;
608
609
610 private final double fraction2;
611
612
613 private final int combo;
614
615
616
617
618
619
620 Intersection(final double fraction1, final double fraction2, final int combo)
621 {
622 this.fraction1 = fraction1;
623 this.fraction2 = fraction2;
624 this.combo = combo;
625 }
626
627
628
629
630 public final double getFraction1()
631 {
632 return this.fraction1;
633 }
634
635
636
637
638 public final double getFraction2()
639 {
640 return this.fraction2;
641 }
642
643
644
645
646 public final int getCombo()
647 {
648 return this.combo;
649 }
650
651
652 @Override
653 public int compareTo(final Intersection o)
654 {
655 int out = Double.compare(this.fraction1, o.fraction1);
656 if (out != 0)
657 {
658 return out;
659 }
660 out = Double.compare(this.fraction2, o.fraction2);
661 if (out != 0)
662 {
663 return out;
664 }
665 return Integer.compare(this.combo, o.combo);
666 }
667
668
669 @Override
670 public int hashCode()
671 {
672 final int prime = 31;
673 int result = 1;
674 result = prime * result + this.combo;
675 long temp;
676 temp = Double.doubleToLongBits(this.fraction1);
677 result = prime * result + (int) (temp ^ (temp >>> 32));
678 temp = Double.doubleToLongBits(this.fraction2);
679 result = prime * result + (int) (temp ^ (temp >>> 32));
680 return result;
681 }
682
683
684 @Override
685 public boolean equals(final Object obj)
686 {
687 if (this == obj)
688 {
689 return true;
690 }
691 if (obj == null)
692 {
693 return false;
694 }
695 if (getClass() != obj.getClass())
696 {
697 return false;
698 }
699 Intersection other = (Intersection) obj;
700 if (this.combo != other.combo)
701 {
702 return false;
703 }
704 if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
705 {
706 return false;
707 }
708 if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
709 {
710 return false;
711 }
712 return true;
713 }
714
715
716
717
718
719
720
721
722
723 public static SortedSet<Intersection> getIntersectionList(final OTSLine3D line1, final OTSLine3D line2, final int combo)
724 throws OTSGeometryException
725 {
726 SortedSet<Intersection> out = new TreeSet<>();
727
728 double cumul1 = 0.0;
729 OTSPoint3D start1 = null;
730 OTSPoint3D end1 = line1.get(0);
731 for (int i = 0; i < line1.size() - 1; i++)
732 {
733 start1 = end1;
734 end1 = line1.get(i + 1);
735
736 double cumul2 = 0.0;
737 OTSPoint3D start2 = null;
738 OTSPoint3D end2 = line2.get(0);
739
740 for (int j = 0; j < line2.size() - 1; j++)
741 {
742 start2 = end2;
743 end2 = line2.get(j + 1);
744
745 OTSPoint3D p = OTSPoint3D.intersectionOfLineSegments(start1, end1, start2, end2);
746 if (p != null)
747 {
748
749 double dx = p.x - start1.x;
750 double dy = p.y - start1.y;
751 double length1 = cumul1 + Math.sqrt(dx * dx + dy * dy);
752 dx = p.x - start2.x;
753 dy = p.y - start2.y;
754 double length2 = cumul2 + Math.sqrt(dx * dx + dy * dy);
755 out.add(new Intersection(length1 / line1.getLengthSI(), length2 / line2.getLengthSI(), combo));
756 }
757
758 double dx = end2.x - start2.x;
759 double dy = end2.y - start2.y;
760 cumul2 += Math.sqrt(dx * dx + dy * dy);
761 }
762
763 double dx = end1.x - start1.x;
764 double dy = end1.y - start1.y;
765 cumul1 += Math.sqrt(dx * dx + dy * dy);
766 }
767
768 return out;
769 }
770
771
772 @Override
773 public String toString()
774 {
775 return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
776 + "]";
777 }
778
779 }
780
781
782
783
784
785
786
787
788
789
790
791
792
793 public interface WidthGenerator
794 {
795
796
797
798
799
800
801
802 double getWidth(Lane lane, double fraction);
803
804 }
805
806
807
808
809
810
811
812
813
814
815
816
817
818 public static class FixedWidthGenerator implements WidthGenerator
819 {
820
821
822 private final double width;
823
824
825
826
827
828 public FixedWidthGenerator(final Length width)
829 {
830 this.width = width.si;
831 }
832
833
834 @Override
835 public final double getWidth(final Lane lane, final double fraction)
836 {
837 return this.width;
838 }
839
840
841 @Override
842 public final String toString()
843 {
844 return "FixedWidthGenerator [width=" + this.width + "]";
845 }
846
847 }
848
849
850
851
852
853
854
855
856
857
858
859
860
861 public static class RelativeWidthGenerator implements WidthGenerator
862 {
863
864
865 private final double factor;
866
867
868
869
870
871 public RelativeWidthGenerator(final double factor)
872 {
873 this.factor = factor;
874 }
875
876
877 @Override
878 public final double getWidth(final Lane lane, final double fraction)
879 {
880 return lane.getWidth(fraction).si * this.factor;
881 }
882
883
884 @Override
885 public final String toString()
886 {
887 return "RelativeWidthGenerator [factor=" + this.factor + "]";
888 }
889
890 }
891
892 }