lnNoam lnNoam - 1 year ago 75
Javascript Question

Resize points based on zoom

I am trying to create a map in d3.js where the points (US Science funding agencies), read in from a .csv, scale with zoom level (based on this starter kit). I have seen other solutions to the problem elsewhere, but they are often instantiated in much more complex projects (making it very hard for me to extract this capability from them).

Read in data:

var data;
function draw(){
d3.csv("data/funders.csv", function(err, locations) {
addPoint(i['lng'], i['lat'], i['TotalFunding']);

I define svg like this:

svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom) // my zoom function
.on("click", click) // my click function

g = svg.append("g");

Next, I've defined a function that appends "g" to the SVG and then I proceed to add a class "gpoint" (geographic point).

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.
.attr("cx", x)
.attr("cy", y)
.style("fill", "blue")
.attr("r", size/10); //*

*this original size information needs to be preserved, just scaled.

Here, I'd like to multiply
by the current level of zoom (which can be obtained from
var scale = d3.event.scale;
). In the code I have, I use scale to adjust a CSS element controlling the stroke-width of country outlines with this:

d3.selectAll(".country").style("stroke-width", 1.5 / scale);

However, this is easy because I can create a simple chain to access the '.country' CSS and mutate this property. However, I am unclear on how I would select and mutate elements in this gpoint class.

I'm happy to add any additional information, I'm just becoming wary of posting a wall of code.


funder,lat,lng, TotalFunding
NIH,39.000443,-77.102394, 5000
NASA,38.883,-77.0163, 1000


I've found that I can alter the radius of the circles with

g.attr("class", "gpoint").selectAll("circle").attr("r", s)

However, I still am having trouble accessing the current radius of the circle and mutating it, e.g.,

g.attr("class", "gpoint").selectAll("circle").data(data).attr("r", function(d){return(d.r*s);})

Edit 2

Thanks to the help from @kscandrett, I was able to make this work.

One of the requirements was that the original size was maintained.
Simply altering
will not do this, but the
information can be set as the dot's
when the points are created (I'm sure there are better solutions, such as using an object to store this information...but, this works).


Save the funding amount information as the dot's
when they're being created (again, probably not idiomatic, but it will do for now).

.attr("id", Math.sqrt(parseInt(amount) * 0.001))
.attr("r", Math.sqrt(parseInt(amount) * 0.001))


Define a pointScale Function

function pointScale(amount, zoomScale){
// Ugly code that will almost certainly be replace, but it is good enough for now.
var maxPossibleZoom = 100;
var sizeFloor = 0.12;
var size = amount * (maxPossibleZoom/(zoomScale*0.05));
if (size > amount){
return amount;
} else if (size < sizeFloor){
return sizeFloor * amount;
} else {
return size;


The starter kit I link to above has a
The final step is to add some code to it using @kscandrett's answer to extract the value assigned to
and use
to scale this value based on the current level of zoom.

function move(){
var s = d3.event.scale;
d3.selectAll('circle').attr('r', function (d, i){
var amount = d3.select(this).attr('id');
return pointScale(amount, s);

Works perfectly!

Answer Source

This example will increase the circles by 10%

function changeSize() {
  d3.selectAll('circle').attr('r', function (d, i)
    return d3.select(this).attr('r') * 1.1;

Example: http://codepen.io/anon/pen/vXRdGx

As a starting point I used some data created by Jerome Cuckier http://www.jeromecukier.net/blog/2012/05/28/manipulating-data-like-a-boss-with-d3/

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download