This version of the course is several years out of date and parts of it will not work - it is here just for the benefit of my 2022 dissertation students who want some grounding in Leaflet and associated technologies.

JavaScript Code Snippets

This page provides you with shippets of generic code that is ready to just copy and paste for your own use. I will add to this as the course progresses.

Here is how to add the libraries that we use in this course

<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>
 
<!-- Proj4js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>

<!-- Turf.js -->
<script src="https://npmcdn.com/@turf/turf/turf.min.js"></script>

<!-- Firebase -->
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-database.js"></script>

<!-- Chart.js -->
<script src="https://www.chartjs.org/dist/2.9.3/Chart.min.js"></script>

A Blank Leaflet Map

This is the basic template with which you can start weeks 1 and 2 - Do not use from week 3 onwards

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>GIS and the Web</title>

 		<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" />
		<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>

		<style>
      
      /* set the web page to full size */
			html, body {
				padding: 0;
				margin: 0;
				width: 100%;
				height: 100%;
			}

      /* set the map to full page */
			#map {
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
	<body>

    <!-- A division to hold the map -->
		<div id='map'></div>

		<script>

			// this is a variable that holds the map
			const map = L.map('map');

			// this is a variable that holds the coordinates of the map centre
			const mapCentre = L.latLng(53.466502, -2.235387);

			// set the map to use the above coordinates, and to zoom level 16
			map.setView(mapCentre, 16);

			// this is a variable that holds a reference the tiles that will be used for the basemap
			const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'});

			//add the tiles to the map
			tiles.addTo(map);
		</script>
	</body>
</html>


A Blank Leaflet Map Using Events

This is a better basic template with which you can start weeks 3-12

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>GIS and the Web</title>

 		<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" />
		<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>

		<style>
      
      /* set the web page to full size */
			html, body {
				padding: 0;
				margin: 0;
				width: 100%;
				height: 100%;
			}

      /* set the map to full page */
			#map {
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
  
	<body onload="initMap();">

    <!-- a division to hold the map -->
		<div id='map'></div>

		<script>
      
      // global variable for map
      let map;
      
      /**
       * Initialise the map (called when the body has loaded)
       */
      function initMap() {

        // this is a variable that holds the map
        map = L.map('map');

        // set the map to use the above coordinates, and to zoom level 16
        map.setView(L.latLng(53.466502, -2.235387), 16);

        // this is a variable that holds a reference the tiles that will be used for the basemap
        const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);
      }
		</script>
	</body>
</html>



AJAX (XMLHttpRequest Object)

For accessing JSON data asynchoronously via HTTP…

/**
 * Make a request for JSON over HTTP, pass resulting text to callback when ready
 */
function makeRequest(url, callback) {

	//initialise the XMLHttpRequest object
	var httpRequest = new XMLHttpRequest();

	//set an event listener for when the HTTP state changes
	httpRequest.onreadystatechange = function () {
	
		//a successful HTTP request returns a state of DONE and a status of 200
		if (httpRequest.readyState === XMLHttpRequest.DONE && httpRequest.status === 200) {
				callback(JSON.parse(httpRequest.responseText));
		}
	};

	//prepare and send the request
	httpRequest.open('GET', url);
	httpRequest.send();
}  

You then call that function, passing the URL where the JSON data is located, and a callback function to which the data will be passed when the data is ready:

/**
 * This is a normal function that will be used as the callback
 */
function myCallback(jsonData){

	// jsonData contains the parsed data in JSON format
	// do something with it here
}

//get the journey data and add to the map when ready
makeRequest(url, myCallback);

Comments

As a rule of thumb, well-written code should be roughly one-third code, one-third comments, and one third whitespace. Programmers call this the Rule of Thirds.

Doc comment (should be used at the top of each function):

 /**
  * Everything here is a comment.
  */

Multiline comment (should be used for sections of code):

/*
 Everything in here is a comment.
*/

/* Also works like this */

Single line comment (should be used for every individual line):

// This is a comment

Conditional Statements

Conditional statements allow your code to make decisions about whether or not to run, or what to do based upon different conditions.

if Statements

if statements are the most common variant of conditional statement, and take this form:

if (condition1) {
	// statement 1;
	
} else if (condition2) {
	// statement 2
	
} else if (condition3) {
	// statement 3
	
} else {
	// statement 4
}

else if allows unlimited extra conditions to be tested, whereas else simply allows for a default action in case all of the conditions evaluate to false. Similarly condition2 will only be tested if condition1 evaluates to false, as soon as one evaluates to true the rest of the if statement will be ignored.

Here is a slightly more realiistic example:

// greet the visitor with an alert box
if (language === "German") {
	alert("Guten Tag");
	
} else if (language === "English") {
	alert("Hello");
	
} else if (language === French) {
	alert("Bonjour")
	
} else {
	alert("Sorry, I don't speak" + language);
}

This will open an alert saying “Hello”.


Coordinate Transformation using Proj4js

To use proj4js to transform coordinates, you need to get the proj4 strings for the CRSs that you want to use. You can get them by searching online, from websites like this or this.

// store the proj4 string for the CRS that you are transforming FROM 
// this one is WGS84 (what Leaflet is using)
const p_wgs84 = "+proj=longlat +datum=WGS84 +no_defs";

// store the proj4 string for the CRS that you are transforming TO
// this one is British National Grid
const p_osgb = "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs";

// use those proj4 strings to create a proj4 object
const transformer = proj4(p_wgs84, p_osgb);

Now we have our object, it’s time to use it! proj4 objects contain two functions:

.forward() transform between the first coordinate reference system and the second (wgs84 to osgb in our case)
.inverse() transform between the second coordinate reference system and the first (osgb to wgs84 in our case)

You pass coordinates to the functions as an array in the form [ longitude, latitude ].

The transformation itself is as simple as:

// transform coordinates to OSGB
let xy = transformer.forward([-2.345, 53.456]);

//xy[0] = longitude;
//xy[1] = latitude;

Events

To add an event to an HTML element, you just need to use the relevant attribute in the opening tag:

<!-- Set the listener for the load event -->
<body onload='initMap();'> 

<!-- Set the listener for the click event -->
<button onclick="alert('You clicked my button!');">Go on, give me a click...</button>

To add listeners to a Leaflet Map, simply use the map.on() function:

// add listener for click event on the map
map.on('click', onMapClick);

Then you just need a listener, which is a function that takes a single argument (the event object):

/**
 * Event handler for map click
 */
function onMapClick(e) {
	
	// add a popup to the marker and open it
	console.log( "you clicked at " + e.latlng );
}

You can also add events to other map elements, such as GeoJSON layers (click for example).


Firebase

Firebase is an online nosql database service offered by Google.

Anonymously sign in to Firebase

// sign in anonymously - this helps stop your database being abused
firebase.auth().signInAnonymously().catch(function(error) {
  console.log(error.code);
  console.log(error.message);
});

Get a reference to a particular collection in Firebase

This gets a reference to a collection called "clicks"

// create a global reference to the 'clicks' collection in your database
myDb = firebase.database().ref().child('clicks');

Adding some GeoJson to Firebase

// push the data into the database, set inline callback
myDb.push(data, function(error) {

  //if no error is returned to the callback, then it was loaded successfully
  if (!error) { 
    console.log("successfully added to firebase!");

    // otherwise pass the error to the console
  } else {
    console.error(error);
  }
});

Deleting some data from Firebase

In this example we are updating a marker location based upon a click event

// prepare the data to update in the database (deleting is done by updating to null)
let updates = {};
updates['/clicks/' + snapshot.key] = null;	//snapshot is the database snapshot

// update the point location in the database and callback to console
firebase.database().ref().update(updates, function(error) {
  if (!error) {
    console.log("successfully deleted point!");
  } else {
    console.error(error);
  }
});

Updating some data in Firebase

In this example we are updating some data in firebase

// prepare the data to update in the database
let updates = {};
updates['/clicks/' + snapshot.key] = data;	//snapshot is the database snapshot

// update the point location in the database and callback to console
firebase.database().ref().update(updates, function(error) {
  if (!error) {
    console.log("successfully updated firebase!");
  } else {
    console.error(error);
  }
});

Getting all of the data from Firebase at once

//get a snapshot containing everything in the database
myDb.once("value").then(function (snapshot) {

  //extract the result
  var dataset = snapshot.val();
  console.log(dataset);

  //get an array containing the keys
  var keys = Object.keys(dataset);
  console.log(keys);

  //loop through everything in the array by key
  for (var j = 0; j < keys.length; j++){
    console.log( dataset[keys[j]] );
  }

  /* --ALTERNATIVELY-- */
  
  //get an array containing all of the the values in the dataset
  var values = Object.values(dataset);
});

Functions

Functions must first be declared, this tells the browser what they do and what arguments they require, but does not actually do anything else:

/**
 * Add two numbers together
 */
function addUp(arg1, arg2) {
  return arg1 + arg2;
}

To make the function actually run, you must then call it:

//get my name from the function and store it in a variable called result
var result = addup(2, 4);

//result = 6

GeoJSON

GeoJSON is a spatial data format commonly used for web applications.

You can add it to a map like this:

// load the geojson data, style it, set events and add to map
geojson = L.geoJson(countries, { }).addTo(map);

You can add it with a global style like this:

// load the geojson data and style it
geojson = L.geoJson(countries, { 
	style: {
		weight: 0.5,
		color: 'white',
		fillOpacity: 1,
		fillColor: 'lightgrey',
	}
}).addTo(map);

You can add it with a dynamic style (based upon property values) like this:

geojson = L.geoJson(countries, {
	style: style,		//set the style using a function
}).addTo(map);

/**
 * This function styles the data (a single country)
 */
function style(feature) {
	
	//return a style
	return {
		weight: 0.5,
		color: 'white',
		fillOpacity: 1,
		fillColor: getColour(feature.properties.POP_EST)	//the colour is set using a function
	};
}

GeoJSON and Events

Events can be added to each individual feature in a GeoJSON layer using thre onEachFeature option, which calls a specified function once for each feature in the GeoJSON dataset:

// load the geojson data, style it, set events and add to map
geojson = L.geoJson(countries, {
	style: style,			//set the style using your function
	onEachFeature: setEvents,	//set the hover events using your function
}).addTo(map);

You then add events to a GeoJSON layer just as you would the map or anything else in leaflet: using the .on() function.

/**
 * Create a function to tie the mouseover and mouseout events to re-styling the layer
 */
function setEvents(feature, layer) {
	layer.on({
		mouseover: highlightFeature,
		mouseout: resetHighlight,
	});
}

Objects

Objects in JavaScript are created using constructors. For example, coordinates are stored in Leaflet using a LatLng object, which can be made using a constructor like this:

const latlng = L.latLng(50.5, 30.5);

Operators

An operator is a mathematical symbol that produces a result based upon two values (which are normally stored in variables). In the following table you can see some of the simplest operators, along with some examples:

Operator Explanation Symbol(s) Example
add/concatenate Used to add two numbers together, or concatenate two strings together. + 6 + 9;

"Hello " + "world!";
subtract, multiply, divide These do what you’d expect them to do in basic maths. -, *, / 9 - 3;

8 * 2;

9 / 3;
assignment operator You’ve seen this already, it assigns a value to a variable. = myVariable = "Bob";
identity operator Does a test to see if two values are equal to one another, and returns a true/false (Boolean) result. === myVariable = 3;

myVariable === 4; (false)
negation, not equal Returns the logically opposite value of what it preceeds; it turns a true into a false, etc.

When it is used alongside the equality operator, the negation operator tests whether two values are not equal.
!, !== myVariable = 3;

!(myVariable === 3); (false)

myVariable = 3;

myVariable !== 3; (false)
greater than, less than these operators compare the operands and determine whether the one to the left is greater than (>) or less than (<) the one on the right.

When combined with an equals (>=, <=), the meaning changes to greater than or equal to or less than or equal to respectively.
>, >=, <, <= var2 > var1

var1 <= var2
increment, decrement ++, -- Adds one to and subtracts one from its operand respectively. If used as a prefix operator (++x), returns the value of its operand after adding one; if used as a postfix operator (x++), returns the value of its operand before adding one. If x is 3, then ++x sets x to 4 and returns 4, whereas x++ returns 3 and, only then, sets x to 4.

Thousands Separators

Covert numbers like 1234567 to more easily readable formats like 1,234,567 with this simple toLocaleString() function.

// add thousands separators to a string representing a number
parseFloat(numberAsString).toLocaleString('en-GB');

// add thousand separators to a number (results in a string)
number.toLocaleString('en-GB');

Variables

// if you DON'T want to edit the value
const myVariable = "Jonny";

// if you DO want to edit the value
let myVariable = "Jonny";

Basic Variable Types:

Variable Explanation Example
String A string of text. To signify that the variable is a string, you should enclose it in quote marks. let myVariable = "Bob";
Number A number. Numbers don’t have quotes around them. let myVariable = 10;
Boolean A True/False value. The words true and false are special keywords in JavaScript, and don’t need quotes. let myVariable = true;

Variables can also hold functions, objects and arrays.

This course has not yet begun.
Course material will appear here week by week as the course progresses.