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.DirectedPoint;
11 import org.opentrafficsim.core.geometry.OTSGeometryException;
12 import org.opentrafficsim.core.gtu.GTUDirectionality;
13 import org.opentrafficsim.core.network.LateralDirectionality;
14 import org.opentrafficsim.core.network.LinkDirection;
15 import org.opentrafficsim.core.network.NetworkException;
16 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
17
18
19
20
21
22
23
24
25
26
27
28
29 public class LaneDirection implements Serializable
30 {
31
32 private static final long serialVersionUID = 20160330L;
33
34
35 private final Lane lane;
36
37
38 private final GTUDirectionality direction;
39
40
41
42
43
44 public LaneDirection(final Lane lane, final GTUDirectionality direction)
45 {
46 this.lane = lane;
47 this.direction = direction;
48 }
49
50
51
52
53 public final Lane getLane()
54 {
55 return this.lane;
56 }
57
58
59
60
61 public final GTUDirectionality getDirection()
62 {
63 return this.direction;
64 }
65
66
67
68
69
70
71 public final Length coveredDistance(final double fraction)
72 {
73 if (this.direction.isPlus())
74 {
75 return getLane().getLength().times(fraction);
76 }
77 return getLane().getLength().times(1.0 - fraction);
78 }
79
80
81
82
83
84
85 public final Length remainingDistance(final double fraction)
86 {
87 if (this.direction.isPlus())
88 {
89 return getLane().getLength().times(1.0 - fraction);
90 }
91 return getLane().getLength().times(fraction);
92 }
93
94
95
96
97
98
99 public final double fractionAtCoveredDistance(final Length distance)
100 {
101 double f = this.lane.fraction(distance);
102 if (this.getDirection().isMinus())
103 {
104 f = 1.0 - f;
105 }
106 return f;
107 }
108
109
110
111
112
113
114 public final LaneDirection getNextLaneDirection(final LaneBasedGTU gtu)
115 {
116 ImmutableMap<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
117 if (next.isEmpty())
118 {
119 return null;
120 }
121
122 Set<LaneDirection> set = getNextForRoute(gtu);
123 if (set.size() == 1)
124 {
125 return set.iterator().next();
126 }
127
128 for (LaneDirection l : set)
129 {
130 if (l.getLane().getGtuList().contains(gtu))
131 {
132 return l;
133 }
134 }
135
136 return Try.assign(() -> gtu.getTacticalPlanner().chooseLaneAtSplit(this, set),
137 "Could not find suitable lane at split after lane %s in %s of link %s for GTU %s.", this.lane.getId(),
138 this.direction, this.lane.getParentLink().getId(), gtu.getId());
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 return out;
194 }
195
196
197
198
199
200 public Length getLength()
201 {
202 return this.lane.getLength();
203 }
204
205
206
207
208
209
210
211 public DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
212 {
213 DirectedPoint p = this.lane.getCenterLine().getLocationFraction(fraction);
214 if (this.direction.isMinus())
215 {
216 return new DirectedPoint(p.x, p.y, p.z, p.dirX, p.dirY, p.dirZ + Math.PI);
217 }
218 return p;
219 }
220
221
222
223
224
225
226
227 public final LaneDirection getAdjacentLaneDirection(final LateralDirectionality laneChangeDirection, final LaneBasedGTU gtu)
228 {
229 Set<Lane> adjLanes = this.lane.accessibleAdjacentLanesLegal(laneChangeDirection, gtu.getGTUType(), this.direction);
230 if (!adjLanes.isEmpty())
231 {
232 return new LaneDirection(adjLanes.iterator().next(), this.direction);
233 }
234 return null;
235 }
236
237
238 @Override
239 public final String toString()
240 {
241 return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
242 }
243
244
245 @Override
246 public final int hashCode()
247 {
248 final int prime = 31;
249 int result = 1;
250 result = prime * result + ((this.direction == null) ? 0 : this.direction.hashCode());
251 result = prime * result + ((this.lane == null) ? 0 : this.lane.hashCode());
252 return result;
253 }
254
255
256 @Override
257 public final boolean equals(final Object obj)
258 {
259 if (this == obj)
260 {
261 return true;
262 }
263 if (obj == null)
264 {
265 return false;
266 }
267 if (getClass() != obj.getClass())
268 {
269 return false;
270 }
271 LaneDirection other = (LaneDirection) obj;
272 if (this.direction != other.direction)
273 {
274 return false;
275 }
276 if (this.lane == null)
277 {
278 if (other.lane != null)
279 {
280 return false;
281 }
282 }
283 else if (!this.lane.equals(other.lane))
284 {
285 return false;
286 }
287 return true;
288 }
289
290 }