Encontrar a geometria mais próxima no PostGIS

16

Eu estive examinando a "API" de funções do PostGIS e percebo que a maioria delas precisa de dois elementos para comparar. Por exemplo, a função ST_Distance usa dois elementos de geometria / geografia para encontrar a distância.

Não há função para fazer algo como: "Dada uma geometria G, me dê a geometria mais próxima GClosest na Tabela T, onde G.id <> GClosest.id"

Sei que poderia escrever uma função PL / PgSQL para iterar sobre a tabela e chamar ST_Distance em cada elemento, mas espero que exista uma solução melhor e mais eficiente.

Jmoney38
fonte
1
Se você estiver interessado na distância até a geometria mais próxima, verifique gis.stackexchange.com/questions/11979/…
underdark
deixe-me saber se entendi direito ... você quer o próximo recurso que tenha a mesma distância que o mais próximo?
falcacibar 9/09/11

Respostas:

7

Sua pergunta também pode ser respondida por uma única consulta (embora complexa), como a seguinte, que retorna o registro inteiro e a distância para a geometria de referência. Observe que, se mais de um registro corresponder à distância mínima, todos serão retornados.

SELECT 
  i.*,
  md.min_distance
FROM
  address AS i, 
  (SELECT 
     ga.address_geom,
     min( ST_Distance(
            ga.address_geom,
            gb.address_geom)
        ) AS min_distance
   FROM
     address AS ga,
     address AS gb 
   WHERE 
     ga.id <> gb.id 
   AND 
     ga.id = 3
   GROUP BY 
     ga.address_geom
  ) AS md 
WHERE 
  ST_Distance( i.address_geom, md.address_geom) = md.min_distance;

Eu testei esta consulta na tabela de endereços e funciona. Na consulta acima, estou procurando o ponto mais próximo do que com id = 3.

unicoletti
fonte
Esta é uma boa informação - obrigado ... Entendo a função agregada min (..) por definição, mas estou confusa sobre como está sendo usada no seu exemplo. st_distance (X, Y) pega dois tipos de geometria e retorna uma distância entre eles, que é um valor único. Por que você está chamando uma função agregada nesse resultado de valor único? Talvez eu estou interpretando mal a instrução select dentro ...
Jmoney38
O grupo by está na geometria ga , que é uma constante para todo o conjunto de resultados (lembre-se que ga é selecionado pelo id = 3), portanto, basicamente não faz nada. Esse é apenas um truque para disponibilizar a geometria ga na st_distance da consulta externa sem ingressar na tabela duas vezes novamente. Hoje eu estava pensando que talvez pudesse me livrar completamente da consulta interna usando a cláusula de partição . Isso também deve melhorar o desempenho. Vou tentar e informá-lo.
Unicoletti 09/09/11
Infelizmente, as funções de janela foram introduzidas na 8.4 e agora não tenho acesso a um servidor que tenha o postgis e essa versão, portanto não posso testar uma consulta reescrita com a cláusula partion.
Unicoletti 12/09/11
7

George MacKerron escreveu uma função simples de vizinho mais próximo que eu achei bastante útil. Esta função retorna o ID do vizinho mais próximo a um determinado recurso:

create or replace function 
  nn(nearTo                   geometry
   , initialDistance          real
   , distanceMultiplier       real 
   , maxPower                 integer
   , nearThings               text
   , nearThingsIdField        text
   , nearThingsGeometryField  text)
 returns integer as $$
declare 
  sql     text;
  result  integer;
begin
  sql := ' select ' || quote_ident(nearThingsIdField) 
      || ' from '   || quote_ident(nearThings)
      || ' where st_dwithin($1, ' 
      ||   quote_ident(nearThingsGeometryField) || ', $2 * ($3 ^ $4))'
      || ' order by st_distance($1, ' || quote_ident(nearThingsGeometryField) || ')'
      || ' limit 1';
  for i in 0..maxPower loop
     execute sql into result using nearTo              -- $1
                                , initialDistance     -- $2
                                , distanceMultiplier  -- $3
                                , i;                  -- $4
    if result is not null then return result; end if;
  end loop;
  return null;
end
$$ language 'plpgsql' stable;

Exemplo de uso:

SELECT id, nn(pt_geom,0.00001,2,100,'nw_node','node_id','node_geom') FROM my_point_table;

... seleciona o nó mais próximo na tabela nw_node para cada entrada em my_point_table.

Há também uma função mais genérica no site GIS de Boston .

underdark
fonte
Estou mais preocupado em como criar consultas 1: N em um sentido mais geral. Por exemplo, em vez de encontrar o elemento mais próximo da geometria G, talvez eu queira encontrar o primeiro elemento que se sobreponha a G. Obrigado pela informação, independentemente. O link para o GIS de Boston foi muito útil! Eu já impressos alguns dos cheat sheets :-)
Jmoney38
Talvez você possa reformular sua pergunta para torná-la um pouco mais clara, @ Jmoney48. Então você não está interessado no problema do vizinho mais próximo especificamente, mas em como comparar uma geometria com todas as geometrias de uma tabela?
underdark
SEMPRE use a função genérica do site GIS de Boston, a simples é incrivelmente lenta para grandes tabelas e o esforço para aplicá-la não é maior.
Vladtn