Exemple complet : la couche « Ville »

Cette page déroule un scénario complet d'utilisation de l'API, de la connexion jusqu'à la suppression d'un objet, sur un jeu de données de démonstration installé par l'application vMap2.

Le jeu de données

Le script d'installation crée la table data_demo_vmap.f_villes_l93, qui contient 1 683 communes françaises, et la publie sous forme de couche vMap nommée « Ville » (layer_id = 2).

Colonne

Type SQL

Rôle

code

character varying(5)

code INSEE — champ identifiant de la couche

nom

character varying(50)

nom de la commune

pop90

integer

population au recensement de 1990

geom

geometry(POINT, 2154)

localisation, en Lambert 93

Avertissement

Le champ identifiant de cette couche est code, une chaîne de 5 caractères, et non un entier auto-généré. En conséquence :

  • dans les URL, {object_id} est un code INSEE (.../query/69123) ;

  • à la création, code doit être fourni explicitement — aucune séquence ne l'attribue.

Dans tous les exemples qui suivent, https://[hostname]/vmap/v2 est l'URL de l'API (valeur de la propriété app_api_url) et $TOKEN contient le jeton obtenu à l'étape 1.


Étape 1 — Obtenir un jeton

TOKEN=$(curl -s -X POST "https://[hostname]/vmap/v2/vitis/privatetoken" \
             -H "Content-Type: application/x-www-form-urlencoded" \
             -d "user=mon_login" \
             -d "password=mon_mot_de_passe" \
        | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")

Les appels suivants réutilisent ce jeton dans l'en-tête Authorization. Voir Authentification : obtenir un jeton pour la variante avec un jeton public.


Étape 2 — Retrouver la source de données de la couche

Les routes genericqueries permettent d'explorer la base sans connaître la structure au préalable. Le schéma de démonstration se repère ainsi :

curl -s -X GET "https://[hostname]/vmap/v2/vitis/genericqueries/schemas?database=vmap" \
     -H "Authorization: $TOKEN"
{
  "status": 1,
  "data": [
    "s_vitis",
    "s_vmap_2",
    "s_cadastre",
    "data_demo_vmap",
    "..."
  ]
}

La route ne filtre rien : les schémas système de PostgreSQL et le schéma technique de Vitis (s_vitis) figurent dans la réponse à côté des schémas applicatifs. Celui qui nous intéresse ici est data_demo_vmap.

Puis les tables de ce schéma :

curl -s -X GET "https://[hostname]/vmap/v2/vitis/genericqueries/tables?database=vmap&schema=data_demo_vmap" \
     -H "Authorization: $TOKEN"
{
  "status": 1,
  "data": ["f_villes_l93", "f_fleuves_l93", "limite_france_continent"]
}

Cette étape est facultative : elle interroge la table directement, alors que les étapes suivantes passent par la couche. Elle reste utile pour vérifier la structure réelle des données ou lire une table qui n'est pas publiée comme couche.

Note

Interroger la couche plutôt que la table présente deux avantages : la source de données (base, schéma, table, champ identifiant, SRID) est résolue par vMap, et les droits vMap de la couche s'appliquent. C'est la méthode à privilégier dès qu'une couche existe.


Étape 3 — Lire les objets de la couche

Les 5 communes les plus peuplées

curl -s -X GET "https://[hostname]/vmap/v2/vmap/layers/2/query" \
     -H "Authorization: $TOKEN" \
     -G \
     --data-urlencode "attributs=code|nom|pop90" \
     --data-urlencode "order_by=pop90" \
     --data-urlencode "sort_order=DESC" \
     --data-urlencode "limit=5"
{
  "status": 1,
  "data": [
    { "code": "13055", "nom": "Marseille",  "pop90": 800309 },
    { "code": "69123", "nom": "Lyon",       "pop90": 415479 },
    { "code": "31555", "nom": "Toulouse",   "pop90": 358598 },
    { "code": "06088", "nom": "Nice",       "pop90": 342903 },
    { "code": "67482", "nom": "Strasbourg", "pop90": 252274 }
  ],
  "total_row_number": 1683,
  "list_count": 5
}

total_row_number vaut 1 683 — le nombre total de communes, sans tenir compte de limit — alors que list_count vaut 5. C'est ce couple qui permet de paginer avec offset.

Ici, attributs restreint la réponse à trois colonnes, ce qui écarte la géométrie et allège considérablement la liste. Le paramètre get_geom=false rend le même service lorsqu'on veut toutes les colonnes sauf la géométrie : il n'a d'effet que si attributs est absent.

Une commune précise

Le champ identifiant étant code, un filtre sur cette colonne suffit :

curl -s -X GET "https://[hostname]/vmap/v2/vmap/layers/2/query" \
     -H "Authorization: $TOKEN" \
     -G \
     --data-urlencode 'filter={"column":"code","compare_operator":"=","value":"69123"}'
{
  "status": 1,
  "data": [
    {
      "code": "69123",
      "nom": "Lyon",
      "pop90": 415479,
      "geom": "SRID=2154;POINT(842578.79 6520181)"
    }
  ],
  "total_row_number": 1,
  "list_count": 1
}

Un opérateur seul est accepté sans enveloppe relation / operators : il est automatiquement encapsulé dans un AND.

Les communes de plus de 100 000 habitants

curl -s -X GET "https://[hostname]/vmap/v2/vmap/layers/2/query" \
     -H "Authorization: $TOKEN" \
     -G \
     --data-urlencode "attributs=code|nom|pop90" \
     --data-urlencode "order_by=nom" \
     --data-urlencode 'filter={"relation":"AND","operators":[{"column":"pop90","compare_operator":">","value":100000}]}'
{
  "status": 1,
  "data": [
    { "code": "13001", "nom": "Aix-en-Provence", "pop90": 123778 },
    { "code": "80021", "nom": "Amiens",          "pop90": 131880 },
    { "code": "49007", "nom": "Angers",          "pop90": 141354 }
  ],
  "total_row_number": 44,
  "list_count": 44
}

Le jeu de données compte 44 communes de plus de 100 000 habitants (réponse tronquée ci-dessus).

Recherche par nom, insensible à la casse et aux accents

curl -s -X GET "https://[hostname]/vmap/v2/vmap/layers/2/query" \
     -H "Authorization: $TOKEN" \
     -G \
     --data-urlencode "attributs=code|nom" \
     --data-urlencode 'filter={"column":"nom","compare_operator":"LIKE","value":"bourg%","compare_operator_options":{"case_insensitive":true,"accent_insensitive":true}}'
{
  "status": 1,
  "data": [
    { "code": "01053", "nom": "Bourg-en-Bresse" },
    { "code": "18033", "nom": "Bourges" },
    { "code": "38053", "nom": "Bourgoin-Jallieu" }
  ],
  "total_row_number": 8,
  "list_count": 8
}

Les communes autour d'un point

Le paramètre intersect_geom attend une géométrie EWKT, intersect_buffer un tampon exprimé dans l'unité de la projection de la couche — ici des mètres. Pour les communes dans un rayon de 10 km autour de Lyon :

curl -s -X GET "https://[hostname]/vmap/v2/vmap/layers/2/query" \
     -H "Authorization: $TOKEN" \
     -G \
     --data-urlencode "attributs=code|nom|pop90" \
     --data-urlencode "intersect_geom=SRID=2154;POINT(842578.79 6520181)" \
     --data-urlencode "intersect_buffer=10000"
{
  "status": 1,
  "data": [
    { "code": "69029", "nom": "Bron",             "pop90": 39683 },
    { "code": "69034", "nom": "Caluire-et-Cuire", "pop90": 41340 },
    { "code": "69266", "nom": "Villeurbanne",     "pop90": 116851 }
  ],
  "total_row_number": 20,
  "list_count": 20
}

Étape 4 — Créer une commune

La création se fait sur la collection, sans {object_id} dans l'URL. Le champ code doit être fourni, puisqu'il porte l'identifiant.

curl -s -X POST "https://[hostname]/vmap/v2/vmap/layers/2/query" \
     -H "Authorization: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
           "code": "99999",
           "nom": "Villeneuve-de-Test",
           "pop90": 4200,
           "geom": "SRID=2154;POINT(842000 6520000)"
         }'
{
  "status": 1,
  "data": {
    "code": "99999",
    "nom": "Villeneuve-de-Test",
    "pop90": 4200,
    "geom": "SRID=2154;POINT(842000 6520000)"
  }
}

La réponse contient l'objet tel qu'il a été inséré en base, ce qui permet de vérifier les valeurs réellement enregistrées (valeurs par défaut, déclencheurs, géométrie reprojetée).

La géométrie peut être transmise dans une autre projection que celle de la colonne : vMap la reprojette. Le même point en WGS 84 s'écrirait :

{ "geom": "SRID=4326;POINT(4.8357 45.7640)" }

Avertissement

Si code est omis, l'insertion échoue en HTTP 302 avec {"status": 0, "message": "Entity not found"} : l'API n'a pas pu récupérer l'identifiant de la ligne créée. Le message est trompeur sur une création — les champs error et sqlstate_error de la réponse donnent la cause réelle.


Étape 5 — Mettre à jour la commune

L'URL porte cette fois l'identifiant, c'est-à-dire le code INSEE. Seules les colonnes présentes dans le corps sont modifiées :

curl -s -X PUT "https://[hostname]/vmap/v2/vmap/layers/2/query/99999" \
     -H "Authorization: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{ "pop90": 4500 }'
{
  "status": 1,
  "data": {
    "code": "99999",
    "nom": "Villeneuve-de-Test",
    "pop90": 4500,
    "geom": "SRID=2154;POINT(842000 6520000)"
  }
}

Déplacer l'objet revient à ne mettre à jour que la colonne géométrique :

curl -s -X PUT "https://[hostname]/vmap/v2/vmap/layers/2/query/99999" \
     -H "Authorization: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{ "geom": "SRID=2154;POINT(843000 6521000)" }'

Étape 6 — Supprimer la commune

curl -s -X DELETE "https://[hostname]/vmap/v2/vmap/layers/2/query/99999" \
     -H "Authorization: $TOKEN"
{
  "status": 1,
  "data": "99999"
}

L'existence de l'objet est vérifiée avant suppression : un code inconnu retourne un HTTP 302 avec {"status": 0, "message": "Entity not found"}.

Un second appel identique retourne donc cette même erreur, la ligne ayant déjà disparu.


Récapitulatif des appels

Étape

Méthode

Route

1. Jeton

POST

/vitis/privatetoken

2. Schémas

GET

/vitis/genericqueries/schemas

2. Tables

GET

/vitis/genericqueries/tables

3. Lecture

GET

/vmap/layers/2/query

4. Création

POST

/vmap/layers/2/query

5. Mise à jour

PUT

/vmap/layers/2/query/99999

6. Suppression

DELETE

/vmap/layers/2/query/99999

Les étapes 4 à 6 exigent le rôle vmap_user ou vmap_data_manager, ainsi que les droits PostgreSQL correspondants sur data_demo_vmap.f_villes_l93.