The world’s leading publication for data science, AI, and ML professionals.

Discover Urban viewer: an interactive web map to visualize population of Urban Areas Worldwide

How to create a web map with the urban population worldwide in Leaflet

Image by the Author. Urban Viewer: interactive web map with urban agglomerations worldwide
Image by the Author. Urban Viewer: interactive web map with urban agglomerations worldwide

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:

Image by the Author. Example of the proper structure of the table.
Image by the Author. Example of the proper structure of the table.

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:

Image by the Author. Dataset ready to use in Urban Viewer
Image by the Author. Dataset ready to use in Urban Viewer

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:

Image by the Author. Repository structure.
Image by the Author. Repository structure.

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

Image by the Author. Structure of js folder
Image by the Author. Structure of js folder

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:

Image by the Author. Structure of CSS folder
Image by the Author. Structure of CSS folder

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:

Image by the Author. Empty canvas
Image by the Author. Empty canvas

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 &amp;copy; Esri &amp;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:

Image by Author. Map object added from Leaflet with ESRI base map
Image by Author. Map object added from Leaflet with ESRI base map

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 &amp;&amp; !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:

Image by the Author. First look of Urban Agglomerations in Urban Viewer
Image by the Author. First look of Urban Agglomerations in Urban Viewer

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:

Image by the Author. Infobox added to Urban Viewer
Image by the Author. Infobox added to Urban Viewer

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] ? '&amp;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:

Image by the Author. Urban Viewer with legend added.
Image by the Author. Urban Viewer with legend added.

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:

Image by the Author. Urban Viewer with OSM base map.
Image by the Author. Urban Viewer with 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.


Related Articles