Manipuler les objets d'une couche

Les routes /vmap/layers/{id}/query permettent de lire et d'écrire les objets d'une couche vMap sans connaître la table sous-jacente : vMap résout la source de données (base, schéma, table, champ identifiant, SRID) à partir de la définition de la couche, puis applique la requête.

Méthode

Route

Objectif

GET

/vmap/layers/{id}/query

lit les objets de la couche

POST

/vmap/layers/{id}/query

crée un objet

PUT

/vmap/layers/{id}/query/{object_id}

met à jour un objet

DELETE

/vmap/layers/{id}/query/{object_id}

supprime un objet

  • {id} : identifiant de la couche (layer_id) ;

  • {object_id} : valeur du champ identifiant de l'objet dans la table de la couche.

Ces quatre routes exigent un jeton d'authentification valide et l'un des rôles vmap_user ou vmap_data_manager.

Avertissement

La création se fait sur la collection (/query), sans {object_id} : l'identifiant de l'objet créé est retourné dans la réponse. Les routes PUT et DELETE portent au contraire sur un objet précis.

Attention : l'identifiant n'est attribué par la base que si la colonne le prévoit (séquence ou valeur par défaut). Sur une couche dont le champ identifiant est une donnée métier, il doit être fourni dans le corps de la requête.

Note

Au-delà des droits applicatifs vMap, les opérations restent soumises aux droits PostgreSQL de l'utilisateur sur la table de la couche. La route GET /vmap/layers/{id}/rights permet de connaître ces droits.


Format des géométries

Les géométries sont échangées en EWKT dans les deux sens : le SRID précède la géométrie, séparé par un point-virgule.

En WGS 84 (EPSG:4326), les coordonnées sont des degrés, dans l'ordre longitude latitude :

SRID=4326;POINT(2.35 48.85)

En Lambert 93 (EPSG:2154), la projection la plus courante en France métropolitaine, ce même point s'écrit avec des coordonnées en mètres :

SRID=2154;POINT(652301.56 6861302.73)

Un polygone en Lambert 93 suit la même logique :

SRID=2154;POLYGON((652300 6861300, 652400 6861300, 652400 6861400, 652300 6861400, 652300 6861300))

En lecture, la géométrie est retournée par ST_AsEWKT. En écriture, elle est reprojetée vers le SRID de la colonne (ST_Transform(ST_GeomFromEWKT(...), srid)) et, si le type déclaré de la couche est MULTIPOLYGON, MULTILINESTRING ou MULTIPOINT, convertie en géométrie multiple.

Note

Le SRID fourni n'a donc pas besoin de correspondre à celui de la colonne : vMap se charge de la reprojection. En revanche, il doit être exact, faute de quoi la géométrie est écrite au mauvais endroit sans qu'aucune erreur ne soit remontée. Le paramètre result_srid permet, en lecture, de demander la géométrie dans une autre projection que celle de la colonne.


GET /vmap/layers/{id}/query

Paramètres de la lecture

Paramètre

Obligatoire

Description

attributs

non

liste des colonnes à retourner, séparées par | ; toutes par défaut

filter

non

filtre JSON, voir Le paramètre filter

order_by

non

colonne de tri

sort_order

non

ASC (défaut) ou DESC

limit

non

nombre maximum de lignes ; par défaut la valeur de la propriété selection_limit (1000 dans le conf.sample), ou 2000 si elle n'est pas définie

offset

non

nombre de lignes ignorées en début de résultat

distinct

non

true pour dédoublonner les résultats

get_geom

non

false pour exclure la colonne géométrique ; sans effet si attributs est fourni

result_srid

non

SRID dans lequel reprojeter la géométrie retournée

intersect_geom

non

géométrie EWKT ; ne retourne que les objets qui l'intersectent

intersect_buffer

non

tampon appliqué à intersect_geom, dans l'unité de la projection de la couche

schema_type

non

form (défaut) ou requestor : bascule vers la table spécifique déclarée par la couche pour ce contexte, si elle en déclare une

Note

Une valeur de limit est toujours appliquée : c'est une protection contre les requêtes trop lourdes. Pour parcourir un volume important, il faut paginer avec limit et offset en s'appuyant sur total_row_number.

Sur une couche polygonale, intersect_buffer est ramené à 0.

Exemple de lecture

curl -X GET "https://[hostname]/vmap/v2/vmap/layers/42/query" \
     -H "Authorization: eyJhbGciOiJ..." \
     -G \
     --data-urlencode "attributs=id|nom|geom" \
     --data-urlencode "limit=100"

Réponse de la lecture

{
  "status": 1,
  "data": [
    { "id": 1, "nom": "Parcelle A", "geom": "SRID=2154;POLYGON((...))" }
  ],
  "total_row_number": 1,
  "list_count": 1
}

POST /vmap/layers/{id}/query

Crée un objet dans la table de la couche.

Corps de la requête (création)

Un objet dont les clés sont les noms des colonnes de la table. Seules les colonnes existantes sont prises en compte ; les autres clés sont ignorées. Le champ identifiant n'a pas à être fourni s'il est auto-généré.

curl -X POST "https://[hostname]/vmap/v2/vmap/layers/42/query" \
     -H "Authorization: eyJhbGciOiJ..." \
     -H "Content-Type: application/json" \
     -d '{
           "nom": "Nouvelle parcelle",
           "surface": 1250,
           "geom": "SRID=4326;POLYGON((2.35 48.85, 2.36 48.85, 2.36 48.86, 2.35 48.85))"
         }'

Réponse de la création

{
  "status": 1,
  "data": {
    "id": 128,
    "nom": "Nouvelle parcelle",
    "surface": 1250,
    "geom": "SRID=2154;MULTIPOLYGON((...))"
  }
}

L'objet complet tel qu'il a été inséré est retourné (RETURNING *), ce qui permet de récupérer l'identifiant attribué ainsi que les valeurs calculées par la base (valeurs par défaut, déclencheurs, géométrie reprojetée).

L'insertion est effectuée dans une transaction : en cas d'échec, aucune ligne n'est créée.

Création avec une image ou un document

Pour joindre un fichier, l'appel doit être envoyé en multipart/form-data, avec une partie fichier nommée comme la colonne qui doit le recevoir. C'est cette présence dans la requête qui déclenche le traitement de la colonne comme champ fichier — aucune configuration particulière de la couche n'est nécessaire.

Pour qu'un fichier soit traité comme une image (redimensionnement et génération d'une miniature) et non comme un simple document, la colonne doit en plus être listée dans le paramètre vitis_thumbnail_image_files, un objet JSON transmis dans la même requête.

Soit une couche dont la table comporte une colonne photo :

curl -X POST "https://[hostname]/vmap/v2/vmap/layers/42/query" \
     -H "Authorization: eyJhbGciOiJ..." \
     -F "nom=Nouvelle parcelle" \
     -F "surface=1250" \
     -F "geom=SRID=2154;POLYGON((652300 6861300, 652400 6861300, 652400 6861400, 652300 6861400, 652300 6861300))" \
     -F "photo=@/chemin/local/vue_aerienne.png" \
     -F 'vitis_thumbnail_image_files={"photo": true}'

Réponse :

{
  "status": 1,
  "data": {
    "id": 129,
    "nom": "Nouvelle parcelle",
    "surface": 1250,
    "photo": "https://[hostname]/vmap/v2/vitis/downloads?file=eyJhbGciOiJ...",
    "geom": "SRID=2154;MULTIPOLYGON((...))"
  }
}

Le fichier est écrit sous <ws_data_dir>/vmap/layer_entities/{layer_id}/documents/{object_id}/{colonne}/{nom_du_fichier}, et la colonne en base ne stocke que le nom du fichier. La réponse, elle, contient directement l'URL de téléchargement.

Avertissement

Pour une colonne traitée comme image, l'extension du fichier est réécrite en .jpg (sauf pour un SVG, conservé tel quel), l'image étant convertie au format JPEG. Un fichier vue_aerienne.png est donc stocké sous le nom vue_aerienne.jpg : il ne faut pas se fier au nom transmis pour reconstruire l'URL, mais utiliser celle retournée par l'API.

Les images sont par ailleurs redimensionnées pour ne pas dépasser 1000 px de large ou de haut, en conservant le ratio.

Note

Les colonnes fichier sont toujours traitées comme multi-fichiers : plusieurs parties portant le même nom de colonne peuvent être envoyées dans un seul appel. En base, les noms de fichiers sont alors concaténés avec le séparateur |, et la lecture retourne un tableau d'URL au lieu d'une seule.

Seule la présence de la clé dans vitis_thumbnail_image_files est testée : la valeur associée importe peu. Sans ce paramètre, le fichier est conservé tel quel, sans redimensionnement ni changement d'extension — c'est le comportement voulu pour un document (PDF, fichier bureautique).

Erreurs de la création

Code HTTP

Réponse

Cause

302

{"status": 0, "message": "Entity not found", "error": ..., "sqlstate_error": ...}

l'insertion a échoué : contrainte violée, identifiant non récupérable, erreur SQL. Les champs error et sqlstate_error précisent la cause

401

ERROR_POSTGRES_USER_BAD_CONNECTION

la base de la couche est inaccessible

500

GENERIC_ERROR

la source de données de la couche n'a pas pu être résolue


PUT /vmap/layers/{id}/query/{object_id}

Met à jour l'objet identifié par {object_id}.

Corps de la requête (mise à jour)

Le même format que pour POST : un objet dont les clés sont les noms des colonnes. Seules les colonnes présentes dans le corps sont modifiées.

curl -X PUT "https://[hostname]/vmap/v2/vmap/layers/42/query/128" \
     -H "Authorization: eyJhbGciOiJ..." \
     -H "Content-Type: application/json" \
     -d '{
           "nom": "Parcelle renommée",
           "surface": 1300
         }'

Réponse de la mise à jour

{
  "status": 1,
  "data": {
    "id": 128,
    "nom": "Parcelle renommée",
    "surface": 1300,
    "geom": "SRID=2154;MULTIPOLYGON((...))"
  }
}

L'objet retourné est celui obtenu après mise à jour, enrichi le cas échéant des références de fichiers de la couche.

Erreurs de la mise à jour

Code HTTP

errorMessage

Cause

401

ERROR_GENERIC_CONTROLLER_UPDATE

l'objet n'existe pas ou la mise à jour a échoué

401

ERROR_POSTGRES_USER_BAD_CONNECTION

la base de la couche est inaccessible

500

GENERIC_ERROR

la source de données de la couche n'a pas pu être résolue


DELETE /vmap/layers/{id}/query/{object_id}

Supprime l'objet identifié par {object_id}. Aucun corps de requête n'est attendu.

curl -X DELETE "https://[hostname]/vmap/v2/vmap/layers/42/query/128" \
     -H "Authorization: eyJhbGciOiJ..."

Réponse de la suppression

{
  "status": 1,
  "data": 128
}

L'existence de l'objet est vérifiée avant suppression. Une fois la ligne supprimée, vMap supprime également le répertoire des documents associés à cet objet (<ws_data_dir>/vmap/layer_entities/{id}/documents/{object_id}).

Avertissement

La suppression est définitive : ni la ligne, ni les documents et images rattachés à l'objet ne sont récupérables.

Erreurs de la suppression

Code HTTP

Réponse

Cause

302

{"status": 0, "message": "Entity not found"}

aucun objet ne correspond à {object_id}

401

ERROR_POSTGRES_USER_BAD_CONNECTION

la base de la couche est inaccessible

500

GENERIC_ERROR

la source de données de la couche n'a pas pu être résolue


Variantes multi-objets

Pour agir sur plusieurs objets en un seul appel, trois routes complémentaires existent :

Méthode

Route

Description

Corps

PUT

/vmap/layers/{id}/query

applique les mêmes valeurs à plusieurs objets

les colonnes à modifier, plus ids : tableau des identifiants

DELETE

/vmap/layers/{id}/query

supprime plusieurs objets

ids : tableau des identifiants

POST

/vmap/layers/{id}/queries

met à jour un objet et en insère d'autres dans la même opération

update : objet à mettre à jour (identifiant inclus), insert : tableau d'objets à créer

Note

POST /vmap/layers/{id}/queries est transactionnel au sens applicatif : si une insertion échoue, les objets déjà insérés sont supprimés et l'objet mis à jour est restauré dans son état d'origine.


Personnalisation par module

Un module peut fournir son propre contrôleur pour une couche donnée. Si ce contrôleur redéfinit postQueryLayer, updateQueryLayer, deleteQueryLayer, multiUpdateQueryLayer ou postAndUpdateQueryLayer, c'est sa version qui est exécutée à la place du comportement décrit ci-dessus. Ces routes peuvent donc se comporter différemment sur les couches gérées par un module métier.