Leaflet - Js Tips and Tricks
Leaflet - Js Tips and Tricks
Leaflet - Js Tips and Tricks
Those who write the plugins also deserve a special thanks. In the same way that
Leaflet provides an excellent framework to work on, the diversity and ingenuity of
the available plugins is staggering. There are so many that I am not going to
attempt to single any one out for individual praise. Instead, please accept my
heartfelt thanks as a group of dedicated individuals.
I don’t want to forget to mention a special group that could be easily overlooked in
receiving thanks here. The tile makers. If you’re not entirely sure what I mean by
this, don’t worry, it will all become clearer as you read on. Without the map tiles,
there would be no maps. These organisations provide a valuable resource that
Leaflet leverages and I would like to recognise them as well.
Lastly, I want to pay homage to Leanpub who have made the publishing of this
document possible. They offer an outstanding service for self-publishing and have
made the task of providing and distributing content achievable.
Introduction
The idea of writing a book on Leaflet came about pretty much as soon as I first
used it. It seemed so easy to use and I went looking for some books to help me
through some of the more advanced topics. Since they are fairly scarce on the
subject in 2014, I thought it would be a neat project to write one!
This decision has been supported mainly by the success of publishing my first
book on d3.js called D3 Tips and Tricks. When I started writing D3 Tips and
Tricks at the end of 2012 I had a desire to put out some documentation, but the
method was a bit sketchy. I was lucky to stumble upon Leanpub while I was
putting information together and it ticked all the boxes I was looking for in terms
of being able to publish easily and distribute for free while providing the ability for
people to get updates to the book when it gets improved. I tend to be a bit
evangelical about Leanpub but the bottom line is that they provide a great service
for people who want to publish a book in a flexible way.
The point to take away from all of this is that any online map is just a collection of
lots of blocks of code, each block designed to carry out a specific function. Pick
the blocks you want and implement them. Leaflet makes this easy and in particular
their support for plugins actively encourages it. I found it was much simpler to
work on one thing (block) at a time, and this helped greatly to reduce the
uncertainty factor when things didn’t work as anticipated. I’m not going to pretend
that everything I’ve done while trying to build maps employs the most elegant or
efficient mechanism, but in the end, if it all works on the screen, I walk away
happy :-). That’s not to say I have deliberately ignored any best practices – I just
never knew what they were. Likewise, wherever possible, I have tried to make
things as extensible as possible. You will find that I have typically eschewed a
simple “Use this code” approach for more of a story telling exercise. This means
that some explanations are longer and more flowery than might be to everyone’s
liking, but there you go, try to be brave :-).
I’m sure most authors try to be as accessible as possible. I’d like to do the same,
but be warned… There’s a good chance that if you ask me a technical question I
may not know the answer. So please be gentle with your emails :-).
Email: [email protected]
What is leaflet.js?
Leaflet.js is an Open Source JavaScript library that makes deploying maps on a
web page easy. Being Open Source means that the code can be easily viewed to
see how it works, anyone can use it and more importantly anyone can contribute
back to the project with improvements to the code.
To provide all this mapping goodness it uses a paltry 140kB (at time of writing)
JavaScript file that loads with your web page and provides access to a range of
functions that will allow you to present a map. There is support for modern
browsers in desktop and mobile platforms so you can deploy your map pretty much
anywhere.
Its goals are to be simple to use while focussing on performance and usability, but
it’s also built to be extended using plugins that extend its functionality. It has an
excellent API which is well documented, so there are no mysteries to using it
successfully in a range of situations. Companies who are already touted as using
Leaflet include Flickr, foursquare, GitHub, Data.gov, IGN, The Washington
Post, OSM, Evernote, Financial Times, Pintrest and Open Street Map. Now I’m
picking that you’ll agree that that’s a list of significant players on the Internet.
However, with Leaflet, you can make and deploy maps in much that same way that
these organisations do.
Out of the box Leaflet provides the functionality to add markers, popups, overlay
lines and shapes, use multiple layers, zoom, pan and generally have a good time :-
). But these are just the the core features of Leaflet. One of the significant strengths
of Leaflet is the ability to extend the functionality of the script with plugins from
third parties. At the time of writing there are hundreds of separate plugins that
allow features such as overlaying a heatmap, animating markers, loading csv files
of data, drawing complex shapes, measuring distance, manipulating layers and
displaying coordinates.
Leaflet is truly a jewel of Open Source software. Simple, elegant, functional but
powerful. There’s a good chance that even if you don’t present maps with Leaflet,
you’ll be using ones that someone else made with it at some stage on the Internet.
However, that doesn’t mean that it’s beyond those with a little computer savy and a
willingness to experiment. Remember failure is your friend (I am fairly sure that I
am also related by blood). Just learn from your mistakes and it’ll all work out.
So, here in no particular order is a list of good things to know. None of which are
essential, but any one (or more) of which will make your life slightly easier.
HTML
This stands for HyperText Markup Language and is the stuff that web pages are
made of. Check out the definition and other information on Wikipedia for a great
overview. Just remember that all you’re going to use HTML for is to hold the code
that you will use to present your information. This will be as a .html (or .htm) file
and they can be pretty simple (we’ll look at some in a moment).
JavaScript
JavaScript is what’s called a ‘scripting language’. It is the code that will be
contained inside the HTML file that will make Leaflet do all its fanciness. In fact,
leaflet.js is a JavaScript Library, it’s the native language for using Leaflet.
Knowing a little bit about this would be really good, but to be perfectly honest, I
didn’t know anything about it before I started using d3.js. I read a book along the
way (JavaScript: The Missing Manual from O’Reilly) and that helped with context,
but the examples and tutorials that are available for Leaflet are understandable, and
with a bit of trial and error, you can figure out what’s going on.
Full disclosure
I know CSS is a ridiculously powerful tool that would make my life easier, but I use it in a
very basic (and probably painful) way. Don’t judge me, just accept that the way I’ve learnt
was what I needed to get the job done (this probably means that noob’s like myself will find
it easier, but where possible try and use examples that include what look like logical CSS
structures)
Web Servers
Ok, this can go one of two ways. If you have access to a web server and know
where to put the files so that you can access them with your browser, you’re on
fire. If you’re not quite sure, read on…
A web server will allow you to access your HTML files and will provide the
structure that allows it to be displayed on a web browser. I can thoroughly
recommend WampServer as a free and simple way to set up a local web server that
includes PHP and a MySQL database. Go to the WampServer web page
(http://www.wampserver.com/en/) and see if it suits you.
Throughout this document I will be describing the files and how they’re laid out in
a way that has suited my personal efforts, but they will work equally well on a
remote server. I will explain a little more about how I arrange the files later in the
‘Getting Leaflet’ section.
There are other options of course. You could host code on GitHub and present the
resulting graphics on bl.ocks.org. You can also present and dynamically edit your
code on the blockbuilder.org page. This is a great way to make sure that your code
is available for peer review and sharing with the wider community.
PHP
PHP is a scripting language for the web. That is to say that it is a programming
language which is executed when you load web pages and it helps web pages do
dynamic things.
You might think that this sounds familiar and that JavaScript does the same thing.
But not quite.
JavaScript is designed so that it travels with the web page when it is downloaded
by a browser (the client). However, PHP is executed remotely on the server that
supplies the web page. This might sound a bit redundant, but it’s a big deal. This
means that the PHP which is executed doesn’t form part of the web page, but it
can form the web page. The implication here is that the web page you are viewing
can be altered by the PHP code that runs on a remote server. This is the dynamic
aspect of it.
In practice, PHP could be analogous to the glue that binds web pages together.
Allowing different portions of the web page to respond to directions from the end
user.
Preferably, you should get an editor that will provide some assistance in the form
of syntax highlighting which is where the editor knows what language you are
writing in (JavaScript for example) and highlights the text in a way that helps you
read it. For example, it will change text that might appear as this;
var map = L.map('map').setView([-41.2858, 174.78682], 14);
mapLink =
'<a href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + ' Contributors',
maxZoom: 18,
}).addTo(map);
var marker = L.marker([-41.29042, 174.78219])
.addTo(map);
There are plenty of editors that will do the trick. I have a preference for Geany,
mainly because it’s what I started with and it grew on me :-).
Getting Leaflet
Luckily this is pretty easy.
You can either get it directly from leafletjs.com off their downloads page or even
go to the Leaflet repository on github and download it by clicking on the ‘ZIP’
button (slightly trickier for the uninitiated).
What you do with it from here depends on how you’re hosting your web pages. If
you’re working on them on your local PC, then you will want to have the leaflet.js
and leaflet.css files in paths that can be seen by the browser. Again, I would
recommend WAMP (a local web server) to access your files locally. If you’re
using WAMP, then you just have to make sure that it knows to use a directory that
will contain the appropriate files and you will be away.
The important things to remember about placing the leaflet files that youdownload
are as follows;
The latest stable release of Leaflet is available on several CDN’s - just place this in
the head of your HTML code:
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/leaflet.css"
/>
<script
src="https://unpkg.com/[email protected]/dist/leaflet.js"
></script>
Sometimes you will strike trouble using these due to a security feature that
browsers use to prevent downloading malicious code. This prevents what is
called CORS (Cross Origin Resource Sharing). This will be evident to users
of blockbuilder.org and leaflet.js. This can be overcome by enabling subresource
integrity when using Leaflet from a CDN as follows;
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha512-
xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUb
eOfBIp\
tF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""/>
<script
src="https://unpkg.com/[email protected]/dist/leaflet.js"
integrity="sha512-
gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQ
tXPSiU\
TtC0TjtGOmxa1AJPuV0CPthew=="
crossorigin=""></script>
leafletjs.com
leafletjs.com would be the first port of call for people wanting to know something
about leaflet.js.
From here you can; - Access the features list to get an overview of the strengths of
Leaflet. - Check out some tutorials on common things that you will want to do with
Leaflet. - Read the extensive API documentation that will be a treasure trove of
information as you use Leaflet. - Find links to an extensive list of plugins that you
can use with Leaflet. - Get updates on the progress of Leaflet development on
the developer blog.
Google Groups
There is a Google Group dedicated to discussions on leaflet.js. This serves as the
official Leaflet community forum.
In theory this forum is for discussion of any Leaflet-related topics that go beyond
the scope of a simple GitHub issue report — ideas, questions, troubleshooting,
feedback, etc.
So, by all means add this group as a favourite and this will provide you with the
opportunity to receive emailed summaries of postings or just an opportunity to
easily browse recent goings-on.
Stack Overflow
Stack Overflow is a question and answer site whose stated desire is “to build a
library of detailed answers to every question about programming”. Ambitious. So
how are they doing? Actually really well. Stack overflow is a fantastic place to get
help and information. It’s also a great place to help people out if you have some
knowledge on a topic.
They have a funny scheme for rewarding users that encourages providing good
answers based on readers voting. It’s a great example of gamification working
well. If you want to know a little more about how it works, check out this page;
http://stackoverflow.com/about.
Github
Github is predominantly a code repository and version control site. It is highly
regarded for its technical acumen and provide a fantastic service that is broadly
used for many purposes.
Whilst not strictly a site that specialises in providing a Q & A function, there is a
significant number of repositories (many thousands at last count) which mention
leaflet. With the help from an astute search phrase, there is potentially a solution to
be found there.
The other associated feature of Github is Gist. Gist is a pastebin service (a place
where you can copy and past code) that can provide a ‘wiki like’ feature for
individual repositories and web pages that can be edited through a Git repository.
Gist plays a role in providing the hub for the bl.ocks.org example hosting service
set up by Mike Bostock.
For a new user, Github / Gist can be slightly daunting. It’s an area where you
almost need to know what’s going on to know before you dive in. This is certainly
true if you want to make use of it’s incredible features that are available for hosting
code. However, if you want to browse other peoples code it’s an easier
introduction. Have a look through what’s available and if you feel so inclined, I
recommend that you learn enough to use their service. It’s time well spent.
bl.ocks.org
bl.ocks.org is a viewer for code examples which are hosted on Gist. You are able
to load your code into Gist, and then from bl.ocks.org you can view them.
This is a really great way for people to provide examples of their work and there
are many who do. However, it’s slightly tricky to know what is there. It is my
intention as I write this book to host examples here, so hopefully I don’ have to
come back and edit this sentence :-).
I would describe the process of getting your own code hosted and displaying as
something that will be slightly challenging for people who are not familiar with
Github / Gist, but again, in terms of visibility of the code and providing an external
hosing solution, it is excellent and well worth the time to get to grips with.
The partner to bl.ocks.org (not literally) is the site blockbuilder.org. This acts as an
area where Gists or bl.ocks can be edited live and displayed and shared. It works in
conjunction with GitHub (in much the same way that bl.ocks.org does).
Twitter
Twitter provides a great alerting service to inform a large disparate group of people
about stuff.
It’s certainly a great way to keep in touch on a hour by hour basis with people who
are involved with Leaflet and this can be accomplished in a few ways. First, there
is the official Leaflet Twitter identity hosted by Vladimir (AKA @mourner).
Second, find as many people from the various Leaflet sites around the web who
you consider to be influential in areas you want to follow (different aspects such as
development, practical output, educational etc) and follow them. Even better, I
found it useful to find a small subset who I considered to be influential people and
I noted who they followed. It’s a bit ‘stalky’ if you’re unfamiliar with it, but the
end result should be a useful collection of people with something useful to say.
Tutorials
There are a good number of tutorials hosted on leaflet.com. Visit to get a good
overview of a range of tools and techniques to extend your skills with Leaflet.
This wont be an exact template for building on, since there are some liberties that are taken,
but it will do the job and provides a base for explanation of the process and it will
demonstrate how easy it can be.
The following is the full code listing of the file simple-map.html that we will be
examining;
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="leaflet.css" />
</head>
<body>
<script src="leaflet.js"></script>
<div id="map" style="width: 600px; height:
400px"></div>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
</script>
</body>
</html>
The output that it will produce on a web page will look like this;
You can access an electronic version from bl.ocks.org, GitHub and there is a copy
of this file called simple-map.html that can be downloaded (in a zip file) when
you download the book from Leanpub. The online version at bl.ocks.org and
GitHub both use subresource integrity to support secure CORS CORS (Cross
Origin Resource Sharing) requests.
You can pan the map by pressing an holding the left mouse button and waving it
about and you can zoom in and out of the map by either clicking on
the + or - buttons or using a scroll wheel.
As you can tell from the code listing, there’s not much there, so what you can take
from that is that there is a significant amount of cleverness going on inside
leaflet.js.
Once we’ve finished explaining the different parts of the code, we’ll start looking
at what we need to add in and adjust so that we can incorporate other useful
functions.
The two parts to the code that we in our example to consider are;
HTML
JavaScript
Technically we could also consider some CSS, but the way that this example loads our styles
is configured for simplicity, not flexibility as we will check out soon.
HTML
Here’s the HTML portions of the code;
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="leaflet.css" />
</head>
<body>
<script src="leaflet.js"></script>
<div id="map" style="width: 600px; height:
400px"></div>
<script>
The D3 JavaScript code is here
</script>
</body>
</html>
Compare it with the full file. It obviously comprises most of the code for the web
page which should tell you two things.
1. The JavaScript portion that draws the map is really small (and therefore it
should hopefully be pretty easy to understand (Spoiler alert: It is)).
2. The heavy lifting done by leaflet.js must be pretty clever.
There are plenty of good options for adding additional HTML stuff into this very
basic part for the file, but for what we’re going to be doing, we really don’t need to
make things too difficult.
HTML works its magic by enclosing instructions for the browser in specific ‘tags’
A tag will begin with a descriptive word inside the greater-than and less-than signs
and end with the same thing except the closing tag includes a backslash. There are
also tags that allow including the contents inside a single tag that is closed off with
a backslash. This appears to be connected with XHTML and sometimes with
browser variants. To be perfectly honest I have trouble keeping up with it all.
For example, the title of our web page is ‘Simple Leaflet Map’. This is enclosed by
an opening title tag (<title>) and a closing title tag (</title>). So that the
whole thing looks like this; <title>Simple Leaflet Map</title>.
Each tag has a function and the browser will know how to execute the contents of a
tag by virtue of standards for web browsers that builders of browsers implement.
Tags can also be nested so hierarchies can be established to create complex
instructions.
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="leaflet.css" />
</head>
The title tag simply provides identification for the page. The <meta> tag defines
the character set that is used for the page. But the link tag is a little different.
In this case we need to load a set of styles for Leaflet to use. In this case we are
loading them from a local file (in the same directory as our html file ‘simple-
map.html’), In the online versions at bl.ocks.org and GitHub we use online files
instead.
<script src="leaflet.js"></script>
This tells the browser that we are loading a script (hence the <script> tags) and
in this case, the script is leaflet.js which we get from a local file. This is using
the same idea as we used for the css file.
JavaScript
The JavaScript portion of our code looks like this;
Firstly I have to apologise since it should look slightly simpler, but in order to
make the code appear ideal for the book I have made it slightly less simple (but
only slightly). But the main thing that we can take away from this piece of code is
that there’s not very much there. The process of presenting a map on a web page
has been rendered to a very few lines of code.
The heart of the process in the code above consists of two actions;
Where the map object is instantiated given an appropriate div element or its id and
(optional) map state options.
The .setView method is used specifically to modify our map state. In this
case setView sets the geographical centre of the map ([-41.2858,
174.78682] is the latitude and longitude of the centre point) and the zoom level
(14).
If this all seems a bit rushed in terms of an explanation, never fear as we will go
over as many mysteries of maps as possible in other sections of the book.
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
In our example we are retrieving our tiles from openstreetmap.org and setting
options for attribution and maxZoom.
attribution is the placing of appropriate reference to the source of the tiles. The
idea is to ensure that credit (and copyright acknowledgement) is provided to the
tile provider as is reasonable. In the example used here we place the words ‘Map
Data’ and then a copyright symbol (which is produced by the text ©) followed
by the variable mapLink which is declared slightly earlier in the JavaScript code
and is set to <a
href="http://openstreetmap.org">OpenStreetMap</a> which provides
the text OpenStreetMap and a link to openstreetmap.org. The end result looks
like this;
Map Attribution
Now, there is a strong possibility that the information I have laid out here is at best
borderline useful and at worst laden with evil practices and gross inaccuracies. But
look on the bright side. Irrespective of the nastiness of the way that any of it was
accomplished or the inelegance of the code, if the map drawn on the screen is
effective, you can walk away with a smile. :-)
This section concludes a very basic description of one way of presenting a map on
a web page. We will look as adding value to it in subsequent chapters.
I’ve said it before and I’ll say it again. This is not strictly a how-to for learning
how to implement leaflet.js. This is how I have managed to muddle through in a
bumbling way to try and put maps on a screen. If some small part of it helps you.
All good. Those with a smattering of knowledge of any of the topics I have
butchered above (or below) are fully justified in feeling a large degree of righteous
indignation. To those I say, please feel free to amend where practical and possible,
but please bear in mind this was written from the point of view of someone with
little to no experience in the topic and therefore try to keep any instructions at a
level where a new entrant can step in.
Leaflet Features
Adding a marker to our map
At some stage we will most likely want to add a marker to our map to pinpoint
something. Leaflet makes the process nice and easy by including
a marker function with several options;
In its most simple form the following is the full code to show a map with a marker;
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Map</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="leaflet.css" />
</head>
<body>
<script src="leaflet.js"></script>
<div id="map" style="width: 600px; height:
400px"></div>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var marker = L.marker([-41.29042, 174.78219])
.addTo(map);
</script>
</body>
</html>
The only difference between this code and the simple map code is the addition of a
single line at the bottom of the JavaScript portion;
Here our additional lines bind a popup to our marker using .bindPopup with the
text <b>Te Papa</b><br>Museum of New Zealand. (where the <b> tags will
make the text bold and the <br> tag will insert a line break). Then we open the
popup with .openPopup().
The end result is…
But wait! The coolness doesn’t end there. You can click on the marker and the
popup will alternately disappear and return. If you omit
the .openPopup() portion the popup won’t be open when your map loads, but if
you click on the marker it will open up.
Marker options
As well as the standard marker functions shown thus far there are several options
that can be utilised when displaying a marker. These are enabled by including an
array of the appropriate options and their desired values in a section contained in
curly braces after the latitude, longitude declaration. Below there is some sample
code for the marker function with three different options demonstrating use for a
boolean value (true / false) a string and a number.
Drag a marker
The draggable option is a boolean which is set to false by default, but when set
to true, the marker can be repositioned by clicking on it with the mouse and
moving it.
. A copy is also in the appendices and a copy of all the files that appear in the book
can be downloaded (in a zip file) when you download the book from Leanpub.
The full code of a live example of a map incorporating a marker with a popup,
draggability, a title and opacity are available from bl.ocks.org, GitHub and there is
a copy of this file called marker-map-complex.html that can be downloaded (in a
zip file) when you download the book from Leanpub. The online version at
bl.ocks.org and GitHub both use subresource integrity to support secure
CORS CORS (Cross Origin Resource Sharing) requests.
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Map</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="leaflet.css" />
</head>
<body>
<script src="leaflet.js"></script>
<div id="map" style="width: 600px; height:
400px"></div>
<script>
var planes = [
["7C6B07",-40.99497,174.50808],
["7C6B38",-41.30269,173.63696],
["7C6CA1",-41.49413,173.5421],
["7C6CA2",-40.98585,174.50659],
["C81D9D",-40.93163,173.81726],
["C82009",-41.5183,174.78081],
["C82081",-41.42079,173.5783],
["C820AB",-42.08414,173.96632],
["C820B6",-41.51285,173.53274]
];
var map = L.map('map').setView([-41.3058,
174.82082], 8);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
for (var i = 0; i < planes.length; i++) {
marker = new
L.marker([planes[i][1],planes[i][2]])
.bindPopup(planes[i][0])
.addTo(map);
}
</script>
</body>
</html>
var planes = [
["7C6B07",-40.99497,174.50808],
["7C6B38",-41.30269,173.63696],
["7C6CA1",-41.49413,173.5421],
["7C6CA2",-40.98585,174.50659],
["C81D9D",-40.93163,173.81726],
["C82009",-41.5183,174.78081],
["C82081",-41.42079,173.5783],
["C820AB",-42.08414,173.96632],
["C820B6",-41.51285,173.53274]
];
Secondly we include a for loop that contains our marker adding line.
In the for loop we go from 0 to the end of the planes array (planes.length).
For each row in our array, we add a marker where the latitude corresponds
to planes[i][1] (in the planes array at row i and column 1 (remembering that
the first column is 0)) and the longitude is planes[i][2]. We also add a popup to
our marker with the identifier (planes[i][0]) before adding each marker to the
map (.addTo(map)).
The following is the full code to show a simple map with a line drawn with 4 lines;
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="leaflet.css" />
</head>
<body>
<script src="leaflet.js"></script>
<div id="map" style="width: 600px; height:
400px"></div>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var polyline = L.polyline([
[-41.286, 174.796],
[-41.281, 174.786],
[-41.279, 174.776],
[-41.290, 174.775],
[-41.292, 174.788]]
).addTo(map);
</script>
</body>
</html>
The only difference between this code and the simple map code is the addition of
the var polyline = section at the bottom of the JavaScript portion;
Here we are defining a path going from one lat/long point to another. We declare a
variable with the L.polyline method and step through our points in the array.
Then we simply add that to our map by adding .addTo(map).
Obviously this hasn’t been set to follow any logical route :-).
The full code of a live example of a map incorporating a the polyline and options is
available online at bl.ocks.org or GitHub. A copy of the file (polyline-options-
map) can be downloaded (in a zip file) when you download the book from
Leanpub.
What we’re going to do is define the locations and appropriate attributions for two
different sets of tiles and then tell leaflet to place a control on the map that allows
us to switch. These different sets of tiles are referred to as ‘base layers’ and as such
only one can be visible at any one time.
The end result is a small icon in the top right hand corner that looks like this…
Layers icon
And when we hover over it with our mouse it changes to show the different tile
layers that we have defined for use.
There is no change to the HTML part of our code from the simple map example.
The full code for this example can be found here on GitHub and a working
example is here on bl.ocks.org. A copy of the file (layers-map.html) can be
downloaded (in a zip file) when you download the book from Leanpub.
The only change is in the JavaScript portion. and that looks like the following;
The first block of code sets up the links that we will use for attribution;
This just makes it easier to add later when juggling multiple layers.
The we declare the URLs for the tiles and the attributions to display;
var osmUrl =
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© ' + osmLink + ' Contributors',
otmUrl =
'http://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
otmAttrib = '© '+otmLink+' Contributors';
Again, by declaring these as variables, the process of defining the distinct layers is
simplified.
Declaring the layers like this is pretty handy since now we have a single variable
for each layer that has all the information associated with it that is required to
display the tiles for that layer.
The second last section of the code declares what our base layers are (there are
other sorts of layers, but we’ll get to that later) and gives them appropriate text to
display in the layers selection box.
var baseLayers = {
"OSM Mapnik": osmMap,
"Topogrophy": otmMap
};
Then the last line adds the control for the baseLayers to the map
L.control.layers(baseLayers).addTo(map);
For a fantastic way of finding different tile providers I can recommend heading to
the preview page set up here.
As I mentioned earlier, the full code for this example can be found here on
GitHub and a working example is here on bl.ocks.org. A copy of the file (layers-
map.html) can be downloaded (in a zip file) when you download the book from
Leanpub.
Overlays are treated in much the same way as base layers. In the sense that they are
declared and controlled using similar methods but Leaflet is clever enough to
recognise that as many overlays as desired can exist on an individual base layer.
What we aim to do is to add an overlay to one to our previous base layer switching
example. The end result will be the same icon in the top right corner of the map;
Layers icon
But this time when we move our mouse over it, it will present an option to select
‘Interesting places’.
Overlay selection
And when selected it will show a series of markers with a connecting line.
Overlay on a map
As with the base layer switching example, there is no change to the HTML part of
our code from the simple map example. The full code for this example can be
found here on GitHub and a working example is here on bl.ocks.org. A copy of the
file (layers-map-overlays.html) can be downloaded (in a zip file) when
you download the book from Leanpub.
The only change is in the JavaScript portion. and that looks like the following;
There are only really two differences between this block of script and that for the
base layers example.
The first is where we define what our overlay will be made up of.
The second change to our code is right at the end of the block of code.
var overlays = {
"Interesting places": coolPlaces
};
L.control.layers(baseLayers,overlays).addTo(map);
Here we declare our overlays (there is only one ( coolPlaces), but you can add as
many as you want) using var overlays = {<put overlays here>}. Then
we add overlays to our layers control so that it knows to include the layer in the
screen widget.
As stated earlier, the full code for this example can be found here on GitHub (and
there’s a copy in the appendices) an online example is here on bl.ocks.org and
copy of the file (layers-map-overlays.html) can be downloaded (in a zip file) when
you download the book from Leanpub.
Leaflet Plugins
Since the stated aim of leaflet.js is to provide a JavaScript library for drawing maps
that is simple, with good performance and usability, there are many features that
it could include which it doesn’t. The idea being that not everyone is going to use
those features, therefore there is no benefit to increasing the size of leaflet and
impacting performance.
At time of writing (December 2013) there are almost exactly 100 different plugins
covering a huge range of options.
Leaflet.draw
Leaflet.draw adds support for drawing and editing vectors and markers overlaid
onto Leaflet maps. Its driving force is Jacob Toye (a good Hamilton lad, so he gets
a special shout-out :-)).
What we will go over though is how to add Leaflet.draw to our simple base map
and how to configure some of the wide range of options.
Here’s what we are aiming to show in terms of the range of controls and options on
the left hand side of our map.
Leaflet.draw toolbar
And here’s some example objects produced with the plugin.
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
<link
rel="stylesheet"
href="http://leaflet.github.io/Leaflet.draw/leaflet.dra
w.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script
src="http://leaflet.github.io/Leaflet.draw/leaflet.draw
.js">
</script>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
drawnItems.addLayer(layer);
});
</script>
</body>
</html>
There are only three ‘blocks’ that have changed in the code from our simple map
example.
<link
rel="stylesheet"
href="http://leaflet.github.io/Leaflet.draw/leaflet.dra
w.css"
/>
(As with the leaflet.css file which is loaded before hand, I have taken some
small formatting liberties to make the code appear more readable on the page.)
This loads the file directly from the Leaflet.draw repository on GitHub, so if you
are loading from a local file you will need to adjust the path appropriately.
<script
src="http://leaflet.github.io/Leaflet.draw/leaflet.draw
.js">
</script>
Leaflet.draw exists as a separate block of JavaScript code and again, here we are
loading the file directly from the Leaflet.draw repository on GitHub (as per the
earlier advice, if you are loading from a local file you will need to adjust the path
appropriately).
The last change to the file is the block of code that runs and configures
Leaflet.draw.
Then the map.addLayer(drawnItems); line adds the layer with our drawn
items to the map.
Next we get to the first of the true Leaflet.draw commands when we initialize the
draw control and pass it the feature group of editable layers;
This is required when adding the edit toolbar and tells the Leaflet.draw plugin
which layer (drawnItems) should be editable. Then the controls are added to the
map (map.addControl(drawControl);).
This is alto the part of the code where you could store the information that
described the element in a database or similar.
Object colours
As our first change, if we use the simple example, all of the elements we generate
have the same colour, so lets change that first.
Leaflet.draw map with common colours
Changing the options is a simple matter of declaring them when we initialize the
draw controls by adding them as required by the documentation on the
Leaflet.draw GitHub page. For example in the following code snippet we have
added in the draw: option which in turn has options for each of the shapes. We
have entered the polygon: option which has it’s own options of which we have
added shapeOptions: as an option. And as if that wasn’t enough we select the
option for color: from this and finally declare it as purple.
This might seem slightly confusing, but it’s just a simple hierarchy which we can
flesh out by doing the same thing for each of the remaining shapes (polyline,
rectangle and circle). The code snippet would then look as follows;
var drawControl = new L.Control.Draw({
draw: {
polygon: {
shapeOptions: {
color: 'purple'
},
},
polyline: {
shapeOptions: {
color: 'red'
},
},
rect: {
shapeOptions: {
color: 'green'
},
},
circle: {
shapeOptions: {
color: 'steelblue'
},
},
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
Luckily there is an option that will provide a warning that this is happening while
drawing and will allow you to correct and carry on. The following screen shot
shows the results when trying to place a point that allows boundary lines to cross;
Leaflet.draw polygon with crossed lines
We can see that not only is a warning raised, but the shape colour changes.
polygon: {
shapeOptions: {
color: 'purple'
},
allowIntersection: false,
drawError: {
color: 'orange',
timeout: 1000
},
},
This has introduced the allowIntersection option and set it to false and
provided the drawError option with the instructions to change the colour of the
object to orange for 1000 milliseconds.
You can see from the screen shot that the area is in hectares, but we can set the
measurement units to not be metric (to show acres instead) by setting
the metric option to false. The code for the polygon now looks like this;
polygon: {
shapeOptions: {
color: 'purple'
},
allowIntersection: false,
drawError: {
color: 'orange',
timeout: 1000
},
showArea: true,
metric: false
},
This option will work with all the other drawing objects.
The code to set up an alternative icon duplicates is covered elsewhere, but consists
of the following;
Here we are using one of the markers (the green leaf) set up as part of the custom
icons tutorial on GitHub.
marker: {
icon: greenIcon
},
And here’s a pretty picture of the green marker.
This will place the toolbar in the top right hand corner of the map as follows;
Moving the Leaflet.draw toolbar
The other options for positioning are bottomright, bottomleft and the default
of topleft.
The full code and a live example of the use of the Leaflet.draw plugin with the
options described here in the appendices and is available online
at bl.ocks.org or GitHub. A copy of all the files that appear in the book can be
downloaded (in a zip file) when you download the book from Leanpub.
OSMGeocoder Search
The OSMGeocoder plugin adds a search facility to a leaflet map that uses the
OpenStreetMap tool ‘Nominatim’ to search for a location and provide a reverse
geolocation on the search term to pinpoint the position on the map.
The plugin was developed by ‘kartenkarsten’ and is hosted on GitHub where it can
be downloaded from.
<!DOCTYPE html>
<html>
<head>
<title>osmGeocoder Search Plugin for Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
<link
rel="stylesheet"
href="http://k4r573n.github.io/leaflet-control-
osm-geocoder/Control.OSMGe\
ocoder.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script
src="http://k4r573n.github.io/leaflet-control-
osm-geocoder/Control.OSMGeo\
coder.js">
</script>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var osmGeocoder = new L.Control.OSMGeocoder();
map.addControl(osmGeocoder);
</script>
</body>
</html>
There are only three ‘blocks’ that have changed in the code from our simple map
example.
<link
rel="stylesheet"
href="http://k4r573n.github.io/leaflet-control-osm-
geocoder/Control.OSMGeocod\
er.css"
/>
(Because of the length of the URL for the file, the formatting may make cutting
and pasting from the ebook problematic. For a more reliable snippet of
code, download the live version from GitHub)
This loads the file directly from the OSMGeocoder repository on GitHub, so if you
are loading from a local file you will need to adjust the path appropriately.
<script
src="http://k4r573n.github.io/leaflet-control-osm-
geocoder/Control.OSMGeocode\
r.js">
</script>
(Again because of the length of the URL for the file, the formatting may make
cutting and pasting from the ebook problematic. For a more reliable snippet of
code, download the live version from GitHub).
The last change to the file is the block of code that runs and configures
Leaflet.draw.
There is not a lot of additional code required to get this plugin up and running and
the following is what we see on the screen;
OSMGeocoder plugin
The only noticeable addition is a svelte magnifying glass in the top left hand
corner. If we hover our mouse over the magnifying glass a search box appears.
OSMGeocoder plugin
OSMGeocoder plugin
OSMGeocoder plugin
A copy of this file and all the files that appear in the book can be downloaded (in a
zip file) when you download the book from Leanpub
The plugin was developed by Mathieu Leplatre and is hosted on GitHub where it
can be downloaded from.
Leaflet.FileLayer code description
The following is a code listing that we will use to describe the required changes
from our simple-map.html example to enable Leaflet.FileLayer. There is also an
online version on bl.ocks.org and GitHub.
<!DOCTYPE html>
<html>
<head>
<title>LeafletFileLayer Plugin</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
<link
rel="stylesheet"
href="http://makinacorpus.github.io/Leaflet.FileLayer/F
ont-Awesome/css/fo\
nt-awesome.min.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script
src="http://makinacorpus.github.io/Leaflet.FileLayer/le
aflet.filelayer.js\
">
</script>
<script
src="http://makinacorpus.github.io/Leaflet.FileLayer/to
geojson/togeojson.\
js">
</script>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var style = {color:'red', opacity: 1.0,
fillOpacity: 1.0, weight: 2, clic\
kable: false};
L.Control.FileLayerLoad.LABEL = '<i class="fa fa-
folder-open"></i>';
L.Control.fileLayerLoad({
fitBounds: true,
layerOptions: {style: style,
pointToLayer: function (data,
latlng) {
return L.circleMarker(latlng,
{style: style});
}},
}).addTo(map);
</script>
</body>
</html>
There are three ‘blocks’ that have changed in the code from our simple map
example.
<link
rel="stylesheet"
href="http://makinacorpus.github.io/Leaflet.FileLayer/F
ont-Awesome/css/font-a\
wesome.min.css"
/>
(Because of the length of the URL for the file, the formatting may make cutting
and pasting from the ebook problematic. For a more reliable snippet of
code, download the live version from GitHub)
This loads the css file directly from the Leaflet.FileLayer repository on GitHub, so
if you are loading from a local file you will need to adjust the path appropriately.
The second is the block that loads the leaflet.filelayer.js script and an additional
script togeojson.js that was written by Tom MacWright to perform the internal
conversion of the GPX and KML traces to GeoJSON.
<script
src="http://makinacorpus.github.io/Leaflet.FileLayer/le
aflet.filelayer.js">
</script>
<script
src="http://makinacorpus.github.io/Leaflet.FileLayer/to
geojson/togeojson.js">
</script>
(Again because of the length of the URL for the file, the formatting may make
cutting and pasting from the ebook problematic. For a more reliable snippet of
code, download the live version from GitHub).
The last change to the file is the block of code that runs and configures
Leaflet.FileLayer.
The fist line (starting with var style =) sets the styles for the control and the
loaded gps traces. Then the icon to initiate the file opening process is declared
(L.Control.FileLayerLoad.LABEL = '<i class="fa fa-folder-
open"></i>';).
The script then sets the options for Leaflet.FileLayer. The first is
the fitBounds option which will present a loaded gps trace in a window that is
zoomed to show its full extent. The second is the layerOptions option which
will apply the styling to the trace based on our previously declared values (this
included the short pointToLayer function that makes circles from point values in
the traces).
So when we load our page we can see the folder icon in the top left hand corner.
Leaflet.FileLayer plugin
If we click on this folder we will be presented with a dialogue box where we can
select a file to load and when we do…
Our gps trace is automatically zoomed and panned to present our trace to its full
extent.
A copy of this file and a copy of all the files that appear in the book can be
downloaded (in a zip file) when you download the book from Leanpub
The example here represents seismic activity in July / August of 2013 in central
New Zealand, when they were unfortunate enough to experience a series of
earthquakes.
In areas of greater intensity and density, the heatmap produces a gradient that
varies from blue to red. This example shows a definite region of increased activity
over the time in question.
There are several variations on heatmap plugins for Leaflet. The one we are going
to examine is called Leaflet.heat. It is fairly new (at time of writing), and it builds
on a separate JavaScript library called simpleheat. Both of these have been
developed by Vladimir Agafonkin (yes, the developer of Leaflet), so they come
with a considerable pedigree.
Leaflet.heat code description
The following is a code listing that we will use to describe the required changes
from our simple-map.html example to enable Leaflet.heat. There is also an online
version on bl.ocks.org, GitHub and can be downloaded along with the data file (in
a zip file) when you download the book from Leanpub.
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map with Heatmap </title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script
src="http://leaflet.github.io/Leaflet.heat/dist/leaflet
-heat.js">
</script>
<script src="2013-earthquake.js"></script>
<script>
var map = L.map('map').setView([-41.5546,174.146],
10);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var heat = L.heatLayer(quakePoints,{
radius: 20,
blur: 15,
maxZoom: 17,
}).addTo(map);
</script>
</body>
</html>
We will be loading our data from a separate JavaScript file called 2013-
earthquake.js. The data has been sourced from New Zealand’s Geonet site over
a date range that covers a period of reasonable seismic activity in July / August
2013.
Loading the file from a separate JavaScript file is purely for conveniences sake and
the format of the data is as follows;
var quakePoints = [
[-41.5396,174.1242,1.7345],
[-38.8725,175.9561,2.6901],
[-41.8992,174.3117,4.6968],
[-41.7495,174.02,1.8642],
[-41.7008,174.0876,2.1629],
[-41.7371,174.0682,2.0408],
[-41.372,173.3502,2.7565],
[-41.7511,174.0623,2.4531],
[-41.7557,174.3391,2.1871],
[-41.6881,174.2726,3.1336],
[-41.7463,174.1194,2.7113],
[-41.6966,174.1238,2.4168],
...
];
There are only two ‘blocks’ that have changed in the code from our simple map
example.
<script
src="http://leaflet.github.io/Leaflet.heat/dist/leaflet
-heat.js">
</script>
<script src="2013-earthquake.js"></script>
The first link loads the Leaflet.heat plugin and the second loads our data
from 2013-earthquake.js.
The second change from our simple map example adds and configures our
heatmap layer;
var heat = L.heatLayer(quakePoints,{
radius: 20,
blur: 15,
maxZoom: 17,
}).addTo(map);
In this block we load the data as the variable quakePoints then set our radius,
blur and maxZoom values for the plugin.
Radii of points
As we zoom in and out of the map, the radii of the points remains constant on the
screen, but the representation that they create on the screen changes as there are
subsequently more or less points to generate an effect.
Radii of points zoomed in
Obviously this is something that you will want to consider as you plan how to set
the configuration options on your maps :-).
A copy of this file and a copy of all the files that appear in the book can be
downloaded (in a zip file) when you download the book from Leanpub
That’s a fine size for embedding somewhere in a page, but There will come a time
when you will want to make your map expand to fill the web page.
The astute reader will immediately notice that with our current size declaration
using fixed units (pixels) unless all browser windows were the same size we won’t
be able to accurately have a map the full size of the web page. It would be too large
or too small most of the time.
To remedy this we need to declare a map size that is referenced to the browser, not
a fixed unit.
We can do this by declaring our map size as a percentage of the browser window
in the <style> section using CSS.
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
<style>
body {
padding: 0;
margin: 0;
}
html, body, #map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © ' + mapLink,
maxZoom: 18,
}).addTo(map);
</script>
</body>
</html>
There are two differences between this example and the simple-map example.
<style>
body {
padding: 0;
margin: 0;
}
html, body, #map {
height: 100%;
width: 100%;
}
</style>
The body of the page is essentially the entire contents of the web page. You can
see in the HTML file that anything that gets drawn on the screen is contained in
the <body> tags. So our styling here ensures that there is
no padding or margin for our body in the browser and then we set the html,
the body and the element with the id map (which we declared in a <div>) to 100
percent of the height and width of the browser.
The only other change we need to make is to remove the fixed size declarations
that we had made in the div. So that line ends up looking like this;
<div id="map"></div>
The final result is a map that will fill a browser window no matter what shape you
make it.
For example;
Full Screen Map Vertical
Or even…
Full
Screen Map Vertical
Notice that there are no scroll bars and no borders. The contents of the page fit the
browser exactly.
The full code and a live example are available online at bl.ocks.org or GitHub. A a
copy of all the files that appear in the book can be downloaded (in a zip file) when
you download the book from Leanpub
If we use the example of a simple map that is going to display a line on the map
that represents a series of connected coordinates, we are going to have to load the
array that represents those coordinates at some point in the script. In the following
example we declare our array named planelatlong with a range of data;
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map with line</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
var map = L.map('map').setView([-41.3058,
174.82082], 12);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var polyline =
L.polyline(planelatlong).addTo(map);
</script>
</body>
</html>
… and then later in the script we access the data by adding it as a polyline with the
line;
The end result is a line on our map that looks like this;
I describe this technique as cheating because it’s just a matter of adding another
JavaScript (.js) file to the web page and loading the data in the added file.
Here is the web page (php file) that will display our map;
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map with line</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script
src="load-js-data.js">
</script>
<script>
var map = L.map('map').setView([-41.3058,
174.82082], 12);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var polyline =
L.polyline(planelatlong).addTo(map);
</script>
</body>
</html>
This is exactly the same as our example from earlier in the section except the
declared array (planelatlong) has been replaced by a piece of script that adds in
an additional piece of JavaScript code;
<script
src="load-js-data.js">
</script>
The file that is being loaded (load-js-data.js) contains our data. Here it is in
full;
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
The best way I have of thinking of this solution is to imagine that it is simply one
file that imports part of itself (in this case the data) from another file.
We’ll start with the assumption that we have created a database and populated it
with information. Now we need to work out how to extract a subset of that
information and how to do it in a format that is valid (won’t cause an error)
JavaScript.
What we’re going to do is to achieve the same result that we attained in the
previous section where we used a line in the main web file to pull in the data in the
format that we want.
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
In this example we are going to run a php script in the position where that data
would normally be placed and the php script will import the array declaration. Our
web file looks almost exactly the same as our ‘import with a JavaScript file’ effort
from the previous section, but in this case the importing line is different;
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
<?php include 'planelatlong.php'; ?>
var map = L.map('map').setView([-41.3058,
174.82082], 12);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var polyline =
L.polyline(planelatlong).addTo(map);
</script>
</body>
</html>
Hopefully you can see it there in the middle. It is the following line;
When the web file gets to that line it will run the script planelatlong.php and
that script will import our declared data array.
The database that we’re going to access is one that contains a range of values. Two
of which are the latitude and longitude of a plane in flight. The database is
organised as follows;
planedb
database
In our example we will pull out only the rows with the latitude and longitude and
return only them in a format that the script will recognise as follows;
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
<?php
$username = "planeuser";
$password = "planeuser";
$host = "localhost";
$database="planedb";
$server = mysql_connect($host, $username, $password);
$connection = mysql_select_db($database, $server);
$myquery = "
SELECT `lat`, `long` FROM `test01`
WHERE `lat` <> 0
";
$query = mysql_query($myquery);
if ( ! $query ) {
echo mysql_error();
die;
}
$data = array();
echo "var planelatlong = [";
for ($x = 0; $x < mysql_num_rows($query); $x++) {
$data[] = mysql_fetch_assoc($query);
echo
"[",$data[$x]['lat'],",",$data[$x]['long'],"]";
if ($x <= (mysql_num_rows($query)-2) ) {
echo ",";
}
}
echo "];";
mysql_close($server);
?>
It’s pretty short, but it packs a punch. Let’s go through it and see what it does.
The <?php line at the start and the ?> line at the end form the wrappers that allow
the requesting page to recognise the contents as php and to execute the code rather
than downloading it for display.
$username = "planeuser";
$password = "planeuser";
$host = "localhost";
$database="planedb";
These are configuration details for the MySQL database. There’s user and
password (don’t worry, because the script isn’t returned to the browser, the
browser doesn’t get to see the password). There’s the host location of our database
(in this case it’s local, but if it was on a remote server, we would just include its
address) and there’s the database we’re going to access.
Then we have our query in a form that we can paste into the right spot and it’s easy
to use.
$myquery = "
SELECT `lat`, `long` FROM `test01`
WHERE `lat` <> 0
";
I have it like this so all I need to do to change the query I use is to paste it into the
middle line there between the speech-marks and I’m done. It’s just a convenience
thing.
The query itself is a fairly simple affair. We return lat and long from our table
called test01 but only if the lat value on the row is not equal to zero
(WHERE lat <> 0) (this will allow us to ignore the rows which do not contain a
latitude (and typically a longitude in this case) value.
The query is then run against the database with the following command;
$query = mysql_query($myquery);
…and then we check to see if it was successful. If it wasn’t, we output the MySQL
error code;
if ( ! $query ) {
echo mysql_error();
die;
}
$data = array();
Now we begin to echo, or print out the values for our piece of code that we expect
to have inserted into our leaflet.js code;
First of all we print out the start of the declaration of our array;
Then we start up a for loop that goes from 0 ($x = 0;) to the number of returned
rows in our query ($x < mysql_num_rows($query)) one step at a time ($x++)
$data[] = mysql_fetch_assoc($query);
… and then echo each row as a latitude and longitude value enclosed by square
brackets and separated by a comma;
echo
"[",$data[$x]['lat'],",",$data[$x]['long'],"]";
[-41.31825,174.80768]
Because we need to separate our lat/long values that are enclosed with brackets
from each other with a comma like this…
[-41.31825,174.80768],
[-41.31606,174.80774]
… but we don’t want a comma after the last lat/long pair. We can use
an if statement to evaluate each row to see if it’s the last and print a comma if its
not;
After the our rows have finished being produced by our loop we need to close it off
with another square bracket;
echo "];";
And all that remains is to close the connection to our MySQL database;
mysql_close($server);
That’s it.
We can actually test the script directly by opening the file in our browser.
If you navigate using your browser to this file and click on it to run it (WAMP
should be your friend here again) this is what you should see printed out on your
screen (at least the information, but probably not formatted as nicely);
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
It looks a bit unusual on the printed page, but it’s bread and butter for JavaScript.
I have included the planelatlong.php file with the files available when you
download the Leaflet Tips and Tricks book.
To reiterate, our main web page file that is presenting our map gets to the point in
its script where it is going to get the data. At this point it calls a script
called planelatlong.php and this script gets the data from a MySQL database,
formats it properly and includes it in the web page’s JavaScript.
d3.js Overview
d3.js is “a JavaScript library for manipulating documents based on data”.
D3 is all about helping you to take information and make it more accessible to
others via a web browser.
Why use d3.js when leaflet.js can include objects on maps too?
There is a difference in the scope of flexibility between how d3.js and leaflet.js can
manipulate added objects (think markers and ploygons). D3.js predominantly
focusses on vector based graphics when drawing maps and leaflet.js leverages the
huge range of bitmap based map tiles that are available for use around the world.
Both bitmap and vector based solutions have strengths and weaknesses depending
on the application. Combining both allows the use of the best of both worlds.
The rectangle will be bound to a set of geographic coordinates so that as the map is
panned and zoomed the rectangle will shrink and grow. For example the following
diagram shows a rectangle (made with d3.js ) superimposed over a leaflet.js map;
Rectangular d3 area on leaflet map
For an excellent example of this please visit Mike Bostock’s tutorial where he
demonstrates superimposing a map of the United States separated by state (which
react individually to the mouse being hovered over them). My following
explanation is a humble derivation of his code.
Speaking of code, here is a full listing of the code that we will be using;
<!DOCTYPE html>
<html>
<head>
<title>Leaflet and D3 Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://d3js.org/d3.v3.min.js"></script>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
var map = L.map('map').setView([-41.2858,
174.7868], 13);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
// Add an SVG element to Leaflet’s overlay
pane
var svg =
d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class",
"leaflet-zoom-hide");
d3.json("rectangle.json", function(geoShape)
{
// create a d3.geo.path to convert GeoJSON
to SVG
var transform = d3.geo.transform({point:
projectPoint}),
path = d3.geo.path().projection(transform);
// create path elements for each of the
features
d3_features = g.selectAll("path")
.data(geoShape.features)
.enter().append("path");
map.on("viewreset", reset);
reset();
// fit the SVG element to leaflet's map
layer
function reset() {
bounds = path.bounds(geoShape);
var topLeft = bounds[0],
bottomRight = bounds[1];
svg .attr("width", bottomRight[0] -
topLeft[0])
.attr("height", bottomRight[1] -
topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g .attr("transform", "translate(" + -
topLeft[0] + ","
+ -topLeft[1]
+ ")");
// initialize the path data
d3_features.attr("d", path)
.style("fill-opacity", 0.7)
.attr('fill','blue');
}
// Use Leaflet to implement a D3 geometric
transformation.
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new
L.LatLng(y, x));
this.stream.point(point.x, point.y);
}
})
</script>
</body>
</html>
There is also an associated json data file (called rectangle.json) that has the
following contents;
{
"type": "FeatureCollection",
"features": [ {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [ [
[ 174.78, -41.29 ],
[ 174.79, -41.29 ],
[ 174.79, -41.28 ],
[ 174.78, -41.28 ],
[ 174.78, -41.29 ]
] ]
}
}
]
}
The full code and a live example are available online at bl.ocks.org or GitHub.
They are also available as the files ‘leaflet-d3-combined.html’ and ‘rectangle.json’
as a separate download with Leaflet Tips and Tricks. A a copy of all the files that
appear in the book can be downloaded (in a zip file) when you download the book
from Leanpub
While I will explain the code below, please be aware that I will gloss over some of
the simpler sections that are covered in other sections of either books and will
instead focus on the portions that are important to understand the combination of
D3 and Leaflet.
Our code begins by setting up the html document in a fairly standard way.
<!DOCTYPE html>
<html>
<head>
<title>Leaflet and D3 Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://d3js.org/d3.v3.min.js"></script>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
Here we’re getting some css styling and loading our leaflet.js / d3.js libraries. The
only configuration item is where we set up the size of the map (in
the <style> section and as part of the map div).
Then we break into the JavaScript code. The first thing we do is to project our
Leaflet map;
This is exactly the same as we have done in any of the simple map explanations
in Leaflet Tips and Tricks and in this case we are using the OpenStreetMap tiles.
The first part of that involves making sure that Leaflet and D3 are synchronised in
the view that they’re projecting. This synchronisation needs to occur in zooming
and panning so we add an SVG element to Leaflet’s overlayPlane
var svg =
d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class", "leaflet-zoom-
hide");
Then we add a g element that ensures that the SVG element and the Leaflet layer
have the same common point of reference. Otherwise when they zoomed and
panned it could be offset. The leaflet-zoom-hide affects the presentation of the
map when zooming. Without it the underlying map zooms to a new size, but the
d3.js elements remain as they are until the zoom effect has taken place and then
they adjust. It still works fine, but it ‘looks’ odd.
d3.json("rectangle.json", function(geoShape) {
This is pretty standard fare for d3.js but it’s worth being mindful that while the
type of data file is .json this is a GeoJSON file and they have particular features
(literally) that allow them to do their magic. There is a good explanation of how
they are structured at geojson.org for those who are unfamiliar with the
differences.
Using our data we need to ensure that it is correctly transformed from our
latitude/longitude coordinates as supplied to coordinates on the screen. We do this
by implementing d3’s geographic transformation features (d3.geo).
Here the path that we want to create in SVG is generated from the points that are
supplied from the data file which are converted by the
function projectPoint This function (which is placed at the end of the file) takes
our latitude and longitudes and transforms them to screen (layer) coordinates.
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y,
x));
this.stream.point(point.x, point.y);
}
With the transformations now all taken care of we can generate our path in the
traditional d3.js way and append it to our g group.
d3_features = g.selectAll("path")
.data(geoShape.features)
.enter().append("path");
The last ‘main’ part of our JavaScript makes sure that when our view of what
we’re looking at changes (we zoom or pan) that our d3 elements change as well;
map.on("viewreset", reset);
reset();
Obviously when our view changes we call the function reset. It’s the job of
the reset function to ensure that whatever the leaflet layer does, the SVG (d3.js)
layer follows;
function reset() {
bounds = path.bounds(geoShape);
var topLeft = bounds[0],
bottomRight = bounds[1];
svg .attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g .attr("transform", "translate(" + -topLeft[0] +
","
+ -
topLeft[1] + ")");
// initialize the path data
d3_features.attr("d", path)
.style("fill-opacity", 0.7)
.attr('fill','blue');
}
The end result being a fine combination of leaflet.js map and ds.js element;
Rectangular d3 area on leaflet map
When we zoom out of the map, those circles remain over the geographic location,
but the same size on the screen.
Zoomed d3.js circles fixed in geographic location on leaflet map but constant size
You may (justifiably) ask yourself why we would want to do this with d3.js when
Leaflet could do the same job with a marker? The answer is that as cool as
leaflet.js’s markers are, d3 elements have a wider range of features that make their
use advantageous in some situations. For instance if you want to animate or rotate
the icons or dynamically adjust some of their attributes, d3.js would have a greater
scope for adjustments.
<!DOCTYPE html>
<html>
<head>
<title>d3.js with leaflet.js</title>
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script type="text/javascript">
var map = L.map('map').setView([-41.2858,
174.7868], 13);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
// Initialize the SVG layer
map._initPathRoot()
// We pick up the SVG from the map object
var svg = d3.select("#map").select("svg"),
g = svg.append("g");
d3.json("circles.json", function(collection) {
// Add a LatLng object to each item in the
dataset
collection.objects.forEach(function(d) {
d.LatLng = new
L.LatLng(d.circle.coordinates[0],
d.circle.coordinates[1])
})
var feature = g.selectAll("circle")
.data(collection.objects)
.enter().append("circle")
.style("stroke", "black")
.style("opacity", .6)
.style("fill", "red")
.attr("r", 20);
map.on("viewreset", update);
update();
function update() {
feature.attr("transform",
function(d) {
return "translate("+
map.latLngToLayerPoint(d.LatLng).x +","+
map.latLngToLayerPoint(d.LatLng).y +")";
}
)
}
})
</script>
</body>
</html>
There is also an associated json data file (called circles.json) that has the
following contents;
{"objects":[
{"circle":{"coordinates":[-41.28,174.77]}},
{"circle":{"coordinates":[-41.29,174.76]}},
{"circle":{"coordinates":[-41.30,174.79]}},
{"circle":{"coordinates":[-41.27,174.80]}},
{"circle":{"coordinates":[-41.29,174.78]}}
]}
The full code and a live example are available online at bl.ocks.org or GitHub.
They are also available as the files ‘leaflet-d3-linked.html’ and ‘circles.json’ as a
separate download with Leaflet Tips and Tricks. A a copy of all the files that
appear in the book can be downloaded (in a zip file) when you download the book
from Leanpub
While I will explain the code below, as with the previous example (which is
similar, but different) please be aware that I will gloss over some of the simpler
sections that are covered in other sections of either books and will instead focus on
the portions that are important to understand the combination of D3 and Leaflet.
Our code begins by setting up the html document in a fairly standard way.
<!DOCTYPE html>
<html>
<head>
<title>d3.js with leaflet.js</title>
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
Here we’re getting some css styling and loading our leaflet.js / d3.js libraries. The
only configuration item is where we set up the size of the map (in
the <div> section and as part of the map div).
Then we break into the JavaScript code. The first thing we do is to project our
Leaflet map;
This is exactly the same as we have done in any of the simple map explanations
in Leaflet Tips and Tricks and in this case we are using the OpenStreetMap tiles.
Then we select the svg layer and append a g element to give a common reference
point g = svg.append("g").
Then we load the json file with the coordinates for the circles;
d3.json("circles.json", function(collection) {
Then for each of the coordinates in the objects section of the json data we
declare a new latitude / longitude pair from the associated coordinates;
collection.objects.forEach(function(d) {
d.LatLng = new L.LatLng(d.circle.coordinates[0],
d.circle.coordinates[1])
})
Then we use a simple d3.js routine to add and place our circles based on the
coordinates of each of our objects.
We declare each as a feature and add a bit of styling just to make them stand out.
The last ‘main’ part of our JavaScript makes sure that when our view of what
we’re looking at changes (we zoom or pan) that our d3 elements change as well;
map.on("viewreset", update);
update();
Obviously when our view changes we call the function update. It’s the job of
the update function to ensure that whenever the leaflet layer moves, the SVG
layer with the d3.js elements follows and the points that designate the locations of
those objects move appropriately;
function update() {
feature.attr("transform",
function(d) {
return "translate("+
map.latLngToLayerPoint(d.LatLng).x +","+
map.latLngToLayerPoint(d.LatLng).y +")";
}
)
}
Here we are using the transform function on each feature to adjust the
coordinates on our LatLng coordinates. We only need to adjust our coordinates
since the size, shape, rotation and any other attribute or style is dictated by the
objects themselves.
d3.js circles fixed in geographic location on leaflet map but constant size
Tile servers that can be used with
Leaflet
A tile server is a source of the tiles that leaflet.js uses to present a map. Because
there are many unique requirements for maps there are a large number of variations
of tile servers that apply different formatting and styling to the geographic
information.
In this chapter we will present the different services available and the different
requirements for use in terms of the URL template, terms of use and attribution
where appropriate.
URL Template
The URL template is the format required when specifying the link to the tiles on
the server. Typically this will be in the form of a server name (which may have
sub-domains) followed by the zoom level and then x/y values for the tiles. For
example, the following is the URL template for the standard OSM server.
http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
Usage Policy
Because there is an overhead involved in generating and providing tiles to make
maps with, it is expected (and entirely reasonable) that providers will have a usage
policy to avoid placing an undue strain on their equipment and bandwidth.
Attribution
Attribution is about providing credit where credit is due and acknowledging the
copyright of the originator of a work. It is significant effort to style and produce
map tiles and that should be recognised and noted appropriately. Typically this
would be in the form of a note in the bottom right hand corner of the map being
presented. Some of the attribution requirements might sound a little formal, but
they need to be that way so that their message is clear in a legal sense. To ordinary
people, we need to recognise that we are using a work created by someone else and
that we make sure we recognise that appropriately :-).
Open Street Map OSM Mapnik
This is the standard tile server in use for Open Street Map.
URL Template
http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
Usage policy
The best place to view the detail on the usage policy for Open Street Maps tiles is
from their tile usage policy wiki page. The main concern with usage is the load
placed on their resources which are finite considering their position as a volunteer
run service. So be gentle and when in doubt, check out the wiki.
Attribution
Open Street Map provides open data, licensed under the Open Data Commons
Open Database License (ODbL). The cartography in their map tiles is licensed
under the Creative Commons Attribution-ShareAlike 2.0 license (CC BY-SA).
They require that you use the credit “© OpenStreetMap contributors” and that the
cartography is licensed as CC BY-SA. You may do this by linking to the copyright
page at http://www.openstreetmap.org/copyright.
Usage example
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
OSM Mapnik tile server map
URL Template
http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png
Usage policy
The best place to view the detail on the usage policy for Open Street Maps tiles is
from their tile usage policy wiki page. The main concern with usage is the load
placed on their resources which are finite considering their position as a volunteer
run service. So be gentle and when in doubt, check out the wiki.
Attribution
Open Street Map provides open data, licensed under the Open Data Commons
Open Database License (ODbL). The cartography in their map tiles is licensed
under the Creative Commons Attribution-ShareAlike 2.0 license (CC BY-SA).
They require that you use the credit “© OpenStreetMap contributors” and that the
cartography is licensed as CC BY-SA. You may do this by linking to the copyright
page at http://www.openstreetmap.org/copyright.
Usage example
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.www.toolserver.org/tiles/bw-
mapnik/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + ' Contributors',
maxZoom: 18,
}).addTo(map);
Usage policy
There are some simple guidelines on the Thunderforest terms and conditions page.
The main concern with usage is the load placed on resources. So be gentle.
Attribution
Thunderforest provides open data, under a Creative Commons licence, specifically
CC-BY-SA 2.0. The full details are available on their terms and conditions page.
Attribution must be given to both “Thunderforest” and “OpenStreetMap
contributors”. Users of your map must have a working link to
www.thunderforest.com.
Usage example
mapLink = '<a
href="http://openstreetmap.org">OpenStreetMap</a>';
ocmlink = '<a
href="http://thunderforest.com/">Thunderforest</a>';
L.tileLayer(
'http://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.pn
g', {
attribution: '© '+mapLink+' Contributors &
'+ocmlink,
maxZoom: 18,
}).addTo(map);
Open Cycle Map tile server map
Outdoors
The ‘Outdoors’ set of map tiles is distributed by the good folks
at Thunderforest who brought you OpenCycleMap. The tiles are aimed towards
outdoor enthusiasts - for hiking, skiing and other activities. They are based on data
from the OpenStreetMap project.
URL Template
http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png
Usage policy
There are some simple guidelines on the Thunderforest terms and conditions page.
The main concern with usage is the load placed on resources. So be gentle.
Attribution
Thunderforest provides open data, under a Creative Commons licence, specifically
CC-BY-SA 2.0. The full details are available on their terms and conditions page.
Attribution must be given to both “Thunderforest” and “OpenStreetMap
contributors”. Users of your map must have a working link to
www.thunderforest.com.
Usage example
mapLink = '<a
href="http://openstreetmap.org">OpenStreetMap</a>';
outlink = '<a
href="http://thunderforest.com/">Thunderforest</a>';
L.tileLayer(
'http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}
.png', {
attribution: '© '+mapLink+' Contributors &
'+outlink,
maxZoom: 18,
}).addTo(map);
Transport
The ‘Transport’ set of map tiles is distributed by the good folks
at Thunderforest who brought you OpenCycleMap. The tiles are designed to show
a high level of detail of available public transport. They are based on data from
the OpenStreetMap project.
URL Template
http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png
Usage policy
There are some simple guidelines on the Thunderforest terms and conditions page.
The main concern with usage is the load placed on resources. So be gentle.
Attribution
Thunderforest provides open data, under a Creative Commons licence, specifically
CC-BY-SA 2.0. The full details are available on their terms and conditions page.
Attribution must be given to both “Thunderforest” and “OpenStreetMap
contributors”. Users of your map must have a working link to
www.thunderforest.com.
Usage example
mapLink = '<a
href="http://openstreetmap.org">OpenStreetMap</a>';
translink = '<a
href="http://thunderforest.com/">Thunderforest</a>';
L.tileLayer(
'http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y
}.png', {
attribution: '© '+mapLink+' Contributors &
'+translink,
maxZoom: 18,
}).addTo(map);
Transport tile server map
Landscape
The ‘Landscape’ set of map tiles is distributed by the good folks
at Thunderforest who brought you OpenCycleMap. The tiles have a global style
focussed on information about the natural world - ideal for rural context. They are
based on data from the OpenStreetMap project.
URL Template
http://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png
Usage policy
There are some simple guidelines on the Thunderforest terms and conditions page.
The main concern with usage is the load placed on resources. So be gentle.
Attribution
Thunderforest provides open data, under a Creative Commons licence, specifically
CC-BY-SA 2.0. The full details are available on their terms and conditions page.
Attribution must be given to both “Thunderforest” and “OpenStreetMap
contributors”. Users of your map must have a working link to
www.thunderforest.com.
Usage example
mapLink = '<a
href="http://openstreetmap.org">OpenStreetMap</a>';
landlink = '<a
href="http://thunderforest.com/">Thunderforest</a>';
L.tileLayer(
'http://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y
}.png', {
attribution: '© '+mapLink+' Contributors &
'+landlink,
maxZoom: 18,
}).addTo(map);
URL Template
http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png
Usage policy
There are some generalised direction (and lots of other good stuff) on
the MapQuest-OSM Tiles + MapQuest Open Aerial Tiles page. They are all very
reasonable and sensible and are concerned with making sure that the load on the
service is not unduly onerous. There is also a link to their official ‘Terms of use’
page which is a more formal wording of their common sense instructions.
Attribution
If using the MapQuest Open Aerial Tiles, attribution is required to read; “Portions
Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service
Agency” (which is quite long and on the example picture used later in the section
results in a serious word-wrapping). Additionally they ask that “Tiles Courtesy of
MapQuest” be placed on the map along with their logo and a link from the
‘MapQuest’ portion that goes to ‘http://www.mapquest.com/’.
Usage example
This usage example is slightly more complex in that it involves a slightly different
approach to including the subdomains. Leaflet will normally operate with
subdomains of the form a, b and c, but in this example we
find otile1, otile2, otile3 and otile4. To accommodate this
the otile portion is included in the URL and the variable parts of the subdomain
are declared in a separate option called subdomains.
mapquestLink = '<a
href="http://www.mapquest.com//">MapQuest</a>';
mapquestPic = '<img
src="http://developer.mapquest.com/content/osm/mq_logo.
png">';
L.tileLayer(
'http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.
png', {
attribution: 'Portions Courtesy NASA/JPL-Caltech and
U.S. Depart. of Agricult\
ure, Farm Service Agency. Tiles courtesy of
'+mapquestLink+mapquestPic,
maxZoom: 18,
subdomains: '1234',
}).addTo(map);
MapQuest-OSM
The ‘MapQuest-OSM’ tiles are a MapQuest styled variation on the OSM dataset.
The tiles are distributed by the good folks at MapQuest who also distribute a set of
OSM tiles called MapQuest Open Aerial with satellite imagery.
URL Template
http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png
Usage policy
There are some generalised direction (and lots of other good stuff) on
the MapQuest-OSM Tiles + MapQuest Open Aerial Tiles page. They are all very
reasonable and sensible and are concerned with making sure that the load on the
service is not unduly onerous. There is also a link to their official ‘Terms of use’
page which is a more formal wording of their common sense instructions.
Attribution
If using the MapQuest-OSM tiles, appropriate copyright attribution is required to
OpenStreetMap. Additionally they ask that “Tiles Courtesy of MapQuest” be
placed on the map along with their logo and a link from the ‘MapQuest’ portion
that goes to ‘http://www.mapquest.com/’.
Usage example
This usage example is slightly more complex in that it involves a slightly different
approach to including the subdomains. Leaflet will normally operate with
subdomains of the form a, b and c, but in this example we
find otile1, otile2, otile3 and otile4. To accommodate this
the otile portion is included in the URL and the variable parts of the subdomain
are declared in a separate option called subdomains.
mapLink = '<a
href="http://openstreetmap.org">OpenStreetMap</a>';
mapquestLink = '<a
href="http://www.mapquest.com//">MapQuest</a>';
mapquestPic = '<img
src="http://developer.mapquest.com/content/osm/mq_logo.
png">';
L.tileLayer(
'http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.
png', {
attribution: '© '+mapLink+'. Tiles courtesy of
'+mapquestLink+mapquestPi\
c,
maxZoom: 18,
subdomains: '1234',
}).addTo(map);
MapQuest-OSM map
Stamen.Watercolor
The ‘Stamen.Watercolor’ tiles are a beautiful watercolor themed series of maps
based on (I believe) OpenStreetMap data.
The tiles are distributed by the good folks at Stamen who also support a mapping
solution / thingy called Map Stack and do some astonishing design work.
URL Template
http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg
Usage policy
I couldn’t locate any official guidance on usage, so I’m going to make the
assumption that any use should fall into the ‘reasonable’ category and advise that if
you are thinking of using their maps for anything remotely ‘substantial’ or
commercial that you contact them.
Attribution
If using the Stamen.Watercolor tiles, appropriate copyright attribution is required
to OpenStreetMap. Additionally in spite of not finding any official guidance on
their web site, it would seem appropriate (at a minimum) that recognition of the
source as ‘Stamen Design’ with a link would be required. Again, if you are
considering using their tiles for any kind of commercial project, you should contact
them.
Usage example
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
wholink =
'<a href="http://stamen.com">Stamen
Design</a>';
L.tileLayer(
'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg'
, {
attribution: '© '+mapLink+' Contributors
& '+wholink,
maxZoom: 18,
}).addTo(map);
Stamen.Watercolor map
The tiles are distributed by the good folks at Esri who also support a range of other
excellent tile sets.
URL Template
The following URL template may suffer from word wrapping in your electronic
publication format. It may therefore be better to get a more accurate copy from an
online version which can be found here in Github or from the visual version
on bl.ocks.org.
http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServe
r/tile/\
{z}/{y}/{x}
Usage policy
Esri has a full terms of use statement here and a human readable version here. It
boils down to making sure that you understand the typical usage of the tiles as well
and the organisation that is using them (there are different conditions for
Government, Education, and NGO users). There are also good common sense rules
on bulk downloading and redistribution (don’t do it).
Attribution
Because there is a wide range of imagery used in the tile set, there are a lot of
organisations that require attribution. This includes Esri, DigitalGlobe, GeoEye, i-
cubed, USDA FSA, USGS, AEX, Getmapping, Aerogrid, IGN, IGP, swisstopo,
and the GIS User Community. Of course if you’re going to make a small map, that
list is going to wrap at the bottom (as in the example picture below), so if you
don’t want that, you will want to come up with an alternative arrangement.
Usage example
The following script may suffer from word wrapping in your electronic publication
format. It may therefore be better to get a more accurate copy from an online
version which can be found here in Github or from the visual version
on bl.ocks.org.
mapLink =
'<a href="http://www.esri.com/">Esri</a>';
wholink =
'i-cubed, USDA, USGS, AEX, GeoEye, Getmapping,
Aerogrid, IGN, IGP, UPR-EGP, and\
the GIS User Community';
L.tileLayer(
'http://server.arcgisonline.com/ArcGIS/rest/services/Wo
rld_Imagery/MapServer/ti\
le/{z}/{y}/{x}', {
attribution: '© '+mapLink+', '+wholink,
maxZoom: 18,
}).addTo(map);
A SVG at
200%
Doubling again (x 8) and we can see that the text ‘James’ is actually composed of a
fill colour and a border.
A
SVG at 800%
Doubling again for the last time (x 16) everything still retains it’s clear sharp
edges.
A SVG at 1600%
Bitmap graphics
A bitmap (or raster) image is one that is composed of lots of discrete individual
dots (let’s call them pixels) which, when joined together (and zoomed out a bit)
give the impression of an image. Bitmaps can be saved in a wide range of formats
depending on users requirements including compression, colour depth,
transparency and a host of other attributes. Typically they can be identified by the
file suffix .jpg, .png or .bmp (and there are an equally large number of other
suffixes). This is what will be presented by by most of the major online map
service providers such as Google, Microsoft, Open Street Map and others when a
map appears in your browser.
If we use the same image as demonstrated in the description of vectors, and look at
a screen shot (and it’s important to remember that this is a screen shot) of the
image we see a picture that looks fairly benign.
However, as we enlarge the image by doubling it’s size (x 2) we begin to see some
rough edges appear.
A bitmap at
200%
A
bitmap at 800%
Doubling again for the last time (x 16) and the pixels are plainly evident.
A
bitmap at 1600%
To break a picture of a map up into small manageable sections (tiles) the tile server
that produces the tiles will distinguish between different levels of zoom and for
each level they will have a different set of tiles that correspond to different
locations on the map. Since the (almost universal) standard for tile size is 256 x
256 pixels, at zoom level 0 the entire world is encapsulated in a single 256 x 256
pixel tile.
With every subsequent increase in zoom level there is a doubling of the resolution
available and therefore an increase in the number of tiles by a factor of four.
Etc, etc.
I’m sure you can appreciate that each increases in zoom level has the consequence
of substantially increasing the number of tiles required. When we get to zoom level
18 there would be a requirement to have over 68 billion tiles (actually
68,719,476,736). The good news is that at the higher zoom levels there are a
significant number of tiles that show nothing of interest ( I would be interested in
knowing how many 256 x 256 tiles of blue sea there would be) and because tiles
are only rendered when someone actually views them, the number of tiles that are
produced at the higher zoom levels is a lot less than the theoretical number
required. In fact as of 2011 there were only just over 617 million tiles at zoom
level 18 rendered meaning that less than 1% of the potential viewable tiles at that
level had ever been requested.
Calculations on the Open Street Map wiki give the theoretical volume of data
required if all tiles up to zoom level 18 were rendered as 54 Terabytes of storage.
But by judicious use of rendering only those required tiles, they report the volume
required for storage as of January 2012 as just over 1.2 Terabytes.
How are the tiles on a map server organised?
There are a few different ways that this section could be titled. It could be “What
are the names of the pictures used for tiles?” or “How does leaflet.js know what
tiles to load?” and I’m sure that there are other variations.
We’ve established that maps are broken down by zoom levels and varying
numbers of tiles for these zoom levels. In order for leaflet.js to use these tiles in the
right way there needs to be a pattern that can be followed to make sure that the
correct ones get from the tile server to the client’s (that’s you) machine.
If you’re generally familiar with the URL templates used for including tile servers
to be used with leaflet.js, you will recall that they are formatted something like;
http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
The {z}/{x}/{y} portion of the path to the png file is the description of how the
files are stored.
For example, the lowest zoom level (the one with the greatest view of area per tile)
is stored as 0/0/0.png.
If you’re wondering what level we’re going to approach this at, let me reassure (or horrify)
you that it will be in the same vein as the rest of this book. I am no expert in MySQL
databases, but through a bit of trial and error I have been able to achieve a small measure of
success. Hopefully the explanation is sufficient for beginners like myself and doesn’t offend
any best practices :-).
phpMyAdmin
I’m not one to dwell on the command line for too long if it can be avoided (sorry).
So in this section you’ll see me delving into a really neat program for managing
your MySQL database called phpMyAdmin
(http://www.phpmyadmin.net/home_page/index.php).
As the name would suggest, it’s been written in PHP and as we know, that’s a sign
that we’re talking about a web based application. In this case phpMyAdmin is
intended to allow a wide range of administrative operations with MySQL databases
via a web browser. You can find a huge amount of information about it on the web
as it is a freely available robust platform that has been around for well over a
decade.
If you have followed my suggestion earlier in the book to install WAMP or you
have phpMyAdmin installed already you’re in luck. If not, I’m afraid that I won’t
be able to provide any guidance on its installation. I just don’t have the experience
to provide that level of support.
Clicking on this icon will provide you with a range of options, including opening
phpMyAdmin.
Opening phpMyAdmin
Go ahead and do this and the phpMyAdmin page will open in your browser.
The page you’re presented with has a range of tabs, and we want to select the
‘Databases’ tab.
The Databases tab
From here we can create ourselves a new database simply by giving it a name and
selecting ‘Create’. I will create one called ‘homedb’.
On the panel on the left hand side of the screen is our new database. Go on and
click on it.
Cool, now we get to create a table. What’s a table? Didn’t we create our database
already?
Databases and Tables
Ahh yes… Think of databases as large collections of data (yes, I can smell the irony).
Databases can have a wide range of different information stored in them, but sometimes the
data isn’t strictly connected. For instance, a business might want to store its inventory and
personnel records in a database. Trying to mash all that together would be a bit of a
nightmare to manage. Instead, we can create two different tables of information. Think of a
table as a spreadsheet with rows of data for specific columns. If we want to connect the data
at some point we can do that via the process of querying the database.
Create a table
I’ve chosen data2 as a name since we will put the same data as we have in a file
called data2.csv. That’s why there are three columns for the date, close and open
columns that we have in the data2.csv file.
So, after clicking on the ‘Go’ button, I get the following screen where I get to enter
all the pertinent details about what I will have in my table.
I’m keeping it really simple by setting the ‘date’ column to be plain text (It could
be set as a ‘DATE’ format, but we’ll keep it simple for the time being), and the two
numeric columns to be decimals with 8 digits overall and 2 of those places for the
digits to the right of the decimal point.
The selection of the most efficient data type to maximise space or speed is something of an
obsession (as it sometimes needs to be) where databases are large and need to have fast
access times, but in this case we’re more concerned with getting a result than perfection.
Once entered, you can scroll down to the bottom of that window and select the
‘Save’ button.
Cool, now you are presented with your table (click on the table name in the left
hand panel) and the details of it in the main panel.
Ah Ha! Data!
In the vein of “Here’s one I prepared earlier”, what we will do is import a csv
(Comma Separated Value) file into our database. To do this I prepared our
data2.csv file by removing the header line (with date, close and open on it), so it
looks like this;
1-May-12,58.13,34.12
30-Apr-12,53.98,45.56
27-Apr-12,67.00,67.89
26-Apr-12,89.70,78.54
25-Apr-12,99.00,89.23
24-Apr-12,130.28,99.23
23-Apr-12,166.70,101.34
20-Apr-12,234.98,122.34
19-Apr-12,345.44,134.56
18-Apr-12,443.34,160.45
17-Apr-12,543.70,180.34
16-Apr-12,580.13,210.23
13-Apr-12,605.23,223.45
12-Apr-12,622.77,201.56
11-Apr-12,626.20,212.67
10-Apr-12,628.44,310.45
9-Apr-12,636.23,350.45
5-Apr-12,633.68,410.23
4-Apr-12,624.31,430.56
3-Apr-12,629.32,460.34
2-Apr-12,618.63,510.34
30-Mar-12,599.55,534.23
29-Mar-12,609.86,578.23
28-Mar-12,617.62,590.12
27-Mar-12,614.48,560.34
26-Mar-12,606.98,580.12
I know it doesn’t look quite as pretty, but csv files are pretty ubiquitous which is
why so many different programs support them as an input and output file type. (To
save everyone some time and trouble I have saved the data.csv file into the Leaflet
Tips and Tricks example files folder which is available when you download the
book from Leanpub).
So armed with this file, click on the ‘Import’ tab in our phpMyAdmin window and
choose your file.
Importing csv data into your table
The format should be automatically recognised and the format specific options at
the bottom of the window should provide sensible defaults for the input. Let’s click
on the ‘Go’ button and give it a try.
Successful
import!
Woo Hoo!
Now if you click on the browse tab, there’s your data in your table!
All the data
successfully imported
Sweet!
The last thing that we should do is add a user to our database so that we don’t end
up accessing it as the root user (not too much of a good look).
So select the ‘homedb’ reference at the top of the window (between ‘localhost’ and
‘data2’).
Then click on the ‘Privileges’ tab to show all the users who have access to
‘homedb’ and select ‘Add a new user’
The ‘Privileges’ tab
Then on the new user create a user, use the ‘Local’ host and put in an appropriate
password.
Enter
the user information
In this case, the user name is ‘homedbuser’ and the password is ‘homedbuser’
(don’t tell).
The other thing to do is restrict what this untrusted user can do with the database.
In this case we can fairly comfortably restrict them to ‘SELECT’ only;
Restrict privileges to ‘SELECT’
Yay!
Believe it or not, that’s pretty much it. There were a few steps involved, but they’re
hopefully fairly explanatory and I don’t imagine there’s anything too confusing
that a quick Googling can’t fix.
To do that we have to ask the database for some information and have it return that
information in a format we can work with.
Now this is something of an art form in itself and believe me, you can dig some
pretty deep holes performing queries. However, we’re going to keep it simple. All
we’re going to do is query our database so that it returns the ‘date’ and the ‘close’
values.
We’ll start by selecting our ‘data2’ table and going to the ‘Browse’ tab.
To the ‘Browse’ tab
We actually already have a query operating on our table. It’s the bit in the middle
that looks like;
SELECT *
FROM `data2`
LIMIT 0, 30
This particular query is telling the database homedb (since that’s where the query
was run from) to SELECT everything (*) FROM the table data2 and when we return
the data, to LIMIT the returned information to those starting at record 0 and to only
show 30 at a time.
You should also be able to see the data in the main body of the window.
So, let’s write our own query. We can ask our query in a couple of different ways.
Either click on the ‘SQL’ tab and you can enter it there, or click on the menu link
that says ‘Edit’ in the current window. I prefer the ‘Edit’ link since it opens a
separate little window which let’s you look at the returned data and your query at
the same time.
Enter your query
So here’s our window and in it I’ve written the query we want to run.
There we go!
If you’re running the query as ‘root’ you may see lots of other editing and copying
and deleting type options. Don’t fiddle with them and they won’t bite.
Righto… That’s the query we’re going to use. If you look at the returned
information with a bit of a squint, you can imagine that it’s in the same type of
format as the *.tsv or *.csv files. (header at the top and ordered data underneath).
All that we need to do now is get our MySQL query to output data into leaflet.js.
Enter php!
What we’re going to do is use a php script that performs the query that extracts
data out of a database it in a way that we can input it into Leaflet really easily.
planedb
database
In our example we will pull out only the rows with the latitude and longitude and
return only them in a format that the script will recognise as follows;
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
We’ll follow an example of how to get this imported into a leaflet.js script after
we’ve gone through the explanation of how to generate the data.
It’s pretty short, but it packs a punch. Let’s go through it and see what it does.
The <?php line at the start and the ?> line at the end form the wrappers that allow
the requesting page to recognise the contents as php and to execute the code rather
than downloading it for display.
$username = "planeuser";
$password = "planeuser";
$host = "localhost";
$database="planedb";
Hopefully you will recognise that these are configuration details for the MySQL
database. There’s user and password (don’t worry, because the script isn’t returned
to the browser, the browser doesn’t get to see the password). There’s the host
location of our database (in this case it’s local, but if it was on a remote server, we
would just include its address) and there’s the database we’re going to access.
Then we have our query in a form that we can paste into the right spot and it’s easy
to use.
$myquery = "
SELECT `lat`, `long` FROM `test01`
WHERE `lat` <> 0
";
I have it like this so all I need to do to change the query I use is to paste it into the
middle line there between the speech-marks and I’m done. It’s just a convenience
thing.
The query itself is a fairly simple affair. We return lat and long from our table
called test01 but only if the lat value on the row is not equal to zero
(WHERE lat <> 0) (this will allow us to ignore the rows which do not contain a
latitude (and typically a longitude in this case) value.
The query is then run against the database with the following command;
$query = mysql_query($myquery);
… and then we check to see if it was successful. If it wasn’t, we output the MySQL
error code;
if ( ! $query ) {
echo mysql_error();
die;
}
$data = array();
Now we begin to echo or print out the values for our piece of code that we expect
to have inserted into our leaflet.js code;
First of all we print out the start of the declaration of our array;
Then we start up a for loop that goes from 0 ($x = 0;) to the number of returned
rows in our query ($x < mysql_num_rows($query)) one step at a time ($x++)
$data[] = mysql_fetch_assoc($query);
… and then echo each row as a latitude and longitude value enclosed by square
brackets and separated by a comma;
echo
"[",$data[$x]['lat'],",",$data[$x]['long'],"]";
[-41.31825,174.80768]
Because we need to separate our lat/long values that are enclosed with brackets
from each other with a comma like this…
[-41.31825,174.80768],
[-41.31606,174.80774]
… but we don’t want a comma after the last lat/long pair. We can use
an if statement to evaluate each row to see if it’s the last and print a comma if its
not;
After the our rows have finished being produced by our loop we need to close it off
with another square bracket;
echo "];";
And all that remains is to close the connection to our MySQL database;
mysql_close($server);
Whew!
That was a little fast and furious, but I want to revisit the point that we covered in
the part about echoing the data back to whatever had requested it. This is because
we are going to use it directly in our leaflet.js script, but we can actually run the
script directly by opening the file in our browser.
So if you can navigate using your browser to this file and click on it to run it
(WAMP should be your friend here again) this is what you should see printed out
on your screen (at least the information, but probably not formatted as nicely);
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
It looks a bit unusual on the printed page, but it’s bread and butter for JavaScript.
I have included the planelatlong.php file with the files available when you
download the Leaflet Tips and Tricks book.
If we consider a php file that we would use to display a web page with a leaflet
map and a line, it could look something like this;
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
var map = L.map('map').setView([-41.3058,
174.82082], 12);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var polyline =
L.polyline(planelatlong).addTo(map);
</script>
</body>
</html>
In the middle there is the section where we declare our data array that contains the
latitude and longitude values;
var planelatlong = [
[-41.31825,174.80768],
[-41.31606,174.80774],
[-41.31581,174.80777],
[-41.31115,174.80827],
[-41.30928,174.80835],
[-41.29127,174.83841],
[-41.33571,174.84846],
[-41.34268,174.82877]];
The is done nice and easily by ‘including’ the php file in the script with the
following line;
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
<?php include 'planelatlong.php'; ?>
var map = L.map('map').setView([-41.3058,
174.82082], 12);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© ' + mapLink + '
Contributors',
maxZoom: 18,
}).addTo(map);
var polyline =
L.polyline(planelatlong).addTo(map);
</script>
</body>
</html>
Of course it still relies on the planelatlong.php script to query the database and
return the values, but now you have the ability to dynamically source different data
from outside your web file.
This example has been a very quick glance at what we need to do to get data from
a MySQL database into Leaflet. I will be including a separate section in the book
that will explore a few different options for importing data from external sources.
Appendices
A Simple Map
<!DOCTYPE html>
<html>
<head>
<title>Simple Leaflet Map</title>
<meta charset="utf-8" />
<link
rel="stylesheet"
href="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.css"
/>
</head>
<body>
<div id="map" style="width: 600px; height:
400px"></div>
<script
src="http://cdn.leafletjs.com/leaflet-
0.7/leaflet.js">
</script>
<script>
var map = L.map('map').setView([-41.2858,
174.78682], 14);
mapLink =
'<a
href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © ' + mapLink,
maxZoom: 18,
}).addTo(map);
</script>
</body>
</html>
L.control.layers(baseLayers,overlays).addTo(map);
</script>
</body>
</html>