2

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:

  1. 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.

  2. 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:

  1. 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).

  2. Is there a method or a way to find the average location of many near ones?

halfer
  • 19,824
  • 17
  • 99
  • 186
Emil Adz
  • 40,709
  • 36
  • 140
  • 187

3 Answers3

4

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)
Community
  • 1
  • 1
cYrixmorten
  • 7,110
  • 3
  • 25
  • 33
  • Thanks for the code, man. I will check it and let you know as well as your previous question. Regarding the second part, Actually I want to save positions even if the user haven't changed place because I want to determine for how long he stayed at the same place and basically I'm saving the time the location was saved as well to do that. – Emil Adz Oct 29 '13 at 19:34
  • Fair enough, but I would argue that you could accomplish the same, saving a timestamp when movement has been detected and when the counter reaches 0. Though, it is just an optimisation, using GPS is rather expensive in energy. – cYrixmorten Oct 29 '13 at 19:37
  • Well Maybe you are right, I will look at your second class as well. Thanks for your help I will try to implement it and let you know. – Emil Adz Oct 29 '13 at 19:40
  • No problem :) Missed the fact that it was every 20mins, thought it was all the time. Deleting the accelerometer part and adding a small edit about the averaging part. – cYrixmorten Oct 29 '13 at 19:43
  • You are saying that if this happens every 20 minutes you don't see a reason to check for movement before saving the location? – Emil Adz Oct 29 '13 at 20:48
  • Yes. What I mean is that if you collect a position that infrequently, then it would be overkill to do more optimization in my opinion. Though, it would be simple to look up the previously saved location, calculate the distance to current location and then choose whether to save or discard it. – cYrixmorten Oct 29 '13 at 21:08
  • Yes, but in this case if the user didn't moved and I didn't saved the marker. When getting this data from server I won't have the time stamp of this marker and so I won't know for how much time user stayed at the same location. – Emil Adz Oct 29 '13 at 21:11
  • In that case I would (assuming the position is discarded) I would update the timestamp of the last saved position on the server. – cYrixmorten Oct 29 '13 at 21:15
  • If you would update the time stamp of the position on the servers, then you will loose the starting time of this position (meaning when the user arrived to this position). And I am not really you can find average the way you presented. – Emil Adz Oct 29 '13 at 23:31
  • what is the getHydrantsLoadradius() method does? – Emil Adz Oct 30 '13 at 01:58
1

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.

Community
  • 1
  • 1
Rarw
  • 7,645
  • 3
  • 28
  • 46
  • I am using Fedor's implementation: http://stackoverflow.com/questions/3145089/what-is-the-simplest-and-most-robust-way-to-get-the-users-current-location-in-a?answertab=active#tab-top to find the current location. It's using a LocationManager and two LocationListeners for both the GPS and the Network providers. And I'm looking for the location every X minutes with can be cofigured by the user.I will look into this code of Location avarage, thank you. – Emil Adz Oct 29 '13 at 20:53
0

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;
}
Emil Adz
  • 40,709
  • 36
  • 140
  • 187