Qual é a precisão do SELECT DISTINCT na coluna de geometria PostGIS?

19

Gostaria de saber qual é a precisão do SELECT DISTINCToperador em uma geometria PostGIS. No meu sistema, a consulta a seguir fornece uma contagem de 5, o que significa que os pontos inseridos são considerados iguais se diferirem em menos de 1e-5 e não tenho certeza se isso é um recurso do PostGIS, um problema da minha instalação ou um bug.

Alguém sabe se esse é o comportamento esperado?

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)

Estou usando:

$ psql --version
psql (PostgreSQL) 9.3.1

e

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

no OSX 10.9

yellowcap
fonte

Respostas:

18

Estou surpreso que seja tão grosseiro, mas aí está. Não é DISTINCT, por si só, é o operador '=', definido para geometria como 'igualdade das chaves de índice', o que significa praticamente 'igualdade das caixas delimitadoras de 32 bits'.

Você pode ver o mesmo efeito usando '=' diretamente,

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;

Infelizmente, fazer '=' se comportar "intuitivamente" envolveria uma enorme perda computacional (fazendo uma avaliação ST_Equals () explícita para a chamada do operador) ou algum código complicado substancial novo (armazenando valores de hash para geometrias maiores, fazendo testes exatos em tempo real por menores escolha o caminho certo do código rapidamente, etc)

E é claro que agora muitos aplicativos / usuários internalizaram o comportamento existente, como é, de modo que "melhorá-lo" seria um rebaixamento para muitas pessoas. Você pode fazer uma distinção "exata" calculando seu conjunto em ST_AsBinary (geom), que fará testes de igualdade exata nas saídas bytea.

Paul Ramsey
fonte
E podemos supor que ST_AsBinary (geom) é uma operação relativamente muito rápida?
27613 Martin F de
Obrigado pela sua resposta, isso explica bem o comportamento. Na verdade, estou trabalhando em um projeto de geodjango, então usarei o __equalsfiltro, que se traduz na função ST_Equals que eu acho.
yellowcap
1
Sim ST_AsBinary é rápido. Os testes de igualdade no bytea provavelmente envolvem memcmp, que é uma operação muito rápida, portanto não deve ser muito terrível.
Paul Ramsey
O que você está propondo aqui, @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? Isso fornece uma representação binária de geomcomo resultado. Você poderia fazer SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(acho que uma função agregada como MAX()é necessária no SELECTporque a GROUP BYcláusula está usando o ST_AsBinary()retorno da função, não o próprio campo.) Isso parece bom?
Martin Burch 16/12
7

Dada a excelente explicação de Paul Ramsey sobre por que a próxima pergunta é o que pode ser feito sobre isso. Como você trabalha SELECT DISTINCTnos campos geométricos e faz com que ele funcione conforme o esperado?

Na resposta de Paul, propus usar SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);mas MAX()é lento, aparentemente exigindo uma varredura de tabela.

Em vez disso, achei mais rápido:

SELECT DISTINCT ON (ST_AsBinary(geom)) geom FROM the_table;
Martin Burch
fonte
4

Apenas uma atualização, para o PostGIS 2.4, SELECT DISTINCTfunciona corretamente para os dados de pontos no 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)

E

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)
tinlyx
fonte