4. Data Visualization on the website hosted in AWS S3 (HTML, JavaScript, CSS)

Julia R
6 min readJul 29, 2023

--

This article is about report/dashboard building using report data saved in AWS S3. It is part of the project where all is automated. From the very beginning of data collection until the data analytics publishing on the website, there is no manual process.

More can be found in Github.

Requirements:

AWS S3, HTML, Java, CSS

After Containerized Lambda exports the data from MySQL to S3, the website can update the charts automatically by setting up refresh option.

  1. JavaScript + HTML
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="refresh" content="900"> <!-- Refresh every 15 minutes -->
<title>VoirLeMonde</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
<!--
The template applies google charts
-->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script src="js/Chart.min.js"></script>
</head>

<body id="reportsPage">
<div class="" id="home">
<nav class="navbar navbar-expand-xl">
<div class="container h-100">
<a class="navbar-brand" href="dashboard.html">
<h1 class="tm-site-title mb-0">Canada Price Indexes for Food, Cosmetics, Energy and Tobacco</h1>
</a>
<button class="navbar-toggler ml-auto mr-0" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<i class="fas fa-bars tm-nav-icon"></i>
</button>

<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mx-auto h-100">

<li class="nav-item">
<a class="nav-link" href="blog.html">

Blog
</a>
</li>

<li class="nav-item">
<a class="nav-link" href="here is your github" target="_blank">

Github
</a>
</li>

<li class="nav-item">
<a class="nav-link" href="here is your linkedin" target="_blank">

LinkedIn
</a>
</li>


</ul>

</div>
</div>

</nav>
<div class="container">
<div class="row">
<div class="col">
<p class="text-white mt-5 mb-5">Data Source: <a rel="data from canada statistics" href="https://www150.statcan.gc.ca/n1/en/type/data?MM=1" target="_blank">https://www150.statcan.gc.ca/n1/en/type/data?MM=1</a></p>

</div>
</div>
<!-- row -->

<div class="row tm-content-row">
<!-- Map of Canada -->
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 tm-block-col">
<div class="tm-bg-primary-dark tm-block">
<h2 class="tm-block-title"> Map </h2>
<div id="map_div"></div>
</div>
</div>
<script type="text/javascript">
google.charts.load('current', {
'packages':['geochart'],
// Note: you might need to get a mapsApiKey for your project.
// See: https://developers.google.com/chart/interactive/docs/basic_load_libs#load-settings

});
google.charts.setOnLoadCallback(drawMap);

function drawMap() {
var data = google.visualization.arrayToDataTable([
["Province","Popularity"],
["CA-AB", 1],
["CA-BC", 2],
["CA-MB", 3],
["CA-NB", 4],
["CA-NL", 5],
["CA-NS", 6],
["CA-ON", 7],
["CA-PE", 8],
["CA-QC", 9],
["CA-SK", 10],
["CA-NT", 11],
["CA-NU", 12],
["CA-YT", 13],

]);

var options = {
region: 'CA',
resolution:'provinces',
backgroundColor : {fill: 'transparent'},
//backgroundColor : {stroke: '#f5f5dc'},
//backgroundColor : {strokeWidth: '50'},
keepAspectRatio:true,
colorAxis: {
colors: ['#DF6040', '#E5954E','#F7B44B','#FFE2A6','#A59679','#c93756']
//colors: ['#fb9b00', '#e4430b','#aa1906','#3a1008','#e4f9f6','#a9e6f5']
},
//defaultColor: '#161D15',
datalessRegionColor: 'transparent',
forceIFrame: true,

};

var container = document.getElementById('map_div');
var chart = new google.visualization.GeoChart(container);

chart.draw(data, options);
};

</script>

<!-- Line Chart -->
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 tm-block-col">
<div class="tm-bg-primary-dark tm-block">
<h2 class="tm-block-title">Changes in Years</h2>
<canvas id="lineChart"></canvas>
</div>
<script>

async function drawLineChart() {

const response = await fetch('priceindex_line.json');
const data = await response.json();
// console.log(data);
length = data.length;
console.log(length);
labels_3 = [];
values_3 = [];
labels_4 = [];
values_4 = [];

for (i = 0; i < length; i++) {

if (3==data[i].Status) {
labels_3.push(data[i].Year);
values_3.push(data[i].AVG);
}
if (4==data[i].Status) {
labels_4.push(data[i].Year);
values_4.push(data[i].AVG);
}
}

new Chart(document.getElementById("lineChart"), {
type: 'line',
data: {
labels: labels_3,
datasets: [
{
label: "Energy",
data: values_3,
fill: false,
borderColor: "rgba(153, 102, 255, 1)",
cubicInterpolationMode: "monotone",
pointRadius: 0
},
{
label: "Tobacco",
data: values_4,
fill: false,
borderColor: "rgb(255,255,0)",
cubicInterpolationMode: "monotone",
pointRadius: 0
}
]
},
options: {
scales: {
yAxes: [
{
scaleLabel: {
display: true,
labelString: "Currency: CAD"
}
}
]
},
legend: { display: true },
title: {
display: true,
text: 'Price Changes Over Years'
}
}
});
};
drawLineChart();
</script>

</div>
<!-- bar Chart -->
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 tm-block-col">
<div class="tm-bg-primary-dark tm-block tm-block-taller">
<h2 class="tm-block-title">Milk and Steak</h2>
<canvas id="barChart"></canvas>
</div>
<script>

async function getData_bar() {

// 1. if to fetch from remote file (succeed)
// const response = await fetch('here is the file url');
// const data = await response.json();
// const data = await response;
// console.log(data);

// 2. if to fetch local file

const response = await fetch('priceindex_bar1.json');
const data = await response.json();
// console.log(data);

length = data.length;
console.log(length);
labels = [];
values = [];
year=[];
month=[];
for (i = 0; i < length; i++) {
labels.push(data[i].Products);
values.push(data[i].Price);
year.push(data[i].Year);
month.push(data[i].Month);
}
console.log(labels);
console.log(values);
year=year[0];
month=month[0];
console.log(year);
console.log(month);

new Chart(document.getElementById("barChart"), {
type: 'bar',
data: {
labels: labels,
datasets: [
{
label: "Products",
data: values,
backgroundColor: [
"#F7604D",
"#4ED6B8",
"#A8D582",
"#D7D768",
"#9D66CC",
"#DB9C3F",
"#3889FC"
],
borderWidth:0

}
]
},
options: {
responsive: true,
scales: {
yAxis: [
{
barPercentage: 0.2,
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: "Currency: CAD"
}
}
],
xAxis:{
beforeUpdate(axis) {
const labels = axis.chart.data.labels;
for (let i=0; i<labels.length; i++) {
const lbl = labels[i];
if (typeof lbl === 'string' && lbl.length > 10) {
labels[i] = lbl.substring(0, 10); // cutting
}
}
}
}
},
legend: { display: false },
title: {
display: true,
text: 'Prices in '+ month + ' / '+ year
}
}
});
};
getData_bar();
</script>

</div>

<!-- Line Chart_2 -->
<div class="col-sm-12 col-md-12 col-lg-6 col-xl-6 tm-block-col">
<div class="tm-bg-primary-dark tm-block tm-block-taller">
<h2 class="tm-block-title">Changes in Years</h2>
<canvas id="lineChart_2"></canvas>
</div>
<script>

async function getData_line_2() {

const response = await fetch('priceindex_line.json');
const data_2 = await response.json();
// console.log(data_2);

length = data_2.length;
console.log(length);
labels_1 = [];
values_1 = [];
labels_2 = [];
values_2 = [];

for (i = 0; i < length; i++) {

if (1==data_2[i].Status) {
labels_1.push(data_2[i].Year);
values_1.push(data_2[i].AVG);
}
if (2==data_2[i].Status) {
labels_2.push(data_2[i].Year);
values_2.push(data_2[i].AVG);
}

}

new Chart(document.getElementById("lineChart_2"), {
type: 'line',
data: {
labels: labels_1,
datasets: [
{
label: "Food",
data: values_1,
fill: false,
borderColor: "rgb(75, 192, 192)",
cubicInterpolationMode: "monotone",
pointRadius: 0
},
{
label: "Cosmetics",
data: values_2,
fill: false,
borderColor: "rgba(255,99,132,1)",
cubicInterpolationMode: "monotone",
pointRadius: 0
}

]
},
options: {
scales: {
yAxes: [
{
scaleLabel: {
display: true,
labelString: "Currency: CAD"
}
}
]
},
legend: { display: true },
title: {
display: true,
text: 'Price Changes'
}
}
});
};
getData_line_2();
</script>

</div>
</div>
</div>
<footer class="tm-footer row tm-mt-small">
<div class="col-12 font-weight-light">
<p class="text-center text-white mb-0 px-4 small">
Copyright &copy; <b>2023</b> All rights reserved.

Design: <a rel="nofollow noopener" href="https://.../..." class="tm-footer-link">Template</a>
</p>
</div>
</footer>
</div>

<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/moment.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script>
Chart.defaults.global.defaultFontColor = 'white';
</script>
</body>

</html>

1) to Get Data from data files

Note: If we test local files or remote files for html file, we need to use Async Function. Otherwise, we can get ‘CORS’ error.

2) to filter data for report charts

The projects applies multiple .json files as data source for charts in the website. One file may be used for more than one chart.

3) to build charts in the website using JavaScript libraries

aaa) ‘Chart.js’ is a free JavaScript library for making HTML-based charts. It supports chart types like Area Chart, Bar Chart, Bubble Chart, Doughnut and Pie Charts, Line Chart, Mixed Chart Types, Polar Area Chart, Radar Chart, and Scatter Chart. According to data characteristics in this project, Line Chart, Bar Chart will be used to analyze data.

bbb) ‘Google Charts’ is also applied to generate a Canada GeoChart where provinces are shown.

ccc) Table is created for source data as well.

4) to fill the filtered data into the charts

5) to set to refresh the webpage every 15 minutes

When Cloud DataCenter receives updated data from data source, it starts automatically to calculate the data for reporting and save it as .json files in s3 bucket (bucket for website). From there, reports/charts are updated based on the files in the bucket.

--

--