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 * Combines a Lane with its GTUDirectionality.
20 * <p>
21 * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
22 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
23 * </p>
24 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
25 * initial version Mar 30, 2016 <br>
26 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
28 */
29 public class LaneDirection implements Serializable
30 {
31 /** */
32 private static final long serialVersionUID = 20160330L;
33
34 /** The lane. */
35 private final Lane lane;
36
37 /** The GTU direction to drive on this lane. */
38 private final GTUDirectionality direction;
39
40 /**
41 * @param lane Lane; the lane
42 * @param direction GTUDirectionality; the direction to drive on this lane
43 */
44 public LaneDirection(final Lane lane, final GTUDirectionality direction)
45 {
46 this.lane = lane;
47 this.direction = direction;
48 }
49
50 /**
51 * @return the lane
52 */
53 public final Lane getLane()
54 {
55 return this.lane;
56 }
57
58 /**
59 * @return the direction to drive on this lane
60 */
61 public final GTUDirectionality getDirection()
62 {
63 return this.direction;
64 }
65
66 /**
67 * Returns the covered distance driven to the given fractional position.
68 * @param fraction double; fractional position
69 * @return Length; covered distance driven to the given fractional position
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 * Returns the remaining distance to be driven from the given fractional position.
82 * @param fraction double; fractional position
83 * @return Length; remaining distance to be driven from the given fractional position
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 * Returns the fraction along the design line for having covered the given distance.
96 * @param distance Length; covered distance
97 * @return double; fraction along the design line for having covered the given distance
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 * Returns the next lane and direction.
111 * @param gtu LaneBasedGTU; gtu
112 * @return LaneDirection; next lane and direction, {@code null} if none
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 // ask strategical planner
122 Set<LaneDirection> set = getNextForRoute(gtu);
123 if (set.size() == 1)
124 {
125 return set.iterator().next();
126 }
127 // check if the GTU is registered on any
128 for (LaneDirection l : set)
129 {
130 if (l.getLane().getGtuList().contains(gtu))
131 {
132 return l;
133 }
134 }
135 // ask tactical planner
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 * Returns a set of {@code LaneDirection}'s that can be followed considering the route.
143 * @param gtu LaneBasedGTU; GTU
144 * @return set of {@code LaneDirection}'s that can be followed considering the route
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 // if (out.size() == 0)
172 // {
173 // gtu.getSimulator().getLogger().always().warn(
174 // "Could not find a next segment for GTU \"{}\" on lane {}, on route {}; LinkDirection is {}, {}",
175 // gtu.getId(), this.lane, next.keySet(), ld.getLink().getId(), ld.getDirection());
176 // gtu.getSimulator().getLogger().always().warn(gtu.getStrategicalPlanner().getRoute());
177 // for (Lane l : next.keySet())
178 // {
179 // GTUDirectionality dir = next.get(l);
180 // gtu.getSimulator().getLogger().always().info("examining l={}, dir={}", l, dir);
181 // if (!l.getParentLink().equals(ld.getLink()))
182 // {
183 // gtu.getSimulator().getLogger().always()
184 // .info("not including lane {} with direction {} because \"parent link does not match\"", l, dir);
185 // }
186 // if (!dir.equals(ld.getDirection()))
187 // {
188 // gtu.getSimulator().getLogger().always()
189 // .info("not including lane {} with direction {} because direction does not match", l, dir);
190 // }
191 // }
192 // }
193 return out;
194 }
195
196 /**
197 * Returns the length of the lane.
198 * @return Length; length of the lane
199 */
200 public Length getLength()
201 {
202 return this.lane.getLength();
203 }
204
205 /**
206 * Returns a directed point at the given fraction, in the direction of travel (not center line).
207 * @param fraction double; fractional position
208 * @return directed point at the given fraction, in the direction of travel
209 * @throws OTSGeometryException in case the fractional position is not correct
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 * Returns the adjacent lane and direction.
223 * @param gtu LaneBasedGTU; gtu
224 * @param laneChangeDirection LateralDirectionality; lane change direction
225 * @return LaneDirection; adjacent lane and direction, {@code null} if none
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 /** {@inheritDoc} */
238 @Override
239 public final String toString()
240 {
241 return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
242 }
243
244 /** {@inheritDoc} */
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 /** {@inheritDoc} */
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../../../org/opentrafficsim/road/network/lane/LaneDirection.html#LaneDirection">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 }