1

I use L.control for select layers. I want posted one point in different layer, like here http://www.gistechsolutions.com/leaflet/DEMO/sports/index2.html But here, different point from different jsondata

There is one large json list of data, where ManyToMany ctg:

loadAsync(map_objs, function (text) {    
    const data = JSON.parse(text);
    // get ..., {"type": "Feature", "properties": {"ctg": [2, 1], "model": "map.hist"}, "id": 86, "geometry": {"type": "MultiPolygon", "coordinates": []}}, ...
   L.geoJSON(data, {
        onEachFeature: onEachFeature
    }).addTo(map);
});

Code before has "ctg": [2, 1], i.e. one marker has several categories

Categories data structure: ...{"id": 2, "tt": "ctg2", "color": "#ff00dd", "icon": "architecture", "sort": 10, "count_in": 184}...

Get "ctg data" and create dict for L.Control:

const ctgs = JSON.parse(loadSync(map_ctg));
const ctgDictIdLayer = ctgs.reduce((a, x) => ({...a, [x.id]: L.layerGroup()}), {});

Create L.control

let ctgControl = L.control({
        position: 'topleft'
    });
    ctgControl.onAdd = function (map) {
        this._div = L.DomUtil.create('div', 'ctgControl');
        let ctgControlItems = "";
        Object.entries(ctgDictIdLayer).forEach(([k, v]) => {
            ctgControlItems += '<button type="button" id="' + id_ + '">" + tt + '</button>';
        })
        this._div.innerHTML = ctgControlItems;
        return this._div;
    }

Event L.control

Object.entries(ctgDictIdLayer).forEach(([k, v]) => {
    let id_ = "id_" + k;
    let this_a = document.getElementById(id_);
    this_a.classList.add('selected');
    this_a.addEventListener('click', function () {
        if (map.hasLayer(v)) {map.removeLayer(v)} else {map.addLayer(v)}
    })
});

Each point belongs to several categories.

function onEachFeature(feature, layer) {
    feature.properties.ctg.forEach((el) => {
        layer.addTo(ctgDictIdLayer[el]).addTo(map);
        ctgDictIdLayer[el].addLayer(layer);
        ctgDictIdLayer[el].addTo(map);
    })
    ...
}
4
  • If I understand correctly, you have one GeoJSON source, where one feature belongs to several categories, and you have group layers, one for each category. You then want each feature to be included tino those group layers that correspond to it's categories. It's not clear from your code above how onEachFeature function is used and why in ctgDictIdLayer object each category has the same layer i assigned. Please edit your question and add more code that will explain this.
    – TomazicM
    Dec 24 '21 at 10:23
  • @TomazicM expanded my question
    – kisean
    Dec 24 '21 at 14:10
  • Any reason why you are creating your own layer control and not using Leaflet native one? And what is content of map_ctg?
    – TomazicM
    Dec 24 '21 at 17:38
  • @TomazicM I use custom layer control for custom styles. map_ctg = ...{"id": 2, "tt": "ctg2", "color": "#ff00dd", "icon": "architecture", "sort": 10, "count_in": 184}... with 6 ctg
    – kisean
    Dec 25 '21 at 6:18
0

As you have found out, if GeoJSON feature layer is member of several group layers and one of those group layers is removed from the map, that feature is completely removed from map, even though it is member of other group layers that are shown on the map.

To solve this, new feature layer has be created from feature each time that layer is added to a different group. Code could then look something like this (assuming that initially all group layer have to be shown):

var ctgsLayers = [];
ctgs.forEach(function(ctg) {
  ctgsLayers[ctg.id] = L.layerGroup();
});

function onEachFeature(feature, layer) {
  feature.properties.ctg.forEach(function(ctgId) {
    ctgsLayers[ctgId].addLayer(L.geoJSON(feature));
    if (!map.hasLayer(ctgsLayers[ctgId])) map.addLayer(ctgsLayers[ctgId]);
  });
}

loadAsync(map_objs, function (text) {    
  const data = JSON.parse(text);
  L.geoJSON(data, {
    onEachFeature: onEachFeature
  });
});

let ctgControl = L.control({
  position: 'topleft'
});
ctgControl.onAdd = function (map) {
  this._div = L.DomUtil.create('div', 'ctgControl');
  let ctgControlItems = "";
  ctgs.forEach(function(ctg) {
    ctgControlItems += '<button type="button" id="id_' + ctg.id + '">' + ctg.tt + '</button>';
  });
  this._div.innerHTML = ctgControlItems;
  return this._div;
}
ctgControl.addTo(map);

ctgs.forEach(function(ctg) {
  let id_ = "id_" + ctg.id;
  let this_a = document.getElementById(id_);
  this_a.classList.add('selected');
  this_a.addEventListener('click', function (evt) {
    var ctgId = evt.target.id.substring(3);
    if (map.hasLayer(ctgsLayers[ctgId])) {
      map.removeLayer(ctgsLayers[ctgId]);
      evt.target.classList.remove('selected');
      }
    else {
      map.addLayer(ctgsLayers[ctgId]);
      evt.target.classList.add('selected');
    }
  })
});

Drawback of this approach is that feature that appears in multiple categories is shown on map that many times when corresponding group layers are displayed. If we want to have such feature displayed only once, this could be done with the help of GeoJSON layer option filter, where only those features are allowed to be displayed that appear in selected categories.

In this case there are no group layers, but GeoJSON layer data has to be reloaded each time group selection changes. Code could look something like this:

var ctgsSelected = [];

ctgs.forEach(function(ctg) {
  ctgsSelected[ctg.id] = false;
});

var dataLayer;
loadAsync(map_objs, function (text) {    
  const data = JSON.parse(text);
  dataLayer = L.geoJSON(data, {
    filter: function(feature) {
      var ctgs = feature.properties.ctg;
      for (var i = 0; i < ctgs.length; i++) {
        if (ctgsSelected[ctgs[i]]) {
          return(true);
        }
      }
      return(false);
    }
  }).addTo(map);
});

let ctgControl = L.control({
  position: 'topleft'
});
ctgControl.onAdd = function (map) {
  this._div = L.DomUtil.create('div', 'ctgControl');
  let ctgControlItems = "";
  ctgs.forEach(function(ctg) {
    ctgControlItems += '<button type="button" id="id_' + ctg.id + '">' + ctg.tt + '</button>';
  });
  this._div.innerHTML = ctgControlItems;
  return this._div;
}
ctgControl.addTo(map);

ctgs.forEach(function(ctg) {
  let id_ = "id_" + ctg.id;
  let this_a = document.getElementById(id_);
  this_a.addEventListener('click', function (evt) {
    var ctgId = evt.target.id.substring(3);
    ctgsSelected[ctgId] = !ctgsSelected[ctgId];
    if (ctgsSelected[ctgId])
      evt.target.classList.add('selected');
    else {
      evt.target.classList.remove('selected');
    }
    dataLayer.clearLayers();
    dataLayer.addData(lakes_data);
  })
});
1
  • Now you are my friend! Thanks a lot
    – kisean
    Dec 27 '21 at 5:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.