If I draw a shape with polygon on Google Maps v2, is there a way to find out if my current location is inside the shape?
please write me a clear code thanks
Draw a rectangle on map with points:
List<LatLng> points = new ArrayList<>();
points.add(new LatLng(lat1, lng1));
points.add(new LatLng(lat2, lng2));
points.add(new LatLng(lat3, lng3));
points.add(new LatLng(lat4, lng4));
Polygon polygon = myMap.addPolygon(new PolygonOptions().addAll(points));
Use android-maps-utils library to check to see is polygon contains your current location point:
boolean contain = PolyUtil.containsLocation(currentLocationLatLng, points, true);
You could create a LatLngBounds based on the specifications of your rectangle and then use the contains method to check whether the current location resides within it.
I split the PolyUtil from the Google Maps Android API Utility Library to one class.
Than just call like follow.
ArrayList<LatLng> polygon = new ArrayList<LatLng>();
LatLng myLocation = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
boolean inPolygon = PolyUtil.containsLocation(myLocation, polygon, false);
And include the PolyUtil class in your code.
import static java.lang.Math.PI;
import static java.lang.Math.log;
import static java.lang.Math.sin;
import static java.lang.Math.tan;
import static java.lang.Math.toRadians;
public class PolyUtil {
/**
* Returns tan(latitude-at-lng3) on the great circle (lat1, lng1) to (lat2, lng2). lng1==0.
* See http://williams.best.vwh.net/avform.htm .
*/
private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) {
return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2);
}
/**
* Wraps the given value into the inclusive-exclusive interval between min and max.
* #param n The value to wrap.
* #param min The minimum.
* #param max The maximum.
*/
static double wrap(double n, double min, double max) {
return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
}
/**
* Returns the non-negative remainder of x / m.
* #param x The operand.
* #param m The modulus.
*/
static double mod(double x, double m) {
return ((x % m) + m) % m;
}
/**
* Returns mercator Y corresponding to latitude.
* See http://en.wikipedia.org/wiki/Mercator_projection .
*/
static double mercator(double lat) {
return log(tan(lat * 0.5 + PI/4));
}
/**
* Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0.
*/
private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) {
return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2;
}
public static boolean containsLocation(LatLng point, List<LatLng> polygon, boolean geodesic) {
return containsLocation(point.latitude, point.longitude, polygon, geodesic);
}
/**
* Computes whether the given point lies inside the specified polygon.
* The polygon is always considered closed, regardless of whether the last point equals
* the first or not.
* Inside is defined as not containing the South Pole -- the South Pole is always outside.
* The polygon is formed of great circle segments if geodesic is true, and of rhumb
* (loxodromic) segments otherwise.
*/
public static boolean containsLocation(double latitude, double longitude, List<LatLng> polygon, boolean geodesic) {
final int size = polygon.size();
if (size == 0) {
return false;
}
double lat3 = toRadians(latitude);
double lng3 = toRadians(longitude);
LatLng prev = polygon.get(size - 1);
double lat1 = toRadians(prev.latitude);
double lng1 = toRadians(prev.longitude);
int nIntersect = 0;
for (LatLng point2 : polygon) {
double dLng3 = wrap(lng3 - lng1, -PI, PI);
// Special case: point equal to vertex is inside.
if (lat3 == lat1 && dLng3 == 0) {
return true;
}
double lat2 = toRadians(point2.latitude);
double lng2 = toRadians(point2.longitude);
// Offset longitudes by -lng1.
if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) {
++nIntersect;
}
lat1 = lat2;
lng1 = lng2;
}
return (nIntersect & 1) != 0;
}
/**
* Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment
* (lat1, lng1) to (lat2, lng2).
* Longitudes are offset by -lng1; the implicit lng1 becomes 0.
*/
private static boolean intersects(double lat1, double lat2, double lng2,
double lat3, double lng3, boolean geodesic) {
// Both ends on the same side of lng3.
if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) {
return false;
}
// Point is South Pole.
if (lat3 <= -PI/2) {
return false;
}
// Any segment end is a pole.
if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) {
return false;
}
if (lng2 <= -PI) {
return false;
}
double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;
// Northern hemisphere and point under lat-lng line.
if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) {
return false;
}
// Southern hemisphere and point above lat-lng line.
if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) {
return true;
}
// North Pole.
if (lat3 >= PI/2) {
return true;
}
// Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
// Compare through a strictly-increasing function (tan() or mercator()) as convenient.
return geodesic ?
tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) :
mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3);
}
}
Follow these -
https://developer.android.com/training/location/geofencing.html
https://developers.google.com/android/reference/com/google/android/gms/location/Geofence
These links may be what you are looking for.
Just tried Ray Casting algorithm which identifies point in polygon. This works perfect.
private boolean isPointInPolygon(LatLng tap, ArrayList<LatLng> vertices) {
int intersectCount = 0;
for (int j = 0; j < vertices.size() - 1; j++) {
if (rayCastIntersect(tap, vertices.get(j), vertices.get(j + 1))) {
intersectCount++;
}
}
return ((intersectCount % 2) == 1); // odd = inside, even = outside;
}
private boolean rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if ((aY > pY && bY > pY) || (aY < pY && bY < pY)
|| (aX < pX && bX < pX)) {
return false; // a and b can't both be above or below pt.y, and a or
// b must be east of pt.x
}
double m = (aY - bY) / (aX - bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
I would like to calculate the are of a polygon drawn in a map fragment for a college project.
This is how I draw my polygon.
#Override
public void onMapClick(LatLng point) {
//tvLocInfo.setText("New marker added#" + point.toString());
map.addMarker(new MarkerOptions().position(point).draggable(true).title(point.toString()));
markerClicked = false;
}
#Override
public void onMapLongClick(LatLng point) {
//tvLocInfo.setText("New marker added#" + point.toString());
map.clear();
}
#Override
public boolean onMarkerClick(Marker marker) {
if(markerClicked){
if(polygon != null){
polygon.remove();
polygon = null;
}
polygonOptions.add(marker.getPosition());
polygonOptions.strokeColor(Color.RED);
polygonOptions.fillColor(Color.BLUE);
polygon = map.addPolygon(polygonOptions);
//Area = google.maps.geometry.spherical.computeArea(polygon.getPath().getArray());
}else{
if(polygon != null){
polygon.remove();
polygon = null;
}
polygonOptions = new PolygonOptions().add(marker.getPosition());
markerClicked = true;
}
I have seen this code on how to calculate the area but I am unsure how to implement it in my application and calculate the area of my polygon.
I use this code to calculate an area of a GPS with Android:
private static final double EARTH_RADIUS = 6371000;// meters
public static double calculateAreaOfGPSPolygonOnEarthInSquareMeters(final List<Location> locations) {
return calculateAreaOfGPSPolygonOnSphereInSquareMeters(locations, EARTH_RADIUS);
}
private static double calculateAreaOfGPSPolygonOnSphereInSquareMeters(final List<Location> locations, final double radius) {
if (locations.size() < 3) {
return 0;
}
final double diameter = radius * 2;
final double circumference = diameter * Math.PI;
final List<Double> listY = new ArrayList<Double>();
final List<Double> listX = new ArrayList<Double>();
final List<Double> listArea = new ArrayList<Double>();
// calculate segment x and y in degrees for each point
final double latitudeRef = locations.get(0).getLatitude();
final double longitudeRef = locations.get(0).getLongitude();
for (int i = 1; i < locations.size(); i++) {
final double latitude = locations.get(i).getLatitude();
final double longitude = locations.get(i).getLongitude();
listY.add(calculateYSegment(latitudeRef, latitude, circumference));
Log.d(LOG_TAG, String.format("Y %s: %s", listY.size() - 1, listY.get(listY.size() - 1)));
listX.add(calculateXSegment(longitudeRef, longitude, latitude, circumference));
Log.d(LOG_TAG, String.format("X %s: %s", listX.size() - 1, listX.get(listX.size() - 1)));
}
// calculate areas for each triangle segment
for (int i = 1; i < listX.size(); i++) {
final double x1 = listX.get(i - 1);
final double y1 = listY.get(i - 1);
final double x2 = listX.get(i);
final double y2 = listY.get(i);
listArea.add(calculateAreaInSquareMeters(x1, x2, y1, y2));
Log.d(LOG_TAG, String.format("area %s: %s", listArea.size() - 1, listArea.get(listArea.size() - 1)));
}
// sum areas of all triangle segments
double areasSum = 0;
for (final Double area : listArea) {
areasSum = areasSum + area;
}
// get abolute value of area, it can't be negative
return Math.abs(areasSum);// Math.sqrt(areasSum * areasSum);
}
private static Double calculateAreaInSquareMeters(final double x1, final double x2, final double y1, final double y2) {
return (y1 * x2 - x1 * y2) / 2;
}
private static double calculateYSegment(final double latitudeRef, final double latitude, final double circumference) {
return (latitude - latitudeRef) * circumference / 360.0;
}
private static double calculateXSegment(final double longitudeRef, final double longitude, final double latitude,
final double circumference) {
return (longitude - longitudeRef) * circumference * Math.cos(Math.toRadians(latitude)) / 360.0;
}
I could also use the following polygon which is static if calculating the area of the drawn polygon is not possible.
Polygon UCCpolygon = map.addPolygon(new PolygonOptions()
.add(new LatLng(51.893728, -8.491865),
new LatLng(51.893550, -8.492479),
new LatLng(51.893216, -8.492224),
new LatLng(51.893404, -8.491598))
.strokeColor(Color.RED)
.fillColor(Color.BLUE));
Thanks for the help!
Sean
There's already a library for that.
import com.google.maps.android.SphericalUtil;
//...
List<LatLng> latLngs = new ArrayList<>();
latLngs.add(new LatLng(51.893728, -8.491865));
latLngs.add(new LatLng(51.893550, -8.492479));
latLngs.add(new LatLng(51.893216, -8.492224));
latLngs.add(new LatLng(51.893404, -8.491598));
Log.i(TAG, "computeArea " + SphericalUtil.computeArea(latLngs));
For me the output is computeArea 1920.8879882782069
If you want to use SphericalUtils code without any library, you can use following code. it's taken from opensource code from SphericalUtils.java and other class. I have taken this code and used it as i was using MapBox and MapBox does not have implemented the calculateArea function in Turf.
import java.util.List;
import pojo.LatLng;
import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.tan;
import static java.lang.Math.toRadians;
public class PolygonUtils {
/**
* The earth's radius, in meters.
* Mean radius as defined by IUGG.
*/
static final double EARTH_RADIUS = 6371009;
/**
* Returns the area of a closed path on Earth.
* #param path A closed path.
* #return The path's area in square meters.
*/
public static double computeArea(List<LatLng> path) {
return abs(computeSignedArea(path,EARTH_RADIUS));
}
/**
* Returns the signed area of a closed path on a sphere of given radius.
* The computed area uses the same units as the radius squared.
* Used by SphericalUtilTest.
*/
static double computeSignedArea(List<LatLng> path, double radius) {
int size = path.size();
if (size < 3) { return 0; }
double total = 0;
LatLng prev = path.get(size - 1);
double prevTanLat = tan((PI / 2 - toRadians(prev.getLatitude())) / 2);
double prevLng = toRadians(prev.getLongitude());
// For each edge, accumulate the signed area of the triangle formed by the North Pole
// and that edge ("polar triangle").
for (LatLng point : path) {
double tanLat = tan((PI / 2 - toRadians(point.getLatitude())) / 2);
double lng = toRadians(point.getLongitude());
total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
prevTanLat = tanLat;
prevLng = lng;
}
return total * (radius * radius);
}
/**
* Returns the signed area of a triangle which has North Pole as a vertex.
* Formula derived from "Area of a spherical triangle given two edges and the included angle"
* as per "Spherical Trigonometry" by Todhunter, page 71, section 103, point 2.
* See http://books.google.com/books?id=3uBHAAAAIAAJ&pg=PA71
* The arguments named "tan" are tan((pi/2 - latitude)/2).
*/
private static double polarTriangleArea(double tan1, double lng1, double tan2, double lng2) {
double deltaLng = lng1 - lng2;
double t = tan1 * tan2;
return 2 * atan2(t * sin(deltaLng), 1 + t * cos(deltaLng));
}
}
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 the current location lat and long can be fetched dynamically.
Suppose I have 5 markers on the map, Bangalore (Lat : 12.971599, Long : 77.594563), Delhi (Lat : 28.635308, Long : 77.224960), Mumbai (Lat : 19.075984, Long : 72.877656), Chennai (Lat : 13.052414, Long : 80.250825), Kolkata (Lat : 22.572646, Long : 88.363895).
Now suppose the user is standing somewhere near Hyderabad (Lat : 17.385044, Long : 78.486671). When the user clicks the button, the app should calculate distance from each marker and pick up and return the shortest one, that will be Bangalore here.
There is a way possible to do it with help of local databases. Can anyone help on that please.?
Can anyone suggest me a nice way to do this, or come up with a good code if you please can. Thanx in advance.
from your comment I see that you expect a maximum of 70-80 locations.
This is not much.
You can simply do a brute force search over all markers and take the minimum.
Iterate over all markers, and search min distance:
List<Marker> markers = createMarkers(); // returns an ArrayList<Markers> from your data source
int minIndex = -1;
double minDist = 1E38; // initialize with a huge value that will be overwritten
int size = markers.size();
for (int i = 0; i < size; i++) {
Marker marker = markers.get(i);
double curDistance = calcDistance(curLatitude, curLongitude, marker.latitude, marker.longitude);
if (curDistance < minDist) {
minDist = curDistance; // update neares
minIndex = i; // store index of nearest marker in minIndex
}
}
if (minIndex >= 0) {
// now nearest maker found:
Marker nearestMarker = markers.get(minIndex);
// TODO do something with nearesr marker
} else {
// list of markers was empty
}
For calcDistance, use the distance calculation method provided by android. (e.g Location.distanceTo() )
For 70-80 markers there is no need to make it faster and much more complex.
If you have some thousands points then it is worth to invest in a faster solution (using a spatial index, and an own distance calculation which avoids the sqrt calc).
Just print out the current time in milli seconds at the begin and at the end of the nearest maker search, and you will see, that it is fast enough.
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 :
Calculate the difference in lat and long.
If both differences are larger then a previously processed pair, discard it.
Calculate distance, keep smallest.
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.
How about looping over all markers and checking the distance using Location.distanceBetween? There is no magic involved ;)
List<Marker> markers;
LatLng currentPosition;
float minDistance = Float.MAX_VALUE;
Marker closest = null;
float[] currentDistance = new float[1];
for (Marker marker : markers) {
LatLng markerPosition = marker.getPosition();
Location.distanceBetween(currentPosition.latitude, currentPosition.longitude, markerPosition.latitude, markerPosition.longitude, currentDistance);
if (minDistance > currentDistance[0]) {
minDistance = currentDistance[0];
closest = marker;
}
}
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
This code could help you getting the distances: https://github.com/BeyondAR/beyondar/blob/master/android/BeyondAR_Framework/src/com/beyondar/android/util/math/Distance.java
Here is my implementation of a so called KDTree, consisting of 3 classes: KDTree, KDTNode and KDTResult.
What you finally need is to create the KDTree using KDTree.createTree(), which returns the rootNode of the tree and gets all your fixed points passed in.
Then use KDTree.findNearestWp() to find the nearest Waypoint to the given location.
KDTree:
public class KDTree {
private Comparator<LatLng> latComparator = new LatLonComparator(true);
private Comparator<LatLng> lonComparator = new LatLonComparator(false);;
/**
* Create a KDTree from a list of Destinations. Returns the root-node of the
* tree.
*/
public KDTNode createTree(List<LatLng> recList) {
return createTreeRecursive(0, recList);
}
/**
* Traverse the tree and find the nearest WP.
*
* #param root
* #param wp
* #return
*/
static public LatLng findNearestWp(KDTNode root, LatLng wp) {
KDTResult result = new KDTResult();
findNearestWpRecursive(root, wp, result);
return result.nearestDest;
}
private static void findNearestWpRecursive(KDTNode node, LatLng wp,
KDTResult result) {
double lat = wp.latitude;
double lon = wp.longitude;
/* If a leaf node, calculate distance and return. */
if (node.isLeaf) {
LatLng dest = node.wp;
double latDiff = dest.latitude - lat;
double lonDiff = dest.longitude - lon;
double squareDist = latDiff * latDiff + lonDiff * lonDiff;
// Replace a previously found nearestDest only if the new one is
// nearer.
if (result.nearestDest == null
|| result.squareDistance > squareDist) {
result.nearestDest = dest;
result.squareDistance = squareDist;
}
return;
}
boolean devidedByLat = node.depth % 2 == 0;
boolean goLeft;
/* Check whether left or right is more promising. */
if (devidedByLat) {
goLeft = lat < node.splitValue;
} else {
goLeft = lon < 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<LatLng> recList) {
KDTNode node = new KDTNode();
node.depth = depth;
if (recList.size() == 1) {
// Leafnode found
node.isLeaf = true;
node.wp = recList.get(0);
return node;
}
boolean divideByLat = node.depth % 2 == 0;
sortRecListByDimension(recList, divideByLat);
List<LatLng> leftList = getHalfOf(recList, true);
List<LatLng> rightList = getHalfOf(recList, false);
// Get split point and distance to last left and first right point.
LatLng lastLeft = leftList.get(leftList.size() - 1);
LatLng firstRight = rightList.get(0);
double minDistanceToSplitValue;
double splitValue;
if (divideByLat) {
minDistanceToSplitValue = (firstRight.latitude - lastLeft.latitude) / 2;
splitValue = lastLeft.latitude + Math.abs(minDistanceToSplitValue);
} else {
minDistanceToSplitValue = (firstRight.longitude - lastLeft.longitude) / 2;
splitValue = lastLeft.longitude + 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<LatLng> getHalfOf(List<LatLng> recList, boolean leftHalf) {
int mid = recList.size() / 2;
if (leftHalf) {
return recList.subList(0, mid);
} else {
return recList.subList(mid, recList.size());
}
}
private void sortRecListByDimension(List<LatLng> recList, boolean sortByLat) {
Comparator<LatLng> comparator = sortByLat ? latComparator
: lonComparator;
Collections.sort(recList, comparator);
}
class LatLonComparator implements Comparator<LatLng> {
private boolean byLat;
public LatLonComparator(boolean sortByLat) {
this.byLat = sortByLat;
}
#Override
public int compare(LatLng lhs, LatLng rhs) {
double diff;
if (byLat) {
diff = lhs.latitude - rhs.latitude;
} else {
diff = lhs.longitude - rhs.longitude;
}
if (diff > 0) {
return 1;
} else if (diff < 0) {
return -1;
} else {
return 0;
}
}
}
}
KDTNode:
/** Node of the KDTree */
public 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. An even depth devides the tree in the
* latitude-axis, an odd depth devides the tree in the longitude-axis.
*/
int depth;
/** The Waypoint in case the node is a leaf node. */
LatLng wp;
}
KDTResult:
/** Holds the result of a tree traversal. */
public class KDTResult {
LatLng nearestDest;
// I use the square of the distance to avoid square-root operations.
double squareDistance;
}
Please note, that I am using a simplified distance calculation, which works in my case, as I am only interested in very nearby waypoints. For points further apart, this may result in getting not exactly the nearest point. The absolute difference of two longitudes expressed as east-west distance in meters, depends on the latitude, where this difference is measured. This is not taken into account in my algorithm and I am not sure about the relevance of this effect in your case.
An efficient way to search for the smallest distance between a single point (that may change frequently), and a large set of points, in two dimensions is to use a QuadTree. There is a cost to initially build the QuadTree (i.e., add your marker locations to the data structure), so you only want to do this once (or as infrequently as possible). But, once constructed, searches for the closest point will typically be faster than a brute force comparison against all points in the large set.
BBN's OpenMap project has an open-source QuadTree Java implementation that I believe should work on Android that has a get(float lat, float lon) method to return the closest point.
Google's android-maps-utils library also has an open-source implementation of a QuadTree intended to run on Android, but as it is currently written it only supports a search(Bounds bounds) operation to return a set of points in a given bounding box, and not the point closest to an input point. But, it could be modified to perform the closest point search.
If you have a relatively small number of points (70-80 may be sufficiently small), then in real-world performance a brute-force comparison may execute in a similar amount of time to the QuadTree solution. But, it also depends on how frequently you intended on re-calculating the closest point - if frequent, a QuadTree may be a better choice.
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);
}
Here, I got a way to do that Using databases.
This is a calculate distance function:
public void calculateDistance() {
if (latitude != 0.0 && longitude != 0.0) {
for(int i=0;i<97;i++) {
Location myTargetLocation=new Location("");
myTargetLocation.setLatitude(targetLatitude[i]);
myTargetLocation.setLongitude(targetLongitude[i]);
distance[i]=myCurrentLocation.distanceTo(myTargetLocation);
distance[i]=distance[i]/1000;
mdb.insertDetails(name[i],targetLatitude[i], targetLongitude[i], distance[i]);
}
Cursor c1= mdb.getallDetail();
while (c1.moveToNext()) {
String station_name=c1.getString(1);
double latitude=c1.getDouble(2);
double longitude=c1.getDouble(3);
double dis=c1.getDouble(4);
//Toast.makeText(getApplicationContext(),station_name+" & "+latitude+" & "+longitude+" & "+dis,1).show();
}
Arrays.sort(distance);
double nearest_distance=distance[0];
Cursor c2=mdb.getNearestStationName();
{
while (c2.moveToNext()) {
double min_dis=c2.getDouble(4);
if(min_dis==nearest_distance)
{
String nearest_stationName=c2.getString(1);
if(btn_clicked.equals("source"))
{
source.setText(nearest_stationName);
break;
}
else if(btn_clicked.equals("dest"))
{
destination.setText(nearest_stationName);
break;
}
else
{
}
}
}
}
}
else
{
Toast.makeText(this, "GPS is Not Working Properly,, please check Gps and Wait for few second", 1).show();
}
}
All we have to do is Create an array named targetLatitude[i] and targetLongitude[i] containing Lats and Longs of all the places you want to calculate distance from.
Then create a database as shown below:
public class MyDataBase {
SQLiteDatabase sdb;
MyHelper mh;
MyDataBase(Context con)
{
mh = new MyHelper(con, "Metro",null, 1);
}
public void open() {
try
{
sdb=mh.getWritableDatabase();
}
catch(Exception e)
{
}
}
public void insertDetails(String name,double latitude,double longitude,double distance) {
ContentValues cv=new ContentValues();
cv.put("name", name);
cv.put("latitude", latitude);
cv.put("longitude",longitude);
cv.put("distance", distance);
sdb.insert("stations", null, cv);
}
public void insertStops(String stop,double latitude,double logitude)
{
ContentValues cv=new ContentValues();
cv.put("stop", stop);
cv.put("latitude", latitude);
cv.put("logitude", logitude);
sdb.insert("stops", null, cv);
}
public Cursor getallDetail()
{
Cursor c=sdb.query("stations",null,null,null,null,null,null);
return c;
}
public Cursor getNearestStationName() {
Cursor c=sdb.query("stations",null,null,null,null,null,null);
return c;
}
public Cursor getStops(String stop)
{
Cursor c;
c=sdb.query("stops",null,"stop=?",new String[]{stop},null, null, null);
return c;
}
class MyHelper extends SQLiteOpenHelper
{
public MyHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("Create table stations(_id integer primary key,name text," +
" latitude double, longitude double, distance double );");
db.execSQL("Create table stops(_id integer primary key,stop text," +
"latitude double,logitude double);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
public void deleteDetail() {
sdb.delete("stations",null,null);
sdb.delete("stops",null,null);
}
public void close() {
sdb.close();
}
}
Then execute the CalculateDistance function wherever you want and you can get the nearest station name.
I am using android maps v2works fine I can add and remove markers onLong Touch on locations.
Problem:
I would like to drop the marker slowly into the touched location i.e. I want the user to see the marker floating from the top of the screen to the point where it is dropped(touched location).
Currently; the marker just appears on the touched location such that you have to lift you finger to see that it has been dropped. It would be nice to see it coming from the top of the screen.
Thanks.
You can achieve this with a code similar to this (untested):
final LatLng target = ...;
final long duration = 400;
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(target);
startPoint.y = 0;
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
double lng = t * target.longitude + (1 - t) * startLatLng.longitude;
double lat = t * target.latitude + (1 - t) * startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 10ms later.
handler.postDelayed(this, 10);
} else {
// animation ended
}
}
});
I have combined the MaciejGórski's approach with the code from this gist. In addition, added a bounce effect.
public class MyBounceInterpolator implements android.view.animation.Interpolator {
double mAmplitude = 1;
double mFrequency = 10;
public MyBounceInterpolator(double amplitude, double frequency) {
mAmplitude = amplitude;
mFrequency = frequency;
}
public float getInterpolation(float time) {
double amplitude = mAmplitude;
if (amplitude == 0) { amplitude = 0.05; }
// The interpolation curve equation:
// -e^(-time / amplitude) * cos(frequency * time) + 1
//
// View the graph live: https://www.desmos.com/calculator/6gbvrm5i0s
return (float) (-1 * Math.pow(Math.E, -time/ mAmplitude) * Math.cos(mFrequency * time) + 1);
}
}
void dropMarker(final Marker marker, GoogleMap map) {
final LatLng finalPosition = new LatLng(marker.getPosition().latitude, marker.getPosition().longitude);
Projection projection = map.getProjection();
Point startPoint = projection.toScreenLocation(finalPosition);
startPoint.y = 0;
final LatLng startLatLng = projection.fromScreenLocation(startPoint);
final Interpolator interpolator = new MyBounceInterpolator(0.11, 4.6);
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
#Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
float t = interpolator.getInterpolation(fraction);
double lng = t * finalPosition.longitude + (1 - t) * startLatLng.longitude;
double lat = t * finalPosition.latitude + (1 - t) * startLatLng.latitude;
return new LatLng(lat, lng);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setDuration(400);
animator.start();
}
It works great, but it seemed to me that sometimes the marker remained one step far from the target, so I added just one more line:
if (t < 1.0) {
// Post again 10ms later.
handler.postDelayed(this, 50);
} else {
// animation ended
marker.setPosition(target);
}
Hope it helps.
I've applied your way but have an issue that is the markers position on map is not correct. I think command below calculate is not correct. So after animation finished then final result is not same as original Lat,Lng.
double lng = t * target.longitude + (1 - t) * startLatLng.longitude;
double lat = t * target.latitude + (1 - t) * startLatLng.latitude;