Problème de voisin le plus proche dans Postgis 2.0 à l'aide de l'index GIST (fonction <->)

25

J'essaie d'utiliser la nouvelle fonction de Postgis 2.0 <-> (Geometry Distance Centroid) afin de calculer, pour chaque ligne de ma table (cosn1), la distance au polygone le plus proche de la même classe.

J'essayais d'utiliser le code suivant:

WITH index_query AS (
  SELECT g1.gid As ref_gid, ST_Distance(g1.the_geom,g2.the_geom) As ENN    
    FROM "cosn1" As g1, "cosn1" As g2   
    WHERE g1.gid <> g2.gid AND g1.class = g2.class
    ORDER BY g1.gid, g1.the_geom <-> g2.the_geom) 
SELECT DISTINCT ON (ref_gid) ref_gid, ENN 
    FROM index_query
ORDER BY ref_gid, ENN;

Mais alors je réalise l'avertissement:

Remarque: L'index ne démarre que si l'une des géométries est une constante (pas dans une sous-requête / cte). par exemple 'SRID = 3005; POINT (1011102 450541)' :: geometry au lieu de a.geom

Cela signifie que l'index ne sera pas utilisé du tout, et la requête prendra presque le même temps qu'avant l'utilisation:

SELECT DISTINCT ON(g1.gid)  g1.gid As ref_gid, ST_Distance(g1.the_geom,g2.the_geom) As ENN    
    FROM "cosn1" As g1, "cosn1" As g2   
    WHERE g1.gid <> g2.gid AND g1.class = g2.class
    ORDER BY g1.gid, ST_Distance(g1.the_geom,g2.the_geom)

Quelqu'un peut-il m'indiquer une solution de contournement qui me permet d'améliorer les performances de ma requête?

Merci beaucoup.

Alexandre Neto
la source
En l'absence de réponse pour le moment, vous souhaiterez peut-être poser cette question sur la liste de diffusion PostGIS.
GIS-Jonathan
Je l'ai déjà fait, mais aussi sans réponse.
Alexandre Neto
3
Vous pouvez utiliser g1.gid> g2.gid dans la clause where, ce qui réduira le nombre de calculs de distance à effectuer. Malheureusement, tant que l'opérateur <-> ne fonctionnera pas sans constantes, nous ne verrons pas beaucoup d'amélioration de la vitesse dans ce type de requête.
John Powell
John, je dois garder toutes les gids, même celles qui sont répétées car j'ai besoin de mettre à jour l'EEN pour chacun des polygones dans ma table "cosn1". Mais ce que tu as dit m'a donné une idée. Je pourrais faire comme vous dites en utilisant g1.gid> g2.gis pour réduire les calculs de distance, mais en gardant g1.gid et g2.gid dans le résultat. Après cela, j'ai pu en associer deux sous-requêtes (l'une avec g1.gis comme gid, et l'autre avec g2.gid). Merci
Alexandre Neto
J'ai trouvé qu'une solution possible pour contourner le problème constant serait d'utiliser le <-> à l'intérieur d'une fonction SQL, en utilisant the_geom comme paramètre. J'ai fait quelques tests, et dans certains cas, c'est beaucoup plus rapide (). Mais dans mon cas, puisque les distances sont à l'intérieur du même tableau, de nombreux calculs de distance sont répétés au cours du processus, ce qui le rend plus lent que l'utilisation de la requête directe.
Alexandre Neto

Réponses:

2

Le fait de faire des tests sur ma machine sonnait comme si cet opérateur <-> ne fonctionnait pas correctement. Je ne suis pas sûr que ce soit un bug mais il a signalé une distance nulle sur des géométries non superposées. Intrigant non?

Et qu'en est-il des optimisations de requêtes SQL traditionnelles? Depuis ces résultats inattendus avec l'opérateur <-> je le remplace par st_centroid. A obtenu de bien meilleurs résultats en vitesse.

La sémantique de Hope avec st_overlaps reste la même. Au moins c'est ce que j'ai compris de la documentation sur <->

À partir de documents sur Postigs <->

Pour les autres types de géométrie, la distance entre les centroïdes de la boîte englobante à virgule flottante est renvoyée.

Sur mes données de test avec ~ 5,5k polygones, la vitesse est passée de ~ 1000 secondes à ~ 5 secondes sans indexation spatiale.

Quoi qu'il en soit, pourquoi utiliser DISTINCT ON pour faire un regroupement? Je vois que certaines personnes l'utilisent mais que le groupe n'existe pas pour éliminer les doublons?

Votre requête avec des optimisations SQL standard sans l'erreur st_centroid introduite

select g1.gid, min( st_distance( g1.the_geom, g2.the_geom ) ) AS enn
FROM 
  "cosn1" AS g1, "cosn1" AS g2
WHERE
  g1.gid <> g2.gid
  AND g1.class = g2.class
  AND g1.the_geom && g2.the_geom
GROUP BY
  g1.gid

Joyeuses fêtes de Noël!

cavila
la source
Désolé mais votre réponse ne résout pas le problème. C'est en fait beaucoup plus rapide, mais les résultats ne sont pas précis, car le résultat final est calculé en utilisant les centroïdes des polygones, au lieu de leur géométrie réelle. Le <-> vise à optimiser la recherche de candidats au plus proche voisin mais à la fin devrait utiliser les géométries réelles pour calculer la distance aux meilleurs candidats. J'ai également essayé d'utiliser le MIN \ GROUP BY au lieu du DISTINCT ON \ ORDER BY, et il semble être plus lent.
Alexandre Neto
Mais le manuel de postgis pour l'opérateur <-> indique qu'il utilise le centroïde pour les géométries non ponctuelles. Ma solution vous donnerait donc des résultats similaires. Il devrait vous donner les mêmes résultats que votre requête principale. Veuillez vérifier que les résultats avec l'opérateur <-> sont également corrects. Il m'a rapporté des géométries de longueur nulle sur mes données de test afin que les résultats puissent être cassés et cette solution a donné des données plus précises. Si vous pouviez poster des exemples d'enregistrements montrant des erreurs sur un site de pastie, nous pourrions découvrir des failles dans la solution.
cavila
Si vous cochez ma requête, l'opérateur <-> ne serait utilisé que pour classer les candidats, le résultat final est calculé en utilisant les géométries réelles. Quoi qu'il en soit, comme je l'ai déjà dit, le <-> boost de performances ne fonctionne qu'avec des points fixes. C'était ma question initiale.
Alexandre Neto
Alors, acceptez-vous que la requête du haut n'est pas équivalente à la requête du bas? Puisque l'ordre changera parce que l'opérateur <-> ORDER BY st_centroid et le st_distance vous donneront une valeur différente? Un ordre différent peut amener une requête différente comme première ligne pour transmettre la clause DISTINCT ON? La requête valide serait celle du bas qui a besoin d'une amélioration de la vitesse?
cavila
Oui, la première requête vise à améliorer la vitesse de la dernière. Et oui, cela pourrait donner un résultat un peu différent, car g1.geom <-> g2.geom utilise les centroïdes, et cela signifie que la première ligne n'est peut-être pas la plus proche. Pour que cela fonctionne, je crois que je devrais mettre une limite à l'ordre par article, disons limite 10, puis extraire les valeurs réelles de la distance. Pourrait même utiliser le <#> à la place, qui utilise les boîtes englobantes au lieu des centroïdes.
Alexandre Neto