Update Routing helper

This commit is contained in:
Victor Shcherb 2013-01-06 20:23:57 +01:00
parent 6a0b93ea63
commit 7e59d34941
9 changed files with 119 additions and 1517 deletions

View file

@ -130,7 +130,7 @@ public class OsmandApplication extends Application implements ClientContext {
public void onTerminate() {
super.onTerminate();
if (routingHelper != null) {
routingHelper.getVoiceRouter().onApplicationTerminate(getApplicationContext());
routingHelper.getVoiceRouter().onApplicationTerminate(this);
}
if (bidforfix != null) {
bidforfix.onDestroy();
@ -619,4 +619,14 @@ public class OsmandApplication extends Application implements ClientContext {
return sqliteAPI;
}
@Override
public void runInUIThread(Runnable run) {
uiHandler.post(run);
}
@Override
public void runInUIThread(Runnable run, long delay) {
uiHandler.postDelayed(run, delay);
}
}

View file

@ -1,194 +0,0 @@
package net.osmand.plus;
import java.util.ArrayList;
import java.util.List;
import net.osmand.Location;
import net.osmand.access.AccessibleToast;
import net.osmand.osm.LatLon;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.routing.RoutingHelper;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.Toast;
public class TargetPointsHelper {
private List<LatLon> intermediatePoints = new ArrayList<LatLon>();
private List<String> intermediatePointNames = new ArrayList<String>();
private LatLon pointToNavigate = null;
private OsmandSettings settings;
private RoutingHelper routingHelper;
public TargetPointsHelper(OsmandSettings settings, RoutingHelper routingHelper){
this.settings = settings;
this.routingHelper = routingHelper;
readFromSettings(settings);
}
private void readFromSettings(OsmandSettings settings) {
pointToNavigate = settings.getPointToNavigate();
intermediatePoints.clear();
intermediatePointNames.clear();
intermediatePoints.addAll(settings.getIntermediatePoints());
intermediatePointNames.addAll(settings.getIntermediatePointDescriptions(intermediatePoints.size()));
}
public LatLon getPointToNavigate() {
return pointToNavigate;
}
public String getPointNavigateDescription(){
return settings.getPointNavigateDescription();
}
public List<String> getIntermediatePointNames() {
return intermediatePointNames;
}
public List<LatLon> getIntermediatePoints() {
return intermediatePoints;
}
public List<LatLon> getIntermediatePointsWithTarget() {
List<LatLon> res = new ArrayList<LatLon>();
res.addAll(intermediatePoints);
if(pointToNavigate != null) {
res.add(pointToNavigate);
}
return res;
}
public List<String> getIntermediatePointNamesWithTarget() {
List<String> res = new ArrayList<String>();
res.addAll(intermediatePointNames);
if(pointToNavigate != null) {
res.add(getPointNavigateDescription());
}
return res;
}
public LatLon getFirstIntermediatePoint(){
if(intermediatePoints.size() > 0) {
return intermediatePoints.get(0);
}
return null;
}
public void removeWayPoint(MapActivity map, boolean updateRoute, int index){
if(index < 0){
settings.clearPointToNavigate();
pointToNavigate = null;
int sz = intermediatePoints.size();
if(sz > 0) {
settings.deleteIntermediatePoint(sz- 1);
pointToNavigate = intermediatePoints.remove(sz - 1);
settings.setPointToNavigate(pointToNavigate.getLatitude(), pointToNavigate.getLongitude(),
intermediatePointNames.remove(sz - 1));
}
} else {
settings.deleteIntermediatePoint(index);
intermediatePoints.remove(index);
intermediatePointNames.remove(index);
}
updateRouteAndReferesh(map, updateRoute);
}
private void updateRouteAndReferesh(MapActivity map, boolean updateRoute) {
if(updateRoute && ( routingHelper.isRouteBeingCalculated() || routingHelper.isRouteCalculated() ||
routingHelper.isFollowingMode())) {
Location lastKnownLocation = map == null ? routingHelper.getLastProjection() : map.getLastKnownLocation();
routingHelper.setFinalAndCurrentLocation(settings.getPointToNavigate(),
settings.getIntermediatePoints(), lastKnownLocation, routingHelper.getCurrentGPXRoute());
}
if(map != null) {
map.getMapView().refreshMap();
}
}
public void clearPointToNavigate(MapActivity map, boolean updateRoute) {
settings.clearPointToNavigate();
settings.clearIntermediatePoints();
readFromSettings(settings);
updateRouteAndReferesh(map, updateRoute);
}
public void reorderAllTargetPoints(MapActivity map, List<LatLon> point,
List<String> names, boolean updateRoute){
settings.clearPointToNavigate();
if (point.size() > 0) {
settings.saveIntermediatePoints(point.subList(0, point.size() - 1), names.subList(0, point.size() - 1));
LatLon p = point.get(point.size() - 1);
String nm = names.get(point.size() - 1);
settings.setPointToNavigate(p.getLatitude(), p.getLongitude(), nm);
} else {
settings.clearIntermediatePoints();
}
readFromSettings(settings);
updateRouteAndReferesh(map, updateRoute);
}
public void navigateToPoint(MapActivity map, LatLon point, boolean updateRoute, int intermediate){
if(point != null){
if(intermediate < 0) {
settings.setPointToNavigate(point.getLatitude(), point.getLongitude(), null);
} else {
settings.insertIntermediatePoint(point.getLatitude(), point.getLongitude(), null,
intermediate, false);
}
} else {
settings.clearPointToNavigate();
settings.clearIntermediatePoints();
}
readFromSettings(settings);
updateRouteAndReferesh(map, updateRoute);
}
public boolean checkPointToNavigate(Context ctx ){
if(pointToNavigate == null){
AccessibleToast.makeText(ctx, R.string.mark_final_location_first, Toast.LENGTH_LONG).show();
return false;
}
return true;
}
public void navigatePointDialogAndLaunchMap(final Context ctx, final double lat, final double lon, final String name){
if(pointToNavigate != null) {
Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle(R.string.new_destination_point_dialog);
builder.setItems(new String[] {
ctx.getString(R.string.replace_destination_point),
ctx.getString(R.string.add_as_first_destination_point),
ctx.getString(R.string.add_as_last_destination_point)
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(which == 0) {
settings.setPointToNavigate(lat, lon, true, name);
} else if(which == 2) {
int sz = intermediatePoints.size();
LatLon pt = pointToNavigate;
settings.insertIntermediatePoint(pt.getLatitude(), pt.getLongitude(),
settings.getPointNavigateDescription(), sz, true);
settings.setPointToNavigate(lat, lon, true, name);
} else {
settings.insertIntermediatePoint(lat, lon, name, 0, true);
}
readFromSettings(settings);
MapActivity.launchMapActivityMoveToTop(ctx);
}
});
builder.show();
} else {
settings.setPointToNavigate(lat, lon, true, name);
readFromSettings(settings);
MapActivity.launchMapActivityMoveToTop(ctx);
}
}
}

View file

@ -24,6 +24,7 @@ import net.osmand.plus.BusyIndicator;
import net.osmand.plus.FavouritesDbHelper;
import net.osmand.plus.GPXUtilities;
import net.osmand.plus.GPXUtilities.GPXFile;
import net.osmand.plus.MapScreen;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.OsmandSettings;
@ -35,6 +36,7 @@ import net.osmand.plus.Version;
import net.osmand.plus.activities.search.SearchActivity;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback;
import net.osmand.plus.views.AnimateDraggingMapThread;
import net.osmand.plus.views.OsmandMapLayer;
import net.osmand.plus.views.OsmandMapTileView;
@ -89,7 +91,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class MapActivity extends AccessibleActivity implements IMapLocationListener, SensorEventListener {
public class MapActivity extends AccessibleActivity implements IMapLocationListener, SensorEventListener, MapScreen {
// stupid error but anyway hero 2.1 : always lost gps signal (temporarily unavailable) for timeout = 2000
private static final int GPS_TIMEOUT_REQUEST = 0;
@ -251,14 +253,27 @@ public class MapActivity extends AccessibleActivity implements IMapLocationListe
Gravity.CENTER_HORIZONTAL | Gravity.TOP);
DisplayMetrics dm = getResources().getDisplayMetrics();
params.topMargin = (int) (60 * dm.density);
ProgressBar pb = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
final ProgressBar pb = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
pb.setIndeterminate(false);
pb.setMax(100);
pb.setLayoutParams(params);
pb.setVisibility(View.GONE);
parent.addView(pb);
routingHelper.setProgressBar(pb, new Handler());
routingHelper.setProgressBar(new RouteCalculationProgressCallback() {
@Override
public void updateProgress(int progress) {
pb.setVisibility(View.VISIBLE);
pb.setProgress(progress);
}
@Override
public void finish() {
pb.setVisibility(View.GONE);
}
});
}
@ -1460,4 +1475,10 @@ public class MapActivity extends AccessibleActivity implements IMapLocationListe
OsmandPlugin.onMapActivityResult(requestCode, resultCode, data);
}
@Override
public void refreshMap() {
getMapView().refreshMap();
}
}

View file

@ -50,10 +50,12 @@ import net.osmand.plus.views.OsmandMapTileView;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Application;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.Intent;
@ -579,7 +581,7 @@ public class MapActivityActions implements DialogProvider {
if (to != null) {
targets.navigateToPoint(mapActivity, to, false, -1);
}
if (!targets.checkPointToNavigate(mapActivity)) {
if (!targets.checkPointToNavigate(getMyApplication())) {
return;
}
Location from = fromOrCurrent;
@ -614,7 +616,7 @@ public class MapActivityActions implements DialogProvider {
if(to != null) {
targets.navigateToPoint(mapActivity, to, false, -1);
}
if (!targets.checkPointToNavigate(mapActivity)) {
if (!targets.checkPointToNavigate(getMyApplication())) {
return;
}
boolean msg = true;
@ -875,7 +877,7 @@ public class MapActivityActions implements DialogProvider {
getDirections(null, new LatLon(latitude, longitude), true);
}
} else if (standardId == R.string.context_menu_item_show_route) {
if (targets.checkPointToNavigate(mapActivity)) {
if (targets.checkPointToNavigate(getMyApplication())) {
Location loc = new Location("map");
loc.setLatitude(latitude);
loc.setLongitude(longitude);
@ -1250,7 +1252,7 @@ public class MapActivityActions implements DialogProvider {
if(onShow != null) {
onShow.onClick(v);
}
app.getTargetPointsHelper().navigatePointDialogAndLaunchMap(activity,
navigatePointDialogAndLaunchMap(activity,
location.getLatitude(), location.getLongitude(), name);
qa.dismiss();
}
@ -1273,4 +1275,42 @@ public class MapActivityActions implements DialogProvider {
// qa.addActionItem(directionsTo);
}
public static void navigatePointDialogAndLaunchMap(final Activity act, final double lat, final double lon, final String name){
OsmandApplication ctx = (OsmandApplication) act.getApplication();
final TargetPointsHelper targetPointsHelper = ctx.getTargetPointsHelper();
final OsmandSettings settings = ctx.getSettings();
if(targetPointsHelper.getPointToNavigate() != null) {
Builder builder = new AlertDialog.Builder(act);
builder.setTitle(R.string.new_destination_point_dialog);
builder.setItems(new String[] {
act.getString(R.string.replace_destination_point),
act.getString(R.string.add_as_first_destination_point),
act.getString(R.string.add_as_last_destination_point)
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(which == 0) {
settings.setPointToNavigate(lat, lon, true, name);
} else if(which == 2) {
int sz = targetPointsHelper.getIntermediatePoints().size();
LatLon pt = targetPointsHelper.getPointToNavigate();
settings.insertIntermediatePoint(pt.getLatitude(), pt.getLongitude(),
settings.getPointNavigateDescription(), sz, true);
settings.setPointToNavigate(lat, lon, true, name);
} else {
settings.insertIntermediatePoint(lat, lon, name, 0, true);
}
targetPointsHelper.updatePointsFromSettings();
MapActivity.launchMapActivityMoveToTop(act);
}
});
builder.show();
} else {
settings.setPointToNavigate(lat, lon, true, name);
targetPointsHelper.updatePointsFromSettings();
MapActivity.launchMapActivityMoveToTop(act);
}
}
}

View file

@ -256,12 +256,11 @@ public class NavigatePointActivity extends Activity implements SearchActivityChi
double lon = convert(((TextView) findViewById(R.id.LongitudeEdit)).getText().toString());
if(navigate){
if(activity != null) {
OsmandApplication app = (OsmandApplication) activity.getApplication();
app.getTargetPointsHelper().navigatePointDialogAndLaunchMap(activity, lat, lon, getString(R.string.point_on_map, lat, lon));
MapActivityActions.navigatePointDialogAndLaunchMap(activity, lat, lon, getString(R.string.point_on_map, lat, lon));
} else {
OsmandApplication app = (OsmandApplication) getApplication();
app.getTargetPointsHelper().navigatePointDialogAndLaunchMap(this, lat, lon, getString(R.string.point_on_map, lat, lon));
MapActivityActions.navigatePointDialogAndLaunchMap(this, lat, lon, getString(R.string.point_on_map, lat, lon));
}
if(dlg != null){
dlg.dismiss();

View file

@ -10,6 +10,7 @@ import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.RegionAddressRepository;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.MapActivityActions;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
@ -236,7 +237,7 @@ public class SearchAddressActivity extends Activity {
} else {
if (navigateTo) {
OsmandApplication app = (OsmandApplication) getApplication();
app.getTargetPointsHelper().navigatePointDialogAndLaunchMap(SearchAddressActivity.this, searchPoint.getLatitude(), searchPoint.getLongitude(), historyName);
MapActivityActions.navigatePointDialogAndLaunchMap(SearchAddressActivity.this, searchPoint.getLatitude(), searchPoint.getLongitude(), historyName);
} else {
osmandSettings.setMapLocationToShow(searchPoint.getLatitude(), searchPoint.getLongitude(), zoom, historyName);
MapActivity.launchMapActivityMoveToTop(SearchAddressActivity.this);

View file

@ -8,9 +8,11 @@ import java.util.List;
import net.osmand.NativeLibrary;
import net.osmand.ResultMatcher;
import net.osmand.data.Amenity;
import net.osmand.plus.NavigationService;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.PoiFilter;
import net.osmand.plus.ResourceManager;
import net.osmand.plus.TargetPointsHelper;
import net.osmand.plus.access.AccessibilityMode;
import net.osmand.plus.render.NativeOsmandLibrary;
@ -18,6 +20,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
@ -128,5 +131,37 @@ public class InternalOsmAndAPIImpl implements InternalOsmAndAPI {
return Build.MODEL;
}
@Override
public TargetPointsHelper getTargetPointsHelper() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isNavigationServiceStarted() {
return app.getNavigationService() != null;
}
@Override
public boolean isNavigationServiceStartedForNavigation() {
return app.getNavigationService() != null && app.getNavigationService().startedForNavigation();
}
@Override
public void startNavigationService(boolean forNavigation) {
Intent serviceIntent = new Intent(app, NavigationService.class);
if(forNavigation) {
serviceIntent.putExtra(NavigationService.NAVIGATION_START_SERVICE_PARAM, true);
}
app.startService(serviceIntent);
}
@Override
public void stopNavigationService() {
Intent serviceIntent = new Intent(app, NavigationService.class);
app.stopService(serviceIntent);
}
}

View file

@ -1,744 +0,0 @@
package net.osmand.plus.routing;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.osmand.Location;
import net.osmand.LogUtil;
import net.osmand.access.AccessibleToast;
import net.osmand.osm.LatLon;
import net.osmand.osm.MapUtils;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.GPXUtilities.GPXFile;
import net.osmand.plus.NavigationService;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.OsmandSettings.MetricsConstants;
import net.osmand.plus.R;
import net.osmand.plus.TargetPointsHelper;
import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
import net.osmand.plus.routing.RouteProvider.RouteService;
import net.osmand.plus.voice.CommandPlayer;
import net.osmand.router.RouteCalculationProgress;
import net.osmand.router.RouteSegmentResult;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
public class RoutingHelper {
private static final org.apache.commons.logging.Log log = LogUtil.getLog(RoutingHelper.class);
public static interface IRouteInformationListener {
public void newRouteIsCalculated(boolean newRoute);
public void routeWasCancelled();
}
private final float POSITION_TOLERANCE = 60;
private List<IRouteInformationListener> listeners = new ArrayList<IRouteInformationListener>();
private OsmandApplication app;
private boolean isFollowingMode = false;
private GPXRouteParams currentGPXRoute = null;
private RouteCalculationResult route = new RouteCalculationResult("");
private LatLon finalLocation;
private List<LatLon> intermediatePoints;
private Location lastProjection;
private Location lastFixedLocation;
private RouteRecalculationThread currentRunningJob;
private long lastTimeEvaluatedRoute = 0;
private int evalWaitInterval = 3000;
private ApplicationMode mode;
private OsmandSettings settings;
private RouteProvider provider = new RouteProvider();
private VoiceRouter voiceRouter;
private Handler uiHandler;
private boolean makeUturnWhenPossible = false;
private long makeUTwpDetected = 0;
//private long wrongMovementDetected = 0;
private ProgressBar progress;
private Handler progressHandler;
public boolean makeUturnWhenPossible() {
return makeUturnWhenPossible;
}
public RoutingHelper(OsmandApplication context, CommandPlayer player){
this.app = context;
settings = context.getSettings();
voiceRouter = new VoiceRouter(this, player);
uiHandler = new Handler();
}
public boolean isFollowingMode() {
return isFollowingMode;
}
public void setFollowingMode(boolean follow) {
isFollowingMode = follow;
Intent serviceIntent = new Intent(app, NavigationService.class);
if(follow) {
if(app.getNavigationService() == null) {
serviceIntent.putExtra(NavigationService.NAVIGATION_START_SERVICE_PARAM, true);
app.startService(serviceIntent);
}
} else {
if(app.getNavigationService() != null && app.getNavigationService().startedForNavigation()) {
app.stopService(serviceIntent);
}
}
}
public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List<LatLon> intermediatePoints, Location currentLocation, GPXRouteParams gpxRoute){
clearCurrentRoute(finalLocation, intermediatePoints);
currentGPXRoute = gpxRoute;
// to update route
setCurrentLocation(currentLocation, false);
}
public synchronized void clearCurrentRoute(LatLon newFinalLocation, List<LatLon> newIntermediatePoints) {
route = new RouteCalculationResult("");
makeUturnWhenPossible = false;
evalWaitInterval = 3000;
uiHandler.post(new Runnable() {
@Override
public void run() {
for (IRouteInformationListener l : listeners) {
l.routeWasCancelled();
}
}
});
this.finalLocation = newFinalLocation;
this.intermediatePoints = newIntermediatePoints;
if(currentRunningJob != null) {
currentRunningJob.stopCalculation();
}
if (newFinalLocation == null) {
settings.FOLLOW_THE_ROUTE.set(false);
settings.FOLLOW_THE_GPX_ROUTE.set(null);
// clear last fixed location
this.lastProjection = null;
setFollowingMode(false);
}
}
public GPXRouteParams getCurrentGPXRoute() {
return currentGPXRoute;
}
public List<Location> getCurrentRoute() {
return currentGPXRoute == null || currentGPXRoute.points.isEmpty() ? route.getImmutableLocations() : Collections
.unmodifiableList(currentGPXRoute.points);
}
public void setAppMode(ApplicationMode mode){
this.mode = mode;
voiceRouter.updateAppMode();
}
public ApplicationMode getAppMode() {
return mode;
}
public LatLon getFinalLocation() {
return finalLocation;
}
public List<LatLon> getIntermediatePoints() {
return intermediatePoints;
}
public boolean isRouteCalculated(){
return route.isCalculated();
}
public VoiceRouter getVoiceRouter() {
return voiceRouter;
}
public Location getLastProjection(){
return lastProjection;
}
public void addListener(IRouteInformationListener l){
listeners.add(l);
}
public boolean removeListener(IRouteInformationListener l){
return listeners.remove(l);
}
public Location setCurrentLocation(Location currentLocation, boolean returnUpdatedLocation ) {
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.getImmutableLocations();
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;
}
}
// 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
boolean inRecalc = calculateRoute || isRouteBeingCalculated();
if (!inRecalc && !uTurnIsNeeded && !wrongMovementDirection) {
voiceRouter.updateStatus(currentLocation);
} 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(currentLocation, finalLocation, intermediatePoints, currentGPXRoute, route.isCalculated() ? route
: null);
}
double projectDist = mode == ApplicationMode.CAR ? posTolerance : posTolerance / 2;
if(returnUpdatedLocation && locationProjection != null && currentLocation.distanceTo(locationProjection) < projectDist) {
return locationProjection;
} else {
return currentLocation;
}
}
private static double getOrthogonalDistance(Location loc, Location from, Location to) {
return MapUtils.getOrthogonalDistance(loc.getLatitude(),
loc.getLongitude(), from.getLatitude(), from.getLongitude(),
to.getLatitude(), to.getLongitude());
}
private static LatLon getProject(Location loc, Location from, Location to) {
return MapUtils.getProjection(loc.getLatitude(),
loc.getLongitude(), from.getLatitude(), from.getLongitude(),
to.getLatitude(), to.getLongitude());
}
private static int lookAheadFindMinOrthogonalDistance(Location currentLocation, List<Location> routeNodes, int currentRoute, int iterations) {
double newDist;
double dist = Double.POSITIVE_INFINITY;
int index = currentRoute;
while (iterations > 0 && currentRoute + 1 < routeNodes.size()) {
newDist = getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute), routeNodes.get(currentRoute + 1));
if (newDist < dist) {
index = currentRoute;
dist = newDist;
}
currentRoute++;
iterations--;
}
return index;
}
private boolean updateCurrentRouteStatus(Location currentLocation, float posTolerance) {
List<Location> routeNodes = route.getImmutableLocations();
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));
voiceRouter.arrivedIntermediatePoint();
route.passIntermediatePoint();
TargetPointsHelper targets = app.getTargetPointsHelper();
int toDel = targets.getIntermediatePoints().size() - route.getIntermediatePointsToPass();
while(toDel > 0) {
targets.removeWayPoint(null, 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) < POSITION_TOLERANCE * 1.5) {
showMessage(app.getString(R.string.arrived_at_destination));
voiceRouter.arrivedDestinationPoint();
clearCurrentRoute(null, null);
TargetPointsHelper targets = app.getTargetPointsHelper();
targets.clearPointToNavigate(null, false);
return true;
}
return false;
}
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 synchronized void setNewRoute(RouteCalculationResult res, Location start){
final boolean newRoute = !this.route.isCalculated();
route = res;
if (isFollowingMode) {
if(lastFixedLocation != null) {
start = lastFixedLocation;
}
// try remove false route-recalculated prompts by checking direction to second route node
boolean wrongMovementDirection = false;
List<Location> routeNodes = res.getImmutableLocations();
if (routeNodes != null && !routeNodes.isEmpty()) {
int newCurrentRoute = lookAheadFindMinOrthogonalDistance(start, routeNodes, res.currentRoute, 15);
if (newCurrentRoute + 1 < routeNodes.size()) {
// This check is valid for Online/GPX services (offline routing is aware of route direction)
wrongMovementDirection = checkWrongMovementDirection(start, routeNodes.get(newCurrentRoute + 1));
// set/reset evalWaitInterval only if new route is in forward direction
if (!wrongMovementDirection) {
evalWaitInterval = 3000;
} else {
evalWaitInterval = evalWaitInterval * 3 / 2;
evalWaitInterval = Math.min(evalWaitInterval, 120000);
}
}
}
// trigger voice prompt only if new route is in forward direction
// If route is in wrong direction after one more setLocation it will be recalculated
if (!wrongMovementDirection || newRoute) {
voiceRouter.newRouteIsCalculated(newRoute);
}
}
uiHandler.post(new Runnable() {
@Override
public void run() {
for (IRouteInformationListener l : listeners) {
l.newRouteIsCalculated(newRoute);
}
}
});
}
public synchronized int getLeftDistance(){
return route.getDistanceToFinish(lastFixedLocation);
}
public synchronized int getLeftDistanceNextIntermediate() {
return route.getDistanceToNextIntermediate(lastFixedLocation);
}
public synchronized int getLeftTime() {
return route.getLeftTime(lastFixedLocation);
}
public String getGeneralRouteInformation(){
int dist = getLeftDistance();
int hours = getLeftTime() / (60 * 60);
int minutes = (getLeftTime() / 60) % 60;
return app.getString(R.string.route_general_information, OsmAndFormatter.getFormattedDistance(dist, app),
hours, minutes);
}
public Location getLocationFromRouteDirection(RouteDirectionInfo i){
return route.getLocationFromRouteDirection(i);
}
public synchronized NextDirectionInfo getNextRouteDirectionInfo(NextDirectionInfo info, boolean toSpeak){
NextDirectionInfo i = route.getNextRouteDirectionInfo(info, lastProjection, toSpeak);
if(i != null) {
i.imminent = voiceRouter.calculateImminent(i.distanceTo, lastProjection);
}
return i;
}
public synchronized float getCurrentMaxSpeed() {
return route.getCurrentMaxSpeed();
}
public synchronized AlarmInfo getMostImportantAlarm(MetricsConstants mc, boolean showCameras){
float mxspeed = route.getCurrentMaxSpeed();
AlarmInfo speedAlarm = null;
if(mxspeed != 0 && lastProjection != null && lastProjection.hasSpeed()) {
float delta = 5f/3.6f;
if(lastProjection.getSpeed() > mxspeed + delta) {
int speed;
if(mc == MetricsConstants.KILOMETERS_AND_METERS) {
speed = Math.round(mxspeed * 3.6f);
} else {
speed = Math.round(mxspeed * 3.6f / 1.6f);
}
speedAlarm = AlarmInfo.createSpeedLimit(speed);
}
}
return route.getMostImportantAlarm(lastProjection, speedAlarm, showCameras);
}
public String formatStreetName(String name, String ref) {
if(name != null && name.length() > 0){
if(ref != null && ref.length() > 0) {
name = ref + " " + name;
}
return name;
} else {
return ref;
}
}
public synchronized String getCurrentName(){
NextDirectionInfo n = getNextRouteDirectionInfo(new NextDirectionInfo(), false);
if((n.imminent == 0 || n.imminent == 1) && (n.directionInfo != null)) {
String nm = n.directionInfo.getStreetName();
String rf = n.directionInfo.getRef();
return formatStreetName(nm, rf);
}
RouteSegmentResult rs = route.getCurrentSegmentResult();
if(rs != null) {
String nm = rs.getObject().getName();
String rf = rs.getObject().getRef();
return formatStreetName(nm, rf);
}
return null;
}
public synchronized NextDirectionInfo getNextRouteDirectionInfoAfter(NextDirectionInfo previous, NextDirectionInfo to, boolean toSpeak){
NextDirectionInfo i = route.getNextRouteDirectionInfoAfter(previous, to, toSpeak);
if(i != null) {
i.imminent = voiceRouter.calculateImminent(i.distanceTo, null);
}
return i;
}
public List<RouteDirectionInfo> getRouteDirections(){
return route.getRouteDirections();
}
private class RouteRecalculationThread extends Thread {
private final RouteCalculationParams params;
public RouteRecalculationThread(String name, RouteCalculationParams params) {
super(name);
this.params = params;
if(params.calculationProgress == null) {
params.calculationProgress = new RouteCalculationProgress();
}
}
public void stopCalculation(){
params.calculationProgress.isCancelled = true;
}
@Override
public void run() {
RouteCalculationResult res = provider.calculateRouteImpl(params);
if (params.calculationProgress.isCancelled) {
currentRunningJob = null;
return;
}
synchronized (RoutingHelper.this) {
if (res.isCalculated()) {
setNewRoute(res, params.start);
} else {
evalWaitInterval = evalWaitInterval * 3 / 2;
evalWaitInterval = Math.min(evalWaitInterval, 120000);
}
currentRunningJob = null;
}
if (res.isCalculated()) {
showMessage(app.getString(R.string.new_route_calculated_dist)
+ ": " + OsmAndFormatter.getFormattedDistance(res.getWholeDistance(), app)); //$NON-NLS-1$
} else if (params.type != RouteService.OSMAND && !settings.isInternetConnectionAvailable()) {
showMessage(app.getString(R.string.error_calculating_route)
+ ":\n" + app.getString(R.string.internet_connection_required_for_online_route), Toast.LENGTH_LONG); //$NON-NLS-1$
} else {
if (res.getErrorMessage() != null) {
showMessage(app.getString(R.string.error_calculating_route) + ":\n" + res.getErrorMessage(), Toast.LENGTH_LONG); //$NON-NLS-1$
} else {
showMessage(app.getString(R.string.empty_route_calculated), Toast.LENGTH_LONG);
}
}
lastTimeEvaluatedRoute = System.currentTimeMillis();
}
}
private void recalculateRouteInBackground(final Location start, final LatLon end, final List<LatLon> intermediates, final GPXRouteParams gpxRoute, final RouteCalculationResult previousRoute){
if (start == null || end == null) {
return;
}
if(currentRunningJob == null){
// do not evaluate very often
if (System.currentTimeMillis() - lastTimeEvaluatedRoute > evalWaitInterval) {
RouteCalculationParams params = new RouteCalculationParams();
params.start = start;
params.end = end;
params.intermediates = intermediates;
params.gpxRoute = gpxRoute;
params.previousToRecalculate = previousRoute;
params.leftSide = settings.LEFT_SIDE_NAVIGATION.get();
params.optimal = settings.OPTIMAL_ROUTE_MODE.getModeValue(mode);
params.fast = settings.FAST_ROUTE_MODE.getModeValue(mode);
params.type = settings.ROUTER_SERVICE.getModeValue(mode);
params.mode = mode;
params.ctx = app;
if(previousRoute == null && params.type == RouteService.OSMAND) {
params.calculationProgress = new RouteCalculationProgress();
updateProgress(params.calculationProgress);
}
synchronized (this) {
currentRunningJob = new RouteRecalculationThread("Calculating route", params); //$NON-NLS-1$
currentRunningJob.start();
}
}
}
}
private void updateProgress(final RouteCalculationProgress calculationProgress) {
if(progress != null) {
progressHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (isRouteBeingCalculated()) {
progress.setVisibility(View.VISIBLE);
float p = calculationProgress.distanceFromBegin + calculationProgress.distanceFromEnd;
float all = calculationProgress.totalEstimatedDistance * 1.5f;
if (all > 0) {
int t = (int) Math.min(p * p / (all * all) * 100f, 99);
progress.setProgress(t);
}
updateProgress(calculationProgress);
} else {
progress.setVisibility(View.GONE);
}
}
}, 300);
}
}
public boolean isRouteBeingCalculated(){
return currentRunningJob != null;
}
private void showMessage(final String msg, final int length) {
uiHandler.post(new Runnable() {
@Override
public void run() {
AccessibleToast.makeText(app, msg, length).show();
}
});
}
private void showMessage(final String msg){
showMessage(msg, Toast.LENGTH_SHORT);
}
// NEVER returns null
public RouteCalculationResult getRoute() {
return route;
}
protected Context getContext() {
return app;
}
public GPXFile generateGPXFileWithRoute(){
return provider.createOsmandRouterGPX(route);
}
public void setProgressBar(ProgressBar pb, Handler handler) {
progress = pb;
progressHandler = handler;
}
}

View file

@ -1,566 +0,0 @@
package net.osmand.plus.routing;
import net.osmand.Location;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo;
import net.osmand.plus.voice.AbstractPrologCommandPlayer;
import net.osmand.plus.voice.CommandBuilder;
import net.osmand.plus.voice.CommandPlayer;
import net.osmand.router.TurnType;
import android.content.Context;
public class VoiceRouter {
private final int STATUS_UTWP_TOLD = -1;
private final int STATUS_UNKNOWN = 0;
private final int STATUS_LONG_PREPARE = 1;
private final int STATUS_PREPARE = 2;
private final int STATUS_TURN_IN = 3;
private final int STATUS_TURN = 4;
private final int STATUS_TOLD = 5;
private final RoutingHelper router;
private boolean mute = false;
private CommandPlayer player;
private int currentStatus = STATUS_UNKNOWN;
private float playGoAheadDist = 0;
// private long lastTimeRouteRecalcAnnounced = 0;
// default speed to have comfortable announcements (if actual speed is higher than it would be problem)
// Speed in m/s
protected float DEFAULT_SPEED = 12;
protected float TURN_DEFAULT_SPEED = 5;
protected int PREPARE_LONG_DISTANCE = 0;
protected int PREPARE_LONG_DISTANCE_END = 0;
protected int PREPARE_DISTANCE = 0;
protected int PREPARE_DISTANCE_END = 0;
protected int TURN_IN_DISTANCE = 0;
protected int TURN_IN_DISTANCE_END = 0;
protected int TURN_DISTANCE = 0;
protected VoiceCommandPending pendingCommand = null;
private RouteDirectionInfo nextRouteDirection;
public VoiceRouter(RoutingHelper router, CommandPlayer player) {
this.router = router;
this.player = player;
updateAppMode();
}
public void setPlayer(CommandPlayer player) {
this.player = player;
if(pendingCommand != null && player != null){
CommandBuilder newCommand = getNewCommandPlayerToPlay();
if (newCommand != null) {
pendingCommand.play(newCommand);
}
pendingCommand = null;
}
}
public CommandPlayer getPlayer() {
return player;
}
public void setMute(boolean mute) {
this.mute = mute;
}
public boolean isMute() {
return mute;
}
protected CommandBuilder getNewCommandPlayerToPlay(){
if(player == null || mute){
return null;
}
return player.newCommandBuilder();
}
public void updateAppMode(){
// turn prompt starts either at distance, or if actual-lead-time(currentSpeed) < maximum-lead-time
// lead time criterion only for TURN_IN and TURN
PREPARE_LONG_DISTANCE = 3500; // (105 sec) - 120 km/h
PREPARE_LONG_DISTANCE_END = 3000; // (90 sec) - 120 km/h
if(router.getAppMode() == ApplicationMode.PEDESTRIAN){
// prepare distance is not needed for pedestrian
PREPARE_DISTANCE = 200; //(100 sec)
PREPARE_DISTANCE_END = 150 + 100; //( 75 sec) + not play
TURN_IN_DISTANCE = 100; // 50 sec
TURN_IN_DISTANCE_END = 70; // 35 sec
TURN_DISTANCE = 25; // 12 sec
TURN_DEFAULT_SPEED = DEFAULT_SPEED = 2f; // 7,2 km/h
} else if(router.getAppMode() == ApplicationMode.BICYCLE){
PREPARE_DISTANCE = 500; //(100 sec)
PREPARE_DISTANCE_END = 350; //( 70 sec)
TURN_IN_DISTANCE = 225; // 45 sec
TURN_IN_DISTANCE_END = 80; // 16 sec
TURN_DISTANCE = 45; // 9 sec
TURN_DEFAULT_SPEED = DEFAULT_SPEED = 5; // 18 km/h
} else {
PREPARE_DISTANCE = 1500; //(125 sec)
PREPARE_DISTANCE_END = 1200;//(100 sec)
TURN_IN_DISTANCE = 390; // 30 sec
TURN_IN_DISTANCE_END = 182; // 14 sec
TURN_DISTANCE = 50; // 7 sec
TURN_DEFAULT_SPEED = 7f; // 25 km/h
DEFAULT_SPEED = 13; // 48 km/h
}
}
protected boolean isDistanceLess(float currentSpeed, double dist, double etalon){
if(dist < etalon || ((dist / currentSpeed) < (etalon / DEFAULT_SPEED))){
return true;
}
return false;
}
protected boolean isDistanceLess(float currentSpeed, double dist, double etalon, double defSpeed){
if(dist < etalon || ((dist / currentSpeed) < (etalon / defSpeed))){
return true;
}
return false;
}
public int calculateImminent(float dist, Location loc){
float speed = DEFAULT_SPEED;
if(loc != null && loc.hasSpeed()) {
speed = loc.getSpeed();
}
if (isDistanceLess(speed, dist, TURN_IN_DISTANCE_END)) {
return 0;
} else if ( dist <= PREPARE_DISTANCE) {
return 1;
} else if (dist <= PREPARE_LONG_DISTANCE) {
return 2;
} else {
return -1;
}
}
private void nextStatusAfter(int previousStatus){
//STATUS_UNKNOWN=0 -> STATUS_LONG_PREPARE=1 -> STATUS_PREPARE=2 -> STATUS_TURN_IN=3 -> STATUS_TURN=4 -> STATUS_TOLD=5
if(previousStatus != STATUS_TOLD){
this.currentStatus = previousStatus + 1;
} else {
this.currentStatus = previousStatus;
}
}
private boolean statusNotPassed(int statusToCheck){
return currentStatus <= statusToCheck;
}
protected void makeUTStatus() {
// Mechanism via STATUS_UTWP_TOLD: Until turn in the right direction, or route is re-calculated in forward direction
if (currentStatus != STATUS_UTWP_TOLD) {
if (playMakeUTwp()) {
currentStatus = STATUS_UTWP_TOLD;
playGoAheadDist = 0;
}
}
}
/**
* Updates status of voice guidance
* @param currentLocation
*/
protected void updateStatus(Location currentLocation) {
// Directly after turn: goAhead (dist), unless:
// < PREPARE_LONG_DISTANCE (3000m): playPrepareTurn
// < PREPARE_DISTANCE (1500m): playPrepareTurn
// < TURN_IN_DISTANCE (300m or 25sec): playMakeTurnIn
// < TURN_DISTANCE (60m or 5sec): playMakeTurn
float speed = DEFAULT_SPEED;
if (currentLocation != null && currentLocation.hasSpeed()) {
speed = Math.max(currentLocation.getSpeed(), speed);
}
NextDirectionInfo nextInfo = router.getNextRouteDirectionInfo(new NextDirectionInfo(), true);
// after last turn say:
if (nextInfo == null || nextInfo.directionInfo == null || nextInfo.directionInfo.distance == 0) {
// if(currentStatus <= STATUS_UNKNOWN && currentDirection > 0){ This caused this prompt to be suppressed when coming back from a
if (currentStatus <= STATUS_UNKNOWN) {
if (playGoAheadToDestination()) {
currentStatus = STATUS_TOLD;
playGoAheadDist = 0;
}
}
return;
}
if(nextInfo.intermediatePoint){
if (currentStatus <= STATUS_UNKNOWN) {
if (playGoAheadToIntermediate()) {
currentStatus = STATUS_TOLD;
playGoAheadDist = 0;
}
}
return;
}
int dist = nextInfo.distanceTo;
RouteDirectionInfo next = nextInfo.directionInfo;
// if routing is changed update status to unknown
if (next != nextRouteDirection) {
nextRouteDirection = next;
currentStatus = STATUS_UNKNOWN;
playGoAheadDist = 0;
}
if (dist == 0 || currentStatus == STATUS_TOLD) {
// nothing said possibly that's wrong case we should say before that
// however it should be checked manually ?
return;
}
// say how much to go if there is next turn is a bit far
if (currentStatus == STATUS_UNKNOWN) {
if (!isDistanceLess(speed, dist, TURN_IN_DISTANCE * 1.3)) {
playGoAheadDist = dist - 80;
}
// say long distance message only for long distances > 10 km
// if (dist >= PREPARE_LONG_DISTANCE && !isDistanceLess(speed, dist, PREPARE_LONG_DISTANCE)) {
if (dist > PREPARE_LONG_DISTANCE + 300) {
nextStatusAfter(STATUS_UNKNOWN);
} else if (dist > PREPARE_DISTANCE + 300) {
// say prepare message if it is far enough and don't say preare long distance
nextStatusAfter(STATUS_LONG_PREPARE);
} else {
// don't say even prepare message
nextStatusAfter(STATUS_PREPARE);
}
}
NextDirectionInfo nextNextInfo = router.getNextRouteDirectionInfoAfter(nextInfo, new NextDirectionInfo(), true);
if (statusNotPassed(STATUS_TURN) && isDistanceLess(speed, dist, TURN_DISTANCE, TURN_DEFAULT_SPEED)) {
if (next.distance < TURN_IN_DISTANCE_END && nextNextInfo != null) {
playMakeTurn(next, nextNextInfo.directionInfo);
} else {
playMakeTurn(next, null);
}
nextStatusAfter(STATUS_TURN);
} else if (statusNotPassed(STATUS_TURN_IN) && isDistanceLess(speed, dist, TURN_IN_DISTANCE)) {
if (dist >= TURN_IN_DISTANCE_END) {
if ((isDistanceLess(speed, next.distance, TURN_DISTANCE) || next.distance < TURN_IN_DISTANCE_END) &&
nextNextInfo != null) {
playMakeTurnIn(next, dist, nextNextInfo.directionInfo);
} else {
playMakeTurnIn(next, dist, null);
}
}
nextStatusAfter(STATUS_TURN_IN);
// } else if (statusNotPassed(STATUS_PREPARE) && isDistanceLess(speed, dist, PREPARE_DISTANCE)) {
} else if (statusNotPassed(STATUS_PREPARE) && (dist <= PREPARE_DISTANCE)) {
if (dist >= PREPARE_DISTANCE_END) {
if(next.getTurnType().keepLeft() || next.getTurnType().keepRight()){
// do not play prepare for keep left/right
} else {
playPrepareTurn(next, dist);
}
}
nextStatusAfter(STATUS_PREPARE);
// } else if (statusNotPassed(STATUS_LONG_PREPARE) && isDistanceLess(speed, dist, PREPARE_LONG_DISTANCE)){
} else if (statusNotPassed(STATUS_LONG_PREPARE) && (dist <= PREPARE_LONG_DISTANCE)) {
if (dist >= PREPARE_LONG_DISTANCE_END) {
playPrepareTurn(next, dist);
}
nextStatusAfter(STATUS_LONG_PREPARE);
} else if (statusNotPassed(STATUS_UNKNOWN)) {
// strange how we get here but
nextStatusAfter(STATUS_UNKNOWN);
} else if (statusNotPassed(STATUS_TURN_IN) && dist < playGoAheadDist) {
playGoAheadDist = 0;
playGoAhead(dist);
}
}
public void announceCurrentDirection(Location currentLocation) {
NextDirectionInfo nextInfo = router.getNextRouteDirectionInfo(new NextDirectionInfo(), true);
if(nextInfo == null) {
playGoAheadToDestination();
return;
}
NextDirectionInfo nextNextInfo = router.getNextRouteDirectionInfoAfter(nextInfo, new NextDirectionInfo(), false);
float speed = DEFAULT_SPEED;
RouteDirectionInfo next = nextInfo.directionInfo;
int dist = nextInfo.distanceTo;
if(currentLocation != null && currentLocation.hasSpeed()){
speed = Math.max(currentLocation.getSpeed(), speed);
}
switch (currentStatus) {
case STATUS_UTWP_TOLD:
playMakeUTwp();
break;
case STATUS_UNKNOWN:
if (nextRouteDirection != null && ((next == null) || (next.distance == 0))) {
playGoAheadToDestination();
} else {
playGoAhead(dist);
}
break;
case STATUS_TOLD:
if (nextRouteDirection != null) {
playGoAheadToDestination();
}
break;
case STATUS_TURN:
if(next.distance < TURN_IN_DISTANCE_END && nextNextInfo != null) {
playMakeTurn(next, nextNextInfo.directionInfo);
} else {
playMakeTurn(next, null);
}
break;
case STATUS_TURN_IN:
if((isDistanceLess(speed, next.distance, TURN_DISTANCE) || next.distance < TURN_IN_DISTANCE_END) &&
nextNextInfo != null) {
playMakeTurnIn(next, dist, nextNextInfo.directionInfo);
} else {
playMakeTurnIn(next, dist, null);
}
break;
case STATUS_PREPARE:
playPrepareTurn(next, dist);
break;
case STATUS_LONG_PREPARE:
playPrepareTurn(next, dist);
break;
default:
break;
}
}
private boolean playGoAheadToDestination() {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
play.goAhead(router.getLeftDistance()).andArriveAtDestination().play();
return true;
}
return false;
}
private boolean playGoAheadToIntermediate() {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
play.goAhead(router.getLeftDistanceNextIntermediate()).andArriveAtIntermediatePoint().play();
return true;
}
return false;
}
private boolean playMakeUTwp() {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
play.makeUTwp().play();
return true;
}
return false;
}
private void playGoAhead(int dist) {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
play.goAhead(dist).play();
}
}
private void playPrepareTurn(RouteDirectionInfo next, int dist) {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
String tParam = getTurnType(next.getTurnType());
if(tParam != null){
play.prepareTurn(tParam, dist).play();
} else if(next.getTurnType().isRoundAbout()){
play.prepareRoundAbout(dist).play();
} else if(next.getTurnType().getValue().equals(TurnType.TU) || next.getTurnType().getValue().equals(TurnType.TRU)){
play.prepareMakeUT(dist).play();
}
}
}
private void playMakeTurnIn(RouteDirectionInfo next, int dist, RouteDirectionInfo pronounceNextNext) {
CommandBuilder play = getNewCommandPlayerToPlay();
if (play != null) {
String tParam = getTurnType(next.getTurnType());
boolean isPlay = true;
if (tParam != null) {
play.turn(tParam, dist);
} else if (next.getTurnType().isRoundAbout()) {
play.roundAbout(dist, next.getTurnType().getTurnAngle(), next.getTurnType().getExitOut());
} else if (next.getTurnType().getValue().equals(TurnType.TU) || next.getTurnType().getValue().equals(TurnType.TRU)) {
play.makeUT(dist);
} else {
isPlay = false;
}
// small preparation to next after next
if (pronounceNextNext != null) {
TurnType t = pronounceNextNext.getTurnType();
isPlay = true;
if (next.getTurnType().getValue().equals(TurnType.C) &&
!TurnType.C.equals(t.getValue())) {
play.goAhead(dist);
}
if (TurnType.TL.equals(t.getValue()) || TurnType.TSHL.equals(t.getValue()) || TurnType.TSLL.equals(t.getValue())
|| TurnType.TU.equals(t.getValue()) || TurnType.KL.equals(t.getValue())) {
play.then().bearLeft();
} else if (TurnType.TR.equals(t.getValue()) || TurnType.TSHR.equals(t.getValue()) || TurnType.TSLR.equals(t.getValue())
|| TurnType.KR.equals(t.getValue())) {
play.then().bearRight();
}
}
if(isPlay){
play.play();
}
}
}
private void playMakeTurn(RouteDirectionInfo next, RouteDirectionInfo nextNext) {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
String tParam = getTurnType(next.getTurnType());
boolean isplay = true;
if(tParam != null){
play.turn(tParam);
} else if(next.getTurnType().isRoundAbout()){
play.roundAbout(next.getTurnType().getTurnAngle(), next.getTurnType().getExitOut());
} else if(next.getTurnType().getValue().equals(TurnType.TU) || next.getTurnType().getValue().equals(TurnType.TRU)){
play.makeUT();
// do not say it
// } else if(next.getTurnType().getValue().equals(TurnType.C)){
// play.goAhead();
} else {
isplay = false;
}
// add turn after next
if (nextNext != null) {
String t2Param = getTurnType(nextNext.getTurnType());
if (t2Param != null) {
if(isplay) { play.then(); }
play.turn(t2Param, next.distance);
} else if (nextNext.getTurnType().isRoundAbout()) {
if(isplay) { play.then(); }
play.roundAbout(next.distance, nextNext.getTurnType().getTurnAngle(), nextNext.getTurnType().getExitOut());
} else if (nextNext.getTurnType().getValue().equals(TurnType.TU)) {
if(isplay) { play.then(); }
play.makeUT(next.distance);
}
isplay = true;
}
if(isplay){
play.play();
}
}
}
private String getTurnType(TurnType t){
if(TurnType.TL.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_LEFT;
} else if(TurnType.TSHL.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_LEFT_SH;
} else if(TurnType.TSLL.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_LEFT_SL;
} else if(TurnType.TR.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_RIGHT;
} else if(TurnType.TSHR.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_RIGHT_SH;
} else if(TurnType.TSLR.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_RIGHT_SL;
} else if(TurnType.KL.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_LEFT_KEEP;
} else if(TurnType.KR.equals(t.getValue())){
return AbstractPrologCommandPlayer.A_RIGHT_KEEP;
}
return null;
}
public void gpsLocationLost(){
CommandBuilder play = getNewCommandPlayerToPlay();
if (play != null) {
play.gpsLocationLost().play();
}
}
public void newRouteIsCalculated(boolean newRoute) {
CommandBuilder play = getNewCommandPlayerToPlay();
if (play != null) {
if (!newRoute) {
// suppress "route recalculated" prompt for GPX-routing, it makes no sense
// suppress "route recalculated" prompt for 60sec (this workaround now outdated after more intelligent route recalculation and directional voice prompt suppression)
// if (router.getCurrentGPXRoute() == null && (System.currentTimeMillis() - lastTimeRouteRecalcAnnounced > 60000)) {
if (router.getCurrentGPXRoute() == null) {
play.routeRecalculated(router.getLeftDistance()).play();
currentStatus = STATUS_UNKNOWN;
// lastTimeRouteRecalcAnnounced = System.currentTimeMillis();
}
} else {
play.newRouteCalculated(router.getLeftDistance()).play();
currentStatus = STATUS_UNKNOWN;
}
} else if (player == null) {
pendingCommand = new VoiceCommandPending(!newRoute ? VoiceCommandPending.ROUTE_RECALCULATED
: VoiceCommandPending.ROUTE_CALCULATED, this);
currentStatus = STATUS_UNKNOWN;
}
nextRouteDirection = null;
}
public void arrivedDestinationPoint() {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
play.arrivedAtDestination().play();
}
}
public void arrivedIntermediatePoint() {
CommandBuilder play = getNewCommandPlayerToPlay();
if(play != null){
play.arrivedAtIntermediatePoint().play();
}
}
public void onApplicationTerminate(Context ctx) {
if (player != null) {
player.clear();
}
}
/**
* Command to wait until voice player is initialized
*/
private class VoiceCommandPending {
public static final int ROUTE_CALCULATED = 1;
public static final int ROUTE_RECALCULATED = 2;
protected final int type;
private final VoiceRouter voiceRouter;
public VoiceCommandPending(int type, VoiceRouter voiceRouter){
this.type = type;
this.voiceRouter = voiceRouter;
}
public void play(CommandBuilder newCommand) {
int left = voiceRouter.router.getLeftDistance();
if (left > 0) {
if (type == ROUTE_CALCULATED) {
newCommand.newRouteCalculated(left).play();
} else if (type == ROUTE_RECALCULATED) {
newCommand.routeRecalculated(left).play();
}
}
}
}
}