Skip to content
Snippets Groups Projects
filtres_couches.md 11.6 KiB
Newer Older
# 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:

<img src="../../images/filtres_couches_3.jpg" width="45%" /> <img src="../../images/filtres_couches_4.jpg" width="45%" />

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

Armand Bahi's avatar
Armand Bahi committed
## 1 - Filtre textuelle non obligatoire et fonctionnement similaire au LIKE en SQL
Armand Bahi's avatar
Armand Bahi committed
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.
Anthony Borghi's avatar
Anthony Borghi committed
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'**)
Armand Bahi's avatar
Armand Bahi committed
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

Armand Bahi's avatar
Armand Bahi committed
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.
Anthony Borghi's avatar
Anthony Borghi committed
En réalité par un moyen détourné, il est possible de gérer autant de champs date/timestamp que vous voulez.
Anthony Borghi's avatar
Anthony Borghi committed
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

Anthony Borghi's avatar
Anthony Borghi committed
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 :

```sql
	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).
Armand Bahi's avatar
Armand Bahi committed
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)

Anthony Borghi's avatar
Anthony Borghi committed
### 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.

Anthony Borghi's avatar
Anthony Borghi committed
Voici le code permettant de remplir les champs cachés pour l'exemple :

```javascript
/* 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;
};
/**********************************************************************************/
```

Anthony Borghi's avatar
Anthony Borghi committed
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

Armand Bahi's avatar
Armand Bahi committed
```