Address lookups with Leaflet and Nominatim
I recently wrote a patch for joind.in to add a map of an event's location to the event detail page. With the same patch, I also replaced the location part of the event edit page with a solution that uses JQuery, Leaflet as map API, OpenStreetMap tiles and Nominatim for doing address lookups. This article forms a small tutorial on how to use this same set-up yourself.
The Basics
To start, we create a new directory for our project:
mkdir addresses cd addresses
Then I downloaded the Leaflet and jQuery libraries and extracted them in the js directory of the project:
mkdir js curl -L https://github.com/CloudMade/Leaflet/zipball/v0.4.5 -o leaflet.zip unzip leaflet.zip mv CloudMade-Leaflet-*/dist/* js rm -rf CloudMade-Leaflet-* rm leaflet.zip curl http://code.jquery.com/jquery-1.8.2.min.js -o js/jquery-1.8.2.min.js
As first step, we are simply going to show a map on a web page. The map is going to be full screen, and will not have any bells and whistles. The code to embed a map is small, but we will separate it into three files for clarity: a CSS file for our styles (site.css), an HTML file for the structure (index.html) and a JS file for all our JavaScript functions (js/map.js).
Let's start with the HTML file:
<html>
<head>
<title>Leaflet and Nominatim example</title>
<link rel="stylesheet" href="js/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="js/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" type="text/css" href="site.css">
<script src="js/leaflet.js"></script>
<script src="js/jquery-1.8.2.min.js"></script>
</head>
<body>
<div id="map"/>
<script src="js/map.js"></script>
</body>
</html>
This HTML file includes the Leaflet and jQuery libraries, as well as the default CSS file that Leaflet needs. We are also including our own CSS file (site.css):
body {
margin: 0;
}
div#map {
width: 100%;
height: 100%;
}
In the body of the HTML file, we place a <div> as contained for the map, and then include a JavaScript file that is responsible for embedding them map:
var map;
function load_map() {
map = new L.Map('map', {zoomControl: false});
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttribution = 'Map data © 2012 <a href="http://openstreetmap.org">OpenStreetMap</a> contributors',
osm = new L.TileLayer(osmUrl, {maxZoom: 18, attribution: osmAttribution});
map.setView(new L.LatLng(51.538594, -0.198075), 12).addLayer(osm);
}
window.onload = load_map;
If you request the index.html page now through the browser, you will see something like:
Adding the Address Search
In order to add an address lookup form, we need to add more HTML. Our HTML will feature an input box (for the address), a submit button, and a place holder to show our results in. We add this code between the <div id="map"/> and the <script... tags:
<div id="search"> <input type="text" name="addr" value="" id="addr" size="10" /> <button type="button" onclick="addr_search();">Search</button> <div id="results"/> </div>
To style this, we add the following at the end of our CSS file:
div#search {
background-color: white;
position: absolute;
bottom: 40px;
left: 40px;
width: auto;
height: auto;
padding: 10px;
}
div#search input {
width: 200px;
}
div#results {
font-style: sans-serif;
color: black;
font-size: 75%;
}
If we reload the page in our browser, we will see something like:
Now the only thing left to do is to implement the addr_search function. In our JS file (js/map.js) we add before window.onload = load_map; the following lines (split over multiple sections in this tutorial):
function addr_search() {
var inp = document.getElementById("addr");
$.getJSON('http://nominatim.openstreetmap.org/search?format=json&limit=5&q=' + inp.value, function(data) {
This above line uses jQuery's AJAX capabilities to request a URL, parse the JSON result and issue a callback if it worked. We query Nominatim here, with as format json and limiting the result to 5 items. Nominatim also supports other parameters, which are documented here.
var items = [];
$.each(data, function(key, val) {
items.push(
"<li><a href='#' onclick='chooseAddr(" +
val.lat + ", " + val.lon + ");return false;'>" + val.display_name +
'</a></li>'
);
});
For each of the items in our result, we create an <li> element which has an <a href containing a call to a JavaScript function (chooseAddr). This function is responsible for re-centering the map according to the picked latitude and longitude.
$('#results').empty();
if (items.length != 0) {
$('<p>', { html: "Search results:" }).appendTo('#results');
$('<ul/>', {
'class': 'my-new-list',
html: items.join('')
}).appendTo('#results');
} else {
$('<p>', { html: "No results found" }).appendTo('#results');
}
});
}
This processes the results that came back from Nominatim. If there are results, we shows those including a Search results: header, and if there are no results, we show No results found.
Then we need to add one more function, the chooseAddr function which looks like:
function chooseAddr(lat, lng, type) {
var location = new L.LatLng(lat, lng);
map.panTo(location);
if (type == 'city' || type == 'administrative') {
map.setZoom(11);
} else {
map.setZoom(13);
}
}
We simply use the latitude and longitude from the function invocation, and in order to make things slightly nicer we zoom in a bit less if the item type is either a city or an administrative border. As each of the returned results actually includes a full bounding box, we probably can use that to zoom in better, but I will leave that for your own experiments - you'd want the panInsideBounds() method of Leaflet's Map class for that.
In the end, if we click on the Search button, a list is presented of all our search results:
And after clicking one of the links, we see the map centered on Paris:
The code for this example is available on github in my osm-tools repository at https://github.com/derickr/osm-tools/tree/master/leaflet-nominatim-example
Life Line
Merged pull request #1055
Fixed issue #2387: Remove INI entries for changed and removed Xdebug …
Merged pull request #1053
Reimplement PR #1052 with normal style
Add missing section comment
Merge branch 'xdebug_3_5'
Merged pull request #1054
Change error retrieval method in ctrl_socket.c
Pink Sky at Sunset
I took this photo over the Christmas period in the Dutch city of Breda.
I walked 8.5km in 1h25m28s
I walked 8.1km in 1h21m10s
I walked 0.8km in 9m03s
I walked 4.8km in 50m12s
Went for a 20k walk through Bushy Park, along the Thames, and through Richmond Park and Wimbledon Common. It was a bit nippy!
I hiked 19.3km in 3h52m02s
Updated a pub
I walked 4.6km in 44m50s
I walked 4.9km in 47m58s
Update Westbourne Green area, now that it is open
I walked 11.9km in 2h3m03s
I walked 9.8km in 1h47m38s
I walked 10.2km in 1h34m25s
Whoop! FOSDEM travel and hotel booked. See you in Brussels at the end of January?
I walked 10.6km in 1h48m23s
I walked 3.0km in 33m38s



Shortlink
This article has a short URL available: https://drck.me/addrlookup-9mg