Shoulder.java
package org.opentrafficsim.road.network;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.djunits.value.vdouble.scalar.Duration;
import org.djutils.exceptions.Throw;
import org.opentrafficsim.core.gtu.GtuType;
import org.opentrafficsim.core.network.LateralDirectionality;
import org.opentrafficsim.road.network.speed.LaneSpeedLimits;
import org.opentrafficsim.road.network.speed.SpeedLimit;
import org.opentrafficsim.road.network.speed.SpeedLimits;
/**
* 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
* differently.
* <p>
* Copyright (c) 2024-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author Wouter Schakel
*/
public class Shoulder extends Lane
{
/** Nearest lane. */
private Lane lane;
/**
* Constructor specifying geometry.
* @param link link
* @param id the id of this lane within the link; should be unique within the link
* @param geometry geometry
* @param laneType lane type
*/
public Shoulder(final CrossSectionLink link, final String id, final CrossSectionGeometry geometry, final LaneType laneType)
{
super(link, id, geometry, laneType, new LaneSpeedLimits(Map.of()));
}
/**
* Returns one adjacent lane.
* @param laneChangeDirection lane change direction
* @param gtuType GTU type.
* @return adjacent lane, empty if none
*/
@Override
public Optional<Lane> getAdjacentLane(final LateralDirectionality laneChangeDirection, final GtuType gtuType)
{
Set<Lane> adjLanes = accessibleAdjacentLanesPhysical(laneChangeDirection, gtuType);
if (!adjLanes.isEmpty())
{
return Optional.of(adjLanes.iterator().next());
}
return Optional.empty();
}
@Override
public double getZ()
{
return -0.00005;
}
@Override
public SpeedLimits getSpeedLimits(final GtuType gtuType)
{
return getLane().getSpeedLimits(gtuType);
}
@Override
public SpeedLimits getSpeedLimits(final GtuType gtuType, final Duration timeOfDay)
{
return getLane().getSpeedLimits(gtuType, timeOfDay);
}
@Override
public Optional<SpeedLimit> getSpeedLimit()
{
return getLane().getSpeedLimit();
}
@Override
public Optional<SpeedLimit> getSpeedLimit(final Duration timeOfDay)
{
return getLane().getSpeedLimit(timeOfDay);
}
/**
* Returns the closest (at the start) lane to this shoulder.
* @return the closest (at the start) lane to this shoulder
*/
private Lane getLane()
{
if (this.lane == null)
{
double minDist = Double.POSITIVE_INFINITY;
for (Lane laneInLink : getLink().getLanes())
{
double dist = laneInLink.getCenterLine().getFirst().distance(getCenterLine().getFirst());
if (dist < minDist)
{
this.lane = laneInLink;
minDist = dist;
}
}
Throw.when(this.lane == null, IllegalStateException.class, "Shoulder is on a link without any lane.");
}
return this.lane;
}
}