如何在JavaScript中按经纬度距离对数组项进行排序?

14

我有一个6个位置的JSON数组。是否有办法根据经纬度对它们进行排序,使得附近的位置在数组中紧随其后?

[
{"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}
]

排序可以从第一项开始,结果应该是这样的。

[
{"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}
]

如果第一个项目是id=279,则数组的下一个项目应该最接近第一个项目(在本例中为id=13)。然后它应该最接近id=13。 - Tharik Kanaka
如果您正在按彼此之间的距离进行排序,那么您将如何选择哪个是第一个,哪个是最后一个? - Qantas 94 Heavy
我们可以将第一个元素设置为第一项,然后根据该项对下一项进行排序。如上所述,如果第一项是id=279,则最接近它的是第二项,即id=13,那么第三项应该是最接近id=13的。 - Tharik Kanaka
因此 - 创建另一个以0开头的数组,每个下一个元素包含从第一个元素到每个其他元素的距离。 - zerkms
5个回答

22

通过添加另一个名为“distance”的属性解决了问题。使用以下函数计算两点之间的距离。

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
}

使用上述函数计算数组中每个项目的距离,然后按距离对数组进行排序。

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

3
calculateDistance函数定义了radlon1和radlon2,但从未使用过。这对算法有影响吗? - Kevin Newman
2
这里使用了Spherical Law of Cosines https://www.movable-type.co.uk/scripts/latlong.html。单位的意义是什么?K代表千米吗?而N代表的是英制单位/英里吗? - solsTiCe
非常有用,请您解释一下。我们如何将uniqueNodes [0] ["latitude"]更改为下一个最近的位置,以便它能够找到其下一个最近的位置,以此类推? - Waheed ur Rehman
1
@KevinNewman 不,radllon2radlong2 可以被移除。@solsTiCe 看起来 K = 公里,N = 海里。如果你只是用来排序,可以完全移除单位参数。 - Russ

3

如果你有可用的经度和纬度,想要实现这个功能的其他人可以直接按照线性排序,得到如下简单的折线图。这将为您提供一个上升/下降的线性结果。

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

输出结果应该像下面的图表一样,您可以通过添加负数和正数来调整值以获得不同的角度和方向。

 ---------------
|       |  /    |
| -1/1  | / 1/1 |
|       |/      |
|--------------
|      /|       |
|-1/-1/ | 1/-1  |
|    /  |       |
 ---------------

1

在排序时,我们只需要计算点之间的相对距离。这可以进行一些性能优化。您不需要乘以地球半径,也不需要对平方差取平方根。

// 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

尝试使用这个:

 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.cos1) * Math.cos2) *
        Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c // in metres
}

-3
你可以循环遍历数组,并嵌套另一个循环来查找最近的元素。
var finalArray = [];

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

这假设数组中的第一个元素是参考点,接下来的元素是最接近该点的元素,以此类推。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接