Akce - Přidání několika vlastních tlačítek spouštějících dotazy PostgreSQL

Princip

Tento modul umožňuje přidat jednu nebo více akcí do webového rozhraní. Koncept byl inspirován QGIS actions, které lze použít ke spouštění skriptů uvnitř QGIS.

Příklad akce funkce:

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

V současné době je jediným motorem pro akce Lizmap dotazy PostgreSQL (Python nebude podporován). K přidání specifické logiky do webové mapy můžete využít výkon PostgreSQL a PostGIS.

Varování

Akce Lizmap se proto liší od nativních akcí QGIS a nejsou vzájemně kompatibilní.

Když uživatel klikne na akční tlačítko, odešle Lizmap Web Client dotaz do databáze PostgreSQL s aktuálními kontextovými údaji (rozsah mapy, ID prvku atd.).

S těmito parametry se volá specifická funkce lizmap_get_data, která vrací odpověď GeoJSON obsahující jeden nebo více prvků vygenerovaných dotazem SQL pro danou akci.

Webový klient Lizmap pak z této odpovědi spustí některé zpětné vazby:

  • přiblížení nebo vycentrování vrácené geometrie,

  • výběr prvků jiné vrstvy, které protínají vrácenou geometrii,

  • zobrazení zprávy atd.

Podporovány jsou tři akční oblasti :

  • project : položka nabídky akcí je přidána na levý panel nabídky, pokud má mapa alespoň jednu akci projektu. Na novém panelu se zobrazí výběr akce a tlačítko pro spuštění vybrané akce.

  • layer: výběr akce se zobrazí v panelu Informace o vrstvě, který je viditelný po kliknutí na tlačítko (i) vpravo od názvu vrstvy.

  • feature: na vyskakovací panel nástrojů jsou přidána akční tlačítka pro funkce vrstvy, která umožňují spustit akci specifickou pro každou funkci.

Tip

Pro rozsah project se použije výchozí databáze (soubor profiles.ini.php v kapitole configuration.

Příklad výběru akce:

../../_images/action-selector.png

Ukázka

Ukázku o požárních hydrantech si můžete prohlédnout na ukázkové webové stránce.

Klikněte na požární hydrant a

  • buď vyberte budovy, které jsou ve vzdálenosti do 150 m

  • nebo najděte nejbližší hasičskou stanici

Předpoklady

Konfigurace nástroje

V současné době nelze akce konfigurovat ze zásuvného modulu Lizmap v QGIS. Je třeba napsat zvláštní konfigurační soubor JSON a umístit jej vně projektu QGIS do stejného adresáře. Tento soubor obsahuje seznam akcí PostgreSQL, které mají být přidány do mapy.

Varování

V modulu Lizmap 3.7 se změnila syntaxe JSON.

Pokud používáte starou syntaxi JSON, zobrazí se v aplikaci Lizmap varování s výzvou k přechodu na novější verzi syntaxe.

Každá akce je charakterizována name, title, scope, layers, icon, některými volitelnými options,

Lze použít vlastnosti style, callbacks a confirm.

  • Akci lze navrhnout pro seznam layers: V poli layers by měly být použity identifikátory vrstev QGIS.

  • Akce může mít seznam callbacks.

Příklad konfiguračního souboru JSON s názvem fire_hydrant_actions.qgs.action, pokud se soubor projektu QGIS jmenuje fire_hydrant_actions.qgs. V tomto projektu existuje vektorová vrstva s názvem Požární hydranty s interním ID vrstvy emergency_fire_hydrant_04132268_86fb_4d5e_a426_ce3133494091. Interní ID vrstvy QGIS můžete získat pomocí výrazu QGIS @layer_id.

[
    {
        "name": "buffer_150",
        "title": "Buildings in the fire hydrant area (150m)",
        "scope": "feature",
        "layers" : [
            "emergency_fire_hydrant_04132268_86fb_4d5e_a426_ce3133494091"
        ],
        "confirm": "Do you want to select buildings within 150m from this fire hydrant ?",
        "icon": "icon-home",
        "options": {
            "buffer_size": 150,
            "other_param": "yes"
        },
        "style": {
            "graphicName": "circle",
            "pointRadius": 6,
            "fill": true,
            "fillColor": "lightred",
            "fillOpacity": 0.3,
            "stroke": true,
            "strokeWidth": 4,
            "strokeColor": "red",
            "strokeOpacity": 0.8
        },
        "callbacks": [
             {"method": "zoom"},
             {"method": "select", "layerId": "building_90f7692a_0ae2_4a7d_91de_b63cddb92963"}
        ]
    },
    {
        "name": "closest_fire_station",
        "title": "Find the closest fire station from this fire hydrant",
        "scope": "feature",
        "layers" : [
            "emergency_fire_hydrant_04132268_86fb_4d5e_a426_ce3133494091"
        ],
        "confirm": "Do you want to select the closest fire station from this fire hydrant ?",
        "icon": "icon-resize-small",
        "options": {},
        "style": {
            "graphicName": "circle",
            "pointRadius": 6,
            "fill": true,
            "fillColor": "lightred",
            "fillOpacity": 0.3,
            "stroke": true,
            "strokeWidth": 4,
            "strokeColor": "red",
            "strokeOpacity": 0.8
        },
        "callbacks": [
             {"method": "zoom"},
             {"method": "select", "layerId": "stations_1a71d61f_cb99_4ac4_8bd4_86304af9be44"}
        ]
    }
]

Konfigurační soubor JSON obsahuje seznam deklarovaných akcí.

Každá akce je objekt definovaný:

  • a name, což je identifikátor akce.

  • title, který se používá jako popisek v rozhraní Lizmap

  • scope, který může být: project, layer nebo feature.

  • icon, která se zobrazí na tlačítku akce (viz dokumentace k Bootstrap). Místo ikony bootstrapu lze jako pozadí vyskakovacích akčních tlačítek použít ikonu SVG. Použijte relativní cestu k médiím (Media).

  • nepovinná vlastnost potvrzení, která obsahuje nějaký text. Pokud je nastavena, zobrazí se uživateli potvrzovací dialogové okno s dotazem, zda má být akce skutečně spuštěna, nebo ne. Použijte ji, pokud akce může změnit některá data v databázi.

  • objekt options, který udává některé další parametry pro tuto akci. Můžete přidat libovolné potřebné parametry. Všimněte si, že tyto parametry jsou pevně zakódovány a uživatel je nemůže měnit.

  • objekt style, který umožňuje nastavit styl vrácené geometrie. Řídí se atributy stylu OpenLayers.

  • objekt callbacks umožňuje spustit některé akce po vrácení vygenerované geometrie. Jsou definovány jménem method, které v současnosti může být:

    • zoom: přiblížení vrácené geometrie

    • select: vybere prvky z dané vrstvy, které protínají vrácenou geometrii. Ve vlastnosti layerId musí být přidáno interní ID cílové vrstvy QGIS. V příkladu budou vybrány prvky vrstvy obsahující budovy s ID building_90f7692a_0ae2_4a7d_91de_b63cddb92963.

    • redraw: překreslí (obnoví) danou vrstvu v mapě. ID cílové vrstvy QGIS musí být přidáno do vlastnosti layerId.

Jak Lizmap používá tento konfigurační soubor ke spouštění akcí

Lizmap zjistí přítomnost tohoto konfiguračního souboru a při načítání mapy přidá potřebnou logiku.

Například u akcí s rozsahem feature se po kliknutí na objekt jedné z vrstev akce v mapě zobrazí vyskakovací panel s údaji o funkci. V horní části každého vyskakovacího panelu je na panelu nástrojů zobrazeno jedno tlačítko pro každou akci vrstvy. Po najetí na tlačítko akce se zobrazí její title.

Každé tlačítko spustí příslušnou akci, pokud ještě není aktivní (jinak se deaktivuje a vymaže geometrii na mapě):

  • Backend Lizmap kontroluje, zda je akce dobře nakonfigurována,

  • vytvoří PostgreSQL dotaz VYBERTE veřejnou.lizmap_get_data(json) s parametry zapsanými v JSON a provede jej ve vrstvě PostgreSQL databáze. (Viz příklad níže)

  • Tento dotaz vrátí GeoJSON, který se poté zobrazí na mapě.

  • Pokud byly nakonfigurovány některé callbacks, spustí se (selection, zoom, redraw).

  • Je emitována událost Lizmap actionResultReceived s vrácenými daty a vlastnostmi akce. To umožňuje uživatelsky definovaným Javascriptovým skriptům používat výsledky akce.

Dotaz vytvořený v PostgreSQL je sestaven webovým klientem Lizmap a používá funkci PostgreSQL lizmap_get_data(json), která musí být předem vytvořena v databázi tabulek PostgreSQL. Tato funkce také používá obecnější funkci query_to_geojson(text), která transformuje libovolný řetězec dotazu PostgreSQL na výstup GeoJSON.

Níže je uveden příklad dotazu, který interně provede Lizmap Web Client v databázi PostgreSQL,

  • pro výše uvedenou ukázkovou konfiguraci,

  • když uživatel klikne na tlačítko akce buffer_150,

  • pro prvek s id 2592251664 vrstvy Požární hydranty

  • odpovídající tabulce PostgreSQL fire_hydrant_actions.emergency_fire_hydrant:

SELECT public.lizmap_get_data('{
    "lizmap_repository": "features",
    "lizmap_project": "fire_hydrant_actions",
    "action_name": "buffer_150",
    "action_scop": "feature",
    "layer_name": "Fire hydrant",
    "layer_schema": "fire_hydrant_actions",
    "layer_table": "emergency_fire_hydrant",
    "feature_id": 2592251664,
    "map_center": "POINT(3.4345918 43.63399141565576)",
    "map_extent": "POLYGON((3.429635077741169 43.63175113378633,3.439548522258832 43.63175113378633,3.439548522258832 43.63623161401291,3.429635077741169 43.63623161401291,3.429635077741169 43.63175113378633))",
    "wkt": "",
    "buffer_size":150,
    "other_param": "yes"
}') AS data;

Vidíte, že Lizmap vytvoří parametr JSON se všemi potřebnými informacemi a spustí funkci PostgreSQL lizmap_get_data(text).

Aktuální rozsah mapy a střed mapy jsou rovněž odeslány jako parametry ve formátu WKT (projekce EPSG:4326) a lze je použít ve funkci PostgreSQL.

Povinné funkce PostgreSQL

Musíte vytvořit tyto funkce PostgreSQL:

  • query_to_geojson(text), který vrací platný text GeoJSON z libovolného dotazu SELECT

  • lizmap_get_data(text), což je „řídicí věž“ akcí Lizmap: vytvoří specifický dotaz pro každou akci na základě parametrů a poté spustí dotaz a vrátí GeoJSON

Následující kód SQL je příklad, který vám pomůže vytvořit potřebné funkce. Je zřejmé, že je musíte přizpůsobit svým potřebám.

-- 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 public.lizmap_get_data(parameters json)
RETURNS json AS
$$
DECLARE
    feature_id varchar;
    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')::varchar;
    layer_name:= parameters->>'layer_name';
    layer_schema:= parameters->>'layer_schema';
    layer_table:= parameters->>'layer_table';

    -- Action buffer_150
    -- Performs a buffer on the geometry
    IF action_name = 'buffer_150' THEN
        datasource:= format('
            SELECT %1$s AS id,
            ''Buildings within 150m of the fire hydrant have been selected'' AS message,
            ST_Buffer(geom, 150) AS geom
            FROM "%2$s"."%3$s"
            WHERE osm_id = ''%1$s''
        ',
        feature_id,
        layer_schema,
        layer_table
        );
    ELSEIF action_name = 'closest_fire_station' THEN
        -- Draw a line to the closest fire station
        datasource:= format('
            WITH tmp_hydrant AS (
                SELECT geom FROM fire_hydrant_actions.emergency_fire_hydrant WHERE osm_id = ''%1$s''
            )
            SELECT
                id, name, ST_Distance(hydrant.geom, stations.geom),
                ''The closest is :  '' || stations.name || '', '' || ST_Distance(hydrant.geom, stations.geom)::integer || ''m, flying air distance'' AS message,
                ST_MakeLine(stations.geom, hydrant.geom) AS geom,
                stations.id AS station_id
            FROM
                fire_hydrant_actions.stations stations,
                tmp_hydrant hydrant
            ORDER BY ST_Distance(hydrant.geom, stations.geom)
            LIMIT 1
        ',
        feature_id
        );
    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 public.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.';
  • Jako příklad je zde uvedena funkce lizmap_get_data(json). Jelikož se jedná o klíčový vstupní bod, musíte si ji přizpůsobit svým potřebám. Jejím cílem je vytvořit dotaz pro každý název akce, dynamicky vytvořený pro zadané parametry, a vrátit reprezentaci dat výsledku dotazu ve formátu GeoJSON. Měli byste mít vrácenou pouze jednu funkci: v případě potřeby použijte agregaci. Ve výše uvedeném příkladu použijeme metodu formát pro nastavení textu dotazu a funkci query_to_geojson pro vrácení formátu GeoJSON pro tento dotaz.

  • Můžete použít všechny dané parametry (název akce, schéma zdrojových dat a název tabulky, id funkce, název vrstvy QGIS) k vytvoření příslušného dotazu pro vaše akce(akcí) pomocí PostgreSQL IF THEN ELSIF ELSE klauzule. Podívejte se na obsah proměnné parametry v příkladu výše, který obsahuje některé vlastnosti konfiguračního souboru JSON a některé vlastnosti vrstvy QGIS:

    • Lizmap úložiště a projektové klíče mapy: lizmap_repository & lizmap_project

    • akce název action_name, například buffer_150. Měli byste použít jednoduché slovo pouze s písmeny, číslicemi a _,

    • akce rozsah action_scope, například feature,

    • QGIS název vrstvy (jako v legendě QGIS): layer_name, například Fire hydrant, pouze pro akce feature,

    • tabulka PostgreSQL schéma layer_schema a název tabulky layer_table pro vrstvu, pouze pro akce s rozsahem feature a layer

    • objekt feature id feature_id, který odpovídá hodnotě pole primárního klíče pro vyskakovací objekt, pouze pro akce feature,

    • ostatní vlastnosti uvedené v konfiguračním souboru JSON ve vlastnosti options, jako je buffer_size, což je v příkladu 150

    • střed mapy map_center a rozsah mapy map_extent

  • Příkaz IF ELSE slouží k provedení jiného dotazu, sestaveného v proměnné datasource, a to kontrolou názvu akce.

  • Pokud vrácená data obsahují pole zpráva, jak je uvedeno v příkladu výše, text obsažený v tomto poli se zobrazí v mapě v bublině se zprávou.

  • Geometrie vrácená funkcí se zobrazí na mapě.

  • Svou funkci můžete použít k úpravě některých dat ve vaší databázi, než vrátíte GeoJSON. Chcete-li tak učinit, musíte nahradit vlastnost IMMUTABLE par VOLATILE. Prosím POUŽÍVEJTE JI OPATRNĚ!

Akce a uživatelsky definované skripty JavaScript

Vzhledem k tomu, že webový klient Lizmap spouští událost actionResultReceived, kdykoli uživatel klikne na tlačítko akce, a data jsou vrácena (ve stejné době, kdy je geometrie výsledku nakreslena na mapě), můžete použít vlastní kód JavaScript pro přidání nějaké logiky po zobrazení výsledku.

Například zde pouze zapíšeme do konzoly prohlížeče přijatý obsah:

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);
    }
});

Tyto údaje můžete libovolně použít v kódu JS.

Akce lze také spouštět z externích skriptů JavaScript: můžete použít akce veřejné metody ke spuštění akce nebo resetování aktivní akce:

// Run an action
lizMap.mainLizmap.action.runLizmapAction(actionName, scope = 'feature', layerId = null, featureId = null, wkt = null);
// Reset the action
lizMap.mainLizmap.action.resetLizmapAction()

Geometrii WKT v EPSG:4326 lze také odeslat jako další parametr. To je možné pouze při spuštění akce pomocí JavaScriptu. To umožňuje odeslat geometrii, kterou použije akční funkce PostgreSQL lizmap_get_data jako vlastnost proměnné SQL parameters. (například pro získání dat z jiné tabulky s geometriemi protínajícími tuto předanou geometrii WKT)