public boolean identifyUTurnIsNeeded(Location currentLocation, float posTolerance) { if (finalLocation == null || currentLocation == null || !route.isCalculated()) { this.makeUturnWhenPossible = false; return makeUturnWhenPossible; } boolean makeUturnWhenPossible = false; if (currentLocation.hasBearing()) { float bearingMotion = currentLocation.getBearing(); Location nextRoutePosition = route.getNextRouteLocation(); float bearingToRoute = currentLocation.bearingTo(nextRoutePosition); double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); // 7. Check if you left the route and an unscheduled U-turn would bring you back (also Issue // 863) // This prompt is an interim advice and does only sound if a new route in forward direction // could not be found in x seconds if (Math.abs(diff) > 135f) { float d = currentLocation.distanceTo(nextRoutePosition); // 60m tolerance to allow for GPS inaccuracy if (d > posTolerance) { // require x sec continuous since first detection if (makeUTwpDetected == 0) { makeUTwpDetected = System.currentTimeMillis(); } else if ((System.currentTimeMillis() - makeUTwpDetected > 10000)) { makeUturnWhenPossible = true; // log.info("bearingMotion is opposite to bearingRoute"); //$NON-NLS-1$ } } } else { makeUTwpDetected = 0; } } this.makeUturnWhenPossible = makeUturnWhenPossible; return makeUturnWhenPossible; }
/** * Wrong movement direction is considered when between current location bearing (determines by 2 * last fixed position or provided) and bearing from currentLocation to next (current) point the * difference is more than 60 degrees */ public boolean checkWrongMovementDirection(Location currentLocation, Location nextRouteLocation) { // measuring without bearing could be really error prone (with last fixed location) // this code has an effect on route recalculation which should be detected without mistakes if (currentLocation.hasBearing() && nextRouteLocation != null) { float bearingMotion = currentLocation.getBearing(); float bearingToRoute = currentLocation.bearingTo(nextRouteLocation); double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); if (Math.abs(diff) > 60f) { // require delay interval since first detection, to avoid false positive // but leave out for now, as late detection is worse than false positive (it may reset voice // router then cause bogus turn and u-turn prompting) // if (wrongMovementDetected == 0) { // wrongMovementDetected = System.currentTimeMillis(); // } else if ((System.currentTimeMillis() - wrongMovementDetected > 500)) { return true; // } } else { // wrongMovementDetected = 0; return false; } } // wrongMovementDetected = 0; return false; }
private boolean updateCurrentRouteStatus(Location currentLocation, float posTolerance) { List<Location> routeNodes = route.getImmutableAllLocations(); int currentRoute = route.currentRoute; // 1. Try to proceed to next point using orthogonal distance (finding minimum orthogonal dist) while (currentRoute + 1 < routeNodes.size()) { double dist = currentLocation.distanceTo(routeNodes.get(currentRoute)); if (currentRoute > 0) { dist = getOrthogonalDistance( currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); } boolean processed = false; // if we are still too far try to proceed many points // if not then look ahead only 3 in order to catch sharp turns boolean longDistance = dist >= 250; int newCurrentRoute = lookAheadFindMinOrthogonalDistance( currentLocation, routeNodes, currentRoute, longDistance ? 15 : 8); double newDist = getOrthogonalDistance( currentLocation, routeNodes.get(newCurrentRoute), routeNodes.get(newCurrentRoute + 1)); if (longDistance) { if (newDist < dist) { if (log.isDebugEnabled()) { log.debug( "Processed by distance : (new) " + newDist + " (old) " + dist); //$NON-NLS-1$//$NON-NLS-2$ } processed = true; } } else if (newDist < dist || newDist < 10) { // newDist < 10 (avoid distance 0 till next turn) if (dist > posTolerance) { processed = true; if (log.isDebugEnabled()) { log.debug( "Processed by distance : " + newDist + " " + dist); // $NON-NLS-1$//$NON-NLS-2$ } } else { // case if you are getting close to the next point after turn // but you have not yet turned (could be checked bearing) if (currentLocation.hasBearing() || lastFixedLocation != null) { float bearingToRoute = currentLocation.bearingTo(routeNodes.get(currentRoute)); float bearingRouteNext = routeNodes.get(newCurrentRoute).bearingTo(routeNodes.get(newCurrentRoute + 1)); float bearingMotion = currentLocation.hasBearing() ? currentLocation.getBearing() : lastFixedLocation.bearingTo(currentLocation); double diff = Math.abs(MapUtils.degreesDiff(bearingMotion, bearingToRoute)); double diffToNext = Math.abs(MapUtils.degreesDiff(bearingMotion, bearingRouteNext)); if (diff > diffToNext) { if (log.isDebugEnabled()) { log.debug("Processed point bearing deltas : " + diff + " " + diffToNext); } processed = true; } } } } if (processed) { // that node already passed route.updateCurrentRoute(newCurrentRoute + 1); currentRoute = newCurrentRoute + 1; } else { break; } } // 2. check if intermediate found if (route.getIntermediatePointsToPass() > 0 && route.getDistanceToNextIntermediate(lastFixedLocation) < POSITION_TOLERANCE * 2) { showMessage(app.getString(R.string.arrived_at_intermediate_point)); route.passIntermediatePoint(); TargetPointsHelper targets = app.getTargetPointsHelper(); List<String> ns = targets.getIntermediatePointNames(); int toDel = targets.getIntermediatePoints().size() - route.getIntermediatePointsToPass(); int currentIndex = toDel - 1; String name = currentIndex < 0 || currentIndex >= ns.size() || ns.get(currentIndex) == null ? "" : ns.get(currentIndex); if (isFollowingMode) { voiceRouter.arrivedIntermediatePoint(name); } while (toDel > 0) { targets.removeWayPoint(false, 0); toDel--; } while (intermediatePoints != null && route.getIntermediatePointsToPass() < intermediatePoints.size()) { intermediatePoints.remove(0); } } // 3. check if destination found Location lastPoint = routeNodes.get(routeNodes.size() - 1); if (currentRoute > routeNodes.size() - 3 && currentLocation.distanceTo(lastPoint) < (((float) settings.getApplicationMode().getArrivalDistance()) * settings.ARRIVAL_DISTANCE_FACTOR.get())) { showMessage(app.getString(R.string.arrived_at_destination)); TargetPointsHelper targets = app.getTargetPointsHelper(); String description = targets.getPointNavigateDescription(); if (isFollowingMode) { voiceRouter.arrivedDestinationPoint(description); } if (OsmandPlugin.onDestinationReached()) { clearCurrentRoute(null, null); setRoutePlanningMode(false); app.runInUIThread( new Runnable() { @Override public void run() { settings.APPLICATION_MODE.set(settings.DEFAULT_APPLICATION_MODE.get()); } }); // targets.clearPointToNavigate(false); return true; } } return false; }
private Location setCurrentLocation( Location currentLocation, boolean returnUpdatedLocation, RouteCalculationResult previousRoute) { Location locationProjection = currentLocation; if (finalLocation == null || currentLocation == null) { makeUturnWhenPossible = false; return locationProjection; } float posTolerance = POSITION_TOLERANCE; if (currentLocation.hasAccuracy()) { posTolerance = POSITION_TOLERANCE / 2 + currentLocation.getAccuracy(); } boolean calculateRoute = false; synchronized (this) { // 0. Route empty or needs to be extended? Then re-calculate route. if (route.isEmpty()) { calculateRoute = true; } else { // 1. Update current route position status according to latest received location boolean finished = updateCurrentRouteStatus(currentLocation, posTolerance); if (finished) { return null; } List<Location> routeNodes = route.getImmutableAllLocations(); int currentRoute = route.currentRoute; // 2. Analyze if we need to recalculate route // >100m off current route (sideways) if (currentRoute > 0) { double dist = getOrthogonalDistance( currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); if (dist > 1.7 * posTolerance) { log.info("Recalculate route, because correlation : " + dist); // $NON-NLS-1$ calculateRoute = true; } if (dist > 350) { voiceRouter.announceOffRoute(dist); } } // 3. Identify wrong movement direction Location next = route.getNextRouteLocation(); boolean wrongMovementDirection = checkWrongMovementDirection(currentLocation, next); if (wrongMovementDirection && currentLocation.distanceTo(routeNodes.get(currentRoute)) > 2 * posTolerance) { log.info( "Recalculate route, because wrong movement direction: " + currentLocation.distanceTo(routeNodes.get(currentRoute))); // $NON-NLS-1$ calculateRoute = true; } // 4. Identify if UTurn is needed boolean uTurnIsNeeded = identifyUTurnIsNeeded(currentLocation, posTolerance); // 5. Update Voice router if (isFollowingMode) { // don't update in route planing mode announceGpxWaypoints(currentLocation); boolean inRecalc = calculateRoute || isRouteBeingCalculated(); if (!inRecalc && !uTurnIsNeeded && !wrongMovementDirection) { voiceRouter.updateStatus(currentLocation, false); } else if (uTurnIsNeeded) { voiceRouter.makeUTStatus(); } } // calculate projection of current location if (currentRoute > 0) { locationProjection = new Location(currentLocation); Location nextLocation = routeNodes.get(currentRoute); LatLon project = getProject( currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); locationProjection.setLatitude(project.getLatitude()); locationProjection.setLongitude(project.getLongitude()); // we need to update bearing too float bearingTo = locationProjection.bearingTo(nextLocation); locationProjection.setBearing(bearingTo); } } lastFixedLocation = currentLocation; lastProjection = locationProjection; } if (calculateRoute) { recalculateRouteInBackground( false, currentLocation, finalLocation, intermediatePoints, currentGPXRoute, previousRoute.isCalculated() ? previousRoute : null); } else if (currentRunningJob != null && currentRunningJob instanceof RouteRecalculationThread) { RouteRecalculationThread thread = (RouteRecalculationThread) currentRunningJob; thread.stopCalculation(); } double projectDist = mode != null && mode.hasFastSpeed() ? posTolerance : posTolerance / 2; if (returnUpdatedLocation && locationProjection != null && currentLocation.distanceTo(locationProjection) < projectDist) { return locationProjection; } else { return currentLocation; } }