使用PostGIS查找给定Point的n个最近邻居?

Sch*_*lle 22 sql postgresql postgis

我试图解决使用PostGIS找到n个最近邻居的问题:

初始点:

  • 包含纬度/经度的地理名称(来自geonames.org)的表格地名(WSG-84)
  • 添加了一个GeometryColumn geom,其中srid = 4326,数据类型= POINT
  • 填充的geom值:UPDATE geoname SET geom = ST_SetSRID(ST_Point(经度,纬度),4326);
  • 创建geom的GIST索引(CREATE INDEX geom_index ON geoname USING GIST(geom);)/ Clustered geom_index:CLUSTER geom_index ON geoname;)
  • 为geonameid创建了PRIMARY KEY UNIQUE BTREE索引

问题:在由id(geoname.geonameid)表示的表geoname中找到给定Point的n个(例如5个)最近邻居.

可能的方法:

http://www.bostongis.com/PrinterFriendly.aspx?content_name=postgis_nearest_neighbor的启发,我尝试了以下查询:

"SELECT start.asciiname, ende.asciiname, distance_sphere(start.geom, ende.geom) as distance " +
"FROM geoname As start, geoname As ende WHERE start.geonameid = 2950159 AND start.geonameid <> ende.geonameid " +
"AND ST_DWithin(start.geom, ende.geom, 300) order by distance limit 5"
Run Code Online (Sandbox Code Playgroud)

处理时间:约60秒

还尝试了一种基于EXPAND的方法:

"SELECT start.asciiname, ende.asciiname, distance_sphere(start.geom, ende.geom) as distance " +
"FROM geoname As start, geoname As ende WHERE start.geonameid = 2950159 AND start.geonameid <> ende.geonameid AND expand(start.geom, 300) && ende.geom " +
"order by distance limit 5"
Run Code Online (Sandbox Code Playgroud)

处理时间:约120s

预期的应用程序是某种自动完成.因此,任何超过> 1s的方法都不适用.通常可以使用PostGIS实现<1s的响应时间吗?

小智 48

现在,自PostGIS 2.0以来,可以获得几何类型的KNN索引.这将为您提供最接近的5条记录,无视离"您的位置......"的距离.

SELECT *
FROM your_table 
ORDER BY your_table.geom <-> "your location..."
LIMIT 5;
Run Code Online (Sandbox Code Playgroud)

请参阅PostgreSQL手册中的<-> operator .

  • 只是为了澄清一下,KNN索引实际上是在PG> 9.1中实现的,所以请确保你安装了正确的PG版本...这绝对值得一试...... (3认同)

Nic*_*vén 7

正如我认为你在列表中回答的那样,单位是度数,因此你几乎在st_dwithin中以300度搜索整个世界.

如果您的数据集很大,那么您无法在投影的基于米的投影中工作(更快且更少CPU计算),您应该考虑使用地理数据类型.然后你可以使用st_dwithin和米.

你应该更快地创建一个新表,其中几何转换为地理.

但是要测试它你可以在飞行中投射:

SELECT start.asciiname, ende.asciiname, 
ST_Distance(start.geom::geography, ende.geom::geography) as distance 
FROM geoname As start, geoname As ende 
WHERE start.geonameid = 2950159 AND start.geonameid <> ende.geonameid AND
ST_DWithin(start.geom::geography, ende.geom::geography, 300) 
order by distance 
limit 5;
Run Code Online (Sandbox Code Playgroud)

HTH尼克拉斯