Recherche de la géométrie la plus proche dans PostGIS

16

J'ai parcouru l '«API» de fonctions PostGIS et je constate que la plupart d'entre elles nécessitent deux éléments pour se comparer. Par exemple, la fonction ST_Distance prend deux éléments de géométrie / géographie pour trouver la distance.

Il n'y a pas de fonction pour faire quelque chose comme: "Étant donné une géométrie G, donnez-moi la géométrie la plus proche GClosest dans le tableau T où G.id <> GClosest.id"

Je me rends compte que je pourrais écrire une fonction PL / PgSQL pour itérer sur la table et appeler ST_Distance sur chaque élément, mais j'espère qu'il y a une meilleure solution, plus efficace.

Jmoney38
la source
1
Si vous êtes intéressé par la distance à la géométrie la plus proche, consultez gis.stackexchange.com/questions/11979/…
underdark
faites-moi savoir si j'ai bien compris ... vous voulez la prochaine fonctionnalité qui a la même distance que la plus proche?
falcacibar

Réponses:

7

Vous pouvez également répondre à votre question par une seule requête (quoique complexe) comme la suivante, qui renvoie l'ensemble de l'enregistrement et la distance par rapport à la géométrie de référence. Veuillez noter que si plusieurs enregistrements correspondent à la distance minimale, ils sont tous retournés.

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;

J'ai testé cette requête sur une table d'adresses et ça marche. Dans la requête ci-dessus, je recherche le point le plus proche de celui avec id = 3.

unicoletti
la source
C'est une bonne information - merci ... Je comprends la fonction d'agrégation min (..) par définition, mais je ne sais pas comment elle est utilisée dans votre exemple. st_distance (X, Y) prend deux types de géométrie et renvoie une distance entre eux, qui est une valeur unique. Pourquoi appelez-vous alors une fonction d'agrégation sur ce résultat de valeur unique? Peut-être que
j'interprète
Le groupe par est sur la géométrie ga qui est une constante pour l'ensemble des résultats (rappelez-vous que ga est sélectionné par id = 3), il ne fait donc rien. C'est juste une astuce pour que la géométrie ga soit disponible dans la distance st_distance de la requête externe sans rejoindre à nouveau la table deux fois. Aujourd'hui, je pensais que je pourrais peut-être me passer complètement de la requête interne en utilisant la clause de partition . Cela devrait également améliorer les performances. Je vais essayer et je vous le ferai savoir.
unicoletti
Malheureusement, les fonctions de fenêtre ont été introduites en 8.4 et maintenant je n'ai pas accès à un serveur qui a à la fois postgis et cette version donc je ne peux pas tester une requête réécrite avec la clause de partition.
unicoletti
7

George MacKerron a écrit une simple fonction de voisin le plus proche que j'ai trouvée très utile. Cette fonction renvoie l'ID du voisin le plus proche d'une entité donnée:

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;

Exemple d'utilisation:

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

... sélectionne le nœud le plus proche dans la table nw_node pour chaque entrée de my_point_table.

Il existe également une fonction plus générique sur le site SIG de Boston .

obscur
la source
Je suis plus préoccupé par la façon de créer des requêtes 1: N requêtes dans un sens plus général. Par exemple, au lieu de trouver l'élément le plus proche de la géométrie G, je pourrais vouloir trouver le premier élément qui chevauche G. Merci pour l'information, malgré tout. Le lien vers Boston GIS a été très utile! J'ai déjà imprimé quelques feuilles de triche :-)
Jmoney38
Peut-être pourriez-vous reformuler votre question pour la rendre un peu plus claire, @ Jmoney48. Vous n'êtes donc pas spécifiquement intéressé par le problème du plus proche voisin, mais plutôt par la manière de comparer une géométrie avec toutes les géométries d'une table?
underdark
Utilisez TOUJOURS la fonction générique du site SIG de Boston, la simple est incroyablement lente pour les grandes tables et l'effort pour l'appliquer n'est pas plus grand.
Vladtn