从点计算可能的线

时间:2014-01-31 17:09:59

标签: javascript geometry latitude-longitude

我正在尝试检查点位置并检测它们是否在一行中,然后输出包含可能行的对象。问题:

  • 这是最好的方法吗?有效的四个循环?
  • 我也在双循环中获得匹配点的重复项,删除它们的最佳方法是什么?
  • 如果我想检测形状,例如方形(90度角),等边三角形(60度角)等,我该如何延伸?
  • 如果我想对点数据中的模式进行高级检测,例如90度1点为1km,100度1点为1.5km,110km 1点为2km等。匹配为:每5度距离增加+ 50km。我怎么能启用它?

这是我所处的地方的小提琴:

http://jsfiddle.net/kmturley/RAQXf/1/

我们知道点1 - 5的长坐标和纬度坐标。我们想要计算它们之间的红线。

enter image description here

起点数据:

var points = [
    {
        name: 'Point 1',
        lat: 51.509440,
        long: -0.126985
    },
    {
        name: 'Point 2',
        lat: 51.509453,
        long: -0.126180
    },
    {
        name: 'Point 3',
        lat: 51.510076,
        long: -0.124804
    },
    {
        name: 'Point 4',
        lat: 51.510327,
        long: -0.124133
    },
    {
        name: 'Point 5',
        lat: 51.509440,
        long: -0.124175
    }
];

以下是我正在使用的功能:

var utils = {
    distHaversine: function (lon1, lat1, lon2, lat2) { // calculate distance between two points
        var R = 6371; // earth's mean radius in km
        var dLat = this.toRad(lat2 - lat1);
        var dLon = this.toRad(lon2 - lon1);
        lat1 = this.toRad(lat1),
        lat2 = this.toRad(lat2);
        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(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;
        return d;
    },
    bearing: function (lon1, lat1, lon2, lat2) { // calculate bearing between two points
        lat1 = this.toRad(lat1);
        lat2 = this.toRad(lat2);
        var dLon = this.toRad(lon2 - lon1);
        var y = Math.sin(dLon) * Math.cos(lat2);
        var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
        return this.toBrng(Math.atan2(y, x));
    },
    toRad: function (val) { // convert degrees to radians
        return val * Math.PI / 180;
    },
    toDeg: function (val) { // convert radians to degrees (signed)
        return val * 180 / Math.PI;
    },
    toBrng: function (val) { // convert radians to degrees (as bearing: 0...360)
        return (this.toDeg(val) + 360) % 360;
    }
};

这就是我所拥有的:

function calculate(items) {
    var i = 0,
        j = 0,
        accuracy = 2,
        bearings = {};

    // loop through the points and check the distance and bearing of each one
    for (i = 0; i < items.length; i += 1) {
        for (j = 0; j < items.length; j += 1) {
            if (i !== j) {
                var bearing = utils.bearing(items[i].long, items[i].lat, items[j].long, items[j].lat);
                var distance = utils.distHaversine(items[i].long, items[i].lat, items[j].long, items[j].lat);
                var key = Math.round(bearing / accuracy) * accuracy;
                // push both points into the bearing array for the same line
                if (!bearings[key]) { bearings[key] = {}; }
                bearings[key][i] = true;
                bearings[key][j] = true;
                console.log(Math.round(distance * 1000) + 'm', Math.round(bearing) + '°', items[i].name + ' > ' + items[j].name);
            }
        }
    }
    return bearings;
}

function lines(bearings, items) {
    var item = {},
        key = '',
        lines = [];

    // loop though the bearings and create lines
    for (item in bearings) {
        if (utils.size(bearings[item]) > 2) {
            var line = { name: 'Line ' + item + '°', points: [] };
            for (key in bearings[item]) {
                line.points.push(items[parseInt(key)]);
            }
            lines.push(line);
        }
    }
    return lines;
}

var bearings = calculate(points);
var lines = lines(bearings, points);

console.log('--------');
console.log(lines);

预期产出:

var lines = [
    {
        name: 'Line 1',
        points: [
            {
                name: 'Point 1',
                lat: 51.509440,
                long: -0.126985
            },
            {
                name: 'Point 2',
                lat: 51.509453,
                long: -0.126180
            },
            {
                name: 'Point 5',
                lat: 51.509440,
                long: -0.124175
            }
        ]
    },
    {
        name: 'Line 2',
        points: [
            {
                name: 'Point 2',
                lat: 51.509453,
                long: -0.126180
            },
            {
                name: 'Point 3',
                lat: 51.510076,
                long: -0.124804
            },
            {
                name: 'Point 4',
                lat: 51.510327,
                long: -0.124133
            }
        ]
    }
];

这是我所处的地方的小提琴:

http://jsfiddle.net/kmturley/RAQXf/1/

2 个答案:

答案 0 :(得分:0)

我更喜欢以独立于语言的方式回答这个问题,因为它使得使用不同语言遇到相同问题的程序员的答案更有用。

如果点之间没有任何其他关系(例如,了解他们所在的街道),则必须首先考虑点对之间的所有线段。 Binomial[n, 2]点有n个分段,因此如果您可以添加启发式算法以避免考虑某些分段,那将会很好。

我们有一些线段,我们可以将每个线段与平面上的特定向量L(S)相关联(让我们称之为L平面)。当且仅当S1时,两个线段S2L(S1) == L(S2)将共线。

L(S)被定义为从某个固定原点O到从S延伸的(无限)线上的最近点的向量。如果两个段位于同一条线上,那么它们将共享相同的最近点O,如果不是,则它们不会。现在,您可以在L平面上使用quadtree等空间树来查看哪些线段是共线的。

enter image description here

您可以使用详细记录的方法计算向量L(S),该方法可以找到到达另一个点的直线上的最近点,但这里有一个快速提醒。

enter image description here

肮脏的细节:当你的原点与任何细分市场共线时,情况会变糟。你必须处理这个案子。我认为处理这种情况的最佳方法是将这些段放在一边,移动原点,然后将算法重新应用于这些段。

此外,您希望用于重合的容差与O的距离成比例。

答案 1 :(得分:0)

所以我设法通过使用这个脚本来解决问题:

http://www.movable-type.co.uk/scripts/latlon.js

然后是以下代码:

var p1 = new LatLon(item1.lat, items1.long);
var p2 = new LatLon(item2.lat, items2.long);
var p3 = new LatLon(item3.lat, items3.long);
var distance = Math.abs(Math.asin(Math.sin(p1.distanceTo(p3) / R) * Math.sin(p1.bearingTo(p3).toRad() - p1.bearingTo(p2).toRad())) * R);

我有一个主要问题:轴承是度数,但需要在Radians中!

p1.bearingTo(p3).toRad() - p1.bearingTo(p2).toRad()

您可以在此处查看工作版本(使用多个点查找它们之间的行):

http://jsfiddle.net/kmturley/Cq2DV/1/