SELECT DISTINCT在PostGIS几何列上的精度是多少?


19

我想知道SELECT DISTINCT操作员在PostGIS几何图形上的精度如何。在我的系统上,以下查询给我一个5的计数,这意味着如果插入的点相差小于1e-5,则认为插入的点相等,并且我不确定这是否是PostGIS的功能,这是我安装的问题或错误。

有人知道这是否是预期的行为吗?

CREATE TEMP TABLE test (geom geometry);
INSERT INTO test
    VALUES 
        (St_GeomFromText('POINT (0.1 0.1)')),
        (St_GeomFromText('POINT (0.001 0.001)')),
        (St_GeomFromText('POINT (0.0001 0.0001)')),
        (St_GeomFromText('POINT (0.00001 0.00001)')),
        (St_GeomFromText('POINT (0.000001 0.000001)')),
        (St_GeomFromText('POINT (0.0000001 0.0000001)')),
        (St_GeomFromText('POINT (0.00000001 0.00000001)')),
        (St_GeomFromText('POINT (0.000000001 0.000000001)'));

SELECT COUNT(*) FROM (SELECT DISTINCT geom FROM test) AS test;

 count 
-------
     5
(1 row)

我在用:

$ psql --version
psql (PostgreSQL) 9.3.1

SELECT PostGIS_full_version();
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
POSTGIS="2.1.1 r12113" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.10.1, released 2013/08/26" LIBXML="2.7.3" LIBJSON="UNKNOWN" RASTER

在OSX 10.9上

Answers:


18

我很惊讶它是如此的粗糙,但是确实如此。它本身不是DISTINCT,而是'='运算符,它为几何定义为“索引键的相等性”,实际上意味着“ 32位边界框的相等性”。

您可以直接使用'='看到相同的效果,

select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.000001)'::geometry;

select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.00001)'::geometry;

不幸的是,使“ =”表现为“直观”会导致巨大的计算量损失(对操作员调用进行显式ST_Equals()评估)或一些新的复杂代码(为较大的几何体存储哈希值,为较小的变量即时进行精确测试) ,即时选择正确的代码路径,等等)

当然,现在许多应用程序/用户已经将现有的行为内部化了,例如,因此,“改进”对于许多人来说将是一个降级。您可以通过在ST_AsBinary(geom)上计算集合来进行“精确”区分,这将对bytea输出进行精确的相等性测试。


我们是否可以假设ST_AsBinary(geom)是一个相对非常快的操作?
马丁F

感谢您的回答,这很好地解释了该行为。我实际上正在处理geodjango项目,因此我将在__equals其中使用过滤器,该过滤器会转换为我认为的ST_Equals函数。
yellowcap 2013年

1
是的ST_AsBinary很快。对bytea的相等性测试可能涉及memcmp,这是一个非常快的操作,因此不要太可怕。
Paul Ramsey 2013年

你在这里提议什么,@ PaulRamsey?SELECT DISTINCT ST_AsBinary(geom)?给出结果的二进制表示形式geom。您可以这样做SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(我认为,MAX()需要使用诸如之类的聚合函数,SELECT因为该GROUP BY子句使用的是ST_AsBinary()函数return,而不是字段本身。)这看起来不错吗?
Martin Burch 2014年

7

鉴于保罗·拉姆齐的优秀解释为什么接下来的问题是什么,可它做。您如何SELECT DISTINCT处理几何字段并使其按预期执行?

在Paul的回答中,我建议使用SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);MAX()很慢,显然需要进行表扫描。

相反,我发现这样做更快:

SELECT DISTINCT ON (ST_AsBinary(geom)) geom FROM the_table;

4

对于PostGIS 2.4,只有一个更新才能SELECT DISTINCT正确处理OP中的点数据:

CREATE TEMP TABLE test (geom geometry);
CREATE TABLE
user=> INSERT INTO test
user->     VALUES 
user->         (St_GeomFromText('POINT (0.1 0.1)')),
user->         (St_GeomFromText('POINT (0.001 0.001)')),
user->         (St_GeomFromText('POINT (0.0001 0.0001)')),
user->         (St_GeomFromText('POINT (0.00001 0.00001)')),
user->         (St_GeomFromText('POINT (0.000001 0.000001)')),
user->         (St_GeomFromText('POINT (0.0000001 0.0000001)')),
user->         (St_GeomFromText('POINT (0.00000001 0.00000001)')),
user->         (St_GeomFromText('POINT (0.000000001 0.000000001)'));
INSERT 0 8
user=> 
user=> SELECT COUNT(*) FROM (SELECT DISTINCT geom FROM test) AS test;
 count 
-------
     8
(1 row)

user=> select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.000001)'::geometry;
 ?column? 
----------
 f
(1 row)

user=> 
user=> select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.00001)'::geometry;
 ?column? 
----------
 f
(1 row)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.