Выберите несколько фильтров, отображайте только те маркеры, которые имеют общие функции

У меня есть листовка-карта, уже работает. В настоящее время у меня есть несколько фильтров флажков (всего почти 40 на двух картах): пользователь может выбрать несколько маркеров одновременно. Фильтры представляют «специальности», и когда я нажимаю на фильтр, он показывает мне больницы, в которых есть эта специальность. Таким образом, на карте у меня могут быть кластеры, которые показывают больницу с несколькими специальностями, и отдельные маркеры, которые показывают разные больницы с одной специализацией.

  • Если пользователь нажимает на фильтр A, он отображает, например, 5 маркеров с функцией A.
  • Если пользователь нажимает на фильтр B, он отображает 5 маркеров с функцией B.
  • И когда пользователь нажимает на фильтры A и B вместе, он отображает A + B, то есть 10 маркеров, с отдельными маркерами и сгруппированными маркерами.

Что я пытаюсь сделать:

Каждый отдельный флажок должен отображать соответствующие маркеры, НО, как только я устанавливаю несколько флажков одновременно, я хочу отображать только маркеры (больница), где я могу найти соответствующие функции. Например, если я нажму на фильтр A и B вместе, я хотел бы видеть только больницы, в которых есть специальности A и B, и скрыть маркер только с одной функцией.

Поскольку я уже закодировал все свои функции, я не хочу перестраивать новые функции GeoJSON. Я пытался, но он дублирует маркеры.

Я ищу такой цикл (конечно, это не работает):

$('#filters input').on('click', function(){// if click on checkbox
    var ID = $(this).attr("id");//I can get the checkbox IDs here, which correspond to the feature name

if ($('#filters input:checked').length >=2 ){ // as soon as there are more than one checkbox checked    
    var checkboxes = $("#filters input:checked");
        for (var i = 0; i < checkboxes.length; i++) {//for all checked checkboxes
            if(feature[i].geometry.coordinates == feature[i].geometry.coordinates){//if the markers have same coordinates
                return layer.feature.properties.Examen; // show me those markers
            }else{
                return false;// hide other markers
            }        
     }
}
})

Я думаю, что у меня уже есть вся необходимая информация со следующим кодом, но я не знаю, как объединить это, чтобы получить то, что мне нужно...

Вот пример одной функции:

{
                "type": "Feature",
                "properties": {
                    "Examen": "iduronate",
                    "Prelevement": "DBS ou Buvard 1 tâche de sang (+ 1 contrôle) ou Sang total EDTA 5 ml (Leucocytes)",
                    "Envoi": "Envoi rapide à température ambiante",
                    "Laboratoire": "Secteur des Enzymopathies UM-BEP-DBTP-IBP",
                    "Chu": "CHU de Grenoble-Alpes",
                    "Adresse": "CS 10217",
                    "Cp": "38043 Grenoble Cedex 9",
                    "Professeur": "Dr Vergnaud Sabrina",
                    "Tel": "Tel : 04 76 76 54 83 ou 04 76 76 59 05",
                    "Fax": "Fax : 04 76 76 56 08",
                    "Mail": "[email protected]",

                    "Url": "http://biologie.chu-grenoble.fr/",
                    "Mode": "Buvard"
                },
                "geometry": {

                    "type": "Point",
                    "coordinates": [5.7423258, 45.2015191]
                }
            }

Чтобы добавить маркеры «iduronate»: я устанавливаю флажок с идентификатором «iduronate». Если я сниму флажок, он скроет мои маркеры.

$("#iduronate").click(function() {
        if (this.checked) {

            iduronate.addTo(clusterade);
        } else {
            clusterade.removeLayer(iduronate);

        }
    });

Добавление маркера на карту, отображение пользовательского значка и отображение данных

var iduronate = L.geoJson(examenData, {
        filter: function(feature, layer) {
            return feature.properties.Examen == "iduronate";
        },
        onEachFeature: onEachFeature2,
        pointToLayer: function(feature, latlng) {

            return L.marker(latlng, {
                icon: iduronateIcon
            })
        }
    })

И, наконец, пример моего фильтра

<div>
   <input type="checkbox" class="gaucher" id="iduronate" name="EnzymeDef_mps1[]">
   <label for="iduronate">Iduronate sulfatase </label>
<div>

Извините, я пытался опубликовать свой код здесь, но он слишком длинный для «тела»... Поэтому я сделал codepen, чтобы показать его в реальном времени. Спасибо за вашу помощь!!


person blogob    schedule 01.05.2020    source источник
comment
Если фильтр A, например. feature.properties.Examen == "nacetylglucosaminidase" и фильтр B, например. feature.properties.Examen == "heparane_sulfamidase", и любой заданный точечный объект имеет одно и только одно значение для свойства Examen (поскольку это свойство является строкой, а не массивом и т. д.), то ни один объект не может удовлетворять обоим условиям одновременно, и поэтому ваш вопрос не имеет смысла.   -  person IvanSanchez    schedule 01.05.2020
comment
Если я правильно понимаю, вы хотели бы скрыть маркеры, которые не образуют кластер с маркерами другого значения свойства? Это звучит немного странно, так как кластеры зависят от уровня масштабирования, поэтому вы можете скрыть все при большом увеличении, но ничего не скрыть при малом увеличении...   -  person ghybs    schedule 01.05.2020
comment
Спасибо за ваши Коментарии. Вы можете видеть на codepen (когда он открыт в полноэкранном режиме), каждый фильтр группирует несколько маркеров. Они могут иметь разные функции или одинаковые функции. Маркеры — это больницы, поэтому в одних и тех же координатах одна больница может иметь одновременно много признаков A и много признаков B. Я понимаю, что когда у больницы есть несколько функций, они группируются. Поэтому мне просто нужно показывать кластеры при нажатии на многие фильтры и скрывать отдельные фильтры. Я думаю, что сама карта говорит больше, чем мои объяснения. Спасибо. На самом деле, да, это имеет смысл!   -  person blogob    schedule 01.05.2020
comment
Я понимаю, что это неясно, потому что я сокращаю свой js-файл. Например, feature.properties.Examen == nacetylglucosaminidase имеет много вхождений с различной информацией, и так далее для всех моих функций. Фактически свойства нацетилглюкозаминидазы действуют как категория. Надеюсь, это более ясно .. извините, если это не так :)   -  person blogob    schedule 01.05.2020
comment
Если два (или более) объекта имеют точно одинаковые координаты, но разные свойства (как кажется), мой подход будет состоять в том, чтобы заранее объединить такие объекты (перебрать их, индексировать по координатам, добавить свойства массива). Затем вы можете фильтровать функции вместо кластерных визуализаций функций (как мне кажется) более разумным способом.   -  person IvanSanchez    schedule 01.05.2020
comment
Большое спасибо Иван, я думаю, учитывая мой низкий уровень в js, что я выберу решение jquery/css и выложу здесь свое решение, даже если будет ясно, что ваши рекомендации кажутся мне правильными!   -  person blogob    schedule 01.05.2020
comment
Большое спасибо за Ваш ответ. @Ivan, я пытался в своем файле GeoJSON создать новые функции, но он дублирует мои маркеры. И поскольку у меня более 40 фильтров и около 100 функций, комбинация огромна. Так что я действительно ищу цикл или что-то более динамичное, и я надеюсь, что это возможно. Я отредактировал свой вопрос, чтобы сделать его более ясным.. Я действительно застрял.. Если кто-то столкнулся с этой проблемой... Я принимаю любое решение!   -  person blogob    schedule 04.05.2020


Ответы (1)


Предположим, что это упрощенная версия вашего набора данных, например. GeoJSON FeatureCollection, содержащий точки для клиник/отделений в больницах, где две (или более) клиники, относящиеся к одной и той же больнице, занимают одну и ту же позицию:

let clinics = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": { "clinic": "cardio", "phone": "11 22 33 44 55" },
      "geometry": {
         "type": "Point", "coordinates": [5.74, 45.20]
      }
    },
    {
      "type": "Feature",
      "properties": { "clinic": "trauma", "phone": "66 77 88 99 00" },
      "geometry": {
         "type": "Point", "coordinates": [5.74, 45.20]
      }
    },
    {
      "type": "Feature",
      "properties": { "clinic": "neuro", "phone": "77 66 55 44 33" },
      "geometry": {
         "type": "Point", "coordinates": [6.12, 43.88]
      }
    } 
  ]
};

Мой подход состоял бы в том, чтобы вручную пройтись по указанному GeoJSON FeatureCollection, индексируя точечные объекты на основе их координат и объединяя их свойства в массив, например:

let clinicsByPosition = {};

for (let clinic of clinics.features) {
  // Let the index be the string representation of the coordinates array;
  // this is probably inefficient, but will suffice.
  // Note there are no sanity checks performed here (e.g. checking geometry type)
  let idx = clinic.geometry.coordinates.toString();

  // Does clinicsByPosition already have something in that index?
  if (clinicsByPosition[idx] === undefined) {
    // Create a new feature in that index
    // Initialize the properties of that new feature to an array with
    // one element, the properties of the clinic, and same geometry
    // than the clinic.
    clinicsByPosition[idx] = { 
      type: "Feature", 
      geometry: clinic.geometry, 
      properties: [clinic.properties]
    }
  } else {
    // Add the properties of the clinic to the already existing indexed
    // feature
    clinicsByPosition[idx].properties.push(clinic.properties);
  }
}

// Second pass: convert the "clinicsByPosition" data structure 
// into a GeoJSON FeatureCollection
let hospitals = {
  type:'FeatureCollection',
  features: []
}

for (let indexedClinicSet of Object.values(clinicsByPosition)) {
  hospitals.features.push(indexedClinicSet)
}

После этого преобразования совокупный набор данных будет выглядеть так:

let hospitals = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [5.74, 45.2] },
      properties: [
        { clinic: "cardio", phone: "11 22 33 44 55" },
        { clinic: "trauma", phone: "66 77 88 99 00" },
      ],
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [6.12, 43.88] },
      properties: [{ clinic: "neuro", phone: "77 66 55 44 33" }],
    },
  ],
};

Давайте поместим это в Leaflet с несколькими информативными всплывающими окнами:

var hospitals = L.geoJson(hospitalsData,{
    onEachFeature: function(feat, marker) {
        // For this example, let the contents of the popup of a hospital marker
        // to be a concatenation of the data for all clinics in that hospital
        marker.bindPopup(feat.properties.map(function(clinicData){
            return clinicData.clinic + " - tlf " + clinicData.phone
        }).join("<br>"))
    }
}).addTo(map);

рабочий пример с этим выглядит не так уж плохо:

всплывающее окно агрегированной точки данных

Теперь должно быть ясно, что существует только один объект для каждого набора клиник с одними и теми же координатами.

Фильтрация теперь может быть сделана (ИМХО) разумным способом. Прокрутите маркеры больниц в экземпляре L.GeoJSON и просмотрите свойства каждого связанного объекта, чтобы увидеть, выполняется ли условие видимости для любого его компонента, например:

function recalculateHospitalVisibility(){
    var cardioVisible = document.getElementById('cardio').checked;
    var traumaVisible = document.getElementById('trauma').checked;
    var neuroVisible = document.getElementById('neuro').checked;

    hospitals.eachLayer(function (marker) {
        // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
        if (
            marker.feature.properties.some(function (clinic) {
                return (
                    (clinic.clinic === "cardio" && cardioVisible) ||
                    (clinic.clinic === "trauma" && traumaVisible) ||
                    (clinic.clinic === "neuro" && neuroVisible)
                );
            })
        ) {
            marker.addTo(map);
        } else {
            marker.remove();
        }
    });
}

Рабочий пример здесь. Даже если код может выглядеть немного запутанным, данные примера упрощены (так что мой немедицинский мозг может работать с ними лучше), использование итераторов и структур данных немного тяжелое, и возможны оптимизации, это должно вам помочь. на ходу.

person IvanSanchez    schedule 04.05.2020
comment
Большое спасибо Иван. Уровень для меня настолько высок, что мне нужно несколько раз прочитать код, чтобы понять!! Но на самом деле спасибо, что нашли время, я попытаюсь адаптировать его к своей карте и посмотреть, работает ли он ... Я уверен, что ваш пример может быть полезен для многих других !! еще раз спасибо - person blogob; 05.05.2020
comment
@blogob: не забудьте проголосовать за ответ, если вы считаете его полезным, и принять его, если он помог вам решить вашу проблему. - person ghybs; 05.05.2020
comment
@IvanSanchez, в приведенном примере мне, к сожалению, не удается использовать потрясающий плагин маркеров (показывать разные маркеры в зависимости от значения функции), а также мне не удается отображать только маркеры с одинаковыми координатами при нажатии на несколько фильтров .. Код явно стал чище, и я многому научился на нем, но я застрял на том же месте из-за отсутствия навыков! Поэтому я позволю себе задать аналогичный вопрос еще раз, чтобы посмотреть, смогу ли я найти в своем коде простой цикл для достижения своих целей. - person blogob; 05.05.2020
comment
Это никоим образом не ставит под сомнение полезность вашего примера, как раз наоборот. Я буду продолжать работать над этим, но параллельно я хотел бы получить результат от своего кода, чтобы лучше понять, как он работает. - person blogob; 05.05.2020