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.djutils.exceptions.Try;
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-2019 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          super();
48          this.lane = lane;
49          this.direction = direction;
50      }
51  
52      /**
53       * @return the lane
54       */
55      public final Lane getLane()
56      {
57          return this.lane;
58      }
59  
60      /**
61       * @return the direction to drive on this lane
62       */
63      public final GTUDirectionality getDirection()
64      {
65          return this.direction;
66      }
67  
68      /**
69       * Returns the covered distance driven to the given fractional position.
70       * @param fraction double; fractional position
71       * @return Length; covered distance driven to the given fractional position
72       */
73      public final Length coveredDistance(final double fraction)
74      {
75          if (this.direction.isPlus())
76          {
77              return getLane().getLength().multiplyBy(fraction);
78          }
79          return getLane().getLength().multiplyBy(1.0 - fraction);
80      }
81  
82      /**
83       * Returns the remaining distance to be driven from the given fractional position.
84       * @param fraction double; fractional position
85       * @return Length; remaining distance to be driven from the given fractional position
86       */
87      public final Length remainingDistance(final double fraction)
88      {
89          if (this.direction.isPlus())
90          {
91              return getLane().getLength().multiplyBy(1.0 - fraction);
92          }
93          return getLane().getLength().multiplyBy(fraction);
94      }
95  
96      /**
97       * Returns the fraction along the design line for having covered the given distance.
98       * @param distance Length; covered distance
99       * @return double; fraction along the design line for having covered the given distance
100      */
101     public final double fractionAtCoveredDistance(final Length distance)
102     {
103         double f = this.lane.fraction(distance);
104         if (this.getDirection().isMinus())
105         {
106             f = 1.0 - f;
107         }
108         return f;
109     }
110 
111     /**
112      * Returns the next lane and direction.
113      * @param gtu LaneBasedGTU; gtu
114      * @return LaneDirection; next lane and direction, {@code null} if none
115      */
116     public final LaneDirection getNextLaneDirection(final LaneBasedGTU gtu)
117     {
118         Map<Lane, GTUDirectionality> next = this.lane.downstreamLanes(this.direction, gtu.getGTUType());
119         if (next.isEmpty())
120         {
121             return null;
122         }
123         // ask strategical planner
124         LinkDirection ld;
125         try
126         {
127             ld = gtu.getStrategicalPlanner().nextLinkDirection(this.lane.getParentLink(), this.direction, gtu.getGTUType());
128         }
129         catch (NetworkException exception)
130         {
131             throw new RuntimeException("Strategical planner experiences exception on network.", exception);
132         }
133         Set<LaneDirection> out = new HashSet<>();
134         for (Lane l : next.keySet())
135         {
136             GTUDirectionality dir = next.get(l);
137             if (l.getParentLink().equals(ld.getLink()) && dir.equals(ld.getDirection()))
138             {
139                 out.add(new LaneDirection(l, dir));
140             }
141         }
142         if (out.isEmpty())
143         {
144             return null;
145         }
146         else if (out.size() == 1)
147         {
148             return out.iterator().next();
149         }
150         else
151         {
152             // ask tactical planner
153             return Try.assign(() -> gtu.getTacticalPlanner().chooseLaneAtSplit(out), "Missing parameter.");
154         }
155     }
156 
157     /**
158      * Returns the length of the lane.
159      * @return Length; length of the lane
160      */
161     public Length getLength()
162     {
163         return this.lane.getLength();
164     }
165 
166     /**
167      * Returns a directed point at the given fraction, in the direction of travel (not center line).
168      * @param fraction double; fractional position
169      * @return directed point at the given fraction, in the direction of travel
170      * @throws OTSGeometryException in case the fractional position is not correct
171      */
172     public DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
173     {
174         DirectedPoint p = this.lane.getCenterLine().getLocationFraction(fraction);
175         if (this.direction.isMinus())
176         {
177             p.setRotZ(p.getRotZ() + Math.PI);
178         }
179         return p;
180     }
181 
182     /**
183      * Returns the adjacent lane and direction.
184      * @param gtu LaneBasedGTU; gtu
185      * @param laneChangeDirection LateralDirectionality; lane change direction
186      * @return LaneDirection; adjacent lane and direction, {@code null} if none
187      */
188     public final LaneDirection getAdjacentLaneDirection(final LateralDirectionality laneChangeDirection, final LaneBasedGTU gtu)
189     {
190         Set<Lane> adjLanes = this.lane.accessibleAdjacentLanesLegal(laneChangeDirection, gtu.getGTUType(), this.direction);
191         if (!adjLanes.isEmpty())
192         {
193             return new LaneDirection(adjLanes.iterator().next(), this.direction);
194         }
195         return null;
196     }
197 
198     /** {@inheritDoc} */
199     @Override
200     public final String toString()
201     {
202         return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
203     }
204 
205     /** {@inheritDoc} */
206     @Override
207     public final int hashCode()
208     {
209         final int prime = 31;
210         int result = 1;
211         result = prime * result + ((this.direction == null) ? 0 : this.direction.hashCode());
212         result = prime * result + ((this.lane == null) ? 0 : this.lane.hashCode());
213         return result;
214     }
215 
216     /** {@inheritDoc} */
217     @Override
218     public final boolean equals(final Object obj)
219     {
220         if (this == obj)
221         {
222             return true;
223         }
224         if (obj == null)
225         {
226             return false;
227         }
228         if (getClass() != obj.getClass())
229         {
230             return false;
231         }
232         LaneDirection other = (LaneDirection) obj;
233         if (this.direction != other.direction)
234         {
235             return false;
236         }
237         if (this.lane == null)
238         {
239             if (other.lane != null)
240             {
241                 return false;
242             }
243         }
244         else if (!this.lane.equals(other.lane))
245         {
246             return false;
247         }
248         return true;
249     }
250 
251 }