How do I find things that are nearby to me? [duplicate] - algorithm
Does the new firestore database from firebase natively support location based geo queries? i.e. Find posts within 10 miles, or find the 50 nearest posts?
I see that there are some existing projects for the real-time firebase database, projects such as geofire- could those be adapted to firestore as well?
UPDATE: Firestore does not support actual GeoPoint queries at present so while the below query executes successfully, it only filters by latitude, not by longitude and thus will return many results that are not nearby. The best solution would be to use geohashes. To learn how to do something similar yourself, have a look at this video.
This can be done by creating a bounding box less than greater than query. As for the efficiency, I can't speak to it.
Note, the accuracy of the lat/long offset for ~1 mile should be reviewed, but here is a quick way to do this:
SWIFT 3.0 Version
func getDocumentNearBy(latitude: Double, longitude: Double, distance: Double) {
// ~1 mile of lat and lon in degrees
let lat = 0.0144927536231884
let lon = 0.0181818181818182
let lowerLat = latitude - (lat * distance)
let lowerLon = longitude - (lon * distance)
let greaterLat = latitude + (lat * distance)
let greaterLon = longitude + (lon * distance)
let lesserGeopoint = GeoPoint(latitude: lowerLat, longitude: lowerLon)
let greaterGeopoint = GeoPoint(latitude: greaterLat, longitude: greaterLon)
let docRef = Firestore.firestore().collection("locations")
let query = docRef.whereField("location", isGreaterThan: lesserGeopoint).whereField("location", isLessThan: greaterGeopoint)
query.getDocuments { snapshot, error in
if let error = error {
print("Error getting documents: \(error)")
} else {
for document in snapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
}
func run() {
// Get all locations within 10 miles of Google Headquarters
getDocumentNearBy(latitude: 37.422000, longitude: -122.084057, distance: 10)
}
UPDATE: Firestore does not support actual GeoPoint queries at present so while the below query executes successfully, it only filters by latitude, not by longitude and thus will return many results that are not nearby. The best solution would be to use geohashes. To learn how to do something similar yourself, have a look at this video.
(First let me apologize for all the code in this post, I just wanted anyone reading this answer to have an easy time reproducing the functionality.)
To address the same concern the OP had, at first I adapted the GeoFire library to work with Firestore (you can learn a lot about geo-stuff by looking at that library). Then I realized I didn't really mind if locations were returned in an exact circle. I just wanted some way to get 'nearby' locations.
I can't believe how long it took me to realize this, but you can just perform a double inequality query on a GeoPoint field using a SW corner and NE corner to get locations within a bounding box around a center point.
So I made a JavaScript function like the one below (this is basically a JS version of Ryan Lee's answer).
/**
* Get locations within a bounding box defined by a center point and distance from from the center point to the side of the box;
*
* #param {Object} area an object that represents the bounding box
* around a point in which locations should be retrieved
* #param {Object} area.center an object containing the latitude and
* longitude of the center point of the bounding box
* #param {number} area.center.latitude the latitude of the center point
* #param {number} area.center.longitude the longitude of the center point
* #param {number} area.radius (in kilometers) the radius of a circle
* that is inscribed in the bounding box;
* This could also be described as half of the bounding box's side length.
* #return {Promise} a Promise that fulfills with an array of all the
* retrieved locations
*/
function getLocations(area) {
// calculate the SW and NE corners of the bounding box to query for
const box = utils.boundingBoxCoordinates(area.center, area.radius);
// construct the GeoPoints
const lesserGeopoint = new GeoPoint(box.swCorner.latitude, box.swCorner.longitude);
const greaterGeopoint = new GeoPoint(box.neCorner.latitude, box.neCorner.longitude);
// construct the Firestore query
let query = firebase.firestore().collection('myCollection').where('location', '>', lesserGeopoint).where('location', '<', greaterGeopoint);
// return a Promise that fulfills with the locations
return query.get()
.then((snapshot) => {
const allLocs = []; // used to hold all the loc data
snapshot.forEach((loc) => {
// get the data
const data = loc.data();
// calculate a distance from the center
data.distanceFromCenter = utils.distance(area.center, data.location);
// add to the array
allLocs.push(data);
});
return allLocs;
})
.catch((err) => {
return new Error('Error while retrieving events');
});
}
The function above also adds a .distanceFromCenter property to each piece of location data that's returned so that you could get the circle-like behavior by just checking if that distance is within the range you want.
I use two util functions in the function above so here's the code for those as well. (All of the util functions below are actually adapted from the GeoFire library.)
distance():
/**
* Calculates the distance, in kilometers, between two locations, via the
* Haversine formula. Note that this is approximate due to the fact that
* the Earth's radius varies between 6356.752 km and 6378.137 km.
*
* #param {Object} location1 The first location given as .latitude and .longitude
* #param {Object} location2 The second location given as .latitude and .longitude
* #return {number} The distance, in kilometers, between the inputted locations.
*/
distance(location1, location2) {
const radius = 6371; // Earth's radius in kilometers
const latDelta = degreesToRadians(location2.latitude - location1.latitude);
const lonDelta = degreesToRadians(location2.longitude - location1.longitude);
const a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) +
(Math.cos(degreesToRadians(location1.latitude)) * Math.cos(degreesToRadians(location2.latitude)) *
Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2));
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return radius * c;
}
boundingBoxCoordinates(): (There are more utils used in here as well that I've pasted below.)
/**
* Calculates the SW and NE corners of a bounding box around a center point for a given radius;
*
* #param {Object} center The center given as .latitude and .longitude
* #param {number} radius The radius of the box (in kilometers)
* #return {Object} The SW and NE corners given as .swCorner and .neCorner
*/
boundingBoxCoordinates(center, radius) {
const KM_PER_DEGREE_LATITUDE = 110.574;
const latDegrees = radius / KM_PER_DEGREE_LATITUDE;
const latitudeNorth = Math.min(90, center.latitude + latDegrees);
const latitudeSouth = Math.max(-90, center.latitude - latDegrees);
// calculate longitude based on current latitude
const longDegsNorth = metersToLongitudeDegrees(radius, latitudeNorth);
const longDegsSouth = metersToLongitudeDegrees(radius, latitudeSouth);
const longDegs = Math.max(longDegsNorth, longDegsSouth);
return {
swCorner: { // bottom-left (SW corner)
latitude: latitudeSouth,
longitude: wrapLongitude(center.longitude - longDegs),
},
neCorner: { // top-right (NE corner)
latitude: latitudeNorth,
longitude: wrapLongitude(center.longitude + longDegs),
},
};
}
metersToLongitudeDegrees():
/**
* Calculates the number of degrees a given distance is at a given latitude.
*
* #param {number} distance The distance to convert.
* #param {number} latitude The latitude at which to calculate.
* #return {number} The number of degrees the distance corresponds to.
*/
function metersToLongitudeDegrees(distance, latitude) {
const EARTH_EQ_RADIUS = 6378137.0;
// this is a super, fancy magic number that the GeoFire lib can explain (maybe)
const E2 = 0.00669447819799;
const EPSILON = 1e-12;
const radians = degreesToRadians(latitude);
const num = Math.cos(radians) * EARTH_EQ_RADIUS * Math.PI / 180;
const denom = 1 / Math.sqrt(1 - E2 * Math.sin(radians) * Math.sin(radians));
const deltaDeg = num * denom;
if (deltaDeg < EPSILON) {
return distance > 0 ? 360 : 0;
}
// else
return Math.min(360, distance / deltaDeg);
}
wrapLongitude():
/**
* Wraps the longitude to [-180,180].
*
* #param {number} longitude The longitude to wrap.
* #return {number} longitude The resulting longitude.
*/
function wrapLongitude(longitude) {
if (longitude <= 180 && longitude >= -180) {
return longitude;
}
const adjusted = longitude + 180;
if (adjusted > 0) {
return (adjusted % 360) - 180;
}
// else
return 180 - (-adjusted % 360);
}
A new project has been introduced since #monkeybonkey first ask this question. The project is called GEOFirestore.
With this library you can perform queries like query documents within a circle:
const geoQuery = geoFirestore.query({
center: new firebase.firestore.GeoPoint(10.38, 2.41),
radius: 10.5
});
You can install GeoFirestore via npm. You will have to install Firebase separately (because it is a peer dependency to GeoFirestore):
$ npm install geofirestore firebase --save
As of today, there is no way to do such a query. There are other questions in SO related to it:
Is there a way to use GeoFire with Firestore?
How to query closest GeoPoints in a collection in Firebase Cloud Firestore?
Is there a way to use GeoFire with Firestore?
In my current Android project I may use https://github.com/drfonfon/android-geohash to add a geohash field while Firebase team is developing native support.
Using Firebase Realtime Database like suggested in other questions means that you can't filter your results set by location and other fields simultaneously, the main reason I want to switch to Firestore in the first place.
As of late 2020 there is now also documentation of how to do geoqueries with Firestore.
These solutions for iOS, Android, and Web, build on top of a slimmed down version of the Firebase-created GeoFire libraries, and then show how to:
Generate geohash values and store them in Firestore
Determine geohash ranges of the bounding box for a certain point and radius
Perform queries across these geohash ranges
This a bit more low-level than most of the other libraries presented here, so it may be a better fit for some use-cases and a worse fit for others.
Hijacking this thread to hopefully help anyone still looking. Firestore still does not support geo-based queries, and using the GeoFirestore library isnt ideal either as it will only let you search by location, nothing else.
I've put this together:
https://github.com/mbramwell1/GeoFire-Android
It basically lets you do nearby searches using a location and distance:
QueryLocation queryLocation = QueryLocation.fromDegrees(latitude, longitude);
Distance searchDistance = new Distance(1.0, DistanceUnit.KILOMETERS);
geoFire.query()
.whereNearTo(queryLocation, distance)
.build()
.get();
There are more docs on the repo. Its working for me so give it a try, hopefully it will do what you need.
For Dart
///
/// Checks if these coordinates are valid geo coordinates.
/// [latitude] The latitude must be in the range [-90, 90]
/// [longitude] The longitude must be in the range [-180, 180]
/// returns [true] if these are valid geo coordinates
///
bool coordinatesValid(double latitude, double longitude) {
return (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180);
}
///
/// Checks if the coordinates of a GeopPoint are valid geo coordinates.
/// [latitude] The latitude must be in the range [-90, 90]
/// [longitude] The longitude must be in the range [-180, 180]
/// returns [true] if these are valid geo coordinates
///
bool geoPointValid(GeoPoint point) {
return (point.latitude >= -90 &&
point.latitude <= 90 &&
point.longitude >= -180 &&
point.longitude <= 180);
}
///
/// Wraps the longitude to [-180,180].
///
/// [longitude] The longitude to wrap.
/// returns The resulting longitude.
///
double wrapLongitude(double longitude) {
if (longitude <= 180 && longitude >= -180) {
return longitude;
}
final adjusted = longitude + 180;
if (adjusted > 0) {
return (adjusted % 360) - 180;
}
// else
return 180 - (-adjusted % 360);
}
double degreesToRadians(double degrees) {
return (degrees * math.pi) / 180;
}
///
///Calculates the number of degrees a given distance is at a given latitude.
/// [distance] The distance to convert.
/// [latitude] The latitude at which to calculate.
/// returns the number of degrees the distance corresponds to.
double kilometersToLongitudeDegrees(double distance, double latitude) {
const EARTH_EQ_RADIUS = 6378137.0;
// this is a super, fancy magic number that the GeoFire lib can explain (maybe)
const E2 = 0.00669447819799;
const EPSILON = 1e-12;
final radians = degreesToRadians(latitude);
final numerator = math.cos(radians) * EARTH_EQ_RADIUS * math.pi / 180;
final denom = 1 / math.sqrt(1 - E2 * math.sin(radians) * math.sin(radians));
final deltaDeg = numerator * denom;
if (deltaDeg < EPSILON) {
return distance > 0 ? 360.0 : 0.0;
}
// else
return math.min(360.0, distance / deltaDeg);
}
///
/// Defines the boundingbox for the query based
/// on its south-west and north-east corners
class GeoBoundingBox {
final GeoPoint swCorner;
final GeoPoint neCorner;
GeoBoundingBox({this.swCorner, this.neCorner});
}
///
/// Defines the search area by a circle [center] / [radiusInKilometers]
/// Based on the limitations of FireStore we can only search in rectangles
/// which means that from this definition a final search square is calculated
/// that contains the circle
class Area {
final GeoPoint center;
final double radiusInKilometers;
Area(this.center, this.radiusInKilometers):
assert(geoPointValid(center)), assert(radiusInKilometers >= 0);
factory Area.inMeters(GeoPoint gp, int radiusInMeters) {
return new Area(gp, radiusInMeters / 1000.0);
}
factory Area.inMiles(GeoPoint gp, int radiusMiles) {
return new Area(gp, radiusMiles * 1.60934);
}
/// returns the distance in km of [point] to center
double distanceToCenter(GeoPoint point) {
return distanceInKilometers(center, point);
}
}
///
///Calculates the SW and NE corners of a bounding box around a center point for a given radius;
/// [area] with the center given as .latitude and .longitude
/// and the radius of the box (in kilometers)
GeoBoundingBox boundingBoxCoordinates(Area area) {
const KM_PER_DEGREE_LATITUDE = 110.574;
final latDegrees = area.radiusInKilometers / KM_PER_DEGREE_LATITUDE;
final latitudeNorth = math.min(90.0, area.center.latitude + latDegrees);
final latitudeSouth = math.max(-90.0, area.center.latitude - latDegrees);
// calculate longitude based on current latitude
final longDegsNorth = kilometersToLongitudeDegrees(area.radiusInKilometers, latitudeNorth);
final longDegsSouth = kilometersToLongitudeDegrees(area.radiusInKilometers, latitudeSouth);
final longDegs = math.max(longDegsNorth, longDegsSouth);
return new GeoBoundingBox(
swCorner: new GeoPoint(latitudeSouth, wrapLongitude(area.center.longitude - longDegs)),
neCorner: new GeoPoint(latitudeNorth, wrapLongitude(area.center.longitude + longDegs)));
}
///
/// Calculates the distance, in kilometers, between two locations, via the
/// Haversine formula. Note that this is approximate due to the fact that
/// the Earth's radius varies between 6356.752 km and 6378.137 km.
/// [location1] The first location given
/// [location2] The second location given
/// sreturn the distance, in kilometers, between the two locations.
///
double distanceInKilometers(GeoPoint location1, GeoPoint location2) {
const radius = 6371; // Earth's radius in kilometers
final latDelta = degreesToRadians(location2.latitude - location1.latitude);
final lonDelta = degreesToRadians(location2.longitude - location1.longitude);
final a = (math.sin(latDelta / 2) * math.sin(latDelta / 2)) +
(math.cos(degreesToRadians(location1.latitude)) *
math.cos(degreesToRadians(location2.latitude)) *
math.sin(lonDelta / 2) *
math.sin(lonDelta / 2));
final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
return radius * c;
}
I just published a Flutter package based on the JS code above
https://pub.dartlang.org/packages/firestore_helpers
Yes, this is an old topic, but I want to help only on Java code. How I solved a problem with longitude? I used a code from Ryan Lee and Michael Teper.
A code:
#Override
public void getUsersForTwentyMiles() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
double latitude = 33.0076665;
double longitude = 35.1011336;
int distance = 20; //20 milles
GeoPoint lg = new GeoPoint(latitude, longitude);
// ~1 mile of lat and lon in degrees
double lat = 0.0144927536231884;
double lon = 0.0181818181818182;
final double lowerLat = latitude - (lat * distance);
final double lowerLon = longitude - (lon * distance);
double greaterLat = latitude + (lat * distance);
final double greaterLon = longitude + (lon * distance);
final GeoPoint lesserGeopoint = new GeoPoint(lowerLat, lowerLon);
final GeoPoint greaterGeopoint = new GeoPoint(greaterLat, greaterLon);
Log.d(LOG_TAG, "local general lovation " + lg);
Log.d(LOG_TAG, "local lesserGeopoint " + lesserGeopoint);
Log.d(LOG_TAG, "local greaterGeopoint " + greaterGeopoint);
//get users for twenty miles by only a latitude
db.collection("users")
.whereGreaterThan("location", lesserGeopoint)
.whereLessThan("location", greaterGeopoint)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
UserData user = document.toObject(UserData.class);
//here a longitude condition (myLocation - 20 <= myLocation <= myLocation +20)
if (lowerLon <= user.getUserGeoPoint().getLongitude() && user.getUserGeoPoint().getLongitude() <= greaterLon) {
Log.d(LOG_TAG, "location: " + document.getId());
}
}
} else {
Log.d(LOG_TAG, "Error getting documents: ", task.getException());
}
}
});
}
Just inside after issuing the result set the filter to longitude:
if (lowerLon <= user.getUserGeoPoint().getLongitude() && user.getUserGeoPoint().getLongitude() <= greaterLon) {
Log.d(LOG_TAG, "location: " + document.getId());
}
I hope this will help someone.
Have a nice day!
You should use GeoFire (works with Firestore). With this you can filter documents on server and read less documents from your Firestore db. This will reduce your read count as well.
Check this lib for GroFire: https://github.com/patpatchpatrick/GeoFirestore-iOS
"patpatchpatrick" made this to Swift 5 compatible.
Just do a pod install as follows:
pod 'Geofirestore', :git => 'https://github.com/patpatchpatrick/GeoFirestore-iOS'
I am using this library in one of my projects and it works fine.
To set a location:
let location: CLLocation = CLLocation(latitude: lat, longitude: lng)
yourCollection.setLocation(location: location, forDocumentWithID: "YourDocId") { (error) in }
To remove location:
collection.removeLocation(forDocumentWithID: "YourDocId")
To get docs:
let center = CLLocation(latitude: lat, longitude: lng)
let collection = "Your collection path"
let circleQuery = collection.query(withCenter: center, radius: Double(yourRadiusVal))
let _ = circleQuery.observe(.documentEntered, with: { (key, location) in
//Use info as per your need
})
I have used .documentEntered, you can use other available geo queries like (Document Exited, Document Moved) as per your need.
You can query using GeoPoint as well.
This is not fully tested yet it should be a bit of an improvement on Ryan Lee's answer
My calculation is more accurate and then I filter the answers to remove hits which fall within the bounding box but outside the radius
Swift 4
func getDocumentNearBy(latitude: Double, longitude: Double, meters: Double) {
let myGeopoint = GeoPoint(latitude:latitude, longitude:longitude )
let r_earth : Double = 6378137 // Radius of earth in Meters
// 1 degree lat in m
let kLat = (2 * Double.pi / 360) * r_earth
let kLon = (2 * Double.pi / 360) * r_earth * __cospi(latitude/180.0)
let deltaLat = meters / kLat
let deltaLon = meters / kLon
let swGeopoint = GeoPoint(latitude: latitude - deltaLat, longitude: longitude - deltaLon)
let neGeopoint = GeoPoint(latitude: latitude + deltaLat, longitude: longitude + deltaLon)
let docRef : CollectionReference = appDelegate.db.collection("restos")
let query = docRef.whereField("location", isGreaterThan: swGeopoint).whereField("location", isLessThan: neGeopoint)
query.getDocuments { snapshot, error in
guard let snapshot = snapshot else {
print("Error fetching snapshot results: \(error!)")
return
}
self.documents = snapshot.documents.filter { (document) in
if let location = document.get("location") as? GeoPoint {
let myDistance = self.distanceBetween(geoPoint1:myGeopoint,geoPoint2:location)
print("myDistance:\(myDistance) distance:\(meters)")
return myDistance <= meters
}
return false
}
}
}
Functions which accurately measure the distance in Meters between 2 Geopoints for filtering
func distanceBetween(geoPoint1:GeoPoint, geoPoint2:GeoPoint) -> Double{
return distanceBetween(lat1: geoPoint1.latitude,
lon1: geoPoint1.longitude,
lat2: geoPoint2.latitude,
lon2: geoPoint2.longitude)
}
func distanceBetween(lat1:Double, lon1:Double, lat2:Double, lon2:Double) -> Double{ // generally used geo measurement function
let R : Double = 6378.137; // Radius of earth in KM
let dLat = lat2 * Double.pi / 180 - lat1 * Double.pi / 180;
let dLon = lon2 * Double.pi / 180 - lon1 * Double.pi / 180;
let a = sin(dLat/2) * sin(dLat/2) +
cos(lat1 * Double.pi / 180) * cos(lat2 * Double.pi / 180) *
sin(dLon/2) * sin(dLon/2);
let c = 2 * atan2(sqrt(a), sqrt(1-a));
let d = R * c;
return d * 1000; // meters
}
The easiest way is to calculate a "geo hash" when storing the location in the database.
A geo hash is a string which represents a location down to a certain accuracy. The longer the geo hash, the closer the locations with said geo hash must be. Two location which are e.g. 100m apart may have the same 6-char geo hash but when calculating a 7-char geo hash the last char might be different.
There are plenty libraries allowing you to calculate geo hashes for any language. Just store it alongside the location and use a == query to find locations with the same geo hash.
In javascript you can simply
const db = firebase.firestore();
//Geofire
import { GeoCollectionReference, GeoFirestore, GeoQuery, GeoQuerySnapshot } from 'geofirestore';
// Create a GeoFirestore reference
const geofirestore: GeoFirestore = new GeoFirestore(db);
// Create a GeoCollection reference
const geocollection: GeoCollectionReference = geofirestore.collection('<Your_collection_name>');
const query: GeoQuery = geocollectionDrivers.near({
center: new firebase.firestore.GeoPoint(location.latitude, location.longitude),
radius: 10000
});
query.onSnapshot(gquerySnapshot => {
gquerySnapshot.forEach(res => {
console.log(res.data());
})
});
A workaround for Flutter till we have native query in Firestore to pull ordered documents based on lat/long:
https://pub.dev/packages/geoflutterfire
A plugin to store geo hashes in the Firestore and query the same.
Limitations: limit not supported
There's a GeoFire library for Firestore called Geofirestore: https://github.com/imperiumlabs/GeoFirestore (Disclaimer: I helped develop it). It's super easy to use and offers the same features for Firestore that Geofire does for Firebase Realtime DB)
Related
creating a right angled rectangle on google maps from a line
I have an application where the user draws a line, then automatically the software should create a rectangle where the line intersects the midpoint of two adjacent sides of a right angled rectangle. See below, the red line is user drawn and the purple rectangle is calculated. My algorithm almost works, in some directions the rectangles aren't right angled. I want to know why, there is a fair amount of approximation in my code, which should be fine based on the scales I use (a few km). I shouldn't have to worry about the curvature of the earth. Anybody know how I can improve my code? Here are my references: calculate points lat and long to meters public static MapRectangle CreatMapRectangle(BalingZone zone, double thickness) { MapsCoordinate rectpoint2; MapsCoordinate rectPoint1 ; MapsCoordinate rectPoint3 ; MapsCoordinate rectPoint4 ; var point1 = zone.Coordinates[0]; var point2 = zone.Coordinates[1]; var latitudeDiff = LatitudeDiffToMeters(point2.Latitude - point1.Latitude); var longitudeDiff = LongitudeDiffToMeters(point1.Longitude - point2.Longitude, point1.Latitude); var slopeB = longitudeDiff / latitudeDiff; double latOffset = thickness * (slopeB / Math.Sqrt(1 + slopeB * slopeB)); double longOffset = thickness * (1 / Math.Sqrt(1 + slopeB * slopeB)); double p3Lat = CalculateLatitude(point1.Latitude, latOffset); double p3Long = CalculateLongitude( point1.Longitude, p3Lat , longOffset); rectPoint1 = new MapsCoordinate(p3Lat, p3Long); double p4Lat = CalculateLatitude(point1.Latitude, -latOffset); double p4Long = CalculateLongitude(point1.Longitude, p4Lat, -longOffset); rectpoint2 = new MapsCoordinate(p4Lat, p4Long); double p5Lat = CalculateLatitude(point2.Latitude, latOffset); double p5Long = CalculateLongitude( point2.Longitude, p5Lat , longOffset); rectPoint4 = new MapsCoordinate(p5Lat, p5Long); double p6Lat = CalculateLatitude(point2.Latitude, -latOffset); double p6Long = CalculateLongitude( point2.Longitude, p6Lat , -longOffset); rectPoint3 = new MapsCoordinate(p6Lat, p6Long); return new MapRectangle(rectPoint4, rectPoint3, rectPoint1, rectpoint2, thickness); } //use the quick and dirty estimate that 111,111 meters (111.111 km) in the y direction is 1 degree (of latitude) // and 111,111 * cos(latitude) meters in the x direction is 1 degree (of longitude). private static double LatitudeDiffToMeters(double latitudeDiff) { return 111111.0 * latitudeDiff; } private static double LongitudeDiffToMeters(double longitudeDiff, double latitude) { return 111111.0*Math.Cos(latitude)*longitudeDiff; } private static double CalculateLatitude(double latitude, double offset) { return latitude + offset/111111.0; } private static double CalculateLongitude(double longitude, double latitude, double offset) { return longitude + offset/(111111.0*Math.Cos(latitude)); } }
Not sure why the question was voted down! Anyway,there was an error in my code, i was using degrees instead of radians in the math functions. I also updated the calculations to assume a spherical earth (to work better closer to the poles) private static double LatitudeDiffToMeters(double latitudeDiff) { return (R*latitudeDiff*Math.PI)/180; } private static double LongitudeDiffToMeters(double longitudeDiff, double latitude) { return (longitudeDiff*Math.PI* R * Math.Cos (Math.PI * latitude / 180))/180; } private static double CalculateLatitude(double latitude, double offset) { return latitude + ((offset/R) * (180/Math.PI)); } private static double CalculateLongitude(double longitude, double latitude, double offset) { return longitude + offset / (R * Math.Cos (Math.PI * latitude / 180)) * (180 / Math.PI); }
How can I rotate a marker towards another point (Location)?
I basically want to know how it'd be possible to calculate and rotate a marker towards another Location point (Lat & Lng). This is using the Google Maps API v2 for Android.
Use a custom bitmap for your marker and rotate it with a matrix before creating the bitmapdescriptor and displaying the marker. works with values of 90, 270 but something similar should work for you. Matrix matrix = new Matrix(); matrix.postRotate(orientation); // anti-clockwise Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); bm = rotatedBitmap.copy(rotatedBitmap.getConfig(), true); BitmapDescriptor bd = BitmapDescriptorFactory.fromBitmap(bm); markeroptions.icon(bd); This is from the google map utils on github https://github.com/googlemaps/android-maps-utils /** * Returns the heading from one LatLng to another LatLng. Headings are * expressed in degrees clockwise from North within the range [-180,180). * #return The heading in degrees clockwise from north. */ public static double computeHeading(LatLng from, LatLng to) { // http://williams.best.vwh.net/avform.htm#Crs double fromLat = toRadians(from.latitude); double fromLng = toRadians(from.longitude); double toLat = toRadians(to.latitude); double toLng = toRadians(to.longitude); double dLng = toLng - fromLng; double heading = atan2( sin(dLng) * cos(toLat), cos(fromLat) * sin(toLat) - sin(fromLat) * cos(toLat) * cos(dLng)); return wrap(toDegrees(heading), -180, 180); }
Calculating new longitude, latitude from old + n meters
I want to create 2 new longitude and 2 new latitudes based on a coordinate and a distance in meters, I want to create a nice bounding box around a certain point. It is for a part of a city and max ±1500 meters. I therefore don't think the curvature of earth has to be taken into account. So I have 50.0452345 (x) and 4.3242234 (y) and I want to know x + 500 meters, x - 500 meters, y - 500 meters, y + 500 meters I found many algorithms but almost all seem to deal with the distance between points.
The number of kilometers per degree of longitude is approximately (pi/180) * r_earth * cos(theta*pi/180) where theta is the latitude in degrees and r_earth is approximately 6378 km. The number of kilometers per degree of latitude is approximately the same at all locations, approx (pi/180) * r_earth = 111 km / degree So you can do: new_latitude = latitude + (dy / r_earth) * (180 / pi); new_longitude = longitude + (dx / r_earth) * (180 / pi) / cos(latitude * pi/180); As long as dx and dy are small compared to the radius of the earth and you don't get too close to the poles.
The accepted answer is perfectly right and works. I made some tweaks and turned into this: double meters = 50; // number of km per degree = ~111km (111.32 in google maps, but range varies // between 110.567km at the equator and 111.699km at the poles) // // 111.32km = 111320.0m (".0" is used to make sure the result of division is // double even if the "meters" variable can't be explicitly declared as double) double coef = meters / 111320.0; double new_lat = my_lat + coef; // pi / 180 ~= 0.01745 double new_long = my_long + coef / Math.cos(my_lat * 0.01745); Hope this helps too.
For latitude do: var earth = 6378.137, //radius of the earth in kilometer pi = Math.PI, m = (1 / ((2 * pi / 360) * earth)) / 1000; //1 meter in degree var new_latitude = latitude + (your_meters * m); For longitude do: var earth = 6378.137, //radius of the earth in kilometer pi = Math.PI, cos = Math.cos, m = (1 / ((2 * pi / 360) * earth)) / 1000; //1 meter in degree var new_longitude = longitude + (your_meters * m) / cos(latitude * (pi / 180)); The variable your_meters can contain a positive or a negative value.
I had to spend about two hours to work out the solution by #nibot , I simply needed a method to create a boundary box given its center point and width/height (or radius) in kilometers: I don't fully understand the solution mathematically/ geographically. I tweaked the solution (by trial and error) to get the four coordinates. Distances in km, given the current position and distance we shift to the new position in the four coordinates: North: private static Position ToNorthPosition(Position center, double northDistance) { double r_earth = 6378; var pi = Math.PI; var new_latitude = center.Lat + (northDistance / r_earth) * (180 / pi); return new Position(new_latitude, center.Long); } East: private static Position ToEastPosition(Position center, double eastDistance) { double r_earth = 6378; var pi = Math.PI; var new_longitude = center.Long + (eastDistance / r_earth) * (180 / pi) / Math.Cos(center.Lat * pi / 180); return new Position(center.Lat, new_longitude); } South: private static Position ToSouthPosition(Position center, double southDistance) { double r_earth = 6378; var pi = Math.PI; var new_latitude = center.Lat - (southDistance / r_earth) * (180 / pi); return new Position(new_latitude, center.Long); } West: private static Position ToWestPosition(Position center, double westDistance) { double r_earth = 6378; var pi = Math.PI; var new_longitude = center.Long - (westDistance / r_earth) * (180 / pi) / Math.Cos(center.Lat * pi / 180); return new Position(center.Lat, new_longitude); }
Have you checked out: How do I find the lat/long that is x km north of a given lat/long ? These calculations are annoying at best, I've done many of them. The haversine formula will be your friend. Some reference: http://www.movable-type.co.uk/scripts/latlong.html
Posting this method for sake of completeness. Use this method "as it is" to: Move any (lat,long) point by given meters in either axis. Python method to move any point by defined meters. def translate_latlong(lat,long,lat_translation_meters,long_translation_meters): ''' method to move any lat,long point by provided meters in lat and long direction. params : lat,long: lattitude and longitude in degrees as decimal values, e.g. 37.43609517497065, -122.17226450150885 lat_translation_meters: movement of point in meters in lattitude direction. positive value: up move, negative value: down move long_translation_meters: movement of point in meters in longitude direction. positive value: left move, negative value: right move ''' earth_radius = 6378.137 #Calculate top, which is lat_translation_meters above m_lat = (1 / ((2 * math.pi / 360) * earth_radius)) / 1000; lat_new = lat + (lat_translation_meters * m_lat) #Calculate right, which is long_translation_meters right m_long = (1 / ((2 * math.pi / 360) * earth_radius)) / 1000; # 1 meter in degree long_new = long + (long_translation_meters * m_long) / math.cos(lat * (math.pi / 180)); return lat_new,long_new
Working Python code to offset coordinates by 10 metres. def add_blur(lat, long): meters = 10 blur_factor = meters * 0.000006279 new_lat = lat + blur_factor new_long = long + blur_factor / math.cos(lat * 0.018) return new_lat, new_long
if you don't have to be very exact then: each 10000 meters is about 0.1 for latitude and longitude. for example I want to load locations 3000 meters around point_A from my database: double newMeter = 3000 * 0.1 / 10000; double lat1 = point_A.latitude - newMeter; double lat2 = point_A.latitude + newMeter; double lon1 = point_A.longitude - newMeter; double lon1 = point_A.longitude + newMeter; Cursor c = mDb.rawQuery("select * from TABLE1 where lat >= " + lat1 + " and lat <= " + lat2 + " and lon >= " + lon1 + " and lon <= " + lon2 + " order by id", null);
public double MeterToDegree(double meters, double latitude) { return meters / (111.32 * 1000 * Math.Cos(latitude * (Math.PI / 180))); }
var meters = 50; var coef = meters * 0.0000089; var new_lat = map.getCenter().lat.apply() + coef; var new_long = map.getCenter().lng.apply() + coef / Math.cos(new_lat * 0.018); map.setCenter({lat:new_lat, lng:new_long});
See from Official Google Maps Documentation (link below) as they solve on easy/simple maps the problems with distance by countries :) I recommended this solution to easy/simply solve issue with boundaries that you can know which area you're solving the problem with boundaries (not recommended globally) Note: Latitude lines run west-east and mark the position south-north of a point. Lines of latitude are called parallels and in total there are 180 degrees of latitude. The distance between each degree of latitude is about 69 miles (110 kilometers). The distance between longitudes narrows the further away from the equator. The distance between longitudes at the equator is the same as latitude, roughly 69 miles (110 kilometers) . At 45 degrees north or south, the distance between is about 49 miles (79 kilometers). The distance between longitudes reaches zero at the poles as the lines of meridian converge at that point. Original source 1 Original source 2 Official Google Maps Documentation: Code Example: Autocomplete Restricted to Multiple Countries See the part of their code how they solve problem with distance center + 10 kilometers by +/- 0.1 degree function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 50.064192, lng: -130.605469 }, zoom: 3, } ); const card = document.getElementById("pac-card") as HTMLElement; map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card); const center = { lat: 50.064192, lng: -130.605469 }; // Create a bounding box with sides ~10km away from the center point const defaultBounds = { north: center.lat + 0.1, south: center.lat - 0.1, east: center.lng + 0.1, west: center.lng - 0.1, }; const input = document.getElementById("pac-input") as HTMLInputElement; const options = { bounds: defaultBounds, componentRestrictions: { country: "us" }, fields: ["address_components", "geometry", "icon", "name"], origin: center, strictBounds: false, types: ["establishment"], };
This is what I did in VBA that seems to be working for me. Calculation is in feet not meters though Public Function CalcLong(OrigLong As Double, OrigLat As Double, DirLong As String, DirLat As String, DistLong As Double, DistLat As Double) Dim FT As Double Dim NewLong, NewLat As Double FT = 1 / ((2 * WorksheetFunction.Pi / 360) * 20902230.971129) If DirLong = "W" Then NewLat = CalcLat(OrigLong, OrigLat, DirLong, DirLat, DistLong, DistLat) NewLong = OrigLong - ((FT * DistLong) / Cos(NewLat * (WorksheetFunction.Pi / 180))) CalcLong = NewLong Else NewLong = OrigLong + ((FT * DistLong) / Math.Cos(CalcLat(OrigLong, OrigLat, DirLong, DirLat, DistLong, DistLat) * (WorksheetFunction.Pi / 180))) CalcLong = NewLong End If End Function Public Function CalcLat(OrigLong As Double, OrigLat As Double, DirLong As String, DirLat As String, DistLong As Double, DistLat As Double) As Double Dim FT As Double Dim NewLat As Double FT = 1 / ((2 * WorksheetFunction.Pi / 360) * 20902230.971129) If DirLat = "S" Then NewLat = (OrigLat - (FT * DistLat)) CalcLat = NewLat Else NewLat = (OrigLat + (FT * DistLat)) CalcLat = NewLat End If End Function
Original poster said: "So I have 50.0452345 (x) and 4.3242234 (y) and I want to know x + 500 meters..." I will assume the units of the x and y values he gave there were in meters (and not degrees Longitude, Latitude). If so then he is stating measurements to 0.1 micrometer, so I will assume he needs similar accuracy for the translated output. I also will assume by "+500 meters" etc. he meant the direction to be due North-South and due East-West. He refers to a reference point: "2 new latitudes based on a coordinate"; but he did not give the Longitude and Latitude, so to explain the procedure concretely I will give the Latitudes and Longitudes for the corners of the 500 meter box he requested around the point [30 degrees Longitude,30 degrees Latitude]. The exact solution on the surface of the GRS80 Ellipsoid is given with the following set of functions (I wrote these for the free-open-source-mac-pc math program called "PARI" which allows any number of digits precision to be setup): \\=======Arc lengths along Latitude and Longitude and the respective scales: dms(u)=[truncate(u),truncate((u-truncate(u))*60),((u-truncate(u))*60-truncate((u-truncate(u))*60))*60]; SpinEarthRadiansPerSec=7.292115e-5;\ GMearth=3986005e8;\ J2earth=108263e-8;\ re=6378137;\ ecc=solve(ecc=.0001,.9999,eccp=ecc/sqrt(1-ecc^2);qecc=(1+3/eccp^2)*atan(eccp)-3/eccp;ecc^2-(3*J2earth+4/15*SpinEarthRadiansPerSec^2*re^3/GMearth*ecc^3/qecc));\ e2=ecc^2;\ b2=1-e2;\ b=sqrt(b2);\ fl=1-b;\ rfl=1/fl;\ U0=GMearth/ecc/re*atan(eccp)+1/3*SpinEarthRadiansPerSec^2*re^2;\ HeightAboveEllipsoid=0;\ reh=re+HeightAboveEllipsoid;\ longscale(lat)=reh*Pi/648000/sqrt(1+b2*(tan(lat))^2); latscale(lat)=reh*b*Pi/648000/(1-e2*(sin(lat))^2)^(3/2); longarc(lat,long1,long2)=longscale(lat)*648000/Pi*(long2-long1); latarc(lat1,lat2)=(intnum(th=lat1,lat2,sqrt(1-e2*(sin(th))^2))+e2/2*sin(2*lat1)/sqrt(1-e2*(sin(lat1))^2)-e2/2*sin(2*lat2)/sqrt(1-e2*(sin(lat2))^2))*reh; \\======= I then plugged the reference point [30,30] into those functions at the PARI command prompt and had PARI solve for the point +/- 500 meters away from it, giving the two new Longitudes and two new Latitudes that the original poster asked for. Here is the input and output showing that: ? dms(solve(x=29,31,longarc(30*Pi/180,30*Pi/180,x*Pi/180)+500)) cpu time = 1 ms, real time = 1 ms. %1172 = [29, 59, 41.3444979398934670450280297216509190843055] ? dms(solve(x=29,31,longarc(30*Pi/180,30*Pi/180,x*Pi/180)-500)) cpu time = 1 ms, real time = 1 ms. %1173 = [30, 0, 18.6555020601065329549719702783490809156945] ? dms(solve(x=29,31,latarc(30*Pi/180,x*Pi/180)+500)) cpu time = 1,357 ms, real time = 1,358 ms. %1174 = [29, 59, 43.7621925447500548285775757329518579545513] ? dms(solve(x=29,31,latarc(30*Pi/180,x*Pi/180)-500)) cpu time = 1,365 ms, real time = 1,368 ms. %1175 = [30, 0, 16.2377963202802863245716034907838199823349] ?
Latitude longitude mapping with image of my house
I have top view image of my house and know lat longitude for left top pixel i.e 0,0 now how to get latitude longitude values of other pixels? While googling i came across following: function longToX(longitudeDegrees) { var longitude =degreesToRadians(longitudeDegrees); return (radius * longitude); } function latToY(latitudeDegrees) { var latitude =degreesToRadians(latitudeDegrees); var newy = radius/2.0 * Math.log( (1.0 + Math.sin(latitude)) / (1.0 - Math.sin(latitude)) ); return newy; } function xToLong(xx) { var longRadians = xx/radius; var longDegrees = radiansToDegrees(longRadians); /* The user could have panned around the world a lot of times. Lat long goes from -180 to 180. So every time a user gets to 181 we want to subtract 360 degrees. Every time a user gets to -181 we want to add 360 degrees. */ var rotations = Math.floor((longDegrees + 180)/360) var longitude = longDegrees - (rotations * 360) return longitude; } function yToLat(yo) { var latitude = (Math.PI/2) - (2 * Math.atan( Math.exp(-1.0 * yo / radius))); return radiansToDegrees(latitude); } radius = 6371 km radius of earth.. but however if i pass 0,0 i get 0,0 how to fix latitude longitude for 0,0 and derive from it other values
You need to know the distance between pixels. To convert distance to degrees, you can use the fact that one nautical minute (1/60 of a degree) latitude is 1,852 meters or about 6076 feet. The longitude is the same at the equator, but you need to multiply by the cosine of the latitude elsewhere. One degree latitude = 1852 meters One degree longitude = 1852 meters * cos(latitude) (You might need to multiply the degrees latitude by pi/180 to convert them to radians for the cosine function.)
Calculate distance between two latitude-longitude points? (Haversine formula)
How do I calculate the distance between two points specified by latitude and longitude? For clarification, I'd like the distance in kilometers; the points use the WGS84 system and I'd like to understand the relative accuracies of the approaches available.
This link might be helpful to you, as it details the use of the Haversine formula to calculate the distance. Excerpt: This script [in Javascript] calculates great-circle distances between the two points – that is, the shortest distance over the earth’s surface – using the ‘Haversine’ formula. function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) { var R = 6371; // Radius of the earth in km var dLat = deg2rad(lat2-lat1); // deg2rad below var dLon = deg2rad(lon2-lon1); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2) ; var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c; // Distance in km return d; } function deg2rad(deg) { return deg * (Math.PI/180) }
I needed to calculate a lot of distances between the points for my project, so I went ahead and tried to optimize the code, I have found here. On average in different browsers my new implementation runs 2 times faster than the most upvoted answer. function distance(lat1, lon1, lat2, lon2) { var p = 0.017453292519943295; // Math.PI / 180 var c = Math.cos; var a = 0.5 - c((lat2 - lat1) * p)/2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))/2; return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km } You can play with my jsPerf and see the results here. Recently I needed to do the same in python, so here is a python implementation: from math import cos, asin, sqrt, pi def distance(lat1, lon1, lat2, lon2): p = pi/180 a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2 return 12742 * asin(sqrt(a)) #2*R*asin... And for the sake of completeness: Haversine on Wikipedia.
Here is a C# Implementation: static class DistanceAlgorithm { const double PIx = 3.141592653589793; const double RADIUS = 6378.16; /// <summary> /// Convert degrees to Radians /// </summary> /// <param name="x">Degrees</param> /// <returns>The equivalent in radians</returns> public static double Radians(double x) { return x * PIx / 180; } /// <summary> /// Calculate the distance between two places. /// </summary> /// <param name="lon1"></param> /// <param name="lat1"></param> /// <param name="lon2"></param> /// <param name="lat2"></param> /// <returns></returns> public static double DistanceBetweenPlaces( double lon1, double lat1, double lon2, double lat2) { double dlon = Radians(lon2 - lon1); double dlat = Radians(lat2 - lat1); double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2)); double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); return angle * RADIUS; } }
Here is a java implementation of the Haversine formula. public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371; public int calculateDistanceInKilometer(double userLat, double userLng, double venueLat, double venueLng) { double latDistance = Math.toRadians(userLat - venueLat); double lngDistance = Math.toRadians(userLng - venueLng); double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat)) * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c)); } Note that here we are rounding the answer to the nearest km.
Thanks very much for all this. I used the following code in my Objective-C iPhone app: const double PIx = 3.141592653589793; const double RADIO = 6371; // Mean radius of Earth in Km double convertToRadians(double val) { return val * PIx / 180; } -(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { double dlon = convertToRadians(place2.longitude - place1.longitude); double dlat = convertToRadians(place2.latitude - place1.latitude); double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2); double angle = 2 * asin(sqrt(a)); return angle * RADIO; } Latitude and Longitude are in decimal. I didn't use min() for the asin() call as the distances that I'm using are so small that they don't require it. It gave incorrect answers until I passed in the values in Radians - now it's pretty much the same as the values obtained from Apple's Map app :-) Extra update: If you are using iOS4 or later then Apple provide some methods to do this so the same functionality would be achieved with: -(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { MKMapPoint start, finish; start = MKMapPointForCoordinate(place1); finish = MKMapPointForCoordinate(place2); return MKMetersBetweenMapPoints(start, finish) / 1000; }
This is a simple PHP function that will give a very reasonable approximation (under +/-1% error margin). <?php function distance($lat1, $lon1, $lat2, $lon2) { $pi80 = M_PI / 180; $lat1 *= $pi80; $lon1 *= $pi80; $lat2 *= $pi80; $lon2 *= $pi80; $r = 6372.797; // mean radius of Earth in km $dlat = $lat2 - $lat1; $dlon = $lon2 - $lon1; $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $km = $r * $c; //echo '<br/>'.$km; return $km; } ?> As said before; the earth is NOT a sphere. It is like an old, old baseball that Mark McGwire decided to practice with - it is full of dents and bumps. The simpler calculations (like this) treat it like a sphere. Different methods may be more or less precise according to where you are on this irregular ovoid AND how far apart your points are (the closer they are the smaller the absolute error margin). The more precise your expectation, the more complex the math. For more info: wikipedia geographic distance
I post here my working example. List all points in table having distance between a designated point (we use a random point - lat:45.20327, long:23.7806) less than 50 KM, with latitude & longitude, in MySQL (the table fields are coord_lat and coord_long): List all having DISTANCE<50, in Kilometres (considered Earth radius 6371 KM): SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta FROM obiective WHERE coord_lat<>'' AND coord_long<>'' HAVING distanta<50 ORDER BY distanta desc The above example was tested in MySQL 5.0.95 and 5.5.16 (Linux).
In the other answers an implementation in r is missing. Calculating the distance between two point is quite straightforward with the distm function from the geosphere package: distm(p1, p2, fun = distHaversine) where: p1 = longitude/latitude for point(s) p2 = longitude/latitude for point(s) # type of distance calculation fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid As the earth is not perfectly spherical, the Vincenty formula for ellipsoids is probably the best way to calculate distances. Thus in the geosphere package you use then: distm(p1, p2, fun = distVincentyEllipsoid) Off course you don't necessarily have to use geosphere package, you can also calculate the distance in base R with a function: hav.dist <- function(long1, lat1, long2, lat2) { R <- 6371 diff.long <- (long2 - long1) diff.lat <- (lat2 - lat1) a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2 b <- 2 * asin(pmin(1, sqrt(a))) d = R * b return(d) }
The haversine is definitely a good formula for probably most cases, other answers already include it so I am not going to take the space. But it is important to note that no matter what formula is used (yes not just one). Because of the huge range of accuracy possible as well as the computation time required. The choice of formula requires a bit more thought than a simple no brainer answer. This posting from a person at nasa, is the best one I found at discussing the options http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html For example, if you are just sorting rows by distance in a 100 miles radius. The flat earth formula will be much faster than the haversine. HalfPi = 1.5707963; R = 3956; /* the radius gives you the measurement unit*/ a = HalfPi - latoriginrad; b = HalfPi - latdestrad; u = a * a + b * b; v = - 2 * a * b * cos(longdestrad - longoriginrad); c = sqrt(abs(u + v)); return R * c; Notice there is just one cosine and one square root. Vs 9 of them on the Haversine formula.
There could be a simpler solution, and more correct: The perimeter of earth is 40,000Km at the equator, about 37,000 on Greenwich (or any longitude) cycle. Thus: pythagoras = function (lat1, lon1, lat2, lon2) { function sqr(x) {return x * x;} function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);} var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0); var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0; var dy = 37000000.0 * (lat1 - lat2) / 360.0; return Math.sqrt(sqr(dx) + sqr(dy)); }; I agree that it should be fine-tuned as, I myself said that it's an ellipsoid, so the radius to be multiplied by the cosine varies. But it's a bit more accurate. Compared with Google Maps and it did reduce the error significantly.
pip install haversine Python implementation Origin is the center of the contiguous United States. from haversine import haversine, Unit origin = (39.50, 98.35) paris = (48.8567, 2.3508) haversine(origin, paris, unit=Unit.MILES) To get the answer in kilometers simply set unit=Unit.KILOMETERS (that's the default).
There is some errors in the code provided, I've fixed it below. All the above answers assumes the earth is a sphere. However, a more accurate approximation would be that of an oblate spheroid. a= 6378.137#equitorial radius in km b= 6356.752#polar radius in km def Distance(lat1, lons1, lat2, lons2): lat1=math.radians(lat1) lons1=math.radians(lons1) R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1 x1=R1*math.cos(lat1)*math.cos(lons1) y1=R1*math.cos(lat1)*math.sin(lons1) z1=R1*math.sin(lat1) lat2=math.radians(lat2) lons2=math.radians(lons2) R2=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2 x2=R2*math.cos(lat2)*math.cos(lons2) y2=R2*math.cos(lat2)*math.sin(lons2) z2=R2*math.sin(lat2) return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5
I don't like adding yet another answer, but the Google maps API v.3 has spherical geometry (and more). After converting your WGS84 to decimal degrees you can do this: <script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script> distance = google.maps.geometry.spherical.computeDistanceBetween( new google.maps.LatLng(fromLat, fromLng), new google.maps.LatLng(toLat, toLng)); No word about how accurate Google's calculations are or even what model is used (though it does say "spherical" rather than "geoid". By the way, the "straight line" distance will obviously be different from the distance if one travels on the surface of the earth which is what everyone seems to be presuming.
You can use the build in CLLocationDistance to calculate this: CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1]; CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2]; [self distanceInMetersFromLocation:location1 toLocation:location2] - (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 { CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2]; return distanceInMeters; } In your case if you want kilometers just divide by 1000.
As pointed out, an accurate calculation should take into account that the earth is not a perfect sphere. Here are some comparisons of the various algorithms offered here: geoDistance(50,5,58,3) Haversine: 899 km Maymenn: 833 km Keerthana: 897 km google.maps.geometry.spherical.computeDistanceBetween(): 900 km geoDistance(50,5,-58,-3) Haversine: 12030 km Maymenn: 11135 km Keerthana: 10310 km google.maps.geometry.spherical.computeDistanceBetween(): 12044 km geoDistance(.05,.005,.058,.003) Haversine: 0.9169 km Maymenn: 0.851723 km Keerthana: 0.917964 km google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km geoDistance(.05,80,.058,80.3) Haversine: 33.37 km Maymenn: 33.34 km Keerthana: 33.40767 km google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km Over small distances, Keerthana's algorithm does seem to coincide with that of Google Maps. Google Maps does not seem to follow any simple algorithm, suggesting that it may be the most accurate method here. Anyway, here is a Javascript implementation of Keerthana's algorithm: function geoDistance(lat1, lng1, lat2, lng2){ const a = 6378.137; // equitorial radius in km const b = 6356.752; // polar radius in km var sq = x => (x*x); var sqr = x => Math.sqrt(x); var cos = x => Math.cos(x); var sin = x => Math.sin(x); var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat)))); lat1 = lat1 * Math.PI / 180; lng1 = lng1 * Math.PI / 180; lat2 = lat2 * Math.PI / 180; lng2 = lng2 * Math.PI / 180; var R1 = radius(lat1); var x1 = R1*cos(lat1)*cos(lng1); var y1 = R1*cos(lat1)*sin(lng1); var z1 = R1*sin(lat1); var R2 = radius(lat2); var x2 = R2*cos(lat2)*cos(lng2); var y2 = R2*cos(lat2)*sin(lng2); var z2 = R2*sin(lat2); return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2)); }
Here is a typescript implementation of the Haversine formula static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number { var deg2Rad = deg => { return deg * Math.PI / 180; } var r = 6371; // Radius of the earth in km var dLat = deg2Rad(lat2 - lat1); var dLon = deg2Rad(lon2 - lon1); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = r * c; // Distance in km return d; }
Here is the SQL Implementation to calculate the distance in km, SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * sin( radians(latitude) ) ) ) AS distance FROM user HAVING distance < 5 ORDER BY distance LIMIT 0 , 5; For further details in the implementation by programming langugage, you can just go through the php script given here
This script [in PHP] calculates distances between the two points. public static function getDistanceOfTwoPoints($source, $dest, $unit='K') { $lat1 = $source[0]; $lon1 = $source[1]; $lat2 = $dest[0]; $lon2 = $dest[1]; $theta = $lon1 - $lon2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; $unit = strtoupper($unit); if ($unit == "K") { return ($miles * 1.609344); } else if ($unit == "M") { return ($miles * 1.609344 * 1000); } else if ($unit == "N") { return ($miles * 0.8684); } else { return $miles; } }
here is an example in postgres sql (in km, for miles version, replace 1.609344 by 0.8684 version) CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat float, blng float) RETURNS float AS $BODY$ DECLARE v_distance float; BEGIN v_distance = asin( sqrt( sin(radians(blat-alat)/2)^2 + ( (sin(radians(blng-alng)/2)^2) * cos(radians(alat)) * cos(radians(blat)) ) ) ) * cast('7926.3352' as float) * cast('1.609344' as float) ; RETURN v_distance; END $BODY$ language plpgsql VOLATILE SECURITY DEFINER; alter function geodistance(alat float, alng float, blat float, blng float) owner to postgres;
Java implementation in according Haversine formula double calculateDistance(double latPoint1, double lngPoint1, double latPoint2, double lngPoint2) { if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) { return 0d; } final double EARTH_RADIUS = 6371.0; //km value; //converting to radians latPoint1 = Math.toRadians(latPoint1); lngPoint1 = Math.toRadians(lngPoint1); latPoint2 = Math.toRadians(latPoint2); lngPoint2 = Math.toRadians(lngPoint2); double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) + Math.cos(latPoint1) * Math.cos(latPoint2) * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2); distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance)); return distance; //km value }
I made a custom function in R to calculate haversine distance(km) between two spatial points using functions available in R base package. custom_hav_dist <- function(lat1, lon1, lat2, lon2) { R <- 6371 Radian_factor <- 0.0174533 lat_1 <- (90-lat1)*Radian_factor lat_2 <- (90-lat2)*Radian_factor diff_long <-(lon1-lon2)*Radian_factor distance_in_km <- 6371*acos((cos(lat_1)*cos(lat_2))+ (sin(lat_1)*sin(lat_2)*cos(diff_long))) rm(lat1, lon1, lat2, lon2) return(distance_in_km) } Sample output custom_hav_dist(50.31,19.08,54.14,19.39) [1] 426.3987 PS: To calculate distances in miles, substitute R in function (6371) with 3958.756 (and for nautical miles, use 3440.065).
To calculate the distance between two points on a sphere you need to do the Great Circle calculation. There are a number of C/C++ libraries to help with map projection at MapTools if you need to reproject your distances to a flat surface. To do this you will need the projection string of the various coordinate systems. You may also find MapWindow a useful tool to visualise the points. Also as its open source its a useful guide to how to use the proj.dll library, which appears to be the core open source projection library.
Here is my java implementation for calculation distance via decimal degrees after some search. I used mean radius of world (from wikipedia) in km. İf you want result miles then use world radius in miles. public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) { double earthRadius = 6371.0d; // KM: use mile here if you want mile result double dLat = toRadian(lat2 - lat1); double dLng = toRadian(lng2 - lng1); double a = Math.pow(Math.sin(dLat/2), 2) + Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * Math.pow(Math.sin(dLng/2), 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return earthRadius * c; // returns result kilometers } public static double toRadian(double degrees) { return (degrees * Math.PI) / 180.0d; }
Here's the accepted answer implementation ported to Java in case anyone needs it. package com.project529.garage.util; /** * Mean radius. */ private static double EARTH_RADIUS = 6371; /** * Returns the distance between two sets of latitudes and longitudes in meters. * <p/> * Based from the following JavaScript SO answer: * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula, * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%). */ public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) { double dLat = toRadians(lat2 - lat1); double dLon = toRadians(lon2 - lon1); double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double d = EARTH_RADIUS * c; return d; } public double toRadians(double degrees) { return degrees * (Math.PI / 180); }
For those looking for an Excel formula based on WGS-84 & GRS-80 standards: =ACOS(COS(RADIANS(90-Lat1))*COS(RADIANS(90-Lat2))+SIN(RADIANS(90-Lat1))*SIN(RADIANS(90-Lat2))*COS(RADIANS(Long1-Long2)))*6371 Source
there is a good example in here to calculate distance with PHP http://www.geodatasource.com/developers/php : function distance($lat1, $lon1, $lat2, $lon2, $unit) { $theta = $lon1 - $lon2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; $unit = strtoupper($unit); if ($unit == "K") { return ($miles * 1.609344); } else if ($unit == "N") { return ($miles * 0.8684); } else { return $miles; } }
Here is the implementation VB.NET, this implementation will give you the result in KM or Miles based on an Enum value you pass. Public Enum DistanceType Miles KiloMeters End Enum Public Structure Position Public Latitude As Double Public Longitude As Double End Structure Public Class Haversine Public Function Distance(Pos1 As Position, Pos2 As Position, DistType As DistanceType) As Double Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371) Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude) Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude) Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2) Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a))) Dim result As Double = R * c Return result End Function Private Function toRadian(val As Double) As Double Return (Math.PI / 180) * val End Function End Class
I condensed the computation down by simplifying the formula. Here it is in Ruby: include Math earth_radius_mi = 3959 radians = lambda { |deg| deg * PI / 180 } coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } } # from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) } def haversine_distance(from, to) from, to = coord_radians[from], coord_radians[to] cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng]) sines_product = sin(to[:lat]) * sin(from[:lat]) return earth_radius_mi * acos(cosines_product + sines_product) end
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) { var R = 6371; // Radius of the earth in km var dLat = deg2rad(lat2-lat1); // deg2rad below var dLon = deg2rad(lon2-lon1); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2) ; var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c; var miles = d / 1.609344; if ( units == 'km' ) { return d; } else { return miles; }} Chuck's solution, valid for miles also.
In Mysql use the following function pass the parameters as using POINT(LONG,LAT) CREATE FUNCTION `distance`(a POINT, b POINT) RETURNS double DETERMINISTIC BEGIN RETURN GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters END;