Fast route calculation

This commit is contained in:
Victor Shcherb 2012-07-13 09:51:00 +02:00
parent cc2f1b1a7d
commit fdd80c9fb7
11 changed files with 451 additions and 253 deletions

View file

@ -26,10 +26,39 @@ public class NativeLibrary {
@Override
protected void finalize() throws Throwable {
deleteNativeResult();
super.finalize();
}
public void deleteNativeResult() {
if (nativeHandler != 0) {
super.finalize();
deleteSearchResult(nativeHandler);
nativeHandler = 0;
}
}
}
public static class NativeRouteSearchResult {
public long nativeHandler;
public RouteDataObject[] objects;
public RouteRegion region;
public NativeRouteSearchResult(long nativeHandler, RouteDataObject[] objects) {
this.nativeHandler = nativeHandler;
this.objects = objects;
}
@Override
protected void finalize() throws Throwable {
deleteNativeResult();
super.finalize();
}
public void deleteNativeResult() {
if (nativeHandler != 0) {
deleteRouteSearchResult(nativeHandler);
nativeHandler = 0;
}
deleteSearchResult(nativeHandler);
}
}
@ -42,13 +71,14 @@ public class NativeLibrary {
return new NativeSearchResult(searchNativeObjectsForRendering(sleft, sright, stop, sbottom, zoom, request, skipDuplicates,
objectWithInterruptedField, msgIfNothingFound));
}
public void deleteSearchResult(NativeSearchResult searchResultHandler) {
if (searchResultHandler.nativeHandler != 0) {
deleteSearchResult(searchResultHandler.nativeHandler);
searchResultHandler.nativeHandler = 0;
public RouteDataObject[] getDataObjects(NativeRouteSearchResult rs, int x31, int y31) {
if(rs.nativeHandler == 0) {
throw new IllegalStateException("Native route handler is 0");
}
return getRouteDataObjects(rs.region, rs.nativeHandler, x31, y31);
}
public boolean initMapFile(String filePath) {
return initBinaryMapFile(filePath);
@ -58,13 +88,24 @@ public class NativeLibrary {
return closeBinaryMapFile(filePath);
}
public RouteDataObject[] loadRouteRegion(RouteRegion reg, int left, int right, int top, int bottom) {
return loadRoutingData(reg, reg.getName(), reg.getFilePointer(), left, right, top, bottom);
public NativeRouteSearchResult loadRouteRegion(RouteRegion reg, int left, int right, int top, int bottom, boolean loadObjects) {
NativeRouteSearchResult lr = loadRoutingData(reg, reg.getName(), reg.getFilePointer(), left, right, top, bottom, loadObjects);
if(lr != null && lr.nativeHandler != 0){
lr.region = reg;
}
return lr;
}
protected static native RouteDataObject[] loadRoutingData(RouteRegion reg, String regName, int fpointer, int left, int right, int top, int bottom);
protected static native NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int fpointer, int left, int right, int top, int bottom,
boolean loadObjects);
protected static native void deleteRouteSearchResult(long searchResultHandle);
protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31);
protected static native void deleteSearchResult(long searchResultHandle);
protected static native boolean initBinaryMapFile(String filePath);

View file

@ -14,16 +14,13 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.TreeSet;
import net.osmand.LogUtil;
import net.osmand.NativeLibrary;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapRouteReaderAdapter;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
@ -48,6 +45,9 @@ public class BinaryRoutePlanner {
public BinaryRoutePlanner(NativeLibrary nativeLib, BinaryMapIndexReader... map) {
this.nativeLib = nativeLib;
if(nativeLib != null) {
RoutingConfiguration.DEFAULT_DESIRABLE_TILES_IN_MEMORY = 25;
}
for (BinaryMapIndexReader mr : map) {
List<RouteRegion> rr = mr.getRoutingIndexes();
List<RouteSubregion> subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>();
@ -119,7 +119,7 @@ public class BinaryRoutePlanner {
List<RouteDataObject> dataObjects = new ArrayList<RouteDataObject>();
Iterator<RoutingTile> it = ts.valueCollection().iterator();
while(it.hasNext()){
loadTileData(ctx, it.next(), dataObjects);
ctx.loadTileData(it.next(), dataObjects, nativeLib, map);
}
RouteSegment road = null;
double sdist = 0;
@ -158,7 +158,7 @@ public class BinaryRoutePlanner {
}
if(road != null) {
// re-register the best road because one more point was inserted
registerRouteDataObject(ctx, road.getRoad(), ctx.getRoutingTile(foundProjX, foundProjY));
ctx.registerRouteDataObject(road.getRoad(), ctx.getRoutingTile(foundProjX, foundProjY));
}
return road;
}
@ -297,11 +297,16 @@ public class BinaryRoutePlanner {
relaxNotNeededSegments(ctx, graphReverseSegments, false);
}
}
printDebugMemoryInformation(ctx, graphDirectSegments, graphReverseSegments, visitedDirectSegments, visitedOppositeSegments);
println("Result is found");
// 4. Route is found : collect all segments and prepare result
return prepareResult(ctx, start, end, leftSideNavigation);
List<RouteSegmentResult> resultPrepared = prepareResult(ctx, start, end, leftSideNavigation);
printDebugMemoryInformation(ctx, graphDirectSegments, graphReverseSegments, visitedDirectSegments, visitedOppositeSegments);
Object[] vls = ctx.tiles.values();
for(Object tl : vls) {
ctx.unloadTile((RoutingTile) tl, false);
}
return resultPrepared;
}
private void unloadUnusedTiles(RoutingContext ctx, int desirableSize) {
@ -334,11 +339,13 @@ public class BinaryRoutePlanner {
return v1 < v2 ? -1 : (v1 == v2 ? 0 : 1);
}
});
int toUnload = Math.max(loaded / 5, loaded - desirableSize);
for (int i = 0; i < loaded; i++) {
list.get(i).access = 0;
if (i < toUnload) {
ctx.unloadTile(list.get(i), true);
if (loaded >= 0.9f * desirableSize) {
int toUnload = Math.max(loaded / 5, loaded - desirableSize);
for (int i = 0; i < loaded; i++) {
list.get(i).access = 0;
if (i < toUnload) {
ctx.unloadTile(list.get(i), true);
}
}
}
}
@ -395,54 +402,7 @@ public class BinaryRoutePlanner {
return result;
}
private void registerRouteDataObject(final RoutingContext ctx, RouteDataObject o, RoutingTile suggestedTile ) {
if(!ctx.getRouter().acceptLine(o)){
return;
}
RoutingTile tl = suggestedTile;
RouteDataObject old = tl.idObjects.get(o.id);
// sometimes way is present only partially in one index
if (old != null && old.getPointsLength() >= o.getPointsLength()) {
return;
};
for (int j = 0; j < o.getPointsLength(); j++) {
int x = o.getPoint31XTile(j);
int y = o.getPoint31YTile(j);
if(!tl.checkContains(x, y)){
// don't register in different tiles
// in order to throw out tile object easily
continue;
}
long l = (((long) x) << 31) + (long) y;
RouteSegment segment = new RouteSegment(o , j);
RouteSegment prev = tl.routes.get(l);
boolean i = true;
if (prev != null) {
if (old == null) {
segment.next = prev;
} else if (prev.road == old) {
segment.next = prev.next;
} else {
// segment somewhere in the middle replace element in linked list
RouteSegment rr = prev;
while (rr != null) {
if (rr.road == old) {
prev.next = segment;
segment.next = rr.next;
break;
}
prev = rr;
rr = rr.next;
}
i = false;
}
}
if (i) {
tl.routes.put(l, segment);
tl.idObjects.put(o.id, o);
}
}
}
private static void println(String logMsg) {
// log.info(logMsg);
@ -453,8 +413,8 @@ public class BinaryRoutePlanner {
TLongObjectHashMap<RouteSegment> visitedDirectSegments,TLongObjectHashMap<RouteSegment> visitedOppositeSegments) {
println("Time to calculate : " + (System.nanoTime() - ctx.timeToCalculate) / 1e6 + ", time to load : " + ctx.timeToLoad / 1e6);
println("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles " + ctx.maxLoadedTiles);
println("Loaded tiles : " + ctx.loadedTiles + ", unloaded tiles " + ctx.unloadedTiles +
" (distinct " + ctx.distinctUnloadedTiles.size()+") "+ ", loaded \"unloaded\" tiles "
println("Loaded tiles " + ctx.loadedTiles + " (distinct "+ctx.distinctLoadedTiles+ "), unloaded tiles " + ctx.unloadedTiles +
" (distinct " + ctx.distinctUnloadedTiles.size()+") "+ ", loaded more than once same tiles "
+ ctx.loadedPrevUnloadedTiles );
println("Visited roads, " + ctx.visitedSegments + ", relaxed roads " + ctx.relaxedSegments);
if (graphDirectSegments != null && graphReverseSegments != null) {
@ -468,88 +428,15 @@ public class BinaryRoutePlanner {
public RoutingTile loadRoutes(final RoutingContext ctx, int tile31X, int tile31Y) {
final RoutingTile tile = ctx.getRoutingTile(tile31X, tile31Y);
loadTileData(ctx, tile, null);
if (tile.isLoaded()) {
tile.access++;
return tile;
}
ctx.loadTileData(tile, null, nativeLib, map);
return tile;
}
private void loadTileData(final RoutingContext ctx, final RoutingTile tile, final List<RouteDataObject> toFillIn) {
if (tile.isLoaded()) {
tile.access++;
if(toFillIn != null){
for(RouteDataObject ro : tile.idObjects.valueCollection()){
toFillIn.add(ro);
}
}
return;
}
long now = System.nanoTime();
ResultMatcher<RouteDataObject> matcher = new ResultMatcher<RouteDataObject>() {
@Override
public boolean publish(RouteDataObject o) {
if (toFillIn != null) {
if (ctx.getRouter().acceptLine(o)) {
toFillIn.add(o);
}
}
registerRouteDataObject(ctx, o, tile);
return false;
}
@Override
public boolean isCancelled() {
return false;
}
};
int zoomToLoad = 31 - tile.getZoom();
int tileX = tile.getTileX();
int tileY = tile.getTileY();
SearchRequest<RouteDataObject> request = BinaryMapIndexReader.buildSearchRouteRequest(tileX << zoomToLoad,
(tileX + 1) << zoomToLoad, tileY << zoomToLoad, (tileY + 1) << zoomToLoad, matcher);
for (Entry<BinaryMapIndexReader, List<RouteSubregion>> r : map.entrySet()) {
if(nativeLib != null) {
for(RouteRegion reg : r.getKey().getRoutingIndexes()) {
nativeLoadRegion(request, reg);
}
} else {
try {
r.getKey().searchRouteIndex(request, r.getValue());
} catch (IOException e) {
throw new RuntimeException("Loading data exception", e);
}
}
}
ctx.loadedTiles++;
if (tile.isUnloaded()) {
ctx.loadedPrevUnloadedTiles++;
}
tile.setLoaded();
ctx.timeToLoad += (System.nanoTime() - now);
}
private void nativeLoadRegion(SearchRequest<RouteDataObject> request, RouteRegion reg) {
boolean intersects = false;
for(RouteSubregion sub : reg.getSubregions()) {
if(request.intersects(sub.left, sub.top, sub.right, sub.bottom)) {
intersects = true;
break;
}
}
if(intersects) {
RouteDataObject[] res = nativeLib.loadRouteRegion(reg, request.getLeft(), request.getRight(), request.getTop(), request.getBottom());
if(res != null){
for(RouteDataObject ro : res) {
if(ro != null) {
request.publish(ro);
}
}
}
}
}
private boolean processRouteSegment(final RoutingContext ctx, boolean reverseWaySearch,
PriorityQueue<RouteSegment> graphSegments, TLongObjectHashMap<RouteSegment> visitedSegments, int targetEndX, int targetEndY,
@ -649,7 +536,7 @@ public class BinaryRoutePlanner {
long l = (((long) x) << 31) + (long) y;
RouteSegment next = tile.routes.get(l);
RouteSegment next = tile.getSegment(l);
// 3. get intersected ways
if (next != null) {
// TO-DO U-Turn
@ -875,6 +762,9 @@ public class BinaryRoutePlanner {
// calculate time
for (int i = 0; i < result.size(); i++) {
if(ctx.runTilesGC()) {
unloadUnusedTiles(ctx, ctx.config.NUMBER_OF_DESIRABLE_TILES_IN_MEMORY);
}
RouteSegmentResult rr = result.get(i);
RouteDataObject road = rr.getObject();
double distOnRoadToPass = 0;
@ -1190,7 +1080,7 @@ public class BinaryRoutePlanner {
}
}
}
RouteSegment routeSegment = tl.getLoadedRoutes().get(l);
RouteSegment routeSegment = tl.getSegment(l);
// try to attach all segments except with current id
while (routeSegment != null) {
if (routeSegment.road.getId() != road.getId() && routeSegment.road.getId() != previousRoadId) {

View file

@ -23,7 +23,8 @@ public class RoutingConfiguration {
// 1.1 tile load parameters (should not affect routing)
public int ZOOM_TO_LOAD_TILES = 13; // 12?, 14?
public int ITERATIONS_TO_RUN_GC = 100;
public int NUMBER_OF_DESIRABLE_TILES_IN_MEMORY = 25;
public static int DEFAULT_DESIRABLE_TILES_IN_MEMORY = 100;
public int NUMBER_OF_DESIRABLE_TILES_IN_MEMORY = DEFAULT_DESIRABLE_TILES_IN_MEMORY;
// 1.2 Dynamic road prioritizing (heuristic)
public boolean useDynamicRoadPrioritising = true;
@ -92,21 +93,21 @@ public class RoutingConfiguration {
}
private static int parseSilentInt(String t, int v) {
if (t == null) {
if (t == null || t.length() == 0) {
return v;
}
return Integer.parseInt(t);
}
private static boolean parseSilentBoolean(String t, boolean v) {
if (t == null) {
if (t == null || t.length() == 0) {
return v;
}
return Boolean.parseBoolean(t);
}
private static double parseSilentDouble(String t, double v) {
if (t == null) {
if (t == null || t.length() == 0) {
return v;
}
return Double.parseDouble(t);

View file

@ -7,10 +7,20 @@ import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TIntHashSet;
import gnu.trove.set.hash.TLongHashSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.osmand.NativeLibrary;
import net.osmand.NativeLibrary.NativeRouteSearchResult;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
import net.osmand.binary.RouteDataObject;
import net.osmand.router.BinaryRoutePlanner.RouteSegment;
import net.osmand.router.BinaryRoutePlanner.RouteSegmentVisitor;
@ -49,6 +59,7 @@ public class RoutingContext {
long timeToLoad = 0;
long timeToCalculate = 0;
public int loadedTiles = 0;
int distinctLoadedTiles = 0;
int maxLoadedTiles = 0;
int loadedPrevUnloadedTiles = 0;
int unloadedTiles = 0;
@ -180,6 +191,145 @@ public class RoutingContext {
distinctUnloadedTiles.add(l);
}
public void loadTileData(final RoutingTile tile, final List<RouteDataObject> toFillIn, NativeLibrary nativeLib,
Map<BinaryMapIndexReader, List<RouteSubregion>> map) {
long now = System.nanoTime();
ResultMatcher<RouteDataObject> matcher = new ResultMatcher<RouteDataObject>() {
@Override
public boolean publish(RouteDataObject o) {
if (toFillIn != null) {
if (getRouter().acceptLine(o)) {
toFillIn.add(o);
}
}
registerRouteDataObject(o, tile);
return false;
}
@Override
public boolean isCancelled() {
return false;
}
};
int zoomToLoad = 31 - tile.getZoom();
int tileX = tile.getTileX();
int tileY = tile.getTileY();
boolean loadData = toFillIn != null;
List<NativeRouteSearchResult> nativeRouteSearchResults = new ArrayList<NativeRouteSearchResult>();
SearchRequest<RouteDataObject> request = BinaryMapIndexReader.buildSearchRouteRequest(tileX << zoomToLoad,
(tileX + 1) << zoomToLoad, tileY << zoomToLoad, (tileY + 1) << zoomToLoad, matcher);
for (Entry<BinaryMapIndexReader, List<RouteSubregion>> r : map.entrySet()) {
if(nativeLib != null) {
for(RouteRegion reg : r.getKey().getRoutingIndexes()) {
NativeRouteSearchResult rs = nativeLoadRegion(request, reg, nativeLib, loadData);
if(rs != null) {
if(!loadData){
if (rs.nativeHandler != 0) {
nativeRouteSearchResults.add(rs);
}
} else {
if(rs.objects != null){
for(RouteDataObject ro : rs.objects) {
if(ro != null) {
request.publish(ro);
}
}
}
}
}
}
} else {
try {
r.getKey().searchRouteIndex(request, r.getValue());
} catch (IOException e) {
throw new RuntimeException("Loading data exception", e);
}
}
}
loadedTiles++;
if (tile.isUnloaded()) {
loadedPrevUnloadedTiles++;
} else {
distinctLoadedTiles++;
}
tile.setLoaded();
if(nativeRouteSearchResults.size() > 0) {
tile.nativeLib = nativeLib;
tile.nativeResults = nativeRouteSearchResults;
}
timeToLoad += (System.nanoTime() - now);
}
private NativeRouteSearchResult nativeLoadRegion(SearchRequest<RouteDataObject> request, RouteRegion reg, NativeLibrary nativeLib, boolean loadData) {
boolean intersects = false;
for(RouteSubregion sub : reg.getSubregions()) {
if(request.intersects(sub.left, sub.top, sub.right, sub.bottom)) {
intersects = true;
break;
}
}
if(intersects) {
return nativeLib.loadRouteRegion(reg, request.getLeft(), request.getRight(), request.getTop(), request.getBottom(), loadData);
}
return null;
}
/*private */void registerRouteDataObject(RouteDataObject o, RoutingTile suggestedTile ) {
if(!getRouter().acceptLine(o)){
return;
}
RoutingTile tl = suggestedTile;
RouteDataObject old = tl.idObjects.get(o.id);
// sometimes way is present only partially in one index
if (old != null && old.getPointsLength() >= o.getPointsLength()) {
return;
};
for (int j = 0; j < o.getPointsLength(); j++) {
int x = o.getPoint31XTile(j);
int y = o.getPoint31YTile(j);
if(!tl.checkContains(x, y)){
// don't register in different tiles
// in order to throw out tile object easily
continue;
}
long l = (((long) x) << 31) + (long) y;
RouteSegment segment = new RouteSegment(o , j);
RouteSegment prev = tl.getSegment(l);
boolean i = true;
if (prev != null) {
if (old == null) {
segment.next = prev;
} else if (prev.road == old) {
segment.next = prev.next;
} else {
// segment somewhere in the middle replace element in linked list
RouteSegment rr = prev;
while (rr != null) {
if (rr.road == old) {
prev.next = segment;
segment.next = rr.next;
break;
}
prev = rr;
rr = rr.next;
}
i = false;
}
}
if (i) {
tl.routes.put(l, segment);
tl.idObjects.put(o.id, o);
}
}
}
public static class RoutingTile {
private int tileX;
private int tileY;
@ -188,8 +338,53 @@ public class RoutingContext {
// make it without get/set for fast access
public int access;
TLongObjectMap<RouteSegment> routes = new TLongObjectHashMap<RouteSegment>();
TLongObjectHashMap<RouteDataObject> idObjects = new TLongObjectHashMap<RouteDataObject>();
private NativeLibrary nativeLib;
// null if it doesn't work with native results
private List<NativeRouteSearchResult> nativeResults;
private TLongHashSet excludeDuplications = new TLongHashSet();
private TLongObjectMap<RouteSegment> routes = new TLongObjectHashMap<RouteSegment>();
private TLongObjectHashMap<RouteDataObject> idObjects = new TLongObjectHashMap<RouteDataObject>();
public RouteSegment getSegment(long id) {
if(nativeResults != null) {
RouteSegment original = loadNativeRouteSegment(id);
return original;
}
return routes.get(id);
}
private RouteSegment loadNativeRouteSegment(long id) {
int y31 = (int) (id & Integer.MAX_VALUE);
int x31 = (int) (id >> 31);
excludeDuplications.clear();
RouteSegment original = null;
RouteSegment prev = null;
for (NativeRouteSearchResult rs : nativeResults) {
RouteDataObject[] res = nativeLib.getDataObjects(rs, x31, y31);
if (res != null) {
for (RouteDataObject ro : res) {
if (ro != null && !excludeDuplications.contains(ro.id)) {
excludeDuplications.add(ro.id);
for (int i = 0; i < ro.pointsX.length; i++) {
if (ro.getPoint31XTile(i) == x31 && ro.getPoint31YTile(i) == y31) {
RouteSegment segment = new RouteSegment(ro, i);
if (prev != null) {
prev.next = segment;
prev = segment;
} else {
original = segment;
prev = segment;
}
}
}
}
}
}
}
return original;
}
public RoutingTile(int tileX, int tileY, int zoom) {
this.tileX = tileX;
@ -231,6 +426,11 @@ public class RoutingContext {
} else {
isLoaded = -Math.abs(isLoaded);
}
if(nativeResults != null) {
for(NativeRouteSearchResult rs : nativeResults) {
rs.deleteNativeResult();
}
}
}
public void setLoaded() {
@ -242,8 +442,5 @@ public class RoutingContext {
return tileX == (x31 >> (31 - zoom)) && tileY == (y31 >> (31 - zoom));
}
public TLongObjectMap<RouteSegment> getLoadedRoutes() {
return routes;
}
}
}

View file

@ -7,7 +7,8 @@
<!-- 1.1 tile load parameters (should not affect routing) -->
<attribute name="zoomToLoadTiles" value="13" />
<attribute name="iterationsToRunGC" value="125" />
<attribute name="desirableTilesInMemory" value="25" />
<!-- by default it is 25 -->
<attribute name="desirableTilesInMemory" value="" />
<!-- 1.2 Dynamic road prioritizing (heuristic) -->
<attribute name="useDynamicRoadPrioritising" value="true" />
@ -78,7 +79,7 @@
<obstacle tag="railway" value="crossing" penalty="25"/>
<obstacle tag="railway" value="level_crossing" penalty="25"/>
<obstacle tag="motorcar" value="no" penalty="-1"/>
<obstacle tag="barrier" value="lift_gate" penalty="-1"/>
<obstacle tag="barrier" value="lift_gate" penalty="100"/>
<obstacle tag="barrier" value="gate" penalty="-1"/>
<obstacle tag="barrier" value="bollard" penalty="-1"/>

View file

@ -13,6 +13,7 @@ import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.router.BinaryRoutePlanner;
import net.osmand.router.RoutingConfiguration;
import net.osmand.swing.DataExtractionSettings;
import net.osmand.swing.NativeSwingRendering;
import org.junit.Before;
import org.junit.Test;
@ -22,11 +23,13 @@ import org.xml.sax.SAXException;
public class JUnitRouteTest {
static BinaryMapIndexReader[] rs;
private NativeSwingRendering lib;
@Before
public void setupFiles() throws IOException {
if(rs != null){
return;
}
lib = NativeSwingRendering.getDefaultFromSettings();
BinaryRoutePlanner.PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = false;
String obfdir = System.getenv("OBF_DIR");
if(Algoritms.isEmpty(obfdir)){
@ -50,12 +53,12 @@ public class JUnitRouteTest {
@Test
public void runNL() throws SAXException, IOException, ParserConfigurationException {
RouterTestsSuite.test(getClass().getResourceAsStream("nl.test.xml"), rs, RoutingConfiguration.getDefault());
RouterTestsSuite.test(lib, getClass().getResourceAsStream("nl.test.xml"), rs, RoutingConfiguration.getDefault());
}
@Test
public void runNL2() throws SAXException, IOException, ParserConfigurationException {
RouterTestsSuite.test(getClass().getResourceAsStream("nl2.test.xml"), rs, RoutingConfiguration.getDefault());
RouterTestsSuite.test(lib, getClass().getResourceAsStream("nl2.test.xml"), rs, RoutingConfiguration.getDefault());
}
}

View file

@ -11,6 +11,7 @@ import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.osmand.NativeLibrary;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.router.BinaryRoutePlanner;
import net.osmand.router.BinaryRoutePlanner.RouteSegment;
@ -101,7 +102,7 @@ public class RouterTestsSuite {
boolean allSuccess = true;
for(File f : params.tests) {
allSuccess &= test(new FileInputStream(f), rs, params.configBuilder);
allSuccess &= test(null, new FileInputStream(f), rs, params.configBuilder);
}
if (allSuccess) {
System.out.println("All is successfull");
@ -115,13 +116,13 @@ public class RouterTestsSuite {
}
public static boolean test(InputStream resource, BinaryMapIndexReader[] rs, RoutingConfiguration.Builder config) throws SAXException, IOException, ParserConfigurationException {
public static boolean test(NativeLibrary lib, InputStream resource, BinaryMapIndexReader[] rs, RoutingConfiguration.Builder config) throws SAXException, IOException, ParserConfigurationException {
Document testSuite = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(resource));
NodeList tests = testSuite.getElementsByTagName("test");
for (int i = 0; i < tests.getLength(); i++) {
Element e = (Element) tests.item(i);
BinaryRoutePlanner router = new BinaryRoutePlanner(null, rs);
BinaryRoutePlanner router = new BinaryRoutePlanner(lib, rs);
testRoute(e, router, config);
}

View file

@ -212,7 +212,7 @@ public class MapClusterLayer implements MapPanelLayer {
long lstart = (((long) startRoad.getPoint31XTile(st.getSegmentStart())) << 31) +
(long) startRoad.getPoint31YTile(st.getSegmentStart());
RouteSegment next = ctx.getRoutingTile((int)lstart>>31, (int) (lstart- (lstart>>31)<<31)).
getLoadedRoutes().get(lstart);
getSegment(lstart);
while (next != null) {
if(next.getRoad().getId() != st.getRoad().getId()){
queue.add(next);
@ -281,7 +281,7 @@ public class MapClusterLayer implements MapPanelLayer {
router.loadRoutes(ctx, x, y);
long l = (((long) x) << 31) + (long) y;
next = ctx.getRoutingTile(x, y).getLoadedRoutes().get(l);
next = ctx.getRoutingTile(x, y).getSegment(l);
boolean addToQueue = true;;
while (next != null) {
String h = getHighway(next.getRoad());
@ -298,7 +298,7 @@ public class MapClusterLayer implements MapPanelLayer {
}
if (addToQueue) {
next = ctx.getRoutingTile(x, y).getLoadedRoutes().get(l);
next = ctx.getRoutingTile(x, y).getSegment(l);
while (next != null) {
if (!visitedIds.contains(calculateId(next, next.getSegmentStart()))) {
queue.add(next);

View file

@ -21,7 +21,7 @@
* Parking plugin
* Enable/disable monitoring widget
</string>
<string name="use_compass_navigation_descr">Use compass in navigation when diretion is not detected</string>
<string name="use_compass_navigation_descr">Use compass when direction is not detected</string>
<string name="use_compass_navigation">Use compass</string>
<string name="select_animate_speedup">Select animate route acceleration</string>
<string name="monitoring_info_control">Show logging button</string>

View file

@ -283,11 +283,11 @@ public class MapRenderRepositories {
NativeSearchResult resultHandler = library.searchObjectsForRendering(leftX, rightX, topY, bottomY, zoom, renderingReq,
PerformanceFlags.checkForDuplicateObjectIds, this, context.getString(R.string.switch_to_raster_map_to_see));
if (checkWhetherInterrupted()) {
library.deleteSearchResult(resultHandler);
resultHandler.deleteNativeResult();
return false;
}
if(cNativeObjects != null) {
library.deleteSearchResult(cNativeObjects);
cNativeObjects.deleteNativeResult();
}
cNativeObjects = resultHandler;
cObjectsBox = dataBox;

View file

@ -380,6 +380,9 @@ jfieldID jfield_RouteDataObject_pointTypes = NULL;
jfieldID jfield_RouteDataObject_id = NULL;
jmethodID jmethod_RouteDataObject_init = NULL;
jclass jclass_NativeRouteSearchResult = NULL;
jmethodID jmethod_NativeRouteSearchResult_init = NULL;
void loadJniRenderingContext(JNIEnv* env)
{
jclass_RenderingContext = findClass(env, "net/osmand/RenderingContext");
@ -409,6 +412,8 @@ void loadJniRenderingContext(JNIEnv* env)
jmethod_JUnidecode_unidecode = env->GetStaticMethodID(jclass_JUnidecode, "unidecode", "(Ljava/lang/String;)Ljava/lang/String;");
jclass_RouteDataObject = findClass(env, "net/osmand/binary/RouteDataObject");
jclass_NativeRouteSearchResult = findClass(env, "net/osmand/NativeLibrary$NativeRouteSearchResult");
jmethod_NativeRouteSearchResult_init = env->GetMethodID(jclass_NativeRouteSearchResult, "<init>", "(J[Lnet/osmand/binary/RouteDataObject;)V");
jfield_RouteDataObject_types = getFid(env, jclass_RouteDataObject, "types", "[I" );
jfield_RouteDataObject_pointsX = getFid(env, jclass_RouteDataObject, "pointsX", "[I" );
@ -439,97 +444,156 @@ void pullFromJavaRenderingContext(JNIEnv* env, jobject jrc, JNIRenderingContext*
// ElapsedTimer routingTimer;
//RouteDataObject[] loadRoutingData(RouteRegion reg, int left, int right, int top, int bottom)
extern "C" JNIEXPORT jobjectArray JNICALL Java_net_osmand_NativeLibrary_loadRoutingData(JNIEnv* ienv,
jobject obj, jobject reg, jstring regName, jint filepointer, jint left, jint right, jint top, jint bottom) {
jobject convertRouteDataObjectToJava(JNIEnv* ienv, RouteDataObject* route, jobject reg) {
jintArray nameInts = ienv->NewIntArray(route->names.size());
jobjectArray nameStrings = ienv->NewObjectArray(route->names.size(), jclassString, NULL);
jint ar[route->names.size()];
UNORDERED(map)<int, std::string >::iterator itNames = route->names.begin();
jsize sz = 0;
for (; itNames != route->names.end(); itNames++, sz++) {
std::string name = itNames->second;
jstring js = ienv->NewStringUTF(name.c_str());
ienv->SetObjectArrayElement(nameStrings, sz, js);
ienv->DeleteLocalRef(js);
ar[sz] = itNames->first;
}
ienv->SetIntArrayRegion(nameInts, 0, route->names.size(), ar);
jobject robj = ienv->NewObject(jclass_RouteDataObject, jmethod_RouteDataObject_init, reg, nameInts, nameStrings);
ienv->DeleteLocalRef(nameInts);
ienv->DeleteLocalRef(nameStrings);
ienv->SetLongField(robj, jfield_RouteDataObject_id, route->id);
jintArray types = ienv->NewIntArray(route->types.size());
if (route->types.size() > 0) {
ienv->SetIntArrayRegion(types, 0, route->types.size(), (jint*) &route->types[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_types, types);
ienv->DeleteLocalRef(types);
jintArray pointsX = ienv->NewIntArray(route->pointsX.size());
if (route->pointsX.size() > 0) {
ienv->SetIntArrayRegion(pointsX, 0, route->pointsX.size(), (jint*) &route->pointsX[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_pointsX, pointsX);
ienv->DeleteLocalRef(pointsX);
jintArray pointsY = ienv->NewIntArray(route->pointsY.size());
if (route->pointsY.size() > 0) {
ienv->SetIntArrayRegion(pointsY, 0, route->pointsY.size(), (jint*) &route->pointsY[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_pointsY, pointsY);
ienv->DeleteLocalRef(pointsY);
jlongArray restrictions = ienv->NewLongArray(route->restrictions.size());
if (route->restrictions.size() > 0) {
ienv->SetLongArrayRegion(restrictions, 0, route->restrictions.size(), (jlong*) &route->restrictions[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_restrictions, restrictions);
ienv->DeleteLocalRef(restrictions);
jobjectArray pointTypes = ienv->NewObjectArray(route->pointTypes.size(), jclassIntArray, NULL);
for (jint k = 0; k < route->pointTypes.size(); k++) {
std::vector<uint32_t> ts = route->pointTypes[k];
if (ts.size() > 0) {
jintArray tos = ienv->NewIntArray(ts.size());
ienv->SetIntArrayRegion(tos, 0, ts.size(), (jint*) &ts[0]);
ienv->SetObjectArrayElement(pointTypes, k, tos);
ienv->DeleteLocalRef(tos);
}
}
ienv->SetObjectField(robj, jfield_RouteDataObject_pointTypes, pointTypes);
ienv->DeleteLocalRef(pointTypes);
return robj;
}
class NativeRoutingTile {
public:
std::vector<RouteDataObject*> result;
UNORDERED(map)<uint64_t, std::vector<RouteDataObject*> > cachedByLocations;
};
// protected static native void deleteRouteSearchResult(long searchResultHandle!);
extern "C" JNIEXPORT void JNICALL Java_net_osmand_NativeLibrary_deleteRouteSearchResult(JNIEnv* ienv,
jobject obj, jlong ref) {
NativeRoutingTile* t = (NativeRoutingTile*) ref;
for (unsigned int i = 0; i < t->result.size(); i++) {
delete t->result[i];
t->result[i] = NULL;
}
delete t;
}
// protected static native RouteDataObject[] getRouteDataObjects(NativeRouteSearchResult rs, int x31, int y31!);
extern "C" JNIEXPORT jobjectArray JNICALL Java_net_osmand_NativeLibrary_getRouteDataObjects(JNIEnv* ienv,
jobject obj, jobject reg, jlong ref, jint x31, jint y31) {
NativeRoutingTile* t = (NativeRoutingTile*) ref;
uint64_t lr = ((uint64_t) x31 << 31) + y31;
std::vector<RouteDataObject*> collected = t->cachedByLocations[lr];
jobjectArray res = ienv->NewObjectArray(collected.size(), jclass_RouteDataObject, NULL);
for (jint i = 0; i < collected.size(); i++) {
jobject robj = convertRouteDataObjectToJava(ienv, collected[i], reg);
ienv->SetObjectArrayElement(res, i, robj);
ienv->DeleteLocalRef(robj);
}
return res;
}
//protected static native NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int fpointer, int left, int right, int top, int bottom,
// boolean loadObjects!);
extern "C" JNIEXPORT jobject JNICALL Java_net_osmand_NativeLibrary_loadRoutingData(JNIEnv* ienv,
jobject obj, jobject reg, jstring regName, jint filepointer, jint left, jint right, jint top, jint bottom, jboolean loadObjects) {
RoutingIndex ind;
ind.filePointer = filepointer;
ind.name = getString(ienv, regName);
std::vector<RouteDataObject*> result;
SearchQuery q(left, right, top, bottom);
searchRouteRegion(&q, result, &ind);
jobjectArray res = ienv->NewObjectArray(result.size(), jclass_RouteDataObject, NULL );
for (jint i = 0; i < result.size(); i++) {
if (result[i] != NULL) {
jintArray nameInts = ienv->NewIntArray(result[i]->names.size());
jobjectArray nameStrings = ienv->NewObjectArray(result[i]->names.size(),
jclassString, NULL);
jint ar[result[i]->names.size()];
UNORDERED(map)<int, std::string >::iterator itNames = result[i]->names.begin();
jsize sz = 0;
for(;itNames != result[i]->names.end(); itNames++, sz++) {
std::string name = itNames->second;
jstring js = ienv->NewStringUTF(name.c_str());
ienv->SetObjectArrayElement(nameStrings, sz, js);
ienv->DeleteLocalRef(js);
ar[sz] = itNames->first;
if (loadObjects) {
jobjectArray res = ienv->NewObjectArray(result.size(), jclass_RouteDataObject, NULL);
for (jint i = 0; i < result.size(); i++) {
if (result[i] != NULL) {
jobject robj = convertRouteDataObjectToJava(ienv, result[i], reg);
ienv->SetObjectArrayElement(res, i, robj);
ienv->DeleteLocalRef(robj);
}
ienv->SetIntArrayRegion(nameInts, 0, result[i]->names.size(),ar);
jobject robj = ienv->NewObject(jclass_RouteDataObject, jmethod_RouteDataObject_init, reg,
nameInts, nameStrings);
ienv->DeleteLocalRef(nameInts);
ienv->DeleteLocalRef(nameStrings);
ienv->SetLongField(robj, jfield_RouteDataObject_id, result[i]->id);
jintArray types = ienv->NewIntArray(result[i]->types.size());
if(result[i]->types.size() > 0) {
ienv->SetIntArrayRegion(types, 0, result[i]->types.size(), (jint*) &result[i]->types[0]);
}
for (unsigned int i = 0; i < result.size(); i++) {
if (result[i] != NULL) {
delete result[i];
}
ienv->SetObjectField(robj, jfield_RouteDataObject_types, types);
ienv->DeleteLocalRef(types);
jintArray pointsX = ienv->NewIntArray(result[i]->pointsX.size());
if (result[i]->pointsX.size() > 0) {
ienv->SetIntArrayRegion(pointsX, 0, result[i]->pointsX.size(), (jint*) &result[i]->pointsX[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_pointsX, pointsX);
ienv->DeleteLocalRef(pointsX);
jintArray pointsY = ienv->NewIntArray(result[i]->pointsY.size());
if (result[i]->pointsY.size() > 0) {
ienv->SetIntArrayRegion(pointsY, 0, result[i]->pointsY.size(), (jint*) &result[i]->pointsY[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_pointsY, pointsY);
ienv->DeleteLocalRef(pointsY);
jlongArray restrictions = ienv->NewLongArray(result[i]->restrictions.size());
if (result[i]->restrictions.size() > 0) {
ienv->SetLongArrayRegion(restrictions, 0, result[i]->restrictions.size(),
(jlong*) &result[i]->restrictions[0]);
}
ienv->SetObjectField(robj, jfield_RouteDataObject_restrictions, restrictions);
ienv->DeleteLocalRef(restrictions);
jobjectArray pointTypes = ienv->NewObjectArray(result[i]->pointTypes.size(), jclassIntArray, NULL);
for(jint k = 0; k < result[i]->pointTypes.size(); k++ ) {
std::vector<uint32_t> ts = result[i]->pointTypes[k];
if (ts.size() > 0) {
jintArray tos = ienv->NewIntArray(ts.size());
ienv->SetIntArrayRegion(tos, 0, ts.size(), (jint*) &ts[0]);
ienv->SetObjectArrayElement(pointTypes, k, tos);
ienv->DeleteLocalRef(tos);
result[i] = NULL;
}
return ienv->NewObject(jclass_NativeRouteSearchResult, jmethod_NativeRouteSearchResult_init, ((jlong) 0), res);
} else {
NativeRoutingTile* r = new NativeRoutingTile();
for (jint i = 0; i < result.size(); i++) {
if (result[i] != NULL) {
r->result.push_back(result[i]);
for (jint j = 0; j < result[i]->pointsX.size(); j++) {
jint x = result[i]->pointsX[j];
jint y = result[i]->pointsY[j];
uint64_t lr = ((uint64_t) x << 31) + y;
r->cachedByLocations[lr].push_back(result[i]);
}
}
ienv->SetObjectField(robj, jfield_RouteDataObject_pointTypes, pointTypes);
ienv->DeleteLocalRef(pointTypes);
ienv->SetObjectArrayElement(res, i, robj);
ienv->DeleteLocalRef(robj);
}
jlong ref = (jlong) r;
if(r->result.size() == 0) {
ref = 0;
delete r;
}
return ienv->NewObject(jclass_NativeRouteSearchResult, jmethod_NativeRouteSearchResult_init, ref, NULL);
}
for (unsigned int i = 0; i < result.size(); i++) {
delete result[i];
result[i] = NULL;
}
return res;
}