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