Autocomplete Map Search:
Autocomplete multi-source Search with ArcGIS REST services
Search First. Search is expected in modern web applications. And what better use of a map application than to start typing what you want, be shown a list of relevant suggestions, pick one and be taken there with potentially some useful context info.
In several posts in the great geospatial blog, MapBrief, Brian Timoney has mentioned a Search First approach to web application building. The most recent example:
Good Thing #1: A Big, Obvious Auto-Complete Text Entry Box
I couldn’t agree more. It is core to nearly every application I build and is of far greater use than most of the novelty web mapping ‘standard’ tools <uhem, looking at you, measure tool>.
Search is the number one request I get in web mapping applications so I thought I’d share how I put all the pieces together to get an autocomplete search up and running with esri ArcGIS REST services.
I’ll walk through the steps to get a fully functioning autocomplete map search feature.
Here’s the source on github and a working sample application.
Let’s build an autocomplete search box for recreation sites in Okanogan-Wenatchee National Forest.
This autocomplete solution uses twitter typeahead js, the bundled download includes the bloodhound suggestion engine and the jQuery plugin, all of which we’ll use here.
<input id="search-box" type="text" placeholder="Search...">
There are three parts to getting things going:
- Define and initialize suggestion engines
- Wireup typeahead with jQuery including adding the engines and defining the UI
- Handle the event when a user selects one of the suggestions
Define the Engines
First, we’ll define an engine (bloodhound) for each source to include. In this example we’ll use four:
A campground (arcgis) feature service layer
A trail head feature service layer
A ski area feature service layer
The esri geocoder service
Construct the remote source(s)
This is the most critical piece of the whole operation and requires some consideration because you potentially have options here. The remote source needs a url to send a request each time the user types characters. Selecting the best option will result in the best performance. Here are our best options with arcgis REST services:
If you have a map server, find is a great option if you want to search multiple layers in the same request. I’d say if you have this option, take it.
If you only have a feature server, this is for you. You can either query a layer individually or query the entire feature server. We are going to query each of our feature layers individually.
Suggest is currently meant for geocoding, for now, but I suspect a locator service could be constructed to take advantage of this service. I’ll make the assumption that this is the best option because it is intended specifically for search.
You could roll your own locator service that supports suggest (ArcGIS 10.3 +). We’ll use the world geocoder and take advantage of suggest.
The code to build the campground site suggestion search engine and initialize it:
Here’s the engine setup for the esri world geocoder service. Note the searchExtent parameter. This can be used to limit the geographic area of you suggestion results. In this case it is (roughly) Washington State.
You can see that in both of these engines we set the ajax property to show the spinner before the request and remove it when the request is complete. These calls can step over each other if you have several engines but works pretty well here.
Add the engines and style the suggestions
Now we need to setup typeahead itself and wire up the engines and tell typeahead the properties we are using for each suggestion generated by the engines. We will also define how the suggestions will be displayed in the drop down.
First, turn the input box into a typeahead via jQuery and set some options like so
minLength is the number of characters a user types before suggestions start firing.
Next tell typeahead about the engines to use, here’s our campground setup
We’ve just told typeahead that the name of this source is ‘camp’, the displayKey is ‘name’, the source is our camp bloodhound engine adapter and set the template to use for the header (a campground icon inside a header tag).
You’ll do the same for all sources chaining them together. The whole set of sources looks like this:
Define what will happen when a user selects a suggestion
Okay, cool, we’ve got some suggestions for our users.
Now we need to setup what will happen when one is actually selected. So we simply set up a jQuery event handler and chain it to our typeahead declaration. the event will pass the callback function the element and the selected datum (generated from the engine).
For the feature service layers we have set the datum to include the objectId (see the filter function in the bloodhound engine definition). This allows us to fetch the feature along with its’ geometry via another ajax call. We can then get attributes, zoom to it, make another call to get more info, whatever you want. In this sample we simply zoom to it and put a text label displaying the name right on the map.
For the geocoder a name and ‘magicKey’ are defined in the datum which allows us to call back to the service to retrieve the feature via a call to the find REST end point. For this one we simply zoom to the extent returned. Here’s the whole event handler:
That’s it, complete autocomplete.
You have an incredible amount of control over display via css as well. Here is what I’ve used in the sample app:
There are many more options available to you in this solution so please review the documentation for further info. I am very impressed with the flexibility of typeahead so if this setup doesn’t quite work for you I suspect there are ways to get it going.
BTW, If you are using esri-leaflet you can use the great geocoder plugin that also allows you to search via a geocoder service as well as map and feature services like we’re doing here.
Finally a shout out to Bryan McBride for his excellent mapping template, bootleaf, that I used as a starter for this solution.
The whole project is in a github repo here
And you can see it in action here