1 package org.opentrafficsim.road.network.conflict;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.SortedSet;
10 import java.util.TreeSet;
11 import java.util.concurrent.Executors;
12 import java.util.concurrent.ThreadPoolExecutor;
13 import java.util.concurrent.atomic.AtomicInteger;
14
15 import org.djunits.value.vdouble.scalar.Length;
16 import org.djutils.draw.line.Polygon2d;
17 import org.djutils.draw.point.Point2d;
18 import org.djutils.exceptions.Throw;
19 import org.djutils.immutablecollections.ImmutableMap;
20 import org.opentrafficsim.base.OtsRuntimeException;
21 import org.opentrafficsim.base.geometry.OtsLine2d;
22 import org.opentrafficsim.base.logger.Logger;
23 import org.opentrafficsim.core.definitions.DefaultsNl;
24 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
25 import org.opentrafficsim.core.network.Link;
26 import org.opentrafficsim.core.network.NetworkException;
27 import org.opentrafficsim.road.network.CrossSectionElement;
28 import org.opentrafficsim.road.network.CrossSectionLink;
29 import org.opentrafficsim.road.network.Lane;
30 import org.opentrafficsim.road.network.RoadNetwork;
31 import org.opentrafficsim.road.network.Shoulder;
32
33 import ch.qos.logback.classic.Level;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public final class ConflictBuilder
54 {
55
56 private static AtomicInteger numberMergeConflicts = new AtomicInteger(0);
57
58
59 private static AtomicInteger numberSplitConflicts = new AtomicInteger(0);
60
61
62 private static AtomicInteger numberCrossConflicts = new AtomicInteger(0);
63
64
65 public static final WidthGenerator DEFAULT_WIDTH_GENERATOR = new RelativeWidthGenerator(0.8);
66
67
68
69
70 private ConflictBuilder()
71 {
72
73 }
74
75
76
77
78
79
80
81 public static void buildConflicts(final RoadNetwork network, final OtsSimulatorInterface simulator,
82 final WidthGenerator widthGenerator)
83 {
84 buildConflicts(network, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
85 }
86
87
88
89
90
91
92
93
94
95 public static void buildConflicts(final RoadNetwork network, final OtsSimulatorInterface simulator,
96 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
97 {
98 buildConflicts(getLanes(network), simulator, widthGenerator, ignoreList, permittedList, null);
99 }
100
101
102
103
104
105
106 private static List<Lane> getLanes(final RoadNetwork network)
107 {
108 ImmutableMap<String, Link> links = network.getLinkMap();
109 List<Lane> lanes = new ArrayList<>();
110 for (String linkId : links.keySet())
111 {
112 Link link = links.get(linkId);
113 if (link instanceof CrossSectionLink)
114 {
115 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
116 {
117 if (element instanceof Lane lane && !(element instanceof Shoulder))
118 {
119 lanes.add((Lane) element);
120 }
121 }
122 }
123 }
124 return lanes;
125 }
126
127
128
129
130
131
132
133 public static void buildConflicts(final List<Lane> lanes, final OtsSimulatorInterface simulator,
134 final WidthGenerator widthGenerator)
135 {
136 buildConflicts(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(), null);
137 }
138
139
140
141
142
143
144
145
146
147
148 public static void buildConflicts(final List<Lane> lanes, final OtsSimulatorInterface simulator,
149 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList,
150 final String conflictId)
151 {
152 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
153 Logger.ots().trace("GENERATING CONFLICTS (NON-PARALLEL MODE). {} COMBINATIONS", totalCombinations);
154 long lastReported = 0;
155 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
156 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
157
158 for (int i = 0; i < lanes.size(); i++)
159 {
160 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
161 if (combinationsDone / 100000000 > lastReported)
162 {
163 Logger.ots()
164 .debug(String.format(
165 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
166 + "conflicts, %d crossing conflicts)",
167 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
168 numberSplitConflicts.get(), numberCrossConflicts.get()));
169 lastReported = combinationsDone / 100000000;
170 }
171 Lane lane1 = lanes.get(i);
172 Set<Lane> down1 = lane1.nextLanes(null);
173 Set<Lane> up1 = lane1.prevLanes(null);
174
175 for (int j = i + 1; j < lanes.size(); j++)
176 {
177 Lane lane2 = lanes.get(j);
178 if (ignoreList.contains(lane1, lane2))
179 {
180 continue;
181 }
182 boolean permitted = permittedList.contains(lane1, lane2);
183
184 Set<Lane> down2 = lane2.nextLanes(null);
185 Set<Lane> up2 = lane2.prevLanes(null);
186
187 try
188 {
189 buildConflicts(lane1, down1, up1, lane2, down2, up2, permitted, simulator, widthGenerator, leftEdges,
190 rightEdges, true, conflictId);
191 }
192 catch (NetworkException ne)
193 {
194 throw new OtsRuntimeException("Conflict build with bad combination of types / rules.", ne);
195 }
196 }
197 }
198 Logger.ots()
199 .trace(String.format(
200 "generating conflicts complete (generated %d merge conflicts, %d split "
201 + "conflicts, %d crossing conflicts)",
202 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
203 }
204
205
206
207
208
209
210
211
212 @SuppressWarnings("checkstyle:parameternumber")
213 public static void buildConflicts(final Lane lane1, final Lane lane2, final OtsSimulatorInterface simulator,
214 final WidthGenerator widthGenerator)
215 {
216 buildConflicts(lane1, lane2, simulator, widthGenerator, false);
217 }
218
219
220
221
222
223
224
225
226
227 @SuppressWarnings("checkstyle:parameternumber")
228 public static void buildConflicts(final Lane lane1, final Lane lane2, final OtsSimulatorInterface simulator,
229 final WidthGenerator widthGenerator, final boolean permitted)
230 {
231 Set<Lane> down1 = lane1.nextLanes(null);
232 Set<Lane> up1 = lane1.prevLanes(null);
233 Set<Lane> down2 = lane2.nextLanes(null);
234 Set<Lane> up2 = lane2.prevLanes(null);
235 try
236 {
237 buildConflicts(lane1, down1, up1, lane2, down2, up2, permitted, simulator, widthGenerator, new LinkedHashMap<>(),
238 new LinkedHashMap<>(), true, null);
239 }
240 catch (NetworkException ne)
241 {
242 throw new OtsRuntimeException("Conflict build with bad combination of types / rules.", ne);
243 }
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"})
264 static void buildConflicts(final Lane lane1, final Set<Lane> down1, final Set<Lane> up1, final Lane lane2,
265 final Set<Lane> down2, final Set<Lane> up2, final boolean permitted, final OtsSimulatorInterface simulator,
266 final WidthGenerator widthGenerator, final Map<Lane, OtsLine2d> leftEdges, final Map<Lane, OtsLine2d> rightEdges,
267 final boolean intersectionCheck, final String conflictId) throws NetworkException
268 {
269
270 if (intersectionCheck)
271 {
272 if (!lane1.getAbsoluteContour().intersects(lane2.getAbsoluteContour()))
273 {
274 return;
275 }
276 }
277
278
279
280 String paddedConflictId = null == conflictId ? "" : (" in conflict group " + conflictId);
281
282 OtsLine2d left1;
283 OtsLine2d right1;
284 synchronized (lane1)
285 {
286 left1 = leftEdges.get(lane1);
287 right1 = rightEdges.get(lane1);
288 OtsLine2d line1 = lane1.getCenterLine();
289 if (null == left1)
290 {
291 left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
292 leftEdges.put(lane1, left1);
293 }
294 if (null == right1)
295 {
296 right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
297 rightEdges.put(lane1, right1);
298 }
299 }
300
301 OtsLine2d left2;
302 OtsLine2d right2;
303 synchronized (lane2)
304 {
305 left2 = leftEdges.get(lane2);
306 right2 = rightEdges.get(lane2);
307 OtsLine2d line2 = lane2.getCenterLine();
308 if (null == left2)
309 {
310 left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
311 leftEdges.put(lane2, left2);
312 }
313 if (null == right2)
314 {
315 right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
316 rightEdges.put(lane2, right2);
317 }
318 }
319
320
321 SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
322 intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
323 intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
324 intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
325
326
327 Iterator<Lane> iterator1 = down1.iterator();
328 Iterator<Lane> iterator2 = down2.iterator();
329 boolean merge = false;
330 while (iterator1.hasNext() && !merge)
331 {
332 Lane next1 = iterator1.next();
333 while (iterator2.hasNext() && !merge)
334 {
335 Lane next2 = iterator2.next();
336 if (next1.equals(next2))
337 {
338
339 double fraction1 = Double.NaN;
340 double fraction2 = Double.NaN;
341 for (Intersection intersection : intersections)
342 {
343
344 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
345 {
346 fraction1 = intersection.getFraction1();
347 fraction2 = intersection.getFraction2();
348 }
349 }
350
351 Iterator<Intersection> iterator = intersections.iterator();
352 while (iterator.hasNext())
353 {
354 if (iterator.next().getFraction1() >= fraction1)
355 {
356 iterator.remove();
357 }
358 }
359 if (Double.isNaN(fraction1))
360 {
361 Logger.ots().info("Fixing fractions of merge conflict between lane {} and {}", lane1.getFullId(),
362 lane2.getFullId());
363 fraction1 = 0;
364 fraction2 = 0;
365 }
366
367 buildMergeConflict(lane1, fraction1, lane2, fraction2, simulator, widthGenerator, permitted);
368
369 merge = true;
370 }
371 }
372 }
373
374
375 iterator1 = up1.iterator();
376 iterator2 = up2.iterator();
377 boolean split = false;
378 while (iterator1.hasNext() && !split)
379 {
380 Lane prev1 = iterator1.next();
381 while (iterator2.hasNext() && !split)
382 {
383 Lane prev2 = iterator2.next();
384 if (prev1.equals(prev2))
385 {
386
387 double fraction1 = Double.NaN;
388 double fraction2 = Double.NaN;
389 for (Intersection intersection : intersections)
390 {
391
392 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
393 {
394 fraction1 = intersection.getFraction1();
395 fraction2 = intersection.getFraction2();
396 break;
397 }
398 }
399
400 Iterator<Intersection> iterator = intersections.iterator();
401 while (iterator.hasNext())
402 {
403 if (iterator.next().getFraction1() <= fraction1)
404 {
405 iterator.remove();
406 }
407 else
408 {
409
410 break;
411 }
412 }
413 if (Double.isNaN(fraction1))
414 {
415 Logger.ots().info("Fixing fractions of split conflict{}", paddedConflictId);
416 fraction1 = 1;
417 fraction2 = 1;
418 }
419
420 buildSplitConflict(lane1, fraction1, lane2, fraction2, simulator, widthGenerator);
421
422 split = true;
423 }
424 }
425 }
426
427
428 if (!lane1.getLink().equals(lane2.getLink()))
429 {
430 boolean[] crossed = new boolean[4];
431 Iterator<Intersection> iterator = intersections.iterator();
432 double f1Start = Double.NaN;
433 double f2Start = Double.NaN;
434 double f2End = Double.NaN;
435 while (iterator.hasNext())
436 {
437 Intersection intersection = iterator.next();
438
439 if (Double.isNaN(f1Start))
440 {
441 f1Start = intersection.getFraction1();
442 }
443 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
444 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
445
446 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
447
448 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
449 || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
450 {
451 if (Double.isNaN(f1Start) || Double.isNaN(f2Start) || Double.isNaN(f2End))
452 {
453 Logger.ots().warn("NOT YET Fixing fractions of crossing conflict{}", paddedConflictId);
454 }
455 buildCrossingConflict(lane1, f1Start, intersection.getFraction1(), lane2, f2Start, f2End, simulator,
456 widthGenerator, permitted);
457 f1Start = Double.NaN;
458 f2Start = Double.NaN;
459 f2End = Double.NaN;
460 }
461 }
462 }
463
464 }
465
466
467
468
469
470
471
472
473
474
475
476
477 @SuppressWarnings("checkstyle:parameternumber")
478 private static void buildMergeConflict(final Lane lane1, final double f1start, final Lane lane2, final double f2start,
479 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator, final boolean permitted)
480 throws NetworkException
481 {
482
483
484 double f1end = 1.0;
485 double f2end = 1.0;
486
487
488 Length longitudinalPosition1 = lane1.getLength().times(f1start);
489 Length longitudinalPosition2 = lane2.getLength().times(f2start);
490 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
491 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
492
493
494 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
495 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
496
497
498 ConflictRule conflictRule;
499 if (lane1.getLink().getPriority().isBusStop() || lane2.getLink().getPriority().isBusStop())
500 {
501 Throw.when(lane1.getLink().getPriority().isBusStop() && lane2.getLink().getPriority().isBusStop(),
502 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
503
504 conflictRule = new BusStopConflictRule(simulator, DefaultsNl.BUS);
505 }
506 else
507 {
508 conflictRule = new DefaultConflictRule();
509 }
510
511
512 Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1,
513 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
514
515 numberMergeConflicts.incrementAndGet();
516 }
517
518
519
520
521
522
523
524
525
526
527
528 @SuppressWarnings("checkstyle:parameternumber")
529 private static void buildSplitConflict(final Lane lane1, final double f1end, final Lane lane2, final double f2end,
530 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator) throws NetworkException
531 {
532
533
534 double f1start = 0.0;
535 double f2start = 0.0;
536
537
538 Length longitudinalPosition1 = lane1.getLength().times(f1start);
539 Length longitudinalPosition2 = lane2.getLength().times(f2start);
540 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
541 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
542
543
544 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
545 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
546
547
548 Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
549 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
550
551 numberSplitConflicts.incrementAndGet();
552 }
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567 @SuppressWarnings("checkstyle:parameternumber")
568 private static void buildCrossingConflict(final Lane lane1, final double f1start, final double f1end, final Lane lane2,
569 final double f2start, final double f2end, final OtsSimulatorInterface simulator,
570 final WidthGenerator widthGenerator, final boolean permitted) throws NetworkException
571 {
572
573
574
575 double f1startDirected;
576 double f2startDirected;
577 if (f1end < f1start)
578 {
579 f1startDirected = f1end;
580 }
581 else
582 {
583 f1startDirected = f1start;
584 }
585 if (f2end < f2start)
586 {
587 f2startDirected = f2end;
588 }
589 else
590 {
591 f2startDirected = f2start;
592 }
593
594
595 Length longitudinalPosition1 = lane1.getLength().times(f1startDirected);
596 Length longitudinalPosition2 = lane2.getLength().times(f2startDirected);
597 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
598 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
599
600
601 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
602 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
603
604
605 ConflictRule conflictRule;
606 if (lane1.getLink().getPriority().isBusStop() || lane2.getLink().getPriority().isBusStop())
607 {
608 Throw.when(lane1.getLink().getPriority().isBusStop() && lane2.getLink().getPriority().isBusStop(),
609 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
610
611 conflictRule = new BusStopConflictRule(simulator, DefaultsNl.BUS);
612 }
613 else
614 {
615 conflictRule = new DefaultConflictRule();
616 }
617
618
619 Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
620 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
621
622 numberCrossConflicts.incrementAndGet();
623 }
624
625
626
627
628
629
630
631
632
633 private static Polygon2d getGeometry(final Lane lane, final double fStart, final double fEnd,
634 final WidthGenerator widthGenerator)
635 {
636
637 double f1;
638 double f2;
639 if (fEnd > fStart)
640 {
641 f1 = fStart;
642 f2 = fEnd;
643 }
644 else
645 {
646 f1 = fEnd;
647 f2 = fStart;
648 }
649 if (Math.abs(f1 - f2) < 1E-8)
650 {
651 Logger.ots().debug("f1 (" + f1 + ") equals f2 (" + f2 + "); problematic lane is " + lane.toString());
652
653 if (f1 > 0)
654 {
655 f1 = f1 - f1 / 1000;
656 }
657 else
658 {
659 f2 = f2 + f2 / 1000;
660 }
661 }
662 OtsLine2d centerLine = lane.getCenterLine().extractFractional(f1, f2);
663 OtsLine2d left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
664 OtsLine2d right =
665 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
666 List<Point2d> points = new ArrayList<>(left.size() + right.size());
667 points.addAll(left.getPointList());
668 points.addAll(right.getPointList());
669 return new Polygon2d(0.0, points);
670 }
671
672
673
674
675
676
677
678
679
680
681
682
683
684 private static class Intersection implements Comparable<Intersection>
685 {
686
687
688 private final double fraction1;
689
690
691 private final double fraction2;
692
693
694 private final int combo;
695
696
697
698
699
700
701 Intersection(final double fraction1, final double fraction2, final int combo)
702 {
703 this.fraction1 = fraction1;
704 this.fraction2 = fraction2;
705 this.combo = combo;
706 }
707
708
709
710
711 public final double getFraction1()
712 {
713 return this.fraction1;
714 }
715
716
717
718
719 public final double getFraction2()
720 {
721 return this.fraction2;
722 }
723
724
725
726
727 public final int getCombo()
728 {
729 return this.combo;
730 }
731
732 @Override
733 public int compareTo(final Intersection o)
734 {
735 int out = Double.compare(this.fraction1, o.fraction1);
736 if (out != 0)
737 {
738 return out;
739 }
740 out = Double.compare(this.fraction2, o.fraction2);
741 if (out != 0)
742 {
743 return out;
744 }
745 return Integer.compare(this.combo, o.combo);
746 }
747
748 @Override
749 public int hashCode()
750 {
751 final int prime = 31;
752 int result = 1;
753 result = prime * result + this.combo;
754 long temp;
755 temp = Double.doubleToLongBits(this.fraction1);
756 result = prime * result + (int) (temp ^ (temp >>> 32));
757 temp = Double.doubleToLongBits(this.fraction2);
758 result = prime * result + (int) (temp ^ (temp >>> 32));
759 return result;
760 }
761
762 @Override
763 public boolean equals(final Object obj)
764 {
765 if (this == obj)
766 {
767 return true;
768 }
769 if (obj == null)
770 {
771 return false;
772 }
773 if (getClass() != obj.getClass())
774 {
775 return false;
776 }
777 Intersection other = (Intersection) obj;
778 if (this.combo != other.combo)
779 {
780 return false;
781 }
782 if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
783 {
784 return false;
785 }
786 if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
787 {
788 return false;
789 }
790 return true;
791 }
792
793
794
795
796
797
798
799
800 public static SortedSet<Intersection> getIntersectionList(final OtsLine2d line1, final OtsLine2d line2, final int combo)
801 {
802 SortedSet<Intersection> out = new TreeSet<>();
803 double cumul1 = 0.0;
804 Point2d start1 = null;
805 Point2d end1 = line1.get(0);
806 for (int i = 0; i < line1.size() - 1; i++)
807 {
808 start1 = end1;
809 end1 = line1.get(i + 1);
810
811 double cumul2 = 0.0;
812 Point2d start2 = null;
813 Point2d end2 = line2.get(0);
814
815 for (int j = 0; j < line2.size() - 1; j++)
816 {
817 start2 = end2;
818 end2 = line2.get(j + 1);
819
820 Point2d p = Point2d.intersectionOfLineSegments(start1, end1, start2, end2);
821 if (p != null)
822 {
823
824 double dx = p.x - start1.x;
825 double dy = p.y - start1.y;
826 double length1 = cumul1 + Math.hypot(dx, dy);
827 dx = p.x - start2.x;
828 dy = p.y - start2.y;
829 double length2 = cumul2 + Math.hypot(dx, dy);
830 out.add(new Intersection(length1 / line1.getLength(), length2 / line2.getLength(), combo));
831 }
832
833 double dx = end2.x - start2.x;
834 double dy = end2.y - start2.y;
835 cumul2 += Math.hypot(dx, dy);
836 }
837
838 double dx = end1.x - start1.x;
839 double dy = end1.y - start1.y;
840 cumul1 += Math.hypot(dx, dy);
841 }
842
843 return out;
844 }
845
846 @Override
847 public String toString()
848 {
849 return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
850 + "]";
851 }
852
853 }
854
855
856
857
858
859
860
861
862
863
864
865
866 public interface WidthGenerator
867 {
868
869
870
871
872
873
874
875 double getWidth(Lane lane, double fraction);
876
877 }
878
879
880
881
882
883
884
885
886
887
888
889
890 public static class FixedWidthGenerator implements WidthGenerator
891 {
892
893
894 private final double width;
895
896
897
898
899
900 public FixedWidthGenerator(final Length width)
901 {
902 this.width = width.si;
903 }
904
905 @Override
906 public final double getWidth(final Lane lane, final double fraction)
907 {
908 return this.width;
909 }
910
911 @Override
912 public final String toString()
913 {
914 return "FixedWidthGenerator [width=" + this.width + "]";
915 }
916
917 }
918
919
920
921
922
923
924
925
926
927
928
929
930 public static class RelativeWidthGenerator implements WidthGenerator
931 {
932
933
934 private final double factor;
935
936
937
938
939
940 public RelativeWidthGenerator(final double factor)
941 {
942 this.factor = factor;
943 }
944
945 @Override
946 public final double getWidth(final Lane lane, final double fraction)
947 {
948 return lane.getWidth(fraction).si * this.factor;
949 }
950
951 @Override
952 public final String toString()
953 {
954 return "RelativeWidthGenerator [factor=" + this.factor + "]";
955 }
956
957 }
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973 public static void buildConflictsParallel(final RoadNetwork network, final OtsSimulatorInterface simulator,
974 final WidthGenerator widthGenerator)
975 {
976 buildConflictsParallel(network, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
977 }
978
979
980
981
982
983
984
985
986
987 public static void buildConflictsParallel(final RoadNetwork network, final OtsSimulatorInterface simulator,
988 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
989 {
990 buildConflictsParallelBig(getLanes(network), simulator, widthGenerator, ignoreList, permittedList);
991 }
992
993
994
995
996
997
998
999 public static void buildConflictsParallel(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1000 final WidthGenerator widthGenerator)
1001 {
1002 buildConflictsParallelBig(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
1003 }
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013 public static void buildConflictsParallelSmall(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1014 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
1015 {
1016 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1017 Logger.ots().trace("PARALLEL GENERATING OF CONFLICTS (SMALL JOBS). " + totalCombinations + " COMBINATIONS");
1018 long lastReported = 0;
1019 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
1020 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
1021
1022
1023 int cores = Runtime.getRuntime().availableProcessors();
1024 Logger.ots().trace("USING " + cores + " CORES");
1025 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1026 AtomicInteger numberOfJobs = new AtomicInteger(0);
1027 final int maxqueue = 2 * cores;
1028
1029 for (int i = 0; i < lanes.size(); i++)
1030 {
1031 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i - 1)) / 2;
1032 if (combinationsDone / 100000000 > lastReported)
1033 {
1034 Logger.ots()
1035 .debug(String.format(
1036 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1037 + "conflicts, %d crossing conflicts)",
1038 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1039 numberSplitConflicts.get(), numberCrossConflicts.get()));
1040 lastReported = combinationsDone / 100000000;
1041 }
1042 Lane lane1 = lanes.get(i);
1043 Set<Lane> down1 = lane1.nextLanes(null);
1044 Set<Lane> up1 = lane1.prevLanes(null);
1045
1046 for (int j = i + 1; j < lanes.size(); j++)
1047 {
1048 Lane lane2 = lanes.get(j);
1049 if (ignoreList.contains(lane1, lane2))
1050 {
1051 continue;
1052 }
1053
1054 try
1055 {
1056 if (!lane1.getAbsoluteContour().intersects(lane2.getAbsoluteContour()))
1057 {
1058 continue;
1059 }
1060 }
1061 catch (Exception e)
1062 {
1063 Logger.ots().error("Contour problem - lane1 = [{}], lane2 = [{}]; skipped", lane1.getFullId(),
1064 lane2.getFullId());
1065 continue;
1066 }
1067
1068 boolean permitted = permittedList.contains(lane1, lane2);
1069
1070 while (numberOfJobs.get() > maxqueue)
1071 {
1072 try
1073 {
1074 Thread.sleep(1);
1075 }
1076 catch (InterruptedException exception)
1077 {
1078
1079 }
1080 }
1081 numberOfJobs.incrementAndGet();
1082 Set<Lane> down2 = lane2.nextLanes(null);
1083 Set<Lane> up2 = lane2.prevLanes(null);
1084 ConflictBuilderRecordSmall cbr = new ConflictBuilderRecordSmall(lane1, down1, up1, lane2, down2, up2, permitted,
1085 simulator, widthGenerator, leftEdges, rightEdges);
1086 executor.execute(new CbrTaskSmall(numberOfJobs, cbr));
1087 }
1088 }
1089
1090 long time = System.currentTimeMillis();
1091
1092 while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1093 {
1094 try
1095 {
1096 Thread.sleep(10);
1097 }
1098 catch (InterruptedException exception)
1099 {
1100
1101 }
1102 }
1103
1104 executor.shutdown();
1105 while (!executor.isTerminated())
1106 {
1107 try
1108 {
1109 Thread.sleep(1);
1110 }
1111 catch (InterruptedException exception)
1112 {
1113
1114 }
1115 }
1116
1117 Logger.ots()
1118 .debug(String.format(
1119 "generating conflicts complete (generated %d merge conflicts, %d split "
1120 + "conflicts, %d crossing conflicts)",
1121 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1122 }
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132 public static void buildConflictsParallelBig(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1133 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
1134 {
1135 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1136 Logger.ots().trace("PARALLEL GENERATING OF CONFLICTS (BIG JOBS). " + totalCombinations + " COMBINATIONS");
1137 long lastReported = 0;
1138 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
1139 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
1140
1141
1142 int cores = Runtime.getRuntime().availableProcessors();
1143 Logger.ots().trace("USING " + cores + " CORES");
1144 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1145 AtomicInteger numberOfJobs = new AtomicInteger(0);
1146 final int maxqueue = 200;
1147
1148 for (int i = 0; i < lanes.size(); i++)
1149 {
1150 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i - 1)) / 2;
1151 if (combinationsDone / 100000000 > lastReported)
1152 {
1153 Logger.ots()
1154 .debug(String.format(
1155 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1156 + "conflicts, %d crossing conflicts)",
1157 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1158 numberSplitConflicts.get(), numberCrossConflicts.get()));
1159 lastReported = combinationsDone / 100000000;
1160 }
1161
1162 while (numberOfJobs.get() > maxqueue)
1163 {
1164 try
1165 {
1166 Thread.sleep(0, 10);
1167 }
1168 catch (InterruptedException exception)
1169 {
1170
1171 }
1172 }
1173 numberOfJobs.incrementAndGet();
1174
1175 ConflictBuilderRecordBig cbr = new ConflictBuilderRecordBig(i, lanes, ignoreList, permittedList, simulator,
1176 widthGenerator, leftEdges, rightEdges);
1177 executor.execute(new CbrTaskBig(numberOfJobs, cbr));
1178
1179 }
1180
1181 long time = System.currentTimeMillis();
1182
1183 while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1184 {
1185 try
1186 {
1187 Thread.sleep(10);
1188 }
1189 catch (InterruptedException exception)
1190 {
1191
1192 }
1193 }
1194
1195 executor.shutdown();
1196 while (!executor.isTerminated())
1197 {
1198 try
1199 {
1200 Thread.sleep(1);
1201 }
1202 catch (InterruptedException exception)
1203 {
1204
1205 }
1206 }
1207
1208 Logger.ots()
1209 .debug(String.format(
1210 "generating conflicts complete (generated %d merge conflicts, %d split "
1211 + "conflicts, %d crossing conflicts)",
1212 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1213 }
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223 public static void buildConflictsParallel(final RoadNetwork network, final Map<String, Set<Link>> conflictCandidateMap,
1224 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator)
1225 {
1226 for (String conflictId : conflictCandidateMap.keySet())
1227 {
1228 List<Lane> lanes = new ArrayList<>();
1229 for (Link link : conflictCandidateMap.get(conflictId))
1230 {
1231 if (link instanceof CrossSectionLink)
1232 {
1233 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
1234 {
1235 if (element instanceof Lane lane && !(element instanceof Shoulder))
1236 {
1237 lanes.add((Lane) element);
1238 }
1239 }
1240 }
1241 }
1242
1243 buildConflicts(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(), conflictId);
1244 }
1245 }
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258 static class CbrTaskSmall implements Runnable
1259 {
1260
1261 final ConflictBuilderRecordSmall cbr;
1262
1263
1264 final AtomicInteger nrOfJobs;
1265
1266
1267
1268
1269
1270
1271 CbrTaskSmall(final AtomicInteger nrOfJobs, final ConflictBuilderRecordSmall cbr)
1272 {
1273 this.nrOfJobs = nrOfJobs;
1274 this.cbr = cbr;
1275 }
1276
1277 @Override
1278 public void run()
1279 {
1280 try
1281 {
1282 buildConflicts(this.cbr.lane1, this.cbr.down1, this.cbr.up1, this.cbr.lane2, this.cbr.down2, this.cbr.up2,
1283 this.cbr.permitted, this.cbr.simulator, this.cbr.widthGenerator, this.cbr.leftEdges,
1284 this.cbr.rightEdges, false, null);
1285 }
1286 catch (NetworkException ne)
1287 {
1288 throw new OtsRuntimeException("Conflict build with bad combination of types / rules.", ne);
1289 }
1290 this.nrOfJobs.decrementAndGet();
1291 }
1292 }
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316 @SuppressWarnings("checkstyle:visibilitymodifier")
1317 static record ConflictBuilderRecordSmall(Lane lane1, Set<Lane> down1, Set<Lane> up1, Lane lane2, Set<Lane> down2,
1318 Set<Lane> up2, boolean permitted, OtsSimulatorInterface simulator, WidthGenerator widthGenerator,
1319 Map<Lane, OtsLine2d> leftEdges, Map<Lane, OtsLine2d> rightEdges)
1320 {
1321 }
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335 static class CbrTaskBig implements Runnable
1336 {
1337
1338 final ConflictBuilderRecordBig cbr;
1339
1340
1341 final AtomicInteger nrOfJobs;
1342
1343
1344
1345
1346
1347
1348 CbrTaskBig(final AtomicInteger nrOfJobs, final ConflictBuilderRecordBig cbr)
1349 {
1350 this.nrOfJobs = nrOfJobs;
1351 this.cbr = cbr;
1352 }
1353
1354 @Override
1355 public void run()
1356 {
1357 try
1358 {
1359 Lane lane1 = this.cbr.lanes.get(this.cbr.starti);
1360 Set<Lane> up1 = lane1.prevLanes(null);
1361 Set<Lane> down1 = lane1.nextLanes(null);
1362 for (int j = this.cbr.starti + 1; j < this.cbr.lanes.size(); j++)
1363 {
1364 Lane lane2 = this.cbr.lanes.get(j);
1365 if (this.cbr.ignoreList.contains(lane1, lane2))
1366 {
1367 continue;
1368 }
1369
1370 try
1371 {
1372 if (!lane1.getAbsoluteContour().intersects(lane2.getAbsoluteContour()))
1373 {
1374 continue;
1375 }
1376 }
1377 catch (Exception e)
1378 {
1379 Logger.ots().error("Contour problem - lane1 = [{}], lane2 = [{}]; skipped", lane1.getFullId(),
1380 lane2.getFullId());
1381 continue;
1382 }
1383
1384 boolean permitted = this.cbr.permittedList.contains(lane1, lane2);
1385
1386 Set<Lane> down2 = lane2.nextLanes(null);
1387 Set<Lane> up2 = lane2.prevLanes(null);
1388
1389 try
1390 {
1391 buildConflicts(lane1, down1, up1, lane2, down2, up2, permitted, this.cbr.simulator,
1392 this.cbr.widthGenerator, this.cbr.leftEdges, this.cbr.rightEdges, false, null);
1393 }
1394 catch (NetworkException ne)
1395 {
1396 Logger.ots().error(ne, "Conflict build with bad combination of types / rules.");
1397 }
1398 }
1399
1400 }
1401 catch (Exception e)
1402 {
1403 e.printStackTrace();
1404 }
1405 this.nrOfJobs.decrementAndGet();
1406 }
1407 }
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429 @SuppressWarnings("checkstyle:visibilitymodifier")
1430 static record ConflictBuilderRecordBig(int starti, List<Lane> lanes, LaneCombinationList ignoreList,
1431 LaneCombinationList permittedList, OtsSimulatorInterface simulator, WidthGenerator widthGenerator,
1432 Map<Lane, OtsLine2d> leftEdges, Map<Lane, OtsLine2d> rightEdges)
1433 {
1434 }
1435
1436 }