0

Given a json with coords:

var centerLat = 51.6000;
var centerLng = 12.8000;

var posts = [
  {
    name: 'PostA',
    latitude: '52.5167',
    longitude: '13.3833',
  },
  {
    name: 'PostB',
    latitude: '52.9667',
    longitude: '13.7167',
  },
  {
    name: 'PostC',
    latitude: '26.7767',
    longitude: '18.4567',
  }
];

I found the haversine formula on this link, how can I check if a list of given lat and lng I get from json are within a radius of 5 km using the haversine?

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)
}
rob.m
  • 9,843
  • 19
  • 73
  • 162
  • Interesting question... But have you tried something? – Louys Patrice Bessette Mar 01 '19 at 23:34
  • 1
    5km radius from what center point? – Randy Casburn Mar 01 '19 at 23:34
  • @LouysPatriceBessette I have tried this one which uses the pytagoras but i wanted to use haversine and I have no clue. https://gist.github.com/Thibaut-B/56071bcc8207fa11be90 – rob.m Mar 01 '19 at 23:36
  • @RandyCasburn sorry yes, I have given an update with some random centers points, thanks – rob.m Mar 01 '19 at 23:38
  • How precise do you want to be. If you need to be precise then you need a geography framework because the earth is now a perfect sphere. – Erik Philips Mar 01 '19 at 23:38
  • @ErikPhilips yes that's why I want to use the Haversine formula – rob.m Mar 01 '19 at 23:38
  • if PostA and PostB are just under 55 km apart, then just use your function `getDistanceFromLatLonInKm(posts[0].latitude, posts[0].longitude, posts[1].latitude, posts[1].longitude)` if you want to check if another point is closer than 5 km, then you can use map and filter. – manonthemat Mar 01 '19 at 23:39
  • @James I have pasted the snipped of code which uses the Haversine's formula – rob.m Mar 01 '19 at 23:39
  • 2
    So, you have the haversine function and a center point, whats stopping you from executing the function once for each point in the array? – James Mar 01 '19 at 23:39
  • @manonthemat that sounds promising, would you mind to place it as an answer and use .map and .filter also for future users? Thanks – rob.m Mar 01 '19 at 23:40
  • @James I got really confused on how to use it when I have more than 1 – rob.m Mar 01 '19 at 23:40
  • @rob.m the Haversine formula is **not precise** it will only estimate it on the earth. – Erik Philips Mar 01 '19 at 23:41
  • @ErikPhilips it should be fine within a 5km radius no? – rob.m Mar 01 '19 at 23:42
  • at first I thought this question was asking if the max distance between posts was > 5km... that fiddle is here if you're interested https://jsfiddle.net/4h62e0q9/ – IrkenInvader Mar 01 '19 at 23:58
  • @IrkenInvader nope but thanks you – rob.m Mar 01 '19 at 23:59

2 Answers2

3

You can do this to filter the cities, the data you provided where too far apart, to get anything in the result i have moved the centerLat and centerLng i have logged the array with the closest cities in at the end.

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)
}

var centerLat = 52.5167;
var centerLng = 13.3933;

var posts = [{
    name: 'PostA',
    latitude: '52.5167',
    longitude: '13.3833',
  },
  {
    name: 'PostB',
    latitude: '52.9667',
    longitude: '13.7167',
  },
  {
    name: 'PostC',
    latitude: '26.7767',
    longitude: '18.4567',
  }
];

let closePosts = [];

posts.forEach((post) => {
  if (getDistanceFromLatLonInKm(centerLat, centerLng, post.latitude, post.longitude) < 5) {
    closePosts.push(post);
  }
});

console.log(closePosts)
Nils Kähler
  • 2,645
  • 1
  • 21
  • 26
  • hey thanks a lot, makes sense. Why did you use (city) tho? – rob.m Mar 01 '19 at 23:46
  • 1
    I guess i copied the data from you question when you called it cities. I will update it to posts to reflect your answer better. – Nils Kähler Mar 01 '19 at 23:49
  • thank you very much, on another note, why is haversine better than the pytagoras if ever? And what would it be the error margins in terms of wrong distances? Sorry, just asking in case you'd knew it. Thank you – rob.m Mar 01 '19 at 23:50
  • @rob.m because two points on opposite sides of the earth would be a straight line through the earth which is not correct. – Erik Philips Mar 01 '19 at 23:53
  • 2
    @rob.m as i understand it, it's because the haversine takes the curvature of the earth into consideration, and pytagoras measures on a 2d plane, there is a great answer here. [haversine vs pytagoras](https://gis.stackexchange.com/questions/58653/what-is-approximate-error-of-pythagorean-theorem-vs-haversine-formula-in-measur) – Nils Kähler Mar 01 '19 at 23:54
  • 1
    Im just logging out the distance to each of the posts, to show the distance, the array `closePosts` contains the posts. I will remove the console logs to make the result clearer. – Nils Kähler Mar 02 '19 at 00:06
  • to add on your work, I used jQuery, maybe you want to add this too to this answer for future users, thanks a lot tho jsfiddle.net/dt3wL4zg/9 – rob.m Mar 02 '19 at 00:16
1

Just adding this to your code would be enough.

posts.forEach(post => {
  const distance = getDistanceFromLatLonInKm(centerLat, centerLng, post.latitude, post.longitude);
  if (distance <= 200) {
    console.log(`Distance to ${post.name}: ${distance} km`);
  }
});

But I'd suggest cleaning up the code a little bit more. I used 200 instead of 5, because your example doesn't yield results.

Here's a refactored snippet.

const posts = [
  {
    name: 'Berlin',
    latitude: '52.520008',
    longitude: '13.404954',
  },
  {
    name: 'Hamburg',
    latitude: '53.551086',
    longitude: '9.993682',
  },
  {
    name: 'München',
    latitude: '48.135124',
    longitude: '11.581981',
  },
  {
    name: 'Lübeck',
    latitude: '53.865467',
    longitude: '10.686559',
  },
  {
    name: 'Schwerin',
    latitude: '53.635502',
    longitude: '11.401250',
  },
];

function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
  const R = 6371; // Radius of the earth in km
  const dLat = deg2rad(lat2-lat1); // deg2rad below
  const dLon = deg2rad(lon2-lon1);
  const 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)
      ;
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  const d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180);
}

function findClosePosts(location, radius, posts) {
  return posts.filter((post) =>
    // find close points within the radius of the location, but exclude the location itself from results
    getDistanceFromLatLonInKm(location.latitude, location.longitude, post.latitude, post.longitude) <= radius && location !== post);
}

function findLocationByName(name, posts) {
  return posts.find((post) => post.name === name);
}

const hamburg = findLocationByName('Hamburg', posts);
const closePosts = findClosePosts(hamburg, 200, posts);
console.log(closePosts);
manonthemat
  • 6,101
  • 1
  • 24
  • 49
  • thanks a lot, I literally used rnadomc oords I just typed on here, don't know what their distance actually is :) This looks much simpler than the other answer, not sure whatt o use but thanks a lot :) – rob.m Mar 01 '19 at 23:53