View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception.categories;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.List;
6   import java.util.Map;
7   import java.util.SortedSet;
8   
9   import org.djunits.value.vdouble.scalar.Length;
10  import org.djunits.value.vdouble.scalar.Speed;
11  import org.opentrafficsim.base.TimeStampedObject;
12  import org.opentrafficsim.base.parameters.ParameterException;
13  import org.opentrafficsim.base.parameters.ParameterTypeLength;
14  import org.opentrafficsim.base.parameters.ParameterTypes;
15  import org.opentrafficsim.core.geometry.OTSGeometryException;
16  import org.opentrafficsim.core.geometry.OTSLine3D;
17  import org.opentrafficsim.core.gtu.GTUException;
18  import org.opentrafficsim.core.gtu.RelativePosition;
19  import org.opentrafficsim.core.gtu.Try;
20  import org.opentrafficsim.core.network.LongitudinalDirectionality;
21  import org.opentrafficsim.core.network.NetworkException;
22  import org.opentrafficsim.core.network.route.Route;
23  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
24  import org.opentrafficsim.road.gtu.lane.perception.LaneBasedObjectIterable;
25  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
26  import org.opentrafficsim.road.gtu.lane.perception.LaneStructure.Entry;
27  import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
28  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
29  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
30  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
31  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
32  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
33  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
34  import org.opentrafficsim.road.network.lane.CrossSectionLink;
35  import org.opentrafficsim.road.network.lane.Lane;
36  import org.opentrafficsim.road.network.lane.conflict.Conflict;
37  import org.opentrafficsim.road.network.lane.conflict.Conflict.ConflictEnd;
38  import org.opentrafficsim.road.network.lane.conflict.ConflictPriority;
39  import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
40  import org.opentrafficsim.road.network.lane.conflict.ConflictType;
41  import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
42  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
43  
44  import nl.tudelft.simulation.language.Throw;
45  
46  /**
47   * Perceives traffic lights and intersection conflicts.
48   * <p>
49   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
50   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
51   * <p>
52   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 22, 2016 <br>
53   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
54   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
55   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
56   */
57  public class DirectIntersectionPerception extends LaneBasedAbstractPerceptionCategory implements IntersectionPerception
58  {
59  
60      /** */
61      private static final long serialVersionUID = 20160811L;
62  
63      /** Look ahead parameter type. */
64      protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
65  
66      /** Margin to find upstream conflicts who's ends are downstream, needed as the algorithm searches beyond a location. */
67      private static final Length MARGIN = Length.createSI(0.001);
68  
69      /** Set of traffic lights. */
70      private Map<RelativeLane, TimeStampedObject<PerceptionCollectable<HeadwayTrafficLight, TrafficLight>>> trafficLights =
71              new HashMap<>();
72  
73      /** Set of conflicts. */
74      private Map<RelativeLane, TimeStampedObject<PerceptionCollectable<HeadwayConflict, Conflict>>> conflicts = new HashMap<>();
75  
76      /** Conflicts alongside left. */
77      private TimeStampedObject<Boolean> alongsideConflictLeft;
78  
79      /** Conflicts alongside right. */
80      private TimeStampedObject<Boolean> alongsideConflictRight;
81  
82      /** Headway GTU type that should be used. */
83      private final HeadwayGtuType headwayGtuType;
84  
85      /**
86       * @param perception perception
87       * @param headwayGtuType type of headway gtu to generate
88       */
89      public DirectIntersectionPerception(final LanePerception perception, final HeadwayGtuType headwayGtuType)
90      {
91          super(perception);
92          this.headwayGtuType = headwayGtuType;
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public final void updateTrafficLights() throws GTUException, ParameterException
98      {
99          this.trafficLights.clear();
100         Route route = getPerception().getGtu().getStrategicalPlanner().getRoute();
101         for (RelativeLane lane : getPerception().getLaneStructure().getExtendedCrossSection())
102         {
103             LaneStructureRecord record = getPerception().getLaneStructure().getFirstRecord(lane);
104             Length pos = record.getStartDistance().neg();
105             pos = record.getDirection().isPlus() ? pos.plus(getGtu().getFront().getDx())
106                     : pos.minus(getGtu().getFront().getDx());
107             PerceptionCollectable<HeadwayTrafficLight, TrafficLight> it =
108                     new LaneBasedObjectIterable<HeadwayTrafficLight, TrafficLight>(getGtu(), TrafficLight.class, record,
109                             Length.max(Length.ZERO, pos), getGtu().getParameters().getParameter(LOOKAHEAD), getGtu().getFront(),
110                             route)
111                     {
112                         /** {@inheritDoc} */
113                         @Override
114                         public HeadwayTrafficLight perceive(final LaneBasedGTU perceivingGtu, final TrafficLight trafficLight,
115                                 final Length distance)
116                         {
117                             try
118                             {
119                                 return new HeadwayTrafficLight(trafficLight, distance);
120                             }
121                             catch (GTUException exception)
122                             {
123                                 throw new RuntimeException(exception);
124                             }
125                         }
126                     };
127             this.trafficLights.put(lane, new TimeStampedObject<>(it, getTimestamp()));
128         }
129     }
130 
131     /** {@inheritDoc} */
132     @Override
133     public final void updateConflicts() throws GTUException, ParameterException
134     {
135         this.conflicts.clear();
136         Route route = getPerception().getGtu().getStrategicalPlanner().getRoute();
137         for (RelativeLane lane : getPerception().getLaneStructure().getExtendedCrossSection())
138         {
139             LaneStructureRecord record = getPerception().getLaneStructure().getFirstRecord(lane);
140             Length pos = record.getStartDistance().neg().plus(getGtu().getRear().getDx());
141             while (pos.lt0() && !record.getPrev().isEmpty())
142             {
143                 pos = pos.plus(record.getLength());
144                 record = record.getPrev().get(0);
145             }
146             // find all ConflictEnd, and the most upstream relating position
147             List<LaneBasedObject> laneObjs;
148             if (record.isDownstreamBranch())
149             {
150                 if (record.getDirection().isPlus())
151                 {
152                     laneObjs = record.getLane().getLaneBasedObjects(Length.max(Length.ZERO, pos), record.getLane().getLength());
153                 }
154                 else
155                 {
156                     laneObjs = record.getLane().getLaneBasedObjects(Length.ZERO, pos);
157                 }
158             }
159             else
160             {
161                 laneObjs = new ArrayList<>();
162             }
163             // TODO if conflicts span multiple lanes, this within-lane search fails
164             for (LaneBasedObject object : laneObjs)
165             {
166                 if (object instanceof ConflictEnd)
167                 {
168                     Conflict c = ((ConflictEnd) object).getConflict();
169                     Length cPos = record.getDirection().isPlus() ? c.getLongitudinalPosition().minus(MARGIN)
170                             : c.getLongitudinalPosition().plus(MARGIN);
171                     pos = record.getDirection().isPlus() ? Length.min(pos, cPos) : Length.max(pos, cPos);
172                 }
173             }
174             PerceptionCollectable<HeadwayConflict, Conflict> it = new LaneBasedObjectIterable<HeadwayConflict, Conflict>(
175                     getGtu(), Conflict.class, record, Length.max(MARGIN.neg(), pos),
176                     getGtu().getParameters().getParameter(LOOKAHEAD), getGtu().getFront(), route)
177             {
178                 /** {@inheritDoc} */
179                 @SuppressWarnings("synthetic-access")
180                 @Override
181                 public HeadwayConflict perceive(final LaneBasedGTU perceivingGtu, final Conflict conflict,
182                         final Length distance)
183                 {
184                     Conflict otherConflict = conflict.getOtherConflict();
185                     ConflictType conflictType = conflict.getConflictType();
186                     ConflictPriority conflictPriority = conflict.conflictPriority();
187                     Class<? extends ConflictRule> conflictRuleType = conflict.getConflictRule().getClass();
188                     String id = conflict.getId();
189                     Length length = conflict.getLength();
190                     Length conflictingLength = otherConflict.getLength();
191                     CrossSectionLink conflictingLink = otherConflict.getLane().getParentLink();
192 
193                     // TODO get from link combination (needs to be a map property on the links)
194                     Length lookAhead =
195                             Try.assign(() -> getGtu().getParameters().getParameter(LOOKAHEAD), "Parameter not present.");
196                     Length conflictingVisibility = lookAhead;
197                     Speed conflictingSpeedLimit;
198                     try
199                     {
200                         conflictingSpeedLimit = otherConflict.getLane().getHighestSpeedLimit();
201                     }
202                     catch (NetworkException exception)
203                     {
204                         throw new RuntimeException("GTU type not available on conflicting lane.", exception);
205                     }
206 
207                     LongitudinalDirectionality otherDir = otherConflict.getDirection();
208                     Throw.when(otherDir.isBoth(), UnsupportedOperationException.class,
209                             "Conflicts on lanes with direction BOTH are not supported.");
210                     // TODO limit 'conflictingVisibility' to first upstream traffic light, so GTU's behind it are
211                     // ignored
212 
213                     HeadwayConflict headwayConflict;
214                     try
215                     {
216                         PerceptionCollectable<HeadwayGTU, LaneBasedGTU> upstreamConflictingGTUs = otherConflict.getUpstreamGtus(
217                                 getGtu(), DirectIntersectionPerception.this.headwayGtuType, conflictingVisibility);
218                         PerceptionCollectable<HeadwayGTU, LaneBasedGTU> downstreamConflictingGTUs =
219                                 otherConflict.getDownstreamGtus(getGtu(), DirectIntersectionPerception.this.headwayGtuType,
220                                         conflictingVisibility);
221                         // TODO stop lines (current models happen not to use this, but should be possible)
222                         HeadwayStopLine stopLine = new HeadwayStopLine("stopLineId", Length.ZERO);
223                         HeadwayStopLine conflictingStopLine = new HeadwayStopLine("conflictingStopLineId", Length.ZERO);
224 
225                         Lane thisLane = conflict.getLane();
226                         Lane otherLane = otherConflict.getLane();
227                         Length pos1a = conflict.getLongitudinalPosition();
228                         Length pos2a = otherConflict.getLongitudinalPosition();
229                         Length pos1b = Length.min(pos1a.plus(conflict.getLength()), thisLane.getLength());
230                         Length pos2b = Length.min(pos2a.plus(otherConflict.getLength()), otherLane.getLength());
231                         OTSLine3D line1 = thisLane.getCenterLine();
232                         OTSLine3D line2 = otherLane.getCenterLine();
233                         double dStart = line1.getLocation(pos1a).distance(line2.getLocation(pos2a));
234                         double dEnd = line1.getLocation(pos1b).distance(line2.getLocation(pos2b));
235                         Length startWidth =
236                                 Length.createSI(dStart + .5 * thisLane.getWidth(pos1a).si + .5 * otherLane.getWidth(pos2a).si);
237                         Length endWidth =
238                                 Length.createSI(dEnd + .5 * thisLane.getWidth(pos1b).si + .5 * otherLane.getWidth(pos2b).si);
239 
240                         headwayConflict = new HeadwayConflict(conflictType, conflictPriority, conflictRuleType, id, distance,
241                                 length, conflictingLength, upstreamConflictingGTUs, downstreamConflictingGTUs,
242                                 conflictingVisibility, conflictingSpeedLimit, conflictingLink,
243                                 HeadwayConflict.Width.linear(startWidth, endWidth), stopLine, conflictingStopLine);
244                     }
245                     catch (GTUException | OTSGeometryException exception)
246                     {
247                         throw new RuntimeException("Could not create headway objects.", exception);
248                     }
249                     // TODO
250                     // if (trafficLightDistance != null && trafficLightDistance.le(lookAhead))
251                     // {
252                     // headwayConflict.setConflictingTrafficLight(trafficLightDistance, conflict.isPermitted());
253                     // }
254                     return headwayConflict;
255                 }
256             };
257             this.conflicts.put(lane, new TimeStampedObject<>(it, getTimestamp()));
258         }
259 
260         // alongside
261         for (RelativeLane lane : new RelativeLane[] { RelativeLane.LEFT, RelativeLane.RIGHT })
262         {
263             if (getPerception().getLaneStructure().getExtendedCrossSection().contains(lane))
264             {
265                 SortedSet<Entry<Conflict>> conflictEntries = getPerception().getLaneStructure().getUpstreamObjects(lane,
266                         Conflict.class, getGtu(), RelativePosition.FRONT);
267                 boolean alongside = false;
268                 if (!conflictEntries.isEmpty())
269                 {
270                     Entry<Conflict> entry = conflictEntries.first();
271                     alongside = entry.getDistance().si < entry.getLaneBasedObject().getLength().si + getGtu().getLength().si;
272                 }
273                 if (lane.isLeft())
274                 {
275                     this.alongsideConflictLeft = new TimeStampedObject<>(alongside, getTimestamp());
276                 }
277                 else
278                 {
279                     this.alongsideConflictRight = new TimeStampedObject<>(alongside, getTimestamp());
280                 }
281             }
282         }
283     }
284 
285     /** {@inheritDoc} */
286     @Override
287     public final Iterable<HeadwayTrafficLight> getTrafficLights(final RelativeLane lane)
288     {
289         return getObjectOrNull(this.trafficLights.get(lane));
290     }
291 
292     /** {@inheritDoc} */
293     @Override
294     public final PerceptionCollectable<HeadwayConflict, Conflict> getConflicts(final RelativeLane lane)
295     {
296         return getObjectOrNull(this.conflicts.get(lane));
297     }
298 
299     /** {@inheritDoc} */
300     @Override
301     public final boolean isAlongsideConflictLeft()
302     {
303         return this.alongsideConflictLeft == null ? false : this.alongsideConflictLeft.getObject();
304     }
305 
306     /** {@inheritDoc} */
307     @Override
308     public final boolean isAlongsideConflictRight()
309     {
310         return this.alongsideConflictRight == null ? false : this.alongsideConflictRight.getObject();
311     }
312 
313     /**
314      * Returns a time stamped set of traffic lights along the route. Traffic lights are sorted by headway value.
315      * @param lane lane
316      * @return set of traffic lights along the route
317      */
318     public final TimeStampedObject<PerceptionCollectable<HeadwayTrafficLight, TrafficLight>> getTimeStampedTrafficLights(
319             final RelativeLane lane)
320     {
321         return this.trafficLights.get(lane);
322     }
323 
324     /**
325      * Returns a time stamped set of traffic lights along the route. Traffic lights are sorted by headway value.
326      * @param lane lane
327      * @return set of traffic lights along the route
328      */
329     public final TimeStampedObject<PerceptionCollectable<HeadwayConflict, Conflict>> getTimeStampedConflicts(
330             final RelativeLane lane)
331     {
332         return this.conflicts.get(lane);
333     }
334 
335     /**
336      * Returns whether there is a conflict alongside to the left, time stamped.
337      * @return whether there is a conflict alongside to the left
338      */
339     public final TimeStampedObject<Boolean> isAlongsideConflictLeftTimeStamped()
340     {
341         return this.alongsideConflictLeft;
342     }
343 
344     /**
345      * Returns whether there is a conflict alongside to the right, time stamped.
346      * @return whether there is a conflict alongside to the right
347      */
348     public final TimeStampedObject<Boolean> isAlongsideConflictRightTimeStamped()
349     {
350         return this.alongsideConflictRight;
351     }
352 
353     /** {@inheritDoc} */
354     @Override
355     public final String toString()
356     {
357         return "DirectIntersectionPerception";
358     }
359 
360 }