Gps lat & long randomly displays very inaccurate results - java

Im listening to my phones GPS and post latitude and longitude to my webservice evey time onLocationChanged gets called. My problem is that in about 2% of my posts, it displays a VERY inaccurate result (several miles off). Should i check the accuracy from my location object before i post? Or do you have any other suggestions? Thanks!
public void onLocationChanged(Location location) {
locationManager.removeUpdates(networkLocationListener);
textView.setText(textView.getText().toString() + "New GPS location: "
+ String.format("%9.6f", location.getLatitude()) + ", "
+ String.format("%9.6f", location.getLongitude()) + "\n");
//float accuracy = location.getAccuracy(); check this value?
postLocationToWebservice(location);
}

This is a very common problem while using the Location services of Android.There are few checks you can introduce to make sure that only the accurate location gets posted to you web service.
1.Compare the accuracy of all the location services.
2.Define a minimum limit for accuracy and a maximum limit for time.(Because some times the location obtained is accurate but it is an old one).
Check this code snippet and make necessary changes to accomplish what you want.Make sure you make a
log build that checks the accuracy of your algorithm.
List<String> matchingProviders = myLocation.lm.getAllProviders();
for (String provider: matchingProviders)
{
Location location = myLocation.lm.getLastKnownLocation(provider);
if (location != null) {
float accuracy = location.getAccuracy();
long time = location.getTime();
if ((time > minTime && accuracy < bestAccuracy)) {
bestResult = location;
bestAccuracy = accuracy;
bestTime = time;
}
else if (time < minTime && bestAccuracy == Float.MAX_VALUE && time > bestTime) {
bestResult = location;
bestTime = time;
}
}
}

It could be possible that the updated location is reported by NETWORK_PROVIDER instead of GPS_PROVIDER
you can always get the last updated GPS location by
mLocationMgr.getLastKnownLocation(LocationManager.GPS_PROVIDER);

Related

Calculating walking distance (Android)

I'm trying to calculate running route distance for fitness application
I tried to find my current location then after 5 seconeds find new location, calculate distance and then sum them all after that swipe the current location to the previous location and update new current location but I think that it always takes the same location and the distance stay at 0.
Here sample of the code-
public void onLocationChanged(Location location) {
//location is the location of user
PrvLoc = new Location(MyLoc);
MyLoc = new Location(location);
Toast.makeText(Running_Activity.this, "" + location.getLatitude() + " , " + location.getLongitude(), Toast.LENGTH_SHORT).show();
Distance += PrvLoc.distanceTo(location);
d.setText("" + Distance+" Km");
s.setText("" + MyLoc.getSpeed()+" Km/h");
}
The is going to be a bad way of implementing this functionality. GPS is just too noisy and inaccurate. When you sum up inaccurate values, your end result is more inaccurate than the individual values. Doing this over the length of a few miles is just going to be nothing remotely related to the actual length traveled.
You can do this of course, but you're either going to have to implement a lot of smoothing, or incorporate another source of data, such as a step count.

Correct way to use delaying method in android

I'm trying to create an adndroid application which will shows a distance from moment when I started the app.
I'm using Location Manager and this is my idea:
Check coordinates.
(2 sec delay)
Chceck coordinates2
If coordinates2 != coordinates then calculate distance between them and add it to double distance.
Repeat
public void onLocationChanged(final Location location) {
sz = location.getLatitude();
dl = location.getLongitude();
String dls = String.valueOf(dl);
String szs = String.valueOf(sz);
aktualna_dlg.setText(dls);
aktualna_szg.setText(szs);
lat1 = location.getLatitude();
lon1 = location.getLongitude();
int secs = 2; // Delay in seconds
Utils.delay(secs, new Utils.DelayCallback() {
#Override
public void afterDelay() {
lat2 = location.getLatitude();
lon2 = location.getLongitude();
}
});
if (lat1!=lat2 || lon1!=lon2){
distance += DistanceCalculator.distance(lat1,lon1,lat2,lon2, "K");
}
if(distance<1000){
String distance_s = String.valueOf(distance*1000);
distance_tv.setText(distance_s + " m");
}
else if(distance>=1000){
String distance_s = String.valueOf(distance/1000);
distance_tv.setText(distance_s + " km");
}
}
But when I compile the app and catch GPS, I'm getting a distance ~ 6km.
What am I doing wrong?
I'm not sure what utils.delay does, since it isn't part of the framework. But assuming it works correctly- it happens on a delay. That means lat2 and lon2 won't update until its called. All of the code that uses those variables has to happen in afterDelay, they aren't valid until then.
Eliminate the delays and simply compare the argument to onLocationChanged with the argument from the last time it was called. As for why you're recording a huge distance traveled...
Use a high-pass filter
How far do you expect to move in 2 seconds? Probably less than your GPS receiver's accuracy, unless you're in an aircraft. Most of your "movement" is actually just random noise.
In one of my apps, I save the starting GPS location as a point of reference. On each call to onLocationChanged, I compute the distance between the current location and the point of reference. If the distance is larger than some high-pass value, I add the distance to the running total and set the new location as the point of reference. I use 100 meters as my high-pass value because I've seen a few devices with 100 meter accuracy or worse around tall buildings.

How to set a speed limit for a speedometer Android app?

For my regions science fair, I am making a speedometer app for Android and I want to set a speed limit. Here is the code I'm having trouble with:
public void onLocationChanged(Location location) {
TextView txt = (TextView) this.findViewById(R.id.textView1);
if (location==null)
{
txt.setText("-.- km/h");
}
else if (location == 1.50)
{
txt.setText("Warning");
}
else
{
float nCurrentSpeed = location.getSpeed();
txt.setText(nCurrentSpeed*3.6 + " km/h");
}
}
I want to make it so that when the speed 1.50 km/h it changes the text to "Warning". I keep getting Incompatible operand types Location and double. I have tried doing this
else if (nCurrentSpeed == 1.50)
{
txt.setText("Warning");
}
But it still gives me the same error, but modified Incompatible operand types float and double. Is there any tips on how to solve this or how to create a speed limit for a speedometer?
The location object is not just a primitive object, but its contents is not know here.
However based upon later code, you are showing that it has
location.getSpeed()
so change your code to
else if (location.getSpeed() == 1.50)
I would also suggest that you use >= 1.5
shouldn't it just be something like
public void onLocationChanged(Location location) {
TextView txt = (TextView) this.findViewById(R.id.textView1);
if (location==null)
{
txt.setText("-.- km/h");
}
else if (location.getSpeed() >= 1.50f)
{
txt.setText("Warning");
}
else
{
float nCurrentSpeed = location.getSpeed();
txt.setText(nCurrentSpeed*3.6 + " km/h");
}
}
i.e. you need to compare location.getSpeed() with 1.5, not the whole location Object

Find average location and remove other locations:

Let's say that I am collecting users location data every 20 minutes. At the end of the day I have an ArrayList of objects that contain lat and long fields and other data.
I have two problems that I am facing and trying to figure out:
Some of the locations are taken from inside of a building so they are not very accurate and could be spread around the actual location where the user was at the time.
Some of the locations are taken at different times but from the same location, as the user didn't moved.
What I want to achieve is to find all the locations that are near one another: lets say 70 meters, find the average location of all those locations and replace them only with this one average location.
So I am coming to the two important questions:
What would be the best way to find all near locations < 70 meter distance (Take in mind that the array contains valid changes in location. So I have to find the groups of near ones and leave the others intact).
Is there a method or a way to find the average location of many near ones?
Regarding near positions I previously answered a similar question here: Android Maps v2 - animate camera to include most markers
Specifically I think you would be able to use this piece of code:
private List<Marker> getSurroundingMarkers(List<Marker> markers,
LatLng origin, int maxDistanceMeters) {
List<Marker> surroundingMarkers = surroundingMarkers = new ArrayList<Marker>();
if (markers == null) return surroundingMarkers ;
for (Marker marker : markers) {
double dist = distBetween(origin, marker.getPosition());
if (dist < maxDistanceMeters) {
surroundingMarkers.add(marker);
}
}
return surroundingMarkers;
}
private float distBetween(LatLng pos1, LatLng pos2) {
return distBetween(pos1.latitude, pos1.longitude, pos2.latitude,
pos2.longitude);
}
/** distance in meters **/
private float distBetween(double lat1, double lng1, double lat2, double lng2) {
double earthRadius = 3958.75;
double dLat = Math.toRadians(lat2 - lat1);
double dLng = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2)
* Math.sin(dLng / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double dist = earthRadius * c;
int meterConversion = 1609;
return (float) (dist * meterConversion);
}
Possibly rewriting the Marker part to use LatLng instead.
regarding the averaging, it should be a simple matter of (say you have n LatLng's):
lat_avg = (lat1+lat2+...+latn)/n
lng_avg = (lng1+lng2+...+lngn)/n
latlng_avg = new LatLng(lat_avg, lat_lng)
I' not sure how you're getting the user's location - whether your using a traditional LocationManager or play services. I've always modeled my location getting on this approach documented on the android developers blog. If you're flexible in switching between location gathering methods, whether the person is inside or outside should not matter that much. You should be getting the best possible location at any given time.
Assuming you're getting locations on a sufficient time schedule (I don't know when you're checking for updates but since you want everything inside a 70m radius I'm assuming its on a time schedule and not distance change) the basic way to find the average point is:
(1) Convert each lat/long pair into a unit-length 3D vector.
(2) Sum each of those vectors
(3) Normalise the resulting vector
(4) Convert back to spherical coordinates
That approach is documented here as well as in a much earlier SO post on calculating the average of a set of angles
The example code is pretty easy to follow - just plug in the lat long values you get from your location grab and you should be ok.
Well for markers that come from the same location I have created the following method:
public ArrayList<MyLocation> removeSameLocationMarkers(List<ParseObject> objects, int maxDistanceMeters)
{
boolean isLocationExist;
ArrayList<MyLocation> acceptedLocations = new ArrayList<MyLocation>();
if (objects == null) return acceptedLocations;
for (ParseObject location1 : objects)
{
isLocationExist = false;
for (MyLocation location2 : acceptedLocations)
{
if (!location1.equals(location2))
{
float distance = distBetween(location1.getDouble("latitude"), location1.getDouble("longitude"), location2.getLatitude(), location2.getLongitude());
if (distance < maxDistanceMeters)
{
location2.addTimeToLocation(location1.getString("time"));
isLocationExist = true;
}
}
}
if (!isLocationExist)
{
Location newLocation = new Location("");
newLocation.setLatitude(location1.getDouble("latitude"));
newLocation.setLongitude(location1.getDouble("longitude"));
String provider = location1.getString("provider");
if (provider != null)
{
newLocation.setProvider(provider);
}
MyLocation newMyLocation = new MyLocation(newLocation);
newMyLocation.addTimeToLocation(location1.getString("time"));
acceptedLocations.add(newMyLocation);
}
}
return acceptedLocations;
}

Unwanted result by locations calculations

In my application I generate a location every 5 minutes and send it to the db if it fits my standards.
One of the standards is that the new location that generated is not equal to the old one.
The following condition should cover it (and some other things) but in real time it doesn't.
if(newLocation != null)
{
if(location != null)
{
if(location.getAccuracy() > newLocation.getAccuracy() + 100)
sendTask();
else
if(newLocation.distanceTo(location) > 1800)
sendTask();
}
else
sendTask();
}
The send task method means, send to db. If the newLocation and the Location has the same coordinates the sendTask method shouldn't be exceuted and it is!
Why?

Categories