【问题标题】:K-Nearest Neighbor Query in PostGISPostGIS中的K最近邻查询
【发布时间】:2018-09-06 22:54:22
【问题描述】:

我在 PostGIS 中使用以下最近邻查询:

SELECT g1.gid g2.gid FROM points as g1, polygons g2   
WHERE g1.gid <> g2.gid
ORDER BY g1.gid, ST_Distance(g1.the_geom,g2.the_geom)
LIMIT k;

现在,我已经在两个表的 the_geom 和 gid 列上创建了索引,这个查询比其他涉及空间连接 b/w 两个表的空间查询花费更多的时间。

有没有更好的方法来找到 K 近邻?我正在使用 PostGIS。

而且,尽管在几何列上创建了索引,但花费异常长时间的另一个查询是:

select g1.gid , g2.gid from polygons as g1 , polygons as g2
where st_area(g1.the_geom) > st_area(g2.the_geom) ;

我相信,这些查询并没有从 gist 索引中受益,但为什么呢?

而这个查询:

select a.polyid , sum(length(b.the_geom)) from polygon as a , roads as b  
where st_intersects(a.the_geom , b.the_geom);

尽管涉及比多边形或点表大得多的“道路”表,并且还涉及更复杂的空间运算符,但一段时间后返回结果。

【问题讨论】:

  • 我假设您的问题是如何加快查询速度?你能告诉我们EXPLAIN ANALYZE SELECT ....的结果吗?这样我们也许可以知道那里发生了什么。
  • 不,我的问题是为什么这个查询所用的时间是上面第三个查询所用时间的 5 倍以上!!
  • 好的,在等待了很久之后,对于 IInd 查询,我收到以下错误消息:“查询结果内存不足”并且查询执行终止。有人能解释一下吗?

标签: postgresql postgis indexing nearest-neighbor


【解决方案1】:

late September 2011 以来,PostGIS 已通过可用于 ORDER BY 子句的特殊运算符支持索引最近邻查询:

SELECT name, gid
FROM geonames
ORDER BY geom <-> st_setsrid(st_makepoint(-90,40),4326)
LIMIT 10;

...将以可扩展的方式返回geom 最接近-90,40 的10 个对象。更多细节(选项和注意事项)在该公告中 postuse of the <->the <#> operators 现在也记录在官方 PostGIS 2.0 参考中。 (两者的主要区别在于&lt;-&gt; 比较形状质心,&lt;#&gt; 比较它们的边界——点没有区别,其他形状选择适合您查询的形状。)

【讨论】:

  • 这两个运算符的一个主要警告,正如它在链接的 postgis 参考页面上所说的那样,只有当其中一个几何图形是常数时,空间索引才会生效,就像你的 st_makepoint 在例子。这意味着您不能使用这些具有高效索引使用的运算符来回答 OP 问题,该问题涉及在其他几何集合 B 附近查找所有几何 A。
  • 啊,好点子。谢谢你提出来。那么@Stefan 的答案是否是“正确”的答案,只需要更多细节和更新链接?
【解决方案2】:

对您的问题的一些想法:

st_distance 和 st_area 不能使用索引。这是因为这两个功能都不能归结为“a 在 b 中吗?”之类的问题。或“a 和 b 是否重叠?”。更具体的是:GIST 索引只能对两个对象的边界框进行操作。

有关这方面的更多信息,您可以查看postgis manual,其中说明了 st_distance 的示例以及如何改进查询以更好地执行。

但是,这并不能解决您的 k 最近邻问题。为此,现在我不知道如何提高查询的性能。我看到的唯一机会是假设 k 最近的邻居总是在 x 米以下的距离内。然后您可以使用与 postgis 手册中类似的方法。

您的第二个查询可能会加快一点。目前,您计算表 1 中每个对象的区域的频率与表具有行的频率一样 - 策略是首先连接数据,然后根据该函数进行选择。您可以通过预先计算区域来显着减少区域计算的数量:

WITH polygonareas AS (
    SELECT gid, the_geom, st_area(the_geom) AS area
    FROM polygons
)
SELECT g1.gid, g2.gid
FROM polygonareas as g1 , polygonareas as g2 
WHERE g1.area > g2.area;

使用边界框可以显着优化您的第三个查询:当两个对象的边界框不重叠时,对象就没有办法重叠。这允许使用给定的索引,从而获得巨大的性能提升。

【讨论】:

    【解决方案3】:

    您可以使用 KNN 索引和横向连接来做到这一点。

    SELECT v.gid, v2.gid,st_distance(v.the_geom, v2.the_geom)
      FROM geonames v, 
           lateral(select * 
                     from geonames v2
                    where v2.id<>v.id
                    ORDER BY v.the_geom <-> v2.the_geom LIMIT 10) v2
    where v.gid in (...) - or other filtering condition
    

    【讨论】:

      【解决方案4】:

      您可能需要的是 KNN 索引,它有望很快在 PostGIS 2.x 和 PostgreSQL 9.1 中可用:请参阅 http://blog.opengeo.org/tag/knn/

      【讨论】:

        【解决方案5】:

        假设您有 p 点和 g 多边形,您的原始查询:

        SELECT g1.gid, g2.gid FROM points as g1, polygons g2   
        WHERE g1.gid <> g2.gid
        ORDER BY g1.gid, ST_Distance(g1.the_geom,g2.the_geom)
        LIMIT k;
        

        返回 p x g 集合中的 k 个最近邻居。该查询可能正在使用索引,但它仍然必须对整个 p x g 集合进行排序以找到距离最小的 k 行。您想要的是以下内容:

        SELECT g1.gid, 
              (SELECT g2.gid FROM polygons g2   
               --prevents you from finding every nearest neighbour twice
               WHERE g1.gid < g2.gid 
               --ORDER BY gid is erroneous if you want to limit by the distance
               ORDER BY ST_Distance(g1.the_geom,g2.the_geom)
               LIMIT k)
        FROM points as g1;
        

        【讨论】:

          猜你喜欢
          • 2016-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-27
          • 2013-08-07
          • 2013-03-21
          • 2014-04-12
          相关资源
          最近更新 更多