Sunday 30 June 2019

How to speed up osm2po when using as java lib?


I'am trying to utilize osm2po as java lib for calculation of distances matrix between some set of points (lat/lon) and for each pair of points I call the following method that code is based on sample provided at osm2po.de web-site:


public int getDistance(GeoPoint source, GeoPoint target) {
int res = Integer.MAX_VALUE;
int sourceId = graph.findClosestVertexId(source.getLatitude(), source.getLongitude(), 1);
int targetId = graph.findClosestVertexId(target.getLatitude(), target.getLongitude(), 1);


router.traverse(graph, sourceId, targetId, Float.MAX_VALUE, params);

if (router.isVisited(targetId)) { // Found!
int[] path = router.makePath(targetId);
float distKm = 0.0f;

for (int segmentId : path) {
RoutingResultSegment rrs = graph.lookupSegment(segmentId);
distKm = distKm + rrs.getKm();
}


res = (int)(distKm * 1000);
}

router.reset();
return res;

But I noticed that it takes about 2-3 seconds per one point to calculate whole distance matrix (I have 10-25 points per matrix in average) that looks not too fast (30-60secs per matrix). Could someone advice what could be improved here - especially I am not sure in correct usage of reset() call - when actually it should be done? - it's not too much documentation on osm2po usage within java...
Also I would appreciate any performance tips to improve this code. One more guess I have is that findClosestVertexId() is expensive enough call and cashing its results for subsequent calls could improve the situation.



Answer




package de.cm.osm2po.test;

import java.io.File;
import java.util.Arrays;

import de.cm.osm2po.logging.Log;
import de.cm.osm2po.logging.Log2poConsoleWriter;
import de.cm.osm2po.model.LatLon;
import de.cm.osm2po.routing.Graph;
import de.cm.osm2po.routing.MultiTargetRouter;

import de.cm.osm2po.routing.PoiRouter;

public class MatrixDemo {

// ###################### Demo Main ##############################

public static void main(String[] args) {
File graphFile = new File("D:/work/osm2po/hh/hh_2po.gph");

MatrixDemo matrixDemo = new MatrixDemo(graphFile);

int[] vertexIds = matrixDemo.findClosestVertexIds(
new LatLon(53.5, 10.1),
new LatLon(53.4, 10.0),
new LatLon(53.5, 9.9));
float[][] matrix = matrixDemo.createMatrix(vertexIds);
matrixDemo.close();

for (int i = 0; i < matrix.length; i++) {
System.out.println(Arrays.toString(matrix[i]));
}

}

// ##################### Intrinsic Clazz #########################

private Graph graph;
private MultiTargetRouter router;

public MatrixDemo(File graphFile) {
Log log = new Log(Log.LEVEL_DEBUG).addLogWriter(new Log2poConsoleWriter());
graph = new Graph(graphFile, log, Graph.SUPPORT_LATLON);

router = new PoiRouter();
}

public void close() {
graph.close();
}

public int[] findClosestVertexIds(LatLon... latLons) {
int[] vertexIds = new int[latLons.length];
for (int i = 0; i < latLons.length; i++) {

vertexIds[i] = graph.findClosestVertexId(
(float)latLons[i].getLat(), (float)latLons[i].getLon());
}
return vertexIds;
}

public float[][] createMatrix(int... vertexIds) {
int n = vertexIds.length;
int[] cpVertexIds = Arrays.copyOf(vertexIds, n);
float[][] matrix = new float[n][n];


for (int y = 0; y < n; y++) {

int swap = cpVertexIds[0];
cpVertexIds[0] = cpVertexIds[y];
cpVertexIds[y] = swap;

int sourceId = cpVertexIds[0];
int[] targetIds = Arrays.copyOfRange(cpVertexIds, 1, n -1);


router.reset();
router.traverse(graph, sourceId, targetIds, Float.MAX_VALUE, null);

for (int z = 0; z < n; z++) {
int x = z + y;
if (x >= n) x -= n;

matrix[y][x] = Float.MAX_VALUE;
if (router.isVisited(vertexIds[x])) {
matrix[y][x] = router.getCost(vertexIds[x]);

}
}

}

return matrix;
}

}


If you have big data but only need the matrix for a small region, you can improve the performance by setting the MaxCost-Parameter to sth. smaller than Float.MAXVALUE. Tipp: Mostly it is sufficient to call a full traversal using MaxValue twice or thrice. After these loops the longest path should be found and you can replace the MaxValue with the cost *


router.traverse(graph, sourceId, targetIds, longestPathCost * 1.5, null);

The last parameter (null) are optional additional properties.


Properties params = new Properties();
params.setProperty("findShortestPath", "true");
router.traverse(graph, sourceId, targetIds, Float.MAX_VALUE, params);

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...