我正在使用Graphhopper核心开发一个项目来计算最佳路线。我通过修改分配给边缘的速度并以两种方式计算最佳路线来合并一些实际交通数据:“默认”方式和考虑交通的方式。
现在,我尝试比较这些路线并调查旅行时间的变化。我想要做的是计算最佳路线上的行程时间,这是使用分配给边缘的默认速度找到的,但是应该使用自定义速度值(考虑实际流量的那些)来计算行程时间。换句话说,是否可以使用Graphhopper计算特定路线上的旅行时间(非最佳路线)?
我想到的解决方案是实现自定义FlagEncoder(如here所述),扩展Path类并使用它们来计算考虑流量的速度值的旅行时间。但是,也许你们,伙计们,知道更简单的方法来实现这一点。
答案 0 :(得分:1)
我终于设法解决了这个问题,所以我分享了我的解决方案。
为了将自定义速度存储为额外值,我扩展了类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 form 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);
}