View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.io.Serializable;
4   import java.util.HashSet;
5   import java.util.Map;
6   import java.util.Set;
7   
8   import org.djunits.value.vdouble.scalar.Length;
9   import org.opentrafficsim.core.geometry.OTSGeometryException;
10  import org.opentrafficsim.core.gtu.GTUDirectionality;
11  import org.opentrafficsim.core.gtu.Try;
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   * <p>
21   * Copyright (c) 2013-2018 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 the lane
42       * @param direction the direction to drive on this lane
43       */
44      public LaneDirection(final Lane lane, final GTUDirectionality direction)
45      {
46          super();
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().multiplyBy(fraction);
77          }
78          return getLane().getLength().multiplyBy(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().multiplyBy(1.0 - fraction);
91          }
92          return getLane().getLength().multiplyBy(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         Map<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
118         if (next.isEmpty())
119         {
120             return null;
121         }
122         // ask strategical planner
123         LinkDirection ld;
124         try
125         {
126             ld = gtu.getStrategicalPlanner().nextLinkDirection(this.lane.getParentLink(), this.direction, gtu.getGTUType());
127         }
128         catch (NetworkException exception)
129         {
130             throw new RuntimeException("Strategical planner experiences exception on network.", exception);
131         }
132         Set<LaneDirection> out = new HashSet<>();
133         for (Lane l : next.keySet())
134         {
135             GTUDirectionality dir = next.get(l);
136             if (l.getParentLink().equals(ld.getLink()) && dir.equals(ld.getDirection()))
137             {
138                 out.add(new LaneDirection(l, dir));
139             }
140         }
141         if (out.isEmpty())
142         {
143             return null;
144         }
145         else if (out.size() == 1)
146         {
147             return out.iterator().next();
148         }
149         else
150         {
151             // ask tactical planner
152             return Try.assign(() -> gtu.getTacticalPlanner().chooseLaneAtSplit(out), "Missing parameter.");
153         }
154     }
155 
156     /**
157      * Returns the length of the lane.
158      * @return Length; length of the lane
159      */
160     public Length getLength()
161     {
162         return this.lane.getLength();
163     }
164 
165     /**
166      * Returns a directed point at the given fraction, in the direction of travel (not center line).
167      * @param fraction double; fractional position
168      * @return directed point at the given fraction, in the direction of travel
169      * @throws OTSGeometryException in case the fractional position is not correct
170      */
171     public DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
172     {
173         DirectedPoint p = this.lane.getCenterLine().getLocationFraction(fraction);
174         if (this.direction.isMinus())
175         {
176             p.setRotZ(p.getRotZ() + Math.PI);
177         }
178         return p;
179     }
180 
181     /**
182      * Returns the adjacent lane and direction.
183      * @param gtu LaneBasedGTU; gtu
184      * @param laneChangeDirection LateralDirectionality; lane change direction
185      * @return LaneDirection; adjacent lane and direction, {@code null} if none
186      */
187     public final LaneDirection getAdjacentLaneDirection(final LateralDirectionality laneChangeDirection, final LaneBasedGTU gtu)
188     {
189         Set<Lane> adjLanes = this.lane.accessibleAdjacentLanesLegal(laneChangeDirection, gtu.getGTUType(), this.direction);
190         if (!adjLanes.isEmpty())
191         {
192             return new LaneDirection(adjLanes.iterator().next(), this.direction);
193         }
194         return null;
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     public final String toString()
200     {
201         return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public final int hashCode()
207     {
208         final int prime = 31;
209         int result = 1;
210         result = prime * result + ((this.direction == null) ? 0 : this.direction.hashCode());
211         result = prime * result + ((this.lane == null) ? 0 : this.lane.hashCode());
212         return result;
213     }
214 
215     /** {@inheritDoc} */
216     @Override
217     public final boolean equals(final Object obj)
218     {
219         if (this == obj)
220         {
221             return true;
222         }
223         if (obj == null)
224         {
225             return false;
226         }
227         if (getClass() != obj.getClass())
228         {
229             return false;
230         }
231         LaneDirection other = (LaneDirection) obj;
232         if (this.direction != other.direction)
233         {
234             return false;
235         }
236         if (this.lane == null)
237         {
238             if (other.lane != null)
239             {
240                 return false;
241             }
242         }
243         else if (!this.lane.equals(other.lane))
244         {
245             return false;
246         }
247         return true;
248     }
249 
250 }