Pages

jeudi 10 février 2011

Tutoriel SpatiaLite: distance minimale entre les objets de deux couches SIG

Pour ceux qui ne connaissent pas SpatiaLite, c'est ici que ça se passe:
http://www.gaia-gis.it/spatialite-2.4.0-4/spatialite-cookbook-fr/index.html

Données de départ:

- table1 (id,nom,geometry)
- table2 (id,nom,geometry) + Index Spatial R*Tree

Objectif: Pour chaque objet de la table1 , determiner la distance de l'objet le plus proche dans la table2 (dans la limite de 1Km, soit 1000m)

Contrainte: De manière intuitive, la solution serait toute simple:
SELECT
table1.nom as nom1,
table2.nom as nom2,
min(st_distance(table1.geometry,table2.geometry)) as distance
FROM table 1
JOIN table2 ON ( ....dist...)
GROUP BY 1
A priori, cette requete retourne effectivement pour chaque objet de la table1, la distance la plus courte le séparant des objets de la table2. Mais en regardant de plus près, pour chaque ligne l'objet de la table2 retourné par la requète correspond non pas à l'objet le plus proche, mais au premier objet trouvé... ceci est dû au fonctionnement même de GROUP BY. Afin de determiner les couples d'objets les plus proches, il faut ruser en passant par plusieurs étapes.

Solution: en plusieurs étapes

Etape 1: calcul des distances entre tous les couples possibles.
CREATE VIEW distances AS
SELECT
table1.nom as nom1,
table2.nom as nom2,
st_distance( table1.geometry,table2.geometry ) as distance
FROM table1
JOIN table2 ON (
st_distance( table1.geometry,table2.geometry )<=1000
AND table2.rowid IN (
SELECT pkid FROM idx_table2_geometry
WHERE pkid MATCH RTreeintersects(
MBRminX(table1.geometry)-1000,
MBRminY(table1.geometry)-1000,
MBRmaxX(table1.geometry)+1000,
MBRmaxY(table1.geometry)+1000
)))
GROUP BY 1

Analyse de la première étape:

Création d'une VUE ( que l'on pourra réutiliser dans les prochaines étapes - intermédiaire entre une table et une requete )
Requete du type SELECT.....FROM.... classique.
La fonction spatiale st_distance() permet de calculer la distance minimale entre chaque couple.
La sous-requete SELECT permet d'exploiter l'Index Spatial, en préfiltrant pour chaque objet de la table1, les objets de la table2 dont le MBR élargi de 1km intersecte le R*tree.

Etape 2: calcul de la distance minimale séparant chaque objet de la table 1 des objets de la table2

CREATE VIEW distances_min AS
SELECT
nom1 as nom1,
min( distance ) as "distance min"
FROM distances
GROUP BY 1

Analyse de la deuxième étape:

On crée une nouvelle vue à partir de la vue précédente (distances).
min(distance) et GROUP BY 1 vont permettre de selectionner pour chaque objet de la table1, la distance minimale le séparant des objets de la table2.

Etape 3: A chaque objet de la table1, nous associons l'objet de la table2 le plus proche

SELECT
distances.nom1 as nom1,
distances.nom2 as nom2,
distances.distance as distance
FROM distances
JOIN distances_min ON (
distances.nom1=distances_min.nom1
AND
distances.distance=distances_min."distance min"
)

Analyse de la dernière étape étape:
On va selectionner ( SELECT ... FROM ) les lignes de la VUE Distances.
Afin de récuperer, pour chaque objet de la table1, uniquement l'objet de la table2 le plus proche, on va JOINdre la vue distances_min.

Et voila, vous avez maintenant, pour chaque objet de la table 1:
- L'objet de la table 2 le plus proche
- La distance entre ces deux objets

Aucun commentaire:

Enregistrer un commentaire