function createZoomAbleMap(chosenWidth=900, chosenHeight=500, minZoom=0.5, maxZoom=100, projection=d3.geoMercator(), data=zoomAbleMapObject, fillColor="red", strokeColor="white", fillColorNone="#444", names = false, textColor = "grey", fontSize = 1, coordinates = zoomAbleWorld, markersData=Catastrophes, radius=1, markersFillColor="orange") {
const width = chosenWidth;
const height = chosenHeight;
const zoom = d3.zoom()
.scaleExtent([minZoom, maxZoom])
.on("zoom", zoomed);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: 100%;")
.on("click", reset);
const path = d3.geoPath(projection);
const g = svg.append("g");
let land = null;
let states = null;
let textLabels = null;
let markers = null;
function update(data){
g.selectAll("*").remove();
if (!data.features) {
console.error("Data features are null or undefined");
return;
}
land = g.append("g")
.attr("fill", "grey")
.selectAll("path")
.data(landData.features)
.join("path")
.attr("d", path)
.attr("cursor", "pointer")
states = g.append("g")
.attr("fill", fillColorNone)
.attr("cursor", "pointer")
.attr("stroke", strokeColor)
.attr("stroke-width", 0.1)
.selectAll("path")
.data(data.features)
.join("path")
.attr("d", path)
.on("click", clicked);
textLabels = g.append("g")
.attr("pointer-events", "none")
.selectAll("text")
.data(data.features)
.join("text")
.attr("text-anchor", "middle")
.style("font-size", fontSize)
.attr("fill", textColor)
.text(d => d.properties.name)
.attr("transform", d => `translate(${path.centroid(d)})`);
states.append("title").text(d => d.properties.name);
markers = g.selectAll("circle")
.data(markersData)
.join("g");
// Append circles
markers.append("circle")
.attr("cx", d => projection([d.Longitude, d.Latitude])[0])
.attr("cy", d => projection([d.Longitude, d.Latitude])[1])
.attr("r", radius)
.attr("fill", function(d){
if(d.DisasterSubgroup === "Biological"){return "limegreen"}
else if(d.DisasterSubgroup === "Climatological"){return "red"}
else if(d.DisasterSubgroup === "Geophysical"){return "orange"}
else if(d.DisasterSubgroup === "Hydrological"){return "lightblue"}
else if(d.DisasterSubgroup === "Meteorological"){return "yellow"}
});
markers.append("title")
.text(d => createTooltip(d));
g.append("path")
.attr("fill", "none")
.attr("stroke", strokeColor)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 10)
.attr("d", path(countrymesh.features));
}
update(data);
svg.call(zoom);
function reset() {
states.transition().style("fill", null);
setTimeout(() => {data = zoomAbleMapObject; fontSize = 10; update(data);}, 750);
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity,
d3.zoomTransform(svg.node()).invert([width, height])
);
}
function clicked(event, d) {
console.log(d)
if(d.properties.name != "Antarctica"){
const [[x0, y0], [x1, y1]] = path.bounds(d);
event.stopPropagation();
states.transition().style("fill", null);
d3.select(this).transition().style("fill", fillColor);
console.log(d)
if(d.properties.name === "South America"){
d = d.features[0]
}
if(data.territoryType !== "Region"){
setTimeout(() => {
data = d;
const transform = d3.zoomTransform(svg.node());
const fontSizeScaled = Math.min((fontSize / transform.k) * 10, 2);
if(data.territoryType == "Country"){
fontSizeScaled = Math.min((fontSize / transform.k) * 10, 1);
}
radius = Math.max(0.01, Math.min((radius / transform.k) * 2, 1))
update(data);
textLabels.style("font-size", fontSizeScaled);}, 750);
}
svg.transition().duration(750).call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(Math.min(100, 0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height)))
.translate(-(x0 + x1) / 2, -(y0 + y1) / 2),
d3.pointer(event, svg.node()),
);
}
}
function zoomed(event) {
const {transform} = event;
g.attr("transform", transform);
g.attr("stroke-width", 10);// / transform.k);
const fontSizeScaled = Math.min((fontSize / transform.k), 2);
textLabels.style("font-size", fontSizeScaled);
textLabels.attr("fill", textColor);
// Update marker radius on zoom
markers.selectAll("circle")
.attr("r", Math.max(0.01, Math.min((radius / transform.k) * 2, 1)));
}
return svg.node();
}