added dot plot and heat map
This commit is contained in:
parent
effcec3bef
commit
17f63a4fc8
2 changed files with 324 additions and 95 deletions
11
index.html
11
index.html
|
|
@ -28,13 +28,22 @@
|
||||||
<option value="All">All</option>
|
<option value="All">All</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<label for="chartTypeSelect">Chart Type:</label>
|
||||||
|
<select id="chartTypeSelect">
|
||||||
|
<option value="grouped">Grouped Bar Chart</option>
|
||||||
|
<option value="dot">Dot Plot</option>
|
||||||
|
<option value="heatmap">Heatmap</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
<button id="resetButton">Reset Filters</button>
|
<button id="resetButton">Reset Filters</button>
|
||||||
<button id="downloadButton">Download Chart as PNG</button>
|
<button id="downloadButton">Download Chart as PNG</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="loading">Loading chart, please wait...</div>
|
<div id="loading">Loading chart, please wait...</div>
|
||||||
|
|
||||||
<svg width="960" height="600"></svg>
|
<svg width="1400" height="600"></svg>
|
||||||
|
|
||||||
<div class="legend" id="legend"></div>
|
<div class="legend" id="legend"></div>
|
||||||
|
|
||||||
|
|
|
||||||
406
script/script.js
406
script/script.js
|
|
@ -1,5 +1,5 @@
|
||||||
const svg = d3.select("svg"),
|
const svg = d3.select("svg"),
|
||||||
margin = {top: 80, right: 30, bottom: 150, left: 60},
|
margin = {top: 80, right: 30, bottom: 150, left: 160},
|
||||||
width = +svg.attr("width") - margin.left - margin.right,
|
width = +svg.attr("width") - margin.left - margin.right,
|
||||||
height = +svg.attr("height") - margin.top - margin.bottom,
|
height = +svg.attr("height") - margin.top - margin.bottom,
|
||||||
g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
||||||
|
|
@ -43,7 +43,7 @@ const noDataText = g.append("text")
|
||||||
.style("display", "none")
|
.style("display", "none")
|
||||||
.text("No data available for selected filters");
|
.text("No data available for selected filters");
|
||||||
|
|
||||||
// Dark mode toggle
|
// Dark Mode toggle button (unchanged)
|
||||||
const darkModeToggle = document.createElement('button');
|
const darkModeToggle = document.createElement('button');
|
||||||
darkModeToggle.textContent = 'Toggle Dark Mode';
|
darkModeToggle.textContent = 'Toggle Dark Mode';
|
||||||
Object.assign(darkModeToggle.style, {
|
Object.assign(darkModeToggle.style, {
|
||||||
|
|
@ -101,8 +101,10 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
||||||
const selectedContinent = d3.select("#continentSelect").property("value");
|
const selectedContinent = d3.select("#continentSelect").property("value");
|
||||||
const selectedSex = d3.select("#sexSelect").property("value");
|
const selectedSex = d3.select("#sexSelect").property("value");
|
||||||
const selectedEdu = d3.select("#incomeSelect").property("value");
|
const selectedEdu = d3.select("#incomeSelect").property("value");
|
||||||
|
const selectedChart = d3.select("#chartTypeSelect").property("value");
|
||||||
|
|
||||||
|
updateLegend(selectedChart);
|
||||||
|
|
||||||
// Two-line title
|
|
||||||
let title1 = "Self-Reported Health";
|
let title1 = "Self-Reported Health";
|
||||||
let title2 = "";
|
let title2 = "";
|
||||||
if (selectedContinent !== "All") title2 += `${selectedContinent}`;
|
if (selectedContinent !== "All") title2 += `${selectedContinent}`;
|
||||||
|
|
@ -123,13 +125,7 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
||||||
!isNaN(parseFloat(d.OBS_VALUE))
|
!isNaN(parseFloat(d.OBS_VALUE))
|
||||||
);
|
);
|
||||||
|
|
||||||
const countries = Array.from(new Set(filteredData.map(d => d["Reference area"])));
|
|
||||||
x0.domain(countries);
|
|
||||||
x1.domain(educationLevels).rangeRound([0, x0.bandwidth()]);
|
|
||||||
y.domain([0, 100]);
|
|
||||||
|
|
||||||
g.selectAll("*").remove();
|
g.selectAll("*").remove();
|
||||||
|
|
||||||
if (filteredData.length === 0) {
|
if (filteredData.length === 0) {
|
||||||
noDataText.style("display", null);
|
noDataText.style("display", null);
|
||||||
return;
|
return;
|
||||||
|
|
@ -137,90 +133,13 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
||||||
noDataText.style("display", "none");
|
noDataText.style("display", "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedData = filteredData.reduce((acc, d) => {
|
// 🆕 Decide which chart type to draw
|
||||||
const found = acc.find(v => v.Country === d["Reference area"]);
|
if (selectedChart === "grouped") {
|
||||||
const value = parseFloat(d.OBS_VALUE);
|
drawGrouped(filteredData);
|
||||||
const entry = { ...d, value };
|
} else if (selectedChart === "dot") {
|
||||||
if (found) found.values.push(entry);
|
drawDotPlot(filteredData);
|
||||||
else acc.push({ Country: d["Reference area"], values: [entry] });
|
} else if (selectedChart === "heatmap") {
|
||||||
return acc;
|
drawHeatmap(filteredData);
|
||||||
}, []);
|
|
||||||
|
|
||||||
const countryGroups = g.append("g")
|
|
||||||
.selectAll("g")
|
|
||||||
.data(groupedData)
|
|
||||||
.join("g")
|
|
||||||
.attr("transform", d => `translate(${x0(d.Country)},0)`)
|
|
||||||
.attr("class", "country-group");
|
|
||||||
|
|
||||||
const bars = countryGroups.selectAll("rect")
|
|
||||||
.data(d => d.values)
|
|
||||||
.join("rect")
|
|
||||||
.attr("x", d => x1(d["Socio-economic status"]))
|
|
||||||
.attr("width", x1.bandwidth())
|
|
||||||
.attr("fill", d => color(d["Socio-economic status"]))
|
|
||||||
.attr("y", height)
|
|
||||||
.attr("height", 0)
|
|
||||||
.attr("class", "bar")
|
|
||||||
.on("mouseover", function(event, d) {
|
|
||||||
tooltip.html(`<strong>${d["Reference area"]}</strong><br/>${d["Socio-economic status"]}: ${d.OBS_VALUE}%`)
|
|
||||||
.style("left", (event.pageX + 10) + "px")
|
|
||||||
.style("top", (event.pageY - 40) + "px")
|
|
||||||
.transition().duration(300)
|
|
||||||
.style("opacity", 1)
|
|
||||||
.style("transform", "translateY(-10px)");
|
|
||||||
d3.select(this.parentNode).selectAll("rect").style("opacity", 1);
|
|
||||||
g.selectAll(".country-group").filter(gd => gd.Country !== d["Reference area"])
|
|
||||||
.selectAll("rect").style("opacity", 0.2);
|
|
||||||
})
|
|
||||||
.on("mouseout", function() {
|
|
||||||
tooltip.transition().duration(500)
|
|
||||||
.style("opacity", 0)
|
|
||||||
.style("transform", "translateY(0px)");
|
|
||||||
g.selectAll("rect").style("opacity", 0.8);
|
|
||||||
});
|
|
||||||
|
|
||||||
bars.transition()
|
|
||||||
.duration(800)
|
|
||||||
.ease(d3.easeCubicOut)
|
|
||||||
.attr("y", d => y(d.value))
|
|
||||||
.attr("height", d => height - y(d.value));
|
|
||||||
|
|
||||||
g.append("g")
|
|
||||||
.attr("transform", `translate(0,${height})`)
|
|
||||||
.call(d3.axisBottom(x0))
|
|
||||||
.selectAll("text")
|
|
||||||
.attr("transform", "rotate(45)")
|
|
||||||
.style("text-anchor", "start");
|
|
||||||
|
|
||||||
g.append("g")
|
|
||||||
.call(d3.axisLeft(y).ticks(10));
|
|
||||||
|
|
||||||
// Annotate lowest
|
|
||||||
if (groupedData.length > 0) {
|
|
||||||
const flat = groupedData.flatMap(g => g.values);
|
|
||||||
const lowest = flat.reduce((min, d) => d.value < min.value ? d : min, flat[0]);
|
|
||||||
|
|
||||||
const barX = x0(lowest["Reference area"]) + x0.bandwidth() / 2;
|
|
||||||
const barY = y(lowest.value);
|
|
||||||
|
|
||||||
g.append("rect")
|
|
||||||
.attr("x", barX - 50)
|
|
||||||
.attr("y", barY - 30)
|
|
||||||
.attr("width", 100)
|
|
||||||
.attr("height", 18)
|
|
||||||
.attr("rx", 4)
|
|
||||||
.attr("fill", darkMode ? "#222" : "#fff")
|
|
||||||
.attr("opacity", 0.85);
|
|
||||||
|
|
||||||
g.append("text")
|
|
||||||
.attr("x", barX)
|
|
||||||
.attr("y", barY - 17)
|
|
||||||
.attr("text-anchor", "middle")
|
|
||||||
.style("font-size", "12px")
|
|
||||||
.style("font-weight", "bold")
|
|
||||||
.style("fill", darkMode ? "#fff" : "red")
|
|
||||||
.text(`Lowest: ${lowest["Reference area"]}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +152,7 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
||||||
d3.select("#continentSelect").property("value", "All");
|
d3.select("#continentSelect").property("value", "All");
|
||||||
d3.select("#sexSelect").property("value", "All");
|
d3.select("#sexSelect").property("value", "All");
|
||||||
d3.select("#incomeSelect").property("value", "All");
|
d3.select("#incomeSelect").property("value", "All");
|
||||||
|
d3.select("#chartTypeSelect").property("value", "grouped"); // 🆕 Reset chart type too
|
||||||
updateChart();
|
updateChart();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -252,3 +172,303 @@ d3.csv("PHSwithContinent.csv").then(function(data) {
|
||||||
saveSvgAsPng.saveSvgAsPng(document.querySelector("svg"), filename);
|
saveSvgAsPng.saveSvgAsPng(document.querySelector("svg"), filename);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateLegend(chartType) {
|
||||||
|
const legend = d3.select("#legend");
|
||||||
|
legend.html(""); // Clear existing
|
||||||
|
|
||||||
|
if (chartType === "heatmap") {
|
||||||
|
// Create a wrapper for gradient + labels
|
||||||
|
const wrapper = legend.append("div")
|
||||||
|
.style("display", "flex")
|
||||||
|
.style("flex-direction", "column")
|
||||||
|
.style("align-items", "center")
|
||||||
|
.style("margin-top", "10px");
|
||||||
|
|
||||||
|
// Gradient bar
|
||||||
|
wrapper.append("div")
|
||||||
|
.style("width", "300px")
|
||||||
|
.style("height", "20px")
|
||||||
|
.style("background", "linear-gradient(to right, #edf8b1, #2c7fb8)")
|
||||||
|
.style("margin-bottom", "5px");
|
||||||
|
|
||||||
|
// Tick labels (0% — 50% — 100%)
|
||||||
|
const ticks = wrapper.append("div")
|
||||||
|
.style("width", "300px")
|
||||||
|
.style("display", "flex")
|
||||||
|
.style("justify-content", "space-between")
|
||||||
|
.style("font-size", "12px");
|
||||||
|
|
||||||
|
ticks.html('<span>0%</span><span>50%</span><span>100%</span>');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Grouped/Dot Chart — categorical legend
|
||||||
|
educationLevels.forEach(level => {
|
||||||
|
const item = legend.append("div").attr("class", "legend-item");
|
||||||
|
item.append("div")
|
||||||
|
.attr("class", "legend-color")
|
||||||
|
.style("background-color", color(level));
|
||||||
|
item.append("span").text(level);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGrouped(data) {
|
||||||
|
const countries = Array.from(new Set(data.map(d => d["Reference area"])));
|
||||||
|
x0.domain(countries);
|
||||||
|
x1.domain(educationLevels).rangeRound([0, x0.bandwidth()]);
|
||||||
|
y.domain([0, 100]);
|
||||||
|
|
||||||
|
const groupedData = data.reduce((acc, d) => {
|
||||||
|
const found = acc.find(v => v.Country === d["Reference area"]);
|
||||||
|
const value = parseFloat(d.OBS_VALUE);
|
||||||
|
const entry = { ...d, value };
|
||||||
|
if (found) found.values.push(entry);
|
||||||
|
else acc.push({ Country: d["Reference area"], values: [entry] });
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const countryGroups = g.append("g")
|
||||||
|
.selectAll("g")
|
||||||
|
.data(groupedData)
|
||||||
|
.join("g")
|
||||||
|
.attr("transform", d => `translate(${x0(d.Country)},0)`)
|
||||||
|
.attr("class", "country-group");
|
||||||
|
|
||||||
|
countryGroups.selectAll("rect")
|
||||||
|
.data(d => d.values)
|
||||||
|
.join("rect")
|
||||||
|
.attr("x", d => x1(d["Socio-economic status"]))
|
||||||
|
.attr("width", x1.bandwidth())
|
||||||
|
.attr("fill", d => color(d["Socio-economic status"]))
|
||||||
|
.attr("y", height)
|
||||||
|
.attr("height", 0)
|
||||||
|
.on("mouseover", function(event, d) {
|
||||||
|
tooltip.html(`<strong>${d["Reference area"]}</strong><br/>${d["Socio-economic status"]}<br/><strong>${d.OBS_VALUE}%</strong>`)
|
||||||
|
.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)");
|
||||||
|
})
|
||||||
|
.transition()
|
||||||
|
.duration(800)
|
||||||
|
.ease(d3.easeCubicOut)
|
||||||
|
.attr("y", d => y(d.value))
|
||||||
|
.attr("height", d => height - y(d.value));
|
||||||
|
|
||||||
|
g.append("g")
|
||||||
|
.attr("transform", `translate(0,${height})`)
|
||||||
|
.call(d3.axisBottom(x0))
|
||||||
|
.selectAll("text")
|
||||||
|
.attr("transform", "rotate(45)")
|
||||||
|
.style("text-anchor", "start");
|
||||||
|
|
||||||
|
g.append("g")
|
||||||
|
.call(d3.axisLeft(y).ticks(10));
|
||||||
|
|
||||||
|
// 📌 Smart Annotate lowest bar
|
||||||
|
if (groupedData.length > 0) {
|
||||||
|
const flat = groupedData.flatMap(g => g.values);
|
||||||
|
const lowest = flat.reduce((min, d) => d.value < min.value ? d : min, flat[0]);
|
||||||
|
|
||||||
|
const barX = x0(lowest["Reference area"]) + x1(lowest["Socio-economic status"]) + x1.bandwidth() / 2;
|
||||||
|
const barY = y(lowest.value);
|
||||||
|
|
||||||
|
const labelText = `Lowest: ${lowest["Reference area"]}`;
|
||||||
|
|
||||||
|
const tempText = g.append("text")
|
||||||
|
.attr("x", -9999) // Place it offscreen first
|
||||||
|
.attr("y", -9999)
|
||||||
|
.style("font-size", "12px")
|
||||||
|
.style("font-weight", "bold")
|
||||||
|
.text(labelText);
|
||||||
|
|
||||||
|
const textWidth = tempText.node().getBBox().width;
|
||||||
|
tempText.remove(); // Cleanup
|
||||||
|
|
||||||
|
// Background rectangle scaled to text size
|
||||||
|
g.append("rect")
|
||||||
|
.attr("x", barX - textWidth / 2 - 6)
|
||||||
|
.attr("y", barY - 28)
|
||||||
|
.attr("width", textWidth + 12)
|
||||||
|
.attr("height", 20)
|
||||||
|
.attr("rx", 4)
|
||||||
|
.attr("fill", darkMode ? "#222" : "#fff")
|
||||||
|
.attr("stroke", darkMode ? "#aaa" : "#999")
|
||||||
|
.attr("stroke-width", 0.5)
|
||||||
|
.attr("opacity", 0.9);
|
||||||
|
|
||||||
|
// Final visible text
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", barX)
|
||||||
|
.attr("y", barY - 14)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.style("font-size", "12px")
|
||||||
|
.style("font-weight", "bold")
|
||||||
|
.style("fill", darkMode ? "#eee" : "red")
|
||||||
|
.text(labelText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDotPlot(data) {
|
||||||
|
const x = d3.scaleLinear()
|
||||||
|
.domain([0, 100])
|
||||||
|
.range([0, width]);
|
||||||
|
|
||||||
|
const y = d3.scaleBand()
|
||||||
|
.domain([...new Set(data.map(d => d["Reference area"]))])
|
||||||
|
.range([0, height])
|
||||||
|
.padding(0.3);
|
||||||
|
|
||||||
|
const groupedData = data.reduce((acc, d) => {
|
||||||
|
const found = acc.find(v => v.Country === d["Reference area"]);
|
||||||
|
const value = parseFloat(d.OBS_VALUE);
|
||||||
|
const entry = { ...d, value };
|
||||||
|
if (found) found.values.push(entry);
|
||||||
|
else acc.push({ Country: d["Reference area"], values: [entry] });
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const countryGroups = g.append("g")
|
||||||
|
.selectAll("g")
|
||||||
|
.data(groupedData)
|
||||||
|
.join("g")
|
||||||
|
.attr("transform", d => `translate(0,${y(d.Country)})`)
|
||||||
|
.attr("class", "country-group");
|
||||||
|
|
||||||
|
countryGroups.selectAll("circle")
|
||||||
|
.data(d => d.values)
|
||||||
|
.join("circle")
|
||||||
|
.attr("cy", d => 0)
|
||||||
|
.attr("cx", 0)
|
||||||
|
.attr("r", 0)
|
||||||
|
.attr("fill", d => color(d["Socio-economic status"]))
|
||||||
|
.on("mouseover", function(event, d) {
|
||||||
|
tooltip.html(`<strong>${d["Reference area"]}</strong><br/>${d["Socio-economic status"]}<br/><strong>${d.OBS_VALUE}%</strong>`)
|
||||||
|
.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)");
|
||||||
|
})
|
||||||
|
.transition()
|
||||||
|
.duration(800)
|
||||||
|
.ease(d3.easeCubicOut)
|
||||||
|
.attr("cx", d => x(d.value))
|
||||||
|
.attr("r", 6);
|
||||||
|
|
||||||
|
g.append("g")
|
||||||
|
.attr("transform", `translate(0,${height})`)
|
||||||
|
.call(d3.axisBottom(x).ticks(10));
|
||||||
|
|
||||||
|
g.append("g")
|
||||||
|
.call(d3.axisLeft(y));
|
||||||
|
|
||||||
|
// Add X-axis label
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", width / 2)
|
||||||
|
.attr("y", height + 50)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.style("font-size", "14px")
|
||||||
|
.style("fill", darkMode ? "#fff" : "#000")
|
||||||
|
.text("Percentage Reporting Good Health (%)");
|
||||||
|
|
||||||
|
// 📌 Smart annotate lowest dot
|
||||||
|
if (groupedData.length > 0) {
|
||||||
|
const flat = groupedData.flatMap(g => g.values);
|
||||||
|
const lowest = flat.reduce((min, d) => d.value < min.value ? d : min, flat[0]);
|
||||||
|
|
||||||
|
const dotX = x(lowest.value);
|
||||||
|
const dotY = y(lowest["Reference area"]);
|
||||||
|
|
||||||
|
const labelText = `Lowest: ${lowest["Reference area"]}`;
|
||||||
|
|
||||||
|
const tempText = g.append("text")
|
||||||
|
.attr("x", -9999)
|
||||||
|
.attr("y", -9999)
|
||||||
|
.style("font-size", "12px")
|
||||||
|
.style("font-weight", "bold")
|
||||||
|
.text(labelText);
|
||||||
|
|
||||||
|
const textWidth = tempText.node().getBBox().width;
|
||||||
|
tempText.remove();
|
||||||
|
|
||||||
|
g.append("rect")
|
||||||
|
.attr("x", dotX - textWidth / 2 - 6)
|
||||||
|
.attr("y", dotY - 28)
|
||||||
|
.attr("width", textWidth + 12)
|
||||||
|
.attr("height", 20)
|
||||||
|
.attr("rx", 4)
|
||||||
|
.attr("fill", darkMode ? "#222" : "#fff")
|
||||||
|
.attr("stroke", darkMode ? "#aaa" : "#999")
|
||||||
|
.attr("stroke-width", 0.5)
|
||||||
|
.attr("opacity", 0.9);
|
||||||
|
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", dotX)
|
||||||
|
.attr("y", dotY - 14)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.style("font-size", "12px")
|
||||||
|
.style("font-weight", "bold")
|
||||||
|
.style("fill", darkMode ? "#eee" : "red")
|
||||||
|
.text(labelText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHeatmap(data) {
|
||||||
|
const countries = [...new Set(data.map(d => d["Reference area"]))];
|
||||||
|
|
||||||
|
const x = d3.scaleBand()
|
||||||
|
.domain(educationLevels)
|
||||||
|
.range([0, width])
|
||||||
|
.padding(0.05);
|
||||||
|
|
||||||
|
const y = d3.scaleBand()
|
||||||
|
.domain(countries)
|
||||||
|
.range([0, height])
|
||||||
|
.padding(0.05);
|
||||||
|
|
||||||
|
const colorScale = d3.scaleSequential()
|
||||||
|
.interpolator(d3.interpolateYlGnBu)
|
||||||
|
.domain([0, 100]);
|
||||||
|
|
||||||
|
g.selectAll()
|
||||||
|
.data(data)
|
||||||
|
.join("rect")
|
||||||
|
.attr("x", d => x(d["Socio-economic status"]))
|
||||||
|
.attr("y", d => y(d["Reference area"]))
|
||||||
|
.attr("width", x.bandwidth())
|
||||||
|
.attr("height", y.bandwidth())
|
||||||
|
.attr("fill", d => colorScale(parseFloat(d.OBS_VALUE)))
|
||||||
|
.on("mouseover", function(event, d) {
|
||||||
|
tooltip.html(`<strong>${d["Reference area"]}</strong><br/>${d["Socio-economic status"]}: ${d.OBS_VALUE}%`)
|
||||||
|
.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)");
|
||||||
|
});
|
||||||
|
|
||||||
|
g.append("g")
|
||||||
|
.attr("transform", `translate(0,${height})`)
|
||||||
|
.call(d3.axisBottom(x));
|
||||||
|
|
||||||
|
g.append("g")
|
||||||
|
.call(d3.axisLeft(y));
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue