Layer Groups and Controls

At this point, we’ve learned how to add a lot of different elements to our maps. This tutorial will cover the basics of managing those elements by grouping them into layers.

Let’s start with a map that has a couple of familiar elements: a raster layer with a map of Haddonfield and a couple of custom popups:

We can piece together bits of code from the last few lessons to make this:

<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>

<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>

<div id='map'></div>
<head>
</head>

<body>

<script>

var streetmap = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png'),
haddonfield = L.tileLayer('http://mapwarper.net/maps/tile/19483/{z}/{x}/{y}.png');

var map = L.map('map', {
     center: [39.898197, -75.031492],
     zoom: 13,
     layers: [streetmap, haddonfield]
     });

var customOptions =
     {
     'maxWidth': '500',
     'className' : 'custom'
     }

var customPopup = "<h1>The Creek Bed, 2010</h1><br/><h2>Here is some<br/>example text<br/></h2><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/NHL_Marl_Pit_2.JPG/1280px-NHL_Marl_Pit_2.JPG' width='350px'/>";
     L.marker([39.910320, -75.028256]).bindPopup(customPopup,customOptions).addTo(map);

var customPopup2 = "<h1>Hadrosaurus Statue</h1><br/><h2>Lantern Lane<br/>Haddonfield, NJ<br/></h2><img src='http://hadrosaurus.com/hadro_sculpture.jpg' width='350px'/>";
     L.marker([39.896909, -75.034466]).bindPopup(customPopup2,customOptions).addTo(map);
</script>

</body>
</html>

The archival map of Haddonfield is nice, but suppose we wanted to add two or three more archival maps to show the surrounding areas. Suppose we also wanted to add another archival map of the geological survey. Suppose we also wanted to be able to switch between the two types of maps and make the popups appear and disappear. We can do this by grouping the different items as layers and adding some controls.

Let’s start by adding the other maps. Add the variables ‘haddon’, ‘delawaretownship’, and ‘geologicalsurvey’ to the map:

<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>

<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>

<div id='map'></div>
<head>
</head>

<body>

<script>

var streetmap = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png'),
     haddonfield = L.tileLayer('http://mapwarper.net/maps/tile/19483/{z}/{x}/{y}.png');
     haddon = L.tileLayer('http://mapwarper.net/maps/tile/19640/{z}/{x}/{y}.png');
     delawaretownship = L.tileLayer('http://mapwarper.net/maps/tile/19637/{z}/{x}/{y}.png');
     goeologicalsurvey = L.tileLayer('http://maps.georeferencer.com/georeferences/890354948079/2017-04-16T23:55:15.190279Z/map/{z}/{x}/{y}.png?key=empIFqiUUldYuevvOzZm', {id: 'Geological map of New Jersey, from the State Geological Survey, of 1868 by Geo. H. Cook. (1872)', attribution: 'David Rumsey Historical Map Collection, © 2000 by Cartography Associates CC BY-NC-SA 2.0'});
 
var map = L.map('map', {
     center: [39.898197, -75.031492],
     zoom: 13,
     layers: [streetmap, haddonfield]
     });

var customOptions =
     {
     'maxWidth': '500',
     'className' : 'custom'
     }

var customPopup = "<h1>The Creek Bed, 2010</h1><br/><h2>Here is some<br/>example text<br/></h2><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/NHL_Marl_Pit_2.JPG/1280px-NHL_Marl_Pit_2.JPG' width='350px'/>";
     L.marker([39.910320, -75.028256]).bindPopup(customPopup,customOptions).addTo(map);

var customPopup2 = "<h1>Hadrosaurus Statue</h1><br/><h2>Lantern Lane<br/>Haddonfield, NJ<br/></h2><img src='http://hadrosaurus.com/hadro_sculpture.jpg' width='350px'/>";
     L.marker([39.896909, -75.034466]).bindPopup(customPopup2,customOptions).addTo(map);
</script>

</body>
</html>

Ordinarily, we would add these new variables to the map as new layers, but we’ll hold off on that by grouping the variables ‘delawaretownship’,  ‘haddon’, and ‘haddonfield’,  into a new variable called ‘counties’. Note the order that we list these because they are going to overlap with the first layer at the bottom and the last layer on top. We want ‘haddonfield’ on top.  We can then add the layer ‘counties’ to the map, which will add the three maps we grouped together at once (notice that we haven’t added geological survey – we’ll come back to this):

<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>

<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>

<div id='map'></div>
<head>
</head>

<body>

<script>

var streetmap = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png'),
     haddonfield = L.tileLayer('http://mapwarper.net/maps/tile/19483/{z}/{x}/{y}.png');
     haddon = L.tileLayer('http://mapwarper.net/maps/tile/19640/{z}/{x}/{y}.png');
     delawaretownship = L.tileLayer('http://mapwarper.net/maps/tile/19637/{z}/{x}/{y}.png');
     goeologicalsurvey = L.tileLayer('http://maps.georeferencer.com/georeferences/890354948079/2017-04-16T23:55:15.190279Z/map/{z}/{x}/{y}.png?key=empIFqiUUldYuevvOzZm', {id: 'Geological map of New Jersey, from the State Geological Survey, of 1868 by Geo. H. Cook. (1872)', attribution: 'David Rumsey Historical Map Collection, © 2000 by Cartography Associates CC BY-NC-SA 2.0'});

var counties = L.layerGroup([delawaretownship, haddon, haddonfield]); 
var map = L.map('map', {
     center: [39.898197, -75.031492],
     zoom: 13,
     layers: [streetmap, counties]
     });

var customOptions =
     {
     'maxWidth': '500',
     'className' : 'custom'
     }

var customPopup = "<h1>The Creek Bed, 2010</h1><br/><h2>Here is some<br/>example text<br/></h2><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/NHL_Marl_Pit_2.JPG/1280px-NHL_Marl_Pit_2.JPG' width='350px'/>";
     L.marker([39.910320, -75.028256]).bindPopup(customPopup,customOptions).addTo(map);

var customPopup2 = "<h1>Hadrosaurus Statue</h1><br/><h2>Lantern Lane<br/>Haddonfield, NJ<br/></h2><img src='http://hadrosaurus.com/hadro_sculpture.jpg' width='350px'/>";
     L.marker([39.896909, -75.034466]).bindPopup(customPopup2,customOptions).addTo(map);
</script>

</body>
</html>

We can see that we have successfully added maps of the surrounding counties, but the map of the geological survey remains invisible for now:

Now we need to add a couple of lines to the bottom to create a new layer called ‘baseMaps’. ‘baseMaps will be the two sets of maps that we switch between with our controls:

var baseMaps = {
     "Geological Survey": goeologicalsurvey,     
     "County Maps": counties
     };

The “key” goes in quotation marks on the left. This is the name of the layer as it will appear on the controls. The “value” appears after a colon on the right to identify the corresponding variables. All of the maps grouped into ‘counties’ will appear when we click on “County Maps”. The layers will be added in the order they are listed here so in this example the maps of the counties will appear as the topmost layer.

Underneath that, we can add the control element to manipulate our new variable ‘baseMaps’:

L.control.layers(baseMaps).addTo(map);

The whole thing now looks like this:

<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>

<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>

<div id='map'></div>
<head>
</head>

<body>

<script>

var streetmap = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png'),
     haddonfield = L.tileLayer('http://mapwarper.net/maps/tile/19483/{z}/{x}/{y}.png');
     haddon = L.tileLayer('http://mapwarper.net/maps/tile/19640/{z}/{x}/{y}.png');
     delawaretownship = L.tileLayer('http://mapwarper.net/maps/tile/19637/{z}/{x}/{y}.png');
     goeologicalsurvey = L.tileLayer('http://maps.georeferencer.com/georeferences/890354948079/2017-04-16T23:55:15.190279Z/map/{z}/{x}/{y}.png?key=empIFqiUUldYuevvOzZm', {id: 'Geological map of New Jersey, from the State Geological Survey, of 1868 by Geo. H. Cook. (1872)', attribution: 'David Rumsey Historical Map Collection, © 2000 by Cartography Associates CC BY-NC-SA 2.0'});

var counties = L.layerGroup([delawaretownship, haddon, haddonfield]); 
var map = L.map('map', {
     center: [39.898197, -75.031492],
     zoom: 13,
     layers: [streetmap, counties] 
     }); 

var customOptions = 
     { 
     'maxWidth': '500', 
     'className' : 'custom' 
     }

var customPopup = "<h1>The Creek Bed, 2010</h1><br/><h2>Here is some<br/>example text<br/></h2><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/NHL_Marl_Pit_2.JPG/1280px-NHL_Marl_Pit_2.JPG' width='350px'/>"; 
     L.marker([39.910320, -75.028256]).bindPopup(customPopup,customOptions).addTo(map); 

var customPopup2 = "<h1>Hadrosaurus Statue</h1><br/><h2>Lantern Lane<br/>Haddonfield, NJ<br/></h2><img src='http://hadrosaurus.com/hadro_sculpture.jpg' width='350px'/>"; 
     L.marker([39.896909, -75.034466]).bindPopup(customPopup2,customOptions).addTo(map); 

var baseMaps = {
     "Geological Survey": goeologicalsurvey,
     "County Maps": counties
     };

L.control.layers(baseMaps).addTo(map);

</script> 
</body> 
</html>

Notice that the new map has a little controller in the top right corner that allows us to toggle between the maps of the counties and the maps of the geological survey. Remember that we didn’t add ‘geological’ survey to the map variable, so it does not initially appear on the map but we call it up using the controller:

We can also add the two popups to the layer controls, but this is going to be a little tricky. If you look at the code that creates a popup, you’ll see that we created a variable called ‘customPopup’, which is called up when we click on the makers. What we need to make appear and disappear are the markers. We can do this fairly easily by adding ‘variable’ tags in front of our markers. We should also remove the ‘.addTo(map)’ notation so that these markers are called up with the controller:

var customPopup = "<h1>The Creek Bed, 2010</h1><br/><h2>Here is some<br/>example text<br/></h2><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/NHL_Marl_Pit_2.JPG/1280px-NHL_Marl_Pit_2.JPG' width='350px'/>"; 
var marker1 = L.marker([39.910320, -75.028256]).bindPopup(customPopup,customOptions); 

var customPopup2 = "<h1>Hadrosaurus Statue</h1><br/><h2>Lantern Lane<br/>Haddonfield, NJ<br/></h2><img src='http://hadrosaurus.com/hadro_sculpture.jpg' width='350px'/>"; 
var marker2 = L.marker([39.896909, -75.034466]).bindPopup(customPopup2,customOptions);

Now that we have variables for the two markers, we can add them to a layer group:

var popups= L.layerGroup([marker1, marker2]);

We can then create an element called ‘overlay’ to control this new layer group as a variable. It should be added just underneath the ‘baseMaps’ variable and follow the same pattern:

var overlay = {
     "Pop-Ups": popups};

Finally, we add ‘overlay’ to the layer control:

L.control.layers(baseMaps, overlay).addTo(map);

We can add this of the script.

<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>

<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>

<div id='map'></div>
<head>
</head>

<body>

<script>

var streetmap = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png'),
     haddonfield = L.tileLayer('http://mapwarper.net/maps/tile/19483/{z}/{x}/{y}.png');
     haddon = L.tileLayer('http://mapwarper.net/maps/tile/19640/{z}/{x}/{y}.png');
     delawaretownship = L.tileLayer('http://mapwarper.net/maps/tile/19637/{z}/{x}/{y}.png');
     goeologicalsurvey = L.tileLayer('http://maps.georeferencer.com/georeferences/890354948079/2017-04-16T23:55:15.190279Z/map/{z}/{x}/{y}.png?key=empIFqiUUldYuevvOzZm', {id: 'Geological map of New Jersey, from the State Geological Survey, of 1868 by Geo. H. Cook. (1872)', attribution: 'David Rumsey Historical Map Collection, © 2000 by Cartography Associates CC BY-NC-SA 2.0'});

var counties = L.layerGroup([delawaretownship, haddon, haddonfield]); 
var map = L.map('map', {
     center: [39.898197, -75.031492],
     zoom: 13,
     layers: [streetmap, goeologicalsurvey, counties] 
     }); 

var customOptions = 
     { 
     'maxWidth': '500', 
     'className' : 'custom' 
     }

var customPopup = "<h1>The Creek Bed, 2010</h1><br/><h2>Here is some<br/>example text<br/></h2><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/NHL_Marl_Pit_2.JPG/1280px-NHL_Marl_Pit_2.JPG' width='350px'/>"; 
var marker1 = L.marker([39.910320, -75.028256]).bindPopup(customPopup,customOptions); 

var customPopup2 = "<h1>Hadrosaurus Statue</h1><br/><h2>Lantern Lane<br/>Haddonfield, NJ<br/></h2><img src='http://hadrosaurus.com/hadro_sculpture.jpg' width='350px'/>"; 
var marker2 = L.marker([39.896909, -75.034466]).bindPopup(customPopup2,customOptions); 

var popups= L.layerGroup([marker1, marker2]);

var baseMaps = { 
     "Geological Survey": goeologicalsurvey, 
     "County Maps": counties 
     };

var overlay = {
     "Pop-Ups": popups};

L.control.layers(baseMaps, overlay).addTo(map); 
</script> 
</body> 
</html>

This should give us a map with a controller that allows us to switch between the two layers of maps and make the pop-ups appear and disappear:

These new skills add a layer of interaction and analysis to the maps we are creating. By grouping variables as layers that can be manipulated with a controller, you can let the user control the amount of clutter on your map while also letting them add or remove data to see what is revealed by different combinations of variables.