Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvng <viktor.govako@gmail.com>2013-09-19 19:54:33 +0400
committerAlex Zolotarev <alex@maps.me>2015-09-23 02:02:11 +0300
commit8816d36b62f5b99fe0147a97afcf9707a1d8c1dc (patch)
tree2358dc753d79c3c2459d485b9605506a49cb1cd4
parenta6d0e9b2f594ebc27b8e4ba573ee4665154ad208 (diff)
[android] New location filter strategy with accuracy, speed and time delta formula.
-rw-r--r--android/src/com/mapswithme/maps/location/LocationService.java147
-rw-r--r--android/src/com/mapswithme/maps/location/WifiLocation.java19
-rw-r--r--android/src/com/mapswithme/util/LocationUtils.java159
3 files changed, 101 insertions, 224 deletions
diff --git a/android/src/com/mapswithme/maps/location/LocationService.java b/android/src/com/mapswithme/maps/location/LocationService.java
index be980c380e..1300d25709 100644
--- a/android/src/com/mapswithme/maps/location/LocationService.java
+++ b/android/src/com/mapswithme/maps/location/LocationService.java
@@ -5,6 +5,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
@@ -16,12 +17,13 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
+import android.os.SystemClock;
import android.view.Display;
import android.view.Surface;
import com.mapswithme.maps.MWMApplication;
import com.mapswithme.util.ConnectionState;
-import com.mapswithme.util.LocationUtils;
+import com.mapswithme.util.Utils;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.StubLogger;
@@ -30,6 +32,11 @@ public class LocationService implements LocationListener, SensorEventListener, W
{
private Logger mLogger = StubLogger.get();//SimpleLogger.get(this.toString());
+ private static final double DEFAULT_SPEED_MpS = 5;
+ private static final float DISTANCE_TO_RECREATE_MAGNETIC_FIELD_M = 1000;
+ private static final float MIN_SPEED_CALC_DIRECTION_MpS = 1;
+ private static final long LOCATION_EXPIRATION_TIME_MILLIS = 5 * 60 * 1000;
+
/// These constants should correspond to values defined in platform/location.hpp
/// Leave 0-value as no any error.
public static final int ERROR_NOT_SUPPORTED = 1;
@@ -45,8 +52,11 @@ public class LocationService implements LocationListener, SensorEventListener, W
private HashSet<Listener> mObservers = new HashSet<Listener>(10);
- /// Used to filter locations from different providers
+ /// Last accepted location
private Location mLastLocation = null;
+ /// System timestamp for the last location
+ private long mLastLocationTime;
+ /// Current heading if we are moving (-1.0 otherwise)
private double mDrivingHeading = -1.0;
private WifiLocation mWifiScanner = null;
@@ -103,17 +113,55 @@ public class LocationService implements LocationListener, SensorEventListener, W
it.next().onCompassUpdated(time, magneticNorth, trueNorth, accuracy);
}
- /*
- private void printLocation(Location l)
+ private static boolean isSameLocationProvider(String p1, String p2)
{
- final String p = l.getProvider();
- Log.d(TAG, "Lat = " + l.getLatitude() +
- "; Lon = " + l.getLongitude() +
- "; Time = " + l.getTime() +
- "; Acc = " + l.getAccuracy() +
- "; Provider = " + (p != null ? p : ""));
+ if (p1 == null || p2 == null)
+ return false;
+ return p1.equals(p2);
+ }
+
+ @SuppressLint("NewApi")
+ private double getLocationTimeDiffS(Location l)
+ {
+ if (Utils.apiEqualOrGreaterThan(17))
+ return (l.getElapsedRealtimeNanos() - mLastLocation.getElapsedRealtimeNanos()) * 1.0E-9;
+ else
+ {
+ long time = l.getTime();
+ long lastTime = mLastLocation.getTime();
+ if (!isSameLocationProvider(l.getProvider(), mLastLocation.getProvider()))
+ {
+ // Do compare current and previous system times in case when
+ // we have incorrect time settings on a device.
+ time = System.currentTimeMillis();
+ lastTime = mLastLocationTime;
+ }
+
+ return (time - lastTime) * 1.0E-3;
+ }
+ }
+
+ private boolean isLocationBetter(Location l)
+ {
+ if (l == null)
+ return false;
+ if (mLastLocation == null)
+ return true;
+
+ final double s = Math.max(DEFAULT_SPEED_MpS, (l.getSpeed() + mLastLocation.getSpeed()) / 2.0);
+ return (l.getAccuracy() < (mLastLocation.getAccuracy() + s * getLocationTimeDiffS(l)));
+ }
+
+ @SuppressLint("NewApi")
+ private static boolean isNotExpired(Location l, long t)
+ {
+ long timeDiff;
+ if (Utils.apiEqualOrGreaterThan(17))
+ timeDiff = (SystemClock.elapsedRealtimeNanos() - l.getElapsedRealtimeNanos()) / 1000000;
+ else
+ timeDiff = System.currentTimeMillis() - t;
+ return (timeDiff <= LOCATION_EXPIRATION_TIME_MILLIS);
}
- */
public void startUpdate(Listener observer)
{
@@ -146,34 +194,23 @@ public class LocationService implements LocationListener, SensorEventListener, W
registerSensorListeners();
// Choose best location from available
- List<Location> notExpiredLocations = getAllNotExpiredLocations(providers);
- Location lastKnownLocation = null;
+ final Location l = getBestLastLocation(providers);
+ mLogger.d("Last location: ", l);
- if (notExpiredLocations.size() > 0)
+ if (isLocationBetter(l))
{
- final Location newestLocation = LocationUtils.getNewestLocation(notExpiredLocations);
- mLogger.d("Last newest location: ", newestLocation);
- final Location mostAccurateLocation = LocationUtils.getMostAccurateLocation(notExpiredLocations);
- mLogger.d("Last accurate location: ", mostAccurateLocation);
-
- if (LocationUtils.isFirstOneBetterLocation(newestLocation, mostAccurateLocation))
- lastKnownLocation = newestLocation;
- else
- lastKnownLocation = mostAccurateLocation;
+ // get last better location
+ emitLocation(l);
}
-
- if (LocationUtils.isNotExpired(mLastLocation) &&
- LocationUtils.isFirstOneBetterLocation(mLastLocation, lastKnownLocation))
+ else if (mLastLocation != null && isNotExpired(mLastLocation, mLastLocationTime))
{
- lastKnownLocation = mLastLocation;
+ // notify UI about last valid location
+ notifyLocationUpdated(mLastLocation);
}
-
- // Pass last known location only in the end of all registerListener
- // in case, when we want to disconnect in listener.
- if (lastKnownLocation != null)
+ else
{
- LocationUtils.hackLocationTime(lastKnownLocation);
- emitLocation(lastKnownLocation);
+ // forget about old location
+ mLastLocation = null;
}
}
@@ -258,32 +295,26 @@ public class LocationService implements LocationListener, SensorEventListener, W
}
}
- private static final long MAXTIME_CALC_DIRECTIONS = 1000 * 10;
-
- private List<Location> getAllNotExpiredLocations(List<String> providers)
+ private Location getBestLastLocation(List<String> providers)
{
- List<Location> locations = new ArrayList<Location>(providers.size());
+ Location res = null;
for (String pr : providers)
{
final Location l = mLocationManager.getLastKnownLocation(pr);
- if (LocationUtils.isNotExpired(l))
- locations.add(l);
+ if (l != null && isNotExpired(l, l.getTime()))
+ {
+ if (res == null || res.getAccuracy() > l.getAccuracy())
+ res = l;
+ }
}
-
- return locations;
+ return res;
}
- private void calcDirection(Location l, long t)
+ private void calcDirection(Location l)
{
- // Try to calculate user direction if he is moving and
- // we have previous close position.
- if ((l.getSpeed() >= 1.0) && (t - mLastLocation.getTime() <= MAXTIME_CALC_DIRECTIONS))
- {
- if (l.hasBearing())
- mDrivingHeading = bearingToHeading(l.getBearing());
- else if (mLastLocation.distanceTo(l) > 5.0)
- mDrivingHeading = bearingToHeading(mLastLocation.bearingTo(l));
- }
+ // Try to calculate direction if we are moving
+ if (l.getSpeed() >= MIN_SPEED_CALC_DIRECTION_MpS && l.hasBearing())
+ mDrivingHeading = bearingToHeading(l.getBearing());
else
mDrivingHeading = -1.0;
}
@@ -293,12 +324,11 @@ public class LocationService implements LocationListener, SensorEventListener, W
mLogger.d("Location accepted: ", l);
mLastLocation = l;
+ mLastLocationTime = System.currentTimeMillis();
+
notifyLocationUpdated(l);
}
- /// Delta distance when we need to recreate GeomagneticField (to calculate declination).
- private final static float DISTANCE_TO_RECREATE_MAGNETIC_FIELD = 1000.0f;
-
@Override
public void onLocationChanged(Location l)
{
@@ -308,19 +338,16 @@ public class LocationService implements LocationListener, SensorEventListener, W
if (l.getAccuracy() <= 0.0)
return;
- LocationUtils.hackLocationTime(l);
- if (LocationUtils.isFirstOneBetterLocation(l, mLastLocation))
+ if (isLocationBetter(l))
{
- final long timeNow = System.currentTimeMillis();
- if (mLastLocation != null)
- calcDirection(l, timeNow);
+ calcDirection(l);
// Used for more precise compass updates
if (mSensorManager != null)
{
// Recreate magneticField if location has changed significantly
if (mMagneticField == null ||
- (mLastLocation == null || l.distanceTo(mLastLocation) > DISTANCE_TO_RECREATE_MAGNETIC_FIELD))
+ (mLastLocation == null || l.distanceTo(mLastLocation) > DISTANCE_TO_RECREATE_MAGNETIC_FIELD_M))
{
mMagneticField = new GeomagneticField((float)l.getLatitude(), (float)l.getLongitude(),
(float)l.getAltitude(), l.getTime());
diff --git a/android/src/com/mapswithme/maps/location/WifiLocation.java b/android/src/com/mapswithme/maps/location/WifiLocation.java
index 1101c252f2..631eaa3b31 100644
--- a/android/src/com/mapswithme/maps/location/WifiLocation.java
+++ b/android/src/com/mapswithme/maps/location/WifiLocation.java
@@ -10,6 +10,7 @@ import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -19,8 +20,8 @@ import android.location.LocationManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
+import android.os.SystemClock;
-import com.mapswithme.util.LocationUtils;
import com.mapswithme.util.Utils;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.StubLogger;
@@ -32,7 +33,7 @@ public class WifiLocation extends BroadcastReceiver
private static final String MWM_GEOLOCATION_SERVER = "http://geolocation.server/";
/// Limit received WiFi accuracy with 20 meters.
- private static final double MIN_PASSED_ACCURACY = 20;
+ private static final double MIN_PASSED_ACCURACY_M = 20;
public interface Listener
{
@@ -77,6 +78,14 @@ public class WifiLocation extends BroadcastReceiver
mWifi = null;
}
+ @SuppressLint("NewApi")
+ private void setLocationCurrentTime(Location l)
+ {
+ if (Utils.apiEqualOrGreaterThan(17))
+ l.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ l.setTime(System.currentTimeMillis());
+ }
+
@Override
public void onReceive(Context context, Intent intent)
{
@@ -199,7 +208,7 @@ public class WifiLocation extends BroadcastReceiver
Utils.closeStream(wr);
// Get the response
- mLogger.d("Get JSON responce with code = ", conn.getResponseCode());
+ mLogger.d("Get JSON response with code = ", conn.getResponseCode());
rd = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line = null;
String response = "";
@@ -213,10 +222,10 @@ public class WifiLocation extends BroadcastReceiver
final double acc = jLocation.getDouble("accuracy");
mLocation = new Location("wifiscanner");
- mLocation.setAccuracy((float) Math.max(MIN_PASSED_ACCURACY, acc));
+ mLocation.setAccuracy((float) Math.max(MIN_PASSED_ACCURACY_M, acc));
mLocation.setLatitude(lat);
mLocation.setLongitude(lon);
- LocationUtils.setLocationCurrentTime(mLocation);
+ setLocationCurrentTime(mLocation);
return true;
}
diff --git a/android/src/com/mapswithme/util/LocationUtils.java b/android/src/com/mapswithme/util/LocationUtils.java
deleted file mode 100644
index 85ec818bd7..0000000000
--- a/android/src/com/mapswithme/util/LocationUtils.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package com.mapswithme.util;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-
-import android.annotation.SuppressLint;
-import android.location.Location;
-import android.os.SystemClock;
-
-/**
- * Locations utils from {@link http://developer.android.com/guide/topics/location/strategies.html}
- * Partly modified and suited for MWM.
- */
-public class LocationUtils
-{
- private static final int TWO_MINUTES = 2 * 60 * 1000;
- private static final long LOCATION_EXPIRATION_TIME = 5 * 60 * 1000;
-
- @SuppressLint("NewApi")
- public static boolean isNotExpired(Location l)
- {
- if (l != null)
- {
- long timeDiff;
- if (Utils.apiEqualOrGreaterThan(17))
- timeDiff = (SystemClock.elapsedRealtimeNanos() - l.getElapsedRealtimeNanos()) / 1000000;
- else
- timeDiff = System.currentTimeMillis() - l.getTime();
- return (timeDiff <= LOCATION_EXPIRATION_TIME);
- }
- return false;
- }
-
- @SuppressLint("NewApi")
- public static long timeDiffMillis(Location l1, Location l2)
- {
- if (Utils.apiEqualOrGreaterThan(17))
- return (l1.getElapsedRealtimeNanos() - l2.getElapsedRealtimeNanos()) / 1000000;
- else
- return (l1.getTime() - l2.getTime());
- }
-
- @SuppressLint("NewApi")
- public static void setLocationCurrentTime(Location l)
- {
- if (Utils.apiEqualOrGreaterThan(17))
- l.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
- l.setTime(System.currentTimeMillis());
- }
-
- /// Call this function before comparing or after getting location to
- /// avoid troubles with invalid system time.
- @SuppressLint("NewApi")
- public static void hackLocationTime(Location l)
- {
- if (Utils.apiLowerThan(17))
- l.setTime(System.currentTimeMillis());
- }
-
- /** Determines whether one Location reading is better than the current Location fix
- * @param firstLoc The new Location that you want to evaluate
- * @param secondLoc The current Location fix, to which you want to compare the new one
- */
- public static boolean isFirstOneBetterLocation(Location firstLoc, Location secondLoc)
- {
- if (firstLoc == null)
- return false;
- if (secondLoc == null)
- return true;
-
- // Check whether the new location fix is newer or older
- final long timeDelta = timeDiffMillis(firstLoc, secondLoc);
-
- // If it's been more than two minutes since the current location,
- // use the new location because the user has likely moved
- if (timeDelta > TWO_MINUTES)
- {
- // significantly newer
- return true;
- }
- else if (timeDelta < -TWO_MINUTES)
- {
- // significantly older
- return false;
- }
-
- // Check whether the new location fix is more or less accurate
- final float accuracyDelta = firstLoc.getAccuracy() - secondLoc.getAccuracy();
- // Relative difference, not absolute
- final boolean almostAsAccurate = Math.abs(accuracyDelta) <= 0.1*secondLoc.getAccuracy();
-
- // Determine location quality using a combination of timeliness and accuracy
- final boolean isNewer = timeDelta > 0;
- if (accuracyDelta < 0)
- {
- // more accurate and has the same time order
- return true;
- }
- else if (isNewer && almostAsAccurate)
- {
- // newer and has the same accuracy order
- return true;
- }
- else if (isNewer && accuracyDelta <= 200 &&
- isSameProvider(firstLoc.getProvider(), secondLoc.getProvider()))
- {
- // not significantly less accurate and from the same provider
- return true;
- }
-
- return false;
- }
-
- /** Checks whether two providers are the same */
- public static boolean isSameProvider(String provider1, String provider2)
- {
- if (provider1 == null)
- return provider2 == null;
- else
- return provider1.equals(provider2);
- }
-
- public static Location getBestLocation(Collection<Location> locations, Comparator<Location> comparator)
- {
- return Collections.max(locations, comparator);
- }
-
- public static Location getMostAccurateLocation(Collection<Location> locations)
- {
- final Comparator<Location> accuracyComparator = new Comparator<Location>()
- {
- @Override
- public int compare(Location lhs, Location rhs)
- {
- return (int) (1 * Math.signum(rhs.getAccuracy() - lhs.getAccuracy()));
- }
- };
-
- return getBestLocation(locations, accuracyComparator);
- }
-
- public static Location getNewestLocation(Collection<Location> locations)
- {
- final Comparator<Location> newerFirstComparator = new Comparator<Location>()
- {
- @Override
- public int compare(Location lhs, Location rhs)
- {
- return (int) (1 * Math.signum(timeDiffMillis(lhs, rhs)));
- }
- };
-
- return getBestLocation(locations, newerFirstComparator);
- }
-
-
- private LocationUtils() {};
-}