Faire une pseudo-jointure avec Grass avec des tables DBF

Voici le topo: vous avez importé des données vecteur dans Grass. Ces données contiennent en plus d'autres données attributaires. Nativement, lors de l'import de la couche, Grass sépare bien les deux types d'information (exemple avec une couche nommée : COMMUNES.SHP):

  • L'information géographique forme une couche vecteur. Elle sera nommée COMMUNES par défaut dans Grass.
  • Les données attributaires sont importées par défaut dans un fichier DBF qui a le même nom que la couche vecteur (soit COMMUNES.DBF)
  • La couche vecteur reçoit un lien (v.db.connect) par défaut vers le fichier COMMUNES.DBF

Notre problème est le suivant: nous souhaitons ajouter des données attributaires dans la table COMMUNES.DBF de la manière la plus simple possible. Si c'est une opération qui peut sembler triviale, elle ne l'est pas nécéssairement: Grass ne sait pas gérer les jointures dynamiques (v.db.join) avec des tables DBF ! C'est également le cas pour les requêtes croisées (UPDATE table SET champ=(SELECT ...)).

Nous devons donc ruser et contourner cette limite en ajoutant les colonnes qui nous intéressent dans la table COMMUNES.DBF, le tout, en utilisant les fonctions de GRASS. L'astuce consiste à utiliser le format SQLite en entrée pour réaliser nos opérations de requêtes puis d'exporter le résultat dans une table DBF.

Voyons le détail des opérations:

  • On commence par importer la couche des communes dans GRASS avec des données attributaires dans une base de données SQLite:
    db.connect driver=sqlite database=temp_db.sqlite 
    v.in.ogr "dsn=/home/user/GRASS_import/COMMUNES.shp" output=COMMUNES snap=-1 min_area=0.0001 -o
  • Si on fait la liste des tables DBF, on repère bien notre table COMMUNES. On peut également observer sa structure. On remarque une colonne nommée cat. C'est un identifiant interne à GRASS qui fait le lien avec les objets géographiques:
    # v.db.connect -p permet d'afficher la liste des tables attributaires connectées à une couche vecteur :
    v.db.connect -p COMMUNES
    Vector map <COMMUNES@PERMANENT> is connected by:
    layer <1> table <COMMUNES> in database </home/user/GRASS/DEP49/PERMANENT/dbf/> through driver<dbf> with key <cat>

    # db.tables permet de liste les tables attributaires disponibles, indépendamment de leur connexion à une couche vecteur:
    db.tables -p
    COMMUNES

    # Pour voir la structure d'une table, on utilise db.columns ou db.describe:
    db.columns table=COMMUNES
    cat
    NOM_COM
    INSEE_COM
    STATUT
    SUPERFICIE
    POPULATION
    INSEE_CANT
    INSEE_ARR
    ID_OBJ

    db.describe -c table=COMMUNES
    ncols: 9
    nrows: 363
    Column 1: cat:INTEGER:11
    Column 2: NOM_COM:CHARACTER:50
    Column 3: INSEE_COM:CHARACTER:5
    Column 4: STATUT:CHARACTER:20
    Column 5: SUPERFICIE:INTEGER:11
    Column 6: POPULATION:INTEGER:11
    Column 7: INSEE_CANT:CHARACTER:2
    Column 8: INSEE_ARR:CHARACTER:1
    Column 9: ID_OBJ:INTEGER:11
  • Ensuite, on importe notre fichier CSV qui contient les données à joindre à la couche COMMUNES. Ce fichier se nomme DATA.csv et il utilise également un fichier DATA.csvt qui permet de définir le type des champs.
    v.in.ogr "dsn=/home/user/tmp/GRASS_import/DATA.csv" output=DATA snap=-1 min_area=0.0001 -o
    db.describe -c table=DATA
    ncols: 6
    nrows: 6
    Column 1: cat:INTEGER:11
    Column 2: value:INTEGER:11
    Column 3: Mini:DOUBLE PRECISION:20
    Column 4: Maxi:DOUBLE PRECISION:20
    Column 5: Nume:INTEGER:11
    Column 6: Producteur:CHARACTER:25
  • Pour des raisons de jointure et de nommage de champs, nous allons supprimer la colonne cat (rajoutée automatiquement par GRASS) de la table DATA. Supprimer une colonne dans SQLite est assez compliqué. C'est pourquoi nous allons utiliser la commande db.dropcol de GRASS. Attention, la colonne cat est utilisée pour faire le lien avec les objets géographiques. Néanmoins, comme cette couche est purement attributaire, il n'y a aucun problème à la supprimer (ce qui n'est pas le cas de la couche COMMUNES):
    db.dropcol -f table=DATA column=cat
  • Création de la table jointe:
    echo "CREATE TABLE COM_JOINTE AS SELECT COMMUNES.*, DATA.* FROM COMMUNES JOIN DATA ON DATA.value=COMMUNES.ID_OBJ" | db.execute
  • Export de la table jointe au format DBF:
    db.copy to_driver=dbf to_database=/home/user/GRASS/DEP49/PERMANENT/dbf/ to_table=COMMUNES from_driver=sqlite from_database=temp_db.sqlite from_table=COM_JOINTE
  • Connexion de la couche vers la table au format DBF:
    # Actuellement, notre couche COMMUNES utilise la table attributaire COMMUNES située dans une base de données SQLite:
    v.db.connect -p map=COMMUNES
    Vector map <COMMUNES@PERMANENT> is connected by:
    layer <1> table <COMMUNES> in database <temp_db.sqlite> through driver <sqlite> with key <cat>
    # On doit reconnecter la table géographique COMMUNES avec la table attributaire DBF COMMUNES:
    v.db.connect -o map=COMMUNES driver=dbf database=/home/user/GRASS/DEP49/PERMANENT/dbf/ table=COMMUNES key=cat
  • Les données sont maintenant importées et on peut éventuellement supprimer nos tables anciennes:
    # On supprime les tables qui ne servent plus à rien
    db.droptable -f DATA
    db.droptable -f COMMUNES

    # Enfin, on indique qu'on souhaite se connecter en utilisant le pilote DBF par défaut pour que les prochains imports se fasse dans ce format
    db.connect driver=dbf database=/home/user/GRASS/DEP49/PERMANENT/dbf/