How to improve the accuracy of position detected using FusedLocationProviderClient?

35 Views Asked by At

I'm working on an app that is used by salespersons visiting stores on a few different cities. While visiting the stores, every salesperson have to log his/her coordinates.

Here's my code:

public class StartVisitActivity extends AppCompatActivity {

    ActivityStartVisitBinding binding;
    private FusedLocationProviderClient mFusedLocationClient;
    private LocationRequest locationRequest;
    private LocationCallback locationCallback;
    private Location currentLocation;


    @SuppressLint("MissingPermission")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityStartVisitBinding.inflate(getLayoutInfalter());
        setContentView(binding.getRoot())

    
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(StartVisitActivity.this);

        locationCallback = new LocationCallback(){
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                onLocationChanged(locationResult);
            }

            @Override
            public void onLocationAvailability(LocationAvailability locationAvailability) {
                super.onLocationAvailability(locationAvailability);
            }
        };

        binding.btnGetLocation.setOnClickListener(v -> {

            CancellationTokenSource cts = new CancellationTokenSource();

            mFusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY,
                    cts.getToken()).addOnSuccessListener(this, new OnSuccessListener<Location>() {
                @Override
                public void onSuccess(Location location) {
                    if (location != null) {
                        strLat = ""+location.getLatitude();
                        strLon = ""+location.getLongitude();
                        binding.txtLocation.setText("Your position (: "+strLat+","+strLon+")");
                    }
                }
            }).addOnFailureListener(this, new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {

                }
            });

            createLocationRequest();
            LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                    .addLocationRequest(locationRequest);
            SettingsClient client = LocationServices.getSettingsClient(StartVisitActivity.this);
            Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());
            task.addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    if (e instanceof ResolvableApiException) {
                        // Location settings are not satisfied, but this can be fixed
                        // by showing the user a dialog.
                        try {
                            // Show the dialog by calling startResolutionForResult(),
                            // and check the result in onActivityResult().
                            ResolvableApiException resolvable = (ResolvableApiException) e;
                            resolvable.startResolutionForResult(StartVisitActivity.this, 500);
                        } catch (IntentSender.SendIntentException sendEx) {
                            // Ignore the error.
                        }
                    }
                }
            });
        });

    }

  
    @Override
    public void onResume() {
        super.onResume();
        startLocationUpdates();
    }

  
    @Override
    protected void onPause() {
        super.onPause();
        mFusedLocationClient.removeLocationUpdates(locationCallback);
    }

    @SuppressLint("MissingPermission")
    void startLocationUpdates(){
        mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
    }

    void createLocationRequest() {
        locationRequest = new LocationRequest();
        locationRequest.setInterval(5000);//time in ms
        locationRequest.setFastestInterval(2000);
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }
}

When analyzing the database, we found some rather big difference (say 200m), that is the difference between the recorded salesperson location and the actual store position saved in DB.

How to improve the accuracy? BTW, those salespersons use different phones (with different Android versions).

1

There are 1 best solutions below

1
On

GPS is not 100% accurate. Especially indoors, and especially around tall buildings. In Manhattan I'm frequently off by a block or two just walking around. Sometimes it corrects itself after a few seconds, sometimes it jumps around for minutes. It all depends on atmospheric conditions and the way the signal is bouncing off surfaces that day. Network location has the same issues, and depending on whether the network used to identify location is cellular or wifi can be far more inaccurate.

The biggest thing you can do is filter the location results. This can be a technique like a kalman filter that expresses location as a location plus an uncertainty, or something less formal like ignoring a new location unless you get N updates within Y meters of the new location to confirm it. A lot depends on how frequently you need updates and how stale you can live with the data being. Any filtering solution either ends up adding a delay or adding it's own incorrectness to the location- smoothing down spikes in inaccuracy at the cost of adding some base amount of inaccuracy.