View Javadoc
1   package org.opentrafficsim.road.network;
2   
3   import java.util.Map;
4   import java.util.Optional;
5   import java.util.Set;
6   
7   import org.djunits.value.vdouble.scalar.Duration;
8   import org.djutils.exceptions.Throw;
9   import org.opentrafficsim.core.gtu.GtuType;
10  import org.opentrafficsim.core.network.LateralDirectionality;
11  import org.opentrafficsim.road.network.speed.LaneSpeedLimits;
12  import org.opentrafficsim.road.network.speed.SpeedLimit;
13  import org.opentrafficsim.road.network.speed.SpeedLimits;
14  
15  /**
16   * This class is mostly the same as a Lane. But as a shoulder it can be recognized by algorithms and models to be responded to
17   * differently.
18   * <p>
19   * Copyright (c) 2024-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
21   * </p>
22   * @author Wouter Schakel
23   */
24  public class Shoulder extends Lane
25  {
26  
27      /** Nearest lane. */
28      private Lane lane;
29  
30      /**
31       * Constructor specifying geometry.
32       * @param link link
33       * @param id the id of this lane within the link; should be unique within the link
34       * @param geometry geometry
35       * @param laneType lane type
36       */
37      public Shoulder(final CrossSectionLink link, final String id, final CrossSectionGeometry geometry, final LaneType laneType)
38      {
39          super(link, id, geometry, laneType, new LaneSpeedLimits(Map.of()));
40      }
41  
42      /**
43       * Returns one adjacent lane.
44       * @param laneChangeDirection lane change direction
45       * @param gtuType GTU type.
46       * @return adjacent lane, empty if none
47       */
48      @Override
49      public Optional<Lane> getAdjacentLane(final LateralDirectionality laneChangeDirection, final GtuType gtuType)
50      {
51          Set<Lane> adjLanes = accessibleAdjacentLanesPhysical(laneChangeDirection, gtuType);
52          if (!adjLanes.isEmpty())
53          {
54              return Optional.of(adjLanes.iterator().next());
55          }
56          return Optional.empty();
57      }
58  
59      @Override
60      public double getZ()
61      {
62          return -0.00005;
63      }
64  
65      @Override
66      public SpeedLimits getSpeedLimits(final GtuType gtuType)
67      {
68          return getLane().getSpeedLimits(gtuType);
69      }
70  
71      @Override
72      public SpeedLimits getSpeedLimits(final GtuType gtuType, final Duration timeOfDay)
73      {
74          return getLane().getSpeedLimits(gtuType, timeOfDay);
75      }
76  
77      @Override
78      public Optional<SpeedLimit> getSpeedLimit()
79      {
80          return getLane().getSpeedLimit();
81      }
82  
83      @Override
84      public Optional<SpeedLimit> getSpeedLimit(final Duration timeOfDay)
85      {
86          return getLane().getSpeedLimit(timeOfDay);
87      }
88  
89      /**
90       * Returns the closest (at the start) lane to this shoulder.
91       * @return the closest (at the start) lane to this shoulder
92       */
93      private Lane getLane()
94      {
95          if (this.lane == null)
96          {
97              double minDist = Double.POSITIVE_INFINITY;
98              for (Lane laneInLink : getLink().getLanes())
99              {
100                 double dist = laneInLink.getCenterLine().getFirst().distance(getCenterLine().getFirst());
101                 if (dist < minDist)
102                 {
103                     this.lane = laneInLink;
104                     minDist = dist;
105                 }
106             }
107             Throw.when(this.lane == null, IllegalStateException.class, "Shoulder is on a link without any lane.");
108         }
109         return this.lane;
110     }
111 
112 }