View Javadoc
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 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 }