# Covid-19 Data Visualizer – Real-Time API-Powered Interactive Map
### Integrating APIs, GeoJSON, and Leaflet.js to Build an Interactive Map.
<br>
by **Chueh-an Kuo(郭爵安)**
**Full Report on HackMD:**
https://hackmd.io/@EhY6joeNTVu4Z9GCmlZRxQ/Sku9R7ACkx
**Covid-19 Interactive Map Website Link:**
https://chuehankuo.github.io/Covid-19-Statistics-Visualizer/
**Full Report on GitHub Gist:**
https://gist.github.com/ChuehanKuo/a10fe6c5e1c58c7f418cb45e1e6afae4
**GitHub Link(full code):**
https://github.com/ChuehanKuo/Covid-19-Statistics-Visualizer
<br>
## Table of Contents
### Project Overview
#### 1. Abstract
#### 2. Motivation
#### 3. Objective
### Concepts
#### 1. Introduction to APIs and Data Fetching
#### 2. Introduction to Leaflet.js and GeoJSON
### Code
#### 1. HTML Design (Structure)
#### 2. CSS Design (Style)
#### 3. JavaScript Design (Brain)
### Results
#### 1. Visual Screenshots and Key Features
### Conclusions: Lessons and Goals
#### 1. Reflection and Learning
#### 2. Future Plans
### References
<br>
## Abstract
This project presents the development of a real-time interactive COVID-19 data visualization website. **Using the API from disease.sh as the data source, combined with Leaflet.js and GeoJSON for mapping, the website displays global COVID-19 statistics through an interactive world map.**
**Rather than relying on pre-built dashboards or libraries, I manually implemented the core functionalities—fetching live data, processing country boundaries, handling data mismatches, and rendering interactive tooltips on a map.** This hands-on approach not only deepened my understanding of API integration, data visualization, and frontend development but also challenged me to solve practical issues such as matching country names between datasets and optimizing data flow for real-time updates.
**The conceptual introductions included in this report reflect my learning process as I explored APIs, data fetching techniques, and map-based visualization frameworks like Leaflet.js and GeoJSON.** Writing these sections helped me consolidate what I learned, turning abstract concepts into practical knowledge applied in this project.
This work demonstrates my ability to integrate data from external sources, transform it for visualization, and build an interactive user experience from scratch. **More importantly, it captures my growth in understanding web-based data visualization, from the foundational concepts to the technical execution.**
<br>
## Motivation
When I first considered building a data visualization project, I knew very little about how web-based dashboards worked. I often came across COVID-19 trackers and interactive maps online, but I never questioned how the data was fetched, processed, and turned into visual insights. **To me, these tools seemed complex and out of reach. However, I wanted to break that barrier and understand what goes into creating such an application.**
Initially, my goal was simple: learn how to fetch data from an API and display it on a webpage. **But as I began exploring real-time COVID-19 data, I realized there was much more behind the scenes—handling asynchronous requests, processing JSON structures, and integrating geographical data using mapping libraries.** Each of these areas introduced challenges I hadn’t anticipated. I didn’t even know how to connect country names from the data source to the shapes on a world map, which seemed like a minor detail at first but quickly became a significant problem to solve.
What began as a technical exercise soon turned into a personal challenge. I wanted to not just piece together different tools, but understand how each part worked and how they could come together to form a cohesive, interactive experience. **This project became a way for me to step outside of purely backend or theoretical learning and build something visual, something users could interact with in real time.**
**It wasn’t just about creating a map—it was about learning how to bring raw data to life.** **By pushing through these challenges, I deepened my understanding of web development, data handling, and visualization, and I came away with a project that felt both technically rewarding and personally meaningful.**
<br>
## Objective
**The goal of this project is to create an interactive web-based map that visualizes global COVID-19 data in real-time.** By integrating live data from the API disease.sh and combining it with Leaflet.js and GeoJSON for mapping, **the project aims to present key pandemic statistics—including confirmed cases, deaths, and recoveries—through a user-friendly interface.** This involves managing the entire process, from fetching and processing external data sources to handling geographical boundaries and ensuring accurate country mapping. **The project also addresses practical challenges, such as resolving data inconsistencies between datasets, while focusing on building an engaging and informative visual experience for users.**
<br>
## Introduction to APIs and Data Fetching
### 1. APIs
An API (Application Programming Interface) is a system that allows different software to communicate and exchange data. There are many types of APIs, each designed for different scenarios. Some of the most common ones include:
* RESTful APIs: The most widely used for web services, based on HTTP methods.
* GraphQL APIs: Allows clients to request specific data, giving more control over responses.
* SOAP APIs: Older and more rigid, relying on XML for structured communication, often used in enterprise systems.
For this project, I used the disease.sh API, which is a RESTful API. RESTful APIs follow simple rules:
* Data is accessed by URLs called endpoints.
* They use HTTP methods like GET (to fetch data), POST (to send data), and DELETE (to remove data).
* Data is commonly returned in JSON format, making it easy to work with in JavaScript.
***-> In my case, the endpoint retrieves COVID-19 data for all countries.***

### 2. Data Fetching
To retrieve data from the API, I used JavaScript’s fetch() function, which sends HTTP requests and handles the asynchronous nature of data fetching. The asynchronous approach (asynchronous refers to operations that don’t block the program while waiting for a task to complete). Ensures that the webpage remains responsive while waiting for the data to load. Basically means the webpage won't freeze while the data is loading.
* The process involves:
1. Sending a GET request to the API endpoint.
2. Receiving the JSON response containing COVID-19 data.
3. Parsing this data into JavaScript objects for further processing and visualization.
Example:
```javascript
const res = await fetch('https://disease.sh/v3/covid-19/countries');
const data = await res.json();
```
* fetch() initiates the request.
* await ensures that the response is received before proceeding.
* The response is converted from JSON into a usable JavaScript object.
<br>
#### Reflections:
**When I first started working with APIs, I had no idea what asynchronous data fetching really meant. I thought I could just grab data from an API and use it immediately. But when I tried to process the data before it finished loading, I ran into errors and undefined values. It was confusing at first—I didn’t understand why the data wasn’t ready.
That’s when I learned about asynchronous operations in JavaScript. Since fetching data from an external source takes time, the program doesn’t just pause and wait—it keeps running. To handle this, I had to use async/await properly, making sure the data was fully fetched before I tried to use it.
This part of the project helped me understand how web data flows work—it’s not just about making a request, it’s about managing the timing of when that data arrives. Learning how to work with promises, fetch(), and asynchronous patterns became a big turning point in this project.**
<br>
## Introduction to Leaflet.js and GeoJSON
### 1. Leaflet.js
Leaflet.js is a JavaScript library designed for building interactive maps on websites. It provides tools to render geographical data and enhance it with features like zooming, panning, tooltips, and layers.
Key components of Leaflet.js:
* Tile layers: The base maps (like country borders, labels, terrain) fetched from external services (such as OpenStreetMap, CartoDB).
* Overlays: Additional layers added on top, such as country shapes, data points, or interactive features.
* Event handling: Allows user interactions like hovering, clicking, or zooming to trigger responses (such as displaying tooltips upon trigger).
***-> In this project, Leaflet.js serves as the mapping framework to display:***
* The world map background (loaded from CartoDB tiles).
* The interactive country boundaries (loaded from a GeoJSON file).
Tooltips and hover effects for data visualization.

### 2. GeoJSON
GeoJSON is a data format based on JSON (JavaScript Object Notation) that represents spatial features like points, lines, and polygons. In this project, GeoJSON polygons define the shapes of country borders, which are rendered on the map.
Key aspects of GeoJSON:
* It contains geometrical data (latitude and longitude coordinates).
* It defines properties for each shape (e.g., country names, IDs).
* It is easy to integrate with mapping libraries like Leaflet.js.
***-> For this project:***
* I used a world GeoJSON file someone else had made that includes the coordinates of all country boundaries.
* Each country shape is drawn on the Leaflet map based on these coordinates.
* The properties (such as country names) were used to match COVID-19 data with the map.
**However, there was a key challenge:**
**The country names in GeoJSON didn’t always match the country names in the COVID-19 API. For Example:**
* GeoJSON: "United States of America"
* API: "USA"
**To solve this, I implemented manual matching logic, ensuring the data aligned with the map shapes.**

***-> This is pre-made country boundries GeoJSON map made by Johan Sundström.***
<br>
#### Reflections
**It was hard figuring out how GeoJSON and Leaflet.js worked at first. I didn’t know how they worked together to create the interactive map that it is now. After going through a lot of tutorials and documentation, I finally understood what each of them means and how they connect.**
There are actually two layers on this map:
* CartoDB, which is built from OpenStreetMap, forms the base layer. This includes the non-interactive parts like country names, terrain, and streets.
* GeoJSON is the layer that contains just country borders—it’s raw coordinate data with no visual features on its own.
* Leaflet.js is what brings them together. It takes the GeoJSON data and provides all the functionality—it lets me:
1. Render the country shapes.
2. Add tooltips that show COVID-19 data.
3. Enable zooming and hover effects.
**Understanding this layering helped me see the bigger picture of how geographical data and web frameworks work together. What started as two unrelated tools eventually became the foundation of this interactive map.**
<br>
## HTML Design (Structure)
### 1. Basic settings for the webpage
```html
<!-- 🔹 Settings 🔹 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" /> <!--Character encoding, ensures all characters work-->
<title>Global COVID-19 Statistics Map</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <!--Leaflet CSS-->
<link rel="stylesheet" href="styles.css" /> <!--Custom stylesheet-->
</head>
```
* Webpage title set to "Global COVID-19 Statistics Map".
* Configures the viewport for responsive design, ensuring that the page scales properly across different devices like mobile phones and desktops.(not yet completed)
* Links to Leaflet’s CSS library, which provides the default styling for the interactive map.
* Links to custom made style.css.
### 2. Header Settings
```html
<!-- 🔹 Header section 🔹 -->
<header>
<div class="header-content">
<h1>🌍 Global COVID-19 Statistics Map</h1>
<p>Hover over a country to see data on total cases, recoveries, and deaths.</p>
</div>
<div class="about-container">
<button id="about-btn">About</button>
</div>
</header>
```
* Page header is called ""🌍 Global COVID-19 Statistics Map".
* Sub-header is "Hover over a country to see data on total cases, recoveries, and deaths."
* Includes an "About" button that triggers a modal with project and author information.
### 3. Map Container and Scripts
```html
<div id="map"></div>
```
```html
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="script.js"></script>
```
* The map container is a simple div with ID "map" - this is where Leaflet will render the interactive map.
* Scripts includes leaflet library and my own custom script.
### 4. Global Total Statistics
This part is the white bar above the world map that shows global total statistics:
```html
<!-- 🔹 Global statistics section 🔹 -->
<!--Global Cases-->
<div id="global-stats">
<div class="stat-box">
<h3>Total Cases</h3>
<p id="global-cases"></p>
</div>
<!--Global Total Recovered-->
<div class="stat-box">
<h3>Total Recovered</h3>
<p id="global-recovered"></p>
</div>
<!--Global Total Deaths-->
<div class="stat-box">
<h3>Total Deaths</h3>
<p id="global-deaths"></p>
</div>
<!--Last Update Time-->
<div class="stat-box">
<h3>Last Updated</h3>
<p id="last-updated"></p>
</div>
</div>
```
* Three global statistics with one update time is shown above the map:
1. Global Cases
2. Global Total Recovered
3. Global Total Deaths
4. Last Update Time
* Each statistic is contained in its own box with a heading and a placeholder paragraph that will be populated with JavaScript(allows data to be dynamic).
### 5. Map Guide Panel
```html
<!-- 🔹 Map Guide panel on the left bottom side 🔹 (newly added)-->
<div id="legend">
<h3>Map Guide</h3>
<div class="legend-item">
<div class="color-box default"></div>
<span>Default</span>
</div>
<div class="legend-item">
<div class="color-box hover"></div>
<span>Hover</span>
</div>
<p class="legend-note">Hover over a country to see detailed COVID-19 statistics</p>
</div>
```
* This panel explains the color-coding used in the map.
* Visual samples show default and hover state colors.
1. Default color is light blue.
2. Hover color is orange.
* Positioned at bottom-left corner of the webpage for easy reference without obstructing the map.
### 6. Top 5 Countries by Case Panel
```html
<div id="top-countries">
<h3>Top 5 Countries by Cases</h3>
<div id="top-countries-list"></div>
</div>
```
* Shows the top 5 countries with the most cases.
* Populated by JavaScript, shows data dynamically from the API.
* Positioned at the left-side of the webpage for easy reference without obstructing the map.
### 7. About Button
```html
<div id="about-modal">
<button class="close-btn" onclick="document.getElementById('about-modal').style.display='none'">×</button>
<h2>About This COVID-19 Map</h2>
<b>
Project Created by Chueh-an Kuo(郭爵安)
</b>
<p>
This project started with a simple question: how can we make global crises feel more real, more human? I built an interactive world map that visualizes real-time COVID-19 data by country, combining API statistics with GeoJSON borders using JavaScript, HTML, and Leaflet.js. Users can hover over any country to explore its confirmed cases, recoveries, and deaths.
</p>
<p>
While building this, I faced challenges like inconsistent country names and mobile responsiveness—but they became opportunities to think critically, debug creatively, and improve the user experience. More importantly, the project taught me that programming isn’t just about solving technical problems — it’s about connecting information and people.
</p>
<p>
Through this map, I hope to remind users that each number represents a life, and every hover is a step toward empathy.
</p>
<div class="footer-note">
Data from <a href="https://disease.sh" target="_blank">disease.sh</a> • Auto-updates every 5 minutes.<br />
Last updated: April 2025
</div>
</div>
```
* Modal containing project and author information that appears when the About button is clicked.
* Includes project motivation, challenges faced, and personal reflection.
* Credits the data source (disease.sh) and notes the automatic refresh interval.
* Closes with a simple click on the × button.
<br>
## CSS Design (Style)
### 1. Header and Body Styling
```javascript
/* 🔹 Main 🔹 */
html, body {
margin: 0;
padding: 0;
height: 100%;
font-family: 'Segoe UI', sans-serif;
background-color: #f4f4f4;
color: #333;
}
header {
position: fixed;
top: 0;
left: 0;
right: 0;
background: #0077b6;
color: white;
padding: 1.2rem 1rem;
z-index: 1000;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
display: flex;
justify-content: space-between;
align-items: center;
}
header h1 {
margin: 0 0 0.4rem;
font-size: 1.8rem;
}
header p {
margin: 0;
font-size: 0.95rem;
opacity: 0.9;
}
.header-content {
flex: 1;
text-align: center;
}
```
* Base Styling:
1. Uses light-gray background for the page.
2. Sets Segoe UI as the font.
3. Remove default padding and margin.
* Header Styling:
1. Fixed Positioning to ensure header stay fixed when scrolling.
2. Blue background for header.
3. Using FlexBox for layout design.
### 2. Map Positioning and Configuration
Positions map in the center, and below the headers.
```css
/* 🔹 Map 🔹 */
#map {
position: absolute;
top: 100px;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
}
```
### 3. Tooltip Design
Tooltip is the box that appears when you hover over a country on this map.
```css
/* 🔹 Tooltip 🔹 */
.tooltip-content {
font-size: 14px;
line-height: 1.5;
}
```
### 4. Global Stats Design
```css
/* 🔹 Global Stats 🔹 */
#global-stats {
position: fixed;
top: 100px;
left: 0;
right: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
justify-content: space-around;
padding: 10px 20px;
z-index: 1000;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.stat-box {
text-align: center;
padding: 0 15px;
}
.stat-box h3 {
margin: 0;
font-size: 0.9rem;
color: #0077b6;
}
.stat-box p {
margin: 5px 0 0;
font-size: 1.2rem;
font-weight: bold;
}
```
### 5. Map Guide Panel Design
```css
/* 🔹 Map Guide Panel Styles 🔹 */
#legend {
position: fixed;
left: 10px;
top: 88%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
max-width: 200px;
}
#legend h3 {
margin-top: 0;
color: #0077b6;
font-size: 1rem;
margin-bottom: 10px;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.color-box {
width: 20px;
height: 20px;
margin-right: 10px;
border: 1px solid #ccc;
}
.color-box.default {
background-color: #0077b6;
opacity: 0.3;
}
.color-box.hover {
background-color: #ff8800;
opacity: 0.5;
}
.legend-note {
font-size: 0.8rem;
margin-top: 10px;
color: #666;
}
```
* Rounded corners for a clean appearance.
* Positioned at bottom-left corner of the webpage to avoid obstructing the map.
### 6. Top Countries by Cases Panel Design
```css
/* 🔹 Top Countries by Cases Panel Styles 🔹 */
#top-countries {
position: fixed;
left: 10px;
top: 60%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
max-width: 250px;
}
#top-countries h3 {
margin-top: 0;
color: #0077b6;
font-size: 1rem;
margin-bottom: 10px;
}
.country-item {
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px solid #eee;
}
.country-item:last-child {
border-bottom: none;
}
```
* Rounded corners for a clean appearance.
* Positioned at left side of the webpage to avoid obstructing the map.
### 7. Adjust Leaflet Zoom Button Position
After adding "Top 5 Countries by Cases" and "Map Guide" Panel the original zoom button from leaflet.js fell out of place.
This code is used to adjust the zoom button:
```css
/* 🔹 Fix Leaflet zoom control placement 🔹 */
.leaflet-top.leaflet-left {
top: 100px;
left: 10px;
z-index: 1001;
}
```
### 8. About Button (click-open design)
```css
/* 🔹 About Button (click-open design) 🔹 */
.about-container {
position: absolute;
left: 20px;
top: 310px;
z-index: 1001;
}
#about-btn {
background-color: #0077b6;
color: white;
border: none;
border-radius: 20px;
padding: 6px 16px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
#about-btn:hover {
background-color: #005f91;
transform: scale(1.05);
}
.close-btn {
position: absolute;
top: 10px;
right: 20px;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close-btn:hover {
color: #0077b6;
}
```
* Positions the button right above Top 5 Countries by Cases panel.
* Creates a rounded button with blue themed color.
* Includes interactive effects (color change and slight enlargement when hovered over).
* Incorporates smooth transitions for a better user experience.
### 9. About Button (dialog design)
```css
/* 🔹 About Button (dialog design) 🔹 */
#about-modal {
position: fixed;
top: 60%;
left: 50%;
transform: translate(-50%, -50%);
background: #ffffff;
border-radius: 16px;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);
padding: 30px 28px;
max-width: 480px;
width: 90%;
z-index: 2000;
display: none;
font-family: 'Segoe UI', sans-serif;
}
#about-modal h2 {
margin-top: 0;
font-size: 1.4rem;
color: #0077b6;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
margin-bottom: 20px;
}
#about-modal p {
margin-bottom: 12px;
font-size: 0.95rem;
line-height: 1.6;
color: #333;
}
#about-modal a {
color: #0077b6;
text-decoration: none;
}
#about-modal a:hover {
text-decoration: underline;
}
#about-modal .close-btn {
position: absolute;
top: 12px;
right: 16px;
background: none;
border: none;
font-size: 1.4rem;
color: #666;
cursor: pointer;
transition: color 0.2s ease;
}
#about-modal .close-btn:hover {
color: #000;
}
#about-modal .footer-note {
font-size: 0.85rem;
color: #777;
margin-top: 15px;
border-top: 1px solid #eee;
padding-top: 12px;
}
```
* Centers the modal in the viewport using fixed positioning.
* Create a clean white container with rounded corners and shadow.
* Uses high z-index (2000) to ensure it appears above all other elements.
* Is hidden by default (display: none) until triggered by JavaScript.
* Includes styling for headings, paragraphs, and links
* Provides a close button at the top-right corner
* Features a differently-styled footer area for additional information.
### 10. Adjustments for other devices (other than pc or laptop)
**This section is a work-in-process**, I haven't completely fixed it yet. This is the setting configuration for other devices. **Currently, this project(website) works best on pc and laptops.** I have not yet found a fix for cellphones and I-Pads.
```css
/* 🔹 Responsive adjustments for mobile 🔹 */
@media (max-width: 600px) {
.about-container {
right: 10px;
top: 15px;
}
#about-btn {
font-size: 0.8rem;
padding: 4px 12px;
}
}
```
```css
/* Detect User's environment(using what electronic device), changing CSS accordingly */
/*
This Part is work-in-process. Trying to figure out how to not break the margins when using mobile phones
*/
/* 🔹 Mobile Layout Fixes 🔹 */
@media (max-width: 600px) {
header h1 {
font-size: 1.4rem;
}
header p {
font-size: 0.8rem;
}
#global-stats {
flex-wrap: wrap;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
padding: 5px 10px;
gap: 6px;
}
.stat-box {
flex: 1 1 45%;
text-align: center;
font-size: 0.8rem;
padding: 6px 0;
}
.stat-box h3 {
font-size: 0.8rem;
}
.stat-box p {
font-size: 1rem;
}
#map {
top: 260px; /* Adjust based on new header + global-stats height */
}
#top-countries,
#legend {
max-width: 150px;
padding: 10px;
font-size: 0.75rem;
}
#top-countries h3,
#legend h3 {
font-size: 0.85rem;
}
.country-item {
font-size: 0.75rem;
margin-bottom: 6px;
}
.legend-note {
font-size: 0.7rem;
}
}
```
<br>
#### Reflections:
**After countless rounds of trial and error, I realized that making a website fully responsive across different devices—especially mobile screens—is much harder than I expected. Adjusting font sizes, panel widths, and map positioning without breaking the layout feels like solving a puzzle with too many moving parts. Sometimes, fixing one section would break another. Although I managed to implement some basic adjustments for mobile view, it’s far from perfect. This section is still a work-in-progress, and I’m continuing to tweak the design to make sure the map, panels, and global stats display correctly on any screen size. Learning how to balance flexibility and consistency in web design has been an ongoing challenge in this project.**
<br>
## JavaScript Design (Brain)
### 1. Map Initialization and Configuration
This block below initializes the Leaflet map and configures its basic settings.
```javascript
// 🔹 Initialize the Leaflet map and configure basic settings 🔹
const map = L.map('map', { //creates leaflet map inside 'map' in html
minZoom: 2.49, //limit the minimum and maximum zoom level
maxZoom: 6
}).setView([35, 0], 2); //center the map
```
* minZoom and maxZoom sets the maximum and minimum zoom level, the numbers 2.49 and 6 are the results of trial and errors.
* setview is used set the initial center point at latitude 35, longitude 0(about the center of the world map). The numbers 35 and 0 makes sure the viewer see the whole world map. It is slightly norther than the equator since Antarctica doesn't have covid-19 information. Therefore, there isn't a necessity to show it. Also, default zoom level is set to 2.
<br>
This block below adds base map tiles (background layers) from CartoDB's light-themed map, which displays country borders, labels, and basic geography:
```javascript
// 🔹 Add the base map(country names, borders, etc) the non-interactive parts, from OpenStreetMap 🔹
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
attribution: '© CartoDB, OpenStreetMap contributors', //credit the creators
subdomains: 'abcd', //for faster loading, request rotate across abcd
maxZoom: 6,
noWrap: true //stop map wrapping, so map won't repeat at the edges
}).addTo(map);
```
* L.tileLayer loads CartoDB's tiles that are built from OpenStreetMap base map.
* Subdomains abcd are used for faster loading. Basically requests rotate across abcd, making the loading process faster.
* "noWrap: true" stops map wrapping. It means that the world map won't wrap around(map won't loop infinitely).
<br>
This block below makes sure the user won't drag outside the map. Setting the boundary for the user.
```javascript
// 🔹 Define the bounds of the map to prevent dragging too far outside the world 🔹
map.setMaxBounds([
[-85, -180], // Southwest corner (latitude, longitude)
[85, 180] // Northeast corner (latitude, longitude)
]);
```
<br>
#### Reflections:
**Setting up the map initialization felt straightforward at first, but fine-tuning the details—like centering the map correctly and adjusting the zoom levels—took much more trial and error than expected. I assumed there would be standard values that worked for any map, but I quickly realized that every project has different needs based on data and design. The decision to slightly adjust the center northward to exclude Antarctica came from these iterations, balancing aesthetics and relevance to COVID-19 data.**
**Additionally, understanding the role of tile layers and attribution wasn’t something I initially considered important. I learned that proper attribution for map data providers like CartoDB and OpenStreetMap is not only ethical but also required. This helped me appreciate the collaborative nature of web development and open-source tools. The map’s configuration stage taught me that small design choices—like zoom limits and map bounds—can significantly affect the user experience.**
<br>
### 2. Data Fetching and Processing
This block below initializes an empty object called covidStats. This object will store Covid-19 statistics for each country.
```javascript
// 🔹 Store fetched COVID-19 data for quick access by country name (converted to lowercase) 🔹
const covidStats = {};
```
<br>
Create a function to fetch covid-19 data from disease.sh:
```javascript
// 🔹 Fetch COVID-19 statistics for all countries from disease.sh 🔹
async function fetchCovidData() {
const res = await fetch('https://disease.sh/v3/covid-19/countries');
const data = await res.json(); //turn json data into js data
data.forEach(entry => {
const name = entry.country;
covidStats[name.toLowerCase()] = { //store data in lower case for easier matching
cases: entry.cases ?? 'N/A', //if there is data, show. If there isn't show N/A
recovered: entry.recovered ?? 'N/A',
deaths: entry.deaths ?? 'N/A',
flag: entry.countryInfo?.flag ?? null //shows the flag
};
});
}
```
* After fetching covid-19 data from disease.sh, we then store the statistics – Cases, Recovered, Deaths, Flag – in the covidStats object.
* When storing, it converts country names(keys) to lower case for later country matching.
* If data is missing, use N/A
* If flag is missing, use null
* disease.sh is an API.
* await res.json() converts the response into JSON format so it can be used in JavaScript.
<br>
#### Reflections:
**This part of the project marked one of my biggest learning curves. I originally thought fetching data from an API was as simple as calling a function and instantly getting usable results. However, I quickly encountered issues with undefined values, which led me to discover the importance of asynchronous behavior in JavaScript. Understanding how await pauses execution until data is ready became a breakthrough moment for me.**
**Storing the fetched data in an object (covidStats) using lowercase keys felt like a small optimization, but it ended up being essential when I had to match country names later. The decision to preprocess and clean data right after fetching also helped reduce complications in later stages. This experience enhanced my sense of designing data flow logically—by transforming the data early, I simplified almost every function that came after.**
<br>
### 3. Global Statistics Update
Defines the function updateGlobalStats(), which calculates global totals for cases, recoveries, and deaths.
```javascript
// 🔹 Function to update global statistics 🔹
function updateGlobalStats() {
let totalCases = 0;
let totalRecovered = 0;
let totalDeaths = 0;
```
* Initializes the counters:
1. totalCases
2. totalRecovered
3. totalDeaths
<br>
Loop through all countries stored in covidStats object.
```javascript
for (const country in covidStats) {
const stats = covidStats[country];
if (typeof stats.cases === 'number') totalCases += stats.cases;
if (typeof stats.recovered === 'number') totalRecovered += stats.recovered;
if (typeof stats.deaths === 'number') totalDeaths += stats.deaths;
}
```
* Adds up data from every country and accumulate it onto the three counters.
* Also checks if there is a valid number for that country, and only add in valid numbers, not N/As.
<br>
This is a helper function used to format large numbers for readability. For example: 1000000 would be formatted into 1,000,000.
```javascript
const formatNumber = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
```
* This is used to insert commas at every thousand.
<br>
Updates the webpage's global statistics section with the newly calculated totals:
```javascript
document.getElementById('global-cases').textContent = formatNumber(totalCases);
document.getElementById('global-recovered').textContent = formatNumber(totalRecovered);
document.getElementById('global-deaths').textContent = formatNumber(totalDeaths);
document.getElementById('last-updated').textContent = new Date().toLocaleString();
}
```
* Uses document.getElementById(id).textContent to insert the formatted totals into the individual HTML elements:
1. Global Cases -> #global-cases
2. Recovered -> #global-recovered
3. Deaths -> #global-deaths
4. Last Updated Time -> #last-updated, formatted as a local date and time string.
### 4. Optimize Flag Loading Speed
This block was later added because when cursor hovers above a country, the flag doesn't show immediately.
```javascript
// 🔹 Load country flags faster 🔹
function preloadFlags() {
//loop through all countries we have data for
for (const country in covidStats) {
//if the country has a flag URL
if (covidStats[country].flag) {
//create an image element to preload it
const img = new Image();
img.src = covidStats[country].flag;
}
}
}
```
* This block iterates through each country stored in the covidStats object and checks if that particular country has a flag URL. If it does, assign flag URL to a new image element.
* This is basically used to enhance user experience.
<br>
#### Reflections:
**The optimization for flag loading speed was something I added at the very end of the project. The idea to include country flags in the tooltip actually came from a friend, and while it seemed like a simple enhancement, it introduced a new problem: the flags didn’t load fast enough. When hovering over a country, the flag would lag or appear after a noticeable delay, which disrupted the smooth experience I wanted.**
**Solving this turned out to be simpler than I expected. By preloading all the flag images beforehand, I ensured that they were cached and ready by the time the tooltip appeared. I discovered this technique through a YouTube tutorial, which reminded me how valuable it is to seek out community solutions. More importantly, this small tweak taught me that not all elements on a webpage load at the same pace—each component may require its own tailored optimization to ensure the best user experience.**
<br>
### 5. Data Preparation
Defines the main function loadWorldMap:
```javascript
//🔹 Load country boundaries (GeoJSON) and apply COVID data tooltips 🔹
async function loadWorldMap() {
await fetchCovidData(); // Step 1: Get COVID data before drawing map
updateTopCountries();
preloadFlags(); //call the preload flag function
// Step 2: Load country boundary data (GeoJSON) //using premade GeoJSON data from github
const geoData = await fetch('https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json');
const worldGeo = await geoData.json();
//🔹 Global statistics on the top bar 🔹
updateGlobalStats();
```
* This function is responsible for:
1. Loading COVID-19 data.
2. Loading GeoJSON country boundaries.
3. Rendering both on the interactive map with tooltips.
* await fetchCovidData() is used to ensure covid data is available before drawing map.
* Calls updateTopCountries(), to update the Top 5 Countries by Cases panel on the left-hand side of the screen.
* Call the preloadFlags() function.
* Fetches the GeoJSON file from GitHub (this is pre-made by someone else. I'll reference at the end).
* Converts GeoJSON response into worldGeo.
* Updates global statistics bar above the map.
<br>
#### Reflections:
**This part of the project really pushed me to understand the flow of data between different sources. Initially, I thought fetching COVID-19 data and rendering the map would be two separate tasks—but combining them required careful timing and coordination. I needed to make sure the COVID-19 statistics were fully loaded before drawing the map, or else the tooltips and country highlights wouldn’t work correctly.**
**The structure of this function—loading data, preloading flags, fetching GeoJSON boundaries, and then finally updating the map—taught me how important sequencing is in web development. Missing just one step or calling something out of order could break the entire visualization. This section helped me appreciate how backend data handling and frontend rendering need to align perfectly to create a seamless user experience.**
<br>
### 6. Country Mapping
Due to several countries (in total 46 countries) not loading data properly, I had to individually run through several possible names of that country to ensure matching data from API and GeoJSON(map). Without this check, tooltips or data mapping would fail for those countries.
For example USA: If the GeoJSON country name is either "United States of America" or "United States", it assigns the COVID-19 data from the API’s "usa" key.
```javascript
//🔹 Individual checks for problematic countries 🔹
/*
Due to some countries having different id names for the api and GeoJSON(map). This if-tree goes through all possible
id names, trying to link the api and map. If not found, fallback to most possible country, or else,
use placeholder showing limited info.
*/
//Check USA
if (countryName.toLowerCase() === "united states of america" || countryName.toLowerCase() === "united states") {
stats = covidStats["usa"];
//Check UK
}else if (countryName.toLowerCase() === "united kingdom") {
stats = covidStats["uk"];
//Check Bosnia and Herzegovina
}else if (countryName.toLowerCase() === "bosnia and herzegovina") {
if (covidStats["bosnia"]) {
stats = covidStats["bosnia"];
}else if (covidStats["bosnia-and-herzegovina"]) {
stats = covidStats["bosnia-and-herzegovina"];
}else {
stats = covidStats["bosnia-herzegovina"];
}
// ... many more country mappings ...
//Check North Korea
}else if (countryName.toLowerCase() === "north korea" ||
countryName.toLowerCase() === "democratic people's republic of korea" ||
countryName.toLowerCase() === "democratic peoples republic of korea") {
if (covidStats["n-korea"]) {
stats = covidStats["n-korea"];
} else if (covidStats["north-korea"]) {
stats = covidStats["north-korea"];
} else if (covidStats["korea, north"]) {
stats = covidStats["korea, north"];
} else if (covidStats["north korea"]) {
stats = covidStats["north korea"];
} else if (covidStats["dprk"]) {
stats = covidStats["dprk"];
} else if (covidStats["democratic people's republic of korea"]) {
stats = covidStats["democratic people's republic of korea"];
} else if (covidStats["democratic peoples republic of korea"]) {
stats = covidStats["democratic peoples republic of korea"];
} else if (covidStats["democratic-peoples-republic-of-korea"]) {
stats = covidStats["democratic-peoples-republic-of-korea"];
} else if (covidStats["democratic-people's-republic-of-korea"]) {
stats = covidStats["democratic-people's-republic-of-korea"];
} else if (covidStats["northkorea"]) {
stats = covidStats["northkorea"];
} else if (covidStats["nkorea"]) {
stats = covidStats["nkorea"];
} else if (covidStats["korea(north)"]) {
stats = covidStats["korea(north)"];
} else if (covidStats["korea (north)"]) {
stats = covidStats["korea (north)"];
} else if (covidStats["korea north"]) {
stats = covidStats["korea north"];
} else if (covidStats["kor. north"]) {
stats = covidStats["kor. north"];
} else if (covidStats["democratic korea"]) {
stats = covidStats["democratic korea"];
} else if (covidStats["n. korea"]) {
stats = covidStats["n. korea"];
} else if (covidStats["n.korea"]) {
stats = covidStats["n.korea"];
} else if (covidStats["korea dem. rep."]) {
stats = covidStats["korea dem. rep."];
} else if (covidStats["korea, dem. rep."]) {
stats = covidStats["korea, dem. rep."];
} else if (covidStats["korea, dem. people's rep."]) {
stats = covidStats["korea, dem. people's rep."];
} else if (covidStats["korea, democratic people's republic of"]) {
stats = covidStats["korea, democratic people's republic of"];
} else {
stats = covidStats["prk"];}
/*
!!This line is important. Default fallback if none of the if and if-else statements work.
If none work, change geoJSON id to lowercase to match API id.
*/
}else {
stats = covidStats[countryName.toLowerCase()];
}
/*
Country check summary: Republic of Congo, South Korea, North Korea took a lot more effort,
but still couldn't find Republic of Congo's corresponding id.
*/
```
* Solves name mismatches between GeoJSON and API data.
* Uses manual mapping for countries with inconsistent naming (like USA, UK, Bosnia, North Korea).
* Ensures COVID-19 data matches the correct country shapes on the map, allowing tooltips to work properly.
<br>
#### Reflections:
**Out of the entire project, this part was definitely the most tedious and time-consuming. After I got the map displaying and the COVID-19 data fetching properly, I assumed the country names would match up easily. But that assumption was wrong. The API and the GeoJSON file used different country names—sometimes abbreviations like “USA” vs. “United States of America,” sometimes different spellings, and sometimes completely different formats.**
**I first tried to make a simple function to handle the mismatches, but it failed on too many edge cases. In the end, I had to manually check each country, one by one. For 46 countries, I wrote out different possible names and matched them with the corresponding API data. North Korea alone had more than 20 name variations I needed to cover. Every time I thought I fixed them all, a new broken case would pop up, and I’d have to go back and tweak the matching logic again.**
**This section really showed me how complicated it can be to merge data from different sources. It’s not always about writing clean code—sometimes it’s about being patient, testing, and making manual adjustments. Although this solution works, I know it isn’t the best way. In the future, I’d like to explore better methods for solving these kinds of mismatches, like using a global standard list of country names and codes to match them more easily. But for this project, I focused on making it work.**
<br>
### 6. Interactive Tooltip Creation
Attaches a tooltip for each country layer from GeoJSON. This tooltip shows the country's covid-19 data when hovered over.
```javascript
// 🔹 Show tooltip for every country — even if data is missing 🔹 !!!Important (show data part)
//tooltip is the box that shows up when hovered over
layer.bindTooltip(() => { //attach tooltip to country layer(GeoJSON)
if (stats) { //if there is stats, stats=true, then continue
return `
<div class="tooltip-content">
<strong>${countryName}</strong><br />
${stats.flag ? `<img src="${stats.flag}" alt="${countryName} flag" width="30"><br />` : ''}
Cases: ${stats.cases}<br />
Recovered: ${stats.recovered}<br />
Deaths: ${stats.deaths}
</div>
`;
} else { //if data is missing show this
return `
<div class="tooltip-content">
<strong>${countryName}</strong><br />
No COVID-19 data available.
</div>
`;
}
}, { sticky: true }); //!!Important. Makes tooltip follow cursor.
```
* Returns the HTML content for the tooltip when data exists:
1. Country name (bolded).
2. Flag image (if available).
3. Cases, Recovered, and Deaths numbers.
* At the end, we implement "sticky: true" to make the tooltip follow the cursor.
* if(stats) makes sures only when there is stats available, return stats.
* A fallback is also implemented incase there isn't any data. For example: Antarctica doesn't have any covid-19 data, so it returns No COVID-19 data available.
#### Reflections:
**Getting the tooltip to work felt like unlocking a key feature of this project. At first, I was excited just to get the map and data to display, but once I added tooltips that pop up when you hover over a country, the whole project finally felt interactive. This step really brought the data to life.**
**The most important piece here was making sure the tooltip followed the cursor smoothly, which wasn't as simple as I thought. I discovered the sticky: true option in Leaflet’s tooltip settings, which made the tooltip stay attached to the cursor instead of staying static in one place. Without this, the tooltip would just float awkwardly and feel disconnected from the user’s action. That single line—sticky: true—made all the difference in making the map feel more responsive and polished.**
**It was a small discovery, but it taught me that sometimes the smallest details can transform the user experience. A feature like this might seem minor, but it adds a layer of interactivity that makes the data feel more personal and engaging.**
### 7. Visual Styling
Applies default and hover over color for each country.
```javascript
// 🔹 Hover Style, When hovered over--> show orange, when default --> light blue 🔹
layer.on({
mouseover: () => layer.setStyle({ fillColor: '#ff8800', fillOpacity: 0.5 }), //orange
mouseout: () => layer.setStyle({ fillColor: '#0077b6', fillOpacity: 0.3 }) //light blue
});
},
```
* When cursor is not on country -> shows default color – light blue.
* When cursor is on country -> show hover color – orange.
* layer.on is a Leaflet.js method that attaches event listeners to the country shapes. An event listener is a function awaiting something to happen, then respond to the action. Here it is waiting for the cursor to hover over a country, and the action would be to change tile color to orange.
<br>
```javascript
// 🔹 Default country polygon style 🔹
style: {
color: '#666', // Border color
weight: 0.5, // Border width
fillColor: '#0077b6', // Default fill color
fillOpacity: 0.3 // Default transparency
}
}).addTo(map);
//map.fitBounds(geoJsonLayer.getBounds()); //removed
}
```
* This is the basic settings for country's:
1. Border color
2. Border Width
3. Default fill color
4. Default Transparency
* map.fitBounds was removed because it prevented the setview to be set where I wanted it.
<br>
#### Reflections:
**This section seemed simple, but the map.fitBounds() function gave me a lot of trouble. Originally, I used it to automatically adjust the map to fit all countries. But later, when I tried to manually set the center with setView(), they conflicted—fitBounds kept overriding my custom center and zoom levels.**
**In the end, I removed fitBounds entirely to maintain full control over the map's initial view. This taught me that sometimes built-in features can work against customization, and understanding when to use or remove them is key.**
<br>
### 7. Application Initialization
Calls loadWorldMap() function. This is the starting point that renders the entire interactive map when the page loads.
```javascript
// 🔹 Load the map and data 🔹
loadWorldMap();
```
<br>
#### Reflections:
**I had a bit of trouble figuring out when exactly to call loadWorldMap(). At first, I didn’t really think about timing—I just placed the function call at the bottom of my script. But sometimes, the map wouldn’t load properly, and I wasn’t sure why.**
**That’s when I learned about the DOM (Document Object Model). The DOM is like the blueprint of the webpage—it represents everything from divs and headers to the map container. The browser needs to finish building this structure first before JavaScript can interact with it.**
**If I called loadWorldMap() before the DOM was fully built, Leaflet wouldn’t find the map container, and nothing would show up. By waiting until the DOM was ready, I ensured that the map could render smoothly without errors.**
**This experience taught me that timing is crucial when working with JavaScript and the DOM. Knowing when to execute certain functions makes all the difference between a working app and a broken one.**
<br>
### 8. Auto-Update Functionality
Sets up automatic data refreshing every 5 minutes. This ensures the map always shows the latest covid-19 data.
```javascript
// 🔹 Automatically updates the Covid-19 data, so user see up-to-date info 🔹
//!!!Important
setInterval(async () => {
await fetchCovidData();
updateGlobalStats();
updateTopCountries();
}, 300000); // Refresh every 5 minutes
```
* setInterval() function:
1. await fetchCovidData() -> Fetches the latest data from the API.
2. updateGlobalStats() -> Recalculates and updates the global totals at the top bar.
3. updateTopCountries() -> Refreshes the Top 5 Countries panel (based on the latest case counts).
<br>
#### Reflections:
**At first, I thought the global statistics bar wasn’t updating because the data wasn’t refreshing frequently enough. I visited my project website multiple times, and the global numbers stayed the same, which made me think something was wrong with my update logic. That’s when I decided to learn how to automatically refresh data at a set interval. I implemented the setInterval function to fetch new data every 5 minutes, thinking that would fix the issue.**
**But after adding the auto-update function, I checked Worldometer (one of the sources used by disease.sh) and realized the source data itself wasn’t changing at the time. So the issue wasn’t my refresh logic—it was the external data being static. This non-mistake ended up teaching me a valuable lesson about understanding where your data comes from and not jumping to conclusions when debugging.**
<br>
### 9. Modal Functionality
This section is for the about button. The Modal is the block that appears when a user clicks the about button.
```javascript
// 🔹 About Modal functionality 🔹
document.addEventListener('DOMContentLoaded', function() {
// Get the modal elements
const modal = document.getElementById('about-modal');
const aboutBtn = document.getElementById('about-btn');
const closeBtn = document.querySelector('.close-btn');
```
* Grabs HTML elements and store them in variables.
<br>
Event Listeners for the modal (meaning after a certain function an action happens):
```javascript
// Open modal when About button is clicked
aboutBtn.addEventListener('click', function() {
modal.style.display = 'block';
});
// Close modal when X is clicked
closeBtn.addEventListener('click', function() {
modal.style.display = 'none';
});
});
```
* There are two functionalities:
1. Open modal when about button is clicked
2. Close modal when X is clicked
<br>
#### Reflections:
**At first, I only made a simple button without any interactive effects—it just sat there, clicked and opened the modal, but it felt dull and boring. I wanted it to feel more dynamic, so I went and learned how to make the button respond to the cursor—enlarging slightly and changing color when hovered over.**
**This wasn’t just about making it look better; it taught me how to connect CSS animations with JavaScript functionality, making the user experience smoother and more engaging. Adding these small interactive details really made the whole project feel polished.**
<br>
## Visual Screenshots and Key Features

***-> Main Functionalities:***
* Country changes color upon hover (blue to orange).
* Shows country tooltip with information like:
1. Cases
2. Recovered
3. Deaths
4. Country Flag
* Top Five Countries by Cases panel shown on the left side of the screen.
* A map guide is present on the bottom-left corner of the screen.
* Global Total Stats is available above the map with the following information
1. Total Cases
2. Total Recovered
3. Total Deaths
4. Statistic Last-Update-Time
<br>

***-> There is an about button with project information.***
<br>

***-> You can zoom in onto any area you want***
<br>
## Reflection and Learning
When I first started this project, I thought building an interactive map would be simple—just connect data to a map and display it. **But I quickly realized that the process was much more complex and filled with unexpected challenges.**
I struggled at the very beginning with asynchronous data fetching. I didn’t understand why my data wasn’t showing up when I needed it. **It wasn’t until I learned about async/await and the timing of data retrieval that I began to understand how the flow of web applications really works.** This felt like a small breakthrough—something that seemed so simple actually taught me how the web truly handles data behind the scenes.
Then came Leaflet.js and GeoJSON, two tools I had never worked with before. At first, I had no clue how they connected. It felt like trying to fit puzzle pieces together without knowing what the picture was supposed to be. **But through trial and error, documentation, and experimenting, I figured out how the base layer (CartoDB from OpenStreetMap) combined with GeoJSON country borders, and how Leaflet.js stitched them together into something interactive.**
The most tedious and time-consuming challenge was matching country names between the COVID-19 API and the GeoJSON map. I didn’t expect that something as simple as a country name could cause so many issues. But it did. Different spellings, abbreviations, even punctuation differences made it impossible to match some countries. I manually worked through 46 mismatched countries, adjusting the logic for each one. **This wasn’t just frustrating—it was exhausting. But it taught me that data alignment between sources is a real challenge in data visualization, one that isn’t talked about enough. I hope to find a better, more scalable solution for this in the future.**
Another subtle but important lesson came from preloading the flag images. When I first added flags to the tooltips, they lagged behind, making the user experience clunky. **Learning how to preload images was a small fix, but it made me realize that every part of a webpage loads differently, and sometimes they need individual attention to optimize performance.**
There were also moments where I misdiagnosed problems, like thinking the global statistics bar wasn’t refreshing because of a bug I caused. I spent time implementing an auto-update feature that refreshes the data every five minutes. Only later did I realize the source API (disease.sh) itself wasn’t updating because the underlying data from Worldometer hadn’t changed. **This “non-mistake” ended up accidentally teaching me more about data sources and the importance of understanding where your data comes from.**
Even small UI details pushed me further. I originally created a basic About button that just opened a modal. It worked, but it felt flat. So I went back and added hover effects and animations—learning how CSS and JavaScript work together to create smoother interactions. These finishing touches made the whole project feel more polished and complete.
**In the end, what seemed like a straightforward project became one of my most challenging and rewarding learning experiences. I didn’t just build an interactive map; I learned how to manage asynchronous data, handle data mismatches, optimize performance, and create an engaging user experience. Most importantly, I developed a better understanding of how different tools connect and work together, something I know will help me as I take on more advanced projects in the future.**
<br>
## Future Plans
This project opened my eyes to just how many moving parts go into building a fully functional data visualization tool. **While I’m proud of what I’ve accomplished, there are several areas I want to improve and expand on.**
**The first major upgrade would be to optimize the country name matching system.** Right now, the manual approach works, but it’s tedious and doesn’t scale well. I want to explore ways to automate the matching process, perhaps by integrating fuzzy string matching algorithms or building a mapping dictionary between common naming conventions across datasets. This would not only improve this project but also prepare me for handling similar issues in future data integration tasks.
**Another key area is responsive design.** The site works best on desktops and laptops, but the mobile experience still needs a lot of work. I want to dive deeper into CSS media queries, flexbox/grid systems, and maybe even mobile-first design principles to make sure the map and panels display correctly across all devices. Making the map truly responsive would elevate the user experience and make the tool more accessible to a wider audience.
**In terms of features, I’d love to expand the data set beyond COVID-19.** The framework is flexible enough to handle other types of global data—like vaccination rates, climate statistics, or even economic indicators. This could transform the project from a COVID-19 tracker into a general-purpose data visualization platform.
**Finally, this project sparked my interest in web-based data visualization libraries. I plan to explore tools like D3.js and Plotly.js to learn more advanced visualization techniques.** Understanding these libraries would allow me to create even more complex, dynamic visualizations in the future.
**Just like my House Prices project pushed me into machine learning and data science, this map project pushed me into the world of frontend development, APIs, and data visualization. I plan to carry these lessons forward, combining them with my growing skills in programming, data handling, and visualization to take on more ambitious projects ahead.**
## Reference
1. RESTful API Visualization – Source:
https://medium.com/@quinyhsieh/%E4%BD%95%E8%AC%82-rest-api-fbc87f8171fe
2. Leaflet Image – Source:
https://leafletjs.com/examples.html
3. GeoJSON file – Source:
https://github.com/johan/world.geo.json
4. Base Layer of World Map Adapted From OpenStreetMap – Source:
https://www.openstreetmap.org/copyright
5. Base Layer of World Map Adaption – CartoDB – Source:
https://carto.com/
6. API source – Source:
https://disease.sh/