问题是我不能使用ST_ClosestPoint函数,而必须使用ST_Distance函数并获取min(ST_distance)记录,但是我
CREATE TABLE traffic_signs
(
id numeric(8,0) ),
"GEOMETRY" geometry,
CONSTRAINT traffic_signs_pkey PRIMARY KEY (id),
CONSTRAINT traffic_signs_id_key UNIQUE (id)
)
WITH (
OIDS=TRUE
);
CREATE TABLE entrance_halls
(
id numeric(8,0) ),
"GEOMETRY" geometry,
CONSTRAINT entrance_halls_pkey PRIMARY KEY (id),
CONSTRAINT entrance_halls_id_key UNIQUE (id)
)
WITH (
OIDS=TRUE
);
我需要获取每个traffic_sign中最接近的entrnce_hall的ID。
SELECT senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY") as dist
FROM traffic_signs As senal, entrance_halls As port
ORDER BY senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY")
这样,我便得到了每个交通标志到每个入口大厅的距离。但是,我如何才能获得最小距离?
请注意,
#1 楼
你快到了。有一个小技巧是使用Postgres的独特运算符,该运算符将返回每个组合的第一个匹配项-当您按ST_Distance进行排序时,它将有效地将每个参量中的最接近点返回到每个端口。SELECT
DISTINCT ON (senal.id) senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY") as dist
FROM traffic_signs As senal, entrance_halls As port
ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");
如果您知道每种情况下的最小距离不超过x的某个数量(并且表上有空间索引),则可以通过将
WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance)
,例如,如果已知所有最小距离都不超过10km,则:SELECT
DISTINCT ON (senal.id) senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY") as dist
FROM traffic_signs As senal, entrance_halls As port
WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", 10000)
ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");
显然,需要谨慎使用,就像最小距离越大,您将无法获得针对该传感器和端口的组合的任何行。
注意:按订单的顺序必须匹配订单上的不重复订单,这很有意义,因为不重复订单采用第一个
假定您在两个表上都有一个空间索引。
编辑1.还有另一种方法,可以使用Postgres的<->和<#>运算符(分别计算中心点和边界框距离),可以更有效地利用空间索引,并且不需要ST_DWithin hack来避免n ^ 2比较。有一篇很好的博客文章解释了它们如何工作。一般要注意的是,这两个运算符在ORDER BY子句中起作用。
SELECT senal.id,
(SELECT port.id
FROM entrance_halls as port
ORDER BY senal.geom <#> port.geom LIMIT 1)
FROM traffic_signs as senal;
EDIT 2.由于这个问题已经引起了很多关注和k-nearest邻居(kNN)在GIS中通常是一个难题(就算法运行时而言),似乎有必要在此问题的原始范围上进行扩展。
查找x的标准方法一个对象的最近邻居是使用LATERAL JOIN(概念上类似于每个循环的)。从dbaston的答案中无耻地借钱,您会做类似的事情:
SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
CROSS JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
ORDER BY signs.geom <-> ports.geom
LIMIT 1
) AS closest_port
因此,如果要查找最接近的10个端口(按距离排序),只需更改横向子查询中的LIMIT子句。如果没有LATERAL JOINS,就很难做到这一点,并且需要使用ARRAY类型的逻辑。
虽然这种方法行之有效,但是如果您知道只需要搜索给定的距离,则可以大大加快这种方法。在这种情况下,您可以在子查询中使用ST_DWithin(signs.geom,ports.geom,1000),由于使用<->运算符建立索引的方式-几何之一应该是常量,而不是a列参考-可能会快得多。因此,例如,要获取10公里以内的3个最近的端口,您可以编写以下内容。
SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
CROSS JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
WHERE ST_DWithin(ports.geom, signs.geom, 10000)
ORDER BY ST_Distance(ports.geom, signs.geom)
LIMIT 3
) AS closest_port;
和往常一样,使用情况会因数据而异分布和查询,所以EXPLAIN是您最好的朋友。
最后,如果使用LEFT而不是CROSS JOIN LATERAL,则有一个小问题,因为您必须在横向查询别名之后添加ON TRUE,例如,
SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
LEFT JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
ORDER BY signs.geom <-> ports.geom
LIMIT 1
) AS closest_port
ON TRUE;
评论
应该注意的是,这在处理大量数据时效果不佳。
–雅各布·卡尼亚(Jakub Kania)
2015年2月23日在14:08
@JakubKania。这取决于是否可以使用ST_DWithin。但是,是的,观点很明确。不幸的是,按<-> / <#>运算符的Order要求其中一个几何是常数,不是吗?
–约翰·鲍威尔(John Powell)
2015年2月23日在14:09
@JohnPowellakaBarça,您是否有机会知道该博客帖子如今的住处? -还是<->和<#>运算符的类似解释?谢谢!!
– DPSSpatial
17年7月3日在15:20
@DPSSpatial,这很烦人。我没有,但是这里和这个都在谈论这种方法。第二个也使用横向连接,这是另一个有趣的增强。
–约翰·鲍威尔(John Powell)
17年7月3日在16:39
@DPSSpatial。这些<->,<#>和横向连接的东西都有点滑。我已经使用非常大的数据集进行了此操作,并且在没有使用ST_DWithin的情况下性能非常糟糕,所有这些都应该避免。最终,knn是一个复杂的问题,因此用法可能会有所不同。祝好运 :-)
–约翰·鲍威尔(John Powell)
17年7月3日在16:51
#2 楼
这可以通过PostgreSQL 9.3+中的LATERAL JOIN
完成:SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
CROSS JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
ORDER BY signs.geom <-> ports.geom
LIMIT 1) AS closest_port
评论
这应该是正确的答案。找到最接近每个点的特征(+距离)。
–Theo F
20年7月1日在10:50
#3 楼
带有交叉联接的方法不使用索引,并且需要大量内存。因此,您基本上有两种选择。在9.3之前的版本中,您将使用相关子查询。 9.3+可以使用LATERAL JOIN
。KNN GIST具有横向扭曲很快就会进入您附近的数据库
(即将进行确切的查询)
评论
酷使用横向连接。在这种情况下,以前从未见过。
–约翰·鲍威尔(John Powell)
2015年2月23日14:33
@JohnBarça这是我见过的最好的上下文之一。我还怀疑当您确实需要使用ST_DISTANCE()查找最近的多边形并且交叉连接导致服务器内存不足时,这将很有用。最近的多边形查询仍未解决AFAIK。
–雅各布·卡尼亚(Jakub Kania)
2015年2月23日14:54
#4 楼
@JohnBarçaORDER BY错误!
ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");
右
senal.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY"),port.id;
否则,它将不返回最接近的值,而仅返回具有较小端口号的
评论
正确的外观如下所示(我使用了点和线):SELECT DISTINCT ON(points.id)points.id,lines.id,ST_Distance(lines.geom,points.geom)as dist FROM development.passed_entries作为点,development 。“ de_muc_rawSections_cleaned”作为ORDER BY points.id,ST_Distance(lines.geom,points.geom),lines.id;的行
– blackgis
18年1月11日,12:30
好吧,我现在得到你。实际上,最好使用LATERAL JOIN方法,就像@dbaston的回答一样,这可以清楚地表明正在比较什么东西和其他东西。我不再使用上面的方法。
–约翰·鲍威尔(John Powell)
18年1月11日在17:03
#5 楼
如果您只是对将最接近的点返回到参考点的查询感兴趣,那么这也可以解决问题:select senal.id,port.id, st_closestPoint(port."GEOMETRY",senal."GEOMETRY"
FROM traffic_signs As senal, entrance_halls As port
评论
什么版本的PostgreSQL?