Skip to content

Commit 547dbba

Browse files
Merge pull request #42 from com-480-data-visualization/#39-add-the-button-all-disasters-in-the-filter-of-the-globe
Added bubble charts
2 parents 0fe020b + 6d0cbd8 commit 547dbba

4 files changed

Lines changed: 846 additions & 15 deletions

File tree

docs/css/style.css

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,20 @@ button {
13761376
width: 100%;
13771377
}
13781378

1379+
.range-slider-container {
1380+
display: flex;
1381+
align-items: center;
1382+
gap: 8px;
1383+
width: 100%;
1384+
}
1385+
1386+
.range-slider-wrapper {
1387+
flex: 1;
1388+
display: flex;
1389+
flex-direction: column;
1390+
gap: 2px;
1391+
}
1392+
13791393
.range-values {
13801394
display: flex;
13811395
justify-content: space-between;
@@ -1387,6 +1401,50 @@ button {
13871401
color: var(--bluegrey);
13881402
}
13891403

1404+
.timeline-play-button {
1405+
flex-shrink: 0;
1406+
width: 32px;
1407+
height: 32px;
1408+
padding: 0;
1409+
background: rgba(151, 174, 196, 0.15);
1410+
border: 1px solid rgba(151, 174, 196, 0.4);
1411+
border-radius: 6px;
1412+
color: var(--bluegrey);
1413+
cursor: pointer;
1414+
display: flex;
1415+
align-items: center;
1416+
justify-content: center;
1417+
transition: all 0.2s ease;
1418+
}
1419+
1420+
.timeline-play-button:hover {
1421+
background: rgba(151, 174, 196, 0.25);
1422+
border-color: rgba(151, 174, 196, 0.6);
1423+
color: #fff;
1424+
}
1425+
1426+
.timeline-play-button:active {
1427+
transform: scale(0.95);
1428+
}
1429+
1430+
.timeline-play-button svg {
1431+
width: 18px;
1432+
height: 18px;
1433+
display: none;
1434+
}
1435+
1436+
.timeline-play-button .play-icon {
1437+
display: block;
1438+
}
1439+
1440+
.timeline-play-button.playing .play-icon {
1441+
display: none;
1442+
}
1443+
1444+
.timeline-play-button.playing .pause-icon {
1445+
display: block;
1446+
}
1447+
13901448
.range-slider {
13911449
position: relative;
13921450
width: 100%;
@@ -1588,6 +1646,200 @@ button {
15881646
text-transform: none;
15891647
}
15901648

1649+
.bubble-chart-button {
1650+
width: 100%;
1651+
margin-top: 16px;
1652+
padding: 12px 16px;
1653+
background: rgba(151, 174, 196, 0.15);
1654+
border: 1px solid rgba(151, 174, 196, 0.4);
1655+
border-radius: 10px;
1656+
color: var(--bluegrey);
1657+
font-family: var(--font-display);
1658+
font-size: 14px;
1659+
letter-spacing: 0.5px;
1660+
text-transform: uppercase;
1661+
cursor: pointer;
1662+
display: flex;
1663+
align-items: center;
1664+
justify-content: center;
1665+
gap: 8px;
1666+
transition: all 0.25s ease;
1667+
}
1668+
1669+
.bubble-chart-button:hover {
1670+
background: rgba(151, 174, 196, 0.25);
1671+
border-color: rgba(151, 174, 196, 0.6);
1672+
color: #fff;
1673+
transform: translateY(-1px);
1674+
}
1675+
1676+
.bubble-chart-button:active {
1677+
transform: translateY(0);
1678+
}
1679+
1680+
.bubble-chart-icon {
1681+
font-size: 10px;
1682+
line-height: 1;
1683+
letter-spacing: -1px;
1684+
display: inline-block;
1685+
}
1686+
1687+
.globe-icon {
1688+
font-size: 16px;
1689+
line-height: 1;
1690+
display: none;
1691+
}
1692+
1693+
.bubble-chart-button.active .bubble-chart-icon {
1694+
display: none;
1695+
}
1696+
1697+
.bubble-chart-button.active .globe-icon {
1698+
display: inline-block;
1699+
}
1700+
1701+
.bubble-chart-container {
1702+
position: absolute;
1703+
top: 0;
1704+
left: 0;
1705+
width: 100%;
1706+
height: 100%;
1707+
background: transparent;
1708+
opacity: 0;
1709+
pointer-events: none;
1710+
transition: opacity 0.8s ease;
1711+
z-index: 10;
1712+
}
1713+
1714+
#globeContainer.hide-for-bubbles {
1715+
opacity: 0;
1716+
pointer-events: none;
1717+
transition: opacity 0.5s ease;
1718+
}
1719+
1720+
.bubble-chart-container.visible {
1721+
opacity: 1;
1722+
pointer-events: auto;
1723+
}
1724+
1725+
.bubble-chart-container.hidden {
1726+
display: none;
1727+
}
1728+
1729+
#bubbleChartSvg {
1730+
width: 100%;
1731+
height: 100%;
1732+
}
1733+
1734+
.close-bubble-chart {
1735+
position: absolute;
1736+
top: 24px;
1737+
right: 24px;
1738+
width: 40px;
1739+
height: 40px;
1740+
padding: 0;
1741+
background: rgba(151, 174, 196, 0.15);
1742+
border: 1px solid rgba(151, 174, 196, 0.4);
1743+
border-radius: 8px;
1744+
color: var(--bluegrey);
1745+
cursor: pointer;
1746+
display: flex;
1747+
align-items: center;
1748+
justify-content: center;
1749+
transition: all 0.2s ease;
1750+
z-index: 11;
1751+
}
1752+
1753+
.close-bubble-chart:hover {
1754+
background: rgba(151, 174, 196, 0.25);
1755+
border-color: rgba(151, 174, 196, 0.6);
1756+
color: #fff;
1757+
}
1758+
1759+
.close-bubble-chart svg {
1760+
width: 20px;
1761+
height: 20px;
1762+
}
1763+
1764+
.bubble-zoom-controls {
1765+
position: absolute;
1766+
bottom: 24px;
1767+
right: 24px;
1768+
display: flex;
1769+
flex-direction: column;
1770+
gap: 8px;
1771+
z-index: 11;
1772+
}
1773+
1774+
.zoom-control-btn {
1775+
width: 40px;
1776+
height: 40px;
1777+
padding: 0;
1778+
background: rgba(151, 174, 196, 0.15);
1779+
border: 1px solid rgba(151, 174, 196, 0.4);
1780+
border-radius: 8px;
1781+
color: var(--bluegrey);
1782+
cursor: pointer;
1783+
display: flex;
1784+
align-items: center;
1785+
justify-content: center;
1786+
transition: all 0.2s ease;
1787+
}
1788+
1789+
.zoom-control-btn:hover {
1790+
background: rgba(151, 174, 196, 0.25);
1791+
border-color: rgba(151, 174, 196, 0.6);
1792+
color: #fff;
1793+
}
1794+
1795+
.zoom-control-btn svg {
1796+
width: 20px;
1797+
height: 20px;
1798+
}
1799+
1800+
.bubble-node {
1801+
cursor: pointer;
1802+
transition: opacity 0.3s ease;
1803+
}
1804+
1805+
.bubble-node circle {
1806+
stroke: rgba(255, 255, 255, 0.3);
1807+
stroke-width: 2px;
1808+
}
1809+
1810+
.bubble-node:hover circle {
1811+
stroke: rgba(255, 255, 255, 0.6);
1812+
stroke-width: 3px;
1813+
}
1814+
1815+
.bubble-label {
1816+
font-family: var(--font-garamond);
1817+
font-size: 12px;
1818+
fill: #000000;
1819+
text-anchor: middle;
1820+
pointer-events: none;
1821+
user-select: none;
1822+
text-transform: capitalize;
1823+
letter-spacing: 0.3px;
1824+
font-weight: 1000;
1825+
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.8))
1826+
drop-shadow(0 0 2px rgba(255, 255, 255, 0.5))
1827+
drop-shadow(0 1px 1px rgba(255, 255, 255, 0.6));
1828+
}
1829+
1830+
.bubble-label tspan {
1831+
fill: #000000;
1832+
}
1833+
1834+
.bubble-count {
1835+
font-family: var(--font-inter);
1836+
font-size: 11px;
1837+
fill: rgba(255, 255, 255, 0.8);
1838+
text-anchor: middle;
1839+
pointer-events: none;
1840+
user-select: none;
1841+
}
1842+
15911843
.earth-story-card {
15921844
position: absolute;
15931845
right: 27px;

docs/globe.html

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,28 @@
4040
<section class="filter-section" aria-labelledby="timePeriodTitle">
4141
<h2 class="filter-title" id="timePeriodTitle">Time period</h2>
4242
<div class="year-range-control">
43-
<div class="range-values" aria-hidden="true">
44-
<span id="yearMinVal">1975</span>
45-
<span id="yearMaxVal">2025</span>
46-
</div>
47-
<div class="range-slider" aria-label="Select event year range">
48-
<div class="range-track">
49-
<div class="range-fill" id="yearRangeFill"></div>
43+
<div class="range-slider-container">
44+
<button type="button" id="timelinePlayButton" class="timeline-play-button" aria-label="Play timeline animation">
45+
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor">
46+
<path d="M8 5v14l11-7z"/>
47+
</svg>
48+
<svg class="pause-icon" viewBox="0 0 24 24" fill="currentColor">
49+
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
50+
</svg>
51+
</button>
52+
<div class="range-slider-wrapper">
53+
<div class="range-values" aria-hidden="true">
54+
<span id="yearMinVal">1975</span>
55+
<span id="yearMaxVal">2025</span>
56+
</div>
57+
<div class="range-slider" aria-label="Select event year range">
58+
<div class="range-track">
59+
<div class="range-fill" id="yearRangeFill"></div>
60+
</div>
61+
<input class="range-input range-input-min" type="range" id="yearStartSlider" min="1975" max="2025" value="1975" aria-label="Start year" />
62+
<input class="range-input range-input-max" type="range" id="yearEndSlider" min="1975" max="2025" value="1975" aria-label="End year" />
63+
</div>
5064
</div>
51-
<input class="range-input range-input-min" type="range" id="yearStartSlider" min="1975" max="2025" value="1975" aria-label="Start year" />
52-
<input class="range-input range-input-max" type="range" id="yearEndSlider" min="1975" max="2025" value="2025" aria-label="End year" />
5365
</div>
5466
</div>
5567
</section>
@@ -64,8 +76,44 @@ <h2 class="filter-title" id="disasterTypesTitle">Disaster types</h2>
6476
<div class="events-label">Events displayed</div>
6577
<div class="data-status hidden" id="dataStatus"></div>
6678
</div>
79+
80+
<button type="button" id="bubbleChartButton" class="bubble-chart-button">
81+
<span class="bubble-chart-icon">●●●</span>
82+
<span class="globe-icon">🌍</span>
83+
<span class="bubble-chart-text">Open bubble chart</span>
84+
</button>
6785
</aside>
6886

87+
<!-- Bubble Chart Container -->
88+
<div id="bubbleChartContainer" class="bubble-chart-container hidden">
89+
<svg id="bubbleChartSvg"></svg>
90+
<button type="button" id="closeBubbleChart" class="close-bubble-chart" aria-label="Close bubble chart">
91+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
92+
<path d="M18 6L6 18M6 6l12 12"/>
93+
</svg>
94+
</button>
95+
<div class="bubble-zoom-controls">
96+
<button type="button" id="zoomIn" class="zoom-control-btn" aria-label="Zoom in">
97+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
98+
<circle cx="11" cy="11" r="8"/>
99+
<path d="M21 21l-4.35-4.35M11 8v6M8 11h6"/>
100+
</svg>
101+
</button>
102+
<button type="button" id="zoomOut" class="zoom-control-btn" aria-label="Zoom out">
103+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
104+
<circle cx="11" cy="11" r="8"/>
105+
<path d="M21 21l-4.35-4.35M8 11h6"/>
106+
</svg>
107+
</button>
108+
<button type="button" id="zoomReset" class="zoom-control-btn" aria-label="Reset zoom">
109+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
110+
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
111+
<path d="M3 3v5h5"/>
112+
</svg>
113+
</button>
114+
</div>
115+
</div>
116+
69117
<aside class="earth-story-card" aria-labelledby="earthStoryCardTitle">
70118
<h2 id="earthStoryCardTitle">Earth Story</h2>
71119
<p>How has the global profile of disasters shifted over time? Explore our Earth Story and uncover 50 years of striking trends.</p>

docs/js/data.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const WORLD_COUNTRY_COUNT = 220;
2020

2121
// Metadata for normalized values from the CSV `type` column.
2222
const DISASTER_TYPE_META = {
23+
all: { label: 'All', color: '#1E3A8A', order: 0 },
2324
drought: { label: 'Drought', color: '#FFC857', order: 10 },
2425
earthquake: { label: 'Earthquake', color: '#C96B32', order: 20 },
2526
extreme_temperature: { label: 'Extreme Temperature', color: '#FF5DB8', order: 30 },
@@ -390,14 +391,24 @@ function getAvailableDisasterTypes() {
390391
? Array.from(new Set(RAW_DATA.map(d => d.type).filter(Boolean)))
391392
: [];
392393

393-
return types
394+
// Add the "All" option at the beginning
395+
const allOption = {
396+
type: 'all',
397+
label: 'All',
398+
color: '#1E3A8A',
399+
order: 0
400+
};
401+
402+
const typeOptions = types
394403
.map(type => ({
395404
type,
396405
label: getDisasterTypeLabel(type),
397406
color: getDisasterColor(type),
398407
order: DISASTER_TYPE_META[type]?.order ?? 999
399408
}))
400409
.sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));
410+
411+
return [allOption, ...typeOptions];
401412
}
402413

403414
// Helper to format large numbers

0 commit comments

Comments
 (0)