We have an app which captures user's location using Google Play Service Location API for each transactions like Geotag land, upon placing an order etc.
After capturing these locations from mobile we'll sync it to server and show it in web dashboard.
We have noticed in few cases transaction captured within few seconds have location results of varied distance.
Example:
Location of the user will be in
Mandsaur, Madhya Pradesh, India, Asia
(Latitude - 24.057291, Longitude - 75.0970672, Captured date - 2017-01-04 09:19:48).
But subsequent transactions will have location of
Paris which is 6772 km away
(Latitude - 48.8581074, Longitude - 2.3525187, Captured date - 2017-01-04 09:20:01)
sometimes its fetch wrong stated of india like user is from gujrat then location fetch of Bihar, Maharastra, Kuwait(out of india) its really headache for indian developer
As this is happening without user interference and no mock location apps installed in user's device.
Can anyone explain why this happens and how could we avoid these scenarios?
NOTE:
These transaction locations are captured usually at open fields with GPS turned on and set to High Accuracy mode
The primary reason for Google Maps giving wrong location details is due to bad or no internet connection. If the internet on your android phone is active and running you will be able to get the exact location details.
For Samsung smartphones running Android 10 OS, the location information may appear inaccurate if the GPS signal is obstructed, location settings is disabled, or if you are not using the best location method.
The Geolocation API returns a location and accuracy radius based on information about cell towers and WiFi nodes that the mobile client can detect. This document describes the protocol used to send this data to the server and to return a response to the client. Communication is done over HTTPS using POST.
The Location that you get from the API will have accuracy in meters. You should also check how old the location is.
https://developer.android.com/reference/android/location/Location.html#getAccuracy()
https://developer.android.com/reference/android/location/Location.html#getTime()
People generally discard the location if accuracy is greater than 50 or 100m.
Why this happens?
It takes some time for the GPS of the device to find satellites and get the signal. Also, the API will try to determine your location based on your network. It kinds of jump between network and GPS, until GPS provides some accurate data.
How to avoid this?
In your location listener, check for accuracy and wait till you have a better accuracy.
The official Android developer site has a page dedicated to Location Strategies for successful Android apps. You can read more there, so without going into much detail, the official documentation states the following...
You might expect that the most recent location fix is the most accurate. However, because the accuracy of a location fix varies, the most recent fix is not always the best. You should include logic for choosing location fixes based on several criteria. The criteria also varies depending on the use-cases of the application and field testing.
Here are a few steps you can take to validate the accuracy of a location fix:
- Check if the location retrieved is significantly newer than the previous estimate.
- Check if the accuracy claimed by the location is better or worse than the previous estimate.
- Check which provider the new location is from and determine if you trust it more.
You might also want to look into implementing a basic Kalman Filter in your app to maintain and update an estimate of the user's location. Good luck.
For google location fused api you can use below code
package com.hydrometcloud.location;
import android.app.Activity;
import android.content.Context;
import android.content.IntentSender;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;
public class GoogleLocation implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener,
ResultCallback<LocationSettingsResult> {
private LocationSettingsRequest mLocationSettingsRequest;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private String TAG = "GoogleLocation";
private Context context;
private long UPDATE_INTERVAL = 10 * 1000; /* 10 secs */
private long FASTEST_INTERVAL = 2000; /* 2 sec */
private GoogleLocationCallback googleLocationCallback;
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
public static final int REQUEST_CHECK_SETTINGS = 0x1;
public GoogleLocation(Context context) {
this.context = context;
setUpLocation();
}
public void setGoogleLocationCallback(GoogleLocationCallback googleLocationCallback) {
this.googleLocationCallback = googleLocationCallback;
}
private void setUpLocation() {
mGoogleApiClient = new GoogleApiClient.Builder(context.getApplicationContext())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
// Create the LocationRequest object
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(UPDATE_INTERVAL) // 10 seconds, in milliseconds
.setFastestInterval(FASTEST_INTERVAL); // 1 second, in milliseconds
locationEnable();
}
public void googleClientConnect() {
mGoogleApiClient.connect();
}
public void googleClientDisConnect() {
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.unregisterConnectionCallbacks(this);
mGoogleApiClient.unregisterConnectionFailedListener(this);
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
mGoogleApiClient = null;
}
}
private void locationEnable() {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
mLocationSettingsRequest = builder.build();
checkLocationSettings();
}
private void checkLocationSettings() {
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(
mGoogleApiClient,
mLocationSettingsRequest
);
result.setResultCallback(this);
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
updateLocation();
} else {
handleNewLocation(location);
}
}
private void handleNewLocation(Location location) {
double currentLatitude = location.getLatitude();
double currentLongitude = location.getLongitude();
Log.e(TAG, "---currentLatitude--" + currentLatitude);
Log.e(TAG, "---currentLongitude--" + currentLongitude);
if (googleLocationCallback != null) {
googleLocationCallback.updateLocationListner(currentLatitude, currentLongitude);
}
}
public void updateLocation() {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult((Activity) context, CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
Log.e(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
}
}
@Override
public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
Log.e(TAG, "All location settings are satisfied.");
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
Log.e(TAG, "Location settings are not satisfied. Show the user a dialog to" +
"upgrade location settings ");
try {
// Show the dialog by calling startResolutionForResult(), and check the result
// in onActivityResult().
status.startResolutionForResult((Activity) context, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "PendingIntent unable to execute request.");
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
Log.e(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog " +
"not created.");
break;
}
}
@Override
public void onLocationChanged(Location location) {
handleNewLocation(location);
}
public interface GoogleLocationCallback {
void updateLocationListner(double latitude, double longitude);
}
}
Now you implement callback GoogleLocation.GoogleLocationCallback on which activity or fragment you fetch location and after that write below code on which activity or fragment you get location
googleLocation = new GoogleLocation(this);
googleLocation.setGoogleLocationCallback(this);
Best way to solve this, filtering the results. Probability of a correct location returned from a Fuse service is 67% which means you can get wrong location in 1/3.
You should compare previous location( or more than one to be sure, keep them in a list) with the current one before getting the location. If there is a noticible difference don't use the latest one.
Note: don't save user locations to a file or folder, it's against privacy policy, just compare with lastKnownLocation or locations received in current session.
Another idea is if you are using GoogleApiClient
, i'm currently using, there is a new class for fused location FusedLocationProvider, you can check that out.
Also as far i understood, they put 4 location retrieval limit per hour for devices with Android 8.0 (API level 26) and later. You can check this document for new behaviour.
I had same problem on Pune, India. The device used to test was Swipe tablet,
Solution : i keep last latitude and longitude and update time, when next latitude and longitude update, calculate distance between that two locations, if distance is higher than 100 KM, then it will not count .
Method i used to calculate distance
public static double calculateDistance(double lat1,double lon1,double lat2,double lon2) {
double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1))
* Math.sin(deg2rad(lat2))
+ Math.cos(deg2rad(lat1))
* Math.cos(deg2rad(lat2))
* Math.cos(deg2rad(theta));
dist = Math.acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
return (dist);
}
Why it happens
On android device there are 2 options to get location
GPS (LocationManager.GPS_PROVIDER): is slow compare to internet, but it will accurate than internet, in remote areas GPS connection will slower and sometimes hard to connect.
Internet (LocationManager.NETWORK_PROVIDER) : is much faster than GPS, but it less accurate than GPS
If GPS unreachable and you need current location then LocationManager will give you available data, which is fetched from internet. (in this case you can also check the location provider.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With