I've been researching this on Google and SO but I'm stuck, I think I'm missing something fundamental. Most examples I've seen don't deal with an arbitrary mapWidth and a single point, just the span of an Overlay.
I have a database of map points, a MapView and a Geocoder. I can search for a postcode in my app, and have an Address returned by my Geocoder.
Using this Address, I can build a GeoPoint and search my DB and get back a list of nearby points. The issue comes from trying to zoomToSpan using a span constructed from from the returned Address point and the distance to the nearest point in the database.
I only want the span to encompass the nearest two points (if available). Here's the relevant code:
Collections.sort(listingDisplay, mComparator);
listingDisplayAdapter.notifyDataSetChanged();
float spanWidth =0;
if (listingDisplay.size() > 1) {
spanWidth = (float) (2 * distanceFromPoint(listingDisplay.get(1),
current));
} else if (listingDisplay.size() == 1) {
spanWidth = (float) (2 * distanceFromPoint(listingDisplay.get(0),
current));
}
Log.v(TAG, "SpanWidth: " + spanWidth);
// Create span
int minLat = (int) (current.getLatitudeE6() - (spanWidth * 1E6) / 2);
int maxLat = (int) (current.getLatitudeE6() + (spanWidth * 1E6) / 2);
int minLong = (int) (current.getLongitudeE6() - (spanWidth * 1E6) / 2);
int maxLong = (int) (current.getLongitudeE6() + (spanWidth * 1E6) / 2);
// Zoom against span. This appears to create a very small region that doesn't encompass the points
mapController.setCenter(current);
mapController.zoomToSpan(Math.abs( minLat - maxLat ), Math.abs( minLong - maxLong ));
ListingDisplay contains a list of the closest points, with a comparator, mComparator sorting this list with the closest locations to my returned Address (the GeoPoint called: current) at the top of the list .
I then set the value of spanWidth based on the closest, and try to figure out the span from this.
My question is, how can I construct a span from a given distance and centerpoint?
After a very, very long time, I eventually realized that I wasn't considering some important information, chiefly among them was the fact that distances on Android are calculated using the WGS84 ellipsoid.
I ended up using the helper methods inside Jan Matuschek's excellent and simple GeoLocation class, which comes with a very thorough explanation of the concepts involved.
My method essentially boiled down to the following. It can probably be optimized a good deal, down to a simple SQL query, but here it is for my purposes, where listingDisplay is an array of database-retrieved custom LocationNode objects, and the GeoPoint current is created directly from a returned Address of a standard Android Geocoder.
public void setRegionForGeoPoint(GeoPoint current) {
// Earth radius in KM
final double EARTH_RADIUS = 6371.01;
// Dummy span distance in KM for initial search; distance buffer is in M
final double DISTANCE_BUFFER = 50;
final double dummyDistance = 100.0;
//Create a list to store distances
List<Double> distancesList = new ArrayList<Double>();
// Loop through and modify LocationNodes with distance from user
for (LocationNode location : listingDisplay) {
location.setDistance((float) distanceFromUser(location));
// Dynamically calculate distance from our current point (epicentre)
distancesList.add(distanceFromPoint(location, current));
}
// Sort distances
Collections.sort(distancesList);
// Calculate regional span
float spanWidth = (float) dummyDistance;
double distance = 0;
if (distancesList.size() > 0) {
if (distancesList.size() > 1) {
distance = distancesList.get(1);
spanWidth = (float) (distance + DISTANCE_BUFFER);
} else if (distancesList.size() == 1) {
distance = distancesList.get(0);
spanWidth = (float) (distance + DISTANCE_BUFFER);
}
//Obtain the spanwidth in metres.
double spanWidthInKM = (double) spanWidth / 1000;
// Create span
GeoLocation[] boundingBoxSpan = currentGeoLocation
.boundingCoordinates(spanWidthInKM, EARTH_RADIUS);
//Create min/max values for final span calculation
int minLatSpan = (int) (boundingBoxSpan[0].getLatitudeInDegrees() * 1E6);
int maxLatSpan = (int) (boundingBoxSpan[1].getLatitudeInDegrees() * 1E6);
int minLongSpan = (int) (boundingBoxSpan[0].getLongitudeInDegrees() * 1E6);
int maxLongSpan = (int) (boundingBoxSpan[1].getLongitudeInDegrees() * 1E6);
//Finally calculate span
int latSpanE6 = Math.abs(minLatSpan - maxLatSpan);
int lonSpanE6 = Math.abs(minLongSpan - maxLongSpan);
// Set center
mapController.setCenter(current);
// Zoom to span
mapController.zoomToSpan(latSpanE6, lonSpanE6);
} else {
//TODO: Handle the case when we have no distance values to use
}
}
public double distanceFromPoint(LocationNode location, GeoPoint point) {
// Calculate distance from user via result
Location locationA = new Location("point A");
locationA.setLatitude(location.getLatitude());
locationA.setLongitude(location.getLongitude());
Location locationB = new Location("point B");
locationB.setLatitude((double) (point.getLatitudeE6() / 1E6));
locationB.setLongitude((double) (point.getLongitudeE6() / 1E6));
double distance = locationA.distanceTo(locationB);
Log.v(TAG, "Calculated Distance: " + distance);
return distance;
}
Related
I have a list of latitude and longitude stored in database. Now I'm trying to develop a function on finding nearby place from my current location. Means that, i have to search all the latitude and longitude from my database and see which of the places are within my area will be shown.
Location.distanceBetween(lat1,lon1, lat2,lon2, result);
To find the distance from one two-dimensional point to another you can use the Haversine formula, (without calling any API functions.)
// All measurements are in meters
var rad = function(x) {
return x * Math.PI / 180;
};
var getDistance = function(coord1, coord2) {
// This is the Earth's radius
var earthRad = 6378137;
var dLat = rad(coord2.lat() - coord1.lat());
var dLong = rad(coord2.lng() - coord1.lng());
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(rad(coord1.lat())) * Math.cos(rad(coord2.lat())) *
Math.sin(dLong / 2) * Math.sin(dLong / 2);
var b = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var distance = earthRad * b;
// Returns distance from coord1 to coord2 in meters
return distance;
};
Your question is a bit broad so this might not be the best solution. If you were constantly updating your current location you'd have to implement a Tree Sorting algorithm or some other way of optimizing performance rather than checking through your database for distances on every update.
You can use the Location object. The distanceBetween or distanceTo method will give you the 'as the crow flies' distance between two points. From there you can filter your results and display only the places you want to.
here is what i use
public void nearBy() {
if (map.getMyLocation() != null) {//check if my location was found
db = new MyDatabase(MainActivity.context);
Cursor medecin = db.lireMedecin();//read values from database
map.clear();//clearing the map
while (medecin.getPosition() < medecin.getCount()) { //cheking if this doctor is nearby...
String Lat1 = medecin.getString(5);//doctor's latitude
String Lon1 = medecin.getString(6);//doctor's longitude
LatLng me = new LatLng(map.getMyLocation().getLatitude(), map.getMyLocation().getLongitude());//my location
LatLng med = new LatLng(Double.parseDouble(Lat1), Double.parseDouble(Lon1));//doctor location
if (CalDist(me, med) < 6) {//if distance is < 6km add marker to the map
map.addMarker(new MarkerOptions().position(med).title(medecin.getString(1) + " " + medecin.getString(2))
.snippet(medecin.getString(4)).icon(BitmapDescriptorFactory
.fromResource(Icone(medecin.getString(7).charAt(0))))); //showing the doctor on the map
}
medecin.moveToNext();//next doctor
}
} else
Toast.makeText(context, "Gps signal not valid", Toast.LENGTH_SHORT).show();//can't find my position
}
calculate distance :
public static double CalDist(LatLng StartP, LatLng EndP) {
int Radius = 6371;//radius of earth in Km
double lat1 = StartP.latitude ;
double lat2 = EndP.latitude ;
double lon1 = StartP.longitude ;
double lon2 = EndP.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));
return Radius * c;
}
I have a GoogleMap in my project. It's set in zoom level 21. I want to draw a line that is 5 meter in length with a specific heading. I used this code:
private LatLng drawHeadingOnMap(LatLng centre, double radius, double heading)
{
double EARTH_RADIUS = 6378100.0;
// Convert to radians.
double lat = Math.toRadians(centre.latitude );
double lon = Math.toRadians(centre.longitude);
// y
double latPoint = lat + (radius / EARTH_RADIUS) * Math.sin(Math.toRadians(heading));
// x
double lonPoint = lon + (radius / EARTH_RADIUS) * Math.cos( Math.sin(Math.toRadians(heading)) / Math.cos(lat));
LatLng point =new LatLng(latPoint * 180.0 / Math.PI, lonPoint * 180.0 / Math.PI);
return point;
}
I run it by:
LatLng ll = drawHeadingOnMap(origin, 5, 90);
LatLng lll = drawHeadingOnMap(origin, 5, 0);
googleMap.addPolyline(new PolylineOptions().add(Mabda).add(ll).color(Color.BLUE).width(3));
googleMap.addPolyline(new PolylineOptions().add(Mabda).add(lll).color(Color.BLUE).width(3));
It draw 0 degree very well!! but others are wrong. for example this pic is shown the above code :
When I want to draw 90 degree, It draw sth like this pic! and after 90 , it get back to 0 degree (When I write drawHeadingOnMap(origin, 5, 180), It draw 0 degree!). How can I fix it? I'm so confused !!!...
Updated:
I tried it for origin= (12,12)...
I got this result:
ll.Latitude = 12.000898320495335
ll.Longitude = 12.00046835742835
lll.latitude = 12.0
lll.longitude = 12.000898320495335
ll is result for moving of (12,12) for 1 meter in direction of 90 degree.
lll is result for moving of (12,12) for 1 meter in direction of 0 degree.
the method is just OK for 0 degree ...
If you have a center point (10, 20), and you want to find the other point (x, y) to its 20 degree with radius 5, you can do the following math:
x = 10 + 5 * Math.sin(Math.toRadians(20));
y = 20 + 5 * Math.cos(Math.toRadians(20));
Not sure why you did Math.cos( Math.sin(Math.toRadians(heading)) / Math.cos(lat)) for your lonPoint.
To understand exact math I suggest reading this link.
If a working implementation is all you need use this function (adopted from Maps SphericalUtil):
/**
* #param loc location to transale (creates a copy)
* #param distance in meters
* #param heading in degrees, where 0 is NORTH, clockwise
* #return new location
*/
public static LatLng translate(LatLng loc, double distance, double heading){
double EARTH_RADIUS = 6378100.0;
heading = Math.toRadians(heading);
distance = distance/EARTH_RADIUS;
// http://williams.best.vwh.net/avform.htm#LL
double fromLat = Math.toRadians(loc.latitude);
double fromLng = Math.toRadians(loc.longitude);
double cosDistance = Math.cos(distance);
double sinDistance = Math.sin(distance);
double sinFromLat = Math.sin(fromLat);
double cosFromLat = Math.cos(fromLat);
double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * Math.cos(heading);
double dLng = Math.atan2(
sinDistance * cosFromLat * Math.sin(heading),
cosDistance - sinFromLat * sinLat);
return new LatLng(Math.toDegrees(Math.asin(sinLat)), Math.toDegrees(fromLng + dLng));
}
I have a little project I've been playing with (Android, GPS, mapping APIs), and I need to figure how to find a longitude/latitude/GeoPoint from a given longitude/latitude/GeoPoint with only knowing the meters/km longitude and latitude. e.g. I want to figure out where a point is from me, that I know is +1000 meters along the longitude and +1000 along the latitude.
It's a little different than the usual GeoPoint/distance questions you'll see, and it's not quite geo fencing radius related as the distance is X,Y meters/kms, and I don't have a bearing. (I could work out a bearing, but I don't have a suitable direct distance)
Basically, if I could reverse GeoPoint.distanceTo() it would do the job for me.
Update
Just a little more background. I'm basically applying a node triangulation idea I had, but the algorithm requires that my inputs be in a map normalized form that's not the same as longitude and latitude. I create a map/grid where 0,0 (the bottom/left) is the left/west and bottom/south most longitude/latitude values from the nodes I'm working with. All the other node X/Y on the map are determined by finding their meters from the 0,0 node's longitude/latitude using GeoPoint.distanceTo(). (note that I find their X/Y by performing distanceTo twice for each node so I have the X and Y meters from 0,0, not a direct line to the node) That distance in meters is fed into the algorithm and new X/Y map points are produced.
And so I need to figure out how to convert distance from a longitude/latitude into another, previously unknown, longitude/latitude.
double startPointLongitude = 23.459821;
double startPointLatitude = 76.998200;
double distanceLongitude = 100; // 100 meters along the longitude
double distanceLatitude = 75; // 75 meters along the latitude
Basically i took the Answer from AlexWien, corrected two things and made it into a java method
private static final double WGS84_RADIUS = 6370997.0;
private static double EarthCircumFence = 2* WGS84_RADIUS * Math.PI;
private static Position getPosition(Position sourcePosition, double mEastWest, double mNorthSouth){
double degreesPerMeterForLat = EarthCircumFence/360.0;
double shrinkFactor = Math.cos((sourcePosition.getLat()*Math.PI/180));
double degreesPerMeterForLon = degreesPerMeterForLat * shrinkFactor;
double newLat = sourcePosition.getLat() + mNorthSouth * (1/degreesPerMeterForLat);
double newLng = sourcePosition.getLng() + mEastWest * (1/degreesPerMeterForLon);
return new Position(newLat, newLng);
}
The distance between two degrees of latitude never change, it is always aprox. 111 km
(The exact value you should caculate by using the WGS84 Earth radius:
EarthCircumFence = 2* WGS84_RADIUS * Math.Pi;
metersPerDegree = (Earth Cirumfence / 360)
With this info you easily can calculate the latitude offset,
just reverse the factor and have:
degreesPerMeterForLat = EarthCircumfenceMeter / 360.0
with longitude its a bit different, the distance between two degrees of longitude shrink
the more you move away from aequator.
shrinkFactor = cos(toRadians(locationLatitude));
compensate now:
degreesPerMeterForLon = degreesPerMeterForLat / shrinkFactor;
Finally
newLatPos = latOld + numMeters * degreesPerMeterForLat;
newLonPos = lonOld + numMeters * degreesPerMeterForLon;
This works for distance offset < 10 - 50 km
Sigh, I posted this like 6 hours ago but it does not appear to have gone through.
Ok, worked it out in spite of most geographical formulas and facts occasionally going over my head. Working with geography is like working with the Gregorian calendar, it makes sense if you program for it all the time, but otherwise it's easy to get confused by an incorrect assumption.
The following except from my app will take a starting GeoPoint's long/lat
/**
* the length of one degree of latitude (and one degree of longitude at equator) in meters.
*/
private static final double DEGREE_DISTANCE_AT_EQUATOR = 111329;
/**
* calculates the x,y in meters from a given starting point's long0, lat0 to a target destination point's long1, lat1.
* #param long0 start point longitude
* #param lat0 start point latitude
* #param long1 end point longitude
* #param lat1 end point latitude
* #return
*/
public static Pair<Double, Double> xyFromLongLat(int long0, int lat0, int long1, int lat1) {
double x = (long1 / 1E6 - long0 / 1E6) * longitudeDistanceAtLatitude(lat0 / 1E6);
double y = (lat1 / 1E6 - lat0 / 1E6) * DEGREE_DISTANCE_AT_EQUATOR;
return new Pair<Double, Double>(x, y);
}
/**
* calculates longitude and latitude from a given starting point, with only the X/Y meters
* #param long0
* #param lat0
* #param x
* #param y
* #return
*/
public static Pair<Double, Double> longLatFromXY(int long0, int lat0, double x, double y) {
double lat1 = (y / DEGREE_DISTANCE_AT_EQUATOR) + (lat0 / 1E6);
double long1 = x / longitudeDistanceAtLatitude(lat0) + (long0 / 1E6);
return new Pair<Double, Double>(lat1, long1);
}
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;
}