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.


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.


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 .


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.