chart = {
const data = almeraMassEmailList1;
const colorScale = d3.scaleOrdinal()
.domain(["EUROPE", "NORTH AND LATIN AMERICA", "ASIA PACIFIC", "AFRICA", "MIDDLE EAST"])
.range(["red", "blue", "green", "yellow", "purple"]);
const context = DOM.context2d(width, height);
const projection = d3.geoEqualEarth().fitSize([width, height], sphere);
const path = d3.geoPath(projection, context);
const tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("padding", "8px")
.style("background", "rgba(0, 0, 0, 0.7)")
.style("color", "white")
.style("border-radius", "4px")
.style("pointer-events", "none")
.style("font-size", "12px")
.style("display", "none");
let selectedPoint = null;
function render(land) {
context.clearRect(0, 0, width, height);
context.beginPath();
path(sphere);
context.fillStyle = "#fff";
context.fill();
// Draw the land with the new color
context.beginPath();
path(land);
context.fillStyle = "#66a5d2"; // New land color
context.fill();
// Draw the dots
const points = data.map(d => {
const [x, y] = projection([+d.Long, +d.Lat]) || [];
return {
x,
y,
color: colorScale(d["Geographic Region"] || "black"),
info: d["Laboratory Name"] || "Laboratory Missing",
contact: d["Contact Person"] || "No contact available",
memberState: d["Member State"] || "No member state",
telephone: d["Telephone"] || "No telephone available",
email: d["Email"] || "No email available",
address: d["Physical Address"] || "No address available",
h3: d["H-3"] ? d["H-3"] : "No",
be7: d["Be-7"] ? d["B-7"] : "No",
c14: d["C-14"] ? d["C-14"] : "No",
k40: d["K-40"] ? d["K-40"] : "No",
co60: d["Co-60"] ? d["Co-60"] : "No",
sr90: d["Sr-90"] ? d["Sr-90"] : "No",
tc99: d["Tc-99"] ? d["Tc-99"] : "No",
i125: d["I-125"] ? d["I-125"] : "No",
i129: d["I-129"] ? d["I-129"] : "No",
i131: d["I-131"] ? d["I-131"] : "No",
ba133: d["Ba-133"] ? d["Ba-133"] : "No",
cs134: d["Cs-134"] ? d["Cs-134"] : "No",
cs137: d["Cs-137"] ? d["Cs-137"] : "No",
eu152: d["Eu-152"] ? d["Eu-152"] : "No",
lu177: d["Lu-177"] ? d["Lu-177"] : "No",
pb210: d["Pb-210"] ? d["Pb-210"] : "No",
po210: d["Po-210"] ? d["Po-210"] : "No",
rn: d["Rn"] ? d["Rn"] : "No",
rn222: d["Rn-222"] ? d["Rn-222"] : "No",
rn226: d["Rn-226"] ? d["Rn-226"] : "No",
ra226: d["Ra-226"] ? d["Ra-226"] : "No",
ra228: d["Ra-228"] ? d["Ra-228"] : "No",
ac228: d["Ac-228"] ? d["Ac-228"] : "No",
th228: d["Th-228"] ? d["Th-228"] : "No",
th232: d["Th-232"] ? d["Th-232"] : "No",
u234: d["U-234"] ? d["U-234"] : "No",
u235: d["U-235"] ? d["U-235"] : "No",
u238: d["U-238"] ? d["U-238"] : "No",
u: d["U"] ? d["U"] : "No",
pu: d["Pu"] ? d["Pu"] : "No",
pu238: d["Pu-238"] ? d["Pu-238"] : "No",
pu239: d["Pu-239"] ? d["Pu-239"] : "No",
pu240: d["Pu-240"] ? d["Pu-240"] : "No",
pu241: d["Pu-241"] ? d["Pu-241"] : "No",
am241: d["Am-241"] ? d["Am-241"] : "No",
cm242: d["Cm-242"] ? d["Cm-242"] : "No",
grossalphabeta: d["Gross alpha / beta"] ? d["Gross alpha / beta"] : "No",
gammaemit: d["Gamma emitters"] ? d["Gamma emitters"] : "No",
grossbeta: d["Gross beta"] ? d["Gross beta"] : "No",
};
});
points.forEach(point => {
if (point.x !== undefined && point.y !== undefined) {
context.beginPath();
context.arc(point.x, point.y, 5, 0, 2 * Math.PI);
context.fillStyle = point.color;
context.fill();
}
});
return points; // Return the points for interaction handling
}
// Initial render
let points = render(land50);
// Add mousemove detection for tooltips
d3.select(context.canvas)
.on("mousemove", event => {
if (selectedPoint) return; // Do nothing if a point is selected
const [mouseX, mouseY] = d3.pointer(event);
const hoveredPoint = points.find(point => {
const dx = mouseX - point.x;
const dy = mouseY - point.y;
return Math.sqrt(dx * dx + dy * dy) <= 5; // Check if within the radius of a dot
});
if (hoveredPoint) {
tooltip
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY + 10}px`)
.style("display", "block")
.html(`
<strong>${hoveredPoint.info}</strong><br>
🌍 ${hoveredPoint.memberState}<br>
H-3: ${hoveredPoint.h3} <br>
C-14: ${hoveredPoint.c14} <br>
K-40: ${hoveredPoint.k40} <br>
Sr-90: ${hoveredPoint.sr90} <br>
I-131: ${hoveredPoint.i131} <br>
Cs-137: ${hoveredPoint.cs137} <br>
Pb-210: ${hoveredPoint.pb210} <br>
Ra-226: ${hoveredPoint.ra226} <br>
Th-232: ${hoveredPoint.th232} <br>
Pu-239: ${hoveredPoint.pu239} <br>
Am-241: ${hoveredPoint.am241} <br>
`)
} else {
tooltip.style("display", "none");
}
});
// Add click detection for dots
d3.select(context.canvas)
.on("click", event => {
const [mouseX, mouseY] = d3.pointer(event);
const clickedPoint = points.find(point => {
const dx = mouseX - point.x;
const dy = mouseY - point.y;
return Math.sqrt(dx * dx + dy * dy) <= 5;
});
if (clickedPoint) {
// Store the clicked point and keep the tooltip visible
selectedPoint = clickedPoint;
tooltip
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY + 10}px`)
.style("display", "block")
.html(`
<strong>${clickedPoint.info}</strong><br>
🌍 ${clickedPoint.memberState}<br>
🏢 ${clickedPoint.address}<br>
🧑💼 ${clickedPoint.contact}<br>
📧 ${clickedPoint.email}<br>
📞 ${clickedPoint.telephone}<br>
H-3: ${clickedPoint.h3} <br>
Be-7: ${clickedPoint.be7} <br>
C-14: ${clickedPoint.c14} <br>
K-40: ${clickedPoint.k40} <br>
Co-60: ${clickedPoint.co60} <br>
Sr-90: ${clickedPoint.sr90} <br>
Tc-99: ${clickedPoint.tc99} <br>
I-125: ${clickedPoint.i125} <br>
I-129: ${clickedPoint.i129} <br>
I-131: ${clickedPoint.i131} <br>
Ba-133: ${clickedPoint.ba133} <br>
Cs-134: ${clickedPoint.cs134} <br>
Cs-137: ${clickedPoint.cs137} <br>
`);
} else {
// If clicked outside, hide the tooltip and reset the selected point
selectedPoint = null;
tooltip.style("display", "none");
}
});
// Handle smooth zoom events
const zoom = d3.zoom()
.scaleExtent([1, 8]) // Adjust the zoom limits
.on("zoom", (event) => {
// Update projection with the new transformation
const transform = event.transform;
projection.translate([transform.x, transform.y]).scale(transform.k * 300); // Scale dynamically
// Dynamically redraw the map and recalculate points
points = render(land50);
});
// Attach zoom behavior to the canvas
d3.select(context.canvas)
.call(zoom)
.on("dblclick.zoom", null); // Disable double-click zoom
return d3.select(context.canvas).node();
}