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
I've finished reading This Way Up. It's about maps, that went wrong.
It's a good read, but htyerr were several chapters that were written in a novel way (as a video transcript, a series of letters), and I found distracting from the a tail content. It'll have worked better in a produced video.
No mention of @openstreetmap though :-(
Updated a bench
Created a tree; Updated 3 humps and a waste_basket
The Early Cormorant Catches the Eel
Sorry, not the best photo! But I caught this Cormorant catching this large eel when looking for Bank Swallows, right next to Eel Pie Island in the Thames.
#Birds #BirdPhotography #BirdsOfMastodon #Photography #London
Updated an estate_agent office
I went to my nieces' birthday party yesterday.
The theme was pink, and that included all the food, mostly died with beet root.
Shock and horror this morning when doing number two. Not only was my turd dark red, it was also glittering at me. Apparently the carrot cake had edible glitter...
So now I know what's worse than glitter.
😂 ✨ 💩 🟣Long-Tailed Tit on a Branch with Lichen
I've been spending some time in random London local nature reserves.
Sitting and listening, and in fifteen minutes you spot countless species.
This one was in Ham Lands Local Nature Reserve near Teddington.
#london #BirdPhotogaphy #BirdsOfMastodon #Birds #LichenSubscribe
A Colourful Mandarin
In The Long Water in Kensington Palace Gardens, London.
Created 7 benches
Created 2 benches
Created a bench
I walked 7.3km in 2h28m39s
Added a note about a duplicate Papersmiths
I walked 4.1km in 49m02s
Fixed website
fix typo
Updated a bench
I walked 1.6km in 20m26s
I walked 1.1km in 11m49s
The Yellow Eye
A blue heron's head, with its very yellow stare-y eye.
#BirdPhotography #Photography #BirdsOfFediverse #BirdsOfMastodon #London
My little Lego box is telling me it really is quite warm outside.
Created a bicycle_parking and a crossing
I walked 3.3km in 41m56s








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