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  
21  
22  
23  
24  
25  
26  
27  
28  
29  public class LaneDirection implements Serializable
30  {
31      
32      private static final long serialVersionUID = 20160330L;
33  
34      
35      private final Lane lane;
36  
37      
38      private final GTUDirectionality direction;
39  
40      
41  
42  
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  
53  
54      public final Lane getLane()
55      {
56          return this.lane;
57      }
58  
59      
60  
61  
62      public final GTUDirectionality getDirection()
63      {
64          return this.direction;
65      }
66  
67      
68  
69  
70  
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  
83  
84  
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  
97  
98  
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 
112 
113 
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         
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             
152             return Try.assign(() -> gtu.getTacticalPlanner().chooseLaneAtSplit(out), "Missing parameter.");
153         }
154     }
155 
156     
157 
158 
159 
160     public Length getLength()
161     {
162         return this.lane.getLength();
163     }
164 
165     
166 
167 
168 
169 
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 
183 
184 
185 
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     
198     @Override
199     public final String toString()
200     {
201         return "[" + this.lane + (this.direction.isPlus() ? " +]" : " -]");
202     }
203 
204     
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     
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 }