<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Raster JS Demo</title>
<!-- 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>
<!-- Turf.js -->
<script src="https://npmcdn.com/@turf/turf/turf.min.js"></script>
<!-- Chart.js -->
<script src="https://www.chartjs.org/dist/2.9.3/Chart.min.js"></script>
<!-- Proj4js - required BEFORE dataset import -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<!-- rasterjs dataset - must be AFTER Proj4js import -->
<script src="./kampala.js"></script>
<style>
/* style the map container*/
#map {
width: 400px;
height: 500px;
float: left;
}
/* style the chart container */
#canvas-holder {
width: 400px;
float: right;
}
/* style the container */
#container {
width: 800px;
}
/* style the green ration report text */
#green-ratio {
font-family: sans-serif;
text-align: center;
}
</style>
</head>
<body onload="initMap();">
<!-- container to control the layout -->
<div id="container">
<!-- the map -->
<div id='map'></div>
<!-- container for the pie chart -->
<div id="canvas-holder">
<!-- chart -->
<canvas id="chart"></canvas>
<!-- report text -->
<div id='green-ratio'>
<p>Click the map to calculate Green Ratio</p>
</div>
</div>
</div>
<script>
// global variable for map
let map, ndviLayer, circleLyr;
/**
* Initialise the map
*/
function initMap() {
// initialise the map
map = L.map('map').setView([0.348384, 32.568841], 14);
// add some satellite imagery tiles
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
}).addTo(map);
// pre-calculate the entire ndvi layer to save computation in click listener
calculateNdviLayer();
// initialise chart data
let chartConfig = {
type: 'pie',
data: {
datasets:[{
data: [], // initialise with empty array as no data yet
backgroundColor: ["#999999", "#27b827"],
}],
labels: ['Not Green', 'Green'],
},
};
// initialise the chart
const ctx = document.getElementById('chart').getContext('2d');
let pieChart = new Chart(ctx, chartConfig);
// set a click listener to report NDVI witihn 500m of a click
map.on('click', function(e) {
// calculate the green ratio
const greenRatio = parseFloat((calculateGreenRatio([e.latlng.lng, e.latlng.lat])* 100).toFixed(2));
// update the chart to reflect the ratio
chartConfig.data.datasets[0].data = [100 - greenRatio, greenRatio];
pieChart.update();
//output ratio to console
document.getElementById('green-ratio').innerHTML = "<p>" + greenRatio + "% Green Space</p>";
});
}
/**
* Calculate the green ratio within 500m of a location
*/
function calculateGreenRatio(lngLat){
// get a polygon representing a 500m radius around the clicked location
const circle = turf.circle(turf.point(lngLat), 0.5, {
units: 'kilometers',
steps: 64,
});
// remove previous circle layer and add a new one
if (circleLyr) map.removeLayer(circleLyr);
circleLyr = L.geoJson(circle, {
style: {
color: 'yellow',
weight: 0.5,
opacity: 0.6,
fillColor: '#ff7800',
fillOpacity: 0.3,
}
}).addTo(map);
// get bounds of the circle and extract the corners in image space
const bl = kampala.geo2image([circleLyr.getBounds().getWest(), circleLyr.getBounds().getSouth()]);
const tr = kampala.geo2image([circleLyr.getBounds().getEast(), circleLyr.getBounds().getNorth()]);
//loop through all cells in dataset within the bounds
let points = [];
for (let x = bl[0]; x <= tr[0]; x++) {
for (let y = tr[1]; y <= bl[1]; y++){
// add point with property for ndvi value
points.push(turf.point(kampala.image2geo([x, y]),{ ndvi: ndviLayer[x][y] }));
}
}
// get the coordinates from the array that are in the aoi
const pointsWithin = turf.pointsWithinPolygon(turf.featureCollection(points), circle);
// count the number of green cells (NDVI above 0.5 threshold)
let green = 0;
for (let p = 0; p < pointsWithin.features.length; p++) {
if (pointsWithin.features[p].properties.ndvi >= 0.5) {
green++;
}
}
// calculate green ratio and return
return green / pointsWithin.features.length;
}
/**
* Calculate NDVI for entire dataset
*/
function calculateNdviLayer(){
// initialise global array for ndvi results
ndviLayer = [];
// loop through every single column
for(let x = 0; x < kampala.width; x++){
// add a new column
ndviLayer.push([]);
// loop through every single pixel in the column
for(let y = 0; y < kampala.height; y++){
// calculate ndvi for each pixel
ndviLayer[x][y] = ndvi(kampala.band3[x][y], kampala.band4[x][y]);
}
}
}
/**
* Normalised Difference Vegetation Index calculation
*/
function ndvi(red, nir){
return (nir - red) / (nir + red);
}
</script>
</body>
</html>
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.
This course has not yet begun.
Course material will appear here week by week as the course progresses.