Choropleth attempt
Tried implementing choropleth while keeping filters and adding zoom function. Zoom & drag function currently active on ALL graph types. Looking to keep zoom & drag only on choropleth
This commit is contained in:
parent
a49cc771b8
commit
3de4b68136
4 changed files with 113 additions and 2 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
|
|
@ -10,6 +10,9 @@
|
|||
<!-- D3.js library -->
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
|
||||
<!-- TopoJSON client library for map rendering -->
|
||||
<script src="https://unpkg.com/topojson-client@3"></script>
|
||||
|
||||
<!-- Save SVG as PNG library -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/save-svg-as-png/1.4.17/saveSvgAsPng.min.js"></script>
|
||||
</head>
|
||||
|
|
|
|||
1
script/countries-50m.json
Normal file
1
script/countries-50m.json
Normal file
File diff suppressed because one or more lines are too long
111
script/script.js
111
script/script.js
|
|
@ -83,8 +83,19 @@ darkModeToggle.addEventListener('click', () => {
|
|||
});
|
||||
|
||||
|
||||
// Load data
|
||||
d3.csv("PHSwithContinent.csv").then(function(data) {
|
||||
// Add 'choropleth' to chart type selector on page load
|
||||
if (document.getElementById("chartTypeSelect") && !Array.from(document.getElementById("chartTypeSelect").options).some(opt => opt.value === "choropleth")) {
|
||||
const option = document.createElement("option");
|
||||
option.value = "choropleth";
|
||||
option.text = "Choropleth Map";
|
||||
document.getElementById("chartTypeSelect").appendChild(option);
|
||||
}
|
||||
|
||||
// Load both CSV and TopoJSON data
|
||||
Promise.all([
|
||||
d3.csv("PHSwithContinent.csv"),
|
||||
d3.json("script/countries-50m.json")
|
||||
]).then(function([data, world]) {
|
||||
// Normalize Socio-economic status values to match educationLevels
|
||||
data.forEach(d => {
|
||||
if (d["Socio-economic status"] === "Pre-primary, primary and lower secondary education") {
|
||||
|
|
@ -96,6 +107,7 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
|||
}
|
||||
});
|
||||
const allData = data;
|
||||
const worldData = world;
|
||||
|
||||
// Populate continent and education dropdowns
|
||||
const continents = Array.from(new Set(data.map(d => d.CONTINENT))).sort();
|
||||
|
|
@ -166,6 +178,8 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
|||
drawDotPlot(filteredData);
|
||||
} else if (selectedChart === "heatmap") {
|
||||
drawHeatmap(filteredData);
|
||||
} else if (selectedChart === "choropleth") {
|
||||
drawChoropleth(filteredData, worldData);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -524,4 +538,97 @@ function drawHeatmap(data) {
|
|||
|
||||
g.append("g")
|
||||
.call(d3.axisLeft(y));
|
||||
}
|
||||
|
||||
// Choropleth map drawing function
|
||||
function drawChoropleth(data, worldData) {
|
||||
// Remove any previous zoom behavior
|
||||
svg.on("mousedown.zoom", null).on("touchstart.zoom", null).on("touchmove.zoom", null).on("touchend.zoom", null);
|
||||
|
||||
// Setup projection and path
|
||||
const projection = d3.geoMercator()
|
||||
.fitSize([width, height], topojson.feature(worldData, worldData.objects.countries));
|
||||
const path = d3.geoPath().projection(projection);
|
||||
|
||||
// Prepare a map from country name to value
|
||||
const valueByCountry = {};
|
||||
data.forEach(d => {
|
||||
valueByCountry[d["Reference area"]] = parseFloat(d.OBS_VALUE);
|
||||
});
|
||||
|
||||
// Color scale for choropleth
|
||||
const colorScale = d3.scaleSequential()
|
||||
.interpolator(d3.interpolateYlGnBu)
|
||||
.domain([0, 100]);
|
||||
|
||||
// Remove previous map if any
|
||||
g.selectAll(".country").remove();
|
||||
g.selectAll(".choropleth-legend").remove();
|
||||
|
||||
// Draw countries
|
||||
const countries = topojson.feature(worldData, worldData.objects.countries).features;
|
||||
const countryPaths = g.selectAll(".country")
|
||||
.data(countries)
|
||||
.join("path")
|
||||
.attr("class", "country")
|
||||
.attr("d", path)
|
||||
.attr("fill", d => {
|
||||
const name = d.properties.name;
|
||||
const val = valueByCountry[name];
|
||||
return val != null ? colorScale(val) : "#ccc";
|
||||
})
|
||||
.attr("stroke", darkMode ? "#222" : "#fff")
|
||||
.attr("stroke-width", 0.5)
|
||||
.on("mouseover", function(event, d) {
|
||||
const name = d.properties.name;
|
||||
const val = valueByCountry[name];
|
||||
tooltip.html(`<strong>${name}</strong><br/>${val != null ? val + "%" : "No data"}`)
|
||||
.style("left", (event.pageX + 10) + "px")
|
||||
.style("top", (event.pageY - 40) + "px")
|
||||
.transition().duration(300)
|
||||
.style("opacity", 1)
|
||||
.style("transform", "translateY(-10px)");
|
||||
})
|
||||
.on("mouseout", function() {
|
||||
tooltip.transition().duration(500)
|
||||
.style("opacity", 0)
|
||||
.style("transform", "translateY(0px)");
|
||||
});
|
||||
|
||||
// Add zoom and pan
|
||||
svg.call(d3.zoom()
|
||||
.scaleExtent([1, 8])
|
||||
.on("zoom", (event) => {
|
||||
g.attr("transform", event.transform);
|
||||
})
|
||||
);
|
||||
|
||||
// Add legend for color scale
|
||||
const legendWidth = 200;
|
||||
const legendHeight = 12;
|
||||
const legendSvg = g.append("g")
|
||||
.attr("class", "choropleth-legend")
|
||||
.attr("transform", `translate(${width - legendWidth - 20},${height - 40})`);
|
||||
|
||||
const defs = svg.select("defs").empty() ? svg.append("defs") : svg.select("defs");
|
||||
defs.selectAll("#choropleth-gradient").remove();
|
||||
const linearGradient = defs.append("linearGradient")
|
||||
.attr("id", "choropleth-gradient");
|
||||
linearGradient.selectAll("stop")
|
||||
.data(d3.range(0, 1.01, 0.01))
|
||||
.enter().append("stop")
|
||||
.attr("offset", d => d)
|
||||
.attr("stop-color", d => colorScale(d * 100));
|
||||
|
||||
legendSvg.append("rect")
|
||||
.attr("width", legendWidth)
|
||||
.attr("height", legendHeight)
|
||||
.style("fill", "url(#choropleth-gradient)");
|
||||
|
||||
// Legend axis
|
||||
const legendScale = d3.scaleLinear().domain([0, 100]).range([0, legendWidth]);
|
||||
const legendAxis = d3.axisBottom(legendScale).ticks(5).tickFormat(d => d + "%");
|
||||
legendSvg.append("g")
|
||||
.attr("transform", `translate(0,${legendHeight})`)
|
||||
.call(legendAxis);
|
||||
}
|
||||
Loading…
Reference in a new issue