Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Play Service location API returns wrong location sometimes

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

like image 242
Vinay Kashyap T S Avatar asked Feb 13 '17 06:02

Vinay Kashyap T S


People also ask

Why does Google keep getting my location wrong?

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.

Why is my location on my Android wrong?

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.

How does Google location API work?

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.


5 Answers

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.

like image 197
Froyo Avatar answered Oct 11 '22 10:10

Froyo


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.

like image 43
v1bri Avatar answered Oct 11 '22 11:10

v1bri


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);
like image 39
Darshan Mistry Avatar answered Oct 11 '22 10:10

Darshan Mistry


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.

like image 41
Thracian Avatar answered Oct 11 '22 11:10

Thracian


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
  • Internet

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.)

like image 44
Anu Martin Avatar answered Oct 11 '22 10:10

Anu Martin