2

I have created a map using Leaflet and QGIS2Web. The search engine works fine, however it looks through only one of column of the attribute table. My goal is to have it search through four different columns (Country, division, subdivision, language) rather than just one (language).

For that, I want to create a custom data array from layer features when loading the layer, which will contain four array items for each feature. The new array will then be used as the source data (using sourceData option) for the search engine.

The only issue remaining is that if the user searches for a language that is spoken at various locations in Africa, how can I have the map show all the locations that speak the language searched for?

map.addControl(new L.Control.Search({
        layer: layer_Eth_Region_2013_Project_Merg_1,
        initial: false,
        hideMarkerOnCollapse: true,
        propertyName: 'q2wHide_lang2'}));
    document.getElementsByClassName('search-button')[0].className +=
     ' fa fa-binoculars';


var popupContent = '<table>\
                <tr>\
                    <th scope="row">Language</th>\
                    <td>' + (!!feature.properties['q2wHide_lang2'] ? autolinker.link(feature.properties['q2wHide_lang2'].toLocaleString()) : '') + '</td>\
                </tr>\
                <tr>\
                    <th scope="row">State</th>\
                    <td>' + (!!feature.properties['State'] ? autolinker.link(feature.properties['State'].toLocaleString()) : '') + '</td>\
                </tr>\
                <tr>\
                    <th scope="row">Local Gove</th>\
                    <td>' + (!!feature.properties['Local Gove'] ? autolinker.link(feature.properties['Local Gove'].toLocaleString()) : '') + '</td>\
                </tr>\
                <tr>\
                    <th scope="row">Country</th>\
                    <td>' + (!!feature.properties['Country'] ? autolinker.link(feature.properties['Country'].toLocaleString()) : '') + '</td>\
                </tr>\
            </table>';

Code sample that I will be using to create a custom data array:

var data = [
    {"loc":[41.575330,13.102411], "title":"aquamarine"},
    {"loc":[41.575730,13.002411], "title":"black"},
    {"loc":[41.807149,13.162994], "title":"blue"},
    {"loc":[41.507149,13.172994], "title":"chocolate"},
    {"loc":[41.847149,14.132994], "title":"coral"},
    {"loc":[41.219190,13.062145], "title":"cyan"},
    {"loc":[41.344190,13.242145], "title":"darkblue"},  
    {"loc":[41.679190,13.122145], "title":"darkred"},
    {"loc":[41.329190,13.192145], "title":"darkgray"},
    {"loc":[41.379290,13.122545], "title":"dodgerblue"},
    {"loc":[41.409190,13.362145], "title":"gray"},
    {"loc":[41.794008,12.583884], "title":"green"}, 
    {"loc":[41.805008,12.982884], "title":"greenyellow"},
    {"loc":[41.536175,13.273590], "title":"red"},
    {"loc":[41.516175,13.373590], "title":"rosybrown"},
    {"loc":[41.506175,13.173590], "title":"royalblue"},
    {"loc":[41.836175,13.673590], "title":"salmon"},
    {"loc":[41.796175,13.570590], "title":"seagreen"},
    {"loc":[41.436175,13.573590], "title":"seashell"},
    {"loc":[41.336175,13.973590], "title":"silver"},
    {"loc":[41.236175,13.273590], "title":"skyblue"},
    {"loc":[41.546175,13.473590], "title":"yellow"},
    {"loc":[41.239190,13.032145], "title":"white"}
];

var map = new L.Map('map', {zoom: 9, center: new L.latLng(data[0].loc) });

map.addLayer(new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'));    //base layer

function localData(text, callResponse)
{
    //here can use custom criteria or merge data from multiple layers

    callResponse(data);

    return {    //called to stop previous requests on map move
        abort: function() {
            console.log('aborted request:'+ text);
        }
    };
}
12
  • One possibility would be to create custom data array from layer features when loading layer. This custom data would have for each feature four array items, one for each feature, with all the necessary properties. This data would then be used is input to search via sourceData option, see opengeo.tech/maps/leaflet-search/examples/…
    – TomazicM
    Oct 27 '21 at 11:10
  • Other possibility would be to concatenate (comma delimited) thos properties to one new property to be searched. In this case search tip would of course show as this concatenated sttring.
    – TomazicM
    Oct 27 '21 at 11:11
  • @TomazicM I am going to try the data array solution. However, since this is my first time doing it, do you know any source (website) that shows how to do it? Jan 14 at 17:16
  • @TomazicM I found this source which I think is helpful bl.ocks.org/jsanz/cda840e6122616c82a52e8638c212629 Jan 14 at 17:21
  • I think example cited in my first comment would be better and simpler to start with.
    – TomazicM
    Jan 14 at 17:29
3

Answer will be based on sourceData demo example code.

Search control sourceData option is a function that has to prepare an array of objects to be searched (here it's static array data), where each object has loc property with location coordinate and title property with associated search text. There is only one coordinate allowed per search text, so if you want to have several of them, a bit of trickery is needed.

Instead of loc property holding real world coordinates, it can in one of the coordinates just hold an index that points to an array (here it's dataCoords), where each element is an array of coordinates, corresponding to the index (if this sounds too complicated, see code below).

Since returned location coordinate now won't be real coordinate but just pointer to array of coordinates, search control moveToLocation option has to be used to process the result of search, which means creating markers in zooming to locations found.

Standard markers display in this case has to be disabled.

Code could then look something like this:

var dataCoords = [
 [[41.575330,13.102411], [41.575730,13.002411]],
 [[41.807149,13.162994], [41.507149,13.172994], [41.847149,14.132994]],
 [[41.219190,13.062145]],
 [[41.344190,13.242145], [41.679190,13.122145]],
 [[41.329190,13.192145], [41.379290,13.122545], [41.409190,13.362145], [41.794008,12.583884]]
];

var data = [
  {"loc":[0,0], "title":"aquamarine"},
  {"loc":[1,0], "title":"black"},
  {"loc":[2,0], "title":"blue"},
  {"loc":[3,0], "title":"chocolate"},
  {"loc":[4,0], "title":"coral"}
];


var map = new L.Map('map', {zoom: 9, center: new L.latLng([41.575330,13.102411]) });
map.addLayer(new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'));

function localData(text, callResponse)
{
  callResponse(data);
  return {
    abort: function() {}
  };
}

var markerLayer = L.layerGroup().addTo(map);

var searchControl = new L.Control.Search({
  sourceData: localData,
  moveToLocation: function (latlng, title, map) {
    markerLayer.clearLayers();
    locations = dataCoords[latlng.lat];
    locations.forEach(function(locationCoords) {
      L.circleMarker(locationCoords).addTo(markerLayer);
    });
    map.fitBounds(L.latLngBounds(locations).pad(0.1));
  },
  text:'Color...',
  markerLocation: false,
  marker: false
});
map.addControl(searchControl);
4
  • So the two digits held by "loc" property both represent an index? Jan 15 at 18:36
  • 1
    No, only the first one is needed to access coordinates in dataCoords array.
    – TomazicM
    Jan 15 at 19:10
  • Of course you cannot just add the all the code, it's meant just to illustrate the principle. You have to modify existing code created by QGIS2Web by applying the principle.
    – TomazicM
    Jan 16 at 8:06
  • The search box now works perfectly. One last thing: When it goes to the location I searched, it zooms in too much. How can I prevent it? 2 days ago

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.