@jie31best, using your example, I notice that the problem is with my custom matrix. Using FastVehicleRoutingTransportCostsMatrix or VehicleRoutingTransportCostsMatrix works fine.
I have a custom matrix with multi time/distance for each from/to pairs and the information is returned considering the current time so, Start/End to A2 could be 0 or 10. I will try to find the problem with my matrix. Thank you for your help.
My matrix:
public class MultiTimeDistanceCostsMatrix extends AbstractForwardVehicleRoutingTransportCosts {
static class RelationKey {
static RelationKey newKey(String from, String to) {
return new RelationKey(from, to);
}
final String from;
final String to;
public RelationKey(String from, String to) {
super();
this.from = from;
this.to = to;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((from == null) ? 0 : from.hashCode());
result = prime * result + ((to == null) ? 0 : to.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RelationKey other = (RelationKey) obj;
if (from == null) {
if (other.from != null)
return false;
} else if (!from.equals(other.from))
return false;
if (to == null) {
if (other.to != null)
return false;
} else if (!to.equals(other.to))
return false;
return true;
}
}
public static class Builder {
private static Logger log = LoggerFactory.getLogger(Builder.class);
private Map<RelationKey, List<TimeDistance>> timesDistances = new HashMap<>();
private boolean timesDistancesSet = false;
private Map<String, BlockArea> blockAreas = new HashMap<>();
public static Builder newInstance() {
return new Builder();
}
private Builder() {
}
public Builder addTimesDistances(String from, String to, List<TimeDistance> timesDistances) {
RelationKey key = RelationKey.newKey(from, to);
if (!timesDistancesSet) timesDistancesSet = true;
if (this.timesDistances.containsKey(key)) {
log.warn("times/distances from " + from + " to " + to + " already exists. This overrides times/distances.");
}
this.timesDistances.put(key, timesDistances);
return this;
}
public Builder addBlockAreas(List<BlockArea> blockAreas) {
for (BlockArea blockArea : blockAreas) {
if (!this.blockAreas.containsKey(blockArea.id)) this.blockAreas.put(blockArea.id, blockArea);
}
return this;
}
public MultiTimeDistanceCostsMatrix build() {
return new MultiTimeDistanceCostsMatrix(this);
}
}
private Map<RelationKey, List<TimeDistance>> timesDistances = new HashMap<>();
private boolean timesDistancesSet;
private List<BlockArea> blockAreas;
private MultiTimeDistanceCostsMatrix(Builder builder) {
timesDistances.putAll(builder.timesDistances);
timesDistancesSet = builder.timesDistancesSet;
blockAreas = new ArrayList<>(builder.blockAreas.values());
}
@Override
public double getTransportTime(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) {
String fromId = from.getId();
String toId = to.getId();
return getTime(fromId, toId, getIdsValidBlockAreas(fromId, toId, departureTime));
}
public List<String> getIdsValidBlockAreas(String fromId, String toId, double departureTime) {
List<String> validBlockAreas = new ArrayList<>();
double arrivalTime = departureTime + getTimeWithoutBlockAreas(fromId, toId);
fillValidBlockAreas(fromId, toId, departureTime, arrivalTime, validBlockAreas);
return validBlockAreas;
}
private void fillValidBlockAreas(String fromId, String toId, double departureTime, double arrivalTime, List<String> valid) {
for (BlockArea blockArea : blockAreas) {
for (BlockInterval interval : blockArea.blockIntervals) {
if (interval.startTime <= departureTime && interval.endTime >= departureTime || interval.startTime >= departureTime && interval.startTime <= arrivalTime) {
if (!valid.contains(blockArea.id)) {
valid.add(blockArea.id);
double newArrivalTime = departureTime + getTime(fromId, toId, valid);
fillValidBlockAreas(fromId, toId, departureTime, newArrivalTime, valid);
}
}
}
}
}
private double getTimeWithoutBlockAreas(String fromId, String toId) {
return getTime(fromId, toId, Collections.emptyList());
}
private double getTime(String fromId, String toId, List<String> blockAreas) {
Optional<TimeDistance> possibleTimeDistance = getTimeDistance(fromId, toId, blockAreas);
if (possibleTimeDistance.isPresent()) {
return possibleTimeDistance.get().getTime();
} else return 0.0;
}
private Optional<TimeDistance> getTimeDistance(String fromId, String toId, List<String> blockAreas) {
if (fromId.equals(toId)) return Optional.empty();
if (!timesDistancesSet) return Optional.empty();
RelationKey key = RelationKey.newKey(fromId, toId);
if (timesDistances.containsKey(key)) {
List<TimeDistance> distances = timesDistances.get(key);
for (TimeDistance distance : distances) {
List<String> ids = distance.getBlockAreas().stream().map(b -> b.getId()).collect(Collectors.toList());
if (ids.size() == blockAreas.size() && ids.containsAll(blockAreas)) return Optional.of(distance);
}
}
throw new IllegalStateException("time/distance value for relation from " + fromId + " to " + toId + " does not exist");
}
public double getDistance(String fromId, String toId, List<String> blockAreas) {
Optional<TimeDistance> possibleTimeDistance = getTimeDistance(fromId, toId, blockAreas);
if (possibleTimeDistance.isPresent()) {
return possibleTimeDistance.get().getDistance();
} else return 0.0;
}
@Override
public double getTransportCost(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) {
String fromId = from.getId();
String toId = to.getId();
List<String> blockAreas = getIdsValidBlockAreas(fromId, toId, departureTime);
if (vehicle == null) return getDistance(fromId, toId, blockAreas);
VehicleTypeImpl.VehicleCostParams costParams = vehicle.getType().getVehicleCostParams();
return costParams.perDistanceUnit * getDistance(fromId, toId, blockAreas) + costParams.perTransportTimeUnit * getTime(fromId, toId, blockAreas);
}
public double getDistance(Location from, Location to, double departureTime) {
String fromId = from.getId();
String toId = to.getId();
return getDistance(from.getId(), to.getId(), getIdsValidBlockAreas(fromId, toId, departureTime));
}
}