diff --git a/OsmAnd/res/menu/map_menu.xml b/OsmAnd/res/menu/map_menu.xml
index 898a6ede73..a5fe35f859 100644
--- a/OsmAnd/res/menu/map_menu.xml
+++ b/OsmAnd/res/menu/map_menu.xml
@@ -10,6 +10,7 @@
+
diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml
index 19f0ffbace..2fd3cd2441 100644
--- a/OsmAnd/res/values/strings.xml
+++ b/OsmAnd/res/values/strings.xml
@@ -1,5 +1,8 @@
+ Animate off
+ Animate on
+
Upload GPX files to OSM community. They will be used to improve maps.
%1$d of %2$d item(s) successfully uploaded.
Contribute to OSM...
diff --git a/OsmAnd/src/net/osmand/LatLonUtils.java b/OsmAnd/src/net/osmand/LatLonUtils.java
new file mode 100644
index 0000000000..91153e9d8a
--- /dev/null
+++ b/OsmAnd/src/net/osmand/LatLonUtils.java
@@ -0,0 +1,34 @@
+package net.osmand;
+
+import android.location.Location;
+
+public class LatLonUtils {
+
+ public static Location middleLocation(Location start, Location end,
+ float meters) {
+ double lat1 = toRad(start.getLatitude());
+ double lon1 = toRad(start.getLongitude());
+ double R = 6371; // radius of earth in km
+ double d = meters / 1000; // in km
+ float brng = (float) (toRad(start.bearingTo(end)));
+ double lat2 = Math.asin(Math.sin(lat1) * Math.cos(d / R)
+ + Math.cos(lat1) * Math.sin(d / R) * Math.cos(brng));
+ double lon2 = lon1
+ + Math.atan2(Math.sin(brng) * Math.sin(d / R) * Math.cos(lat1),
+ Math.cos(d / R) - Math.sin(lat1) * Math.sin(lat2));
+ Location nl = new Location(start);
+ nl.setLatitude(toDegree(lat2));
+ nl.setLongitude(toDegree(lon2));
+ nl.setBearing(brng);
+ return nl;
+ }
+
+ private static double toDegree(double radians) {
+ return radians * 180 / Math.PI;
+ }
+
+ private static double toRad(double degree) {
+ return degree * Math.PI / 180;
+ }
+
+}
diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java
index a27c48828c..20ca65753e 100644
--- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java
+++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java
@@ -28,12 +28,12 @@ import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.plus.views.PointLocationLayer;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
-import android.app.AlertDialog.Builder;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.DialogInterface;
@@ -58,6 +58,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings.Secure;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -65,8 +66,8 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.view.Window;
import android.view.View.OnClickListener;
+import android.view.Window;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -121,6 +122,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
// Store previous map rotation settings for rotate button
private Integer previousMapRotate = null;
+ private RouteAnimation routeAnimation = new RouteAnimation();
+
private boolean isMapLinkedToLocation = false;
private ProgressDialog startProgressDialog;
@@ -436,6 +439,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
protected void onDestroy() {
super.onDestroy();
savingTrackHelper.close();
+ routeAnimation.close();
if(mNotificationManager != null){
mNotificationManager.cancel(APP_NOTIFICATION_ID);
}
@@ -948,6 +952,15 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
muteMenu.setVisible(false);
}
}
+ MenuItem animateMenu = menu.findItem(R.id.map_animate_route);
+ if (animateMenu != null) {
+ animateMenu.setTitle(routeAnimation.isRouteAnimating() ? R.string.animate_route_off
+ : R.string.animate_route);
+ animateMenu.setVisible("1".equals(Secure.getString(
+ getContentResolver(), Secure.ALLOW_MOCK_LOCATION))
+ && settings.getPointToNavigate() != null
+ && routingHelper.isRouteCalculated());
+ }
return val;
}
@@ -1003,6 +1016,10 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
case R.id.map_show_point_options:
contextMenuPoint(mapView.getLatitude(), mapView.getLongitude());
return true;
+ case R.id.map_animate_route:
+ //animate moving on route
+ routeAnimation.startStopRouteAnimation(routingHelper, this);
+ return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -1104,9 +1121,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
@Override
public void onClick(DialogInterface dialog, int which) {
ApplicationMode mode = getAppMode(buttons);
- Location location = new Location("map"); //$NON-NLS-1$
- location.setLatitude(lat);
- location.setLongitude(lon);
+ Location location = getLocationToStartFrom(lat, lon);
routingHelper.setAppMode(mode);
settings.FOLLOW_TO_THE_ROUTE.set(false);
routingHelper.setFollowingMode(false);
diff --git a/OsmAnd/src/net/osmand/plus/activities/RouteAnimation.java b/OsmAnd/src/net/osmand/plus/activities/RouteAnimation.java
new file mode 100644
index 0000000000..17dc8467a5
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/activities/RouteAnimation.java
@@ -0,0 +1,65 @@
+package net.osmand.plus.activities;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.osmand.LatLonUtils;
+import android.location.Location;
+
+public class RouteAnimation {
+
+ private Thread routeAnimation;
+
+ public boolean isRouteAnimating() {
+ return routeAnimation != null;
+ }
+
+ public void startStopRouteAnimation(final RoutingHelper routingHelper,
+ final MapActivity ma) {
+ if (!isRouteAnimating()) {
+ routeAnimation = new Thread() {
+ public void run() {
+ final List directions = new ArrayList(
+ routingHelper.getCurrentRoute());
+ Location current = null;
+ float meters = 20.0f;
+ while (!directions.isEmpty() && routeAnimation != null) {
+ if (current == null) {
+ current = new Location(directions.remove(0));
+ } else {
+ if (current.distanceTo(directions.get(0)) > meters) {
+ current = LatLonUtils.middleLocation(current,
+ directions.get(0), meters);
+ } else {
+ current = new Location(directions.remove(0));
+ }
+ }
+ current.setSpeed(meters);
+ current.setTime(System.currentTimeMillis());
+ ma.setLocation(current);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ RouteAnimation.this.stop();
+ };
+ };
+ routeAnimation.start();
+ } else {
+ // stop the animation
+ stop();
+ }
+ }
+
+ private void stop() {
+ routeAnimation = null;
+ }
+
+ public void close() {
+ if (isRouteAnimating()) {
+ stop();
+ }
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/activities/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/activities/RoutingHelper.java
index 9d70a5059f..66dc487219 100644
--- a/OsmAnd/src/net/osmand/plus/activities/RoutingHelper.java
+++ b/OsmAnd/src/net/osmand/plus/activities/RoutingHelper.java
@@ -117,13 +117,18 @@ public class RoutingHelper {
this.lastFixedLocation = null;
this.isFollowingMode = false;
}
-
}
public List getCurrentGPXRoute() {
return currentGPXRoute;
}
+ public List getCurrentRoute() {
+ return currentGPXRoute == null || currentGPXRoute.isEmpty() ? Collections
+ .unmodifiableList(routeNodes) : Collections
+ .unmodifiableList(currentGPXRoute);
+ }
+
public void setAppMode(ApplicationMode mode){
this.mode = mode;
voiceRouter.updateAppMode();