PerceivedConflict.java

package org.opentrafficsim.road.gtu.lane.perception.object;

import java.util.Optional;

import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.djutils.exceptions.Try;
import org.opentrafficsim.base.geometry.OtsLine2d;
import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.PerceivedGtuType;
import org.opentrafficsim.road.network.lane.CrossSectionLink;
import org.opentrafficsim.road.network.lane.Lane;
import org.opentrafficsim.road.network.lane.conflict.Conflict;
import org.opentrafficsim.road.network.lane.conflict.ConflictPriority;
import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
import org.opentrafficsim.road.network.lane.conflict.ConflictType;

/**
 * Interface for perceived {@code Conflict} objects. A standard implementation is provided under {@code of(...)} which wraps a
 * {@code Conflict} and returns most values as is.
 * <p>
 * Copyright (c) 2024-2024 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 <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
 */
public interface PerceivedConflict extends PerceivedLaneBasedObject
{

    /**
     * Returns the conflict type.
     * @return conflict type
     */
    ConflictType getConflictType();

    /**
     * Returns whether this is a crossing conflict.
     * @return whether this is a crossing conflict
     */
    boolean isCrossing();

    /**
     * Returns whether this is a merge conflict.
     * @return whether this is a merge conflict
     */
    boolean isMerge();

    /**
     * Returns whether this is a split conflict.
     * @return whether this is a split conflict
     */
    boolean isSplit();

    /**
     * Returns the conflict priority.
     * @return conflict priority
     */
    ConflictPriority getConflictPriority();

    /**
     * Returns the length of the conflict on the conflicting lane.
     * @return length of the conflict on the conflicting lane
     */
    Length getConflictingLength();

    /**
     * Returns a set of conflicting GTU's upstream of the <i>start</i> of the conflict ordered close to far from the conflict.
     * @return set of conflicting GTU's upstream of the <i>start</i> of the conflict ordered close to far from the conflict
     */
    PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getUpstreamConflictingGTUs();

    /**
     * Returns a set of conflicting GTU's downstream of the <i>start</i> of the conflict ordered close to far from the conflict.
     * Distance is given relative to the <i>end</i> of the conflict, or null for conflicting vehicles on the conflict. In the
     * latter case the overlap is used.
     * @return set of conflicting GTU's downstream of the <i>start</i> of the conflict ordered close to far from the conflict
     */
    PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getDownstreamConflictingGTUs();

    /**
     * Returns the visibility on the conflicting lane within which conflicting vehicles are visible. All upstream conflicting
     * GTUs have a distance smaller than the visibility. Depending on a limited visibility, a certain (lower) speed may be
     * required while approaching the conflict.
     * @return visibility on the conflicting lane within which conflicting vehicles are visible
     */
    Length getConflictingVisibility();

    /**
     * Returns the speed limit on the conflicting lane.
     * @return speed limit on the conflicting lane
     */
    Speed getConflictingSpeedLimit();

    /**
     * Returns the conflicting link.
     * @return the conflicting link
     */
    CrossSectionLink getConflictingLink();

    /**
     * Returns the stop line.
     * @return stop line
     */
    PerceivedObject getStopLine();

    /**
     * Returns the stop line on the conflicting lane.
     * @return stop line
     */
    PerceivedObject getConflictingStopLine();

    /**
     * Returns the conflict rule type.
     * @return conflict rule type
     */
    Class<? extends ConflictRule> getConflictRuleType();

    /**
     * Returns the distance of a traffic light upstream on the conflicting lane.
     * @return distance of a traffic light upstream on the conflicting lane, empty if no traffic light
     */
    Optional<Length> getConflictingTrafficLightDistance();

    /**
     * Whether the conflict is permitted by the traffic light.
     * @return whether the conflict is permitted by the traffic light
     */
    boolean isPermitted();

    /**
     * Returns the width at the given fraction.
     * @param fraction fraction from 0 to 1
     * @return width at the given fraction
     */
    Length getWidthAtFraction(double fraction);

    /**
     * Returns a standard implementation of this interface that wraps a {@code Conflict} and uses a given perceived GTU type to
     * perceive the upstream and downstream GTUs.
     * @param perceivingGtu perceiving GTU
     * @param conflict conflict to perceive
     * @param perceivedGtuType perceived GTU type
     * @param distance distance from perceiving GTU to conflict
     * @param conflictingVisibility visibility range at other conflict
     * @return perceived conflict
     */
    @SuppressWarnings("methodlength")
    static PerceivedConflict of(final LaneBasedGtu perceivingGtu, final Conflict conflict,
            final PerceivedGtuType perceivedGtuType, final Length distance, final Length conflictingVisibility)
    {
        final Kinematics kinematics = Kinematics.staticAhead(distance);
        // TODO stop lines (current models happen not to use this, but should be possible)
        final PerceivedObject stopLine =
                new PerceivedObjectBase("stopLineId", ObjectType.STOPLINE, Length.ZERO, Kinematics.staticAhead(Length.ZERO));
        final PerceivedObject conflictingStopLine = new PerceivedObjectBase("conflictingStopLineId", ObjectType.STOPLINE,
                Length.ZERO, Kinematics.staticAhead(Length.ZERO));
        final Speed conflictingSpeedLimit = Try.assign(() -> conflict.getOtherConflict().getLane().getHighestSpeedLimit(),
                "Unable to obtain higest speed limit on conflicting lane.");
        final Length conflictingTrafficLightDistance =
                conflict.getOtherConflict().getTrafficLightDistance(conflictingVisibility);

        final PerceptionCollectable<PerceivedGtu, LaneBasedGtu> upstreamConflictingGTUs =
                conflict.getOtherConflict().getUpstreamGtus(perceivingGtu, perceivedGtuType, conflictingVisibility);
        final PerceptionCollectable<PerceivedGtu, LaneBasedGtu> downstreamConflictingGTUs =
                conflict.getOtherConflict().getDownstreamGtus(perceivingGtu, perceivedGtuType, conflictingVisibility);

        Length pos1a = conflict.getLongitudinalPosition();
        Length pos2a = conflict.getOtherConflict().getLongitudinalPosition();
        Length pos1b = Length.min(pos1a.plus(conflict.getLength()), conflict.getLane().getLength());
        Length pos2b = Length.min(pos2a.plus(conflict.getOtherConflict().getLength()),
                conflict.getOtherConflict().getLane().getLength());
        OtsLine2d line1 = conflict.getLane().getCenterLine();
        OtsLine2d line2 = conflict.getOtherConflict().getLane().getCenterLine();
        double dStart = line1.getLocation(pos1a).distance(line2.getLocation(pos2a));
        double dEnd = line1.getLocation(pos1b).distance(line2.getLocation(pos2b));
        double startWidth = dStart + .5 * conflict.getLane().getWidth(pos1a).si
                + .5 * conflict.getOtherConflict().getLane().getWidth(pos2a).si;
        double endWidth = dEnd + .5 * conflict.getLane().getWidth(pos1b).si
                + .5 * conflict.getOtherConflict().getLane().getWidth(pos2b).si;

        return new PerceivedConflict()
        {
            @Override
            public Lane getLane()
            {
                return conflict.getLane();
            }

            @Override
            public ObjectType getObjectType()
            {
                return ObjectType.CONFLICT;
            }

            @Override
            public Length getLength()
            {
                return conflict.getLength();
            }

            @Override
            public Kinematics getKinematics()
            {
                return kinematics;
            }

            @Override
            public String getId()
            {
                return conflict.getId();
            }

            @Override
            public ConflictType getConflictType()
            {
                return conflict.getConflictType();
            }

            @Override
            public boolean isCrossing()
            {
                return conflict.getConflictType().isCrossing();
            }

            @Override
            public boolean isMerge()
            {
                return conflict.getConflictType().isMerge();
            }

            @Override
            public boolean isSplit()
            {
                return conflict.getConflictType().isSplit();
            }

            @Override
            public ConflictPriority getConflictPriority()
            {
                return conflict.conflictPriority();
            }

            @Override
            public Length getConflictingLength()
            {
                return conflict.getOtherConflict().getLength();
            }

            @Override
            public PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getUpstreamConflictingGTUs()
            {
                return upstreamConflictingGTUs;
            }

            @Override
            public PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getDownstreamConflictingGTUs()
            {
                return downstreamConflictingGTUs;
            }

            @Override
            public Length getConflictingVisibility()
            {
                return conflictingVisibility;
            }

            @Override
            public Speed getConflictingSpeedLimit()
            {
                return conflictingSpeedLimit;
            }

            @Override
            public CrossSectionLink getConflictingLink()
            {
                return conflict.getOtherConflict().getLane().getLink();
            }

            @Override
            public PerceivedObject getStopLine()
            {
                return stopLine;
            }

            @Override
            public PerceivedObject getConflictingStopLine()
            {
                return conflictingStopLine;
            }

            @Override
            public Class<? extends ConflictRule> getConflictRuleType()
            {
                return conflict.getConflictRule().getClass();
            }

            @Override
            public Optional<Length> getConflictingTrafficLightDistance()
            {
                return Optional.of(conflictingTrafficLightDistance);
            }

            @Override
            public boolean isPermitted()
            {
                return conflict.isPermitted();
            }

            @Override
            public Length getWidthAtFraction(final double fraction)
            {
                return Length.ofSI((1.0 - fraction) * startWidth + fraction * endWidth);
            }
        };
    }

}