23

I need to get the speed and heading from the gps. However the only number i have from location.getSpeed() is 0 or sometimes not available. my code:

        String provider = initLocManager();
    if (provider == null)
        return false;
    LocationListener locListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            updateWithNewLocation(location, interval, startId);
            Log.i(getString(R.string.logging_tag), "speed =" + location.getSpeed());
        }

        public void onProviderDisabled(String provider){
            updateWithNewLocation(null, interval, startId);
        }

        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

    _locManager.requestLocationUpdates(provider, interval,  DEFAULT_GPS_MIN_DISTANCE, locListener);


    private String initLocManager() {
    String context = Context.LOCATION_SERVICE;
    _locManager = (LocationManager) getSystemService(context);

    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setAltitudeRequired(false);
    criteria.setBearingRequired(true);
    criteria.setSpeedRequired(true);
    criteria.setCostAllowed(true);
    //criteria.setPowerRequirement(Criteria.POWER_LOW);
    String provider = _locManager.getBestProvider(criteria, true);

    if (provider == null || provider.equals("")) {
        displayGPSNotEnabledWarning(this);
        return null;
    }

    return provider;
}

I tried to play the Criteria with but no success. Does anyone have an idea what is the problem?

Mike Mackintosh
  • 13,917
  • 6
  • 60
  • 87
user591539
  • 233
  • 1
  • 2
  • 4

11 Answers11

26

location.getSpeed() only returns what was set with location.setSpeed(). This is a value that you can set for a location object.

To calculate the speed using GPS, you'll have to do a little math:

Speed = distance / time

So you would need to do:

(currentGPSPoint - lastGPSPoint) / (time between GPS points)

All converted to ft/sec, or however you want to show the speed. This is how I did it when I made a runner app.

More specifically, you'll need to calculate for absolute distances:

(sqrt((currentGPSPointX - lastGPSPointX)^2) + (currentGPSPointY - lastGPSPointY)^2)) / (time between GPS points)

It might help to make a new TrackPoint class or something, which keeps the GPS location and time it was taken inside.

Mike Mackintosh
  • 13,917
  • 6
  • 60
  • 87
John Leehey
  • 22,052
  • 8
  • 61
  • 88
  • 1
    it will return distance in meter or kilometer? – Shashwat Gupta Jul 07 '17 at 05:28
  • 6
    I have seen issues with getSpeed() returning zero, even though 'hasSpeed()' is true and there has been motion. On one 6.0.1 device it always returns zero! From the Fused Location service. The regular LocationManager provides speeds, but seem less accurate and less frequent in its updates. So I went with my own speed calculation as suggested here. – Dave Hubbard Aug 18 '17 at 19:34
  • Over 9 years later and this worked for me, location.speed (kotlin) returned correct results (assumed) from the Android Studio emulator location route movement, however, real world driving always returned 0 KM/H tisk tisk. – BENN1TH May 27 '20 at 07:08
  • 1
    this answer is wrong, it doesn't always returns 0, and no need to use `setSpeed()` method to make it no zero. Android can get speed without you, no manual calculation is needed – user924 Feb 25 '21 at 19:59
13

Imbru's answer looks really good, but it is not very helpful if you are working with units.

Here's what I did to calculate the speed in meters per second (m/s).

object : LocationListener() {
    var previousLocation: Location? = null

    override fun onLocationChanged(location: Location) {
        val speed = if (location.hasSpeed()) {
            location.speed
        } else {
            previousLocation?.let { lastLocation ->
                // Convert milliseconds to seconds
                val elapsedTimeInSeconds = (location.time - lastLocation.time) / 1_000.
                val distanceInMeters = lastLocation.distanceTo(location)
                // Speed in m/s
                distanceInMeters / elapsedTimeInSeconds
            } ?: 0.0
        }
        previousLocation = location

        /* There you have it, a speed value in m/s */
        functionThatUsesSpeedInMeterPerSecond(speed)

        . . .

    }

    . . .

}
Nivaldo Bondança
  • 853
  • 2
  • 10
  • 20
  • 5
    The speed calculation should be done only if `location.hasSpeed()==false`. Some people also reported that `location.getSpeed()` always return 0 even if `location.hasSpeed()==true`. Thus, I would use `location.hasSpeed() && location.getSpeed()>0` as a condition. – Julien Kronegg Apr 15 '18 at 05:30
  • 1
    I second Julien Kroneggs suggestion to prefer direkt getSpeed usage (if .hasSpeed()==true) because GPS does not need two positions and their distance to compute speed but can use the doppler effect to compute speed more accuratly. Meaning getSpeed will be more accurate for the moment in time while distance / time calculations will get more precise with higher time elapsed between two locations but only giving you the average speed between the two measurements not the speed at the moment of the new location coming in. – FrankKrumnow Jun 25 '20 at 09:22
  • @JulienKronegg who reported it that it can 0 even if hasSpeed? – user924 Dec 24 '20 at 15:00
  • @user924 you can find some posts on SO https://www.google.com/search?q=Android+location.getSpeed()+always+return+0+site:stackoverflow.com – Julien Kronegg Dec 26 '20 at 11:30
  • @JulienKronegg mb they didn't have the correct check (with hasSpeed) – user924 Dec 26 '20 at 12:56
9

There is my custom LocationListener used to get speed manually and by location object if has speed.

 new LocationListener() {
        private Location mLastLocation;

        @Override
        public void onLocationChanged(Location pCurrentLocation) {
            //calcul manually speed
            double speed = 0;
            if (this.mLastLocation != null)
                speed = Math.sqrt(
                        Math.pow(pCurrentLocation.getLongitude() - mLastLocation.getLongitude(), 2)
                                + Math.pow(pCurrentLocation.getLatitude() - mLastLocation.getLatitude(), 2)
                ) / (pCurrentLocation.getTime() - this.mLastLocation.getTime());
            //if there is speed from location
            if (pCurrentLocation.hasSpeed())
                //get location speed
                speed = pCurrentLocation.getSpeed();
            this.mLastLocation = pCurrentLocation;
            ////////////
            //DO WHAT YOU WANT WITH speed VARIABLE
            ////////////
        }

        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {

        }

        @Override
        public void onProviderEnabled(String s) {

        }

        @Override
        public void onProviderDisabled(String s) {

        }
    };
Kevin ABRIOUX
  • 16,507
  • 12
  • 93
  • 99
  • 1
    i used your code it works like this it retruns speed as 2.25 but my vehicle speed is about 60km/h .How to calculate the movement speed exactly? – prasanthMurugan Nov 21 '16 at 12:22
  • @Kevin ABRIOUX what is the unit of calculated speed. is it km/hr ? – AKASH WANGALWAR Sep 19 '18 at 08:46
  • Instead of self-computing the distance between the locations I suggest using the build-in method of location.distanceTo(other_location). The divisor with the time delta can stay as is. Output is meters per second so multiply by 3.6 to get to km/h – FrankKrumnow Jun 25 '20 at 09:39
  • I wrongly suggestet the divisor was correct. But it computed the time delta in milliseconds but the speed is in meters per second. – FrankKrumnow Jun 25 '20 at 09:50
6

On a spherical planet distance should be calculated with these formulas :

private static Double distance(Location one, Location two) {
       int R = 6371000;        
       Double dLat = toRad(two.getLatitude() - one.getLatitude());
       Double dLon = toRad(two.getLongitude() - one.getLongitude());
       Double lat1 = toRad(one.getLatitude());
       Double lat2 = toRad(two.getLatitude());         
       Double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
               + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);        
       Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));        
       Double d = R * c;
       return d;
   }
private static double toRad(Double d) {
       return d * Math.PI / 180;
   }
Hyli
  • 61
  • 1
  • 1
  • 3
    what if the user is on a slope then?? – Anil P Babu Mar 01 '17 at 09:37
  • Even google seems to just forget about altitude delta. Maybe because bad satellite fixes and celltower/wifi only give 2D Locations? Looking at the Location.distanceTo code - that I don't fully understand - I can see lat and long of the two locations and bearing playing a role but altitude is nowhere to be seen. – FrankKrumnow Jun 25 '20 at 09:37
1

(1) I believe you can use the requestLocationUpdates() method and then create a LocationListener class with an onLocationChange method set to display getSpeed(). This is how i recently saw it done with Location.getLatitude and Location.getLongitude, so I believe you could just use getSpeed() the same way, correct?

(2) After just reading the eclipse description window, though, I see it says exactly what the previous person said: "if hasSpeed() is false, 0.0f is returned." But maybe this will help: http://www.ehow.com/how_5708473_convert-latitude-feet.html :)

Andro Selva
  • 53,910
  • 52
  • 193
  • 240
Bob
  • 11
  • 2
1

getSpeed() method actually works fine but you have to use a high request interval like 1 second and you need high accuracy, first i was doing 3 second interval and PRIORITY_BALANCED_POWER_ACCURACY and i kept getting 0 values until i change it like i say. I am using fused location provider api.

public class Main3Activity extends AppCompatActivity {

private FusedLocationProviderClient mFusedLocationClient;
private int speed;
private double lat;
private double lng;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);

    mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}

@Override
protected void onResume() {
    super.onResume();
    if(!runtime_permissions()) {
        requestLocations();
    }
}

@Override
protected void onPause() {
    super.onPause();
    //stop location updates when Activity is no longer active
    if (mFusedLocationClient != null) {
        mFusedLocationClient.removeLocationUpdates(mLocationCallback);
    }
}

@SuppressLint("MissingPermission")
private void requestLocations(){
    LocationRequest mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(1000);;
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}

LocationCallback mLocationCallback = new LocationCallback() {
    @Override
    public void onLocationResult(LocationResult locationResult) {
        List<Location> locationList = locationResult.getLocations();
        if (locationList.size() > 0) {
            //The last location in the list is the newest
            Location location = locationList.get(locationList.size() - 1);

            lat = location.getLatitude();
            lng = location.getLongitude();

            //speed in km/h
            speed = (int) ((location.getSpeed() * 3600) / 1000);

        }
    }
};

private boolean runtime_permissions() {
    if(Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){

        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);

        return true;
    }
    return false;
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == 100){
        if( grantResults[0] == PackageManager.PERMISSION_GRANTED){
            onResume();
        }else{
            runtime_permissions();
        }
    }
}

}

  • I would assume that with the fused location provider you would not always get a speed or a very bad one because with bad or no GPS you will get triangulated cell-tower or wifi-vicinity readings that are quite inaccurate for accuracy and must be horrible for speed. I suggest using GPS Prodiver only if speed is of a high value. I'm no expert though and I only use GPS to save power with wifi turned off. – FrankKrumnow Jun 25 '20 at 09:14
1

Hey I too was suffering from the same but now I have got it solved ! Just multiply the value by 18/5 , it gives almost the accurate value.

speed=location.getSpeed()*18/5

Also specify the interval as 1000*2 and fastest interval as 1000*1 for more accuracy

  • 2
    Where does this `18/5` magic number come from? Also, how does this help the problem of *" `getSpeed()` returns 0 "* ? – juzraai Jul 15 '18 at 17:25
  • 2
    getSpeed() method returns the value in m/s which returns always zero so convert into km/h by using 18/5 1 m/s=18/5 Km/hr – Aswath shobi Jul 16 '18 at 05:23
  • Seconds in hour = 3600; meters in kilometer = 1000. So 1 m/s = 3600/1000 km/h = 36/10 km/h = 18/5 km/h = 3.6 km/h. – FrankKrumnow Jun 25 '20 at 09:07
0

getspeed() works fine. You don't have to do the math using distance formula. It is already there in getspeed, As long as there are latitude and longitude, there will be a speed in getspeed.

Dosa
  • 53
  • 2
0

I also encountered this problem before, I hope this can help.

It returns 0 because your device cannot get a lock on the GPS, or cannot connect to the GPS.

I tried to get the speed using an older lenovo device and it returns 0 because it cannot lock on a gps.

I tried using a samsung galaxy nexus and it returned my speed(has a better GPS sensor).

The GPS sensor in your phone might not be good or you are in an area that has a weak GPS signal such as inside a house or building.

black1011
  • 284
  • 3
  • 18
0

I basicaslly calculate the instantaneous speed and then use the setSpeed() method to add it in the location. Its pretty accurate because I compared it inside a vehicle where I could check the tachymeter.

private double calculateInstantaneousSpeed(Location location) {



    double insSpeed = 0;
    if (y1 == null && x1 <= -1) {
        //mark the location y1 at time x1
        y1 = location;
        x1 = duration.getDurationAsSeconds();


    } else {
         //mark the location y2 at time x2
        y2 = location;
        x2 = duration.getDurationAsSeconds();


        //calculate the slope of the curve (instantaneous speed)
        dy = y1.distanceTo(y2);
        dx = x2 - x1;

        insSpeed = dy / dx;

        y1 = y2;
        x1 = x2;

    }

    Singleton.getInstance().instantaneousSpeedSamples.add(insSpeed);
    //System.out.println("Instantaneous Speed m/s: "+insSpeed);
    return insSpeed;
}
D3bian0s
  • 51
  • 3
0

Based on other suggestions here I put together this code with comments to why:

public static float getSpeedKmh(Location newLocation, Location previousLocation)
{
    if(newLocation == null) throw new IllegalArgumentException("newLocation must not be null");
    float speed = -1.0f;
    if(newLocation.hasSpeed()) /* gps doppler speed at the current moment preferred */
    {
        speed = newLocation.getSpeed();
    }else /* may be wifi or celltower based location: compute AVERAGE speed since last fix */
    {
       if(previousLocation == null) throw new IllegalArgumentException("Cannot compute speed without previousLocation (was null)");
       if(previousLocation.getTime()==newLocation.getTime())
           throw new IllegalArgumentException("Cannot compute speed from same time locations!"); /* diff by zero protection */
       speed = newLocation.distanceTo(previousLocation) * 1000.0f /* time diff in millis so multiply by 1000 */
           / Math.abs(newLocation.getTime() - previousLocation.getTime()); /* Math abs: so you can even swap new and older location without effect */
    }
       return speed * 3.6f; /* m/s -> km/h */
}
FrankKrumnow
  • 501
  • 5
  • 13