1 package org.opentrafficsim.kpi.sampling;
2
3 import java.util.Iterator;
4 import java.util.LinkedHashSet;
5 import java.util.Set;
6
7 import org.djunits.value.vdouble.scalar.Length;
8 import org.djutils.exceptions.Throw;
9 import org.djutils.immutablecollections.ImmutableIterator;
10 import org.opentrafficsim.kpi.interfaces.LaneData;
11 import org.opentrafficsim.kpi.interfaces.LinkData;
12 import org.opentrafficsim.kpi.sampling.CrossSection.LanePosition;
13
14 /**
15 * A cross sections contains locations on lanes that together make up a cross section. It is not required that this is on a
16 * single road, i.e. the cross section may be any section in space.
17 * <p>
18 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
19 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
20 * </p>
21 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
22 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
23 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
24 */
25 public class CrossSection implements Iterable<LanePosition>
26 {
27
28 /** Set of lane locations. */
29 private final Set<LanePosition> lanePositions;
30
31 /**
32 * Constructor with set of lane positions.
33 * @param lanePositions set of lane locations
34 */
35 public CrossSection(final Set<LanePosition> lanePositions)
36 {
37 Throw.whenNull(lanePositions, "Lane positions may not be null.");
38 this.lanePositions = new LinkedHashSet<>(lanePositions);
39 }
40
41 /**
42 * Constructor with link and fraction. Note that the fraction is used with the length of each lane. For curved links this
43 * fraction may not point to locations on the lane that are perfectly laterally adjacent. GTUs can thus possibly change lane
44 * at these gaps and be missed.
45 * @param link link
46 * @param fraction fraction on link
47 */
48 public CrossSection(final LinkData<?> link, final double fraction)
49 {
50 Throw.whenNull(link, "Link lane positions may not be null.");
51 this.lanePositions = new LinkedHashSet<>();
52 for (LaneData<?> lane : link.getLanes())
53 {
54 LanePosition lanePosition = new LanePosition(lane, lane.getLength().times(fraction));
55 this.lanePositions.add(lanePosition);
56 }
57 }
58
59 /**
60 * Returns the number of lane positions.
61 * @return number of directed lane positions
62 */
63 public final int size()
64 {
65 return this.lanePositions.size();
66 }
67
68 /**
69 * Returns a safe copy of the lane positions.
70 * @return safe copy of lane positions
71 */
72 public final Set<LanePosition> getLanePositions()
73 {
74 return new LinkedHashSet<>(this.lanePositions);
75 }
76
77 /**
78 * Returns an iterator over the lane positions.
79 * @return iterator over lane positions
80 */
81 @Override
82 public final Iterator<LanePosition> iterator()
83 {
84 return new ImmutableIterator<>(this.lanePositions.iterator());
85 }
86
87 @Override
88 public String toString()
89 {
90 return "CrossSection [lanePositions=" + this.lanePositions + "]";
91 }
92
93 /**
94 * Position on a lane.
95 * @param lane lane
96 * @param position position
97 */
98 public record LanePosition(LaneData<?> lane, Length position)
99 {
100 /**
101 * Construct a new LanePosition.
102 * @param lane lane
103 * @param position position
104 */
105 public LanePosition
106 {
107 Throw.whenNull(lane, "lane is null");
108 Throw.whenNull(position, "position is null");
109 }
110 }
111
112 }