Pages

mardi 1 mars 2011

[SpatiaLite] Soustraction vectorielle

Aujourd'hui nous allons réaliser une soustraction vectorielle avec SpatiaLite

Données:
- table1 (id,geometry) - type: polygone -
- table2 (id,geometry) + Index Spatial R*Tree - type: polygone -

Objectif: Soustraction vectorielle simple: table3=table1-table2
(l'ordre est important: dans notre cas, on va soustraire à la table 2 à la table)

Mise en situation:
Imaginons que:
- Notre table1 représente des territoires à explorer
- Notre table 2 représente des zones inaccessibles (fossés, lacs, etc...)
Notre objectif serait ici de determiner parmis les territoires à explorer (table1), les zones accessibles.

Solution basique:
CREATE TABLE table3 AS SELECT
t1.id AS id1
st_multi( st_difference( t1.geometry,t2.geometry) ) AS geometry
FROM table1 AS t1
JOIN table2 AS t2 ON (
GeometryType( st_multi(st_difference( t1.geometry,t2.geometry) ) ) = "MULTIPOLYGON")

Quelques explications:
Ici, on créée la table3 contenant le resultat de la soustraction vectorielle via la fonction st_difference(). L'utilisation de st_multi() force le type de géométrie créée en multi: ceci est recommandé pour la validation ulterieure de la colonne géométrique via la fonction RecoverGeometryColumn(). De plus, le critère de jointure GeomtryType()="MULTIPOLYGON" permet de ne garder que les résultats de soustraction de type MULTIPOLYGON, écartant ainsi les géométries NULLes (objets de table1 entièrement contenu dans objets de table2) et les lignes (POLYLINESTRING).
Ainsi, la table3 contiendra uniquement des objets de type MULTIPOLYGON. La fonction RecoverGeometryColumn() permet ainsi d'authentifier correctement la colonne geometry.

Cette requête basique marche, mais son fonctionnement n'est pas optimal et les temps de calcul risquent d'être très longs pour des gros jeux de donnée. Essayons de l'optimiser un peu ...

Solution optimisée:

CREATE TABLE table3 AS
SELECT
t1.id AS id1
st_multi( st_difference( t1.geometry,t2.geometry) ) AS geometry
FROM table1 AS t1
JOIN table2 AS t2 ON (
st_intersects( t1.geometry,t2.geometry)
AND
GeometryType( st_multi(st_difference( t1.geometry,t2.geometry) ) ) = "MULTIPOLYGON"
AND
t2.rowid IN (
SELECT pkid FROM idx_table2_geometry
WHERE pkid MATCH RTreeIntersects(
MBRminX(t1.geometry),
MBRmaxY(t1.geometry),
MBRminX(t1.geometry),
MBRmaxY(t1.geometry)
))

)
UNION
SELECT
t1.id AS id1,
st_multi(t1.geometry) AS geometry
FROM table1 AS t1, table2 AS t2
WHERE
NOT st_intersects( t1.geometry,t2.geometry)

Quelques explications:

L'astuce consiste à filtrer au préalable les objets s'intersectant, avant de réaliser le géotraitement (soustraction vectorielle) uniquement sur ces objets:

Dans un premier temps, on selectionne tous les objets de table1 qui intersectent les objets de table2 via la fonction st_intersects() (l'utilisation du R*Tree en rouge). Pour chacun de ces objets, on réalise une soustraction vectorielle via la fonction spatiale st_difference(obj1,obj2).

Dans un second temps, on ajoute au résultat les objets de table1 qui n'intersectent pas ceux de table2. La clause UNION va permettre de fusionner les résultats des deux SELECT en un seul résultat. Ces objets ne sont pas modifiés.

A vous de jouer !

N'hesitez pas à laisser des commentaires en cas de soucis, je tâcherais de vous aider du mieux que je peux.

Aucun commentaire:

Enregistrer un commentaire