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