1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.util.LinkedHashSet;
5 import java.util.Set;
6
7 import org.djunits.value.vdouble.scalar.Length;
8 import org.djutils.exceptions.Try;
9 import org.djutils.immutablecollections.ImmutableMap;
10 import org.opentrafficsim.core.geometry.OTSGeometryException;
11 import org.opentrafficsim.core.gtu.GTUDirectionality;
12 import org.opentrafficsim.core.network.LateralDirectionality;
13 import org.opentrafficsim.core.network.LinkDirection;
14 import org.opentrafficsim.core.network.NetworkException;
15 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
16
17 import nl.tudelft.simulation.language.d3.DirectedPoint;
18
19
20
21
22
23
24
25
26
27
28
29
30 public class LaneDirection implements Serializable
31 {
32
33 private static final long serialVersionUID = 20160330L;
34
35
36 private final Lane lane;
37
38
39 private final GTUDirectionality direction;
40
41
42
43
44
45 public LaneDirection(final Lane lane, final GTUDirectionality direction)
46 {
47 super();
48 this.lane = lane;
49 this.direction = direction;
50 }
51
52
53
54
55 public final Lane getLane()
56 {
57 return this.lane;
58 }
59
60
61
62
63 public final GTUDirectionality getDirection()
64 {
65 return this.direction;
66 }
67
68
69
70
71
72
73 public final Length coveredDistance(final double fraction)
74 {
75 if (this.direction.isPlus())
76 {
77 return getLane().getLength().times(fraction);
78 }
79 return getLane().getLength().times(1.0 - fraction);
80 }
81
82
83
84
85
86
87 public final Length remainingDistance(final double fraction)
88 {
89 if (this.direction.isPlus())
90 {
91 return getLane().getLength().times(1.0 - fraction);
92 }
93 return getLane().getLength().times(fraction);
94 }
95
96
97
98
99
100
101 public final double fractionAtCoveredDistance(final Length distance)
102 {
103 double f = this.lane.fraction(distance);
104 if (this.getDirection().isMinus())
105 {
106 f = 1.0 - f;
107 }
108 return f;
109 }
110
111
112
113
114
115
116 public final LaneDirection getNextLaneDirection(final LaneBasedGTU gtu)
117 {
118 ImmutableMap<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
119 if (next.isEmpty())
120 {
121 return null;
122 }
123
124 Set<LaneDirection> set = getNextForRoute(gtu);
125 if (set.size() == 1)
126 {
127 return set.iterator().next();
128 }
129
130 for (LaneDirection l : set)
131 {
132 if (l.getLane().getGtuList().contains(gtu))
133 {
134 return l;
135 }
136 }
137
138 return Try.assign(() -> gtu.getTacticalPlanner().chooseLaneAtSplit(this, set), "Missing parameter.");
139 }
140
141
142
143
144
145
146 public Set<LaneDirection> getNextForRoute(final LaneBasedGTU gtu)
147 {
148 ImmutableMap<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
149 if (next.isEmpty())
150 {
151 return null;
152 }
153 LinkDirection ld;
154 try
155 {
156 ld = gtu.getStrategicalPlanner().nextLinkDirection(this.lane.getParentLink(), this.direction, gtu.getGTUType());
157 }
158 catch (NetworkException exception)
159 {
160 throw new RuntimeException("Strategical planner experiences exception on network.", exception);
161 }
162 Set<LaneDirection> out = new LinkedHashSet<>();
163 for (Lane l : next.keySet())
164 {
165 GTUDirectionality dir = next.get(l);
166 if (l.getParentLink().equals(ld.getLink()) && dir.equals(ld.getDirection()))
167 {
168 out.add(new LaneDirection(l, dir));
169 }
170 }
171 return out;
172 }
173
174
175
176
177
178 public Length getLength()
179 {
180 return this.lane.getLength();
181 }
182
183
184
185
186
187
188
189 public DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
190 {
191 DirectedPoint p = this.lane.getCenterLine().getLocationFraction(fraction);
192 if (this.direction.isMinus())
193 {
194 p.setRotZ(p.getRotZ() + Math.PI);
195 }
196 return p;
197 }
198
199
200
201
202
203
204
205 public final LaneDirection getAdjacentLaneDirection(final LateralDirectionality laneChangeDirection, final LaneBasedGTU gtu)
206 {
207 Set<Lane> adjLanes = this.lane.accessibleAdjacentLanesLegal(laneChangeDirection, gtu.getGTUType(), this.direction);
208 if (!adjLanes.isEmpty())
209 {
210 return new LaneDirection(adjLanes.iterator().next(), this.direction);
211 }
212 return null;
213 }
214
215
216 @Override
217 public final String toString()
218 {
219 return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
220 }
221
222
223 @Override
224 public final int hashCode()
225 {
226 final int prime = 31;
227 int result = 1;
228 result = prime * result + ((this.direction == null) ? 0 : this.direction.hashCode());
229 result = prime * result + ((this.lane == null) ? 0 : this.lane.hashCode());
230 return result;
231 }
232
233
234 @Override
235 public final boolean equals(final Object obj)
236 {
237 if (this == obj)
238 {
239 return true;
240 }
241 if (obj == null)
242 {
243 return false;
244 }
245 if (getClass() != obj.getClass())
246 {
247 return false;
248 }
249 LaneDirection../../../org/opentrafficsim/road/network/lane/LaneDirection.html#LaneDirection">LaneDirection other = (LaneDirection) obj;
250 if (this.direction != other.direction)
251 {
252 return false;
253 }
254 if (this.lane == null)
255 {
256 if (other.lane != null)
257 {
258 return false;
259 }
260 }
261 else if (!this.lane.equals(other.lane))
262 {
263 return false;
264 }
265 return true;
266 }
267
268 }