13

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}
]
Tharik Kanaka
  • 2,490
  • 6
  • 31
  • 54
  • if the first item is id=279 next item of the array should be nearest to first one (In this case it is id=13). then it should b nearest to id=13 – Tharik Kanaka Nov 10 '14 at 03:15
  • So if you are sorting by distance from one another, how would you choose which is first and which is last? – Qantas 94 Heavy Nov 10 '14 at 03:19
  • As first element we can take first item, then based on that item we can sort next items. As i explained above if first item is id=279, then nearest one for that will be the second item which is id=13, then third item should be nearest to id=13. – Tharik Kanaka Nov 10 '14 at 03:22
  • So - create another array which starts with `0` and every next element contains distance from 1st element to the every other – zerkms Nov 10 '14 at 03:23

5 Answers5

21

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;
});
solsTiCe
  • 379
  • 5
  • 15
Tharik Kanaka
  • 2,490
  • 6
  • 31
  • 54
  • 3
    The calculateDistance function defines radlon1 and radlon2, but never uses them. Is that a problem for the algorithm? – Kevin Newman Aug 05 '16 at 18:43
  • 2
    This use the *Spherical Law of Cosines* https://www.movable-type.co.uk/scripts/latlong.html. What is the meaning of units ? K for km ? and N for Imperial units / miles ? – solsTiCe May 18 '18 at 10:24
  • very useful could you please explain this. how do we change uniqueNodes[0]["latitude"] to next closest location so that it will find its next closest location and so on? – Waheed ur Rehman Nov 25 '19 at 18:28
  • 1
    @KevinNewman No, the `radllon2` and `radlong2` can be removed. @solsTiCe It appears K = km and N = nautical miles. You can remove the unit parameter altogether if you are using this for sorting. – Russ Sep 07 '20 at 19:36
3

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  |
|    /  |       |
 ---------------
Caleb Swank
  • 619
  • 5
  • 17
1

For sorting we only need to calculate the relative distances between points. This allows for some performance optimizations. You don't need to multiply by the earth's radius and you don't need 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)
0

Try to use this one:

 function calculateDistance(lat1, lon1, lat2, lon2) {
    const R = 6371e3; // metres
    const φ1 = lat1 * Math.PI / 180; // φ, λ in radians
    const φ2 = lat2 * Math.PI / 180;
    const Δφ = (lat2 - lat1) * Math.PI / 180;
    const Δλ = (lon2 - lon1) * Math.PI / 180;
    const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
        Math.cos(φ1) * Math.cos(φ2) *
        Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c // in metres
}
Iman Marashi
  • 5,593
  • 38
  • 51
-3

You could loop through the array, and nest another loop that finds the nearest one.

var finalArray = [];

while(entries){
  //for each item
  while(what's left){
    //find the nearest against the current item
    //push to final
  }
}

This assumes that the very first in the array is the point of reference, that what comes next would be the nearest to that point, and so on.

Joseph
  • 117,725
  • 30
  • 181
  • 234