Leaflet et filtre de données

Le problème exposé dans ce sujet a été résolu.

Hello tout le monde,

J’essaye de bricoler une petite carte avec Leaflet. Jusqu’à présent, j’arrive très bien à charger ma carte et mes données geojson. Maintenant, ma difficulté est de réussir à filtrer les données. J’aimerais avoir un contrôle qui me permette de remplir plusieurs filtres (par menu déroulant, case à cocher, et date).

Malheureusement, ce n’est pas du tout une fonction native de Leaflet (j’ai jeté un coup d’œil chez OpenLayers, qui a l’air d’avoir un peu ces fonctionnalités, mais j’ai déjà pas réussi à afficher la pop up…). En cherchant sur le net, je n’ai rien trouvé de vraiment concluant, ni même dans les pluging ou les exemples de Leaflet.

J’ai cru comprendre qu’il me faudrait retirer le layer concerné, et recharger le layer en passant le filtre par dessus. Ça me semble faisable, mais je n’arrive déjà pas jusque là. Je suis en train d’ajouter le contrôle personnalisé, et je galère déjà. Je me suis basé sur l’exemple de la carte chloroplète et le tuto en bêta de shevek pour réaliser le petit bout que j’ai.

Le problème, c’est que je connais vraiment pas grand chose à HTML, et que je suis carrément débutant en JS. Alors manipuler du Dom de partout, ça me perd complètement. Est-ce que vous auriez des indications pour m’aider à aller plus loin (par exemple, vers quoi je devrais me tourner en html pour mettre dans mon control), voire, idéalement, des ressources de quelqu’un qui aurait déjà mis un filtre en place ?

let map = L.map('map').setView([48.13, -1.64], 7);

L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
    attribution: "\n" + "© <a href=https://www.openstreetmap.org/copyright>Contributeurs d’OpenStreetMap</a>",
    maxZoom: 19,
}).addTo(map);

let jsonData = JSON.parse(document.getElementById("json-data").textContent);
function onEachPoint(feature, layer) {
    let popupContent = `<p><strong>${feature.properties.name}</strong><br />${feature.properties.website}<br />${feature.properties.type}<br />Du ${feature.properties.startDate} au ${feature.properties.endDate}<br />${feature.properties.free ? "Gratuit" : "Payant"}</p>`
    layer.bindPopup(popupContent);
    feature.alt = feature.properties.name;
}
let festivalsLayer = L.geoJSON(jsonData,{onEachFeature: onEachPoint}).addTo(map);

map.locate({setView: true, maxZoom: 11});

function filter() {
    // Là je ne sais pas quoi mettre
}

let festivalsFilter = L.control("topright");
festivalsFilter.onAdd = function (map) {
    let div = L.DomUtil.create('div', 'filter');
    div.innerHTML = "Type<br />Gratuit uniquement<br />Dates</p>"
    let button = L.DomUtil.create('button', 'filter-button', div);
    button.innerHTML = "Filtrer";
    // Cet event semble faire buguer l’exécution, c’est quasiment copié du tuto de shevek
    // L.DomEvent.on(button, "click", filter(), this);
    return div;
}
map.addControl(festivalsFilter);
+0 -0

Re-bonjour,

En fait j’ai réussi. Par contre, c’est dommage, je re-parse les données geojson à chaque changement. Je crois avoir vu sur OpenLayers que la solution était un formattage différent. C’est peut-être la même chose sur Leaflet, pour faire ça plus proprement, de rechanger le style.

En tout cas, pour l’instant ça fonctionne. Je suis preneur de tout conseil :) Il me reste le filtrage par date à réaliser. (j’ai masqué la version précédente, du coup, cf l’edit plus bas)

let map = L.map('map').setView([48.13, -1.64], 7);

L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
    attribution: "\n" + "© <a href=https://www.openstreetmap.org/copyright>Contributeurs d’OpenStreetMap</a>",
    maxZoom: 19,
}).addTo(map);

let jsonData = JSON.parse(document.getElementById("json-data").textContent);

function onEachPoint(feature, layer) {
    let popupContent = `<p><strong>${feature.properties.name}</strong><br />${feature.properties.website}<br />${feature.properties.type}<br />Du ${feature.properties.startDate} au ${feature.properties.endDate}<br />${feature.properties.free ? "Gratuit" : "Payant"}</p>`
    layer.bindPopup(popupContent);
    feature.alt = feature.properties.name;
}

let festivalsLayer = L.geoJSON(jsonData, {onEachFeature: onEachPoint}).addTo(map);

map.locate({setView: true, maxZoom: 11});

let festivalsFilter = L.control("topright");
festivalsFilter.onAdd = function (map) {
    let div = L.DomUtil.create('div', 'filter');
    div.innerHTML = `<form name="festival-filter" id="festival-filter" class="form-filter">
                        <fieldset>
                        <legend>Type</legend>
                            <div class="form-filter">
                                <input type="checkbox" name="type" id="type-festival" value="festival" checked>
                                <label for="type-festival">Festival</label>
                            </div>
                            <div>
                                <input type="checkbox" name="type" id="type-marche" value="marche" checked>
                                <label for="type-marche">Marché médiéval</label>
                            </div>
                        </fieldset>
                        <div class="form-filter">
                            <label for="free">Gratuit uniquement :</label>
                            <input type="checkbox" name="free" id="free" value="True">
                        </div>
                        <div class="form-filter">
                            <label for="start-date">Entre le </label>
                            <input type="date" name="start-date" id="start-date">
                            <label for="end-date"> et le </label>
                            <input type="date" name="end-date" id="end-date">
                        </div>
                    </form>`
    let button = L.DomUtil.create('button', 'filter-button', div);
    button.innerHTML = "Filtrer";
    // Cet event semble faire buguer l’exécution, c’est quasiment copié du tuto de shevek
    L.DomEvent.on(button, "click", (div) => filterSubmit(div), this);
    return div;
}

function filterSubmit(div) {
    const form = document.getElementById("festival-filter");
    let formData = new FormData(form);

    map.removeLayer(festivalsLayer);
    festivalsLayer = L.geoJSON(jsonData,
    {
        onEachFeature: onEachPoint,
        filter: function (feature) {
            if (formData.has("free")) return feature.properties.free;
            if (formData.getAll("type").includes(feature.properties.type)) {
                return true;
            }
            return false;
        }
    }).addTo(map);

    return div;
}

map.addControl(festivalsFilter);

Edit : Finalement ça ne m’a pas pris longtemps le filtre par date :
Edit bis : @shevek, penses-tu que ce soit un sujet intéressant à ajouter à ton tutoriel ?

let map = L.map('map').setView([48.13, -1.64], 7);

L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
    attribution: "\n" + "© <a href=https://www.openstreetmap.org/copyright>Contributeurs d’OpenStreetMap</a>",
    maxZoom: 19,
}).addTo(map);

let jsonData = JSON.parse(document.getElementById("json-data").textContent);

function onEachPoint(feature, layer) {
    let popupContent = `<p><strong>${feature.properties.name}</strong><br />${feature.properties.website}<br />${feature.properties.type}<br />Du ${feature.properties.startDate} au ${feature.properties.endDate}<br />${feature.properties.free ? "Gratuit" : "Payant"}</p>`
    layer.bindPopup(popupContent);
    feature.alt = feature.properties.name;
}

let festivalsLayer = L.geoJSON(jsonData, {onEachFeature: onEachPoint}).addTo(map);

map.locate({setView: true, maxZoom: 11});

let festivalsFilter = L.control("topright");
festivalsFilter.onAdd = function (map) {
    let div = L.DomUtil.create('div', 'filter');
    div.innerHTML = `<form name="festival-filter" id="festival-filter" class="form-filter">
                        <fieldset>
                        <legend>Type</legend>
                            <div class="form-filter">
                                <input type="checkbox" name="type" id="type-festival" value="festival" checked>
                                <label for="type-festival">Festival</label>
                            </div>
                            <div>
                                <input type="checkbox" name="type" id="type-marche" value="marche" checked>
                                <label for="type-marche">Marché médiéval</label>
                            </div>
                        </fieldset>
                        <div class="form-filter">
                            <label for="free">Gratuit uniquement :</label>
                            <input type="checkbox" name="free" id="free" value="True">
                        </div>
                        <div class="form-filter">
                            <label for="start-date">Entre le </label>
                            <input type="date" name="start-date" id="start-date">
                            <label for="end-date"> et le </label>
                            <input type="date" name="end-date" id="end-date">
                        </div>
                    </form>`
    let button = L.DomUtil.create('button', 'filter-button', div);
    button.innerHTML = "Filtrer";
    // Cet event semble faire buguer l’exécution, c’est quasiment copié du tuto de shevek
    L.DomEvent.on(button, "click", (div) => filterSubmit(div), this);
    return div;
}

function filterSubmit(div) {
    const form = document.getElementById("festival-filter");
    let formData = new FormData(form);

    map.removeLayer(festivalsLayer);
    festivalsLayer = L.geoJSON(jsonData,
    {
        onEachFeature: onEachPoint,
        filter: function (feature) {
            let startDate = formData.get("start-date");
            let endDate = formData.get("end-date");
            if (formData.has("free") && feature.properties.free)
                return false;
            if (!formData.getAll("type").includes(feature.properties.type))
                return false;
            if (startDate) {
                if (endDate) {
                    if (!((feature.properties.startDate >= startDate && feature.properties.startDate <= endDate) ||
                        (feature.properties.endDate >= startDate && feature.properties.endDate <= endDate)))
                        return false;
                } else if (feature.properties.endDate < startDate)
                    return false;
            } else if (endDate) {
                if (feature.properties.endDate > endDate)
                    return false;
            }
            return true;
        }
    }).addTo(map);

    return div;
}

map.addControl(festivalsFilter);
+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte