Leaflet GeoJSON Feature Search

person searching for records

Last month I published the PaMAGIC Members Map on the organization’s website.  The map was built using the Esri plugins to Leaflet.js.  One of my favorite features of their geocoding plugin is the ability to search within the features of a map/feature service.  The map is using this plugin to allow users to search for members by name.  When a user types a member’s name into the search control, matching results appear.  When the user selects a member from the list of results, the map zooms to that member’s location.

However, I found myself asking the following question: can I build the membership search without this plugin?  During my spare time over the past couple weeks, I’ve been testing this concept.  The good news is I got it figured out, and wanted to share it with you.  But first, I need to provide a shout out my friend Bryan Grill and his Historical Maps project.  He figured out how to do this, and I am only benefiting from his work.

For this solution, we’ll use a GeoJSON file for the members layer and the jQuery UI Autocomplete widget to perform the member search.   I’ll walk you through the solution, but here’s a summary:

  1. We add information about each member (name and coordinates) to an array within the onEachFeature method of the Leaflet GeoJSON object.
  2.  We create a separate array with the name of each member that is used as the source for the autocomplete search.
  3. We call the map.setView() method using the member’s coordinates when they are selected from the search control.

HTML Set-up

You’ll need to link to jQuery and jQuery UI in your html document:

<head>
   <!-- Jquery UI -->
   	<link rel="stylesheet" href="../assets/jquery-ui/v-1.12.1/jquery-ui.min.css" />
</head>
<body>
   <!-- jQuery -->
   <script src="../assets/jquery/v-3.1.1/jquery.min.js"></script>
   <!-- jQuery UI -->
   <script src="../assets/jquery-ui/v-1.12.1/jquery-ui.min.js"></script>
</body>

Next, you’ll need to set-up the user-interface for the search.  We’ll use a simple input element within a form.

<!-- For me, this is within your standard Bootstrap navigation menu -->
<form class="navbar-form navbar-search">
<div id="geocode" class="geocode-navbar">
      <!-- this is the DOM element we'll target with jQuery UI -->
      <input id="userSearch" type="text" /></div>
</form>

Create Array from GeoJSON Features

The next step is to create an array containing data for each member.  The array will be used for the following:

  1. creating a new array used as the source for the autocomplete search control
  2. to zoom the map to the member when they are selected from the search control

// empty array to contain data for each member
var memberSearch = [];

// path to GeoJSON file
var geojsonData = './assets/data/pamagic_members_04_2017.geojson';

// Construct members GeoJSON layer
// I like to use the Leaflet AJAX plugin
var pmgMembers = new L.GeoJSON.AJAX(geojsonData, {
    onEachFeature: function(feature, layer) {
       // for each feature in the layer, we will add data to our
       // memberSearch array
       memberSearch.push({
          // the member's full name will be the search field
          fullName: layer.feature.properties.FirstName + " " + layer.feature.properties.LastName,
          // we will create a L.latLng object for each member to pass into the map.setView method to zoom to each member
         // GeoJSON data coordinates are X,Y (longitude, latitude)
         coords: L.latLng(layer.feature.geometry.coordinates[1],
                 layer.feature.geometry.coordinates[0])
       });
    }
}).addTo(map);

Configuring the Autocomplete Search

jQuery UI’s Autocomplete widget provides users with suggestions while typing into an input element (input, textarea, and elements with contenteditable attribute are mentioned in the documentation).  The source for the suggestions is a JavScript array.  The documentation also states that a local source is good for small datasets, while a remote source is better for large datasets.

Our next step will be to create an array to use as the source for the autocomplete search.  We are going to use the full name key from the memberSearch array created above.  Although I’m not sure why a timeout function is used to create this array, I’m guessing it is to compensate for the time it takes the GeoJSON data to load.

// empty array for member's name
var returnMember = [];

var createMembersList = setTimeout(function() {
    // loop through memberSearch array
    for (var i=0; i < memberSearch.length; i++) {
       // add full name record to returnMember array
       returnMember.push(memberSearch[i]["fullName"]);
    }
    return returnMember;
}, 5000);

The final step is to configure the autocomplete widget using the returnMember array as the data source.  It is here where we will add the code to zoom to each member when they are selected in the search.  The coords key in the memberSearch array contains a L.latLng object for each member, which stores their coordinates.  We’ll pass these coordinates and a zoom level into the map.setView() method to zoom to each member.

$("#userSearch").autocomplete({
  autoFocus:true,
  minLength:2,
  disabled: false,
  delay:500,
  source: returnMember,
  select: function(event, ui) {
    // get member and zoom to selected member
    new function() {
      for (var i=0; i < memberSearch.length; i++) {
        if (ui.item.value === memberSearch[i]["fullName"]) {
            var coords = memberSearch[i]["coords"];
            map.setView(coords, 15);
        } // end if
      } // end for loop
    }; // end new function
  } // end select
});

I’ve created a CodePen to show a live demo of the finished product:

Advertisements

3 thoughts on “Leaflet GeoJSON Feature Search

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s