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 * Combines a Lane with its GTUDirectionality.
21 * <p>
22 * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
24 * </p>
25 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
26 * initial version Mar 30, 2016 <br>
27 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
29 */
30 public class LaneDirection implements Serializable
31 {
32 /** */
33 private static final long serialVersionUID = 20160330L;
34
35 /** The lane. */
36 private final Lane lane;
37
38 /** The GTU direction to drive on this lane. */
39 private final GTUDirectionality direction;
40
41 /**
42 * @param lane Lane; the lane
43 * @param direction GTUDirectionality; the direction to drive on this lane
44 */
45 public LaneDirection(final Lane lane, final GTUDirectionality direction)
46 {
47 this.lane = lane;
48 this.direction = direction;
49 }
50
51 /**
52 * @return the lane
53 */
54 public final Lane getLane()
55 {
56 return this.lane;
57 }
58
59 /**
60 * @return the direction to drive on this lane
61 */
62 public final GTUDirectionality getDirection()
63 {
64 return this.direction;
65 }
66
67 /**
68 * Returns the covered distance driven to the given fractional position.
69 * @param fraction double; fractional position
70 * @return Length; covered distance driven to the given fractional position
71 */
72 public final Length coveredDistance(final double fraction)
73 {
74 if (this.direction.isPlus())
75 {
76 return getLane().getLength().times(fraction);
77 }
78 return getLane().getLength().times(1.0 - fraction);
79 }
80
81 /**
82 * Returns the remaining distance to be driven from the given fractional position.
83 * @param fraction double; fractional position
84 * @return Length; remaining distance to be driven from the given fractional position
85 */
86 public final Length remainingDistance(final double fraction)
87 {
88 if (this.direction.isPlus())
89 {
90 return getLane().getLength().times(1.0 - fraction);
91 }
92 return getLane().getLength().times(fraction);
93 }
94
95 /**
96 * Returns the fraction along the design line for having covered the given distance.
97 * @param distance Length; covered distance
98 * @return double; fraction along the design line for having covered the given distance
99 */
100 public final double fractionAtCoveredDistance(final Length distance)
101 {
102 double f = this.lane.fraction(distance);
103 if (this.getDirection().isMinus())
104 {
105 f = 1.0 - f;
106 }
107 return f;
108 }
109
110 /**
111 * Returns the next lane and direction.
112 * @param gtu LaneBasedGTU; gtu
113 * @return LaneDirection; next lane and direction, {@code null} if none
114 */
115 public final LaneDirection getNextLaneDirection(final LaneBasedGTU gtu)
116 {
117 ImmutableMap<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
118 if (next.isEmpty())
119 {
120 return null;
121 }
122 // ask strategical planner
123 Set<LaneDirection> set = getNextForRoute(gtu);
124 if (set.size() == 1)
125 {
126 return set.iterator().next();
127 }
128 // check if the GTU is registered on any
129 for (LaneDirection l : set)
130 {
131 if (l.getLane().getGtuList().contains(gtu))
132 {
133 return l;
134 }
135 }
136 // ask tactical planner
137 return Try.assign(() -> gtu.getTacticalPlanner().chooseLaneAtSplit(this, set),
138 "Could not find suitable lane at split after lane %s in %s of link %s for GTU %s.", this.lane.getId(),
139 this.direction, this.lane.getParentLink().getId(), gtu.getId());
140 }
141
142 /**
143 * Returns a set of {@code LaneDirection}'s that can be followed considering the route.
144 * @param gtu LaneBasedGTU; GTU
145 * @return set of {@code LaneDirection}'s that can be followed considering the route
146 */
147 public Set<LaneDirection> getNextForRoute(final LaneBasedGTU gtu)
148 {
149 ImmutableMap<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
150 if (next.isEmpty())
151 {
152 return null;
153 }
154 LinkDirection ld;
155 try
156 {
157 ld = gtu.getStrategicalPlanner().nextLinkDirection(this.lane.getParentLink(), this.direction, gtu.getGTUType());
158 }
159 catch (NetworkException exception)
160 {
161 throw new RuntimeException("Strategical planner experiences exception on network.", exception);
162 }
163 Set<LaneDirection> out = new LinkedHashSet<>();
164 for (Lane l : next.keySet())
165 {
166 GTUDirectionality dir = next.get(l);
167 if (l.getParentLink().equals(ld.getLink()) && dir.equals(ld.getDirection()))
168 {
169 out.add(new LaneDirection(l, dir));
170 }
171 }
172 // if (out.size() == 0)
173 // {
174 // gtu.getSimulator().getLogger().always().warn(
175 // "Could not find a next segment for GTU \"{}\" on lane {}, on route {}; LinkDirection is {}, {}",
176 // gtu.getId(), this.lane, next.keySet(), ld.getLink().getId(), ld.getDirection());
177 // gtu.getSimulator().getLogger().always().warn(gtu.getStrategicalPlanner().getRoute());
178 // for (Lane l : next.keySet())
179 // {
180 // GTUDirectionality dir = next.get(l);
181 // gtu.getSimulator().getLogger().always().info("examining l={}, dir={}", l, dir);
182 // if (!l.getParentLink().equals(ld.getLink()))
183 // {
184 // gtu.getSimulator().getLogger().always()
185 // .info("not including lane {} with direction {} because \"parent link does not match\"", l, dir);
186 // }
187 // if (!dir.equals(ld.getDirection()))
188 // {
189 // gtu.getSimulator().getLogger().always()
190 // .info("not including lane {} with direction {} because direction does not match", l, dir);
191 // }
192 // }
193 // }
194 return out;
195 }
196
197 /**
198 * Returns the length of the lane.
199 * @return Length; length of the lane
200 */
201 public Length getLength()
202 {
203 return this.lane.getLength();
204 }
205
206 /**
207 * Returns a directed point at the given fraction, in the direction of travel (not center line).
208 * @param fraction double; fractional position
209 * @return directed point at the given fraction, in the direction of travel
210 * @throws OTSGeometryException in case the fractional position is not correct
211 */
212 public DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
213 {
214 DirectedPoint p = this.lane.getCenterLine().getLocationFraction(fraction);
215 if (this.direction.isMinus())
216 {
217 p.setRotZ(p.getRotZ() + Math.PI);
218 }
219 return p;
220 }
221
222 /**
223 * Returns the adjacent lane and direction.
224 * @param gtu LaneBasedGTU; gtu
225 * @param laneChangeDirection LateralDirectionality; lane change direction
226 * @return LaneDirection; adjacent lane and direction, {@code null} if none
227 */
228 public final LaneDirection getAdjacentLaneDirection(final LateralDirectionality laneChangeDirection, final LaneBasedGTU gtu)
229 {
230 Set<Lane> adjLanes = this.lane.accessibleAdjacentLanesLegal(laneChangeDirection, gtu.getGTUType(), this.direction);
231 if (!adjLanes.isEmpty())
232 {
233 return new LaneDirection(adjLanes.iterator().next(), this.direction);
234 }
235 return null;
236 }
237
238 /** {@inheritDoc} */
239 @Override
240 public final String toString()
241 {
242 return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
243 }
244
245 /** {@inheritDoc} */
246 @Override
247 public final int hashCode()
248 {
249 final int prime = 31;
250 int result = 1;
251 result = prime * result + ((this.direction == null) ? 0 : this.direction.hashCode());
252 result = prime * result + ((this.lane == null) ? 0 : this.lane.hashCode());
253 return result;
254 }
255
256 /** {@inheritDoc} */
257 @Override
258 public final boolean equals(final Object obj)
259 {
260 if (this == obj)
261 {
262 return true;
263 }
264 if (obj == null)
265 {
266 return false;
267 }
268 if (getClass() != obj.getClass())
269 {
270 return false;
271 }
272 LaneDirection../../../org/opentrafficsim/road/network/lane/LaneDirection.html#LaneDirection">LaneDirection other = (LaneDirection) obj;
273 if (this.direction != other.direction)
274 {
275 return false;
276 }
277 if (this.lane == null)
278 {
279 if (other.lane != null)
280 {
281 return false;
282 }
283 }
284 else if (!this.lane.equals(other.lane))
285 {
286 return false;
287 }
288 return true;
289 }
290
291 }