[GIS] GeoJSON search box for multiple layers

geojsonjavascriptleaflet

I am wondering how to make the GeoJSON search more efficient. I would like to get the search result from more than 1 layer in GeoJSON.

My code is (I attached one of two GeoJSON examples):

var test = [{
"type": "Feature",
             "properties": {
             "Title": "Waterbeach Church",
                 "Head": "Chapel Street",
                 "Description": "Parish community"
        },
            "geometry": {
            "type": "Point",
            "coordinates": [
            0.192763,
            52.263692
                ]
                  }
                    }];

var geojsonMarkerOptions2 = {
radius: 5,
fillColor: "#ffc34d",
color: "#1a1100",
weight: 1,
opacity: 1,
fillOpacity: 0.4
};

var church = L.geoJSON(test, {
pointToLayer: function (feature, latlng) {
    return L.circleMarker(latlng, geojsonMarkerOptions2);
},
    onEachFeature: function (feature, layer) {
        layer.bindPopup('<h1><u><font color="red">'+feature.properties.Title+'</h1></u></font><h2>Address: '+feature.properties.Head+'</h2><p>'+feature.properties.Description+'</p><a>'+feature.properties.URL+'</a>');
}

    }).addTo(map);

Afterwards, I am using Leaflet GeoJSON control search, where I want to search between both multiple geoJSON layers (already solved using this link:

L.Control.Search: Using looping for searching data on Leaflet

and for their property features. I am interested in search for more than 1 properties.

When I place another value in "propertyName", then query is redirected to this 2nd value instead of 1st or both.

L.control.search({
layer: L.layerGroup ([sitis, church]),
initial: false,
propertyName: 'Title', 'Head' // Specify which property is searched into.
  })
  .addTo(map);

Can I have something like:
propertyName: L.GeoJSON.PropertyGroup (['Title', 'Head', 'Description]),

Or is there another reasonable solution for this issue?

Best Answer

Answer is based on Leaflet.Control.Search plugin example Search markers with Fuzzy Search, which uses Fuse.js library for searching objects by keywords in properties.

The key steps to make possible search on two properties of two GeoJSON layers are:

  1. At the time of creating GeoJSON layer, define new feature property myKey, which combines both properties we want to search: Title and Head. This property will be used for search and for displaying search suggestions.
  2. Create fuse search object on the basis of concatenated GeoJSON objects. Object are concatenated so that there is only one sorted search list. There are various search options possible. Below example searches beginning of words, with at leat two characters required to start search.
  3. Create searchControl for searching, with filterData option where search is done with the help of fuse search.
var sitis = L.geoJSON(test1, {
  pointToLayer: function (feature, latlng) {
    feature.properties.myKey = feature.properties.Title + ', ' + feature.properties.Head
    return L.circleMarker(latlng, geojsonMarkerOptions2);
  },
  onEachFeature: function (feature, layer) {
    layer.bindPopup('<h1><u><font color="red">'+feature.properties.Title+'</h1></u></font><h2>Address: '+feature.properties.Head+'</h2><p>'+feature.properties.Description+'</p><a>'+feature.properties.URL+'</a>');
  }
}).addTo(map);

var church = L.geoJSON(test2, {
  pointToLayer: function (feature, latlng) {
    feature.properties.myKey = feature.properties.Title + ', ' + feature.properties.Head
    return L.circleMarker(latlng, geojsonMarkerOptions2);
  },
  onEachFeature: function (feature, layer) {
    layer.bindPopup('<h1><u><font color="red">'+feature.properties.Title+'</h1></u></font><h2>Address: '+feature.properties.Head+'</h2><p>'+feature.properties.Description+'</p><a>'+feature.properties.URL+'</a>');
  }
}).addTo(map);

var fuseOptions = {
  shouldSort: true,
  tokenize: true,
  threshold: 0,
  location: 0,
  distance: 100,
  maxPatternLength: 32,
  minMatchCharLength: 2,
  keys: ['properties.myKey']
};

var fuse = new Fuse(test1.features.concat(test2.features), fuseOptions);

var allLayers = L.layerGroup([sitis, church]);

var searchControl = new L.Control.Search({
  layer: allLayers,
  propertyName: 'myKey',
  marker: false,
  filterData: function(text, records) {
    var ret = {}, key;
    var jsons = fuse.search(text);
    for(var i in jsons) {
      key = jsons[i].properties.myKey;
      ret[key]= records[key];
    }
    return ret;
  }
});