0

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.

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
  • Have you checcked out "haversine" formula ? – JPX Jun 13 '20 at 23:28
  • I'll check this out, thanks for your response. – MindGames2015 Jun 14 '20 at 12:53
  • Does this answer your question? [How do I create a circle with latitude, longitude and radius with GeoTools?](https://stackoverflow.com/questions/36481651/how-do-i-create-a-circle-with-latitude-longitude-and-radius-with-geotools) – Ian Turton Jun 15 '20 at 07:26

1 Answers1

1

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;
}
VGR
  • 40,506
  • 4
  • 48
  • 63
  • Thanks very much for your help, I now have a clearer understanding of the way it works. I'm working on it now, thanks. – MindGames2015 Jun 14 '20 at 12:51
  • Oops, forgot that londonLon needs to be negative since it’s in the western hemisphere. Corrected. – VGR Jun 14 '20 at 16:00
  • Hi, VGR I'm almost there with my application, but the double userLat = convertToDecimalDegrees(latitude); is a method call to convertToDecimalDegrees. The JSON format of latitude and longitude is "latitude": 34.003135, "longitude": -117.7228641, I'm struggling to find the formula to convert straight to decimal degrees, without firstly converting to degrees, minutes and seconds, is there a way to do this? Thanks for any help – MindGames2015 Jun 16 '20 at 07:42
  • Those numbers are already decimal degrees. You need to change `int latitude = userLondon.getInt("latitude");` to `double latitude = userLondon.getDouble("latitude");`, and do the same for longitude. Then you won’t need a convertToDecimalDegrees method at all. – VGR Jun 16 '20 at 11:36
  • Hi, sorry to keep picking your brains, so I have changed it to this: double userLat = latitude; double userLon = longitude; GeodesicData result = Geodesic.WGS84.Inverse(londonLat, londonLon, userLat, userLon); I have User class with all of the User properties fields. It has no errors, when I run, it produces no output, I have the getters and setters where it is testing for if distanceInMiles <= 50, as user.setLatitude(latitude); user.getLatitude();, is this correct? Thanks for your help. – MindGames2015 Jun 16 '20 at 12:48
  • It looks correct. Make sure all latitude and longitude values are of type `double`. You may want to edit your question and add your new code. – VGR Jun 16 '20 at 13:14
  • Thanks VGR, I've sorted those problems out, I've posted a Java problem, I'm having, with code and error message. Thanks again. – MindGames2015 Jun 16 '20 at 14:50