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),
139 "Could not find suitable lane at split after lane %s in %s of link %s for GTU %s.", this.lane.getId(),
140 this.direction, this.lane.getParentLink().getId(), gtu.getId());
141 }
142
143
144
145
146
147
148 public Set<LaneDirection> getNextForRoute(final LaneBasedGTU gtu)
149 {
150 ImmutableMap<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
151 if (next.isEmpty())
152 {
153 return null;
154 }
155 LinkDirection ld;
156 try
157 {
158 ld = gtu.getStrategicalPlanner().nextLinkDirection(this.lane.getParentLink(), this.direction, gtu.getGTUType());
159 }
160 catch (NetworkException exception)
161 {
162 throw new RuntimeException("Strategical planner experiences exception on network.", exception);
163 }
164 Set<LaneDirection> out = new LinkedHashSet<>();
165 for (Lane l : next.keySet())
166 {
167 GTUDirectionality dir = next.get(l);
168 if (l.getParentLink().equals(ld.getLink()) && dir.equals(ld.getDirection()))
169 {
170 out.add(new LaneDirection(l, dir));
171 }
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 return out;
196 }
197
198
199
200
201
202 public Length getLength()
203 {
204 return this.lane.getLength();
205 }
206
207
208
209
210
211
212
213 public DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
214 {
215 DirectedPoint p = this.lane.getCenterLine().getLocationFraction(fraction);
216 if (this.direction.isMinus())
217 {
218 p.setRotZ(p.getRotZ() + Math.PI);
219 }
220 return p;
221 }
222
223
224
225
226
227
228
229 public final LaneDirection getAdjacentLaneDirection(final LateralDirectionality laneChangeDirection, final LaneBasedGTU gtu)
230 {
231 Set<Lane> adjLanes = this.lane.accessibleAdjacentLanesLegal(laneChangeDirection, gtu.getGTUType(), this.direction);
232 if (!adjLanes.isEmpty())
233 {
234 return new LaneDirection(adjLanes.iterator().next(), this.direction);
235 }
236 return null;
237 }
238
239
240 @Override
241 public final String toString()
242 {
243 return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
244 }
245
246
247 @Override
248 public final int hashCode()
249 {
250 final int prime = 31;
251 int result = 1;
252 result = prime * result + ((this.direction == null) ? 0 : this.direction.hashCode());
253 result = prime * result + ((this.lane == null) ? 0 : this.lane.hashCode());
254 return result;
255 }
256
257
258 @Override
259 public final boolean equals(final Object obj)
260 {
261 if (this == obj)
262 {
263 return true;
264 }
265 if (obj == null)
266 {
267 return false;
268 }
269 if (getClass() != obj.getClass())
270 {
271 return false;
272 }
273 LaneDirection../../../org/opentrafficsim/road/network/lane/LaneDirection.html#LaneDirection">LaneDirection other = (LaneDirection) obj;
274 if (this.direction != other.direction)
275 {
276 return false;
277 }
278 if (this.lane == null)
279 {
280 if (other.lane != null)
281 {
282 return false;
283 }
284 }
285 else if (!this.lane.equals(other.lane))
286 {
287 return false;
288 }
289 return true;
290 }
291
292 }