Encontrar la geometría más cercana en PostGIS

16

He estado revisando la "API" de funciones de PostGIS, y noté que la mayoría de ellas requieren dos elementos para comparar. Por ejemplo, la función ST_Distance toma dos elementos de geometría / geografía para encontrar la distancia.

No hay ninguna función para hacer algo como: "Dada una geometría G, dame la geometría más cercana GClosest en la Tabla T donde G.id <> GClosest.id"

Me doy cuenta de que podría escribir una función PL / PgSQL para iterar sobre la tabla y llamar a ST_Distance en cada elemento, pero espero que haya una solución mejor y más eficiente.

Jmoney38
fuente
1
Si está interesado en la distancia a la geometría más cercana, consulte gis.stackexchange.com/questions/11979/…
underdark
avíseme si entendí bien ... ¿desea que la próxima característica tenga la misma distancia que la más cercana?
falcacibar

Respuestas:

7

Su pregunta también puede responderse mediante una sola consulta (aunque compleja) como la siguiente, que devuelve todo el registro y la distancia a la geometría de referencia. Tenga en cuenta que si más de un registro coincide con la distancia mínima, todos se devuelven.

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;

He probado esta consulta en la tabla de direcciones y funciona. En la consulta anterior, estoy buscando el punto más cercano a ese con id = 3.

unicoletti
fuente
Esta es una buena información: gracias ... Entiendo la función agregada min (..) por definición, pero estoy confundido sobre cómo se está utilizando en su ejemplo. st_distance (X, Y) toma dos tipos de geometría y devuelve una distancia entre ellos, que es un valor único. ¿Por qué estás llamando a una función agregada en ese resultado de valor único? Tal vez estoy malinterpretando la declaración de selección interna ...
Jmoney38
El grupo by está en la geometría ga , que es una constante para todo el conjunto de resultados (recuerde que ga se selecciona con id = 3), por lo que básicamente no hace nada. Eso es solo un truco para tener la geometría ga disponible en st_distance de la consulta externa sin volver a unirse a la tabla dos veces. Hoy estaba pensando que tal vez podría librarme de la consulta interna usando la cláusula de partición . Eso también debería mejorar el rendimiento. Lo intentaré y te lo haré saber.
unicoletti
Desafortunadamente, las funciones de ventana se introdujeron en 8.4 y ahora no tengo acceso a un servidor que tenga tanto postgis como esa versión, por lo que no puedo probar una consulta reescrita con la cláusula de partición.
unicoletti
7

George MacKerron ha escrito una función simple de Vecino más cercano que he encontrado bastante útil. Esta función devuelve la ID del vecino más cercano a una característica dada:

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;

Ejemplo de uso:

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

... selecciona el nodo más cercano en la tabla nw_node para cada entrada en my_point_table.

También hay una función más genérica en el sitio SIG de Boston .

bajo oscuro
fuente
Me preocupa más cómo crear consultas 1: N consultas en un sentido más general. Por ejemplo, en lugar de encontrar el elemento más cercano a la geometría G, es posible que desee encontrar el primer elemento que se superpone a G. Gracias por la información, independientemente. ¡El enlace a Boston GIS fue muy útil! Ya he impreso algunas de las hojas de trucos :-)
Jmoney38
Tal vez podría reformular su pregunta para aclararla un poco, @ Jmoney48. Entonces, ¿no está interesado en el problema vecino más cercano específicamente, sino en cómo comparar una geometría con todas las geometrías en una tabla?
oscuro
SIEMPRE use la función genérica del sitio SIG de Boston, la simple es increíblemente lenta para tablas grandes y el esfuerzo para aplicarla no es mayor.
Vladtn