Action dans une popup

C’est une fonctionnalité dans Lizmap 3.4.

Principe

Ce module permet d’ajouter un ou plusieurs boutons d’action dans la popup Lizmap affichée pour un objet PostgreSQL, qui déclenchera une requête dans la base de données et renverra une **géométrie ** à afficher sur la carte.

Il lit un fichier de configuration JSON qui doit être placé à côté du projet QGIS dans le même répertoire. Ce fichier répertorie les actions PostgreSQL à ajouter dans la popup pour une ou plusieurs couches vectorielles QGIS PostgreSQL.

../../_images/publish-configuration-action-popup.gif

Configurer l’outil

  • Chaque action est caractérisée par un layer_id, un name, un title, une icon, quelques options facultatives, style` ` et ``callbacks. Une nouvelle propriété confirm peut être utilisée depuis Lizmap 3.5
  • Un calque peut avoir une ou plusieurs actions
  • Vous pouvez avoir un ou plusieurs calques avec leurs propres actions

Exemple avec ce fichier de configuration JSON, nom myproject.qgs.action si le fichier de projet QGIS est nommé myproject.qgs. Dans ce projet, il existe une couche vectorielle appelée Points avec l’ID de couche interne points_a7e8943b_7138_4788_a775_f94cbd0ad8b6 (vous pouvez obtenir l’ID interne de la couche QGIS avec l’expression @layer_id)

{
    "points_a7e8943b_7138_4788_a775_f94cbd0ad8b6": [
        {
            "name": "buffer_500",
            "title": "Buffer 500m around this object",
            "confirm": "Do you really want to show the buffer ?",
            "icon": "icon-leaf",
            "options": {
                "buffer_size": 500,
                "other_param": "yes",
            },
            "style": {
                "graphicName": "circle",
                "pointRadius": 6,
                "fill": true,
                "fillColor": "lightblue",
                "fillOpacity": 0.3,
                "stroke": true,
                "strokeWidth": 4,
                "strokeColor": "blue",
                "strokeOpacity": 0.8
            },
            "callbacks": [
                {"method": "zoom"},
                {"method": "select", "layerId": "bati_1a016229_287a_4b5e_a4f7_a2080333f440"},
                {"method": "redraw", "layerId": "bati_1a016229_287a_4b5e_a4f7_a2080333f440"}
            ]
        }
    ]
}

Le fichier configuration JSON répertorie les couches QGIS pour lesquelles vous souhaitez déclarer des actions. Chaque couche est définie par son ID de couche QGIS, par exemple ici points_a7e8943b_7138_4788_a775_f94cbd0ad8b6, et pour chaque ID, une liste d’objets décrivant les actions à autoriser.

Chaque action est un objet défini par :

  • un name qui est l’identifiant de l’action.

  • un title qui est utilisé comme étiquette dans l’interface Lizmap

  • un icon qui sera affiché sur le bouton de l’action (voir https://getbootstrap.com/2.3.2/base-css.html#icons)

  • une propriété facultative confirm, depuis Lizmap 3.5, contenant du texte. Si défini, une boîte de dialogue de confirmation sera affichée à l’utilisateur pour demander si l’action doit vraiment être lancée ou non. Utilisez-le si l’action peut modifier certaines données de votre base de données.

  • un objet options, donnant quelques paramètres supplémentaires pour cette action. Vous pouvez ajouter n’importe quel paramètre nécessaire.

  • un objet style permettant de configurer le style de géométrie renvoyé. Il suit les attributs de style OpenLayers.

  • un objet callbacks permet de déclencher certaines actions après le retour de la géométrie générée. Ils sont définis par un nom de method, qui peut actuellement être :

    • zoom: zoome vers la géométrie retournée
    • select : sélectionnez les entités d’une couche donnée coupant la géométrie renvoyée. La couche cible QGIS internal ID doit être ajoutée dans la propriété layerId. Dans l’exemple, les entités de la couche contenant les bâtiments, l’ID bati_1a016229_287a_4b5e_a4f7_a2080333f440 sera sélectionné
    • redraw : redessine (rafraîchit) une couche donnée dans la carte. L’ID QGIS de la couche cible doit être ajouté dans la propriété layerId.

Lizmap détecte la présence de ce fichier de configuration et ajoute la logique nécessaire lors du chargement de la carte. Lorsque les utilisateurs cliquent sur un objet de l’une de ces couches sur la carte, le panneau contextuel affiche les données de l’entité. En haut de chaque élément contextuel, une barre d’outils affichera un bouton pour chaque action de couche. L’action titre s’affichera en survolant le bouton d’action.

Chaque bouton déclenche l’action correspondante, s’il n’est pas encore actif (sinon il désactive et efface la géométrie) :

  • l’application Lizmap vérifie si l’action est bien configuré,
  • crée la requête PostgreSQL et l’exécute dans la couche de base de données PostgreSQL. (Voir exemple ci-dessous)
  • Cette requête retourne un GeoJSON qui est ensuite affiché sur la carte.
  • Si des callbacks ont été configurés, ils sont lancés
  • Depuis Lizmap 3.5, un événement Lizmap actionResultReceived est émis avec les données et les propriétés de l’action.

La requête PostgreSQL créée est construite par le client Web Lizmap et utilise la fonction PostgreSQL lizmap_get_data(json) qui doit être créée au préalable dans la table de la base de données PostgreSQL. Cette fonction utilise également une fonction plus générique query_to_geojson(text) qui transforme n’importe quelle chaîne de requête PostgreSQL en une sortie GeoJSON.

Here is an example below of the query executed in the PostgreSQL database by Lizmap Web Client internally, for the example configuration given above, when the users clicks on the button action buffer_500, for the feature with id 1 of the layer Points corresponding to the PostgreSQL table test.points:

SELECT public.lizmap_get_data('{
    "layer_name":"points",
    "layer_schema":"test",
    "layer_table":"points",
    "feature_id":1,
    "action_name":"buffer_500",
    "buffer_size":500,
    "other_param": "yes"
}') AS data;

You can see that Lizmap creates a JSON parameters with all needed information and run the PostgreSQL function lizmap_get_data(text).

Vous devez créer cette fonction PostgreSQL lizmap_get_data(text) qui renvoie un texte GeoJSON valide contenant un seul objet. Le code SQL suivant est un exemple pour vous aider à créer les fonctions nécessaires. Évidemment, vous devez l’adapter à vos besoins.

-- Returns a valid GeoJSON from any query
CREATE OR REPLACE FUNCTION query_to_geojson(datasource text)
RETURNS json AS
$$
DECLARE
    sqltext text;
    ajson json;
BEGIN
    sqltext:= format('
        SELECT jsonb_build_object(
            ''type'',  ''FeatureCollection'',
            ''features'', jsonb_agg(features.feature)
        )::json
        FROM (
          SELECT jsonb_build_object(
            ''type'',       ''Feature'',
            ''id'',         id,
            ''geometry'',   ST_AsGeoJSON(ST_Transform(geom, 4326))::jsonb,
            ''properties'', to_jsonb(inputs) - ''geom''
          ) AS feature
          FROM (
              SELECT * FROM (%s) foo
          ) AS inputs
        ) AS features
    ', datasource);
    RAISE NOTICE 'SQL = %s', sqltext;
    EXECUTE sqltext INTO ajson;
    RETURN ajson;
END;
$$
LANGUAGE 'plpgsql'
IMMUTABLE STRICT;

COMMENT ON FUNCTION query_to_geojson(text) IS 'Generate a valid GEOJSON from a given SQL text query.';

-- Create a query depending on the action, layer and feature and returns a GeoJSON.
CREATE OR REPLACE FUNCTION lizmap_get_data(parameters json)
RETURNS json AS
$$
DECLARE
    feature_id integer;
    layer_name text;
    layer_table text;
    layer_schema text;
    action_name text;
    sqltext text;
    datasource text;
    ajson json;
BEGIN

    action_name:= parameters->>'action_name';
    feature_id:= (parameters->>'feature_id')::integer;
    layer_name:= parameters->>'layer_name';
    layer_schema:= parameters->>'layer_schema';
    layer_table:= parameters->>'layer_table';

    -- Action buffer_500
    -- Written here as an example
    -- Performs a buffer on the geometry
    IF action_name = 'buffer_500' THEN
        datasource:= format('
            SELECT
            %1$s AS id,
            ''The buffer '' || %4$s || ''m has been displayed in the map'' AS message,
            ST_Buffer(geom, %4$s) AS geom
            FROM "%2$s"."%3$s"
            WHERE id = %1$s
        ',
        feature_id,
        layer_schema,
        layer_table,
        parameters->>'buffer_size'
        );
    ELSE
    -- Default : return geometry
        datasource:= format('
            SELECT
            %1$s AS id,
            ''The geometry of the object have been displayed in the map'' AS message
            geom
            FROM "%2$s"."%3$s"
            WHERE id = %1$s
        ',
        feature_id,
        layer_schema,
        layer_table
        );

    END IF;

    SELECT query_to_geojson(datasource)
    INTO ajson
    ;
    RETURN ajson;
END;
$$
LANGUAGE 'plpgsql'
IMMUTABLE STRICT;

COMMENT ON FUNCTION lizmap_get_data(json) IS 'Generate a valid GeoJSON from an action described by a name, PostgreSQL schema and table name of the source data, a QGIS layer name, a feature id and additional options.';
  • La fonction lizmap_get_data(json) est fournie ici à titre d’exemple. Puisqu’il s’agit du point d’entrée clé, vous devez l’adapter à vos besoins. Il vise à créer une requête pour chaque action, créée dynamiquement pour les paramètres donnés, et à renvoyer une représentation GeoJSON des données par rapport à la requête. Vous devriez avoir une seule entité renvoyée : utilisez l’agrégation si nécessaire. Dans l’exemple ci-dessus, nous utilisons la méthode format pour définir le texte de la requête et la fonction query_to_geojson pour renvoyer le GeoJSON pour cette requête.
  • You can use all the given parameters (action name, source data schema and table name, feature id, QGIS layer name) to create the appropriate query for your action(s), by using the PostgreSQL IF THEN ELSIF ELSE clauses. See the content of the parameters variable in the example above, containing some of the JSON configuration file properties, and some properties of the QGIS layer:
    • the action name action_name, for example buffer_500. You should use a simple word with only letters, digits and _,
    • QGIS layer name (as in QGIS legend): layer_name, for example Points,
    • the PostgreSQL table schema layer_schema and table name layer_table for this layer,
    • the object feature id feature_id, which corresponds to the value of the primary key field for the popup object,
    • the other properties given in the JSON configuration file, in the options property, such as buffer_size which is 500 in the example
  • The IF ELSE is used to do a different query, built in the datasource variable, by checking the action name
  • If the return data contains a message field, such as shown in the example above, the text contained in this field will be displayed in the map in a message bubble.
  • The geometry returned by the function will be displayed on the map.
  • You could use your function to edit some data in your database, before returning a GeoJSON. To do so, you need to replace the IMMUTABLE property par VOLATILE. Please use it with care !

Since Lizmap Web Client triggers an event actionResultReceived any time the user clicks on an action button, and data is returned (in the same time as the result geometry is drawn on the map), you could use your own Javascript code to add some logic after the result is shown.

Voir aussi

Chapter Ajouter votre propre JavaScript

Par exemple, ici nous écrivons simplement dans la console du navigateur le contenu reçu :

lizMap.events.on({

    actionResultReceived: function(e) {
        // QGIS Layer id
        var layerId = e.layerId;
        console.log('Layer ID = ' + layerId);
        // Feature ID, which means the value of the primary key field
        var featureId = e.featureId;
        console.log('Feature ID = ' + featureId);
        // Action item with its name and other properties: name, title, options, styles, etc.
        var action = e.action;
        console.log('Action properties = ');
        console.log(action);
        // Features returned by the action
        var features = e.features;
        console.log('Returned object = ');
        console.log(features);
    }
});

Vous pouvez utiliser ces données à votre guise dans votre code JS.