0

I need to calculate distance traveled by car! Not distance between, not distance to no. The distance can be fully different if we calculate it via Google's provided API. Google can give 1KM from one point to another but car can go 800 meters in the way the rider want. Using Accelerometer didn't help. It works for walking but never for faster speed.

Any suggestions on how can I get the distance traveled by the car?

I've tried using Location APIs of Google: distanceTo or distanceBetween is not an option at all. It can give significant different result than IN REAL. In real car can travel through very short places and reach goal in 800 meters whereas Google can give 1KM distance between locations.

Below is the code for my application. The speed is amazingly correct.

class HomeScreen : AppCompatActivity(), GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener, SensorEventListener {
    override fun onSensorChanged(event: SensorEvent?) {
        val sensor = event?.sensor
        val values = event?.values
        var value = -1

        if (values != null && values.size ?: 0 > 0) {
            value = values[0].toInt()
        }


        if (sensor != null &&
            sensor.type == Sensor.TYPE_STEP_DETECTOR
        ) {
            val finalSteps = getDistanceRun(steps)
            val finalStepsTruncated = String.format("%.2f", finalSteps)
            distanceTV.text = "$finalStepsTruncated"
            steps++
        }
    }

    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {

    }

    override fun onConnectionFailed(p0: ConnectionResult) {
        val failed = p0
    }

    @SuppressLint("MissingPermission")
    override fun onConnected(p0: Bundle?) {
        if (locationPermissionsGranted(this)) {
            fusedLocationClient?.requestLocationUpdates(locationRequest, object : LocationCallback() {
                override fun onLocationResult(p0: LocationResult?) {
                    val location = p0
                    val metersPerSecond: Float = location?.lastLocation?.speed ?: 0f
                    val speed = metersPerSecond * 3600 / 1000
                    speedTV.text = "${Math.round(speed)} KM/H"
                }
            }, null)

        } else {
            requestPermission(
                this, 0,
                Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
            )
        }
    }

    override fun onConnectionSuspended(p0: Int) {
        val suspended = p0
    }

    private var fusedLocationClient: FusedLocationProviderClient? = null
    private var mGoogleApiClient: GoogleApiClient? = null
    private lateinit var locationRequest: LocationRequest
    private var steps: Long = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home_screen)
        locationRequest = LocationRequest()
        locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY;
        locationRequest.interval = 1000
        locationRequest.fastestInterval = 500

        if (PermissionManager.locationPermissionsGranted(this)) {
            mGoogleApiClient = GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build()

            mGoogleApiClient?.connect()
            fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
            createLocationRequest()
        } else {
            requestPermission(
                this, 0,
                Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
            )
        }

        val sManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        val stepSensor = sManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)
        sManager.registerListener(this, stepSensor, SensorManager.SENSOR_DELAY_FASTEST);
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (PermissionManager.locationPermissionsGranted(this)) {
            mGoogleApiClient = GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build()
            mGoogleApiClient?.connect()
            fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
            createLocationRequest()
        }
    }

    protected fun createLocationRequest() {
        val builder = LocationSettingsRequest.Builder()
            .addLocationRequest(locationRequest)

        val client = LocationServices.getSettingsClient(this)
        val task = client.checkLocationSettings(builder.build())

        task.addOnSuccessListener(this) {

            // All location settings are satisfied. The client can initialize
            // location requests here.
            // ...
        }

        task.addOnFailureListener(this) { e ->

            if (e is 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().
                    e.startResolutionForResult(
                        this@HomeScreen,
                        0
                    )
                } catch (sendEx: IntentSender.SendIntentException) {
                    // Ignore the error.
                }

            }
        }
    }

    fun getDistanceRun(steps: Long): Float {
        return (steps * 78).toFloat() / 100000.toFloat()
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
  • 1
    Maybe get GPS updates for example every second or every 20 meters and sum up all the displacements for the whole distance(?) If you request them for every 20 meters then you don't even need to calculate the displacement. – Markus Kauppinen Feb 11 '19 at 10:55

2 Answers2

2

I've implemented an odometer based on gps readings and, with one second readings I have been able to get odometer lectures well below under 1% error in accuracy. And well over distances up to 50-70km I was unable to detect over 50m difference from the road marks (I was even capable to detect when a mark has been moved) The procedure used involved integrating the velocity readings (velocity is given by gps as a vector, so no need to compute it's modulus) and precise readings of the timestamps given by the device. Never use the position readings... as those are bad enough to make your odometer to depart over 20% in the readings. But using the velocities and integrating those gives you a good bass band pass filtering.

GPS used was a standard OEM gps with NMEA.0183 output. The velocity readings were 0.1 value in resolution, and so I was not expecting under 1% in accuracy, present gps devices give velocity readings with under 0.001 resolution.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
0

Hello i don't know if this would help but i once wrote a class where i calculated it manually by feeding in the parameters to the respective methods:

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.widget.Toast;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;

import java.util.List;

/**
 * Created by Ibkunle Adeoluwa on 1/8/2019.
 */


public class LocationManager {

    private FusedLocationProviderClient fusedLocationProviderClient;
    private LocationCallback locationCallback;
    private LocationRequest locationRequest;



    //TODO Import these Libraries in gradle
    /*
    implementation 'com.google.android.gms:play-services-location:11.8.0'
    implementation 'com.karumi:dexter:5.0.0'
    */

    private Activity myActivity;

    public LocationManager(Activity myActivity) {
        this.myActivity = myActivity;
    }


    public void requestLocationPermissions() {
        //Request required permissions using Dexter Library ACCESS_COARSE_LOCATION & ACCESS_FINE_LOCATION
        Dexter.withActivity(myActivity)
                .withPermissions(Manifest.permission.ACCESS_COARSE_LOCATION,
                        Manifest.permission.ACCESS_FINE_LOCATION)
                .withListener(new MultiplePermissionsListener() {
                    @Override
                    public void onPermissionsChecked(MultiplePermissionsReport report) {
                        if (report.areAllPermissionsGranted()) {
                            buildLocationRequest();
                            buildLocationCallback();
                            if (ActivityCompat.checkSelfPermission(myActivity, Manifest.permission.ACCESS_FINE_LOCATION)
                                    != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(myActivity, Manifest.permission.ACCESS_COARSE_LOCATION)
                                    != PackageManager.PERMISSION_GRANTED) {
                                return;
                            }
                            fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(myActivity);
                            fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
                        }
                    }

                    @Override
                    public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
                        Toast.makeText(myActivity, "Location Permission Denied", Toast.LENGTH_SHORT).show();

                    }
                }).check();
    }


    private void buildLocationRequest() {
        //Get Location
        locationRequest = new LocationRequest();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        locationRequest.setInterval(5000);
        locationRequest.setSmallestDisplacement(10.0f);
    }

    private Location buildLocationCallback() {
        //Process location response
        final Location location = new Location();

        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                //Log
                Log.d("Location", locationResult.getLastLocation().getLatitude()
                        + "/" + locationResult.getLastLocation().getLongitude());

                location.setLatitude(locationResult.getLastLocation().getLatitude());
                location.setLongitude(locationResult.getLastLocation().getLongitude());


            }
        };
        return location;
    }

    //TODO How to Use

    /***
     * Sample Request
     *
     System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'M') + " Miles\n");
     System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'K') + " Kilometers\n");
     System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'N') + " Nautical Miles\n");

     System.out.println(distance(10.46786, -98.53506,32.9697, -96.80322,  'M') + " Initial Miles\n");
     System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'M') + " Covered Miles\n");

     System.out.println(coveredDistance(distance(10.46786, -98.53506,32.9697, -96.80322), distance(32.9697, -96.80322, 29.46786, -98.53506))+ " Left Distance\n");

     System.out.println(percentageCoveredDistance(262.6777938054349,1558.5453389875424)+ " Covered Distance Percentage \n");
     *
     *
     ***/


    /**
     * Expected Output
     *
     262.6777938054349 Miles
     422.73893139401383 Kilometers
     228.10939614063963 Nautical Miles

     1558.5453389875424 Initial Miles
     262.6777938054349 Covered Miles

     1295.8675451821075 Left Distance
     17% Covered Distance Percentage
     *
     *
     **/


    /**
     *
     * @param lat1
     * @param lon1
     * @param lat2
     * @param lon2
     * @param unit
     * @return
     */
    private static double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
        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;
        if (unit == 'K') {
            dist = dist * 1.609344;
        } else if (unit == 'N') {
            dist = dist * 0.8684;
        }
        return (dist);
    }

    /**
     *
     * @param lat1
     * @param lon1
     * @param lat2
     * @param lon2
     * @return
     */
    private static double distance(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);
    }

    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts decimal degrees to radians             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/

    /**
     *
     * @param deg
     * @return
     */
    private static double deg2rad(double deg) {
        return (deg * Math.PI / 180.0);
    }

    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts radians to decimal degrees             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/

    /**
     *
     * @param rad
     * @return
     */
    private static double rad2deg(double rad) {
        return (rad * 180.0 / Math.PI);
    }

    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts subtracts initial from current distance:*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/

    /**
     *
     * @param initialDistance
     * @param currentDistance
     * @return
     */
    private static double coveredDistance(double initialDistance, double currentDistance) {
        return (initialDistance - currentDistance);
    }

    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::This function converts the covered distance to percentage of the total :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/

    /**
     *
     * @param coveredDistance
     * @param totalDistance
     * @return
     */
    private static String percentageCoveredDistance(double coveredDistance, double totalDistance) {
        double percent = (100 * coveredDistance) / totalDistance;
        return String.format("%.0f%%", percent);
    }


}
Asendo
  • 158
  • 1
  • 10