OsmAnd/OsmAnd-java/src/net/osmand/router/RoutePlannerFrontEnd.java

413 lines
15 KiB
Java
Raw Normal View History

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;
import net.osmand.PlatformUtil;
2014-01-19 21:29:11 +01:00
import net.osmand.binary.BinaryMapIndexReader;
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;
import net.osmand.router.BinaryRoutePlanner.RouteSegment;
2014-09-22 02:08:34 +02:00
import net.osmand.router.BinaryRoutePlanner.RouteSegmentPoint;
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);
}
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;
}
2014-09-22 02:08:34 +02:00
public RouteSegmentPoint 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);
}
2014-09-22 02:08:34 +02:00
RouteSegmentPoint 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);
if (road == null || currentsDist < sdist) {
RouteDataObject ro = new RouteDataObject(r);
2014-09-22 02:08:34 +02:00
road = new RouteSegmentPoint(ro, j);
road.preciseX = (int) pr.x;
road.preciseY = (int) pr.y;
sdist = currentsDist;
}
}
}
}
2014-09-22 02:08:34 +02:00
// if (road != null) {
// re-register the best road because one more point was inserted
2014-09-22 02:08:34 +02:00
// 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 {
2014-03-03 14:55:32 +01:00
return searchRoute(ctx, start, end, intermediates, null);
}
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates,
PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException {
if(ctx.calculationProgress == null) {
ctx.calculationProgress = new RouteCalculationProgress();
}
2014-01-19 21:29:11 +01:00
boolean intermediatesEmpty = intermediates == null || intermediates.isEmpty();
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;
}
}
2014-03-03 14:55:32 +01:00
if(ctx.calculationMode == RouteCalculationMode.COMPLEX && routeDirection == null
2014-03-26 12:55:43 +01:00
&& maxDistance > ctx.config.DEVIATION_RADIUS * 6) {
2014-01-19 21:29:11 +01:00
RoutingContext nctx = buildRoutingContext(ctx.config, ctx.nativeLib, ctx.getMaps(), RouteCalculationMode.BASE);
nctx.calculationProgress = ctx.calculationProgress ;
2014-01-19 21:29:11 +01:00
List<RouteSegmentResult> ls = searchRoute(nctx, start, end, intermediates);
2014-02-07 02:38:42 +01:00
routeDirection = PrecalculatedRouteDirection.build(ls, 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) {
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);
if(res != null) {
new RouteResultPreparation().printResults(ctx, start, end, res);
}
2014-09-22 02:08:34 +02:00
makeStartEndPointsPrecise(res, start, end, intermediates);
2014-03-03 18:44:17 +01:00
return res;
}
int indexNotFound = 0;
2014-09-22 02:08:34 +02:00
List<RouteSegmentPoint> points = new ArrayList<RouteSegmentPoint>();
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);
2014-09-22 02:08:34 +02:00
// make start and end more precise
makeStartEndPointsPrecise(res, start, end, intermediates);
if(res != null) {
new RouteResultPreparation().printResults(ctx, start, end, res);
}
return res;
}
2014-09-22 02:08:34 +02:00
protected void makeStartEndPointsPrecise(List<RouteSegmentResult> res, LatLon start, LatLon end, List<LatLon> intermediates) {
if (res.size() > 0) {
updateResult(res.get(0), start, true);
updateResult(res.get(res.size() - 1), end, false);
if (intermediates != null) {
int k = 1;
for (int i = 0; i < intermediates.size(); i++) {
LatLon ll = intermediates.get(i);
int px = MapUtils.get31TileNumberX(ll.getLongitude());
int py = MapUtils.get31TileNumberY(ll.getLatitude());
for (; k < res.size(); k++) {
double currentsDist = projectDistance(res, k, px, py);
if (currentsDist < 500 * 500) {
for (int k1 = k + 1; k1 < res.size(); k1++) {
double c2 = projectDistance(res, k1, px, py);
if (c2 < currentsDist) {
k = k1;
currentsDist = c2;
} else if (k1 - k > 15) {
break;
}
}
updateResult(res.get(k), ll, false);
if (k < res.size() - 1) {
updateResult(res.get(k + 1), ll, true);
}
break;
}
}
}
}
}
}
protected double projectDistance(List<RouteSegmentResult> res, int k, int px, int py) {
RouteSegmentResult sr = res.get(k);
RouteDataObject r = sr.getObject();
QuadPoint pp = MapUtils.getProjectionPoint31(px, py,
r.getPoint31XTile(sr.getStartPointIndex()), r.getPoint31YTile(sr.getStartPointIndex()),
r.getPoint31XTile(sr.getEndPointIndex()), r.getPoint31YTile(sr.getEndPointIndex()));
double currentsDist = squareDist((int) pp.x, (int)pp.y, px, py);
return currentsDist;
}
2014-09-22 02:08:34 +02:00
private void updateResult(RouteSegmentResult routeSegmentResult, LatLon point, boolean st) {
int px = MapUtils.get31TileNumberX(point.getLongitude());
int py = MapUtils.get31TileNumberY(point.getLatitude());
int pind = st ? routeSegmentResult.getStartPointIndex() : routeSegmentResult.getEndPointIndex();
RouteDataObject r = routeSegmentResult.getObject();
QuadPoint before = null;
QuadPoint after = null;
if(pind > 0) {
before = MapUtils.getProjectionPoint31(px, py, r.getPoint31XTile(pind - 1),
r.getPoint31YTile(pind - 1), r.getPoint31XTile(pind ), r.getPoint31YTile(pind ));
}
if(pind < r.getPointsLength() - 1) {
after = MapUtils.getProjectionPoint31(px, py, r.getPoint31XTile(pind + 1),
r.getPoint31YTile(pind + 1), r.getPoint31XTile(pind ), r.getPoint31YTile(pind ));
}
int insert = 0;
double dd = MapUtils.getDistance(point, MapUtils.get31LatitudeY(r.getPoint31YTile(pind)),
MapUtils.get31LongitudeX(r.getPoint31XTile(pind)));
double ddBefore = Double.POSITIVE_INFINITY;
double ddAfter = Double.POSITIVE_INFINITY;
QuadPoint i = null;
if(before != null) {
ddBefore = MapUtils.getDistance(point, MapUtils.get31LatitudeY((int) before.y),
MapUtils.get31LongitudeX((int) before.x));
if(ddBefore < dd) {
insert = -1;
i = before;
}
}
if(after != null) {
ddAfter = MapUtils.getDistance(point, MapUtils.get31LatitudeY((int) after.y),
MapUtils.get31LongitudeX((int) after.x));
if(ddAfter < dd && ddAfter < ddBefore) {
insert = 1;
i = after;
}
}
if (insert != 0) {
if (st && routeSegmentResult.getStartPointIndex() < routeSegmentResult.getEndPointIndex()) {
routeSegmentResult.setEndPointIndex(routeSegmentResult.getEndPointIndex() + 1);
}
if (!st && routeSegmentResult.getStartPointIndex() > routeSegmentResult.getEndPointIndex()) {
routeSegmentResult.setStartPointIndex(routeSegmentResult.getStartPointIndex() + 1);
}
if (insert > 0) {
r.insert(pind + 1, (int) i.x, (int) i.y);
if (st) {
routeSegmentResult.setStartPointIndex(routeSegmentResult.getStartPointIndex() + 1);
}
if (!st) {
routeSegmentResult.setEndPointIndex(routeSegmentResult.getEndPointIndex() + 1);
}
} else {
r.insert(pind, (int) i.x, (int) i.y);
}
}
}
private boolean addSegment(LatLon s, RoutingContext ctx, int indexNotFound, List<RouteSegmentPoint> res) throws IOException {
RouteSegmentPoint 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 {
2014-02-21 01:03:27 +01:00
ctx.initStartAndTargetPoints(start, end);
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);
} 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);
}
}
2014-02-21 01:03:27 +01: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);
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 {
refreshProgressDistance(ctx);
RouteRegion[] regions = ctx.reverseMap.keySet().toArray(new BinaryMapRouteReaderAdapter.RouteRegion[ctx.reverseMap.size()]);
ctx.checkOldRoutingFiles(ctx.startX, ctx.startY);
ctx.checkOldRoutingFiles(ctx.targetX, ctx.targetY);
2014-04-12 23:37:18 +02:00
long time = System.currentTimeMillis();
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);
2014-04-12 23:37:18 +02:00
System.out.println("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds");
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);
}
2014-09-22 02:08:34 +02:00
private List<RouteSegmentResult> searchRoute(final RoutingContext ctx, List<RouteSegmentPoint> points, PrecalculatedRouteDirection routeDirection)
2014-01-19 21:57:46 +01:00
throws IOException, InterruptedException {
2014-07-03 11:35:11 +02:00
if (points.size() <= 2) {
ctx.previouslyCalculatedRoute = null;
return searchRoute(ctx, points.get(0), points.get(1), routeDirection);
}
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));
}
}
2014-07-03 11:35:11 +02:00
break;
}
}
2014-07-03 11:35:11 +02:00
}
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;
List<RouteSegmentResult> res = searchRouteInternalPrepare(local, points.get(i), points.get(i + 1), routeDirection);
2014-07-03 11:35:11 +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;
}
}
2014-07-03 11:35:11 +02:00
ctx.unloadAllData();
return results;
}
@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 {
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);
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;
}
}