如何查询给定坐标(字符串类型的经纬度)中最近的记录?

13 投票
6 回答
5602 浏览
提问于 2025-04-17 04:44

我正在使用GeoDjango和PostGIS。现在我遇到了一个问题,就是如何从我的Postgres数据库表中,根据给定的坐标找到最近的记录。

6 个回答

3

我同意delawen的回答,但单独使用st_distance非常慢。为了加快速度,你需要使用GIST索引(注意大多数PostGIS函数,包括st_distance,都不使用索引,具体可以查看:postgis索引推荐)。

所以你首先需要在某个点周围创建一个缓冲区,然后使用“&&”来检查它的边界框(这会利用内置的GIST索引,所以性能会好很多),接着再用“st_distance”来检查距离。

举个例子,如果你想从某个特定位置(比如X=1, Y=1)找到最近的“餐馆”,你可以这样写:

select *,st_distance(the_geom_col,st_geomfromtext('POINT(1 1)',27700)) as distance 
from restaurants where st_buffer(st_geomfromtext('POINT(1 1)',27700),100) 
&& "the_geom_col"

这样做的速度会比“st_distance”快很多,但结果可能会包含距离给定位置超过100米的餐馆(尤其是当几何形状是线或多边形格式时)。

如果你想得到更准确的结果,也就是距离在100米以内的餐馆,你需要在上面的查询后面加上

and st_distance(the_geom_col,st_geomfromtext('POINTFROMTEXT(1 1)',27700)) <= 100

这样做仍然会比单独使用st_distance更高效、更快。因为数据库只会对满足第一个条件的记录运行st_distance

所以作为一个经验法则,每当你需要进行耗时的空间查找时,尽量:

  • 通过使用特殊操作符来过滤掉尽可能多的错误结果(可以查看官方postgis文档中的特殊操作)。
  • 然后编写实际的空间关系检查函数。
  • 确保你的“几何”列上有GIST索引。
  • 通过使用st_addbbox为你的“几何”添加边界框。
  • 定期重新索引并清理/分析你的表。

注意:缓冲区的大小或实际距离必须符合你使用的投影系统,也就是说,如果你使用的是EPSG:4326(经纬度),那么你需要以度数来表示这些距离。例如,1米在现实中大约等于0.00000899度,100米就自己算一下吧 :)

5

PostGIS 2.0 及以后的版本可以使用 KNN 最近邻搜索 来找到最近的中心点。举个例子:

SELECT ST_Distance(geom, 'SRID=26910;POINT(34.5 -23.2)'::geometry) AS d
FROM mypoints
ORDER BY geom <-> 'SRID=26910;POINT(34.5 -23.2)'::geometry
LIMIT 1;
19

这是使用GeoDjango和PostGIS的答案。

点的坐标必须是一个GEOSGeometry对象。要创建它,可以使用:

from django.contrib.gis.geos import GEOSGeometry
point = GEOSGeometry('POINT(5 23)')

接下来,假设你有一个“餐厅”模型和点的坐标。要找到最近的餐厅,只需使用:

Restaurants.objects.distance(point).order_by('distance')[0] 

撰写回答