Kia ora!
Apologies, here’s another question about my Java source implementation, this time re: parallelization for Matrix and Route requests. Note that I do not want to parallelise the internal aspects of the request algorithms themselves, but simply to run several of them simultaneously.
Each process on each thread calls ORS methods from a process-unique ORSGraph
object, which I believe is, in itself, threadsafe:
public class ORSGraph extends Graph {
private APIEnums.Profile car;
private MatrixRequest req;
private MatrixResult res;
public ORSGraph(String name){
super(name);
car = APIEnums.Profile.DRIVING_CAR;
}
@Override
public Pair<Double, List<Location>> getShortestPath(Location s, List<Location> tL, boolean needPath) {
PriorityQueue<Pair<Double,List<Location>>> queue = new PriorityQueue<>(Comparator.comparingDouble(Pair::getFirst));
for(Location t: tL) {
Pair<Double, List<Location>> res = getShortestPath(s, t, needPath);
queue.add(res);
}
return queue.poll();
}
@Override
public Pair<Double, List<Location>> getShortestPath(Location i, Location j, boolean needPath) {
CoordinateLoc from = (CoordinateLoc) i;
CoordinateLoc to = (CoordinateLoc) j;
try {
if(!needPath)
try {
Pair<Double, List<Location>> res = new Pair(Utility.truncate(getDurations(Utility.coordinateLocsToArray(new CoordinateLoc[]{from, to}), new String[]{"0"}, new String[]{"1"})[0]), null);
return res;
} catch (Exception e) {
// do nothing. Continue to find the route.
}
RouteRequest request1 = new RouteRequest(Utility.coordinateLocsToArray(new CoordinateLoc[]{from, to}));
request1.setProfile(car);
request1.setAttributes(new APIEnums.Attributes[]{APIEnums.Attributes.DETOUR_FACTOR});
request1.setExtraInfo(new APIEnums.ExtraInfo[]{APIEnums.ExtraInfo.OSM_ID});
request1.setIncludeGeometry(true);
request1.setRoutePreference(APIEnums.RoutePreference.FASTEST);
RouteRequestOptions options = new RouteRequestOptions();
options.setAvoidBorders(APIEnums.AvoidBorders.CONTROLLED);
request1.setRouteOptions(options);
RouteResult[] routeRes = request1.generateRouteFromRequest();
if(routeRes != null && routeRes.length > 0) {
for (RouteResult rr : routeRes) {
List<Location> res = new LinkedList<>();
int len = rr.getGeometry().length;
Coordinate[] coords = rr.getGeometry();
for(int k = 0; k < len; k++){
Coordinate c = coords[k];
res.add(new CoordinateLoc(c.y, c.x));
}
Pair<Double, List<Location>> resPair = new Pair<>(Utility.truncate(rr.getSummary().getDuration()/60), res);
return resPair;
}
}
} catch (Exception e){
throw new IllegalStateException("Your route request failed: from " + from.toString() + " to " + to.toString() + " error: " + e.getMessage());
}
return null;
}
@Override
public Location getCurrLoc(DecisionProcessState state, Agent a) {
List<Location> currPath = a.getCurrPath();
try {
if (currPath.size() > 0) {
List<DecisionProcessEvent> eList = a.getEvents();
double startTime = 0.0;
if (eList.size() > 1)
startTime = eList.get(eList.size() - 2).getTime();
double currTime = state.getTime();
double sumTime = 0.0;
CoordinateLoc curr = (CoordinateLoc) currPath.get(0);
for (int i = 1; i < currPath.size(); i++) {
CoordinateLoc next = (CoordinateLoc) currPath.get(i);
double arcTime = getShortestPath(curr, next, true).getFirst();
if ((startTime + sumTime + arcTime) > currTime) {
double diff = startTime + sumTime + arcTime - currTime;
double arcFrac = Utility.round(diff / arcTime);
if (!(arcFrac <= 1.0 && arcFrac >= 0.0)) {
if (currPath.size() > i) {
curr = next;
continue;
} else
throw new IllegalStateException("Your arc fraction must be between 0.0 and 1.0. Arc frac: " + arcFrac + " diff: " + diff + " startTime: " + startTime + " sumTime: " + sumTime + " arcTime: " + arcTime + " currTime: " + currTime + " path size: " + currPath.size() + " i: " + i);
}
if (arcFrac + (1 - arcFrac) != 1)
throw new IllegalStateException("Your arc fraction must be [0,1]. Something is going wrong. Arc frac: " + arcFrac + " diff: " + diff + " startTime: " + startTime + " sumTime: " + sumTime + " arcTime: " + arcTime + " currTime: " + currTime + " path size: " + currPath.size() + " i: " + i);
double newX = Double.min(curr.getX(), next.getX()) + Math.abs(curr.getX() - next.getX()) * arcFrac;
double newY = Double.min(curr.getY(), next.getY()) + Math.abs(curr.getY() - next.getY()) * arcFrac;
return new CoordinateLoc(newX, newY);
} else {
sumTime += arcTime;
curr = next;
}
}
}
} catch (Exception e){
e.printStackTrace();
throw new IllegalStateException("You have failed to find an agent's current location: " + e.getMessage());
}
return a.getCurrLoc();
}
public double[] getDurations(Double[][] coordinates, String[] sources, String[] destinations) throws StatusCodeException {
req = new MatrixRequest(coordinates);
req.setSources(sources);
req.setDestinations(destinations);
req.setProfile(car);
req.setMetrics(new MatrixRequestEnums.Metrics[]{MatrixRequestEnums.Metrics.DURATION});
req.setResponseType(APIEnums.MatrixResponseType.JSON);
res = req.generateMatrixFromRequest();
float[] durations = res.getTable(1);
double[] durSec = new double[durations.length];
for(int i = 0; i < durations.length; i++) // convert
durSec[i] = durations[i] / 60;
return durSec;
}
public synchronized Graph clone(){
return new ORSGraph(this.getGlobal_name());
}
}
When running my code on a single thread, I get repeatable results. However, if I run a multithreaded process, such results are not repeatable.
I attempted to tie a RoutingProfileManager
object to each Graph, however, this very quickly lead to a memory issue (while also being rather ‘hacky’).
Is this (currently) infeasible, or am I making a flawed assumption?
Cheers,
Mak