In this tutorial, we will learn about some basic HTML, CSS, and JavaScript (specifically, the Leaflet.js library) to create a series of progressively complex web maps.
Leaflet is an open-source JavaScript library that gives us code to create interactive, mobile friendly web maps. Think of it as a collection (or library) of prewritten JavaScript that does some of the heavy lifting/scripting of web map stuff for us. We will interact with the library through its well documented API. It has a small file size but is packed with useful features and can be extended even further with plugins. Leaflet was created by Vladimir Agafonkin (currently at MapBox)
In essence:
"Slippy" maps with tile based layers with zooming, panning, and feature layers that dev supplies. It handles various basic overhead tasks like converting data to map layers and mouse interactions, and it's easy to extend with plugins. It will also work well across most types of mobile and non-mobile devices.
Leaflet is a great low budget option for those that need web mapping capabilities, but don't necessarily have a fully fledged GIS license where they can leverage the online capabilities.
Leaflet is also not GIS, although it can perform some GIS-like functions. It can combined with tools like CartoDB for GIS-like capabilities. If you need total freedom of form, interaction, transitions, and map projections, consider working with something like D3.js.
Leaflet is also not point and click software, you need to know some basics about programming to get up and running.
There are a wealth of options out there (ArcGIS Online, Web AppBuilder for ArcGIS, ArcGIS JavaScript API, OpenLayers, Leaflet, Mapbox, Carto, D3.js, Google Maps and more) to achieve this dream...but today we will look at Leaflet.js
HTML (HyperText Markup Language) allows us to structure content for our web page. Our map will be contained in an element within an HTML file.
CSS (Cascading Style Sheets) gives us control of the style and visual presentation of our web page. We use it to determine the placement and sizing of the map and to customize some Leaflet elements.
JS (JavaScript) gives us the ability to add interactivity to our web page. We will use it to pull in map tiles to our web page, add data (or content layers), and handle user interaction with the map.
BEFORE YOU START!
We're going to need a few things to get going.
You'll want a proper text editor (none of that notepad nonsense!). I am going to be using Visual Studio Code but other alternatives are Sublime Text, Atom Editor, Notepad++ (if you like the barebones approach), or even Vim if you're super daring
To display our maps, it is useful to have a local server running on our machine. Today, I will be using a VS Code Live server plugin. For today, this isn't totally necessary, but very nice to have.
To display your map, it is useful to have a local server running on your computer. There are many ways to create a local server on your machine, but one that I prefer is the Live Server plugin for VSCode because it's a single click solution to starting and displaying your HTML files.
Last, to set up some chronological history of development we should implement some source control with Git.
Here is the base map we'll build first:
view example
Open a text editor (vscode) --> Create a new file --> Save the file using a .html extension.
Next, set up the structure of the web page by adding the following markup to the file:
<!doctype html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quick Start - Leaflet</title>
<style>
</style>
</head>
<body>
<script>
</script>
</body>
</html>
Next, in order to use the Leaflet library, we need to reference its CSS and JavaScript files in the HTML file.
There are a few ways that you can reference these files:
For simplicity's sake, we are going to references the hosted files. To do this, insert the code below into the <head> section of the HTML page:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin="" />
Next, include Leaflet's JavaScript file after Leaflet's CSS file
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""></script>
Leaflet requires a <div> element to contain the map and that that <div> element have a set height.
Create a <div> element with the id of "map" to contain the map by adding the following to the HTML <body>:
<div id="map"></div>
To create a map that is 800 px by 600 px add the following CSS code between the <style> tags in the <head> section of the HTML page:
#map {
width: 800px;
height: 600px;
}
Now that the HTML and CSS are set, we have set up the basic structure and style of our web map. Next, all we need to do is add some JavaScript and we'll have a web map!
Easy, right?
The first script we will write pulls in some map tiles and configures a few basic map settings.
Enter the following JavaScript between the <script> tags of the HTML file.
<script>
var map = L.map('map',{
center: [30.4383, -84.2807],
zoom: 15
});
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a>;
}).addTo(map);
</script>
The code is now complete so the file should now look like this:
<!doctype html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quick Start - Leaflet</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin="" />
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""></script>
<style>
#map {
width: 960px;
height: 500px;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map('map',{
center: [30.4383, -84.2807],
zoom: 15
});
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a>'
}).addTo(map);
</script>
</body>
</html>
First, the code within the <script></script> creates the 'map' variable, assigns it a new L.map object, then passes it the id (‘map’in this case) of the div element in which the map is to be contained. The script goes on to pass some options that set an initial center point and zoom level for the map. Essentially, this creates a map on the page that we can manipulate.
Next, the script creates a new L.tileLayer object, specifying a particular set of tiles to be loaded into the map container and passes in an 'attribution' option. In this case OpenStreetMap tiles are used but there are many map tile providers. Experiment with different sets and remember to always properly attribute the data and imagery used!
Finally, the addTo() method is used to add the tile layer to the map.
Side note:
Alternatively, instead of using the addTo() method to individually add our layers, we can define our layers ahead of time, and include them into the map declaration, see example below:
const map = L.map('map',{
center: [43.64701, -79.39425],
zoom: 15,
layers: [citiesLayer, statesLayer] // adding our layers as a group
});
Make sure to save your file and open your map on your local server.
Hopefully, you will see leaflet map showing the FSU campus!
One minor modification that we can make to this map is removing the scrolling mouse scroll capabilities for our map. This forces the user to press the zoom buttons to scroll the map closer and farther.
view example
<script>
var map = L.map('map',{scrollWheelZoom:false}).setView([43.64701, -79.39425], 15);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
</script>
First, remove the 'center' and 'zoom' options from the L.map constructor.
Next, modify the script to deactivate the scrollWheelZoom interactivity option so that we don't accidentally zoom the map when we are trying to scroll down the page. This is done by passing 'scrollWheelZoom: false' as an option to L.map(). This can be a particularly important usability issue if the map is large or if there is content above and/or below it.
Finally, set the initial center and zoom level with the setView() method.
The map should look just like our first one but we can't control the zoom level by scrolling and the code is a bit streamlined.
Now we have a customized base map with which to build on.
This next bit won't be practical right away. It may not seem very useful to add a single marker to a map, but we will explore a useful concept of binding a popup to data. Next, we will add a simple point marker to our map and bind a popup for some description.
We will be exploring the L.marker and bindPopup() functionality ** reword this!
<!doctype html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quick Start - Leaflet</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin="" />
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""></script>
<style>
#map {
width: 960px;
height: 500px;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map('map',{
center: [30.4383, -84.2807],
zoom: 15
});
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a>'
}).addTo(map);
// Add a single marker
const singleMarkerPopupMarker = L.marker([30.4383, -84.2807]);
singleMarkerPopupMarker.bindPopup('Hello world');
singleMarkerPopupMarker.addTo(map);
</script>
</body>
</html>
view example
Next, let's explore one way to add some data to our map...
Let's take some local GeoJSON data of Pace Bike Racks around Tallahassee and put them into our map using the fetch API and Leaflet's GeoJSON functionality, L.GeoJSON().
First, we are going to use the fetch API to grab our data:
fetch('./Pace_Bike_Racks_View.json')
.then(response => {
if (response.ok === true) {
return response.json();
} else {
alert('Geojson request failed.');
}
});
Let's unpack this a bit, the fetch API will let us give a reference to the location of our GeoJSON data file. Once we receive the response, we ask if the response came back okay (request was successful), we return the contents of that response and convert to JSON format with response.json()
Next, let's add the request contents to our map with L.GeoJSON().
Here's the whole code snippit:
<script>
const basemapLayer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a>',
});
var map = L.map('map', {
center: [30.4383, -84.2807],
zoom: 13,
layers: [basemapLayer]
});
// Let's add some data of pace bike racks in Tallahassee
fetch('../data/Pace_Bike_Racks_View.json')
.then(response => {
if (response.ok === true) {
return response.json(); // our data was fetched successfully
} else {
alert('Geojson request failed.');
}
});
.then(bikeRacks => {
L.geoJSON(bikeRacks).addTo(map);
});
</script>
view example
We can also leverage the L.icon class to style our markers
<script>
const basemapLayer = L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution: "© <a href="http://osm.org/copyright">OpenStreetMap</a>",
});
var map = L.map("map", {
center: [30.4383, -84.2807],
zoom: 13,
layers: [basemapLayer]
});
var paceBikeIcon = new L.Icon({
iconSize: [35, 27],
iconAnchor: [13, 27],
popupAnchor: [1, -24],
iconUrl: "../images/bicycle.png"
});
// Let"s add some data of pace bike racks in Tallahassee
fetch("../Pace_Bike_Racks_View.json")
.then(response => {
if (response.ok === true) {
return response.json(); // our data was fetched successfully
} else {
alert("Geojson request failed.");
}
})
.then(bikeRacks => {
L.geoJSON(bikeRacks, {
pointToLayer: function (feature, latlng) {
const popup = L.popup().setContent("I'm a hippity hoppity pace bike docking location");
const marker = L.marker(latlng, {
icon: paceBikeIcon
});
marker.bindPopup(popup);
return marker;
}
}).addTo(map);
});
</script>
view example
One of the cool things about Leaflet is that it's built to be completely extendable with and there are an abundance of plugins to be used and modified. It's also completely necessary because Leaflet gives you very little functionality out of the box.
One "plugin" (really like a small library of functionality) that we will look at today is ESRI Leaflet that will help us interface with ESRI's data services.
ESRI is pervasive throughout the GIS industry, and there is a good chance that your organization uses their technologies.
To bring in ESRI Leaflet, all we need to do is include Leaflet's CDN in our html file
<script src="https://unpkg.com/esri-leaflet@2.3.2/dist/esri-leaflet.js"
integrity="sha512-6LVib9wGnqVKIClCduEwsCub7iauLXpwrd5njR2J507m3A2a4HXJDLMiSZzjcksag3UluIfuW1KzuWVI5n/cuQ=="
crossorigin=""></script>
Next, we can bring in a FeatureLayer by using the L.esri.featureLayer() method. Let's bring in some TLCGIS data:
<!doctype html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quick Start - Leaflet</title>
// load Leaflet css
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin="" />
// load Leaflet JavaScript
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""></script>
// load ESRI Leaflet
<script src="https://unpkg.com/esri-leaflet@2.3.2/dist/esri-leaflet.js"
integrity="sha512-6LVib9wGnqVKIClCduEwsCub7iauLXpwrd5njR2J507m3A2a4HXJDLMiSZzjcksag3UluIfuW1KzuWVI5n/cuQ=="
crossorigin=""></script>
<style>
#map {
width: 960px;
height: 500px;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map('map', {
center: [30.4383, -84.2807],
zoom: 15
});
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a>'
}).addTo(map);
// add our esri feature layer
L.esri.featureLayer({
url: 'https://services.arcgis.com/ptvDyBs1KkcwzQNJ/arcgis/rest/services/Pace_Bike_Racks/FeatureServer/0'
}).addTo(map);
</script>
</body>
</html>
We've only scratched the surface of what you can do with Leaflet maps. Tinker around with these examples and see what you can come up with!
While we focused on simply making maps today, there is much to be said about map design. If you're familiar with the Calcite Maps framework often used with the ArcGIS JavaScript API, there is a Leaflet adaptation as well.
One of the awesome things about Leaflet is that there are tons of resources out there for learning in addition to their documentation. I used many of these resources to prepare this tutorial for SHRUG.
Leaflet tutorials on the official Leaflet website.
A Leaflet tutorial written for maptime boston by Andy Woodruff and Ryan Mullins that gets into some of the more complicated functionality and techniques.
a Leaflet tutorial written for DUSPViz (MIT’s Department of Urban Studies & Planning) that covers the basics, but also adds things like mouseclick events to trigger actions on the map.
Anatomy of a Web Map, by Alan McConchie and Beth Schechter of Maptime and Stamen.
Leaflet.js Essentials, a book by Paul Crickard III.
Leaflet and Mapbox JavaScript API Fundamentals, a presentation by Mapbox's Rafa Gutierrez.
Leaflet Intro, a Maptime PDX presentation by Lyzi Diamond.
Leaflet.JS Introduction, by Thierry Nicola for JS Luxembourg.
Leaflet provider map, an open source Leaflet extension that contains configurations for various free tile providers.
Mapbox Guides and examples are great for learning about web maps in general in addition to Mapbox.js, which is built on top of Leaflet.
Find a mistake? Submit an Issue on Github!Back to Top