LimitedAccessWeighting.java

/*  This file is part of Openrouteservice.
 *
 *  Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
 *  GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
 *  of the License, or (at your option) any later version.

 *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Lesser General Public License for more details.

 *  You should have received a copy of the GNU Lesser General Public License along with this library;
 *  if not, see <https://www.gnu.org/licenses/>.
 */
package org.heigit.ors.routing.graphhopper.extensions.weighting;

import com.graphhopper.routing.ev.EnumEncodedValue;
import com.graphhopper.routing.ev.RoadAccess;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.weighting.AbstractAdjustedWeighting;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.PMap;

/**
 * Modifies weight of edges marked with access destination or private by multiplying it by a factor.
 *
 * @author Andrzej Oles
 */
public class LimitedAccessWeighting extends AbstractAdjustedWeighting {
    public static double DEFAULT_DESTINATION_FACTOR = 1;
    public static double VEHICLE_DESTINATION_FACTOR = 10;
    public static double DEFAULT_PRIVATE_FACTOR = 1.2;
    public static double VEHICLE_PRIVATE_FACTOR = 10;

    static double MIN_DESTINATION_FACTOR = 1;
    static double MAX_DESTINATION_FACTOR = 10;
    static double MIN_PRIVATE_FACTOR = 1;
    static double MAX_PRIVATE_FACTOR = 10;

    private final EnumEncodedValue<RoadAccess> roadAccessEnc;
    // this factor puts a penalty on roads with a "destination"-only or private access, see GH#733 and GH#1936
    private final double destinationPenalty, privatePenalty;

    public LimitedAccessWeighting(Weighting superWeighting, PMap map) {
        super(superWeighting);
        FlagEncoder encoder = superWeighting.getFlagEncoder();
        if (!encoder.hasEncodedValue(RoadAccess.KEY))
            throw new IllegalArgumentException("road_access is not available");

        // ensure that we do not need to change getMinWeight, i.e. road_access_factor >= 1
        double defaultDestinationFactor = encoder.getTransportationMode().isMotorVehicle() ? VEHICLE_DESTINATION_FACTOR : DEFAULT_DESTINATION_FACTOR;
        destinationPenalty = checkBounds("road_access_destination_factor", map.getDouble("road_access_destination_factor", defaultDestinationFactor), MIN_DESTINATION_FACTOR, MAX_DESTINATION_FACTOR);
        double defaultPrivateFactor = encoder.getTransportationMode().isMotorVehicle() ? VEHICLE_PRIVATE_FACTOR : DEFAULT_PRIVATE_FACTOR;
        privatePenalty = checkBounds("road_access_private_factor", map.getDouble("road_access_private_factor", defaultPrivateFactor), MIN_PRIVATE_FACTOR, MAX_PRIVATE_FACTOR);
        roadAccessEnc = destinationPenalty > 1 || privatePenalty > 1 ? encoder.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class) : null;
    }

    static double checkBounds(String key, double val, double from, double to) {
        if (val < from || val > to)
            throw new IllegalArgumentException(key + " has invalid range should be within [" + from + ", " + to + "]");

        return val;
    }

    @Override
    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse, long edgeEnterTime) {
        double weight = super.calcEdgeWeight(edgeState, reverse, edgeEnterTime);

        weight = modifyWeight(weight, edgeState);

        return weight;
    }

    @Override
    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
        double weight = super.calcEdgeWeight(edgeState, reverse);

        weight = modifyWeight(weight, edgeState);

        return weight;
    }

    private double modifyWeight(double weight, EdgeIteratorState edgeState) {
        if (roadAccessEnc != null) {
            RoadAccess access = edgeState.get(roadAccessEnc);
            if (access == RoadAccess.DESTINATION)
                weight *= destinationPenalty;
            else if (access == RoadAccess.PRIVATE)
                weight *= privatePenalty;
        }
        return weight;
    }

    @Override
    public String toString() {
        return "limited_access|" + this.superWeighting.toString();
    }

    public String getName() {
        return this.superWeighting.getName();
    }
}