
The world is becoming urban. In 2018 the urban settlements worldwide hosted approximately 55.3% of the world’s population. The projections of the United Nations estimates by 2030 that 60% of the world’s population is going to be living in urban agglomerations. The key trends in urbanization unfold the concern of governments to make human settlements inclusive, safe, resilient, and sustainable[1]. Nature is having a devastating impact due to urban metabolism (waste), the main concern of authorities is to diminish the impact by creating efficient processes that reduce waste such as recycling, alternative energy, or transitions in fossil fuel, just to name a few.
Urban Viewer [HERE!](https://github.com/bryanvallejo16/urban-viewer-worldwide) Repository HERE!
The United Nations did a fascinating population revision named: _World Urbanization Prospect: The 2018 revision_ which shows the population projection of urban agglomerations worldwide. So, we will understand the concept of urban agglomeration properly, which is the delimitation we are using for this practice, and differentiate it from two other urban concepts: City proper, and Metropolitan area.
The City Proper represents the administrative core of the urban area. It keeps a central delimitation. The next concept is Urban Agglomeration and it refers to the extent of the contiguous urban area or built-up area that delimitates the city’s boundaries. Finally, the Metropolitan Area represents the economic and social interconnection boundary, interlinked by commerce or commuting patterns. These concepts can be reviewed in the 2018 revision mentioned.
"The aim of this practice is to create an interactive web map that shows the estimated population in Urban Agglomerations worldwide by 2020 in Leaflet"
DATA
The dataset we are using for this map-visualization practice comes from the Department of Economic and Social Affairs – Population Dynamics at the United Nations. Specifically, we are using file 12 that contains the population of urban agglomerations with 300.000 inhabitants or more in 2018, by country, 1950–2035 (thousands) [2] The dataset is licensed under Creative Commons By 3.0.
The population revision in 2018 released publications with vast information about urban projections and can be found in the Publications section.
PRACTICE
The practice is divided into two sections: 1) Data format, and 2) Urban Viewer – web map creation.
1) Data format
After downloading the dataset [WUP2018-F12-Cities_Over_300K.xls](https://population.un.org/wup/Download/Files/WUP2018-F12-Cities_Over_300K.xls)
from the Downloads page. You may notice that it is an excel file and the structure is not proper for usage, especially in a Leaflet map. Furthermore, you have to structure properly the dataset so it looks for example like this:

You can do it directly in excel and you may need to keep the columns: Urban Agglomeration
, Latitude
, Longitude
, and 2020
. Then, you have to change the column’s name in this way: Urban Agglomeration
to City_name
and 2020
to a2020
so it matches the code of the Urban Viewer.
When you have the proper structure you save it as .csv
, then you open it in geopandas and include the geometry column (Longitude, Latitude) as a geometry object. You can review this process in the Data Generation section of the article "Bike Sharing System movements in Helsinki: aggregation and visualization with an interactive flow-map". Finally, save it as GeoJSON with the name pop_cities.geojson
. You will find the dataset already in the Repository.
The final step that is done manually for Leaflet mapping is to add the name of the variable inside the dataset. Leaflet web has an example. So the final dataset we are going to use should look like this:

In case you want to automate creating more datasets with different years or need help in the creation of the GeoJSON do not hesitate to ask for support. You can contact me on my profile on LinkedIn or simply leave a comment in this article.
2) Urban Viewer creation
Now, we have to create a repository containing the necessary files to make the Urban Viewer work. We add a folder called css
and another folder called js.
Also, we add an HTML file called index.html
You can obtain these files by cloning the repository in your local disk. So your local folder may look like these:

Be aware that inside the data folder there is already a pop_cities.geojson
file which is the results of step 1). To work with the files for web mapping, I recommend using Atom or simply you can use Notepad++.
First, we are going to download and copy the Leaflet API for web mapping in the folder js.
Additionally, we add an empty JS file named in this case main.js

Then, in the folder, css
we add the CSS file that comes from Leaflet and an empty file that we will call in this case style.css
CSS folder looks like this:

2.1) Loading files to index.html
We are going to open the index.html file with Atom or Notepad++ and we start loading the files. It will contain a head and a body. In the body section, is where we include the main files that run the map animation, also the Leaflet files, and the data file. It includes base maps from ESRI.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Urban Viewer Demo</title>
<!--link to stylesheet-->
<link rel="stylesheet" href="css/style.css">
<!-- link to leaflet stylesheet-->
<link rel="stylesheet" href="css/leaflet.css">
</head>
<body>
<!-- title of your map-->
<h1> 🌎 Urban viewer - Urban agglomerations worldwide </h1>
<!-- division div for the map -->
<div id="map"></div>
<!-- link to leaflet javascript library-->
<script src="js/leaflet-src.js"></script>
<!-- load Esri Leaflet because we want to use an Esri basemap -->
<script src="https://unpkg.com/[email protected]/dist/esri-leaflet.js"></script>
<!--link to the files that contains geoJson data-->
<script src="data/pop_cities.geojson" type="text/javascript"> </script>
<!-- link to main javascript file -->
<script src="js/main.js"></script>
</body>
<html>
2.2) Parameters in map-style.css
We have to style the HTML objects and we do it styling in the .css file the objects: title, body, legend, and map. Open the style.css
and include the next code:
h1{
position: fixed;
font-family: "Times New Roman", Times, serif;
font-size: 24px;
box-shadow: 2px 2px 3px 3px black;
background: lightgray;
color: black;
margin-left:5%;
margin-top: 0.6%;
z-index: 2;
}
body{
width: 100%;
height: 100%;
margin: 0px;
font-family: "Helvetica Neue", Arial, Helveticam sans-serif;
}
.info {
padding: 6px 8px;
font-family: "Times New Roman", Times, sans-serif;
font-size: 20px;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h3 {
font-size: 24px;
font-family: "Times New Roman", Times, serif;
text-shadow: 2px 2px 5px gray;
margin: 0 0 5px;
color: #282825 ;
}
.legendcolor {
line-height: 18px;
color: #555;
}
.legend i {
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 1;
}
.legend .circle {
border-radius: 50%;
width: 15px;
height: 15px;
margin-top: 8px;
}
#map {
height:100%;
width:100%;
left:0%;
overflow:hidden;
position:fixed;
border:1px #444 solid;
}
You may notice it contains parameters of visualization like font, position, or size of the map. You can change them if you want to re-design your own Urban Viewer.
After adding the CSS code, If you open the index.html in the browser you may look at an empty canvas like this:

2.3) Creating the Urban Viewer
Here, we are going to start creating the map with Leaflet. It is time to open and start editing the main.js
file. Let’s go step by step. You can take a look at the Leaflet Interactive Choropleth Map example where I took some help for establishing the map variable and defining the colors for legend.
To start, we add a variable for the map, defining the zoom level and center. Then, we add OSM (OpenStreetMap) and ESRI as base maps. You can change the attribution here let say for your name or institution name so it is shown in the final web map.
// ADDING BASE MAPS, MAP AND SCALE BAR
var map = L.map('map').setView([25, 12], 3);
var osm =
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{attribution:'Open Street Maps | Bryan R. Vallejo'});
var esri =
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community | Bryan R. Vallejo',maxZoom: 18});
var esridark =
L.esri.basemapLayer('DarkGray',{attribution: 'Bryan R. Vallejo'});
var esrigray =
L.esri.basemapLayer('Gray',{attribution: 'Bryan R. Vallejo'});
esridark.addTo(map)
var basemaps={
'DarkGray': esridark,
'Satellite': esri,
'OSM': osm
}
L.control.scale({imperial:false, position:'bottomleft'}).addTo(map);
if you refresh your index.html file in your browser it should look like this:

Now, we are going to add the dataset and include functions that retrieve the population info in an infobox. Do check the Leaflet example if needed.
//FUNCTIONS
/Function to highlight Features
function highlightFeature(e) {
var activefeature = e.target;
activefeature.setStyle({
weight: 5,
color: '#F0F92B',
dashArray: '',
fillOpacity: 0.3
});
if (!L.Browser.ie && !L.Browser.opera) {
activefeature.bringToFront();
} info.update(activefeature.feature.properties);
}
//function for resetting the highlight
function resetHighlight(e) {
cities.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.flyTo(e.target.getLatLng(),6);
}
//to call these methods we need to add listeners to our features
//the word ON is a short version of addEventListener
function interactiveFunction(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature,
} );
}
Then, we are going to keep adding functions. The first one is going to define the radius size of the circles based on the population number. The second one, the color of the circle. The third one, define the style of the point based on point size (population) and color.
// calculate the circles' radius given the cities' population
function getRadius(pop) {
var maxSymbolsize = 20; // maximum symbol size
var maxValue = 37393129; // highest population value in the dataset
r = maxSymbolsize * Math.sqrt(pop/maxValue); // proportional by area
return r;
}
// create the circles' style
function getColor(d) {
return d > 10000000 ? '#d7301f' :
d > 5000000 ? '#fc8d59' :
d > 1000000 ? '#fdcc8a' :
'#fef0d9' ;
}
// radius calculated with function above and population property form GeoJSON as input
function style(feature) {
return {
radius: getRadius(feature.properties.a2020),
fillColor:getColor(feature.properties.a2020),
color: "#000",
weight: 1,
opacity: 0,
fillOpacity: 0.9
};
}
At the moment we have defined the function that creates the interaction of the web map, and the style (size and color) of the Urban Agglomerations.
Next step, to add the cities layer.
// Add circles, popups and tooltips to the map
var cities=L.geoJson(pop_cities, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, style(feature));
},
onEachFeature: interactiveFunction
}).addTo(map)
If you refresh the index.html
in your browser it may look like this:

The next step is to retrieve in an infobox the number of inhabitants of each city. For this, we may include an infobox object and the functions that make it interactive.
//ADDING A INFO CONTROL BOX
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
// method that we will use to update the control based on feature properties passed
info.update = function (props) {
this._div.innerHTML = '<h3> Cities and population </h3>' + (props ?
'<b>'+props.City_name +'</b>'+ ' 🌆 ' +'<br/>' + props.a2020 + ' Inhabitants in 2020':
'Hover the mouse over the map to see data.'+'<br/>'+'¡Try clicking over the cities!' );
};
info.addTo(map);
If you refresh the index.html file the infobox may look like this:

Then, we add the legend defining the color of the class through a loop.
//ADDING A LEGEND WITH COLORS
var legendcolor = L.control({position: 'bottomleft'});
legendcolor.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [300000, 1000000, 5000000, 10000000],
labels = [];
// loop through our density intervals and generate a label with a colored square for each interval
for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i class ="circle" style="background:' + getColor(grades[i] + 1) + '"></i> ' +
grades[i] + (grades[i + 1] ? '&ndash;' + grades[i + 1] + '<br>' : '+');
}
return div;
};
legendcolor.addTo(map);
If you make a refresh of the index.html
file in your browser. You will notice that infobox retrieves information when you hover the mouse over the cities. Also, the legend with the proper colors is added. It looks like this:

The final step is to add a Layer Controler that can turn on the base maps and the cities if desired.
//ADDING A LAYER CONTROL
var features={
'Cities': cities
}
var legend = L.control.layers(basemaps, features, {position: 'bottomleft', collapsed:true}).addTo(map);
Now, you can change the base maps and turn off cities if wanted. Finally, the Urban Viewer looks like this with an OSM base map:

RECOMMENDATION
If you are experienced in JavaScript you can keep adding functionalities to the Urban Viewer. For example, you can add and Search box so you type the city. It depends on the utility you want to give it. The one explained in this practice is mainly for visualization and information. The users can have a quick look at the population of Urban Agglomerations Worldwide.
If you find interesting the creation of the Urban Viewer and and need support to create your own projects based on web mapping. Ping me on my profile LinkedIn.
REFERENCES
[1] United Nations, Department of Economic and Social Affairs, Population Division (2018). The World’s Cities in 2018 – Data Booklet (ST/ESA/ SER.A/417).
[2] United Nations, Department of Economic and Social Affairs, Population Division (2018). World Urbanization Prospects: The 2018 Revision, Online Edition.