1 package org.opentrafficsim.road.network.lane.object;
2
3 import java.util.OptionalDouble;
4
5 import org.djunits.value.vdouble.scalar.Length;
6 import org.djutils.exceptions.Throw;
7 import org.opentrafficsim.core.network.LateralDirectionality;
8 import org.opentrafficsim.core.network.NetworkException;
9 import org.opentrafficsim.road.network.lane.Lane;
10
11 /**
12 * Distraction following a distance profile.
13 * <p>
14 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
15 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
16 * </p>
17 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
18 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
19 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
20 */
21 public class RoadSideDistraction extends AbstractLaneBasedObject
22 {
23
24 /** Distraction profile. */
25 private final DistractionProfile profile;
26
27 /** Side of the distraction. */
28 private final LateralDirectionality side;
29
30 /**
31 * Constructor.
32 * @param id id
33 * @param lane lane
34 * @param longitudinalPosition longitudinal position
35 * @param profile distraction profile
36 * @param side side of the distraction, left of road (LEFT), right of road (RIGHT), or on the lane of the object (NONE)
37 * @throws NetworkException on network exception
38 */
39 public RoadSideDistraction(final String id, final Lane lane, final Length longitudinalPosition,
40 final DistractionProfile profile, final LateralDirectionality side) throws NetworkException
41 {
42 super(id, lane, longitudinalPosition, LaneBasedObject.makeLine(lane, longitudinalPosition), Length.ZERO);
43 this.profile = profile;
44 this.side = side;
45 init();
46 }
47
48 /**
49 * Returns the level of distraction at the given distance.
50 * @param distance negative when approaching
51 * @return level of distraction (task-demand), empty if the distraction is no longer important
52 */
53 public OptionalDouble getDistraction(final Length distance)
54 {
55 return this.profile.getDistraction(distance);
56 }
57
58 /**
59 * Returns the side of the distraction, relative to the driving direction.
60 * @return side of the distraction, left of road (LEFT), right of road (RIGHT), or on the lane of the object (NONE)
61 */
62 public LateralDirectionality getSide()
63 {
64 return this.side;
65 }
66
67 /**
68 * Describes the profile around the distraction.
69 * <p>
70 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
71 * <br>
72 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
73 * </p>
74 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
75 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
76 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
77 */
78 @FunctionalInterface
79 public interface DistractionProfile
80 {
81 /**
82 * Returns the level of distraction at the given distance.
83 * @param distance negative when approaching
84 * @return level of distraction (task-demand), empty if the distraction is no longer important
85 */
86 OptionalDouble getDistraction(Length distance);
87 }
88
89 /**
90 * Distraction profile with trapezoid shape. The constant part is from the location of the distraction downstream.
91 * <p>
92 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
93 * <br>
94 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
95 * </p>
96 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
97 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
98 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
99 */
100 public static class TrapezoidProfile implements DistractionProfile
101 {
102 /** Maximum distraction. */
103 private final double maxDistraction;
104
105 /** Distance before distraction where distraction starts to have effect. */
106 private final Length dMin;
107
108 /** Distance beyond distraction where distraction has maximum effect. */
109 private final Length dMed;
110
111 /** Distance beyond distraction where distraction no longer has effect. */
112 private final Length dMax;
113
114 /**
115 * Constructor.
116 * @param maxDistraction maximum distraction (task-demand)
117 * @param dMin distance before distraction where distraction starts to have effect (<0)
118 * @param dMed distance beyond distraction where distraction has maximum effect (>0)
119 * @param dMax distance beyond distraction where distraction no longer has effect (>dMed)
120 */
121 public TrapezoidProfile(final double maxDistraction, final Length dMin, final Length dMed, final Length dMax)
122 {
123 Throw.when(dMin.si > 0 || dMed.si < 0 || dMax.si < dMed.si, IllegalArgumentException.class,
124 "dMin < 0 < dMed < dMax does not hold");
125 Throw.when(maxDistraction < 0 || maxDistraction > 1, IllegalArgumentException.class,
126 "0 <= maxDistraction <= 1 does not hold");
127 this.maxDistraction = maxDistraction;
128 this.dMin = dMin;
129 this.dMed = dMed;
130 this.dMax = dMax;
131 }
132
133 @Override
134 public OptionalDouble getDistraction(final Length distance)
135 {
136 if (distance.si < this.dMin.si)
137 {
138 // before scope
139 return OptionalDouble.of(0.0);
140 }
141 else if (distance.si < 0)
142 {
143 // increasing distraction on approach
144 return OptionalDouble.of(this.maxDistraction * (1.0 - distance.si / this.dMin.si));
145 }
146 else if (distance.si < this.dMed.si)
147 {
148 // max distraction at location (defined over a distance dMed)
149 return OptionalDouble.of(this.maxDistraction);
150 }
151 else if (distance.si < this.dMax.si)
152 {
153 // reducing distraction beyond location
154 return OptionalDouble
155 .of(this.maxDistraction * (1.0 - (distance.si - this.dMed.si) / (this.dMax.si - this.dMed.si)));
156 }
157 else
158 {
159 // beyond scope
160 return OptionalDouble.empty();
161 }
162 }
163 }
164
165 }