查询AREA中距离线段

时间:2015-07-21 22:06:58

标签: javascript php sql database

我有一个存储在数据库中的线段和点。如何查询数据库以检索多个线段的特定距离内的所有点。 目的是当用户点击路径(道路)时,应突出显示距路径一定距离内的所有对象。

谢谢。

更新 例... 我有一条从(0,0)到(0,10)的路径。程序应查找并突出显示此路径x距离内的所有对象。 假设x距离为“2”......那么,程序应突出显示矩形(0,2)(10,-2)内的所有对象。基本上,这与找到靠近线的所有对象(不仅仅是单个点)相同。

当线是水平的时很容易......但是我不知道如何解决所有情况,包括那条线可能是斜率。

更新:这些点存储在一个大型数据库中,因此我无法检查它们中的每一个是否接近。我试图找到一种方法来只检索那些足够接近但没有重叠请求的方法......一旦检索到它们,我就可以使用“点和线段之间的距离”中描述的方法来优化搜索”。 (我认为!) 谢谢!

1 个答案:

答案 0 :(得分:1)

这将为您提供从点p到线段v,w的距离。 (基于这个问题:Shortest distance between a point and a line segment)。您必须遍历所有点并计算到所有线段的距离,以找到距离路线足够近的线段。
如果它太慢,你必须进行一些不需要平方根的简化。



function distanceToLineSegment(p, v, w)
{
    var len2 = dist2(v, w);
    if (len2 == 0) return Math.sqrt(dist2(p, v));
    var s = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / len2;
    if (s < 0) return Math.sqrt(dist2(p, v));
    if (s > 1) return Math.sqrt(dist2(p, w));
    var i = {x: v.x + s * (w.x - v.x), y: v.y + s * (w.y - v.y)};
    return Math.sqrt(dist2(p, i));

    function dist2(p, q) {
        return Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2);
    }
}

alert(distanceToLineSegment({x:2, y:3}, {x:-1, y:4}, {x:3, y:8}));
&#13;
&#13;
&#13;

这是一个稍微优化的实现,它根据路径检查点列表 要检查的点存储为具有x和y值以及id字符串的点的数组far[]。有一个第二个,最初是空的数组close[],如果发现它们靠近路线,则移动点,这样点数就不会被检查两次。这两个数组存储在对象points中,因此它们可以通过函数之间的引用传递,而不是不断被复制。为了提高效率,我还删除了平方根函数 通过将距离计算更改为粗略近似(可能使用矩形)而不是数学上正确的方法,可能可以进一步优化。

&#13;
&#13;
function isCloseToRoute(points, route, distance) {
    var distance2 = Math.pow(distance, 2);
    for (var i = 0; i < route.length - 1; i++) {
        isCloseToLineSegment(points, route[i], route[i + 1], distance2);
    }

    function isCloseToLineSegment(points, v, w, distance2) {
        for (var i = points.far.length - 1; i >= 0; i--) {
            if (distanceToLineSegment2(points.far[i], v, w) <= distance2) {
                points.close.push(points.far.splice(i, 1)[0]);
            }
        }
    }

    function distanceToLineSegment2(p, v, w) {
        var len2 = dist2(v, w);
        if (len2 == 0) return dist2(p, v);
        var q = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / len2;
        if (q < 0) return dist2(p, v);
        if (q > 1) return dist2(p, w);
        var i = {x: v.x + q * (w.x - v.x), y: v.y + q * (w.y - v.y)};
        return dist2(p, i);
    
        function dist2(p, q) {
            return Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2);
        }
    }
}

var points = {close: [], far: [{x: 1, y: 0, id: "A"}, 
                               {x: 2, y: 1, id: "B"}, 
                               {x:-1, y: 8, id: "C"}, 
                               {x:-3, y: 4, id: "D"}]};
var route = [{x: 0, y: 0}, {x: 1, y: 2}, {x:-1, y: 4}, {x: 2, y: 8}];

isCloseToRoute(points, route, 2);
alert(points.close.length + " points found near route");
for (i in points.close) console.log(points.close[i].id);
&#13;
&#13;
&#13;

如果将地图划分为网格,则可以使用isCloseToRoute()来检查路径附近的网格单元格。它将返回一个网格单元列表,其中有一个键,如#34; 6,4&#34 ;;如果您为数据库中的每个点指定一个键,指示它所在的网格单元格,您可以查看它们而无需对坐标进行任何数学计算。
您可以像检查点列表一样创建输入对象,使用网格单元格的中心点填充far []数组,并在其上运行isCloseToRoute(),距离为(distance + gridSize * sqrt(2)/ 2)。
在该示例中,地图是1000 x 1000平方,分为64个网格单元,每个单元格大小为125 x 125。

&#13;
&#13;
function isCloseToRoute(points, route, distance) {
    var distance2 = Math.pow(distance, 2);
    for (var i = 0; i < route.length - 1; i++) {
        isCloseToLineSegment(points, route[i], route[i + 1], distance2);
    }

    function isCloseToLineSegment(points, v, w, distance2) {
        for (var i = points.far.length - 1; i >= 0; i--) {
            if (distanceToLineSegment2(points.far[i], v, w) <= distance2) {
                points.close.push(points.far.splice(i, 1)[0]);
            }
        }
    }

    function distanceToLineSegment2(p, v, w) {
        var len2 = dist2(v, w);
        if (len2 == 0) return dist2(p, v);
        var q = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / len2;
        if (q < 0) return dist2(p, v);
        if (q > 1) return dist2(p, w);
        var i = {x: v.x + q * (w.x - v.x), y: v.y + q * (w.y - v.y)};
        return dist2(p, i);
    
        function dist2(p, q) {
            return Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2);
        }
    }
}

var route = [{x: 210, y: 190}, {x: 820, y: 480}, {x:530, y: 470}, {x: 440, y: 760}];
var distance = 100;
var mapSize = 1000;
var gridSize = 125;
var gridCells = Math.floor(mapSize / gridSize);
var grid = {close: [], far: []};

for (x = 0; x < gridCells; x++) {
    for (y = 0; y < gridCells; y++) {
        grid.far[y * (gridCells) + x] = {x: (x + 0.5) * gridSize, 
                                         y: (y + 0.5) * gridSize, 
                                       key: x + "," + y};
    }
}

isCloseToRoute(grid, route, distance + 0.707107 * gridSize);
alert(grid.close.length + " grid cells near route");
for (i in grid.close) console.log(grid.close[i].key);
&#13;
&#13;
&#13;

我已经优化了isCloseToRoute()了一点。该示例运行测试,针对1000段随机路径检查1000个随机点。

&#13;
&#13;
function isCloseToRoute(points, route, distance) {
    var distance2 = Math.pow(distance, 2);
    for (var i = 0; i < route.length - 1; i++) {
        isCloseToLineSegment(route[i], route[i + 1]);
    }

    function isCloseToLineSegment(v, w) {
        var len2 = distanceToPoint2(v, w);
        var lenX = w.x - v.x, lenY = w.y - v.y;
        for (var i = points.far.length - 1; i >= 0; i--) {
            if (distanceToLineSegment2(points.far[i]) <= distance2) {
                points.near.push(points.far.splice(i, 1)[0]);
            }
        }

        function distanceToLineSegment2(p) {
          if (len2 == 0) return distanceToPoint2(p, v);   // enable if zero-length segments are possible
            var q = ((p.x - v.x) * lenX + (p.y - v.y) * lenY) / len2;
            if (q < 0) return distanceToPoint2(p, v);
            if (q > 1) return distanceToPoint2(p, w);
            var r = {x: v.x + q * lenX, y: v.y + q * lenY};
            return distanceToPoint2(p, r);
        }

        function distanceToPoint2(p, q) {
            return Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2);
        }
    }
}

// generate random test data
var points = {near: [], far: [{x: 500, y: 500}]};
var route = [{x: 200, y: 200}];
var distance = 100;
for (var i = 1; i < 1000; i++) {
    points.far[i] = {x: Math.random() * 1000, y: Math.random() * 1000};
    route[i] = {x: route[i - 1].x + 3 * Math.random() - 1, y: route[i - 1].y + 3 * Math.random() - 1};
}

var t = new Date().getTime();
isCloseToRoute(points, route, distance);
t = new Date().getTime() - t;
alert(points.near.length + " points found near route.\n(1000 points checked against 1000 segments in " + t + " ms)");
for (i in points.near) console.log(points.near[i].x + "," + points.near[i].y);
&#13;
&#13;
&#13;