2016-06-27 6 views
0

Я разрабатываю проект с использованием ядра Graphhopper для расчета оптимальных маршрутов. Я включил некоторые реальные данные трафика, изменив скорость, назначенную на ребра, и рассчитал оптимальные маршруты двумя способами: способом «по умолчанию» и способом, который рассматривает трафик.Graphhopper - расчет времени поездки

Теперь, я пытаюсь сравнить эти маршруты и исследовать, как меняется время в пути. То, что я хотел бы сделать, это рассчитать время поездки на оптимальном маршруте, который был найден с использованием скорости по умолчанию, назначенной краям, но время в пути должно быть рассчитано с использованием пользовательских значений скорости (те, которые учитывают реальный трафик). Другими словами, можно ли использовать Graphhopper для вычисления времени поездки по определенному маршруту (не оптимальному)?

Решение, которое, по моему мнению, заключается в реализации пользовательского FlagEncoder (как описано here), расширить класс Path и использовать их для расчета времени в пути с использованием значений скорости, которые учитывают трафик. Однако, может быть, вы, ребята, знаете более простой способ добиться этого.

+0

Если вы, например. используйте FastestWeighting или ShortestWeighting, вы получите в обоих случаях время и расстояние. Если вы берете данные о трафике в счет изменения скорости, вам не нужно что-то менять - он должен уже работать. Если вы, например, иметь данные о трафике в виде отдельного временного смещения, сохраненного на каждый край, вам нужно будет сделать окончательный расчет самостоятельно (только цикл по всем краям) или дождаться исправления этих проблем: https://github.com/graphhopper/graphhopper/ вопросы/439 & https://github.com/graphhopper/graphhopper/pull/730 – Karussell

+0

Спасибо @Karussell за ваш ответ. Поэтому, я думаю, я должен немного изменить свой подход. Является ли хорошей идеей установить пользовательскую скорость для края, используя метод setAdditionalField (значение int) от EdgeIteratorState? –

+0

установка скорость делается, например. через edge.setProperties или использовать EncodedValue в FlagEncoder для хранения дополнительных значений – Karussell

ответ

0

Мне, наконец, удалось решить проблему, поэтому я разделяю свое решение.

Чтобы сохранить пользовательскую скорость в качестве дополнительного значения, я расширил класс 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));

Наконец, чтобы использовать пользовательскую скорость при расчете времени поездки, я слегка модифицированный способ clacMillis форма класс Path из упаковка com.graphhoper.routing.

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); 
}