12

I am drawing heart as a polyline on Google Map depends on radius(1 - 100 meter). Once heart is drawn, user needs to walk on that heart border and need to finish from starting to end (Walking start from bottom then left then right and then bottom again).

I am able to draw heart and I am getting 360 points(latlng). Here is my code which will draw heart and image.

private void initPath() {
    path = new PolylineOptions();
    path.color(ContextCompat.getColor(mActivity,R.color.heart_green));
    path.width(25);

    // offset to bottom
    double offsetY = getY(Math.toRadians(180));

    for (int angle = 0; angle <= 360; angle++) {
        double t = Math.toRadians(angle);
        double x = getX(t);
        double y = getY(t) - offsetY;
        //Log.d(TAG, "angle = " + angle + "(x = " + x + ",y= " + y + ")");

        //convert x,y to lat lng
        LatLng latLng = getLatLng(x, y, center);
        path.add(latLng);

        heart360Points.add(latLng);

    }

}

private double getX(double t) {
    return radius * 16 * Math.sin(t) * Math.sin(t) * Math.sin(t) / HEART_RATIO;
}

private double getY(double t) {
    return radius * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)) / HEART_RATIO;
}

private LatLng getLatLng(double dx, double dy, LatLng centerHeart) {
    return new LatLng(centerHeart.latitude + Math.toDegrees(dy / EARTH_RADIUS),
            centerHeart.longitude + Math.toDegrees(dx / EARTH_RADIUS) / Math.cos(Math.toRadians(centerHeart.latitude)));

}   

Here is heart image.

But whenever I tried to walk on heart border then GPS location are too much fluctuating so I am never able to complete walking on heart. I am currently requesting location every second.

Here is my location code.

 private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 2000 ;
 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.love_lead_perform);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    mApiClient = new GoogleApiClient.Builder(this)
            .addApi(ActivityRecognition.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

    mApiClient.connect();


    mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
    mSettingsClient = LocationServices.getSettingsClient(this);
    createLocationCallback();
    createLocationRequest();
    createLocationSettingsRequest();


} 

private void createLocationSettingsRequest() {
    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
    builder.addLocationRequest(mLocationRequest);
    mLocationSettingsRequest = builder.build();
}

private void createLocationRequest() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}   

I don't understand why GPS location is fluctuating too much. I am getting different GPS location even I am standing.

How can i get accurate GPS location?

James Z
  • 12,209
  • 10
  • 24
  • 44
Sandip Armal Patil
  • 6,241
  • 21
  • 93
  • 160
  • Which are the permissions you're invoking on your manifest.xml? – statosdotcom Jan 02 '18 at 17:21
  • 1
    Maybe you need to join the military - I have heared they get better GSP data than the average person :) but I think youre not alone with that problem, even PokemonGo (wich I do not play) wobbles around the map all the time. So you *might* suffer the "default" randomness of the GPS signal on your device which might be influenced by your location (tunnels, buildings, Trees == shadowing the satelites needed for GPS) and/or weather -which seems to influence geocaching. – Patrick Artner Jan 02 '18 at 17:23
  • 2
    Yes, it's true, but it is also true that "Waz3", "Ub3r" and the proper "G00gle Directions" works like a charm, with smooth movement and precision around few meters... I think they hide some secret from us, little devs. – statosdotcom Jan 02 '18 at 17:25
  • 2
    @statosdotcom - they might combine phonecell location data with gps or average a sliding 5 to 20s worth of data to eliminate zigzagging depending on the speed wiht which you are moving. Possibly even discard sudden "side-movements" based on a vector taken from your last n locations - i.e. lots of stuff to fill in/eleminate wrong data and predict "good" samples. – Patrick Artner Jan 02 '18 at 17:28
  • 1
    @PatrickArtner nice analysis and comment, thank you. You're perfectly right, it's not only a matter of using sharply the correct tools, but marry this with know-how in terms of human reason, i.e., not just blindly lying on technical data, but use that accurate data to **predict** what's going on. Man+Machine... waw... where does we are going to? :D – statosdotcom Jan 02 '18 at 17:43
  • I've heard from somewhere, that GPS has up to 20m infelicity on purpose (for military reasons), whereas in fact it can give up to 1m preciseness. There's no way for you to affect those values. – azizbekian Jan 14 '18 at 11:54
  • Sometimes the location fluctuation actually happens due to the device hardware. Do other navigation apps such as Google Maps work properly on your device without fluctuation? – Rahulrr2602 Jan 20 '18 at 07:01

4 Answers4

5

Fluctuation of location is it's normal behavior. The thing is, when/how you should accept or discard the unexpected location. As user(Patrick Artner) commented, you should discard sudden side-movements.

You can discard these movements in two ways,

  1. Lets decide minimum criteria (of 1 meter).

    Assuming min distance of 1 meter because, in case human is 'walking (not standing)', that can be possible, human can cover at least distance of 1 meter in 'X' seconds. So if covered distance returned less than 1 meter, you can simply discard.

  2. Lets decided maximum criteria (of 5 meters).

    Assuming max distance of 5 meters because, in case human is 'walking (not running)', that cannot be possible, human can cover distance more than 5 meters in 'X' seconds. So if covered distance returned more than 5 meters, you can simply discard.

where,

distance = NEW_LOCATION - OLD_LOCATION (How to get distance?)

X = certain amount of time (in seconds)

Just accept location less than 5 m and more than 1 m (scope of distance you can choose as per your understanding).

Dhruv Patel
  • 1,529
  • 1
  • 18
  • 27
3

Basics of GPS system errors described, for example, here. Using Extended Kalman Filter with fusing the data from GPS (IMHO raw, non filtered, GPS data received from GpsStatus.NmeaListener/OnNmeaMessageListener ) and another sensors (accelerometer, gyroscope, magnetometer, etc.) can improve accuracy, but today there is not possibilities to create good application with described functionality (track path around 100m "complex" figure). If you simplify path (like on fig.)

"Simplified" heart path

and track crossing checkpoints "geofence" and its order - may be you can implement functionality, but accuracy in many cases will be unacceptable.

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
2

Not getting exact GPS coordinates is quite common, as it depends upon many factors like

  • Open sky or indoor condition
  • Obstructions like clouds, buildings
  • GPS chip configuration on device / CPU power
  • Cellular network signal strength

You can do some workarounds

  • Use Kalman Filter algorithm as suggested by @andrii-omelchenko
  • Or you can use geofencing mechanism and set geofences and android system will give you callbacks on entering/exit from geofence

    • A) Set few geofence circles (maybe 3~5 for next set of lat longs)
    • B) Mark step as complete on entering that geofence
    • C) Repeat A until full heart is covered by user
  • Use Android activity recognition along with above geofence logic to check whether user is walking or not.

Below is sample geofence image to give you an idea

enter image description here

Akhil
  • 6,667
  • 4
  • 31
  • 61
2

Yes gps location fluctuating is normal but if you want more accuracy do one thing. Here i'm showing you my trick.

I'm sure you also doing same thing. Just showing you my way.

Step 1. Make this class GoogleLocationService.java

public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;

public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 2000;


public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
    this.locationUpdateListener = locationUpdateListener;
    this.activity = activity;
    buildGoogleApiClient();
}

protected synchronized void buildGoogleApiClient() {
    //Log.i(TAG, "Building GoogleApiClient");
    mGoogleApiClient = new GoogleApiClient.Builder(activity)
            .addConnectionCallbacks(callbacks)
            .addOnConnectionFailedListener(callbacks)
            .addApi(LocationServices.API)
            .build();
    createLocationRequest();
    mGoogleApiClient.connect();
}

protected void createLocationRequest() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

}

private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

        if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
            Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();

        }
        locationUpdateListener.cannotReceiveLocationUpdates();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location.hasAccuracy()) {
            if (location.getAccuracy() < 30) {
                locationUpdateListener.updateLocation(location);
            }
        }
    }
}

private static boolean locationEnabled(Context context) {
    boolean gps_enabled = false;
    LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    try {
        gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return gps_enabled;
}

private boolean servicesConnected(Context context) {
    return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}

private boolean isPackageInstalled(String packagename, Context context) {
    PackageManager pm = context.getPackageManager();
    try {
        pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
        return false;
    }
}


public void startUpdates() {
    /*
     * Connect the client. Don't re-start any requests here; instead, wait
     * for onResume()
     */
    if (servicesConnected(activity)) {
        if (locationEnabled(activity)) {
            locationUpdateListener.canReceiveLocationUpdates();
            startLocationUpdates();
        } else {
            locationUpdateListener.cannotReceiveLocationUpdates();
            Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
        }
    } else {
        locationUpdateListener.cannotReceiveLocationUpdates();
        Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
    }
}

//stop location updates
public void stopUpdates() {
    stopLocationUpdates();
}

//start location updates
private void startLocationUpdates() {

    if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
    }
}

public void stopLocationUpdates() {
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
    }
}

public void startGoogleApi() {
    mGoogleApiClient.connect();
}

public void closeGoogleApi() {
    mGoogleApiClient.disconnect();
}

 }

Step2. Make this interface LocationUpdateListener.java

 public interface LocationUpdateListener {

/**
 * Called immediately the service starts if the service can obtain location
 */
void canReceiveLocationUpdates();

/**
 * Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
 */
void cannotReceiveLocationUpdates();

/**
 * Called whenever the location has changed (at least non-trivially)
 * @param location
 */
void updateLocation(Location location);

/**
 * Called when GoogleLocationServices detects that the device has moved to a new location.
 * @param localityName The name of the locality (somewhere below street but above area).
 */
void updateLocationName(String localityName, Location location);
}

You can call directly below code in class where location need to be update and remove locationservice.

 private GoogleLocationService googleLocationService;

 googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
    @Override
    public void canReceiveLocationUpdates() {
    }

    @Override
    public void cannotReceiveLocationUpdates() {
    }

    //update location to our servers for tracking purpose
    @Override
    public void updateLocation(Location location) {
        if (location != null ) {
            Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());

        }
    }

    @Override
    public void updateLocationName(String localityName, Location location) {

        googleLocationService.stopLocationUpdates();
    }
});
googleLocationService.startUpdates();


and call this onDestroy 
if (googleLocationService != null) {
    googleLocationService.stopLocationUpdates();
}

I did one thing if you look. getAccuracy() describes the deviation in meters. So, the smaller the number, the better the accuracy.

public void onLocationChanged(Location location) {
    if (location.hasAccuracy()) {
        if (location.getAccuracy() < 30) {
            locationUpdateListener.updateLocation(location);
        }
    }
}

Thanks hope this help you.

Saveen
  • 4,120
  • 14
  • 38
  • 41