Filtres sur les couches

Il est possible d”ajouter des formulaires de filtre sur les couches dans le but de visualiser certaines données uniquement. Alors les utilisateurs du mode cartographique auront accès à un bouton faisant apparaître le formulaire de filtrage, sur la carte « Carte de France » créée par défaut lors de l”installation de l”application un filtre est mis en place à titre d”exemple.

Sur la couche « Commune » un bouton de filtre apparaît

../../../_images/filtres_couches_1.jpg

En cliquant dessus le formulaire suivant est affiché dans une fenêtre modale

../../../_images/filtres_couches_2.jpg

L”utilisateur peut alors filtrer la couche pour afficher uniquement les communes ayant une population comprise dans la fourchette saisie par l”utilisateur, ainsi si on saisit 50000 à « Population suppérieure à » nous obtenons la carte suivante:

Pour ce faire il faudra effectuer plusieurs étapes détaillées ci-dessous.

1 - Écrire le filtre sur la couche Mapserver

Après avoir saisit le filtre, vMap va ajouter aux URL permanentant de récupérer les tuiles les paramètres saisis, dans le module Mapserver il est possible de récupérer et d”utiliser ces valeurs. Bien évidemment cette opération est également fonctionnelle si vous utilisez un autre générateur de flux WMS.

Sur le module Mapserver il faudra se rendre dans la définition de la couche pour y renseigner un filtre, nous allons détailler l”exemple des communes, mais si vous voulez plus de détails vous trouverez toute l”information disponible sur la documentation Mapserver http://mapserver.org/fr/cgi/runsub.html#filters

Dans notre exemple voici ce qui est écrit:

FILTER ([pop90] > '%pop90_sup%' and [pop90] < '%pop90_inf%')
VALIDATION
    'pop90_sup' '^[0-9]*$'
    'default_pop90_sup' '0'
    'pop90_inf' '^[0-9]*$'
    'default_pop90_inf' '100000000'
END

Dans la balise FILTER on écria la condition à respecter, les noms des colonnes devrons être écrites entre crochets ex: [pop90] et les noms des attributs récupérés à travers le l”URL (c”est à dire le formulaire de filtre) devront être écris entre pourcentages ex: %pop90_sup%

La balise VALIDATION est obligatoire et on doit y écrire pour chaque attribut récupéré à travers l”URL (c”est à dire le formulaire de filtre) une expression régulière empéchant les personnes mal intentionnées de faire des injections SQL, dans notre exemple nous avons écrit “^[0-9]*$” ce qui signifie autant de chiffres entre 0 et 9 que souhaité. Pour que la carte affiche des valeurs lorsque les filtres sont vides il faudra définir des valeurs par défaut en écrivant default_[nom de votre attribut], dans notre exemple par défaut la carte filtre les villes entre 0 et 100000000 d”habitants.

On peut également utiliser la balise FILTERITEM pour faire un filtre plus générique:

FILTERITEM "id_com"
FILTER /%id_com%/
VALIDATION
    "id_com" "^[a-zA-Z0-9_|]*$"
    "default_id_com" "^[a-zA-Z0-9_|]*$"
END

2 - Mettre en place un formulaire de filtre

Pour mettre en place le formulaire il faudra aller dans la partie calques et mettre Oui à « Calque filtrable (Mapserver) »

../../../_images/filtres_couches_5.jpg

Après avoir mis à jour le calque, une nouvelle section « Formulaire de filtre » va apparaître dans lequel vous pourrez mettre en place votre filtre.

../../../_images/filtres_couches_6.jpg

Autres exemples d”utilisation des filtres dans vMap

1 - Filtre textuelle non obligatoire et fonctionnement similaire au LIKE en SQL

Dans le studio il faudra définir un champ de type texte ou une liste déroulante avec une source de données adaptée.

Il faudra définir votre filtre pour qu”il utilise une valeur par défaut qui permettra de court-circuiter le filtre. (Dans le cas présent “empty”)

Si vous voulez un fonctionnement plus proche d”un LIKE SQL utilisez ~ à la place de =. (voir exemple ci-dessous)

	FILTER (([type] = '%type%' or '%type%' = 'empty') and ([liketext] ~ '%liketext%' or '%liketext%' = 'empty')
    VALIDATION
        'type' '^empty|value_type_1|value_type_2|value_type_3$'
        'liketext' '^empty|.{1,}$'
        'default_type' 'empty'
        'default_liketext' 'empty'
    END

2 - Filtre sur une colonne de type date/timestamp

Pour filtrer sur un attribut unique de type date/timestamp référez vous à la documentation de MapServer http://mapserver.org/ogc/wms_time.html.

3 - Filtre sur plusieurs colonnes de type date/timestamp

Vue que la spécification d”un champ de type date/timestamp se fait dans la partie METADATA, il est impossible pour MapServer de traiter deux champs de ce type en théorie.

En réalité par un moyen détourné, il est possible de gérer autant de champs date/timestamp que vous voulez.

Pour l”exemple, je vais prendre trois champs en base creation_date (date de création d”un objet, type postgres timestamp with time zone), date_debut_travaux (une date de début de travaux pour un objet métier par exemple, type timestamp with time zone), duree_travaux_jour (la durée des travaux en jours à partie de la date de début, type integer)

a - Adapter votre vue

Pour permettre à MapServer de les traiter comme des entiers il va falloir que la vue retourne des entiers.

Exemple de code permettant de faire cela :

	date_part('epoch'::text, table.creation_date)::integer AS mstmstp_creation_date,
    date_part('epoch'::text, table.date_debut_travaux)::integer AS mstmstp_date_debut_travaux,
    date_part('epoch'::text, table.date_debut_travaux + table.duree_travaux_jour * '1 day'::interval)::integer AS mstmstp_date_fin_travaux

b - Adapter votre formulaire avec le studio

Ajouter les champs dates que vous voulez. Dans le cas présent on va laisser la possibilité de prendre une date au dessus, en dessous, ou d”encadrer la/les date(s). Pour chaque champ date il faut un champ caché. Le champ date va renvoyer une valeur formatée du type “DD/MM/YYYY” ou “YYYY-MM-DD”, mais nous voulons un timestamp. Nous allons voir dans la prochaine partie comment utiliser un champ date pour remplir un champ caché avec un timestamp.

../../../_images/filtres_couches_exemples_1.jpg

c - Permettre au formulaire de calculer les timestamps

Il va falloir ajouter du code javascript spécifique à ce formulaire. Pour ce faire il va falloir changer de mode d”utilisation du studio, pour pouvoir éditer du javascript.

../../../_images/filtres_couches_exemples_2.jpg

Vous allez arriver devant un champ vide.

Voici le code permettant de remplir les champs cachés pour l”exemple :

/* global angular, goog, moment, vitisApp, bootbox */

// goog fonctionne en mode décompilé mais pas en mode compilé

console.info("filtre_mapserver_couche_NOM loaded --> your functions are ready");
/***********************************************************************************
 filtre_mapserver_couche_NOM Javascript
 ***********************************************************************************/

var oFormRequired = {
    "sUrl": "",
    "scope_": {},
    "toDestructor": []
};
/**
 * constructor_form
 * Fonction appelée à l'initialisation du formulaire
 * @param {type} scope
 * @param {type} s_url
 * @returns {undefined}
 */
var constructor_form = function (scope, s_url) {

    oFormRequired.sUrl = s_url;
    oFormRequired.scope_ = scope;

    var formaterStringFrom = "DD/MM/YYYY";
    var formaterStringTo = "X"; // timestamp UNIX
    oFormRequired.toDestructor.push(oFormRequired.scope_.$watch("oFormValues." + oFormRequired.scope_.sFormDefinitionName + ".creation_date_min_viz", function (value) {
            if (typeof(value) !== "undefined" && value !== "") {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["creation_date_min"] = moment(value, formaterStringFrom).format(formaterStringTo);
            } else {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["creation_date_min"] = null;
            }
        })
    );
    oFormRequired.toDestructor.push(oFormRequired.scope_.$watch("oFormValues." + oFormRequired.scope_.sFormDefinitionName + ".creation_date_max_viz", function (value) {
            if (typeof(value) !== "undefined" && value !== "") {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["creation_date_max"] = moment(value, formaterStringFrom).format(formaterStringTo);
            } else {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["creation_date_max"] = null;
            }
        })
    );
    oFormRequired.toDestructor.push(oFormRequired.scope_.$watch("oFormValues." + oFormRequired.scope_.sFormDefinitionName + ".tavaux_date_min_viz", function (value) {
            if (typeof(value) !== "undefined" && value !== "") {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["travaux_date_min"] = moment(value, formaterStringFrom).format(formaterStringTo);
            } else {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["travaux_date_min"] = null;
            }
        })
    );
    oFormRequired.toDestructor.push(oFormRequired.scope_.$watch("oFormValues." + oFormRequired.scope_.sFormDefinitionName + ".tavaux_date_max_viz", function (value) {
            if (typeof(value) !== "undefined" && value !== "") {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["travaux_date_max"] = moment(value, formaterStringFrom).format(formaterStringTo);
            } else {
                oFormRequired.scope_["oFormValues"][oFormRequired.scope_["sFormDefinitionName"]]["travaux_date_max"] = null;
            }
        })
    );

};
/**
 * destructor_form
 * Fonction appelée quand on quitte le formulaire (ne pas modifier)
 * @returns {undefined}
 */
var destructor_form = function () {
    console.log("Destructor");

    for (var i = 0; i < oFormRequired.toDestructor.length; i++) {
        oFormRequired.toDestructor[i] = undefined;
        delete oFormRequired.toDestructor[i];
    }

    //supprimer la balise script du js pour pouvoir le recharger si on revient sur le formulaire plus tard
    angular.element('[src="' + oFormRequired.sUrl + '?version=' + oFormRequired.scope_["oProperties"]["build"] + '"]').remove();

    oFormRequired = undefined;
    delete oFormRequired;

    constructor_form = undefined;
    delete constructor_form;
    destructor_form = undefined;
    delete destructor_form;
};
/**********************************************************************************/

Ce code créé une instance pour surveiller les changements des champs dates ($watch) et utilise une librairie intégrée à Vitis (moment.js) pour parser la date, la transformer en timestamp Unix et stocker le résultat dans le champ caché.

d - Intégration du filtre dans la couche MapServer

	FILTER (([mstmstp_date_debut_travaux] < '%travaux_date_max%' or [mstmstp_date_fin_travaux] < '%travaux_date_max%') and ([mstmstp_date_debut_travaux] < '%travaux_date_min%' or [mstmstp_date_fin_travaux] < '%travaux_date_min%') and ([mstmstp_creation_date] < '%travaux_date_max%') and ([mstmstp_creation_date] > '%creation_date_min%'))

    VALIDATION
        'creation_date_max' '^[0-9]{1,}$'
        'creation_date_min' '^[0-9]{1,}$'
        'travaux_date_max' '^[0-9]{1,}$'
        'travaux_date_min' '^[0-9]{1,}$'
        'default_creation_date_max' '10000000000000000000000000'
        'default_creation_date_min' '0'
        'default_travaux_date_max' '10000000000000000000000000'
        'default_travaux_date_min' '0'
    END