I am getting far and near LatLng as below :
mFarLeft = mMapView.getProjection().getVisibleRegion().farLeft;
mFarRight = mMapView.getProjection().getVisibleRegion().farRight;
mNearLeft = mMapView.getProjection().getVisibleRegion().nearLeft;
mNearRight = mMapView.getProjection().getVisibleRegion().nearRight;
Now, How can I calculate the distance between this far and near points?
Is there any method available to get distance in KM and Feet?
The fields farLeft, farRight, etc. are actually LatLng objects, according to the Android documentation. However, there is a field latLngBounds which represents the smallest bounding box including the visible region. This bounds object itself has two LatLng fields for the northeast and southwest corners of the box. Using the Haversine formula we can compute the distance between the corners of this box. For example, if you wanted to compute the height of the bounding box we can try:
public static double haversine(double lat1, double lon1, double lat2, double lon2) {
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
double a = Math.pow(Math.sin(dLat / 2),2) + Math.pow(Math.sin(dLon / 2),2) * Math.cos(lat1) * Math.cos(lat2);
double c = 2 * Math.asin(Math.sqrt(a));
return 6372.8 * c; // in kilometers
// if you want to return feet, then use 6372.8 * 3280.84 instead
}
public static void main(String[] args) {
LatLng ne = mMapView.getProjection().getVisibleRegion().latLngBounds.northeast;
LatLng sw = mMapView.getProjection().getVisibleRegion().latLngBounds.southwest;
double latLow = sw.latitude;
double latHigh = ne.latitude;
double longitude = sw.longitude;
// now compute the "height" of the bounding box
// note that the longitude value is the same
double height = haversine(latLow, longitude, latHigh, longitude);
}
Google provides a utility library for Maps that can, among other things,
Calculate distances, areas and headings via spherical geometry
Using the spherical geometry utilities in SphericalUtil, you can compute distances, areas, and headings based on latitudes and longitudes. Here are some of the methods available in the utility:
computeDistanceBetween() – Returns the distance, in meters, between two latitude/longitude coordinates.
computeHeading() – Returns the bearing, in degrees, between two latitude/longitude coordinates.
computeArea() – Returns the area, in square meters, of a closed path on the Earth.
interpolate() – Returns the latitude/longitude coordinates of a point that lies a given fraction of the distance between two given points. You can use this to animate a marker between two points, for example.
Refer to the reference documentation for a full list of methods in the utility.
The library is available on maven central.
dependencies {
compile 'com.google.maps.android:android-maps-utils:0.5+'
}
Source: https://developers.google.com/maps/documentation/android-api/utility/
Related
I'm trying to add an image to a Google Map using the Google Maps Android API v2:
mMap = googleMap;
LatLng sw = new LatLng(47.01377857060625, 8.305519705172628);
LatLng ne = new LatLng(47.01395211967171, 8.306270482717082);
LatLng nw = new LatLng(47.014014755501165, 8.305559697328135);
LatLng se = new LatLng(47.01370751919609, 8.306236284552142);
LatLngBounds latLngBounds = new LatLngBounds(sw, ne).including(nw).including(se);
GroundOverlayOptions groundOverlayOptions = new GroundOverlayOptions();
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromAsset("t2c.png");
groundOverlayOptions.image(bitmapDescriptor);
groundOverlayOptions.positionFromBounds(latLngBounds);
mMap.addGroundOverlay(groundOverlayOptions);
Unfortunately, the LatLng values are not 100% accurate, so the background image doesn't rotate but is skewed according to Google:
If the bounds do not match the original aspect ratio, the image will be skewed.
Using only LatLng sw and ne:
https://developers.google.com/maps/documentation/android-api/groundoverlay#use_latlngbounds_to_position_an_image
I don't know how I should be able to figure out the exact LatLng of south-west and north-east, so I'm interested in a way to define a polygon and squeeze the image somehow into it with the four LatLng as anchors. Using four LatLng currently looks like this. Using LatLng sw, ne, nw and se:
you will never achieve what you want using LatLngBounds because you are trying to rotate your GroundOverlay.
What you need to do is to use bearing (rotation) and width/height of the ground overlay.
Let's say your building in -20 degrees rotated and has width-height of 40-20 (only you know these values.
1- Get the LatLng of the center of the building, which is finding the center of the four coordinates in your code above.
That value will be your "centerLocation"
GroundOverlayOptions groundOverlayOptions = new GroundOverlayOptions();
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromAsset("t2c.png");
groundOverlayOptions.image(bitmapDescriptor);
groundOverlayOptions.position(centerLocation,widthInMeters,heightInMeters);
groundOverlayOptions.bearing(rotated);//the value is clockwise and rotation is about anchor point (which should be by default 0.5,0.5 of your image
mMap.addGroundOverlay(groundOverlayOptions);
This should work, obviously you have to calculate values or go step by step trying different values.
Hope it helps
The first answer is what will works for u. I will just write my solution in detail so it could be easy to just copy paste code and get it done...
First Step if u have four bounds, get center of them for which u can use the below the function
private LatLng computeCentroid(List<LatLng> points) {
double latitude = 0;
double longitude = 0;
int n = points.size();
for (LatLng point : points) {
latitude += point.latitude;
longitude += point.longitude;
}
return new LatLng(latitude/n, longitude/n);
}
Second get the width and height in meters. Here are method to get distance between two points in meters.
private double distance(double lat1, double lon1, double lat2, double lon2) {
double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1))
* Math.sin(deg2rad(lat2))
+ Math.cos(deg2rad(lat1))
* Math.cos(deg2rad(lat2))
* Math.cos(deg2rad(theta));
dist = Math.acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
return dist * 1609.34;
}
private double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
private double rad2deg(double rad) {
return (rad * 180.0 / Math.PI);
}
Last just use these values and done
double height=distance(selectedFloorPlan.getGeoData().get(0).getLat(), selectedFloorPlan.getGeoData().get(0).getLng(),selectedFloorPlan.getGeoData().get(1).getLat(), selectedFloorPlan.getGeoData().get(1).getLng());
double width=distance(selectedFloorPlan.getGeoData().get(0).getLat(), selectedFloorPlan.getGeoData().get(0).getLng(),selectedFloorPlan.getGeoData().get(3).getLat(), selectedFloorPlan.getGeoData().get(3).getLng());
GroundOverlayOptions newarkMap = new GroundOverlayOptions()
.image(images.get(0))
.position(centerPoint,(float)width,(float)height)
.bearing(-67.18571490414709f);
imageOverlay = map.addGroundOverlay(newarkMap);
I am working on an Android version of an Ios app I have developed and are running into problems with differences in the Google Maps API on the two platforms. In Ios I can use the method: GMSGeometryOffset. However, it is not present in the Android API. Therefore, I am looking for an alternative to this method so that I can calculate a new location based on the current location, a direction/bearing and a distance/radius.
I am using it to Draw a round circle while avoiding the egg shape. My code so far looks like the following. However, it gives me an egg shape unless the current location is directly on top of equator.
for(double angle = 0; angle < 2*Math.PI; angle = angle+ Math.PI/resolutionCircle)
{
latitude = position.latitude + (radiusCircle * Math.sin(angle));
longitude = position.longitude + (radiusCircle * Math.cos(angle));
inner.add(new LatLng(latitude, longitude));
}
There is a computeOffset method in the Google Maps Android API Utility Library.
It is a static method:
public static LatLng computeOffset(LatLng from,
double distance,
double heading)
It returns the LatLng resulting from moving a distance from an origin in the specified heading (expressed in degrees clockwise from north).
You can refer this documentation for more detail about Google Maps Android API Utility Library.
Google Maps' GitHub page also provide its detail implementation:
public static LatLng computeOffset(LatLng from, double distance, double heading) {
distance /= EARTH_RADIUS;
heading = toRadians(heading);
// http://williams.best.vwh.net/avform.htm#LL
double fromLat = toRadians(from.latitude);
double fromLng = toRadians(from.longitude);
double cosDistance = cos(distance);
double sinDistance = sin(distance);
double sinFromLat = sin(fromLat);
double cosFromLat = cos(fromLat);
double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading);
double dLng = atan2(
sinDistance * cosFromLat * sin(heading),
cosDistance - sinFromLat * sinLat);
return new LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng));
}
Basically the title says it all. Rather than plotting every single entry in my database on a map as I do at the moment, I want to query the database and only plot the entries whose co-ordinates fall within the circle drawn around the users current location, however I can't quite figure out how to do it. At the moment, I have written code that plots the users current location on the map along with the locations of all the entries stored on my database and also the circle around the current location (See picture below). Based on my picture below, I only want the three markers within the circle to be plotted.
Does anyone know if there are any ways of checking if the latlng co-ordinates stored in my database fall within the area of the circle? Or if not, could anyone suggest any alternatives that use a similar idea.
An alternative I was thinking of was using a square / rectangle rather than a circle so that I could simply compare the co-ordinates of my entries with bounds of the square / rectangle, however I don't really know how feasible that is seen as those shapes aren't supported by the Google Maps API. I also came across the LatLngBounds class which could be useful but I can't find any sample code using it. How I might do this either?
I believe the circle has a fixed Radius and a center point.
So, go with this method to get the distance between the center and some LatLng and set a condition
distance <= radius
public static String getDistance(LatLng ll_source, LatLng ll_destination,
int unit) {
int Radius = 6371;// radius of earth in Km
double lat1 = ll_source.latitude;
double lat2 = ll_destination.latitude;
double lon1 = ll_source.longitude;
double lon2 = ll_destination.longitude;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(dLon / 2)
* Math.sin(dLon / 2);
double c = 2 * Math.asin(Math.sqrt(a));
double valueResult = Radius * c;
double km = valueResult / 1;
DecimalFormat newFormat = new DecimalFormat("####");
Integer kmInDec = Integer.valueOf(newFormat.format(km));
double meter = valueResult % 1000;
Integer meterInDec = Integer.valueOf(newFormat.format(meter));
DecimalFormat df = new DecimalFormat("#.#");
return df.format(valueResult);
}
Ok, I've figured out a solution using the LatLngBounds class I referred to in my question. What it does is:
Creates a new rectangular perimeter around the user's current latitude and longitude co-ordinates (this example is roughly one square kilometer).
It then checks if the perimeter contains the co-ordinates stored in the database.
If it does, the marker is plotted on the map.
public void getNearbyMarkers(){
LatLngBounds perimeter = new LatLngBounds(new LatLng(currentLat - 0.004,
currentLon - 0.004), new LatLng(currentLat + 0.004, currentLon + 0.004));
if (perimeter.contains(LatlongFromDatabase)) {
//Plot Marker on Map
} else {
Toast.makeText(getApplicationContext(), "Co-ordinates not in perimeter!", 8).show();
}
}
I'm working on an Android app that uses Geopoints and I want to determinate a Geopoint from another Geopoint, a distance (in any format) and a polar angle. For example, I want to get coordinates of a place 100 meters in the North-North-East (22,5 degres) of my location got by the GPS in my phone.
The only method I've found is Location.distanceBetween(...).
Implementation for Android. This code is great for Unit Testing in your aplication:
public double radiansFromDegrees(double degrees)
{
return degrees * (Math.PI/180.0);
}
public double degreesFromRadians(double radians)
{
return radians * (180.0/Math.PI);
}
public Location locationFromLocation(Location fromLocation, double distance, double bearingDegrees)
{
double distanceKm = distance / 1000.0;
double distanceRadians = distanceKm / 6371.0;
//6,371 = Earth's radius in km
double bearingRadians = this.radiansFromDegrees(bearingDegrees);
double fromLatRadians = this.radiansFromDegrees(fromLocation.getLatitude());
double fromLonRadians = this.radiansFromDegrees(fromLocation.getLongitude());
double toLatRadians = Math.asin( Math.sin(fromLatRadians) * Math.cos(distanceRadians)
+ Math.cos(fromLatRadians) * Math.sin(distanceRadians) * Math.cos(bearingRadians) );
double toLonRadians = fromLonRadians + Math.atan2(Math.sin(bearingRadians)
* Math.sin(distanceRadians) * Math.cos(fromLatRadians), Math.cos(distanceRadians)
- Math.sin(fromLatRadians) * Math.sin(toLatRadians));
// adjust toLonRadians to be in the range -180 to +180...
toLonRadians = ((toLonRadians + 3*Math.PI) % (2*Math.PI) ) - Math.PI;
Location result = new Location(LocationManager.GPS_PROVIDER);
result.setLatitude(this.degreesFromRadians(toLatRadians));
result.setLongitude(this.degreesFromRadians(toLonRadians));
return result;
}
Take a look at great-circle formulas: http://en.wikipedia.org/wiki/Great-circle_distance
This should give You some hints on how to calculate the distances.
For a point in a given distance and heading, check http://williams.best.vwh.net/avform.htm#LL
Those formulas look quite complicated, but are easy to implement ;)
I have location from GPS (lon_base, lat_base).
I have a list of locations (lon1, lat1|lon2, lat2|lon3, lat3...)
This list is very long and is around the world.
My questions are:
1. How do I get from that list only the lon\lat that are 1 mile from my lon_base\lat_base?
2. How do I sort them from closest to farthest?
Thanks in advance!
public static List<Location> sortLocations(List<Location> locations, final double myLatitude,final double myLongitude) {
Comparator comp = new Comparator<Location>() {
#Override
public int compare(Location o, Location o2) {
float[] result1 = new float[3];
android.location.Location.distanceBetween(myLatitude, myLongitude, o.Lat, o.Long, result1);
Float distance1 = result1[0];
float[] result2 = new float[3];
android.location.Location.distanceBetween(myLatitude, myLongitude, o2.Lat, o2.Long, result2);
Float distance2 = result2[0];
return distance1.compareTo(distance2);
}
};
Collections.sort(locations, comp);
return locations;
}
Where the List of Locations is a list containing your own Location class, not the android.location.Location.
You may use the great circle distance to calculate the distance between two points whose you know the latitude-longitude coordinates. The formulae are quite easy to code:
static double distance(double fromLat, double fromLon, double toLat, double toLon) {
double radius = 6378137; // approximate Earth radius, *in meters*
double deltaLat = toLat - fromLat;
double deltaLon = toLon - fromLon;
double angle = 2 * Math.asin( Math.sqrt(
Math.pow(Math.sin(deltaLat/2), 2) +
Math.cos(fromLat) * Math.cos(toLat) *
Math.pow(Math.sin(deltaLon/2), 2) ) );
return radius * angle;
}
You want to define your own Comparator that, in general, looks something like this:
LonLat myHouse = /* whatever */ ;
Comparable comp = new Comparable () {
LonLat a;
int compareTo (Object b) {
int aDist = calcDistance(a, myHouse) ;
int bDist = calcDistance(b, myHouse) ;
return aDist - bDist;
}
};
myLonLatList.sort(lonLatList, comp);
where calcDistance() simply calculates the distance between the two points. If you're on Android, I think Google Maps has a function somewhere in their API that will do this for you.
EDIT : You'll want your calcDistance() function to look like ChrisJ's distance function.
-tjw
You can use followig approximation (since 1 mile is much smaller than the radius of the earth) to calculate the distances from your base:
dx = cos(phi_base) * (theta - theta_base)
dy = phi - phi_base
dist = sqrt(dx*dx+dy*dy)
with: phi = latitude and theta = longitude
The result is in units of 60 nautical miles if theta and phi are given in degrees.
The results will be quite wrong for points that have a latitude that is much different from your base latitude, but this doesn't matter if you just want to know wich points are about 1 mile from your base.
For most programming languages you have to convert phi_base to radians (multiply by pi/180) in order to use it for cos().
(Attention: You have to take special care if your base longitude is very close to 180° or -180°, but probably that is not the case :-)
Use the calculated distances as sorting key to sort your points.
If you have to be more exact (e.g. if you want to know all points that are about 2000 miles from your home), than you must use the formula for Great Circle Distance to calculate the exact distance of two points on a sphere.
According to this link
i made working method. The answer above was wrong, because it doesn't convert lat/lng degrees to radians.
private double getDistance(double fromLat, double fromLon, double toLat, double toLon){
double radius = 6371; // Earth radius in km
double deltaLat = Math.toRadians(toLat - fromLat);
double deltaLon = Math.toRadians(toLon - fromLon);
double lat1 = Math.toRadians(fromLat);
double lat2 = Math.toRadians(toLat);
double aVal = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
Math.sin(deltaLon/2) * Math.sin(deltaLon/2) * Math.cos(lat1) * Math.cos(lat2);
double cVal = 2*Math.atan2(Math.sqrt(aVal), Math.sqrt(1-aVal));
double distance = radius*cVal;
Log.d("distance","radius * angle = " +distance);
return distance;
}