Simple Search with JQuery AutoComplete and Leaflet.

Working Map

This is just a simple demo on how to get these apps working together. For data, I took a GeoJSON USA polygon file. It has eight fields that I show in the popup. (STATE_NAME, SUB_REGION, STATE_ABBR, POP2010, POP10_SQMI, MALES, FEMALES, SQMI).

The purpose of the app is to enter a state name in the search and zoom to it. The JQuery app will allow you to start typing and it will self-populate with possible matches. The more you type the smaller the selection list. It is not case sensitive which helps users, who do not know how it is stored in the database. Finally select one and the map zooms to the selected state and highlights it yellow.

Ready state.
As you start typing in a state name notice the possible choices show up.

Once selected it zooms to the state polygon and fires off a click event which highlights the polygon yellow and opens a popup showing the database attributes.


Now the code.

In the head section, I have the list of libraries I used, basically Leaflet, JQuery, and JQuery UI (User Interface), both the JavaScript and CSS files.

<!DOCTYPE html>

	<link rel="stylesheet" href="" />
	<script src=""></script>
        <script src=""></script>
   	<link rel="stylesheet" href=""/>
        <script src=""></script>

Now the CSS information. The AutoComplete needs a z-index defined or the selection list will hide behind the map div. I also included a margin for spacing on the page. The map has the map div sized up with a black border defined and a z-index to help the AutoComplete app out.
      #autocomplete {
        z-index: 100;
        margin-bottom: 5px;
      #map {
        width: 800px;
        height: 600px;
        border: 1px solid black;
        z-index: 0;
Define the div's one for the autoComplete and one for the map.

On to the JavaScript, here I define the JSON file. It's a static file instead of a web service (REST) feed. Since the state boundaries shouldn't change this method saves on server processing. Below this is the variables being declared for my two arrays.
<div class="ui-widget" style="text-align:left;"> <input id="autocomplete" placeholder="Search for: State"> </div> <div id="map" > </div>
<script> var url = 'usa.json'; // my GeoJSON data source, in this case a static file not a live PHP data feed. var arr = []; var arr1 = [];
Here I define the JQuery AutoComplete app, it is being loaded without data now. I'll load it when I read the GeoJSON data.

The map is declared with the center and zoom level set for the US.

Next the two basemaps, both opensource maps with the balck and white being the loaded map.

  // Initialize autocomplete with empty source.
  $( "#autocomplete" ).autocomplete();

	var map ='map').setView([47.7541, -107.05078], 3); 

	var osm=new L.tileLayer('http://{s}{z}/{x}/{y}.png',{ 
				attribution: '© <a href="">OpenStreetMap</a> contributors'});
	var OpenStreetMap_BlackAndWhite = L.tileLayer('http://{s}{z}/{x}/{y}.png', {
	maxZoom: 18,
	attribution: '© <a href="">OpenStreetMap</a>'
Now the basic polygon styling. I've also included a style for highlighting a polygon.

The forEachFeature function. In the first part of it, I set the layer._leaflet_id which is each polygons internal ID. I set it to the state name, I grab this from the polygon attributes as it is loading. Make sure it is a unique value for the search.

I create the popup content using HTML and the polygon attributes. I then bind the popup content to the feature.

I included a layer on click function. First it sets the symbology to the layer, this is to unselect or reset a prior feature and then set the highlight to the new feature.
// Set style function that sets fill color property
function style(feature) {
    return {
        fillColor: 'green', 
        fillOpacity: 0.5,  
        weight: 2,
        opacity: 1,
        color: '#ffffff',
        dashArray: '3'
var highlight = {
	'fillColor': 'yellow',
	'weight': 2,
	'opacity': 1

	function forEachFeature(feature, layer) {
            // Tagging each state poly with their name for the search control.
            layer._leaflet_id =;

            var popupContent = "<p><b>STATE: </b>"+ +
                "</br>REGION: "+ +
                "</br>STATE ABBR: "+ +
                "</br>POP2010: "+ +
                "</br>Pop 2010 per SQMI: "+ +
                "</br>Males: "+ +
                "</br>Females: "+ +
                "</br>SQ Miles: "+ +'</p>';


            layer.on("click", function (e) { 
                stateLayer.setStyle(style); //resets layer colors
                layer.setStyle(highlight);  //highlights selected.

Here I am defining the layer but setting it with a null value. Using JQuery I am loading the GeoJSON data into the layer and before adding it to the map, I am running through each feature and populating an array with the State Name from its attributes value. This is the same field I used for the unique internal ID. Once the array is done I pass it to the addDataToAutocomplete function for processing. Lastly, I am adding the layer to the map.
// Null variable that will hold layer
var stateLayer = L.geoJson(null, {onEachFeature: forEachFeature, style: style});

$.getJSON(url, function(data) {
        for (i = 0; i < data.features.length; i++) {  //loads State Name into an Array for searching
            arr1.push({label:data.features[i].properties.STATE_NAME, value:""});
       addDataToAutocomplete(arr1);  //passes array for sorting and to load search control.

Loading the AutoComplete app is next. Once the array is finished, it hits this function and gets sorted. Next I use it as the data source for the AutoComplete app. Once text is selected, the app calls a polySelect function and passes the state Name along. I then reset the AutoComplete apps textbox with a null value making it ready for the next search.

  ////////// Autocomplete search
	function addDataToAutocomplete(arr) {
        arr.sort(function(a, b){ // sort object by Name
            var nameA=a.label, nameB=b.label
            if (nameA < nameB) //sort string ascending
                return -1 
            if (nameA > nameB)
                return 1
            return 0 //default return value (no sorting)

   		// The source for autocomplete.
		$( "#autocomplete" ).autocomplete("option", "source", arr); 
		$( "#autocomplete" ).on( "autocompleteselect", function( event, ui ) {
			polySelect(ui.item.label);  //grabs selected state name
	}	///////////// Autocomplete search end
Now with the state name selected, I fire off a click event to the selected feature. (Leaflet is using its internal ID to select the polygon) the fire(click) is the same as using a mouse to click on a feature. Using the internal ID I get the feature and the bounds and have the map zoom to the feature bounds (Zoom to feature)

// fire off click event and zoom to polygon  
  	function polySelect(a){
		map._layers[a].fire('click');  // 'clicks' on state name from search
		var layer = map._layers[a];
		map.fitBounds(layer.getBounds());  // zooms to selected poly
// off click event and zoom to polygon
Finishing up my Leaflet app, I just configure out the layer switcher control and add it to the map.
var baseMaps = {
    "Open Street Map": osm,
   "OSM B&W":OpenStreetMap_BlackAndWhite

var overlayMaps = {
//Add layer control
L.control.layers(baseMaps, overlayMaps).addTo(map);
That is it, toss in some closing tags and I am done. My finished script,    My GeoJSON data