Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sort array items by longitude latitude distance in javascripts?

I am having following JSON array of 6 locations. Is there any way sort these based on longitude and latitude where nearby locations come next in the array?

[
{"id" : 279, "longitude":79.853239,"latitude":6.912283},
{"id" : 284, "longitude":79.865699,"latitude":6.885697},
{"id" : 13,  "longitude":79.851187,"latitude":6.912220},
{"id" : 282, "longitude":79.858904,"latitude":6.871041},
{"id" : 281, "longitude":79.853346,"latitude":6.899757},
{"id" : 16,  "longitude":79.854786,"latitude":6.894039}
]

Sorting can be started from first item and result should be something like this

[
{"id" : 279, "longitute":79.853239,"latitude":6.912283},
{"id" : 13,  "longitute":79.851187,"latitude":6.912220},
{"id" : 281, "longitute":79.853346,"latitude":6.899757},
{"id" : 16,  "longitute":79.854786,"latitude":6.894039},
{"id" : 284, "longitute":79.865699,"latitude":6.885697},
{"id" : 282, "longitute":79.858904,"latitude":6.871041}
]
like image 915
Tharik Kanaka Avatar asked Nov 10 '14 03:11

Tharik Kanaka


3 Answers

Problem resolved by adding another attribute called distance. Used following function to calculate distance between two points

function calculateDistance(lat1, lon1, lat2, lon2, unit) {
  var radlat1 = Math.PI * lat1/180
  var radlat2 = Math.PI * lat2/180
  var radlon1 = Math.PI * lon1/180
  var radlon2 = Math.PI * lon2/180
  var theta = lon1-lon2
  var radtheta = Math.PI * theta/180
  var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
  dist = Math.acos(dist)
  dist = dist * 180/Math.PI
  dist = dist * 60 * 1.1515
  if (unit=="K") { dist = dist * 1.609344 }
  if (unit=="N") { dist = dist * 0.8684 }
  return dist
}

Then calculated distance for each item in the array by using above function. Then sorted array by distance.

for ( i = 0; i < uniqueNodes.length; i++) {
  uniqueNodes[i]["distance"] = calculateDistance(uniqueNodes[0]["latitude"],uniqueNodes[0]["longitute"],uniqueNodes[i]["latitude"],uniqueNodes[i]["longitute"],"K");
}

uniqueNodes.sort(function(a, b) { 
  return a.distance - b.distance;
});
like image 172
Tharik Kanaka Avatar answered Oct 06 '22 04:10

Tharik Kanaka


Anyone else looking to do this, if you have the longitude and latitude avaliable, you can just sort linearly on it to get a simple line diagram like below. this will give you a rise/run linear result.

var $array = [
[79.853239, 6.912283, 279],
[79.851187, 6.912220, 13],
[79.853346, 6.899757, 281],
[79.854786, 6.894039, 16],
[79.865699, 6.885697, 284],
[79.858904, 6.87104, 282]
]

function sortLngLat(a, b){
var x = a[0] / a[1];
var y = b[0] / b[1];
}
var sortedArray = $array.sort(sortLngLat);
console.log(sortedArray);

output should be like graph below and you can tweak your values with negatives and positives to get different angles and directions.

 ---------------
|       |  /    |
| -1/1  | / 1/1 |
|       |/      |
|--------------
|      /|       |
|-1/-1/ | 1/-1  |
|    /  |       |
 ---------------
like image 43
Caleb Swank Avatar answered Oct 06 '22 05:10

Caleb Swank


If execution speed is relevant, there is no need to calculate the actual distances. This allows for some optimizations. You don't need to multiply by the earth's radius and you to take the square root of the squared differences.

// define local constants for frequently used functions
const asin = Math.asin
const cos = Math.cos
const sin = Math.sin
const PI_180 = Math.PI / 180

function hav(x) {
  const s = sin(x / 2)
  return s * s
}

function relativeHaversineDistance(lat1, lon1, lat2, lon2) {
  const aLatRad = lat1 * PI_180
  const bLatRad = lat2 * PI_180
  const aLngRad = lon1 * PI_180
  const bLngRad = lon2 * PI_180

  const ht = hav(bLatRad - aLatRad) + cos(aLatRad) * cos(bLatRad) * hav(bLngRad - aLngRad)
  // since we're only interested in relative differences,
  // there is no need to multiply by earth radius or to sqrt the squared differences
  return asin(ht)
}

const locations = [
  { "id": 279, "longitude": 79.853239, "latitude": 6.912283 },
  { "id": 284, "longitude": 79.865699, "latitude": 6.885697 },
  { "id": 13, "longitude": 79.851187, "latitude": 6.912220 },
  { "id": 282, "longitude": 79.858904, "latitude": 6.871041 },
  { "id": 281, "longitude": 79.853346, "latitude": 6.899757 },
  { "id": 16, "longitude": 79.854786, "latitude": 6.894039 }
]

const distanceTo = {
  "id": 279,
  "longitude": 79.853239,
  "latitude": 6.912283
}
const sorted = locations.sort((a, b) => relativeHaversineDistance(a.latitude, a.longitude, distanceTo.latitude, distanceTo.longitude) - relativeHaversineDistance(b.latitude, b.longitude, distanceTo.latitude, distanceTo.longitude))

console.log(sorted)
like image 1
Sam Theisens Avatar answered Oct 06 '22 05:10

Sam Theisens