地图上的最近点

时间:2011-08-18 18:33:22

标签: java algorithm computational-geometry

我正在制作一个程序,您可以点击地图查看其周围区域的“特写视图”,例如在Google地图上。

当用户点击地图时,它会获得他们点击的X和Y坐标。

让我们假设我有一系列布尔值这些特写视图图片的位置:

public static boolean[][] view_set=new boolean[Map.width][Map.height];
//The array of where pictures are.  The map has a width of 3313, and a height of 3329.

程序搜索文件夹,其中图像被命名为在地图上拍摄地点的X和Y坐标。该文件夹包含以下图像(以及更多,但我只列出五个):

2377,1881.jpg, 2384,1980.jpg, 2389,1923.jpg, 2425,1860.jpg, 2475,1900.jpg

这意味着:

view_set[2377][1881]=true;
view_set[2384][1980]=true;
view_set[2389][1923]=true;
view_set[2425][1860]=true;
view_set[2475][1900]=true;

如果用户点击了X和Y,例如2377,1882,那么我需要程序来确定哪个图像最接近(在这种情况下答案是2377,1881)。

任何帮助将不胜感激, 感谢。

4 个答案:

答案 0 :(得分:3)

你的boolean[][]对于这个问题不是一个好的数据结构,至少如果它不是真的很密集(例如通常在周围的3×3或5×5平方中有特写视图的点可用)。

您需要具有最近邻搜索的二维地图。此目标的有用数据结构是 QuadTree 。这是一个4级树,用于表示空间数据。 (我在这里描述了“带点数据的区域QuadTree”。)

基本上,它将一个矩形分成四个大小相等的矩形,如果其中有多个点,则进一步细分每个矩形

因此树中的节点就是其中之一:

  • 空叶节点(对应于没有点的矩形)
  • 仅包含一个点的叶节点(对应于其中包含一个点的矩形)
  • 具有四个子节点的内部节点(对应于其中包含多个点的矩形)

(在实现中,我们可以用其父指针中的空指针替换空叶节点。)

要找到一个点(或“一个点将在其中的节点”),我们从根节点开始,查看我们的点是否是分界点的北/南/东/西,然后转到相应的子节点节点。我们继续这个,直到我们到达一些叶子节点。

  • 为了添加一个新点,我们要么结束一个空节点 - 然后我们可以把新点放在这里。如果我们最终在一个已经有一个点的节点上,创建四个子节点(通过拆分矩形)并将两个点添加到适当的子节点。 (这可能是相同的,然后递归重复。)

  • 对于最近邻搜索,我们将结束一个空节点 - 然后我们备份一个级别,并查看该父级的其他子节点(比较每个距离)。如果我们到达其中有一个点的子节点,我们会测量搜索点到此点的距离。如果它小于到边缘或节点的距离,我们就完成了。否则,我们也必须查看相邻节点中的点,并在此处比较结果,取最小值。 (我认为,我们最多只能看四点。)

  • 要删除,找到一个点后,我们将其节点设为空。如果父节点现在只包含一个点,我们将其替换为单点叶节点。

搜索和添加/删除是O(深度)时间复杂度,其中最大深度受 log((地图长度+宽度)/结构中两点的最小距离)的限制 ,平均深度取决于点的分布(例如到下一个点的平均距离),或多或少。

所需空间取决于点的数量和树的平均深度。

此数据结构存在一些变体(例如,仅当节点中存在多个X点时才拆分节点,或者不一定在中间拆分),以优化空间使用并避免树的深度过大

答案 1 :(得分:2)

根据用户点击的位置,您可以使用Dijkstra搜索搜索最近的图像。 基本上,您开始在点击的图像位置周围搜索越来越大的矩形。当然,你只需搜索这些矩形的边界,因为你已经搜索了身体。一旦找到图像,该算法就应该停止。

伪代码:

int size = 0
Point result = default
while(result == default)
   result = searchRectangleBoundary(size++, pointClicked)

function Point searchRectangleBoundary(int size, Point centre)
{
    point p = {centre.X - size, centre.Y - size}
    for i in 0 to and including size
    {
        if(view_set[p.X + i][p.Y]) return { p.X + i, p.Y}
        if(view_set[p.X][p.Y + i]) return { p.X, p.Y + i}
        if(view_set[p.X + i][p.Y + size]) return { p.X + i, p.Y + size}
        if(view_set[p.X + size][p.Y + i]) return { p.X + size, p.Y + i}
    }
    return default
}

请注意我为了简洁而省略了范围检查。

有一个小问题,但根据应用程序,它可能不是问题。它不使用欧几里德距离,而是曼哈顿指标。所以它不一定能找到最接近的图像,而是最多可以找到2倍平方根的图像。

答案 2 :(得分:1)

基于

  • 您的评论声明您有350-500个兴趣点,
  • 您的问题表明您的地图宽度为3313,高度为3329
    • 我的计算器告诉我,这代表了大约1100万个布尔值

......你正在以错误的方式解决这个问题。 @ JBSnorro的回答是一个非常优雅的方法,可以找到大海捞针(350点)(1100万点),但实际上,为什么要创造干草堆呢?

根据我对你的问题的评论,为什么不使用Pair<Integer,Integer>类来表示坐标,将它们存储在一个集合中,然后扫描它们?它更简单,更快速,更少内存消耗,并且方式对于更大的地图更具可扩展性(假设感兴趣的点是稀疏的...这似乎是一个明智的假设,因为它们是感兴趣的点)。

..相信我,计算欧几里德距离〜425次绕着一个1100万的值boolean[][]徘徊,寻找感兴趣的25,950中的1值(特别是在最坏的情况下分析)。


如果你真的不为每次扫描〜425个值而感到兴奋,那么(i)你的OCD比我更强(:P); (ii)您应该查看nearest neighbour search算法。

答案 3 :(得分:-1)

我不知道你是否要求这个。如果用户点为P1 {x1,y1},并且您想要计算其到P2 {x2,y2}的距离,则使用毕达哥拉斯定理计算距离

distance^2 = (x2-x1)^2 + (y2-y1)^2

如果你只想知道最近的,你可以避免计算平方根(距离越小,方形也越小,所以它也会为你提供相同的效果)。