Ciara Spencer
Ciara Spencer

Reputation: 129

Filtering Leaflet Map based on dropdown selection in D3

I'm trying to filter my map markers layer based on selection of industry in dropdown list of which I've manually referenced in my html here - I'd like to initially retain the pre-loaded marker data and provide an option to filter the markers by selection from the dropdown.

<select class="dropdown" id="selDataset">
              <option value="" selected="selected">Industry</option>
              <option value ="ind1">Accomodation and Food Services</option>
              <option value ="ind2">Educational Services</option>
              <option value ="ind3">Administrative and Support and Waste Management and Remediation Services</option>
              <option value ="ind4">Professional, Scientific, and Technical Services</option>
              <option value ="ind5">Manufacturing</option>
              <option value ="ind6">Construction</option>
              <option value ="ind7">Health Care and Social Assistance</option>
              <option value ="ind8">Wholesale Trade</option>
              <option value ="ind9">Information</option>
     </select>
          

Here is my .js code - My guess is I need to define a function that encloses the first for loop and setting an if else statement if a selection is made to filter lat,lng for that industry, else return all by default. I'm just not sure how to reference the html dropdown selection to filter the markers. I have implemented drop downs via D3.js for table filtering, and have used overlay layers before, but I can't find an implementation of this using leaflet marker layers.

// Creating map object
var myMap = L.map("map", {
  center: [40.73, -74.0059],
  zoom: 11
});

// Adding tile layer to the map
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(myMap);

var data = [{
    lat: 40.75,
    lng: -73.99,
    BusinessName: 'Bank LLC',
    Industry: 'Banking'
  },
  {
    lat: 40.73,
    lng: -73.97,
    BusinessName: 'Some Hotel',
    Industry: 'Hospitality'
  },
  {
    lat: 40.74,
    lng: -74.00,
    BusinessName: 'Other Hotel',
    Industry: 'Hospitality'
  }
];

// Create a new marker cluster group
var markers = L.layerGroup();

// Loop through data
for (var i = 0; i < data.length; i++) {
  // Set the latitude, longitude property to a variable
  var latitude = data[i].lat;
  var longitude = data[i].lng;
  var popText = ("<h3>" + data[i].BusinessName + "</h3><h3>Industry: " + data[i].Industry + "</h3>");
  // var industry = data[i].Industry;
  // var mySelector = $("#Industry").val();
  // Check for location property

  if (latitude) {
    // Add a new marker to the cluster group and bind a pop-up
    markers.addLayer(L.marker([latitude, longitude])
      .bindPopup(popText));
  };
}
// Add our marker cluster layer to the map
myMap.addLayer(markers);
#map {
  height: 400px;
  width: 600px;
}
 <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
   integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
   crossorigin=""/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js"></script>

<div id="map"></div>

Upvotes: 1

Views: 1155

Answers (1)

Ruben Helsloot
Ruben Helsloot

Reputation: 13139

The way D3 works is not by redrawing everything from scratch, but by updating the underlying data that the selection works with:

  1. Have a function draw() that draws the given data. Inside draw(), make sure you also remove any remnants of the old drawings;
  2. Use D3 to set the correct options in the dropdown (better than hardcoded, because definitions might change in the future);
  3. Whenever the dropdown value changes, call draw with a subset of the underlying data;
  4. Call draw() with all data to provide initial markers.

For your code, have a look at array functions, like Array.prototype.map and Array.prototype.filter, as they're generally very useful when working with data like this.

// Creating map object
var myMap = L.map("map", {
  center: [40.73, -74.0059],
  zoom: 11
});

// Adding tile layer to the map
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(myMap);

var allData = [{
    lat: 40.75,
    lng: -73.99,
    BusinessName: 'Bank LLC',
    Industry: 'Banking'
  },
  {
    lat: 40.73,
    lng: -73.97,
    BusinessName: 'Some Hotel',
    Industry: 'Hospitality'
  },
  {
    lat: 40.74,
    lng: -74.00,
    BusinessName: 'Other Hotel',
    Industry: 'Hospitality'
  }
];

// Create a new marker cluster group
var markers = L.layerGroup();
// Add our marker cluster layer to the map
myMap.addLayer(markers);

function draw(data) {
  // Start fresh
  markers.clearLayers();

  // Loop through data
  for (var i = 0; i < data.length; i++) {
    // Set the latitude, longitude property to a variable
    var latitude = data[i].lat;
    var longitude = data[i].lng;
    var popText = ("<h3>" + data[i].BusinessName + "</h3><h3>Industry: " + data[i].Industry + "</h3>");
    // var industry = data[i].Industry;
    // var mySelector = $("#Industry").val();
    // Check for location property

    if (latitude) {
      // Add a new marker to the cluster group and bind a pop-up
      markers.addLayer(L.marker([latitude, longitude])
        .bindPopup(popText));
    };
  }
}

var industries = allData
  .map(function(row) {
    return row.Industry;
  })
  .sort()
  .filter(function(v, i, arr) {
    // This selects only unique industries:
    // Only return a value if it's the first occurrence or if
    // it's different from the previous entry in the sorted(!) array
    return i === 0 || arr[i - 1] != v;
  });

draw(allData);

d3.select("#selectIndustries")
  // Select all non-disabled options
  // to avoid touching the placeholder one
  .selectAll("option:not([disabled])")
  .data(industries)
  .enter()
  .append("option")
  .attr("value", function(d) {
    return d;
  })
  .text(function(d) {
    return d;
  });

d3.select("#selectIndustries")
  .on("change", function() {
    var selection = this.value;
    var filteredData = allData.filter(function(d) {
      return d.Industry == selection;
    });
    draw(filteredData);
  });
#map {
  height: 400px;
  width: 600px;
}
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js"></script>

<div id="map"></div>

<select id="selectIndustries">
  <option disabled selected>Select an option</option>
</select>

Upvotes: 1

Related Questions