diff --git a/OsmAnd-java/src/net/osmand/router/BinaryRoutePlanner.java b/OsmAnd-java/src/net/osmand/router/BinaryRoutePlanner.java index 881bf33df0..2c3ad11768 100644 --- a/OsmAnd-java/src/net/osmand/router/BinaryRoutePlanner.java +++ b/OsmAnd-java/src/net/osmand/router/BinaryRoutePlanner.java @@ -4,6 +4,7 @@ import gnu.trove.map.hash.TLongObjectHashMap; import java.io.IOException; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; @@ -65,12 +66,15 @@ public class BinaryRoutePlanner { * return list of segments */ @SuppressWarnings("unused") - FinalRouteSegment searchRouteInternal(final RoutingContext ctx, RouteSegmentPoint start, RouteSegmentPoint end) throws InterruptedException, IOException { + FinalRouteSegment searchRouteInternal(final RoutingContext ctx, RouteSegmentPoint start, RouteSegmentPoint end, + RouteSegment recalculationEnd ) throws InterruptedException, IOException { // measure time ctx.timeToLoad = 0; ctx.visitedSegments = 0; ctx.memoryOverhead = 1000; ctx.timeToCalculate = System.nanoTime(); + + // Initializing priority queue to visit way segments Comparator nonHeuristicSegmentsComparator = new NonHeuristicSegmentsComparator(); PriorityQueue graphDirectSegments = new PriorityQueue(50, new SegmentsComparator(ctx)); @@ -80,8 +84,7 @@ public class BinaryRoutePlanner { TLongObjectHashMap visitedDirectSegments = new TLongObjectHashMap(); TLongObjectHashMap visitedOppositeSegments = new TLongObjectHashMap(); - - initQueuesWithStartEnd(ctx, start, end, graphDirectSegments, graphReverseSegments); + initQueuesWithStartEnd(ctx, start, end, recalculationEnd, graphDirectSegments, graphReverseSegments); // Extract & analyze segment with min(f(x)) from queue while final segment is not found boolean forwardSearch = true; @@ -90,7 +93,7 @@ public class BinaryRoutePlanner { FinalRouteSegment finalSegment = null; boolean onlyBackward = ctx.getPlanRoadDirection() < 0; - boolean onlyForward = ctx.getPlanRoadDirection() > 0; + boolean onlyForward = ctx.getPlanRoadDirection() > 0 ; while (!graphSegments.isEmpty()) { RouteSegment segment = graphSegments.poll(); // use accumulative approach @@ -169,6 +172,9 @@ public class BinaryRoutePlanner { } + + + protected void checkIfGraphIsEmpty(final RoutingContext ctx, boolean allowDirection, PriorityQueue graphSegments, RouteSegmentPoint pnt, TLongObjectHashMap visited, String msg) { @@ -238,7 +244,7 @@ public class BinaryRoutePlanner { private void initQueuesWithStartEnd(final RoutingContext ctx, RouteSegment start, RouteSegment end, - PriorityQueue graphDirectSegments, PriorityQueue graphReverseSegments) { + RouteSegment recalculationEnd, PriorityQueue graphDirectSegments, PriorityQueue graphReverseSegments) { RouteSegment startPos = initRouteSegment(ctx, start, true); RouteSegment startNeg = initRouteSegment(ctx, start, false); RouteSegment endPos = initRouteSegment(ctx, end, true); @@ -258,6 +264,10 @@ public class BinaryRoutePlanner { } } } + if(recalculationEnd != null) { + ctx.targetX = recalculationEnd.getRoad().getPoint31XTile(recalculationEnd.getSegmentStart()); + ctx.targetY = recalculationEnd.getRoad().getPoint31YTile(recalculationEnd.getSegmentStart()); + } float estimatedDistance = (float) estimatedDistance(ctx, ctx.targetX, ctx.targetY, ctx.startX, ctx.startY); if(startPos != null) { startPos.distanceToEnd = estimatedDistance; @@ -267,13 +277,17 @@ public class BinaryRoutePlanner { startNeg.distanceToEnd = estimatedDistance; graphDirectSegments.add(startNeg); } - if(endPos != null) { - endPos.distanceToEnd = estimatedDistance; - graphReverseSegments.add(endPos); - } - if(endNeg != null) { - endNeg.distanceToEnd = estimatedDistance; - graphReverseSegments.add(endNeg); + if(recalculationEnd != null) { + graphReverseSegments.add(recalculationEnd); + } else { + if (endPos != null) { + endPos.distanceToEnd = estimatedDistance; + graphReverseSegments.add(endPos); + } + if (endNeg != null) { + endNeg.distanceToEnd = estimatedDistance; + graphReverseSegments.add(endNeg); + } } } diff --git a/OsmAnd-java/src/net/osmand/router/RoutePlannerFrontEnd.java b/OsmAnd-java/src/net/osmand/router/RoutePlannerFrontEnd.java index 9c82c36fa2..483c9bde29 100644 --- a/OsmAnd-java/src/net/osmand/router/RoutePlannerFrontEnd.java +++ b/OsmAnd-java/src/net/osmand/router/RoutePlannerFrontEnd.java @@ -16,6 +16,7 @@ import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.RouteDataObject; import net.osmand.data.LatLon; import net.osmand.data.QuadPoint; +import net.osmand.router.BinaryRoutePlanner.RouteSegment; import net.osmand.router.BinaryRoutePlanner.RouteSegmentPoint; import net.osmand.util.MapUtils; @@ -25,6 +26,7 @@ public class RoutePlannerFrontEnd { private boolean useOldVersion; protected static final Log log = PlatformUtil.getLog(RoutePlannerFrontEnd.class); + public boolean useSmartRouteRecalculation = true; public RoutePlannerFrontEnd(boolean useOldVersion) { this.useOldVersion = useOldVersion; @@ -99,6 +101,10 @@ public class RoutePlannerFrontEnd { public List searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List intermediates) throws IOException, InterruptedException { return searchRoute(ctx, start, end, intermediates, null); } + + public void setUseFastRecalculation(boolean use) { + useSmartRouteRecalculation = use; + } public List searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List intermediates, @@ -127,10 +133,14 @@ public class RoutePlannerFrontEnd { ctx.startY = MapUtils.get31TileNumberY(start.getLatitude()); ctx.targetX = MapUtils.get31TileNumberX(end.getLongitude()); ctx.targetY = MapUtils.get31TileNumberY(end.getLatitude()); + RouteSegment recalculationEnd = getRecalculationEnd(ctx); + if(recalculationEnd != null) { + ctx.initTargetPoint(recalculationEnd); + } if(routeDirection != null) { ctx.precalculatedRouteDirection = routeDirection.adopt(ctx); - } - List res = runNativeRouting(ctx); + } + List res = runNativeRouting(ctx, recalculationEnd); if(res != null) { new RouteResultPreparation().printResults(ctx, start, end, res); } @@ -283,24 +293,62 @@ public class RoutePlannerFrontEnd { private List searchRouteInternalPrepare(final RoutingContext ctx, RouteSegmentPoint start, RouteSegmentPoint end, PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException { - ctx.initStartAndTargetPoints(start, end); + RouteSegment recalculationEnd = getRecalculationEnd(ctx); + if(recalculationEnd != null) { + ctx.initStartAndTargetPoints(start, recalculationEnd); + } else { + ctx.initStartAndTargetPoints(start, end); + } if(routeDirection != null) { ctx.precalculatedRouteDirection = routeDirection.adopt(ctx); } if (ctx.nativeLib != null) { - return runNativeRouting(ctx); + return runNativeRouting(ctx, recalculationEnd); } 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); + ctx.finalRouteSegment = new BinaryRoutePlanner().searchRouteInternal(ctx, start, end, recalculationEnd); } // 4. Route is found : collect all segments and prepare result return new RouteResultPreparation().prepareResult(ctx, ctx.finalRouteSegment); } } + + private RouteSegment getRecalculationEnd(final RoutingContext ctx) { + RouteSegment recalculationEnd = null; + boolean runRecalculation = ctx.previouslyCalculatedRoute != null && ctx.previouslyCalculatedRoute.size() > 0 + && ctx.config.recalculateDistance != 0; + if (runRecalculation) { + List rlist = new ArrayList(); + float distanceThreshold = ctx.config.recalculateDistance; + float threshold = 0; + for (RouteSegmentResult rr : ctx.previouslyCalculatedRoute) { + threshold += rr.getDistance(); + if (threshold > distanceThreshold) { + rlist.add(rr); + } + } + runRecalculation = rlist.size() > 0; + if (rlist.size() > 0) { + RouteSegment previous = null; + for (int i = 0; i < rlist.size() - 1; i++) { + RouteSegmentResult rr = rlist.get(i); + RouteSegment segment = new RouteSegment(rr.getObject(), rr.getEndPointIndex()); + if (previous != null) { + previous.setParentRoute(segment); + previous.setParentSegmentEnd(rr.getStartPointIndex()); + } else { + recalculationEnd = segment; + } + previous = segment; + } + } + } + return recalculationEnd; + } private void refreshProgressDistance(RoutingContext ctx) { @@ -316,16 +364,26 @@ public class RoutePlannerFrontEnd { } - private List runNativeRouting(final RoutingContext ctx) throws IOException { + private List runNativeRouting(final RoutingContext ctx, RouteSegment recalculationEnd) 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); + long time = System.currentTimeMillis(); RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx.startX, ctx.startY, ctx.targetX, ctx.targetY, ctx.config, regions, ctx.calculationProgress, ctx.precalculatedRouteDirection, ctx.calculationMode == RouteCalculationMode.BASE); log.info("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds"); ArrayList result = new ArrayList(Arrays.asList(res)); + if(recalculationEnd != null) { + log.info("Native routing use precalculated route"); + RouteSegment current = recalculationEnd; + while(current.getParentRoute() != null) { + RouteSegment pr = current.getParentRoute(); + result.add(new RouteSegmentResult(pr.getRoad(), current.getParentSegmentEnd(), pr.getSegmentStart())); + current = pr; + } + } ctx.routingTime = ctx.calculationProgress.routingCalculatedTime; ctx.visitedSegments = ctx.calculationProgress.visitedSegments; ctx.loadedTiles = ctx.calculationProgress.loadedTiles; @@ -336,7 +394,9 @@ public class RoutePlannerFrontEnd { private List searchRoute(final RoutingContext ctx, List points, PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException { if (points.size() <= 2) { - ctx.previouslyCalculatedRoute = null; + if(!useSmartRouteRecalculation) { + ctx.previouslyCalculatedRoute = null; + } return searchRoute(ctx, points.get(0), points.get(1), routeDirection); } @@ -372,7 +432,9 @@ public class RoutePlannerFrontEnd { for (int i = 0; i < points.size() - 1; i++) { RoutingContext local = new RoutingContext(ctx); if (i == 0) { - //local.previouslyCalculatedRoute = firstPartRecalculatedRoute; + if (useSmartRouteRecalculation) { + local.previouslyCalculatedRoute = firstPartRecalculatedRoute; + } } local.visitor = ctx.visitor; local.calculationProgress = ctx.calculationProgress; diff --git a/OsmAnd-java/src/net/osmand/router/RoutingConfiguration.java b/OsmAnd-java/src/net/osmand/router/RoutingConfiguration.java index 4098427f07..758ec8a302 100644 --- a/OsmAnd-java/src/net/osmand/router/RoutingConfiguration.java +++ b/OsmAnd-java/src/net/osmand/router/RoutingConfiguration.java @@ -47,7 +47,7 @@ public class RoutingConfiguration { public Double initialDirection; // 1.5 Recalculate distance help - public float recalculateDistance = 10000f; + public float recalculateDistance = 20000f; public static class Builder { diff --git a/OsmAnd-java/src/net/osmand/router/RoutingContext.java b/OsmAnd-java/src/net/osmand/router/RoutingContext.java index ca2f4615fc..d5b8474ca6 100644 --- a/OsmAnd-java/src/net/osmand/router/RoutingContext.java +++ b/OsmAnd-java/src/net/osmand/router/RoutingContext.java @@ -207,11 +207,15 @@ public class RoutingContext { } public void initStartAndTargetPoints(RouteSegment start, RouteSegment end) { - targetX = end.road.getPoint31XTile(end.getSegmentStart()); - targetY = end.road.getPoint31YTile(end.getSegmentStart()); + initTargetPoint(end); startX = start.road.getPoint31XTile(start.getSegmentStart()); startY = start.road.getPoint31YTile(start.getSegmentStart()); } + + public void initTargetPoint(RouteSegment end) { + targetX = end.road.getPoint31XTile(end.getSegmentStart()); + targetY = end.road.getPoint31YTile(end.getSegmentStart()); + } public void unloadAllData() { unloadAllData(null); diff --git a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java index b9e4f59896..da28bc9723 100644 --- a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java +++ b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java @@ -199,6 +199,7 @@ public class MapRendererContext implements RendererRegistry.IRendererLoadedEvent private void recreateRasterAndSymbolsProvider() { // Create new map primitiviser + // TODO Victor ask MapPrimitiviser, ObfMapObjectsProvider MapPrimitiviser mapPrimitiviser = new MapPrimitiviser(mapPresentationEnvironment); ObfMapObjectsProvider obfMapObjectsProvider = new ObfMapObjectsProvider(obfsCollection); // Create new map primitives provider diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index c7e81b9181..61a6e599a5 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -623,7 +623,7 @@ public class OsmandSettings { registeredPreferences.put(id, p); return p; } - + public final CommonPreference USE_FAST_RECALCULATION = new BooleanPreference("use_fast_recalculation", true).makeGlobal().cache(); // this value string is synchronized with settings_pref.xml preference name public final CommonPreference USE_INTERNET_TO_DOWNLOAD_TILES = new BooleanPreference("use_internet_to_download_tiles", true).makeGlobal().cache(); diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java index ac9fdc7c5f..600e39a063 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java @@ -89,6 +89,17 @@ public abstract class SettingsBaseActivity extends ActionBarPreferenceActivity return p; } + public CheckBoxPreference createCheckBoxPreference(OsmandPreference b, String title, String summary) { + CheckBoxPreference p = new CheckBoxPreference(this); + p.setTitle(title); + p.setKey(b.getId()); + p.setSummary(summary); + p.setOnPreferenceChangeListener(this); + screenPreferences.put(b.getId(), p); + booleanPreferences.put(b.getId(), b); + return p; + } + public CheckBoxPreference createCheckBoxPreference(OsmandPreference b) { CheckBoxPreference p = new CheckBoxPreference(this); p.setKey(b.getId()); diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index c073324a75..8101cc8491 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -40,6 +40,9 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { cat.addPreference(dbg); cat.addPreference(createCheckBoxPreference(settings.DISABLE_COMPLEX_ROUTING, R.string.disable_complex_routing, R.string.disable_complex_routing_descr)); + + cat.addPreference(createCheckBoxPreference(settings.USE_FAST_RECALCULATION, "Smart route recalculation", "Recalculate only initial part of the route for long trips")); + cat.addPreference(createCheckBoxPreference(settings.USE_MAGNETIC_FIELD_SENSOR_COMPASS, R.string.use_magnetic_sensor, R.string.use_magnetic_sensor_descr)); diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java index 1a51cd706f..9e24267e30 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java @@ -630,6 +630,7 @@ public class RouteProvider { BinaryMapIndexReader[] files = params.ctx.getResourceManager().getRoutingMapFiles(); RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(false); OsmandSettings settings = params.ctx.getSettings(); + router.setUseFastRecalculation(settings.USE_FAST_RECALCULATION.get()); RoutingConfiguration.Builder config = params.ctx.getDefaultRoutingConfig(); GeneralRouter generalRouter = SettingsNavigationActivity.getRouter(config, params.mode); @@ -666,6 +667,9 @@ public class RouteProvider { RouteCalculationMode.COMPLEX); complexCtx.calculationProgress = params.calculationProgress; complexCtx.leftSideNavigation = params.leftSide; + if(params.previousToRecalculate != null && params.onlyStartPointChanged) { + complexCtx.previouslyCalculatedRoute = params.previousToRecalculate.getOriginalRoute(); + } } ctx.leftSideNavigation = params.leftSide; ctx.calculationProgress = params.calculationProgress;