1 package org.opentrafficsim.road.network.lane.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.RoadNetwork;
28 import org.opentrafficsim.road.network.lane.CrossSectionElement;
29 import org.opentrafficsim.road.network.lane.CrossSectionLink;
30 import org.opentrafficsim.road.network.lane.Lane;
31 import org.opentrafficsim.road.network.lane.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{}", paddedConflictId);
362 fraction1 = 0;
363 fraction2 = 0;
364 }
365
366 buildMergeConflict(lane1, fraction1, lane2, fraction2, simulator, widthGenerator, permitted);
367
368 merge = true;
369 }
370 }
371 }
372
373
374 iterator1 = up1.iterator();
375 iterator2 = up2.iterator();
376 boolean split = false;
377 while (iterator1.hasNext() && !split)
378 {
379 Lane prev1 = iterator1.next();
380 while (iterator2.hasNext() && !split)
381 {
382 Lane prev2 = iterator2.next();
383 if (prev1.equals(prev2))
384 {
385
386 double fraction1 = Double.NaN;
387 double fraction2 = Double.NaN;
388 for (Intersection intersection : intersections)
389 {
390
391 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
392 {
393 fraction1 = intersection.getFraction1();
394 fraction2 = intersection.getFraction2();
395 break;
396 }
397 }
398
399 Iterator<Intersection> iterator = intersections.iterator();
400 while (iterator.hasNext())
401 {
402 if (iterator.next().getFraction1() <= fraction1)
403 {
404 iterator.remove();
405 }
406 else
407 {
408
409 break;
410 }
411 }
412 if (Double.isNaN(fraction1))
413 {
414 Logger.ots().info("Fixing fractions of split conflict{}", paddedConflictId);
415 fraction1 = 1;
416 fraction2 = 1;
417 }
418
419 buildSplitConflict(lane1, fraction1, lane2, fraction2, simulator, widthGenerator);
420
421 split = true;
422 }
423 }
424 }
425
426
427 if (!lane1.getLink().equals(lane2.getLink()))
428 {
429 boolean[] crossed = new boolean[4];
430 Iterator<Intersection> iterator = intersections.iterator();
431 double f1Start = Double.NaN;
432 double f2Start = Double.NaN;
433 double f2End = Double.NaN;
434 while (iterator.hasNext())
435 {
436 Intersection intersection = iterator.next();
437
438 if (Double.isNaN(f1Start))
439 {
440 f1Start = intersection.getFraction1();
441 }
442 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
443 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
444
445 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
446
447 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
448 || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
449 {
450 if (Double.isNaN(f1Start) || Double.isNaN(f2Start) || Double.isNaN(f2End))
451 {
452 Logger.ots().warn("NOT YET Fixing fractions of crossing conflict{}", paddedConflictId);
453 }
454 buildCrossingConflict(lane1, f1Start, intersection.getFraction1(), lane2, f2Start, f2End, simulator,
455 widthGenerator, permitted);
456 f1Start = Double.NaN;
457 f2Start = Double.NaN;
458 f2End = Double.NaN;
459 }
460 }
461 }
462
463 }
464
465
466
467
468
469
470
471
472
473
474
475
476 @SuppressWarnings("checkstyle:parameternumber")
477 private static void buildMergeConflict(final Lane lane1, final double f1start, final Lane lane2, final double f2start,
478 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator, final boolean permitted)
479 throws NetworkException
480 {
481
482
483 double f1end = 1.0;
484 double f2end = 1.0;
485
486
487 Length longitudinalPosition1 = lane1.getLength().times(f1start);
488 Length longitudinalPosition2 = lane2.getLength().times(f2start);
489 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
490 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
491
492
493 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
494 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
495
496
497 ConflictRule conflictRule;
498 if (lane1.getLink().getPriority().isBusStop() || lane2.getLink().getPriority().isBusStop())
499 {
500 Throw.when(lane1.getLink().getPriority().isBusStop() && lane2.getLink().getPriority().isBusStop(),
501 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
502
503 conflictRule = new BusStopConflictRule(simulator, DefaultsNl.BUS);
504 }
505 else
506 {
507 conflictRule = new DefaultConflictRule();
508 }
509
510
511 Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1,
512 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
513
514 numberMergeConflicts.incrementAndGet();
515 }
516
517
518
519
520
521
522
523
524
525
526
527 @SuppressWarnings("checkstyle:parameternumber")
528 private static void buildSplitConflict(final Lane lane1, final double f1end, final Lane lane2, final double f2end,
529 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator) throws NetworkException
530 {
531
532
533 double f1start = 0.0;
534 double f2start = 0.0;
535
536
537 Length longitudinalPosition1 = lane1.getLength().times(f1start);
538 Length longitudinalPosition2 = lane2.getLength().times(f2start);
539 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
540 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
541
542
543 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
544 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
545
546
547 Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
548 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
549
550 numberSplitConflicts.incrementAndGet();
551 }
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566 @SuppressWarnings("checkstyle:parameternumber")
567 private static void buildCrossingConflict(final Lane lane1, final double f1start, final double f1end, final Lane lane2,
568 final double f2start, final double f2end, final OtsSimulatorInterface simulator,
569 final WidthGenerator widthGenerator, final boolean permitted) throws NetworkException
570 {
571
572
573
574 double f1startDirected;
575 double f2startDirected;
576 if (f1end < f1start)
577 {
578 f1startDirected = f1end;
579 }
580 else
581 {
582 f1startDirected = f1start;
583 }
584 if (f2end < f2start)
585 {
586 f2startDirected = f2end;
587 }
588 else
589 {
590 f2startDirected = f2start;
591 }
592
593
594 Length longitudinalPosition1 = lane1.getLength().times(f1startDirected);
595 Length longitudinalPosition2 = lane2.getLength().times(f2startDirected);
596 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
597 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
598
599
600 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
601 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
602
603
604 ConflictRule conflictRule;
605 if (lane1.getLink().getPriority().isBusStop() || lane2.getLink().getPriority().isBusStop())
606 {
607 Throw.when(lane1.getLink().getPriority().isBusStop() && lane2.getLink().getPriority().isBusStop(),
608 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
609
610 conflictRule = new BusStopConflictRule(simulator, DefaultsNl.BUS);
611 }
612 else
613 {
614 conflictRule = new DefaultConflictRule();
615 }
616
617
618 Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
619 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
620
621 numberCrossConflicts.incrementAndGet();
622 }
623
624
625
626
627
628
629
630
631
632 private static Polygon2d getGeometry(final Lane lane, final double fStart, final double fEnd,
633 final WidthGenerator widthGenerator)
634 {
635
636 double f1;
637 double f2;
638 if (fEnd > fStart)
639 {
640 f1 = fStart;
641 f2 = fEnd;
642 }
643 else
644 {
645 f1 = fEnd;
646 f2 = fStart;
647 }
648 if (Math.abs(f1 - f2) < 1E-8)
649 {
650 Logger.ots().debug("f1 (" + f1 + ") equals f2 (" + f2 + "); problematic lane is " + lane.toString());
651
652 if (f1 > 0)
653 {
654 f1 = f1 - f1 / 1000;
655 }
656 else
657 {
658 f2 = f2 + f2 / 1000;
659 }
660 }
661 OtsLine2d centerLine = lane.getCenterLine().extractFractional(f1, f2);
662 OtsLine2d left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
663 OtsLine2d right =
664 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
665 List<Point2d> points = new ArrayList<>(left.size() + right.size());
666 points.addAll(left.getPointList());
667 points.addAll(right.getPointList());
668 return new Polygon2d(points);
669 }
670
671
672
673
674
675
676
677
678
679
680
681
682
683 private static class Intersection implements Comparable<Intersection>
684 {
685
686
687 private final double fraction1;
688
689
690 private final double fraction2;
691
692
693 private final int combo;
694
695
696
697
698
699
700 Intersection(final double fraction1, final double fraction2, final int combo)
701 {
702 this.fraction1 = fraction1;
703 this.fraction2 = fraction2;
704 this.combo = combo;
705 }
706
707
708
709
710 public final double getFraction1()
711 {
712 return this.fraction1;
713 }
714
715
716
717
718 public final double getFraction2()
719 {
720 return this.fraction2;
721 }
722
723
724
725
726 public final int getCombo()
727 {
728 return this.combo;
729 }
730
731 @Override
732 public int compareTo(final Intersection o)
733 {
734 int out = Double.compare(this.fraction1, o.fraction1);
735 if (out != 0)
736 {
737 return out;
738 }
739 out = Double.compare(this.fraction2, o.fraction2);
740 if (out != 0)
741 {
742 return out;
743 }
744 return Integer.compare(this.combo, o.combo);
745 }
746
747 @Override
748 public int hashCode()
749 {
750 final int prime = 31;
751 int result = 1;
752 result = prime * result + this.combo;
753 long temp;
754 temp = Double.doubleToLongBits(this.fraction1);
755 result = prime * result + (int) (temp ^ (temp >>> 32));
756 temp = Double.doubleToLongBits(this.fraction2);
757 result = prime * result + (int) (temp ^ (temp >>> 32));
758 return result;
759 }
760
761 @Override
762 public boolean equals(final Object obj)
763 {
764 if (this == obj)
765 {
766 return true;
767 }
768 if (obj == null)
769 {
770 return false;
771 }
772 if (getClass() != obj.getClass())
773 {
774 return false;
775 }
776 Intersection other = (Intersection) obj;
777 if (this.combo != other.combo)
778 {
779 return false;
780 }
781 if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
782 {
783 return false;
784 }
785 if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
786 {
787 return false;
788 }
789 return true;
790 }
791
792
793
794
795
796
797
798
799 public static SortedSet<Intersection> getIntersectionList(final OtsLine2d line1, final OtsLine2d line2, final int combo)
800 {
801 SortedSet<Intersection> out = new TreeSet<>();
802 double cumul1 = 0.0;
803 Point2d start1 = null;
804 Point2d end1 = line1.get(0);
805 for (int i = 0; i < line1.size() - 1; i++)
806 {
807 start1 = end1;
808 end1 = line1.get(i + 1);
809
810 double cumul2 = 0.0;
811 Point2d start2 = null;
812 Point2d end2 = line2.get(0);
813
814 for (int j = 0; j < line2.size() - 1; j++)
815 {
816 start2 = end2;
817 end2 = line2.get(j + 1);
818
819 Point2d p = Point2d.intersectionOfLineSegments(start1, end1, start2, end2);
820 if (p != null)
821 {
822
823 double dx = p.x - start1.x;
824 double dy = p.y - start1.y;
825 double length1 = cumul1 + Math.hypot(dx, dy);
826 dx = p.x - start2.x;
827 dy = p.y - start2.y;
828 double length2 = cumul2 + Math.hypot(dx, dy);
829 out.add(new Intersection(length1 / line1.getLength(), length2 / line2.getLength(), combo));
830 }
831
832 double dx = end2.x - start2.x;
833 double dy = end2.y - start2.y;
834 cumul2 += Math.hypot(dx, dy);
835 }
836
837 double dx = end1.x - start1.x;
838 double dy = end1.y - start1.y;
839 cumul1 += Math.hypot(dx, dy);
840 }
841
842 return out;
843 }
844
845 @Override
846 public String toString()
847 {
848 return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
849 + "]";
850 }
851
852 }
853
854
855
856
857
858
859
860
861
862
863
864
865 public interface WidthGenerator
866 {
867
868
869
870
871
872
873
874 double getWidth(Lane lane, double fraction);
875
876 }
877
878
879
880
881
882
883
884
885
886
887
888
889 public static class FixedWidthGenerator implements WidthGenerator
890 {
891
892
893 private final double width;
894
895
896
897
898
899 public FixedWidthGenerator(final Length width)
900 {
901 this.width = width.si;
902 }
903
904 @Override
905 public final double getWidth(final Lane lane, final double fraction)
906 {
907 return this.width;
908 }
909
910 @Override
911 public final String toString()
912 {
913 return "FixedWidthGenerator [width=" + this.width + "]";
914 }
915
916 }
917
918
919
920
921
922
923
924
925
926
927
928
929 public static class RelativeWidthGenerator implements WidthGenerator
930 {
931
932
933 private final double factor;
934
935
936
937
938
939 public RelativeWidthGenerator(final double factor)
940 {
941 this.factor = factor;
942 }
943
944 @Override
945 public final double getWidth(final Lane lane, final double fraction)
946 {
947 return lane.getWidth(fraction).si * this.factor;
948 }
949
950 @Override
951 public final String toString()
952 {
953 return "RelativeWidthGenerator [factor=" + this.factor + "]";
954 }
955
956 }
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972 public static void buildConflictsParallel(final RoadNetwork network, final OtsSimulatorInterface simulator,
973 final WidthGenerator widthGenerator)
974 {
975 buildConflictsParallel(network, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
976 }
977
978
979
980
981
982
983
984
985
986 public static void buildConflictsParallel(final RoadNetwork network, final OtsSimulatorInterface simulator,
987 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
988 {
989 buildConflictsParallelBig(getLanes(network), simulator, widthGenerator, ignoreList, permittedList);
990 }
991
992
993
994
995
996
997
998 public static void buildConflictsParallel(final List<Lane> lanes, final OtsSimulatorInterface simulator,
999 final WidthGenerator widthGenerator)
1000 {
1001 buildConflictsParallelBig(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012 public static void buildConflictsParallelSmall(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1013 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
1014 {
1015 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1016 Logger.ots().trace("PARALLEL GENERATING OF CONFLICTS (SMALL JOBS). " + totalCombinations + " COMBINATIONS");
1017 long lastReported = 0;
1018 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
1019 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
1020
1021
1022 int cores = Runtime.getRuntime().availableProcessors();
1023 Logger.ots().trace("USING " + cores + " CORES");
1024 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1025 AtomicInteger numberOfJobs = new AtomicInteger(0);
1026 final int maxqueue = 2 * cores;
1027
1028 for (int i = 0; i < lanes.size(); i++)
1029 {
1030 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i - 1)) / 2;
1031 if (combinationsDone / 100000000 > lastReported)
1032 {
1033 Logger.ots()
1034 .debug(String.format(
1035 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1036 + "conflicts, %d crossing conflicts)",
1037 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1038 numberSplitConflicts.get(), numberCrossConflicts.get()));
1039 lastReported = combinationsDone / 100000000;
1040 }
1041 Lane lane1 = lanes.get(i);
1042 Set<Lane> down1 = lane1.nextLanes(null);
1043 Set<Lane> up1 = lane1.prevLanes(null);
1044
1045 for (int j = i + 1; j < lanes.size(); j++)
1046 {
1047 Lane lane2 = lanes.get(j);
1048 if (ignoreList.contains(lane1, lane2))
1049 {
1050 continue;
1051 }
1052
1053 try
1054 {
1055 if (!lane1.getAbsoluteContour().intersects(lane2.getAbsoluteContour()))
1056 {
1057 continue;
1058 }
1059 }
1060 catch (Exception e)
1061 {
1062 Logger.ots().error("Contour problem - lane1 = [{}], lane2 = [{}]; skipped", lane1.getFullId(),
1063 lane2.getFullId());
1064 continue;
1065 }
1066
1067 boolean permitted = permittedList.contains(lane1, lane2);
1068
1069 while (numberOfJobs.get() > maxqueue)
1070 {
1071 try
1072 {
1073 Thread.sleep(1);
1074 }
1075 catch (InterruptedException exception)
1076 {
1077
1078 }
1079 }
1080 numberOfJobs.incrementAndGet();
1081 Set<Lane> down2 = lane2.nextLanes(null);
1082 Set<Lane> up2 = lane2.prevLanes(null);
1083 ConflictBuilderRecordSmall cbr = new ConflictBuilderRecordSmall(lane1, down1, up1, lane2, down2, up2, permitted,
1084 simulator, widthGenerator, leftEdges, rightEdges);
1085 executor.execute(new CbrTaskSmall(numberOfJobs, cbr));
1086 }
1087 }
1088
1089 long time = System.currentTimeMillis();
1090
1091 while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1092 {
1093 try
1094 {
1095 Thread.sleep(10);
1096 }
1097 catch (InterruptedException exception)
1098 {
1099
1100 }
1101 }
1102
1103 executor.shutdown();
1104 while (!executor.isTerminated())
1105 {
1106 try
1107 {
1108 Thread.sleep(1);
1109 }
1110 catch (InterruptedException exception)
1111 {
1112
1113 }
1114 }
1115
1116 Logger.ots()
1117 .debug(String.format(
1118 "generating conflicts complete (generated %d merge conflicts, %d split "
1119 + "conflicts, %d crossing conflicts)",
1120 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1121 }
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131 public static void buildConflictsParallelBig(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1132 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
1133 {
1134 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1135 Logger.ots().trace("PARALLEL GENERATING OF CONFLICTS (BIG JOBS). " + totalCombinations + " COMBINATIONS");
1136 long lastReported = 0;
1137 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
1138 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
1139
1140
1141 int cores = Runtime.getRuntime().availableProcessors();
1142 Logger.ots().trace("USING " + cores + " CORES");
1143 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1144 AtomicInteger numberOfJobs = new AtomicInteger(0);
1145 final int maxqueue = 200;
1146
1147 for (int i = 0; i < lanes.size(); i++)
1148 {
1149 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i - 1)) / 2;
1150 if (combinationsDone / 100000000 > lastReported)
1151 {
1152 Logger.ots()
1153 .debug(String.format(
1154 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1155 + "conflicts, %d crossing conflicts)",
1156 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1157 numberSplitConflicts.get(), numberCrossConflicts.get()));
1158 lastReported = combinationsDone / 100000000;
1159 }
1160
1161 while (numberOfJobs.get() > maxqueue)
1162 {
1163 try
1164 {
1165 Thread.sleep(0, 10);
1166 }
1167 catch (InterruptedException exception)
1168 {
1169
1170 }
1171 }
1172 numberOfJobs.incrementAndGet();
1173
1174 ConflictBuilderRecordBig cbr = new ConflictBuilderRecordBig(i, lanes, ignoreList, permittedList, simulator,
1175 widthGenerator, leftEdges, rightEdges);
1176 executor.execute(new CbrTaskBig(numberOfJobs, cbr));
1177
1178 }
1179
1180 long time = System.currentTimeMillis();
1181
1182 while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1183 {
1184 try
1185 {
1186 Thread.sleep(10);
1187 }
1188 catch (InterruptedException exception)
1189 {
1190
1191 }
1192 }
1193
1194 executor.shutdown();
1195 while (!executor.isTerminated())
1196 {
1197 try
1198 {
1199 Thread.sleep(1);
1200 }
1201 catch (InterruptedException exception)
1202 {
1203
1204 }
1205 }
1206
1207 Logger.ots()
1208 .debug(String.format(
1209 "generating conflicts complete (generated %d merge conflicts, %d split "
1210 + "conflicts, %d crossing conflicts)",
1211 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222 public static void buildConflictsParallel(final RoadNetwork network, final Map<String, Set<Link>> conflictCandidateMap,
1223 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator)
1224 {
1225 for (String conflictId : conflictCandidateMap.keySet())
1226 {
1227 List<Lane> lanes = new ArrayList<>();
1228 for (Link link : conflictCandidateMap.get(conflictId))
1229 {
1230 if (link instanceof CrossSectionLink)
1231 {
1232 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
1233 {
1234 if (element instanceof Lane lane && !(element instanceof Shoulder))
1235 {
1236 lanes.add((Lane) element);
1237 }
1238 }
1239 }
1240 }
1241
1242 buildConflicts(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(), conflictId);
1243 Logger.setLogLevel(Level.DEBUG);
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 }