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