I've been working on creating a dynamic map in d3.js that displays US Science funding agencies as points, with their sizes scaling based on zoom level. I referred to this starter kit for guidance. While there are other solutions out there, they often come within larger, more complex projects which makes it challenging to extract just the functionality I need.
The first step is reading in the data:
var data;
function draw(){
....
d3.csv("data/funders.csv", function(err, locations) {
locations.forEach(function(i){
addPoint(i['lng'], i['lat'], i['TotalFunding']);
});
});
}
To define the svg, I use the following code:
svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom) // my zoom function
.on("click", click) // my click function
.append("g");
g = svg.append("g");
Next, I create a function that appends "g" to the SVG and adds a "gpoint" (geographic point) class to it.
function addPoint(lat, lon, size){
var gpoint = g.append("g").attr("class", "gpoint");
var location = projection([lat,lon])
var x = location[0];
var y = location[1];
// Here I append 'circle' to the svg.
gpoint.append("svg:circle")
.attr("cx", x)
.attr("cy", y)
.attr("class","point")
.style("fill", "blue")
.attr("r", size/10); //*
}
*The original size information needs to be preserved but scaled accordingly.
To dynamically adjust the circle radius based on the current zoom level, I aim to multiply the size by the zoom scale. A similar approach involving CSS adjustments for country outlines can be applied here:
d3.selectAll(".country").style("stroke-width", 1.5 / scale);
However, implementing this logic for elements within the "gpoint" class poses a challenge that I'm currently exploring.
If needed, additional details can be provided without overwhelming with excessive code.
funders.csv:
funder,lat,lng,TotalFunding
NIH,39.000443,-77.102394,5000
NASA,38.883,-77.0163,1000
Edit
While I managed to adjust circle radii using
g.attr("class", "gpoint").selectAll("circle").attr("r", s)
I'm still figuring out how to access and modify existing circle radii, for instance, through:
g.attr("class", "gpoint").selectAll("circle").data(data).attr("r", function(d){return(d.r*s);})
Edit 2
With @kscandrett's assistance, I made progress on this issue.
Preserving the original size was essential. By assigning the funding amount as the dot's ID upon creation, we ensure this data remains intact:
gpoint.append("svg:circle")
//...
.attr("id", Math.sqrt(parseInt(amount) * 0.001))
.attr("r", Math.sqrt(parseInt(amount) * 0.001))
//...
EDIT 3:
A more efficient method involves using datum
instead of attr
to associate information with each circle:
.datum(Math.sqrt(parseInt(amount) * 0.001))
Further updates involve defining a pointScale function and incorporating it into the move() function for full functionality.