Graphhopper - 计算行程时间

Graphhopper - calculating travel time

我正在开发一个使用 Graphhopper 核心计算最佳路线的项目。我通过修改分配给边缘的速度并以两种方式计算最佳路线来合并一些真实的交通数据:"default" 方式和考虑流量的方式。

现在,我尝试比较这些路线并调查旅行时间如何变化。我想做的是计算最佳路线上的旅行时间,这是使用分配给边缘的默认速度发现的,但旅行时间应该使用自定义速度值(那些考虑到实际交通的)来计算。换句话说,是否可以使用 Graphhopper 来计算特定路线(不是最佳路线)的旅行时间?

我想到的一个解决方案是实现自定义 FlagEncoder(如 here 所述),扩展 Path class 并使用它们来计算使用速度值的旅行时间,这考虑了交通.但是,伙计们,也许你们知道实现这一目标的更简单方法。

我终于设法解决了这个问题,所以我分享我的解决方案。

为了将自定义速度存储为额外值,我扩展了 class CarFlagEncoder。

public class CustomCarFlagEncoder extends CarFlagEncoder {

    public static final int CUSTOM_SPEED_KEY = 12345;

    private EncodedDoubleValue customSpeedEncoder;

    public CustomCarFlagEncoder() {
        super();
    }

    public CustomCarFlagEncoder(PMap properties) {
        super(properties);
    }

    public CustomCarFlagEncoder(String propertiesStr) {
        super(propertiesStr);
    }

    public CustomCarFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts) {
        super(speedBits, speedFactor, maxTurnCosts);
    }

    @Override
    public int defineWayBits(int index, int shift) {

        shift = super.defineWayBits(index, shift);
        customSpeedEncoder = new EncodedDoubleValue("Custom speed", shift, speedBits, speedFactor,
                defaultSpeedMap.get("secondary"), maxPossibleSpeed);
        shift += customSpeedEncoder.getBits();

        return shift;
    }

    @Override
    public double getDouble(long flags, int key) {
        switch (key) {
            case CUSTOM_SPEED_KEY:
                return customSpeedEncoder.getDoubleValue(flags);
            default:
                return super.getDouble(flags, key);
        }
    }

    @Override
    public long setDouble(long flags, int key, double value) {
        switch (key) {
            case CUSTOM_SPEED_KEY:
                if (value < 0 || Double.isNaN(value))
                    throw new IllegalArgumentException("Speed cannot be negative or NaN: " + value
                            + ", flags:" + BitUtil.LITTLE.toBitString(flags));

                if (value > getMaxSpeed())
                    value = getMaxSpeed();
                return customSpeedEncoder.setDoubleValue(flags, value);
            default:
                return super.setDouble(flags, key, value);
        }
    }

    @Override
    public String toString() {
        return CustomEncodingManager.CUSTOM_CAR;
    }
}

为了能够使用自定义 FlagEncoder,我创建了 CustomEncodingManager,它扩展了 EncodingManager 并处理 CustomCarFlagEncoder。

public class CustomEncodingManager extends EncodingManager {

    public static final String CUSTOM_CAR = "custom_car";

    public CustomEncodingManager(String flagEncodersStr) {
        this(flagEncodersStr, 4);
    }

    public CustomEncodingManager(String flagEncodersStr, int bytesForFlags )
    {
        this(parseEncoderString(flagEncodersStr), bytesForFlags);
    }

    public CustomEncodingManager(FlagEncoder... flagEncoders) {
        super(flagEncoders);
    }

    public CustomEncodingManager(List<? extends FlagEncoder> flagEncoders) {
        super(flagEncoders);
    }

    public CustomEncodingManager(List<? extends FlagEncoder> flagEncoders, int bytesForEdgeFlags) {
        super(flagEncoders, bytesForEdgeFlags);
    }

    static List<FlagEncoder> parseEncoderString(String encoderList )
    {
        if (encoderList.contains(":"))
            throw new IllegalArgumentException("EncodingManager does no longer use reflection instantiate encoders directly.");

        String[] entries = encoderList.split(",");
        List<FlagEncoder> resultEncoders = new ArrayList<FlagEncoder>();

        for (String entry : entries)
        {
            entry = entry.trim().toLowerCase();
            if (entry.isEmpty())
                continue;

            String entryVal = "";
            if (entry.contains("|"))
            {
                entryVal = entry;
                entry = entry.split("\|")[0];
            }
            PMap configuration = new PMap(entryVal);

            AbstractFlagEncoder fe;
            if (entry.equals(CAR))
                fe = new CarFlagEncoder(configuration);

            else if (entry.equals(BIKE))
                fe = new BikeFlagEncoder(configuration);

            else if (entry.equals(BIKE2))
                fe = new Bike2WeightFlagEncoder(configuration);

            else if (entry.equals(RACINGBIKE))
                fe = new RacingBikeFlagEncoder(configuration);

            else if (entry.equals(MOUNTAINBIKE))
                fe = new MountainBikeFlagEncoder(configuration);

            else if (entry.equals(FOOT))
                fe = new FootFlagEncoder(configuration);

            else if (entry.equals(MOTORCYCLE))
                fe = new MotorcycleFlagEncoder(configuration);

            else if (entry.equals(CUSTOM_CAR)) {
                fe = new CustomCarFlagEncoder(configuration);
            }

            else
                throw new IllegalArgumentException("entry in encoder list not supported " + entry);

            if (configuration.has("version"))
            {
                if (fe.getVersion() != configuration.getInt("version", -1))
                {
                    throw new IllegalArgumentException("Encoder " + entry + " was used in version "
                            + configuration.getLong("version", -1) + ", but current version is " + fe.getVersion());
                }
            }

            resultEncoders.add(fe);
        }
        return resultEncoders;
    }

}

然后,我将自定义 EncodingManager 设置为 GraphHopper 对象 hopper.setEncodingManager(new CustomEncodingManager(CustomEncodingManager.CUSTOM_CAR));

我将自定义速度作为额外值分配给边缘 edge.setFlags(customCarEncoder.setDouble(existingFlags, CustomCarFlagEncoder.CUSTOM_SPEED_KEY, newSpeed));

最后,为了在计算旅行时间时使用自定义速度,我稍微修改了包 com.graphhoper.routing.

中的方法 clacMillis 形式 class Path
protected long calcMillis( double distance, long flags, boolean revert )
{
    if (revert && !encoder.isBackward(flags)
            || !revert && !encoder.isForward(flags))
        throw new IllegalStateException("Calculating time should not require to read speed from edge in wrong direction. "
                + "Reverse:" + revert + ", fwd:" + encoder.isForward(flags) + ", bwd:" + encoder.isBackward(flags));

    double speed = revert ? encoder.getReverseSpeed(flags) : encoder.getSpeed(flags);
    double customSpeed = encoder.getDouble(flags, 12345);
    if (customSpeed > 0) {
        speed = customSpeed;
    }
    if (Double.isInfinite(speed) || Double.isNaN(speed) || speed < 0)
        throw new IllegalStateException("Invalid speed stored in edge! " + speed);

    if (speed == 0)
        throw new IllegalStateException("Speed cannot be 0 for unblocked edge, use access properties to mark edge blocked! Should only occur for shortest path calculation. See #242.");

    return (long) (distance * 3600 / speed);
}