I have to get distance from different markers on the map to the current location of the device and the pick up the shortest one. I have the lat and long for the markers and
If you want to find the shortest one not list the closest and you want the process to scale to a large amount of locations, you can do some filtering before you calculate distances and you can simplify the formula to speed it up as you don't care about actual distances (i.e. remove the multiplication by the radius of the earth).
Filtering algorithm, looping through each location :
You can further help the algorithm by feeding it with what might be close locations first. For example if you know one of the points is in the same country or state.
Here is some Python code to do that, use it as pseudocode for your solution :
locations = {
'Bangalore' : (12.971599, 77.594563),
'Delhi' : (28.635308, 77.224960),
'Mumbai' : (19.075984, 72.877656),
'Chennai' : (13.052414, 80.250825),
'Kolkata' : (22.572646, 88.363895)
}
from math import sin, cos, atan2, sqrt
EARTH_RADIUS = 6373 # km
def distance(a, b): # pass tuples
(lat1, lon1) = a
(lat2, lon2) = b
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))**2 + cos(lat1) * cos(lat2) * (sin(dlon/2))**2
c = 2 * atan2( sqrt(a), sqrt(1-a) )
return EARTH_RADIUS * c
current = (17.385044, 78.486671) # current lat & lng
closest = None
closest_name = None
for name, cordinates in locations.iteritems():
d = distance(current, cordinates)
if closest is None or d < closest:
closest = d
closest_name = name
print "~%dkm (%s)" % (distance(current, cordinates), name)
print "\nClosest location is %s, %d km away." % (closest_name, closest)
Output :
~5700km (Kolkata)
~13219km (Chennai)
~12159km (Bangalore)
~7928km (Delhi)
~10921km (Mumbai)
Closest location is Kolkata, 5700 km away.
Although there has already been posted some answer, I thought I would present my implementation in java. This has been used with 4000+ markers wrapped in an AsyncTask and has been working with no problems.
First, the logic to calculate distance (assuming you only have the markers and not Location objects, as those gives the possibility to do loc1.distanceTo(loc2)):
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);
}
Next, the code for selecting the nearest marker:
private Marker getNearestMarker(List<Marker> markers,
LatLng origin) {
Marker nearestMarker = null;
double lowestDistance = Double.MAX_VALUE;
if (markers != null) {
for (Marker marker : markers) {
double dist = distBetween(origin, marker.getPosition());
if (dist < lowestDistance) {
nearestMarker = marker;
lowestDistance = dist;
}
}
}
return nearestMarker;
}
Perhaps not relevant for your use case but I use the algorithm to select the nearest markers based on a predefined distance. This way I weed out a lot of unnecessary markers:
private List<Marker> getSurroundingMarkers(List<Marker> markers,
LatLng origin, int maxDistanceMeters) {
List<Marker> surroundingMarkers = null;
if (markers != null) {
surroundingMarkers = new ArrayList<Marker>();
for (Marker marker : markers) {
double dist = distBetween(origin, marker.getPosition());
if (dist < maxDistanceMeters) {
surroundingMarkers.add(marker);
}
}
}
return surroundingMarkers;
}
Hope this helps you
I thought it should not be too difficult to extend my KDTree (see my other answer) also to a 3 dimensional version, and here is the result. But as I do not use this version myself so far, take it with care. I added a unit-test, which shows that it works at least for your example.
/** 3 dimensional implementation of a KDTree for LatLng coordinates. */
public class KDTree {
private XYZComparator xComparator = new XYZComparator(0);
private XYZComparator yComparator = new XYZComparator(1);
private XYZComparator zComparator = new XYZComparator(2);
private XYZComparator[] comparators = { xComparator, yComparator,
zComparator };
/**
* Create a KDTree from a list of lat/lon coordinates. Returns the root-node
* of the tree.
*/
public KDTNode createTree(List<LatLng> recList) {
List<XYZ> xyzList = convertTo3Dimensions(recList);
return createTreeRecursive(0, xyzList);
}
/**
* Traverse the tree and find the point nearest to wp.
*/
static public LatLng findNearestWp(KDTNode root, LatLng wp) {
KDTResult result = new KDTResult();
XYZ xyz = convertTo3Dimensions(wp);
findNearestWpRecursive(root, xyz, result);
return result.nearestWp;
}
/** Convert lat/lon coordinates into a 3 dimensional xyz system. */
private static XYZ convertTo3Dimensions(LatLng wp) {
// See e.g.
// http://stackoverflow.com/questions/8981943/lat-long-to-x-y-z-position-in-js-not-working
double cosLat = Math.cos(wp.latitude * Math.PI / 180.0);
double sinLat = Math.sin(wp.latitude * Math.PI / 180.0);
double cosLon = Math.cos(wp.longitude * Math.PI / 180.0);
double sinLon = Math.sin(wp.longitude * Math.PI / 180.0);
double rad = 6378137.0;
double f = 1.0 / 298.257224;
double C = 1.0 / Math.sqrt(cosLat * cosLat + (1 - f) * (1 - f) * sinLat
* sinLat);
double S = (1.0 - f) * (1.0 - f) * C;
XYZ result = new XYZ();
result.x = (rad * C) * cosLat * cosLon;
result.y = (rad * C) * cosLat * sinLon;
result.z = (rad * S) * sinLat;
result.wp = wp;
return result;
}
private List<XYZ> convertTo3Dimensions(List<LatLng> recList) {
List<XYZ> result = new ArrayList<KDTree.XYZ>();
for (LatLng latLng : recList) {
XYZ xyz = convertTo3Dimensions(latLng);
result.add(xyz);
}
return result;
}
private static void findNearestWpRecursive(KDTNode node, XYZ wp,
KDTResult result) {
/* If a leaf node, calculate distance and return. */
if (node.isLeaf) {
double xDiff = node.xyz.x - wp.x;
double yDiff = node.xyz.y - wp.y;
double zDiff = node.xyz.z - wp.z;
double squareDist = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
// Replace a previously found nearestDest only if the new one is
// nearer.
if (result.nearestWp == null || result.squareDistance > squareDist) {
result.nearestWp = node.xyz.wp;
result.squareDistance = squareDist;
}
return;
}
int devidedByDimension = node.depth % 3;
boolean goLeft;
/* Check whether left or right is more promising. */
if (devidedByDimension == 0) {
goLeft = wp.x < node.splitValue;
} else if (devidedByDimension == 1) {
goLeft = wp.y < node.splitValue;
} else {
goLeft = wp.z < node.splitValue;
}
KDTNode child = goLeft ? node.left : node.right;
findNearestWpRecursive(child, wp, result);
/*
* Check whether result needs to be checked also against the less
* promising side.
*/
if (result.squareDistance > node.minSquareDistance) {
KDTNode otherChild = goLeft ? node.right : node.left;
findNearestWpRecursive(otherChild, wp, result);
}
}
private KDTNode createTreeRecursive(int depth, List<XYZ> recList) {
KDTNode node = new KDTNode();
node.depth = depth;
if (recList.size() == 1) {
// Leafnode found
node.isLeaf = true;
node.xyz = recList.get(0);
return node;
}
int dimension = node.depth % 3;
sortWayPointListByDimension(recList, dimension);
List<XYZ> leftList = getHalfOf(recList, true);
List<XYZ> rightList = getHalfOf(recList, false);
// Get split point and distance to last left and first right point.
XYZ lastLeft = leftList.get(leftList.size() - 1);
XYZ firstRight = rightList.get(0);
double minDistanceToSplitValue;
double splitValue;
if (dimension == 0) {
minDistanceToSplitValue = (firstRight.x - lastLeft.x) / 2;
splitValue = lastLeft.x + Math.abs(minDistanceToSplitValue);
} else if (dimension == 1) {
minDistanceToSplitValue = (firstRight.y - lastLeft.y) / 2;
splitValue = lastLeft.y + Math.abs(minDistanceToSplitValue);
} else {
minDistanceToSplitValue = (firstRight.z - lastLeft.z) / 2;
splitValue = lastLeft.z + Math.abs(minDistanceToSplitValue);
}
node.splitValue = splitValue;
node.minSquareDistance = minDistanceToSplitValue
* minDistanceToSplitValue;
/** Call next level */
depth++;
node.left = createTreeRecursive(depth, leftList);
node.right = createTreeRecursive(depth, rightList);
return node;
}
/**
* Return a sublist representing the left or right half of a List. Size of
* recList must be at least 2 !
*
* IMPORTANT !!!!! Note: The original list must not be modified after
* extracting this sublist, as the returned subList is still backed by the
* original list.
*/
List<XYZ> getHalfOf(List<XYZ> xyzList, boolean leftHalf) {
int mid = xyzList.size() / 2;
if (leftHalf) {
return xyzList.subList(0, mid);
} else {
return xyzList.subList(mid, xyzList.size());
}
}
private void sortWayPointListByDimension(List<XYZ> wayPointList, int sortBy) {
XYZComparator comparator = comparators[sortBy];
Collections.sort(wayPointList, comparator);
}
class XYZComparator implements Comparator<XYZ> {
private int sortBy;
public XYZComparator(int sortBy) {
this.sortBy = sortBy;
}
@Override
public int compare(XYZ lhs, XYZ rhs) {
double diff;
if (sortBy == 0) {
diff = lhs.x - rhs.x;
} else if (sortBy == 1) {
diff = lhs.y - rhs.y;
} else {
diff = lhs.z - rhs.z;
}
if (diff > 0) {
return 1;
} else if (diff < 0) {
return -1;
} else {
return 0;
}
}
}
/** 3 Dimensional coordinates of a waypoint. */
static class XYZ {
double x;
double y;
double z;
// Keep also the original waypoint
LatLng wp;
}
/** Node of the KDTree */
public static class KDTNode {
KDTNode left;
KDTNode right;
boolean isLeaf;
/** latitude or longitude of the nodes division line. */
double splitValue;
/** Distance between division line and first point. */
double minSquareDistance;
/**
* Depth of the node in the tree. Depth 0,3,6.. devides the tree in the
* x-axis, depth 1,4,7,.. devides the tree in the y-axis and depth
* 2,5,8... devides the tree in the z axis.
*/
int depth;
/** The Waypoint in case the node is a leaf node. */
XYZ xyz;
}
/** Holds the result of a tree traversal. */
static class KDTResult {
LatLng nearestWp;
// We use the square of the distance to avoid square-root operations.
double squareDistance;
}
}
And here is the unit test:
public void testSOExample() {
KDTree tree = new KDTree();
LatLng Bangalore = new LatLng(12.971599, 77.594563);
LatLng Delhi = new LatLng(28.635308, 77.224960);
LatLng Mumbai = new LatLng(19.075984, 72.877656);
LatLng Chennai = new LatLng(13.052414, 80.250825);
LatLng Kolkata = new LatLng(22.572646, 88.363895);
List<LatLng> cities = Arrays.asList(new LatLng[] { Bangalore, Delhi,
Mumbai, Chennai, Kolkata });
KDTree.KDTNode root = tree.createTree(cities);
LatLng Hyderabad = new LatLng(17.385044, 78.486671);
LatLng nearestWp = tree.findNearestWp(root, Hyderabad);
assertEquals(nearestWp, Bangalore);
}