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.
Related
I have written a Java API, which consumes another API, which is a list of users, with the following properties in JSON format. Users have firstname, lastname, IP address, email, and location coordinates of latitude and longitude.
The Java API written is supposed to get all the users who live in London and/or live in a 50 mile radius.
Mine doesn't because I can't figure out the formula needed to check for the users who live in London, or within a 50 mile radius.
Here is my Java API:
package com.company;
import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main
{
public static void main(String[] args)
{
// Using java.net.http.HttpClient
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://bpdts-test-app.herokuapp.com/city/London/users")).build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(Main::parse)
.join();
}
// Parse the returned JSON data
public static String parse(String responseBody)
{
System.out.println("People who live in London");
JSONArray usersLondon = new JSONArray((responseBody));
for (int i = 0; i < usersLondon.length(); i++)
{
JSONObject userLondon = usersLondon.getJSONObject(i);
int id = userLondon.getInt("id");
String first_name = userLondon.getString("first_name");
String last_name = userLondon.getString("last_name");
String email = userLondon.getString("email");
String ip_address = userLondon.getString("ip_address");
int latitude = userLondon.getInt("latitude");
int longitude = userLondon.getInt("longitude");
System.out.println("id: " + id + " " + "first name: " + first_name + " " + "last name: " + last_name + " " + "email: " + email + " "
+ "IP Address: " + ip_address + " " + "latitude: " + latitude + " " + "longtitude: " + longitude);
}
return null;
}
}
So it returns just 6 users, which I know is incorrect. What would the Mathematical formula be, to test whether the coordinates for users in the API are are living in London, and/or living within a 50 mile radius of London?
Appreciate your help.
The way to properly and accurately calculate distance between two points on the earth is with the Inverse/Forward formulas (written in 1975; as far as I know, no one has managed to come up with better formulas since then).
Since your distance is short, you might be able to get away with using sphere-based calculations, but if you want to be truly correct, Inverse/Forward is the way to do it. This is because our planet is not a perfect sphere. The distance from pole to pole is slightly smaller than the equatorial diameter, so the planet is a “squashed” sphere, formally known as an oblate spheroid. This difference is enough to matter when navigating and calculating distances, unless those distances are very small.
Translating the original Inverse/Forward Fortran code is possible (I’ve done it for more than one project), but it’s likely to be easier to make use of the free libraries which do it, such as this one.
The numbers that describe the difference between the planet’s polar and equatorial diameters are known as a “reference ellipsoid.” The one most commonly used is the WGS84 ellipsoid, which is accurate enough for most purposes. It just so happens that the above class has a convenient static WGS84 instance defined.
Before you can calculate anything, first you need to define what “within a 50 mile radius” means. Within 50 miles of what, exactly? The center of London?
Wikipedia says that London is at 51°30′26″N 0°7′39″W, which seems like as reasonable a starting point as any for distance calculations.
Traditionally, latitude and longitude are expressed in decimal degrees when doing calculations, which means a double precision floating point number (that is, a Java double) whose integer part is the number of degrees, and whose decimal part is the minutes and seconds. By convention, positive values are north or east, while negative values are south or west.
Thus, 50°30′N 99°15′W is a latitude of 50.5 and a longitude of -99.25, in decimal degrees.
Your code is obtaining latitude and longitude as ints. I very much doubt that your locations are integer numbers of degrees, since very few locations are at, for example, exactly 49° north. Only you know how those int values are intended to represent minutes and seconds. It is up to you to convert those values to decimal degrees.
Once you have London’s location in decimal degrees, and you know how to convert your user locations into decimal degrees, you can invoke the Inverse method of the Geodesic class I linked to above:
public static List<User> parse(String responseBody)
{
List<User> qualifyingUsers = new ArrayList<>();
// 51 deg 30 min 26 sec N
double londonLat = 51 + (30 / 60.0) + (26 / 60.0 / 60.0);
// 0 deg 7 min 39 sec W
double londonLon = 0 - (7 / 60.0) - (39 / 60.0 / 60.0);
for (int i = 0; i < usersLondon.length(); i++)
{
JSONObject userLondon = usersLondon.getJSONObject(i);
// ...
int latitude = userLondon.getInt("latitude");
int longitude = userLondon.getInt("longitude");
double userLat = convertToDecimalDegrees(latitude);
double userLon = convertToDecimalDegrees(longitude);
GeodesicData result =
Geodesic.WGS84.Inverse(londonLat, londonLon, userLat, userLon);
double distanceInMeters = result.s12;
double distanceInMiles = distanceInMeters / 1609.34;
if (distanceInMiles <= 50)
{
User user = new User();
user.setId(id);
user.setFirstName(first_name);
// etc.
qualifyingUsers.add(user);
}
}
return qualifyingUsers;
}
I'm accessing this API that gives me global weather:
https://callforcode.weather.com/doc/v3-global-weather-notification-headlines/
However, it takes lat/lng as an input parameter, and I need the data for the entire world.
I figure I can loop through every latitude longitude, every 2 latitudes and 2 longitude degrees, giving me a point on the world, every ~120 miles across and roughly 100 degrees north/south, which should give me all the data in 16,200 API calls ((360/2) * (180/2)).
How can I do this effectively in Java?
I'd conceived something like this; but is there a better way of doing this?
for(int i = 0; i < 360; i+2){
var la = i;
for(int x = 0 x < 180; x+2) {
var ln = x;
//call api with lat = i, lng = x;
}
}
It's somewhat of a paradigm shift, but I would NOT use a nested for-loop for this problem. In many situations where you are looking at iterating over an entire result set, it is often possible to trim the coverage dramatically without losing much or any effectiveness. Caching, trimming, prioritizing... these are the things you need: not a for-loop.
Cut sections entirely - maybe you can ignore ocean, maybe you can ignore Antartica and the North Pole (since people there have better ways of checking weather anyway)
Change your search frequency based on population density. Maybe northern Canada doesn't need to be checked as thoroughly as Los Angeles or Chicago.
Rely on caching in low-usage areas - presumably you can track what areas are actually being used and can then more frequently refresh those sections.
So what you end up with is some sort of weighted caching system that takes into account population density, usage patterns, and other priorities to determine what latitude/longitude coordinates to check and how frequently.
High-level code might look something like this:
void executeUpdateSweep(List<CoordinateCacheItem> cacheItems)
{
for(CoordinateCacheItem item : cacheItems)
{
if(shouldRefreshCache(item))
{
//call api with lat = item.y , lng = item.x
}
}
}
boolean shouldRefreshCache(item)
{
long ageWeight = calculateAgeWeight(item);//how long since last update?
long basePopulationWeight = item.getBasePopulationWeight();//how many people (users and non-users) live here?
long usageWeight = calculateUsageWeight(item);//how much is this item requested?
return ageWeight + basePopulationWeight + usageWeight > someArbitraryThreshold;
}
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.
I'm using Mapbox Android SDK
compile ('com.mapbox.mapboxsdk:mapbox-android-sdk:3.0.0#aar').
I asked the similar question before at here, but still have some problem. I don't know how to implement when I get currentRoute. my code is as below:
private Waypoint lastCorrectWayPoint;
private boolean checkOffRoute(Waypoint target) {
boolean isOffRoute = false;
if(currentRoute != null){
if (currentRoute.isOffRoute(target)) {
showMessage("You are off-route, recalculating...");
isOffRoute = true;
lastCorrectWayPoint = null;
//would recalculating route.
} else {
lastCorrectWayPoint = target;
String direction = "Turn right"; //The message what should I prompt to user
double distance = 0.0;//The distance which from target to next step.
int duration = 0;//The time which from target to next step.
String desc = "Turn right to xx street.";
//Implement logic to get them here.
showMessage("direction:" + direction + ", distance:" + distance + ", duration:" + duration + ", desc:" + desc);
}
}
checkOffRoute() would be called within onLocationChanged(). I think MapBox SDK should provide these data to developer instead of developer implement it by himself. or if I miss something important information in SDK? Any suggestion?
hope your app is coming along well. I see your trying to get direction, distance, and duration to next step. I'll try and answer this as best I can while keeping it short.
Direction
First when you request the route you need to include a couple lines:
MapboxDirections client = new MapboxDirections.Builder()
.setAccessToken(getString(R.string.accessToken))
.setOrigin(origin)
.setDestination(destination)
.setProfile(DirectionsCriteria.PROFILE_DRIVING)
.setAlternatives(true) // Gives you more then one route if alternative routes available
.setSteps(true) // Gives you the steps for each direction
.setInstructions(true) // Gives human readable instructions
.build();
Once you receive the response you can do something along the lines of
response.body().getRoutes().get(0).getSteps().get(0).getDirection()
which will give you the approximate cardinal direction of travel following the maneuver. Typically one of the following: 'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', or 'NW'. This specific line gives you the first route in the list (also typically the shortest and best choice route) and the first step. To change through the steps you simply change the integer value of the second .get(int) to whatever step you need.
Duration and Distance
Same as above but instead of .getDirection() you use:
response.body().getRoutes().get(0).getSteps().get(0).getDuration()
and
response.body().getRoutes().get(0).getSteps().get(0).getDistance()
respectively. I hope this at least helps to guide you in the right direction while creating your app.
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);