2013-04-18 23:35:02 +02:00
|
|
|
package net.osmand.router;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.List;
|
|
|
|
|
2014-01-19 21:29:11 +01:00
|
|
|
import net.osmand.NativeLibrary;
|
2013-04-18 23:35:02 +02:00
|
|
|
import net.osmand.PlatformUtil;
|
2014-01-19 21:29:11 +01:00
|
|
|
import net.osmand.binary.BinaryMapIndexReader;
|
2013-04-18 23:35:02 +02:00
|
|
|
import net.osmand.binary.BinaryMapRouteReaderAdapter;
|
|
|
|
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
|
|
|
import net.osmand.binary.RouteDataObject;
|
|
|
|
import net.osmand.data.LatLon;
|
2013-10-26 14:40:59 +02:00
|
|
|
import net.osmand.data.QuadPoint;
|
2013-04-18 23:35:02 +02:00
|
|
|
import net.osmand.router.BinaryRoutePlanner.RouteSegment;
|
|
|
|
import net.osmand.util.MapUtils;
|
|
|
|
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
|
|
|
|
|
public class RoutePlannerFrontEnd {
|
|
|
|
|
|
|
|
private boolean useOldVersion;
|
|
|
|
protected static final Log log = PlatformUtil.getLog(BinaryRoutePlannerOld.class);
|
|
|
|
|
|
|
|
public RoutePlannerFrontEnd(boolean useOldVersion) {
|
|
|
|
this.useOldVersion = useOldVersion;
|
|
|
|
}
|
|
|
|
|
2014-01-19 21:29:11 +01:00
|
|
|
public enum RouteCalculationMode {
|
|
|
|
BASE,
|
|
|
|
NORMAL,
|
|
|
|
COMPLEX
|
|
|
|
}
|
|
|
|
|
|
|
|
public RoutingContext buildRoutingContext(RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map, RouteCalculationMode rm) {
|
|
|
|
return new RoutingContext(config, nativeLibrary, map, rm);
|
|
|
|
}
|
|
|
|
|
|
|
|
public RoutingContext buildRoutingContext(RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map) {
|
|
|
|
return new RoutingContext(config, nativeLibrary, map, RouteCalculationMode.NORMAL);
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
|
|
|
|
private static double squareDist(int x1, int y1, int x2, int y2) {
|
|
|
|
// translate into meters
|
|
|
|
double dy = MapUtils.convert31YToMeters(y1, y2);
|
|
|
|
double dx = MapUtils. convert31XToMeters(x1, x2);
|
|
|
|
return dx * dx + dy * dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
public RouteSegment findRouteSegment(double lat, double lon, RoutingContext ctx) throws IOException {
|
|
|
|
int px = MapUtils.get31TileNumberX(lon);
|
|
|
|
int py = MapUtils.get31TileNumberY(lat);
|
|
|
|
ArrayList<RouteDataObject> dataObjects = new ArrayList<RouteDataObject>();
|
|
|
|
ctx.loadTileData(px, py, 17, dataObjects);
|
|
|
|
if (dataObjects.isEmpty()) {
|
|
|
|
ctx.loadTileData(px, py, 15, dataObjects);
|
|
|
|
}
|
|
|
|
RouteSegment road = null;
|
|
|
|
double sdist = 0;
|
|
|
|
|
|
|
|
for (RouteDataObject r : dataObjects) {
|
|
|
|
if (r.getPointsLength() > 1) {
|
|
|
|
for (int j = 1; j < r.getPointsLength(); j++) {
|
2013-10-26 14:40:59 +02:00
|
|
|
QuadPoint pr = MapUtils.getProjectionPoint31(px, py, r.getPoint31XTile(j - 1),
|
|
|
|
r.getPoint31YTile(j - 1), r.getPoint31XTile(j ), r.getPoint31YTile(j ));
|
|
|
|
double currentsDist = squareDist((int) pr.x, (int)pr.y, px, py);
|
2013-04-18 23:35:02 +02:00
|
|
|
if (road == null || currentsDist < sdist) {
|
|
|
|
RouteDataObject ro = new RouteDataObject(r);
|
|
|
|
road = new RouteSegment(ro, j);
|
2013-10-26 14:40:59 +02:00
|
|
|
ro.insert(j, (int) pr.x, (int)pr.y);
|
2013-04-18 23:35:02 +02:00
|
|
|
sdist = currentsDist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (road != null) {
|
|
|
|
// re-register the best road because one more point was inserted
|
|
|
|
ctx.registerRouteDataObject(road.getRoad());
|
|
|
|
}
|
|
|
|
return road;
|
|
|
|
}
|
|
|
|
|
2014-01-19 21:29:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates) throws IOException, InterruptedException {
|
2013-04-18 23:35:02 +02:00
|
|
|
if(ctx.calculationProgress == null) {
|
|
|
|
ctx.calculationProgress = new RouteCalculationProgress();
|
|
|
|
}
|
2014-01-19 21:29:11 +01:00
|
|
|
boolean intermediatesEmpty = intermediates == null || intermediates.isEmpty();
|
2014-01-19 21:57:46 +01:00
|
|
|
PrecalculatedRouteDirection routeDirection = null;
|
2014-01-30 00:22:12 +01:00
|
|
|
double maxDistance = MapUtils.getDistance(start, end);
|
|
|
|
if(!intermediatesEmpty) {
|
|
|
|
LatLon b = start;
|
|
|
|
for(LatLon l : intermediates) {
|
|
|
|
maxDistance = Math.max(MapUtils.getDistance(b, l), maxDistance);
|
|
|
|
b = l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(ctx.calculationMode == RouteCalculationMode.COMPLEX && maxDistance > Math.max(ctx.config.DEVIATION_RADIUS * 4, 30000)) {
|
2014-01-19 21:29:11 +01:00
|
|
|
RoutingContext nctx = buildRoutingContext(ctx.config, ctx.nativeLib, ctx.getMaps(), RouteCalculationMode.BASE);
|
2014-01-19 23:58:17 +01:00
|
|
|
nctx.calculationProgress = ctx.calculationProgress ;
|
2014-01-19 21:29:11 +01:00
|
|
|
List<RouteSegmentResult> ls = searchRoute(nctx, start, end, intermediates);
|
2014-01-19 21:57:46 +01:00
|
|
|
routeDirection = PrecalculatedRouteDirection.build(ls,
|
2014-01-28 21:03:25 +01:00
|
|
|
ctx.config.DEVIATION_RADIUS, ctx.getRouter().getMaxDefaultSpeed() );
|
2014-01-19 21:29:11 +01:00
|
|
|
}
|
2014-01-22 23:41:43 +01:00
|
|
|
if(intermediatesEmpty && ctx.nativeLib != null) {
|
2013-04-18 23:35:02 +02:00
|
|
|
ctx.startX = MapUtils.get31TileNumberX(start.getLongitude());
|
|
|
|
ctx.startY = MapUtils.get31TileNumberY(start.getLatitude());
|
|
|
|
ctx.targetX = MapUtils.get31TileNumberX(end.getLongitude());
|
|
|
|
ctx.targetY = MapUtils.get31TileNumberY(end.getLatitude());
|
2014-01-19 21:57:46 +01:00
|
|
|
if(routeDirection != null) {
|
|
|
|
ctx.precalculatedRouteDirection = routeDirection.adopt(ctx);
|
|
|
|
}
|
2014-01-19 21:29:11 +01:00
|
|
|
List<RouteSegmentResult> res = runNativeRouting(ctx);
|
2013-04-18 23:35:02 +02:00
|
|
|
if(res != null) {
|
|
|
|
new RouteResultPreparation().printResults(ctx, start, end, res);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
int indexNotFound = 0;
|
|
|
|
List<RouteSegment> points = new ArrayList<BinaryRoutePlanner.RouteSegment>();
|
|
|
|
if(!addSegment(start, ctx, indexNotFound++, points)){
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (intermediates != null) {
|
|
|
|
for (LatLon l : intermediates) {
|
|
|
|
if (!addSegment(l, ctx, indexNotFound++, points)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!addSegment(end, ctx, indexNotFound++, points)){
|
|
|
|
return null;
|
|
|
|
}
|
2014-01-19 21:57:46 +01:00
|
|
|
List<RouteSegmentResult> res = searchRoute(ctx, points, routeDirection);
|
2013-04-18 23:35:02 +02:00
|
|
|
if(res != null) {
|
|
|
|
new RouteResultPreparation().printResults(ctx, start, end, res);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean addSegment(LatLon s, RoutingContext ctx, int indexNotFound, List<RouteSegment> res) throws IOException {
|
|
|
|
RouteSegment f = findRouteSegment(s.getLatitude(), s.getLongitude(), ctx);
|
|
|
|
if(f == null){
|
|
|
|
ctx.calculationProgress.segmentNotFound = indexNotFound;
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
log.info("Route segment found " + f.getRoad().id + " " + f.getRoad().getName());
|
|
|
|
res.add(f);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-01-19 21:57:46 +01:00
|
|
|
private List<RouteSegmentResult> searchRouteInternalPrepare(final RoutingContext ctx, RouteSegment start, RouteSegment end,
|
|
|
|
PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException {
|
2013-04-18 23:35:02 +02:00
|
|
|
ctx.targetX = end.road.getPoint31XTile(end.getSegmentStart());
|
|
|
|
ctx.targetY = end.road.getPoint31YTile(end.getSegmentStart());
|
|
|
|
ctx.startX = start.road.getPoint31XTile(start.getSegmentStart());
|
|
|
|
ctx.startY = start.road.getPoint31YTile(start.getSegmentStart());
|
2014-01-19 21:57:46 +01:00
|
|
|
if(routeDirection != null) {
|
|
|
|
ctx.precalculatedRouteDirection = routeDirection.adopt(ctx);
|
|
|
|
}
|
2014-01-22 23:41:43 +01:00
|
|
|
if (ctx.nativeLib != null) {
|
2014-01-19 21:29:11 +01:00
|
|
|
return runNativeRouting(ctx);
|
2013-04-18 23:35:02 +02:00
|
|
|
} else {
|
|
|
|
refreshProgressDistance(ctx);
|
|
|
|
// Split into 2 methods to let GC work in between
|
|
|
|
if(useOldVersion) {
|
|
|
|
new BinaryRoutePlannerOld().searchRouteInternal(ctx, start, end);
|
|
|
|
} else {
|
|
|
|
ctx.finalRouteSegment = new BinaryRoutePlanner().searchRouteInternal(ctx, start, end);
|
|
|
|
}
|
|
|
|
// 4. Route is found : collect all segments and prepare result
|
2014-01-19 21:29:11 +01:00
|
|
|
return new RouteResultPreparation().prepareResult(ctx, ctx.finalRouteSegment);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void refreshProgressDistance(RoutingContext ctx) {
|
|
|
|
if(ctx.calculationProgress != null) {
|
|
|
|
ctx.calculationProgress.distanceFromBegin = 0;
|
|
|
|
ctx.calculationProgress.distanceFromEnd = 0;
|
|
|
|
ctx.calculationProgress.reverseSegmentQueueSize = 0;
|
|
|
|
ctx.calculationProgress.directSegmentQueueSize = 0;
|
2013-10-26 14:40:59 +02:00
|
|
|
float rd = (float) MapUtils.squareRootDist31(ctx.startX, ctx.startY, ctx.targetX, ctx.targetY);
|
2013-04-18 23:35:02 +02:00
|
|
|
float speed = 0.9f * ctx.config.router.getMaxDefaultSpeed();
|
|
|
|
ctx.calculationProgress.totalEstimatedDistance = (float) (rd / speed);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-01-19 21:29:11 +01:00
|
|
|
private List<RouteSegmentResult> runNativeRouting(final RoutingContext ctx) throws IOException {
|
2013-04-18 23:35:02 +02:00
|
|
|
refreshProgressDistance(ctx);
|
|
|
|
RouteRegion[] regions = ctx.reverseMap.keySet().toArray(new BinaryMapRouteReaderAdapter.RouteRegion[ctx.reverseMap.size()]);
|
2014-02-06 00:17:07 +01:00
|
|
|
ctx.checkOldRoutingFiles(ctx.startX, ctx.startY);
|
|
|
|
ctx.checkOldRoutingFiles(ctx.targetX, ctx.targetY);
|
2013-04-18 23:35:02 +02:00
|
|
|
RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx.startX, ctx.startY, ctx.targetX, ctx.targetY,
|
2014-01-22 23:41:43 +01:00
|
|
|
ctx.config, regions, ctx.calculationProgress, ctx.precalculatedRouteDirection, ctx.calculationMode == RouteCalculationMode.BASE);
|
2013-04-18 23:35:02 +02:00
|
|
|
ArrayList<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>(Arrays.asList(res));
|
|
|
|
ctx.routingTime = ctx.calculationProgress.routingCalculatedTime;
|
|
|
|
ctx.visitedSegments = ctx.calculationProgress.visitedSegments;
|
|
|
|
ctx.loadedTiles = ctx.calculationProgress.loadedTiles;
|
2014-01-19 21:29:11 +01:00
|
|
|
return new RouteResultPreparation().prepareResult(ctx, result);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-19 21:57:46 +01:00
|
|
|
private List<RouteSegmentResult> searchRoute(final RoutingContext ctx, List<RouteSegment> points, PrecalculatedRouteDirection routeDirection)
|
|
|
|
throws IOException, InterruptedException {
|
2013-04-18 23:35:02 +02:00
|
|
|
if(points.size() > 2) {
|
|
|
|
ArrayList<RouteSegmentResult> firstPartRecalculatedRoute = null;
|
|
|
|
ArrayList<RouteSegmentResult> restPartRecalculatedRoute = null;
|
|
|
|
if (ctx.previouslyCalculatedRoute != null) {
|
|
|
|
List<RouteSegmentResult> prev = ctx.previouslyCalculatedRoute;
|
|
|
|
long id = points.get(1).getRoad().id;
|
|
|
|
int ss = points.get(1).getSegmentStart();
|
|
|
|
for (int i = 0; i < prev.size(); i++) {
|
|
|
|
RouteSegmentResult rsr = prev.get(i);
|
|
|
|
if (id == rsr.getObject().getId() && ss == rsr.getEndPointIndex()) {
|
|
|
|
firstPartRecalculatedRoute = new ArrayList<RouteSegmentResult>(i + 1);
|
|
|
|
restPartRecalculatedRoute = new ArrayList<RouteSegmentResult>(prev.size() - i);
|
|
|
|
for(int k = 0; k < prev.size(); k++) {
|
|
|
|
if(k <= i) {
|
|
|
|
firstPartRecalculatedRoute.add(prev.get(k));
|
|
|
|
} else {
|
|
|
|
restPartRecalculatedRoute.add(prev.get(k));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
List<RouteSegmentResult> results = new ArrayList<RouteSegmentResult>();
|
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
|
|
|
RoutingContext local = new RoutingContext(ctx);
|
|
|
|
if(i == 0) {
|
|
|
|
local.previouslyCalculatedRoute = firstPartRecalculatedRoute;
|
|
|
|
}
|
|
|
|
local.visitor = ctx.visitor;
|
|
|
|
local.calculationProgress = ctx.calculationProgress;
|
2014-01-19 21:57:46 +01:00
|
|
|
List<RouteSegmentResult> res = searchRouteInternalPrepare(local, points.get(i), points.get(i + 1), routeDirection);
|
2013-04-18 23:35:02 +02:00
|
|
|
|
|
|
|
results.addAll(res);
|
|
|
|
ctx.distinctLoadedTiles += local.distinctLoadedTiles;
|
|
|
|
ctx.loadedTiles += local.loadedTiles;
|
|
|
|
ctx.visitedSegments += local.visitedSegments;
|
|
|
|
ctx.loadedPrevUnloadedTiles += local.loadedPrevUnloadedTiles;
|
|
|
|
ctx.timeToCalculate += local.timeToCalculate;
|
|
|
|
ctx.timeToLoad += local.timeToLoad;
|
|
|
|
ctx.timeToLoadHeaders += local.timeToLoadHeaders;
|
|
|
|
ctx.relaxedSegments += local.relaxedSegments;
|
|
|
|
ctx.routingTime += local.routingTime;
|
|
|
|
|
|
|
|
local.unloadAllData(ctx);
|
|
|
|
if(restPartRecalculatedRoute != null) {
|
|
|
|
results.addAll(restPartRecalculatedRoute);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctx.unloadAllData();
|
|
|
|
return results;
|
|
|
|
}
|
2014-01-19 21:57:46 +01:00
|
|
|
return searchRoute(ctx, points.get(0), points.get(1), routeDirection);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("static-access")
|
2014-01-19 21:57:46 +01:00
|
|
|
private List<RouteSegmentResult> searchRoute(final RoutingContext ctx, RouteSegment start, RouteSegment end, PrecalculatedRouteDirection routeDirection)
|
|
|
|
throws IOException, InterruptedException {
|
2013-04-18 23:35:02 +02:00
|
|
|
if(ctx.SHOW_GC_SIZE){
|
|
|
|
long h1 = ctx.runGCUsedMemory();
|
|
|
|
float mb = (1 << 20);
|
|
|
|
log.warn("Used before routing " + h1 / mb+ " actual");
|
|
|
|
}
|
2014-01-19 21:57:46 +01:00
|
|
|
List<RouteSegmentResult> result = searchRouteInternalPrepare(ctx, start, end, routeDirection);
|
2013-04-18 23:35:02 +02:00
|
|
|
if (RoutingContext.SHOW_GC_SIZE) {
|
|
|
|
int sz = ctx.global.size;
|
|
|
|
log.warn("Subregion size " + ctx.subregionTiles.size() + " " + " tiles " + ctx.indexedSubregions.size());
|
|
|
|
ctx.runGCUsedMemory();
|
|
|
|
long h1 = ctx.runGCUsedMemory();
|
|
|
|
ctx.unloadAllData();
|
|
|
|
ctx.runGCUsedMemory();
|
|
|
|
long h2 = ctx.runGCUsedMemory();
|
|
|
|
float mb = (1 << 20);
|
|
|
|
log.warn("Unload context : estimated " + sz / mb + " ?= " + (h1 - h2) / mb + " actual");
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|