我有一个存储在数据库中的线段和点。如何查询数据库以检索多个线段的特定距离内的所有点。 目的是当用户点击路径(道路)时,应突出显示距路径一定距离内的所有对象。
谢谢。
更新 例... 我有一条从(0,0)到(0,10)的路径。程序应查找并突出显示此路径x距离内的所有对象。 假设x距离为“2”......那么,程序应突出显示矩形(0,2)(10,-2)内的所有对象。基本上,这与找到靠近线的所有对象(不仅仅是单个点)相同。
当线是水平的时很容易......但是我不知道如何解决所有情况,包括那条线可能是斜率。
更新:这些点存储在一个大型数据库中,因此我无法检查它们中的每一个是否接近。我试图找到一种方法来只检索那些足够接近但没有重叠请求的方法......一旦检索到它们,我就可以使用“点和线段之间的距离”中描述的方法来优化搜索”。 (我认为!) 谢谢!
答案 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;
这是一个稍微优化的实现,它根据路径检查点列表
要检查的点存储为具有x和y值以及id字符串的点的数组far[]
。有一个第二个,最初是空的数组close[]
,如果发现它们靠近路线,则移动点,这样点数就不会被检查两次。这两个数组存储在对象points
中,因此它们可以通过函数之间的引用传递,而不是不断被复制。为了提高效率,我还删除了平方根函数
通过将距离计算更改为粗略近似(可能使用矩形)而不是数学上正确的方法,可能可以进一步优化。
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;
如果将地图划分为网格,则可以使用isCloseToRoute()来检查路径附近的网格单元格。它将返回一个网格单元列表,其中有一个键,如#34; 6,4&#34 ;;如果您为数据库中的每个点指定一个键,指示它所在的网格单元格,您可以查看它们而无需对坐标进行任何数学计算。
您可以像检查点列表一样创建输入对象,使用网格单元格的中心点填充far []数组,并在其上运行isCloseToRoute(),距离为(distance + gridSize * sqrt(2)/ 2)。
在该示例中,地图是1000 x 1000平方,分为64个网格单元,每个单元格大小为125 x 125。
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;
我已经优化了isCloseToRoute()了一点。该示例运行测试,针对1000段随机路径检查1000个随机点。
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;